From 53bc2c949a154cbc34807a97795c90e8894aac0b Mon Sep 17 00:00:00 2001 From: Planet-Lab Support Date: Mon, 8 Aug 2005 21:11:58 +0000 Subject: [PATCH] This commit was manufactured by cvs2svn to create tag 'after-2_6_9-imports'. --- .cvsignore | 13 + Documentation/CodingStyle | 4 +- Documentation/DocBook/Makefile | 4 +- Documentation/DocBook/gadget.tmpl | 245 +- Documentation/DocBook/mousedrivers.tmpl | 1 - Documentation/DocBook/usb.tmpl | 1 + Documentation/IPMI.txt | 30 + Documentation/ManagementStyle | 276 + Documentation/RCU/RTFP.txt | 387 + Documentation/RCU/UP.txt | 64 + Documentation/RCU/arrayRCU.txt | 141 + Documentation/RCU/checklist.txt | 157 + Documentation/RCU/listRCU.txt | 307 + Documentation/RCU/rcu.txt | 67 + Documentation/arm/IXP2000 | 69 + .../arm/Samsung-S3C24XX/EB2410ITX.txt | 44 + Documentation/arm/Samsung-S3C24XX/GPIO.txt | 122 + .../arm/Samsung-S3C24XX/Overview.txt | 97 + Documentation/ckrm/cpusched | 86 + Documentation/crypto/api-intro.txt | 4 + Documentation/driver-model/bus.txt | 74 +- Documentation/filesystems/Locking | 29 +- Documentation/filesystems/ext2.txt | 34 +- Documentation/filesystems/ext3.txt | 72 +- Documentation/filesystems/tmpfs.txt | 12 +- Documentation/filesystems/vfs.txt | 2 +- Documentation/filesystems/xfs.txt | 29 +- .../firmware_sample_firmware_class.c | 3 +- Documentation/firmware_class/hotplug-script | 4 +- Documentation/i2c/sysfs-interface | 2 +- Documentation/i2o/README | 63 + Documentation/i2o/ioctl | 394 + Documentation/input/joystick-parport.txt | 13 +- Documentation/kbuild/makefiles.txt | 145 +- Documentation/networking/README.ipw2100 | 179 + Documentation/networking/README.ipw2200 | 194 + Documentation/networking/gen_stats.txt | 117 + Documentation/networking/netdevices.txt | 13 + Documentation/powerpc/hvcs.txt | 39 +- Documentation/powerpc/mpc52xx.txt | 11 +- Documentation/sched-domains.txt | 27 +- Documentation/sched-stats.txt | 153 + Documentation/scsi/ChangeLog.megaraid | 241 +- Documentation/scsi/megaraid.txt | 70 + .../sound/alsa/DocBook/alsa-driver-api.tmpl | 1 - Documentation/sysctl/kernel.txt | 22 +- Documentation/time_interpolators.txt | 40 + Documentation/tty.txt | 194 + Documentation/usb/sn9c102.txt | 55 +- Documentation/vm/overcommit-accounting | 8 +- Documentation/vserver/debug.txt | 77 +- Documentation/watchdog/pcwd-watchdog.txt | 3 +- Documentation/x86_64/boot-options.txt | 24 +- MAINTAINERS | 11 + Makefile | 6 +- TAGS | 367509 +++++++++++++++ arch/alpha/Kconfig.debug | 59 + arch/alpha/boot/Makefile | 2 +- arch/alpha/kernel/Makefile | 4 +- arch/alpha/kernel/alpha_ksyms.c | 59 +- arch/alpha/kernel/io.c | 630 + arch/alpha/kernel/irq_impl.h | 29 - arch/alpha/kernel/pci-noop.c | 75 +- arch/alpha/kernel/pci.c | 49 +- arch/alpha/kernel/pci_iommu.c | 39 + arch/alpha/kernel/srmcons.c | 14 +- arch/alpha/kernel/sys_alcor.c | 6 +- arch/alpha/kernel/sys_cabriolet.c | 5 - arch/alpha/kernel/sys_eb64p.c | 2 - arch/alpha/kernel/sys_jensen.c | 3 - arch/alpha/kernel/sys_marvel.c | 1 - arch/alpha/kernel/sys_miata.c | 1 - arch/alpha/kernel/sys_mikasa.c | 2 - arch/alpha/kernel/sys_nautilus.c | 1 - arch/alpha/kernel/sys_noritake.c | 2 - arch/alpha/kernel/sys_rawhide.c | 1 - arch/alpha/kernel/sys_rx164.c | 1 - arch/alpha/kernel/sys_sable.c | 3 - arch/alpha/kernel/sys_sio.c | 5 - arch/alpha/kernel/sys_titan.c | 18 +- arch/alpha/kernel/sys_wildfire.c | 1 - arch/alpha/kernel/vmlinux.lds.S | 6 +- arch/alpha/lib/Makefile | 1 - arch/arm/Kconfig.debug | 115 + arch/arm/boot/compressed/big-endian.S | 13 + arch/arm/boot/compressed/misc.c | 24 +- arch/arm/configs/ebsa110_defconfig | 1 - arch/arm/configs/enp2611_defconfig | 795 + arch/arm/configs/ep80219_defconfig | 849 + arch/arm/configs/h7201_defconfig | 511 + arch/arm/configs/h7202_defconfig | 652 + arch/arm/configs/iq31244_defconfig | 818 + arch/arm/configs/iq80321_defconfig | 433 +- arch/arm/configs/iq80331_defconfig | 753 + arch/arm/configs/ixdp2400_defconfig | 796 + arch/arm/configs/ixdp2401_defconfig | 797 + arch/arm/configs/ixdp2800_defconfig | 796 + arch/arm/configs/ixdp2801_defconfig | 797 + arch/arm/configs/ixp4xx_defconfig | 1 - arch/arm/configs/mx1ads_defconfig | 652 + arch/arm/configs/s3c2410_defconfig | 8 + arch/arm/kernel/apm.c | 362 +- arch/arm/kernel/calls.S | 9 +- arch/arm/kernel/iwmmxt.S | 320 + arch/arm/kernel/vmlinux.lds.S | 4 +- arch/arm/lib/getuser.S | 57 +- arch/arm/lib/putuser.S | 59 +- arch/arm/mach-h720x/Kconfig | 27 + arch/arm/mach-h720x/Makefile | 16 + arch/arm/mach-h720x/common.c | 247 + arch/arm/mach-h720x/cpu-h7201.c | 59 + arch/arm/mach-h720x/cpu-h7202.c | 165 + arch/arm/mach-h720x/h7201-eval.c | 42 + arch/arm/mach-h720x/h7202-eval.c | 85 + arch/arm/mach-imx/Kconfig | 10 + arch/arm/mach-imx/Makefile | 19 + arch/arm/mach-imx/dma.c | 203 + arch/arm/mach-imx/generic.c | 274 + arch/arm/mach-imx/generic.h | 14 + arch/arm/mach-imx/irq.c | 252 + arch/arm/mach-imx/leds-mx1ads.c | 54 + arch/arm/mach-imx/leds.c | 31 + arch/arm/mach-imx/leds.h | 9 + arch/arm/mach-imx/mx1ads.c | 88 + arch/arm/mach-imx/time.c | 95 + arch/arm/mach-integrator/clock.c | 9 +- arch/arm/mach-iop3xx/Kconfig | 61 +- arch/arm/mach-iop3xx/Makefile | 21 +- arch/arm/mach-iop3xx/common.c | 75 + arch/arm/mach-iop3xx/iop321-irq.c | 3 +- arch/arm/mach-iop3xx/iop321-mm.c | 43 + arch/arm/mach-iop3xx/iop321-pci.c | 64 +- arch/arm/mach-iop3xx/iop321-setup.c | 63 + arch/arm/mach-iop3xx/iop331-irq.c | 127 + arch/arm/mach-iop3xx/iop331-mm.c | 43 + arch/arm/mach-iop3xx/iop331-pci.c | 217 + arch/arm/mach-iop3xx/iop331-setup.c | 102 + arch/arm/mach-iop3xx/iop331-time.c | 141 + arch/arm/mach-iop3xx/iq31244-mm.c | 44 + arch/arm/mach-iop3xx/iq31244-pci.c | 134 + arch/arm/mach-iop3xx/iq80321-mm.c | 44 + arch/arm/mach-iop3xx/iq80321-pci.c | 83 +- arch/arm/mach-iop3xx/iq80331-mm.c | 36 + arch/arm/mach-iop3xx/iq80331-pci.c | 138 + arch/arm/mach-ixp2000/Kconfig | 59 + arch/arm/mach-ixp2000/Makefile | 14 + arch/arm/mach-ixp2000/core.c | 426 + arch/arm/mach-ixp2000/enp2611.c | 210 + arch/arm/mach-ixp2000/ixdp2400.c | 177 + arch/arm/mach-ixp2000/ixdp2800.c | 178 + arch/arm/mach-ixp2000/ixdp2x00.c | 307 + arch/arm/mach-ixp2000/ixdp2x01.c | 381 + arch/arm/mach-ixp2000/pci.c | 233 + arch/arm/mach-ixp4xx/common.c | 2 +- arch/arm/mach-lh7a40x/time.c | 8 +- arch/arm/mach-omap/board-h2.c | 115 + arch/arm/mach-omap/board-h3.c | 90 + arch/arm/mach-omap/clocks.c | 11 +- arch/arm/mach-omap/common.c | 53 +- arch/arm/mach-omap/fpga.c | 30 +- arch/arm/mach-omap/leds-h2p2-debug.c | 104 + arch/arm/mach-omap/leds.c | 9 +- arch/arm/mach-omap/leds.h | 2 +- arch/arm/mach-omap/mcbsp.c | 669 + arch/arm/mach-omap/ocpi.c | 4 +- arch/arm/mach-omap/usb.c | 541 + arch/arm/mach-pxa/irq.c | 103 +- arch/arm/mach-pxa/leds-mainstone.c | 1 + arch/arm/mach-pxa/mainstone.c | 1 + arch/arm/mach-pxa/pxa25x.c | 1 + arch/arm/mach-pxa/pxa27x.c | 1 + arch/arm/mach-pxa/sleep.S | 43 +- arch/arm/mach-pxa/time.c | 1 + arch/arm/mach-s3c2410/clock.c | 310 + arch/arm/mach-s3c2410/clock.h | 20 + arch/arm/mach-s3c2410/cpu.c | 152 + arch/arm/mach-s3c2410/cpu.h | 41 + arch/arm/mach-s3c2410/devs.c | 442 + arch/arm/mach-s3c2410/devs.h | 36 + arch/arm/mach-s3c2410/dma.c | 1085 + arch/arm/mach-s3c2410/gpio.c | 118 +- arch/arm/mach-s3c2410/irq.c | 201 +- arch/arm/mach-s3c2410/mach-smdk2410.c | 22 +- arch/arm/mach-s3c2410/s3c2440-dsc.c | 57 + arch/arm/mach-s3c2410/s3c2440.c | 192 + arch/arm/mach-s3c2410/s3c2440.h | 18 + arch/arm/mach-s3c2410/time.c | 19 +- arch/arm/mach-s3c2410/usb-simtec.c | 123 + arch/arm/mach-s3c2410/usb-simtec.h | 19 + arch/arm/mach-sa1100/pm.c | 19 +- arch/arm/mach-versatile/clock.c | 4 +- arch/arm/mm/Makefile | 3 +- arch/arm/mm/abort-ev5tj.S | 1 - arch/arm/mm/cache-v3.S | 14 + arch/arm/mm/cache-v4.S | 14 + arch/arm/mm/cache-v4wb.S | 14 + arch/arm/mm/cache-v4wt.S | 14 + arch/arm/mm/copypage-v6.c | 92 +- arch/arm/mm/flush.c | 94 + arch/arm/mm/proc-arm1020.S | 14 + arch/arm/mm/proc-arm1020e.S | 15 +- arch/arm/mm/proc-arm1022.S | 16 +- arch/arm/mm/proc-arm1026.S | 15 +- arch/arm/mm/proc-arm920.S | 14 + arch/arm/mm/proc-arm922.S | 14 + arch/arm/mm/proc-arm925.S | 14 + arch/arm/mm/proc-arm926.S | 16 +- arch/arm/mm/proc-syms.c | 8 +- arch/arm/mm/proc-v6.S | 10 +- arch/arm/nwfpe/fpa11_cpdt.c | 10 + arch/arm/oprofile/op_model_xscale.c | 29 +- arch/arm26/Kconfig.debug | 60 + arch/arm26/Makefile | 4 +- arch/arm26/boot/Makefile | 4 +- arch/arm26/kernel/Makefile | 2 +- arch/arm26/kernel/armksyms.c | 98 +- arch/arm26/kernel/signal.c | 4 +- arch/arm26/kernel/sys_arm.c | 2 +- arch/arm26/kernel/time.c | 24 +- arch/arm26/kernel/vmlinux-arm26-xip.lds.in | 3 - arch/arm26/kernel/vmlinux-arm26.lds.in | 3 - arch/cris/Kconfig.debug | 28 + arch/cris/arch-v10/mm/init.c | 2 +- arch/cris/arch-v10/vmlinux.lds.S | 3 - arch/cris/kernel/crisksyms.c | 5 +- arch/h8300/Kconfig.debug | 68 + arch/h8300/kernel/asm-offsets.c | 2 +- arch/h8300/kernel/ints.c | 2 +- arch/h8300/kernel/time.c | 23 +- arch/h8300/lib/checksum.c | 1 + arch/h8300/platform/h8s/ints.c | 2 +- arch/i386/Kconfig | 68 + arch/i386/Kconfig.debug | 80 + arch/i386/Kconfig.debug~ | 71 + arch/i386/boot/.cvsignore | 4 + arch/i386/boot/compressed/.cvsignore | 3 + arch/i386/boot/compressed/misc.c | 3 + arch/i386/boot/tools/.cvsignore | 1 + arch/i386/defconfig | 2 +- arch/i386/kernel/.cvsignore | 2 + arch/i386/kernel/Makefile | 1 + arch/i386/kernel/apic.c | 30 + arch/i386/kernel/asm-offsets.c | 2 +- arch/i386/kernel/cpu/centaur.c | 27 +- arch/i386/kernel/cpu/cpufreq/Kconfig | 10 + arch/i386/kernel/cpu/cpufreq/acpi.c | 103 +- arch/i386/kernel/cpu/intel_cacheinfo.c | 140 + arch/i386/kernel/cpu/mtrr/mtrr.h | 1 - arch/i386/kernel/entry.S | 49 +- arch/i386/kernel/i386_ksyms.c | 6 + arch/i386/kernel/i8259.c | 12 + arch/i386/kernel/init_task.c | 7 + arch/i386/kernel/io_apic.c | 31 + arch/i386/kernel/ioport.c | 36 +- arch/i386/kernel/irq.c | 14 +- arch/i386/kernel/kprobes.c | 349 + arch/i386/kernel/machine_kexec.c | 208 + arch/i386/kernel/pci-dma.c | 111 +- arch/i386/kernel/process.c | 26 + arch/i386/kernel/reboot.c | 81 +- arch/i386/kernel/relocate_kernel.S | 118 + arch/i386/kernel/time.c | 17 +- arch/i386/kernel/timers/timer_pit.c | 44 + arch/i386/kernel/vsyscall.lds.S | 65 + arch/i386/lib/mmx.c | 2 +- arch/i386/mach-es7000/es7000plat.c | 83 +- arch/i386/mm/mmap.c | 6 +- arch/i386/oprofile/op_model_athlon.c | 4 +- arch/i386/oprofile/op_model_ppro.c | 4 +- arch/i386/pci/common.c | 2 +- arch/i386/pci/i386.c | 10 +- arch/i386/pci/mmconfig.c | 9 +- arch/i386/pci/numa.c | 5 +- arch/i386/pci/pcbios.c | 2 +- arch/i386/power/Makefile | 1 - arch/ia64/Kconfig.debug | 64 + arch/ia64/configs/bigsur_defconfig | 1132 + arch/ia64/configs/generic_defconfig | 29 +- arch/ia64/configs/tiger_defconfig | 1028 + arch/ia64/configs/zx1_defconfig | 662 +- arch/ia64/hp/common/sba_iommu.c | 86 +- arch/ia64/hp/sim/simserial.c | 8 +- arch/ia64/ia32/elfcore32.h | 14 +- arch/ia64/ia32/ia32_ldt.c | 25 +- arch/ia64/kernel/acpi-ext.c | 2 +- arch/ia64/kernel/asm-offsets.c | 29 +- arch/ia64/kernel/cyclone.c | 61 +- arch/ia64/kernel/mca_drv.c | 639 + arch/ia64/kernel/mca_drv.h | 113 + arch/ia64/kernel/mca_drv_asm.S | 45 + arch/ia64/kernel/minstate.h | 6 +- arch/ia64/kernel/patch.c | 26 +- arch/ia64/kernel/salinfo.c | 13 +- arch/ia64/kernel/sigframe.h | 2 +- arch/ia64/kernel/sys_ia64.c | 4 +- arch/ia64/kernel/unaligned.c | 18 +- arch/ia64/kernel/vmlinux.lds.S | 7 +- arch/ia64/lib/csum_partial_copy.c | 12 +- arch/ia64/lib/io.c | 22 +- arch/ia64/lib/swiotlb.c | 112 +- arch/ia64/mm/extable.c | 2 +- arch/ia64/oprofile/Kconfig | 4 + arch/ia64/oprofile/Makefile | 1 + arch/ia64/oprofile/init.c | 9 +- arch/ia64/oprofile/perfmon.c | 105 + arch/ia64/sn/include/ioerror.h | 81 + arch/ia64/sn/include/pci/pcibr_provider.h | 149 + .../sn/include/pci/pcibus_provider_defs.h | 43 + arch/ia64/sn/include/pci/pcidev.h | 54 + arch/ia64/sn/include/pci/pic.h | 261 + arch/ia64/sn/include/pci/tiocp.h | 256 + arch/ia64/sn/include/shub.h | 39 + arch/ia64/sn/include/shubio.h | 3476 + arch/ia64/sn/include/tio.h | 37 + arch/ia64/sn/include/xtalk/hubdev.h | 67 + arch/ia64/sn/include/xtalk/xbow.h | 291 + arch/ia64/sn/include/xtalk/xwidgetdev.h | 70 + arch/ia64/sn/kernel/bte_error.c | 188 + arch/ia64/sn/kernel/huberror.c | 201 + arch/ia64/sn/kernel/io_init.c | 410 + arch/ia64/sn/kernel/iomv.c | 75 + arch/ia64/sn/kernel/klconflib.c | 108 + arch/ia64/sn/kernel/sn2/Makefile | 2 +- arch/ia64/sn/kernel/sn2/sn_hwperf.c | 652 + arch/ia64/sn/kernel/sn2/sn_proc_fs.c | 182 +- arch/ia64/sn/kernel/sn2/timer.c | 49 +- arch/ia64/sn/pci/Makefile | 10 + arch/ia64/sn/pci/pci_dma.c | 477 + arch/ia64/sn/pci/pcibr/Makefile | 11 + arch/ia64/sn/pci/pcibr/pcibr_ate.c | 188 + arch/ia64/sn/pci/pcibr/pcibr_dma.c | 379 + arch/ia64/sn/pci/pcibr/pcibr_provider.c | 170 + arch/ia64/sn/pci/pcibr/pcibr_reg.c | 282 + arch/m32r/Kconfig | 441 + arch/m32r/Makefile | 55 + arch/m32r/boot/Makefile | 19 + arch/m32r/boot/compressed/Makefile | 38 + arch/m32r/boot/compressed/boot.h | 59 + arch/m32r/boot/compressed/head.S | 115 + arch/m32r/boot/compressed/install.sh | 57 + arch/m32r/boot/compressed/m32r_sio.c | 53 + arch/m32r/boot/compressed/misc.c | 223 + arch/m32r/boot/compressed/vmlinux.lds.S | 23 + arch/m32r/boot/compressed/vmlinux.scr | 9 + arch/m32r/boot/setup.S | 162 + arch/m32r/defconfig | 635 + arch/m32r/kernel/Makefile | 20 + arch/m32r/kernel/align.c | 585 + arch/m32r/kernel/entry.S | 999 + arch/m32r/kernel/head.S | 287 + arch/m32r/kernel/init_task.c | 41 + arch/m32r/kernel/io_m32700ut.c | 455 + arch/m32r/kernel/io_mappi.c | 368 + arch/m32r/kernel/io_mappi2.c | 370 + arch/m32r/kernel/io_oaks32r.c | 243 + arch/m32r/kernel/io_opsput.c | 377 + arch/m32r/kernel/io_usrv.c | 247 + arch/m32r/kernel/irq.c | 1020 + arch/m32r/kernel/m32r_ksyms.c | 141 + arch/m32r/kernel/module.c | 253 + arch/m32r/kernel/process.c | 356 + arch/m32r/kernel/ptrace.c | 858 + arch/m32r/kernel/semaphore.c | 186 + arch/m32r/kernel/setup.c | 403 + arch/m32r/kernel/setup_m32700ut.c | 478 + arch/m32r/kernel/setup_mappi.c | 160 + arch/m32r/kernel/setup_mappi2.c | 212 + arch/m32r/kernel/setup_oaks32r.c | 143 + arch/m32r/kernel/setup_opsput.c | 482 + arch/m32r/kernel/setup_usrv.c | 256 + arch/m32r/kernel/signal.c | 621 + arch/m32r/kernel/smp.c | 965 + arch/m32r/kernel/smpboot.c | 635 + arch/m32r/kernel/sys_m32r.c | 217 + arch/m32r/kernel/time.c | 317 + arch/m32r/kernel/traps.c | 330 + arch/m32r/kernel/vmlinux.lds.S | 143 + arch/m32r/lib/Makefile | 7 + arch/m32r/lib/ashxdi3.S | 297 + arch/m32r/lib/checksum.S | 322 + arch/m32r/lib/csum_partial_copy.c | 58 + arch/m32r/lib/delay.c | 126 + arch/m32r/lib/getuser.S | 88 + arch/m32r/lib/memcpy.S | 95 + arch/m32r/lib/memset.S | 178 + arch/m32r/lib/putuser.S | 84 + arch/m32r/lib/strlen.S | 120 + arch/m32r/lib/usercopy.c | 391 + arch/m32r/m32700ut/defconfig.m32700ut.smp | 662 + arch/m32r/m32700ut/defconfig.m32700ut.up | 659 + arch/m32r/m32700ut/dot.gdbinit_200MHz_16MB | 249 + arch/m32r/m32700ut/dot.gdbinit_300MHz_32MB | 249 + arch/m32r/mappi/defconfig.nommu | 529 + arch/m32r/mappi/defconfig.smp | 646 + arch/m32r/mappi/defconfig.up | 642 + arch/m32r/mappi/dot.gdbinit | 242 + arch/m32r/mappi/dot.gdbinit.nommu | 245 + arch/m32r/mappi/dot.gdbinit.smp | 344 + arch/m32r/mm/Makefile | 12 + arch/m32r/mm/cache.c | 68 + arch/m32r/mm/discontig.c | 170 + arch/m32r/mm/extable.c | 22 + arch/m32r/mm/fault-nommu.c | 164 + arch/m32r/mm/fault.c | 572 + arch/m32r/mm/init.c | 250 + arch/m32r/mm/ioremap-nommu.c | 52 + arch/m32r/mm/ioremap.c | 192 + arch/m32r/mm/mmu.S | 350 + arch/m32r/mm/page.S | 82 + arch/m32r/oaks32r/defconfig.nommu | 521 + arch/m32r/oaks32r/dot.gdbinit.nommu | 155 + arch/m32r/oprofile/Kconfig | 23 + arch/m32r/oprofile/Makefile | 9 + arch/m32r/oprofile/init.c | 25 + arch/m32r/opsput/defconfig.opsput | 598 + arch/m32r/opsput/dot.gdbinit | 180 + arch/m68k/Kconfig.debug | 5 + arch/m68k/kernel/Makefile | 2 +- arch/m68k/kernel/m68k_ksyms.c | 25 +- arch/m68k/kernel/time.c | 22 +- arch/m68knommu/Kconfig.debug | 42 + arch/m68knommu/kernel/Makefile | 2 +- arch/m68knommu/kernel/asm-offsets.c | 2 +- arch/m68knommu/kernel/signal.c | 8 +- arch/m68knommu/kernel/time.c | 23 +- arch/m68knommu/kernel/vmlinux.lds.S | 3 - arch/m68knommu/platform/5307/timers.c | 13 +- arch/mips/Kconfig.debug | 76 + arch/mips/au1000/common/time.c | 2 +- arch/mips/kernel/vmlinux.lds.S | 4 +- arch/mips/mips-boards/generic/time.c | 1 - arch/mips/mm/fault.c | 1 - arch/mips/pci/fixup-atlas.c | 10 +- arch/mips/pci/fixup-cobalt.c | 12 +- arch/mips/pci/fixup-ddb5074.c | 7 +- arch/mips/pci/fixup-ddb5477.c | 16 +- arch/mips/pci/fixup-ip32.c | 4 - arch/mips/pci/fixup-jaguar.c | 4 - arch/mips/pci/fixup-lasat.c | 4 - arch/mips/pci/fixup-malta.c | 11 +- arch/mips/pci/fixup-mpc30x.c | 4 - arch/mips/pci/fixup-ocelot-c.c | 4 - arch/mips/pci/fixup-ocelot-g.c | 4 - arch/mips/pci/fixup-sni.c | 4 - arch/mips/pci/fixup-tb0219.c | 4 - arch/mips/pci/pci-ip27.c | 21 +- arch/mips/pci/pci-sb1250.c | 4 - arch/mips/pmc-sierra/yosemite/ht.c | 5 - arch/mips/sgi-ip27/ip27-memory.c | 2 +- arch/parisc/Kconfig.debug | 14 + arch/parisc/kernel/pci.c | 9 - arch/parisc/kernel/time.c | 40 +- arch/parisc/lib/debuglocks.c | 2 +- arch/ppc/Kconfig.debug | 84 + arch/ppc/amiga/config.c | 3 - arch/ppc/boot/common/ns16550.c | 6 +- arch/ppc/boot/common/serial_stub.c | 5 - arch/ppc/boot/common/util.S | 16 +- arch/ppc/boot/include/nonstdio.h | 2 + arch/ppc/boot/include/serial.h | 46 + arch/ppc/boot/lib/Makefile | 4 +- arch/ppc/boot/openfirmware/coffmain.c | 1 - arch/ppc/boot/openfirmware/common.c | 106 +- arch/ppc/boot/openfirmware/misc.S | 2 +- arch/ppc/boot/simple/chrpmap.c | 12 + arch/ppc/boot/simple/misc-embedded.c | 9 +- arch/ppc/boot/simple/misc-prep.c | 1 + arch/ppc/boot/simple/misc-spruce.c | 3 - arch/ppc/boot/simple/pibs.c | 101 + arch/ppc/boot/simple/prepmap.c | 12 + arch/ppc/boot/simple/relocate.S | 6 +- arch/ppc/boot/utils/mkbugboot.c | 12 +- arch/ppc/boot/utils/mktree.c | 4 + arch/ppc/configs/mvme5100_defconfig | 448 +- arch/ppc/kernel/cpu_setup_6xx.S | 42 +- arch/ppc/kernel/cpu_setup_power4.S | 8 +- arch/ppc/kernel/dma-mapping.c | 4 +- arch/ppc/kernel/head_booke.h | 240 + arch/ppc/kernel/head_e500.S | 236 +- arch/ppc/kernel/idle.c | 34 + arch/ppc/kernel/idle_6xx.S | 6 +- arch/ppc/kernel/idle_power4.S | 2 +- arch/ppc/kernel/machine_kexec.c | 132 + arch/ppc/kernel/pci.c | 63 +- arch/ppc/kernel/ppc_htab.c | 33 +- arch/ppc/kernel/relocate_kernel.S | 127 + arch/ppc/kernel/vector.S | 4 +- arch/ppc/lib/checksum.S | 12 +- arch/ppc/lib/rheap.c | 8 +- arch/ppc/math-emu/op-common.h | 1 - arch/ppc/mm/44x_mmu.c | 2 +- arch/ppc/mm/4xx_mmu.c | 5 + arch/ppc/platforms/85xx/mpc8540_ads.c | 4 - arch/ppc/platforms/85xx/mpc85xx_cds_common.c | 6 +- arch/ppc/platforms/85xx/sbc8560.c | 3 - arch/ppc/platforms/chrp_setup.c | 9 +- arch/ppc/platforms/chrp_smp.c | 1 - arch/ppc/platforms/k2.c | 4 - arch/ppc/platforms/lite5200.c | 55 +- arch/ppc/platforms/lopec.c | 410 + arch/ppc/platforms/lopec.h | 39 + arch/ppc/platforms/mcpn765.c | 6 - arch/ppc/platforms/mcpn765.h | 50 +- arch/ppc/platforms/mpc5200.c | 24 + arch/ppc/platforms/mvme5100.c | 349 + arch/ppc/platforms/mvme5100.h | 25 +- arch/ppc/platforms/pcore.c | 4 - arch/ppc/platforms/pmac_pci.c | 23 +- arch/ppc/platforms/pmac_smp.c | 2 +- arch/ppc/platforms/pmac_time.c | 2 +- arch/ppc/platforms/powerpmc250.h | 18 +- arch/ppc/platforms/pq2ads.c | 26 + arch/ppc/platforms/pq2ads.h | 4 + arch/ppc/platforms/prep_pci.c | 176 +- arch/ppc/platforms/prpmc750.c | 4 - arch/ppc/platforms/prpmc750.h | 26 +- arch/ppc/platforms/prpmc800.c | 4 - arch/ppc/platforms/prpmc800.h | 28 +- arch/ppc/platforms/residual.c | 133 +- arch/ppc/platforms/rpx8260.h | 7 + arch/ppc/platforms/spruce.c | 5 - arch/ppc/syslib/cpm2_pic.c | 25 +- arch/ppc/syslib/cpm2_pic.h | 8 +- arch/ppc/syslib/hawk_common.c | 32 + arch/ppc/syslib/ibm44x_common.c | 165 + arch/ppc/syslib/ibm44x_common.h | 8 +- arch/ppc/syslib/m8260_pci.h | 1 + arch/ppc/syslib/m8xx_wdt.c | 99 + arch/ppc/syslib/m8xx_wdt.h | 16 + arch/ppc/syslib/mpc52xx_pic.c | 22 +- arch/ppc/syslib/mpc52xx_setup.c | 41 +- arch/ppc/syslib/open_pic2.c | 2 +- arch/ppc/syslib/ppc4xx_setup.c | 4 - arch/ppc/syslib/ppc85xx_setup.c | 4 +- arch/ppc/syslib/todc_time.c | 8 +- arch/ppc64/Kconfig.debug | 59 + arch/ppc64/boot/Makefile | 39 +- arch/ppc64/boot/main.c | 170 +- arch/ppc64/boot/zImage.lds | 12 +- arch/ppc64/configs/g5_defconfig | 197 +- arch/ppc64/configs/pSeries_defconfig | 263 +- arch/ppc64/kernel/btext.c | 231 +- arch/ppc64/kernel/hvconsole.c | 66 +- arch/ppc64/kernel/hvcserver.c | 86 +- arch/ppc64/kernel/iSeries_htab.c | 2 + arch/ppc64/kernel/iSeries_pci.c | 45 +- arch/ppc64/kernel/iSeries_pci_reset.c | 3 +- arch/ppc64/kernel/iSeries_setup.h | 4 - arch/ppc64/kernel/idle_power4.S | 2 +- arch/ppc64/kernel/iomap.c | 119 + arch/ppc64/kernel/lmb.c | 63 +- arch/ppc64/kernel/nvram.c | 4 +- arch/ppc64/kernel/open_pic_u3.c | 2 +- arch/ppc64/kernel/pSeries_iommu.c | 200 +- arch/ppc64/kernel/pSeries_lpar.c | 266 +- arch/ppc64/kernel/pSeries_setup.c | 579 + arch/ppc64/kernel/pci.c | 377 +- arch/ppc64/kernel/pci_dn.c | 4 +- arch/ppc64/kernel/pci_iommu.c | 4 +- arch/ppc64/kernel/pmac_feature.c | 11 +- arch/ppc64/kernel/pmac_low_i2c.c | 2 +- arch/ppc64/kernel/pmac_pci.c | 26 +- arch/ppc64/kernel/pmac_setup.c | 267 +- arch/ppc64/kernel/pmac_time.c | 2 +- arch/ppc64/kernel/prom_init.c | 1714 + arch/ppc64/kernel/u3_iommu.c | 321 + arch/ppc64/kernel/vector.S | 4 +- arch/ppc64/kernel/vmlinux.lds.S | 12 +- arch/ppc64/lib/checksum.S | 14 +- arch/ppc64/lib/locks.c | 226 +- arch/ppc64/mm/hash_low.S | 16 +- arch/ppc64/mm/hash_native.c | 419 + arch/ppc64/mm/imalloc.c | 77 +- arch/ppc64/mm/mmap.c | 9 +- arch/ppc64/mm/slb.c | 85 +- arch/ppc64/mm/slb_low.S | 99 +- arch/ppc64/mm/stab.c | 240 + arch/ppc64/oprofile/op_impl.h | 15 +- arch/ppc64/oprofile/op_model_rs64.c | 6 +- arch/s390/Kconfig.debug | 5 + arch/s390/appldata/appldata_mem.c | 8 +- arch/s390/kernel/asm-offsets.c | 4 + arch/s390/kernel/ebcdic.c | 12 +- arch/s390/kernel/irq.c | 99 + arch/s390/kernel/profile.c | 41 +- arch/s390/kernel/s390_ext.c | 1 + arch/s390/kernel/vmlinux.lds.S | 4 +- arch/s390/kernel/vtime.c | 2 +- arch/s390/lib/string.c | 34 +- arch/sh/Kconfig.debug | 124 + arch/sh/boards/mpc1211/pci.c | 9 +- arch/sh/boards/overdrive/galileo.c | 8 +- arch/sh/drivers/pci/fixups-dreamcast.c | 6 +- arch/sh/drivers/pci/pci-st40.c | 8 +- arch/sh/kernel/signal.c | 8 +- arch/sh/mm/fault-nommu.c | 1 - arch/sh/mm/fault.c | 1 - arch/sh/mm/tlb-sh4.c | 1 - arch/sh64/Kconfig | 50 +- arch/sh64/Kconfig.debug | 37 + arch/sh64/boot/compressed/Makefile | 6 +- arch/sh64/kernel/irq.c | 6 +- arch/sh64/kernel/pci_sh5.c | 7 +- arch/sh64/kernel/process.c | 4 +- arch/sh64/kernel/ptrace.c | 9 +- arch/sh64/kernel/sh_ksyms.c | 29 +- arch/sh64/kernel/signal.c | 8 +- arch/sh64/kernel/time.c | 34 +- arch/sh64/kernel/vmlinux.lds.S | 4 +- arch/sh64/mm/fault.c | 1 - arch/sh64/mm/init.c | 4 +- arch/sh64/mm/tlbmiss.c | 1 - arch/sparc/Kconfig.debug | 14 + arch/sparc/boot/Makefile | 42 +- arch/sparc/kernel/Makefile | 2 +- arch/sparc/kernel/entry.S | 2 +- arch/sparc/kernel/pcic.c | 4 - arch/sparc/kernel/sun4d_smp.c | 18 +- arch/sparc/kernel/vmlinux.lds.S | 4 +- arch/sparc/mm/srmmu.c | 2 +- arch/sparc64/Kconfig.debug | 54 + arch/sparc64/boot/Makefile | 2 +- arch/sparc64/kernel/Makefile | 3 +- arch/sparc64/kernel/binfmt_elf32.c | 4 +- arch/sparc64/kernel/chmc.c | 2 +- arch/sparc64/kernel/etrap.S | 301 +- arch/sparc64/kernel/kprobes.c | 373 + arch/sparc64/kernel/pci_common.c | 122 +- arch/sparc64/kernel/pci_impl.h | 1 - arch/sparc64/kernel/pci_iommu.c | 60 +- arch/sparc64/kernel/pci_sabre.c | 25 +- arch/sparc64/kernel/ttable.S | 2 +- arch/sparc64/kernel/vmlinux.lds.S | 4 +- arch/sparc64/lib/PeeCeeI.c | 12 +- arch/sparc64/lib/U1copy_from_user.S | 33 + arch/sparc64/lib/U1copy_to_user.S | 33 + arch/sparc64/lib/U1memcpy.S | 555 + arch/sparc64/lib/U3copy_from_user.S | 424 +- arch/sparc64/lib/U3copy_to_user.S | 424 +- arch/sparc64/lib/U3memcpy.S | 331 +- arch/sparc64/lib/U3patch.S | 32 + arch/sparc64/lib/atomic.S | 9 +- arch/sparc64/lib/bitops.S | 44 +- arch/sparc64/lib/copy_in_user.S | 119 + arch/sparc64/lib/copy_page.S | 3 + arch/sparc64/lib/delay.c | 49 + arch/sparc64/lib/iomap.c | 48 + arch/sparc64/lib/ipcsum.S | 6 +- arch/sparc64/lib/memmove.S | 33 + arch/sparc64/lib/strlen.S | 6 +- arch/sparc64/lib/strncmp.S | 7 +- arch/sparc64/lib/strncpy_from_user.S | 4 +- arch/sparc64/lib/user_fixup.c | 68 + arch/sparc64/lib/xor.S | 28 +- arch/sparc64/solaris/misc.c | 69 +- arch/um/Kconfig.debug | 43 + arch/um/Makefile-tt | 3 +- arch/um/kernel/dyn.lds.S | 171 + arch/um/kernel/main.c | 249 + arch/um/kernel/signal_user.c | 11 + arch/um/kernel/skas/tlb.c | 2 +- arch/um/kernel/uml.lds.S | 96 + arch/um/sys-i386/syscalls.c | 22 + arch/v850/Kconfig.debug | 10 + arch/v850/kernel/Makefile | 2 +- arch/v850/kernel/asm-consts.c | 2 +- arch/v850/kernel/rte_mb_a_pci.c | 2 - arch/v850/kernel/signal.c | 8 +- arch/v850/kernel/v850_ksyms.c | 53 +- arch/x86_64/Kconfig.debug | 59 + arch/x86_64/boot/Makefile | 2 +- arch/x86_64/boot/video.S | 86 +- arch/x86_64/kernel/aperture.c | 23 +- arch/x86_64/kernel/apic.c | 62 +- arch/x86_64/kernel/asm-offsets.c | 2 +- arch/x86_64/kernel/cpufreq/Kconfig | 54 + arch/x86_64/kernel/cpufreq/Makefile | 8 +- arch/x86_64/kernel/early_printk.c | 19 +- arch/x86_64/kernel/entry.S | 19 +- arch/x86_64/kernel/ioport.c | 40 +- arch/x86_64/kernel/machine_kexec.c | 246 + arch/x86_64/kernel/nmi.c | 3 +- arch/x86_64/kernel/pci-dma.c | 21 +- arch/x86_64/kernel/pci-nommu.c | 62 +- arch/x86_64/kernel/relocate_kernel.S | 141 + arch/x86_64/kernel/suspend_asm.S | 82 +- arch/x86_64/lib/bitops.c | 140 + arch/x86_64/lib/bitstr.c | 3 + arch/x86_64/lib/memmove.c | 16 +- arch/x86_64/mm/k8topology.c | 4 +- arch/x86_64/mm/mmap.c | 5 +- arch/x86_64/pci/Makefile | 4 +- arch/x86_64/pci/Makefile-BUS | 22 + arch/x86_64/pci/k8-bus.c | 74 + configs/kernel-2.6.8-i686-planetlab.config | 16 +- configs/kernel-2.6.9-i586-smp.config | 2571 + configs/kernel-2.6.9-i586.config | 2588 + configs/kernel-2.6.9-i686-smp.config | 2574 + configs/kernel-2.6.9-i686.config | 2590 + crypto/aes.c | 14 +- crypto/arc4.c | 4 +- crypto/blowfish.c | 6 +- crypto/internal.h | 1 - crypto/scatterwalk.c | 2 +- crypto/signature/key.h | 4 +- crypto/signature/ksign-keyring.c | 4 +- crypto/signature/ksign-parse.c | 2 +- crypto/signature/ksign-publickey.c | 2 +- crypto/tea.c | 2 +- crypto/wp512.c | 1208 + drivers/acpi/Makefile | 2 +- drivers/acpi/blacklist.c | 58 +- drivers/acpi/dispatcher/dsmethod.c | 70 +- drivers/acpi/dispatcher/dsmthdat.c | 8 +- drivers/acpi/dispatcher/dsutils.c | 53 +- drivers/acpi/dispatcher/dswload.c | 22 + drivers/acpi/dispatcher/dswstate.c | 18 +- drivers/acpi/events/evevent.c | 12 +- drivers/acpi/events/evgpe.c | 397 +- drivers/acpi/events/evgpeblk.c | 181 +- drivers/acpi/events/evmisc.c | 45 +- drivers/acpi/events/evregion.c | 388 +- drivers/acpi/events/evrgnini.c | 12 +- drivers/acpi/events/evxfevnt.c | 173 +- drivers/acpi/events/evxfregn.c | 203 +- drivers/acpi/executer/exconfig.c | 47 +- drivers/acpi/executer/exfldio.c | 200 +- drivers/acpi/executer/exmisc.c | 149 +- drivers/acpi/executer/exmutex.c | 46 +- drivers/acpi/executer/exoparg2.c | 48 +- drivers/acpi/executer/exresolv.c | 8 +- drivers/acpi/executer/exstore.c | 17 +- drivers/acpi/hardware/hwgpe.c | 341 +- drivers/acpi/hardware/hwregs.c | 104 +- drivers/acpi/hardware/hwsleep.c | 57 +- drivers/acpi/hardware/hwtimer.c | 28 +- drivers/acpi/motherboard.c | 177 + drivers/acpi/namespace/nsaccess.c | 2 +- drivers/acpi/namespace/nseval.c | 90 +- drivers/acpi/namespace/nsinit.c | 42 +- drivers/acpi/namespace/nsparse.c | 5 +- drivers/acpi/namespace/nsxfeval.c | 52 +- drivers/acpi/namespace/nsxfname.c | 4 +- drivers/acpi/numa.c | 16 +- drivers/acpi/parser/psopcode.c | 8 +- drivers/acpi/parser/psxface.c | 51 +- drivers/acpi/resources/rsutils.c | 7 +- drivers/acpi/resources/rsxface.c | 3 +- drivers/acpi/sleep/Makefile | 2 +- drivers/acpi/sleep/proc.c | 86 + drivers/acpi/sleep/sleep.h | 3 + drivers/acpi/sleep/wakeup.c | 181 + drivers/acpi/tables/tbxfroot.c | 205 +- drivers/acpi/utilities/utalloc.c | 13 +- drivers/acpi/utilities/uteval.c | 18 +- drivers/acpi/utilities/utglobal.c | 88 +- drivers/acpi/utilities/utxface.c | 41 +- drivers/atm/Makefile | 2 +- drivers/atm/eni.c | 73 +- drivers/atm/eni.h | 20 +- drivers/atm/firestream.h | 2 +- drivers/atm/he.h | 2 +- drivers/atm/idt77252.h | 4 +- drivers/atm/nicstar.c | 5 +- drivers/atm/nicstar.h | 2 +- drivers/atm/nicstarmac.c | 2 +- drivers/atm/nicstarmac.h | 2 +- drivers/block/DAC960.h | 188 +- drivers/block/amiflop.c | 16 +- drivers/block/cciss.h | 6 +- drivers/block/cfq-iosched-orig.c | 706 - drivers/block/cfq-iosched.c | 408 +- drivers/block/ckrm-io.c | 280 +- drivers/block/ckrm-iostub.c | 18 +- drivers/block/cpqarray.h | 2 +- drivers/block/nbd.c | 39 +- drivers/block/sx8.c | 98 +- drivers/block/ub.c | 2097 + drivers/cdrom/cdu31a.c | 6 +- drivers/cdrom/mcd.c | 7 +- drivers/char/.cvsignore | 2 + drivers/char/agp/agp.h | 2 +- drivers/char/agp/hp-agp.c | 16 +- drivers/char/amiserial.c | 22 +- drivers/char/drm/Makefile | 2 + drivers/char/drm/drm_core.h | 40 + drivers/char/drm/drm_irq.h | 81 +- drivers/char/drm/drm_memory.h | 38 +- drivers/char/drm/drm_pciids.h | 8 + drivers/char/drm/drm_scatter.h | 6 + drivers/char/drm/ffb_context.c | 30 +- drivers/char/drm/ffb_drv.h | 10 + drivers/char/drm/gamma_context.h | 4 +- drivers/char/drm/gamma_drv.h | 5 +- drivers/char/drm/i810_drv.c | 17 +- drivers/char/drm/i810_drv.h | 1 - drivers/char/drm/i830_drv.h | 5 +- drivers/char/drm/i915.h | 53 + drivers/char/drm/i915_dma.c | 755 + drivers/char/drm/i915_drm.h | 154 + drivers/char/drm/i915_drv.c | 17 + drivers/char/drm/i915_drv.h | 219 + drivers/char/drm/i915_irq.c | 165 + drivers/char/drm/i915_mem.c | 347 + drivers/char/drm/sis.h | 17 - drivers/char/drm/sis_drv.c | 16 +- drivers/char/drm/sis_drv.h | 2 - drivers/char/ds1620.c | 3 +- drivers/char/dsp56k.c | 14 +- drivers/char/ec3104_keyb.c | 2 +- drivers/char/efirtc.c | 19 +- drivers/char/epca.c | 29 +- drivers/char/ftape/lowlevel/ftape-calibr.c | 71 +- drivers/char/generic_serial.c | 14 +- drivers/char/hpet.c | 100 +- drivers/char/hvc_console.c | 754 +- drivers/char/hvcs.c | 443 +- drivers/char/hvsi.c | 1319 + drivers/char/ip2/ip2types.h | 3 + drivers/char/ipmi/Kconfig | 7 + drivers/char/ipmi/Makefile | 1 + drivers/char/ipmi/ipmi_poweroff.c | 543 + drivers/char/ipmi/ipmi_si_sm.h | 3 + drivers/char/isicom.c | 32 +- drivers/char/mmtimer.c | 216 + drivers/char/moxa.c | 16 +- drivers/char/nwflash.c | 19 +- drivers/char/pcxx.c | 58 +- drivers/char/rio/linux_compat.h | 2 +- drivers/char/rio/rio_linux.c | 6 +- drivers/char/riscom8.c | 32 +- drivers/char/rocket_int.h | 4 - drivers/char/ser_a2232.c | 5 +- drivers/char/serial167.c | 40 +- drivers/char/serial_tx3912.c | 5 +- drivers/char/snsc.c | 456 + drivers/char/snsc.h | 50 + drivers/char/specialix.c | 29 +- drivers/char/sx.c | 5 +- drivers/char/synclink.c | 647 +- drivers/char/viocons.c | 5 +- drivers/char/vme_scc.c | 8 +- drivers/char/watchdog/ixp4xx_wdt.c | 1 + drivers/char/watchdog/mpc8xx_wdt.c | 164 + drivers/char/watchdog/pcwd.c | 3 +- drivers/char/watchdog/wdt285.c | 1 + drivers/char/watchdog/wdt977.c | 1 + drivers/cpufreq/Kconfig | 15 + drivers/cpufreq/Makefile | 1 + drivers/cpufreq/cpufreq_ondemand.c | 437 + drivers/cpufreq/proc_intf.c | 17 + drivers/fc4/soc.h | 20 +- drivers/fc4/socal.c | 4 +- drivers/fc4/socal.h | 6 +- drivers/i2c/Makefile | 3 + drivers/i2c/algos/Kconfig | 11 + drivers/i2c/algos/Makefile | 1 + drivers/i2c/algos/i2c-algo-ite.c | 14 +- drivers/i2c/algos/i2c-algo-pca.c | 395 + drivers/i2c/algos/i2c-algo-pca.h | 26 + drivers/i2c/algos/i2c-algo-pcf.c | 12 +- drivers/i2c/busses/i2c-hydra.c | 2 +- drivers/i2c/busses/i2c-i810.c | 2 +- drivers/i2c/busses/i2c-ite.c | 24 +- drivers/i2c/busses/i2c-ixp2000.c | 171 + drivers/i2c/busses/i2c-mpc.c | 392 + drivers/i2c/busses/i2c-pca-isa.c | 184 + drivers/i2c/busses/i2c-prosavage.c | 2 +- drivers/i2c/busses/i2c-savage4.c | 2 +- drivers/i2c/chips/adm1025.c | 2 +- drivers/i2c/chips/ds1621.c | 2 +- drivers/i2c/chips/isp1301_omap.c | 1660 + drivers/i2c/chips/pcf8591.c | 2 +- drivers/i2c/chips/rtc8564.c | 4 +- drivers/i2c/chips/smsc47m1.c | 579 + drivers/i2c/i2c-sensor-detect.c | 169 + drivers/i2c/i2c-sensor-vid.c | 99 + drivers/ide/arm/Makefile | 1 + drivers/ide/arm/bast-ide.c | 71 + drivers/ide/arm/icside.c | 5 - drivers/ide/ide-lib.c | 4 - drivers/ide/legacy/Makefile | 1 - drivers/ide/legacy/qd65xx.h | 6 +- drivers/ide/pci/alim15x3.c | 6 +- drivers/ide/pci/cmd640.c | 6 +- drivers/ide/pci/cy82c693.c | 2 +- drivers/ide/pci/delkin_cb.c | 140 + drivers/ide/pci/hpt34x.c | 70 +- drivers/ide/pci/it8212.c | 851 + drivers/ide/ppc/pmac.c | 83 +- drivers/ieee1394/eth1394.h | 7 + drivers/ieee1394/ohci1394.h | 2 +- drivers/input/gameport/cs461x.c | 2 +- drivers/input/gameport/emu10k1-gp.c | 5 +- drivers/input/gameport/fm801-gp.c | 2 +- drivers/input/joystick/Kconfig | 4 +- drivers/input/joystick/gamecon.c | 194 +- drivers/input/joystick/iforce/iforce-main.c | 4 +- drivers/input/joystick/iforce/iforce-serio.c | 18 +- drivers/input/joystick/iforce/iforce.h | 2 +- drivers/input/joystick/magellan.c | 24 +- drivers/input/joystick/spaceball.c | 24 +- drivers/input/joystick/spaceorb.c | 24 +- drivers/input/joystick/stinger.c | 24 +- drivers/input/joystick/tmdc.c | 2 +- drivers/input/joystick/twidjoy.c | 20 +- drivers/input/joystick/warrior.c | 24 +- drivers/input/keyboard/lkkbd.c | 24 +- drivers/input/keyboard/newtonkbd.c | 24 +- drivers/input/keyboard/sunkbd.c | 24 +- drivers/input/keyboard/xtkbd.c | 24 +- drivers/input/mouse/logips2pp.c | 2 +- drivers/input/mouse/pc110pad.c | 6 +- drivers/input/mouse/psmouse-base.c | 312 +- drivers/input/mouse/psmouse.h | 40 +- drivers/input/mouse/sermouse.c | 24 +- drivers/input/mouse/synaptics.c | 54 +- drivers/input/mouse/vsxxxaa.c | 24 +- drivers/input/serio/Makefile | 1 + drivers/input/serio/ct82c710.c | 104 +- drivers/input/serio/gscps2.c | 64 +- drivers/input/serio/i8042-sparcio.h | 8 +- drivers/input/serio/i8042.h | 7 + drivers/input/serio/maceps2.c | 86 +- drivers/input/serio/parkbd.c | 48 +- drivers/input/serio/pcips2.c | 54 +- drivers/input/serio/q40kbd.c | 117 +- drivers/input/serio/rpckbd.c | 48 +- drivers/input/serio/serio.c | 576 +- drivers/input/serio/serio_raw.c | 390 + drivers/input/touchscreen/gunze.c | 24 +- drivers/input/touchscreen/h3600_ts_input.c | 24 +- drivers/isdn/hardware/avm/b1.c | 8 +- drivers/isdn/hardware/avm/b1dma.c | 2 - drivers/isdn/hardware/avm/c4.c | 2 - drivers/isdn/hardware/avm/t1isa.c | 7 +- drivers/isdn/hardware/eicon/capifunc.h | 4 +- drivers/isdn/hardware/eicon/debug_if.h | 6 + drivers/isdn/hardware/eicon/debuglib.h | 6 +- drivers/isdn/hardware/eicon/diva_didd.c | 10 +- drivers/isdn/hardware/eicon/divasync.h | 21 +- drivers/isdn/hardware/eicon/io.c | 50 +- drivers/isdn/hardware/eicon/io.h | 63 +- drivers/isdn/hardware/eicon/maintidi.h | 4 +- drivers/isdn/hardware/eicon/pc.h | 87 + drivers/isdn/hardware/eicon/platform.h | 37 +- drivers/isdn/hisax/avm_pci.c | 106 +- drivers/isdn/hisax/bkm_a4t.c | 4 +- drivers/isdn/hisax/bkm_a8.c | 4 +- drivers/isdn/hisax/elsa.c | 2 +- drivers/isdn/hisax/enternow_pci.c | 2 +- drivers/isdn/hisax/gazel.c | 2 +- drivers/isdn/hisax/hfc_pci.c | 4 +- drivers/isdn/hisax/niccy.c | 2 +- drivers/isdn/hisax/nj_s.c | 2 +- drivers/isdn/hisax/nj_u.c | 4 +- drivers/isdn/hisax/sedlbauer.c | 2 +- drivers/isdn/hisax/telespci.c | 2 +- drivers/isdn/hisax/w6692.c | 2 +- drivers/isdn/tpam/tpam_main.c | 47 +- drivers/macintosh/adbhid.c | 146 +- drivers/macintosh/ans-lcd.c | 6 +- drivers/macintosh/macserial.c | 22 +- drivers/macintosh/therm_windtunnel.c | 3 +- drivers/macintosh/via-pmu.c | 22 +- drivers/md/dm-io.c | 2 +- drivers/md/dm-log.c | 110 +- drivers/md/dm-log.h | 6 + drivers/md/dm-raid1.c | 29 +- drivers/md/dm-stripe.c | 9 +- drivers/md/linear.c | 25 +- drivers/md/raid0.c | 29 +- drivers/md/raid10.c | 1780 + drivers/media/common/saa7146_i2c.c | 21 +- drivers/media/dvb/bt8xx/bt878.c | 12 +- drivers/media/dvb/frontends/alps_tdlb7.c | 3 - drivers/media/dvb/frontends/dst.c | 6 +- drivers/media/dvb/frontends/sp887x.c | 2 - drivers/media/dvb/ttpci/Kconfig | 8 +- drivers/media/dvb/ttpci/Makefile | 2 +- drivers/media/radio/radio-aimslab.c | 7 +- drivers/media/radio/radio-maestro.c | 54 +- drivers/media/radio/radio-maxiradio.c | 9 +- drivers/media/radio/radio-sf16fmi.c | 6 +- drivers/media/radio/radio-sf16fmr2.c | 15 +- drivers/media/video/adv7175.c | 28 + drivers/media/video/bttv-gpio.c | 14 + drivers/media/video/zoran_device.c | 3 +- drivers/message/fusion/Makefile | 3 +- drivers/message/i2o/debug.c | 571 + drivers/message/i2o/device.c | 674 + drivers/message/i2o/driver.c | 367 + drivers/message/i2o/exec-osm.c | 505 + drivers/message/i2o/i2o_block.h | 99 + drivers/message/i2o/iop.c | 1258 + drivers/message/i2o/pci.c | 528 + drivers/mmc/Kconfig | 52 + drivers/mmc/Makefile | 21 + drivers/mmc/mmc.c | 913 + drivers/mmc/mmc.h | 16 + drivers/mmc/mmc_block.c | 495 + drivers/mmc/mmc_queue.c | 222 + drivers/mmc/mmc_queue.h | 32 + drivers/mmc/mmc_sysfs.c | 241 + drivers/mmc/mmci.c | 679 + drivers/mmc/mmci.h | 183 + drivers/mmc/pxamci.c | 604 + drivers/mmc/pxamci.h | 94 + drivers/mtd/maps/ixp2000.c | 281 + drivers/mtd/maps/ixp4xx.c | 28 +- drivers/net/8139cp.c | 2 +- drivers/net/8390.c | 46 +- drivers/net/amd8111e.c | 2 +- drivers/net/arcnet/arc-rimi.c | 12 +- drivers/net/bonding/bond_alb.c | 2 +- drivers/net/cs89x0.h | 19 +- drivers/net/dl2k.h | 2 +- drivers/net/gt64240eth.h | 402 + drivers/net/hamradio/bpqether.c | 2 +- drivers/net/hamradio/mkiss.c | 2 - drivers/net/ibm_emac/ibm_emac.h | 32 +- drivers/net/ibm_emac/ibm_emac_core.c | 74 +- drivers/net/ibm_emac/ibm_emac_core.h | 2 +- drivers/net/ibm_emac/ibm_emac_mal.c | 4 - drivers/net/ibmveth.h | 2 + drivers/net/ioc3-eth.c | 4 +- drivers/net/mv643xx_eth.c | 2646 + drivers/net/mv643xx_eth.h | 601 + drivers/net/myri_sbus.c | 69 +- drivers/net/myri_sbus.h | 17 +- drivers/net/sk98lin/skaddr.c | 4 +- drivers/net/slip.c | 24 +- drivers/net/smc91x.c | 31 +- drivers/net/smc91x.h | 22 +- drivers/net/sunbmac.c | 40 +- drivers/net/sunbmac.h | 8 +- drivers/net/sunhme.h | 14 +- drivers/net/sunlance.c | 64 +- drivers/net/sunqe.c | 14 +- drivers/net/sunqe.h | 6 +- drivers/net/tokenring/olympic.h | 4 +- drivers/net/via-velocity.c | 640 +- drivers/net/via-velocity.h | 16 +- drivers/net/wan/cycx_x25.c | 6 +- drivers/net/wan/lmc/lmc_debug.c | 2 +- drivers/net/wan/pc300.h | 14 +- drivers/net/wan/pc300_drv.c | 153 +- drivers/net/wan/sdla_chdlc.c | 20 +- drivers/net/wan/sdla_fr.c | 4 +- drivers/net/wan/sdladrv.c | 6 +- drivers/net/wan/sdlamain.c | 2 +- drivers/net/wireless/airport.c | 52 +- drivers/net/wireless/hermes.c | 12 +- drivers/net/wireless/hermes_rid.h | 99 +- drivers/net/wireless/ieee80211/LICENSE | 339 + drivers/net/wireless/ieee80211/Makefile | 23 + drivers/net/wireless/ieee80211/ieee80211.h | 847 + .../net/wireless/ieee80211/ieee80211_crypt.c | 253 + .../net/wireless/ieee80211/ieee80211_crypt.h | 104 + .../wireless/ieee80211/ieee80211_crypt_ccmp.c | 477 + .../wireless/ieee80211/ieee80211_crypt_tkip.c | 714 + .../wireless/ieee80211/ieee80211_crypt_wep.c | 277 + .../net/wireless/ieee80211/ieee80211_module.c | 252 + drivers/net/wireless/ieee80211/ieee80211_rx.c | 1161 + drivers/net/wireless/ieee80211/ieee80211_tx.c | 452 + drivers/net/wireless/ieee80211/ieee80211_wx.c | 516 + drivers/net/wireless/ieee802_11.h | 1 - drivers/net/wireless/ipw2100/LICENSE | 339 + drivers/net/wireless/ipw2100/Makefile | 15 + drivers/net/wireless/ipw2100/ipw2100.c | 8857 + drivers/net/wireless/ipw2100/ipw2100.h | 1282 + drivers/net/wireless/ipw2200/LICENSE | 339 + drivers/net/wireless/ipw2200/Makefile | 15 + drivers/net/wireless/ipw2200/ipw2200.c | 6744 + drivers/net/wireless/ipw2200/ipw2200.h | 1722 + drivers/net/wireless/orinoco.h | 36 +- drivers/net/wireless/orinoco_cs.c | 70 +- drivers/net/wireless/ray_cs.h | 6 +- drivers/net/wireless/wavelan.c | 11 +- drivers/oprofile/buffer_sync.h | 3 + drivers/oprofile/cpu_buffer.h | 11 +- drivers/oprofile/oprofile_stats.c | 13 +- drivers/oprofile/timer_int.c | 2 +- drivers/parisc/lasi.c | 2 +- drivers/pci/.cvsignore | 3 + drivers/pci/Makefile | 2 +- drivers/pci/hotplug/Kconfig | 12 + drivers/pci/hotplug/Makefile | 1 + drivers/pci/hotplug/acpiphp_ibm.c | 474 + drivers/pci/pci-acpi.c | 209 + drivers/pci/remove.c | 20 +- drivers/pci/setup-bus.c | 5 +- drivers/pcmcia/au1000_generic.c | 15 +- drivers/pcmcia/hd64465_ss.c | 11 +- drivers/pcmcia/pd6729.c | 14 +- drivers/pcmcia/pxa2xx_base.c | 34 +- drivers/pcmcia/pxa2xx_lubbock.c | 32 +- drivers/pcmcia/pxa2xx_mainstone.c | 24 + drivers/pcmcia/soc_common.c | 14 +- drivers/pcmcia/yenta_socket.h | 2 +- drivers/pnp/interface.c | 10 +- drivers/pnp/pnpbios/proc.c | 8 +- drivers/pnp/pnpbios/rsparser.c | 9 +- drivers/pnp/system.c | 6 +- drivers/s390/block/dcssblk.c | 56 + drivers/s390/char/con3215.c | 10 +- drivers/s390/char/sclp_tty.c | 5 +- drivers/s390/char/sclp_vt220.c | 5 +- drivers/s390/cio/blacklist.c | 4 +- drivers/s390/cio/ccwgroup.c | 16 +- drivers/s390/s390mach.h | 4 - drivers/s390/scsi/zfcp_fsf.h | 11 +- drivers/sbus/char/aurora.c | 13 +- drivers/sbus/char/bbc_envctrl.c | 5 +- drivers/sbus/char/bbc_i2c.c | 4 +- drivers/scsi/3w-9xxx.c | 10 +- drivers/scsi/BusLogic.h | 1468 +- drivers/scsi/NCR5380.c | 222 +- drivers/scsi/NCR5380.h | 3 +- drivers/scsi/aha1542.h | 10 +- drivers/scsi/ahci.c | 1045 + drivers/scsi/aic7xxx/.cvsignore | 4 + drivers/scsi/aic7xxx/Kconfig.aic79xx | 2 +- drivers/scsi/aic7xxx/Kconfig.aic7xxx | 2 +- drivers/scsi/aic7xxx/aic79xx_pci.c | 4 +- drivers/scsi/aic7xxx/aic7xxx_pci.c | 4 +- drivers/scsi/aic7xxx/aicasm/Makefile | 6 +- drivers/scsi/ibmvscsi/Makefile | 5 + drivers/scsi/ibmvscsi/ibmvscsi.c | 1393 + drivers/scsi/ibmvscsi/ibmvscsi.h | 108 + drivers/scsi/ibmvscsi/iseries_vscsi.c | 144 + drivers/scsi/ibmvscsi/rpa_vscsi.c | 260 + drivers/scsi/ibmvscsi/srp.h | 225 + drivers/scsi/ibmvscsi/viosrp.h | 126 + drivers/scsi/ipr.c | 240 +- drivers/scsi/ipr.h | 24 +- drivers/scsi/lpfc/Makefile | 7 + drivers/scsi/lpfc/lpfc.h | 449 + drivers/scsi/lpfc/lpfc_compat.h | 120 + drivers/scsi/lpfc/lpfc_crtn.h | 265 + drivers/scsi/lpfc/lpfc_ct.c | 1288 + drivers/scsi/lpfc/lpfc_disc.h | 270 + drivers/scsi/lpfc/lpfc_els.c | 3235 + drivers/scsi/lpfc/lpfc_fcp.c | 3419 + drivers/scsi/lpfc/lpfc_fcp.h | 108 + drivers/scsi/lpfc/lpfc_hbadisc.c | 2703 + drivers/scsi/lpfc/lpfc_hw.h | 2688 + drivers/scsi/lpfc/lpfc_init.c | 1345 + drivers/scsi/lpfc/lpfc_logmsg.h | 46 + drivers/scsi/lpfc/lpfc_mbox.c | 672 + drivers/scsi/lpfc/lpfc_mem.c | 192 + drivers/scsi/lpfc/lpfc_mem.h | 56 + drivers/scsi/lpfc/lpfc_nportdisc.c | 2145 + drivers/scsi/lpfc/lpfc_scsi.h | 92 + drivers/scsi/lpfc/lpfc_scsiport.c | 1300 + drivers/scsi/lpfc/lpfc_sli.c | 3349 + drivers/scsi/lpfc/lpfc_sli.h | 218 + drivers/scsi/lpfc/lpfc_version.h | 37 + drivers/scsi/megaraid/Kconfig.megaraid | 77 + drivers/scsi/megaraid/Makefile | 2 + drivers/scsi/megaraid/mbox_defs.h | 790 + drivers/scsi/megaraid/mega_common.h | 283 + drivers/scsi/megaraid/megaraid_ioctl.h | 291 + drivers/scsi/megaraid/megaraid_mbox.c | 3891 + drivers/scsi/megaraid/megaraid_mbox.h | 268 + drivers/scsi/megaraid/megaraid_mm.c | 1160 + drivers/scsi/megaraid/megaraid_mm.h | 102 + drivers/scsi/sata_nv.c | 129 +- drivers/scsi/sata_sx4.c | 178 +- drivers/scsi/sata_uli.c | 283 + drivers/scsi/sym53c8xx_2/sym_defs.h | 3 + drivers/scsi/sym53c8xx_2/sym_glue.c | 122 +- drivers/scsi/sym53c8xx_2/sym_glue.h | 42 +- drivers/scsi/sym53c8xx_2/sym_hipd.c | 126 +- drivers/scsi/sym53c8xx_2/sym_hipd.h | 40 +- drivers/scsi/sym53c8xx_2/sym_misc.c | 118 - drivers/scsi/sym53c8xx_2/sym_nvram.c | 16 - drivers/serial/68328serial.c | 17 +- drivers/serial/8250.h | 14 + drivers/serial/icom.c | 1702 + drivers/serial/icom.h | 290 + drivers/serial/mcfserial.c | 17 +- drivers/serial/s3c2410.c | 4 +- drivers/serial/serial_cs.c | 45 +- drivers/serial/sn_console.c | 353 +- drivers/usb/atm/Kconfig | 30 + drivers/usb/atm/Makefile | 7 + drivers/usb/atm/speedtch.c | 863 + drivers/usb/atm/usb_atm.c | 1201 + drivers/usb/atm/usb_atm.h | 160 + drivers/usb/class/cdc-acm.h | 2 +- drivers/usb/core/Kconfig | 37 + drivers/usb/core/otg_whitelist.h | 112 + drivers/usb/gadget/Makefile | 4 +- drivers/usb/gadget/lh7a40x_udc.c | 2168 + drivers/usb/gadget/lh7a40x_udc.h | 261 + drivers/usb/gadget/net2280.h | 23 +- drivers/usb/gadget/omap_udc.c | 2695 + drivers/usb/gadget/omap_udc.h | 199 + drivers/usb/host/Kconfig | 1 + drivers/usb/host/hc_sl811.c | 8 +- drivers/usb/media/pwc/ChangeLog | 143 + drivers/usb/media/pwc/Makefile | 20 + drivers/usb/media/pwc/philips.txt | 236 + drivers/usb/media/pwc/pwc-ctrl.c | 1630 + drivers/usb/media/pwc/pwc-dec1.c | 42 + drivers/usb/media/pwc/pwc-dec1.h | 36 + drivers/usb/media/pwc/pwc-dec23.c | 623 + drivers/usb/media/pwc/pwc-dec23.h | 58 + drivers/usb/media/pwc/pwc-if.c | 2211 + drivers/usb/media/pwc/pwc-ioctl.h | 292 + drivers/usb/media/pwc/pwc-kiara.c | 891 + drivers/usb/media/pwc/pwc-kiara.h | 45 + drivers/usb/media/pwc/pwc-misc.c | 140 + drivers/usb/media/pwc/pwc-nala.h | 66 + drivers/usb/media/pwc/pwc-timon.c | 1446 + drivers/usb/media/pwc/pwc-timon.h | 61 + drivers/usb/media/pwc/pwc-uncompress.c | 147 + drivers/usb/media/pwc/pwc-uncompress.h | 41 + drivers/usb/media/pwc/pwc.h | 278 + drivers/usb/media/sn9c102.h | 6 +- drivers/usb/media/sn9c102_core.c | 95 +- drivers/usb/media/sn9c102_pas106b.c | 76 +- drivers/usb/media/sn9c102_pas202bcb.c | 238 + drivers/usb/media/sn9c102_sensor.h | 133 +- drivers/usb/media/sn9c102_tas5110c1b.c | 47 +- drivers/usb/media/sn9c102_tas5130d1b.c | 56 +- drivers/usb/misc/phidgetservo.c | 16 +- drivers/usb/net/catc.c | 4 +- drivers/usb/serial/io_edgeport.c | 32 +- drivers/usb/serial/io_edgeport.h | 4 +- drivers/usb/serial/io_fw_down.h | 1968 +- drivers/usb/serial/io_fw_down3.h | 1532 +- drivers/usb/serial/io_tables.h | 67 +- drivers/usb/serial/io_usbvend.h | 377 +- drivers/usb/storage/datafab.h | 2 +- drivers/usb/storage/debug.c | 8 +- drivers/usb/storage/debug.h | 6 +- drivers/usb/storage/dpcm.c | 6 +- drivers/usb/storage/dpcm.h | 2 +- drivers/usb/storage/freecom.c | 24 +- drivers/usb/storage/freecom.h | 2 +- drivers/usb/storage/isd200.h | 2 +- drivers/usb/storage/jumpshot.h | 2 +- drivers/usb/storage/protocol.c | 27 +- drivers/usb/storage/protocol.h | 20 +- drivers/usb/storage/sddr09.h | 2 +- drivers/usb/storage/sddr55.c | 48 +- drivers/usb/storage/sddr55.h | 2 +- drivers/usb/storage/transport.h | 19 +- drivers/usb/usb-skeleton.c | 622 +- drivers/video/amba-clcd.c | 515 + drivers/video/amifb.c | 19 +- drivers/video/asiliantfb.c | 7 +- drivers/video/aty/aty128fb.c | 11 +- drivers/video/bw2.c | 14 +- drivers/video/cfbcopyarea.c | 13 +- drivers/video/cfbfillrect.c | 16 +- drivers/video/cfbimgblt.c | 32 +- drivers/video/cg14.c | 8 +- drivers/video/cg3.c | 8 +- drivers/video/cg6.c | 9 +- drivers/video/chipsfb.c | 7 +- drivers/video/clps711xfb.c | 7 +- drivers/video/controlfb.c | 8 +- drivers/video/dnfb.c | 8 + drivers/video/epson1355fb.c | 1012 +- drivers/video/fbmon.c | 113 +- drivers/video/ffb.c | 155 +- drivers/video/fm2fb.c | 11 +- drivers/video/g364fb.c | 6 +- drivers/video/gbefb.c | 13 +- drivers/video/hitfb.c | 8 +- drivers/video/hpfb.c | 6 +- drivers/video/i810/i810_dvt.c | 14 +- drivers/video/i810/i810_main.c | 78 +- drivers/video/igafb.c | 6 +- drivers/video/leo.c | 7 +- drivers/video/macfb.c | 11 +- drivers/video/matrox/matroxfb_crtc2.h | 2 +- drivers/video/matrox/matroxfb_misc.c | 12 +- drivers/video/maxinefb.c | 7 +- drivers/video/offb.c | 33 +- drivers/video/p9100.c | 8 +- drivers/video/platinumfb.c | 11 +- drivers/video/pmag-ba-fb.c | 6 +- drivers/video/pmagb-b-fb.c | 6 +- drivers/video/pvr2fb.c | 11 +- drivers/video/pxafb.c | 23 +- drivers/video/riva/nv_driver.c | 2 + drivers/video/riva/riva_hw.h | 4 +- drivers/video/riva/rivafb-i2c.c | 36 +- drivers/video/sgivwfb.c | 135 +- drivers/video/stifb.c | 15 +- drivers/video/tcx.c | 8 +- drivers/video/tx3912fb.c | 13 +- drivers/video/valkyriefb.c | 8 +- drivers/video/vgastate.c | 12 +- drivers/w1/Kconfig | 26 + drivers/w1/Makefile | 7 + drivers/w1/ds_w1_bridge.c | 174 + drivers/w1/dscore.c | 783 + drivers/w1/dscore.h | 173 + drivers/w1/matrox_w1.c | 2 +- drivers/w1/w1.c | 276 +- drivers/w1/w1.h | 19 + drivers/w1/w1_family.c | 11 + drivers/w1/w1_family.h | 2 +- drivers/w1/w1_int.c | 16 +- drivers/w1/w1_io.c | 69 +- drivers/w1/w1_io.h | 3 + drivers/w1/w1_netlink.h | 15 +- drivers/w1/w1_smem.c | 118 + drivers/w1/w1_therm.c | 87 +- drivers/zorro/Makefile | 2 +- fs/adfs/adfs.h | 11 +- fs/adfs/dir.c | 3 +- fs/adfs/dir_f.c | 16 +- fs/adfs/dir_fplus.h | 28 +- fs/adfs/inode.c | 11 +- fs/adfs/map.c | 10 +- fs/adfs/super.c | 10 +- fs/affs/namei.c | 2 +- fs/afs/inode.c | 5 +- fs/afs/main.c | 3 +- fs/afs/vlclient.h | 3 - fs/afs/vnode.c | 6 +- fs/afs/volume.h | 1 - fs/aio.c | 2 +- fs/bfs/inode.c | 9 +- fs/cifs/cifsencrypt.c | 14 +- fs/cifs/netmisc.c | 15 +- fs/cifs/ntlmssp.h | 26 +- fs/coda/coda_linux.c | 2 +- fs/coda/file.c | 18 + fs/coda/upcall.c | 10 +- fs/efs/inode.c | 4 +- fs/efs/super.c | 18 +- fs/ext2/acl.h | 12 +- fs/ext2/dir.c | 11 +- fs/ext2/ext2.h | 7 +- fs/ext2/xattr.h | 16 +- fs/ext3/acl.h | 12 +- fs/ext3/fsync.c | 4 - fs/ext3/xattr.h | 16 +- fs/fat/cache.c | 11 +- fs/fat/dir.c | 33 +- fs/fat/misc.c | 13 +- fs/freevxfs/vxfs_extern.h | 2 +- fs/freevxfs/vxfs_inode.c | 11 +- fs/freevxfs/vxfs_super.c | 2 +- fs/hfs/bfind.c | 3 +- fs/hfs/bitmap.c | 35 +- fs/hfs/bnode.c | 10 +- fs/hfs/brec.c | 8 +- fs/hfs/btree.c | 2 +- fs/hfs/btree.h | 28 +- fs/hfs/extent.c | 6 +- fs/hfs/hfs.h | 134 +- fs/hfs/hfs_fs.h | 15 +- fs/hfs/mdb.c | 4 +- fs/hfs/part_tbl.c | 20 +- fs/hfsplus/bfind.c | 3 +- fs/hfsplus/bitmap.c | 33 +- fs/hfsplus/btree.c | 2 +- fs/hfsplus/extents.c | 4 +- fs/hfsplus/hfsplus_fs.h | 3 +- fs/hfsplus/hfsplus_raw.h | 172 +- fs/hfsplus/part_tbl.c | 20 +- fs/hfsplus/super.c | 9 +- fs/jbd/recovery.c | 32 +- fs/jfs/inode.c | 22 +- fs/jfs/jfs_inode.c | 14 +- fs/jfs/resize.c | 5 +- fs/libfs.c | 72 +- fs/lockd/clntproc.c | 174 +- fs/lockd/svc.c | 8 +- fs/lockd/svc4proc.c | 3 +- fs/lockd/svcproc.c | 3 +- fs/lockd/svcsubs.c | 10 +- fs/minix/itree_common.c | 2 +- fs/ncpfs/sock.c | 2 +- fs/ncpfs/symlink.c | 15 +- fs/nfs/Makefile | 3 +- fs/nfs/callback.c | 325 + fs/nfs/callback.h | 70 + fs/nfs/callback_proc.c | 85 + fs/nfs/callback_xdr.c | 481 + fs/nfs/delegation.c | 342 + fs/nfs/delegation.h | 57 + fs/nfs/mount_clnt.c | 2 - fs/nfs/nfs3proc.c | 111 +- fs/nfs/pagelist.c | 62 +- fs/nfsd/Makefile | 3 +- fs/nfsd/lockd.c | 11 +- fs/nfsd/nfs3proc.c | 12 +- fs/nfsd/nfs4acl.c | 974 + fs/nfsd/nfssvc.c | 2 + fs/nls/nls_cp932.c | 43 +- fs/ntfs/bitmap.c | 191 + fs/ntfs/bitmap.h | 118 + fs/ntfs/collate.c | 20 +- fs/ntfs/collate.h | 12 +- fs/ntfs/index.c | 36 +- fs/ntfs/index.h | 2 +- fs/ntfs/lcnalloc.c | 1001 + fs/ntfs/lcnalloc.h | 83 + fs/ntfs/quota.c | 2 +- fs/openpromfs/inode.c | 48 +- fs/partitions/acorn.c | 42 +- fs/partitions/amiga.c | 42 +- fs/partitions/atari.h | 4 +- fs/partitions/efi.h | 36 +- fs/partitions/ldm.c | 16 +- fs/partitions/ldm.h | 6 +- fs/partitions/mac.h | 36 +- fs/partitions/osf.c | 46 +- fs/partitions/sgi.c | 32 +- fs/partitions/sun.c | 33 +- fs/proc/task_nommu.c | 8 +- fs/quota_v1.c | 4 +- fs/reiserfs/xattr_acl.c | 28 +- fs/reiserfs/xattr_security.c | 10 +- fs/reiserfs/xattr_trusted.c | 10 +- fs/reiserfs/xattr_user.c | 10 +- fs/romfs/inode.c | 33 +- fs/sysv/balloc.c | 31 +- fs/sysv/ialloc.c | 2 +- fs/sysv/super.c | 36 +- fs/sysv/sysv.h | 72 +- fs/udf/balloc.c | 22 +- fs/udf/directory.c | 16 +- fs/udf/ecma_167.h | 294 +- fs/udf/fsync.c | 4 +- fs/udf/inode.c | 114 +- fs/udf/osta_udf.h | 86 +- fs/udf/partition.c | 12 +- fs/udf/truncate.c | 14 +- fs/udf/udfdecl.h | 35 +- fs/udf/udfend.h | 22 +- fs/udf/udftime.c | 8 +- fs/ufs/dir.c | 10 +- fs/ufs/inode.c | 22 +- fs/ufs/super.c | 24 +- fs/ufs/swab.h | 72 +- fs/ufs/truncate.c | 16 +- fs/ufs/util.h | 38 +- fs/umsdos/inode.c | 6 +- fs/vfat/namei.c | 40 +- fs/xfs/linux-2.6/xfs_aops.c | 74 +- fs/xfs/linux-2.6/xfs_buf.c | 54 +- fs/xfs/linux-2.6/xfs_buf.h | 2 +- fs/xfs/linux-2.6/xfs_file.c | 46 +- fs/xfs/linux-2.6/xfs_fs_subr.h | 1 - fs/xfs/linux-2.6/xfs_globals.c | 1 + fs/xfs/linux-2.6/xfs_ioctl32.c | 162 + fs/xfs/linux-2.6/xfs_ioctl32.h | 41 + fs/xfs/linux-2.6/xfs_iops.h | 2 +- fs/xfs/linux-2.6/xfs_linux.h | 2 + fs/xfs/linux-2.6/xfs_lrw.c | 276 +- fs/xfs/linux-2.6/xfs_lrw.h | 6 +- fs/xfs/linux-2.6/xfs_stats.c | 4 +- fs/xfs/linux-2.6/xfs_sysctl.c | 7 +- fs/xfs/linux-2.6/xfs_sysctl.h | 2 + fs/xfs/linux-2.6/xfs_vfs.c | 2 + fs/xfs/linux-2.6/xfs_vfs.h | 21 +- fs/xfs/linux-2.6/xfs_vnode.h | 16 +- fs/xfs/quota/xfs_dquot.c | 108 +- fs/xfs/quota/xfs_dquot.h | 2 + fs/xfs/quota/xfs_qm.h | 8 +- fs/xfs/quota/xfs_qm_stats.c | 4 +- fs/xfs/quota/xfs_trans_dquot.c | 41 +- fs/xfs/support/ktrace.c | 9 +- fs/xfs/support/move.c | 8 +- fs/xfs/xfs_alloc.c | 20 +- fs/xfs/xfs_alloc_btree.c | 2 +- fs/xfs/xfs_attr_leaf.h | 1 - fs/xfs/xfs_dfrag.c | 2 +- fs/xfs/xfs_dfrag.h | 2 +- fs/xfs/xfs_dir2_trace.c | 2 +- fs/xfs/xfs_iget.c | 7 +- fs/xfs/xfs_iomap.h | 5 +- fs/xfs/xfs_log_priv.h | 1 - fs/xfs/xfs_quota.h | 18 +- fs/xfs/xfs_sb.h | 6 +- fs/xfs/xfs_types.h | 2 +- include/.cvsignore | 1 + include/acpi/acconfig.h | 25 +- include/acpi/acdebug.h | 8 + include/acpi/acdisasm.h | 15 + include/acpi/acdispat.h | 3 +- include/acpi/acevents.h | 54 +- include/acpi/acexcep.h | 8 +- include/acpi/acglobal.h | 44 +- include/acpi/achware.h | 36 +- include/acpi/acinterp.h | 15 +- include/acpi/aclocal.h | 39 +- include/acpi/acmacros.h | 23 +- include/acpi/acnamesp.h | 18 +- include/acpi/acobject.h | 13 +- include/acpi/acparser.h | 4 +- include/acpi/acpi_bus.h | 28 +- include/acpi/acpixf.h | 10 +- include/acpi/acstruct.h | 23 +- include/acpi/actbl.h | 29 +- include/acpi/actypes.h | 78 +- include/acpi/platform/acenv.h | 8 +- include/asm-alpha/a.out.h | 2 +- include/asm-alpha/bug.h | 12 +- include/asm-alpha/cache.h | 2 +- include/asm-alpha/compiler.h | 16 +- include/asm-alpha/core_apecs.h | 224 +- include/asm-alpha/core_cia.h | 303 +- include/asm-alpha/core_irongate.h | 134 +- include/asm-alpha/core_marvel.h | 243 +- include/asm-alpha/core_t2.h | 110 +- include/asm-alpha/core_titan.h | 146 +- include/asm-alpha/core_tsunami.h | 149 +- include/asm-alpha/core_wildfire.h | 149 +- include/asm-alpha/dma-mapping.h | 64 +- include/asm-alpha/hardirq.h | 41 - include/asm-alpha/io_trivial.h | 127 + include/asm-alpha/jensen.h | 99 +- include/asm-alpha/machvec.h | 42 +- include/asm-alpha/mmu_context.h | 1 + include/asm-alpha/pci.h | 4 +- include/asm-alpha/ptrace.h | 1 + include/asm-alpha/socket.h | 16 - include/asm-alpha/sysinfo.h | 1 + include/asm-alpha/thread_info.h | 2 +- include/asm-alpha/tlbflush.h | 3 +- include/asm-alpha/vga.h | 18 +- include/asm-arm/apm.h | 49 +- include/asm-arm/arch-cl7500/uncompress.h | 2 +- include/asm-arm/arch-clps711x/uncompress.h | 2 +- include/asm-arm/arch-ebsa110/param.h | 2 +- include/asm-arm/arch-ebsa285/uncompress.h | 2 +- include/asm-arm/arch-epxa10db/uncompress.h | 2 +- include/asm-arm/arch-h720x/boards.h | 53 + include/asm-arm/arch-h720x/dma.h | 26 + include/asm-arm/arch-h720x/h7201-regs.h | 67 + include/asm-arm/arch-h720x/h7202-regs.h | 151 + include/asm-arm/arch-h720x/hardware.h | 182 + include/asm-arm/arch-h720x/io.h | 24 + include/asm-arm/arch-h720x/irq.h | 14 + include/asm-arm/arch-h720x/irqs.h | 116 + include/asm-arm/arch-h720x/memory.h | 31 + include/asm-arm/arch-h720x/param.h | 10 + include/asm-arm/arch-h720x/serial.h | 101 + include/asm-arm/arch-h720x/system.h | 31 + include/asm-arm/arch-h720x/timex.h | 15 + include/asm-arm/arch-h720x/uncompress.h | 39 + include/asm-arm/arch-h720x/vmalloc.h | 21 + include/asm-arm/arch-imx/dma.h | 71 + include/asm-arm/arch-imx/hardware.h | 101 + include/asm-arm/arch-imx/imx-regs.h | 548 + include/asm-arm/arch-imx/io.h | 28 + include/asm-arm/arch-imx/irq.h | 20 + include/asm-arm/arch-imx/irqs.h | 116 + include/asm-arm/arch-imx/memory.h | 38 + include/asm-arm/arch-imx/mx1ads.h | 36 + include/asm-arm/arch-imx/param.h | 19 + include/asm-arm/arch-imx/serial.h | 26 + include/asm-arm/arch-imx/system.h | 40 + include/asm-arm/arch-imx/timex.h | 27 + include/asm-arm/arch-imx/uncompress.h | 78 + include/asm-arm/arch-imx/vmalloc.h | 32 + include/asm-arm/arch-integrator/uncompress.h | 2 +- include/asm-arm/arch-iop3xx/dma.h | 103 +- include/asm-arm/arch-iop3xx/hardware.h | 51 +- include/asm-arm/arch-iop3xx/iop321-irqs.h | 26 +- include/asm-arm/arch-iop3xx/iop321.h | 95 +- include/asm-arm/arch-iop3xx/iop331-irqs.h | 113 + include/asm-arm/arch-iop3xx/iop331.h | 342 + include/asm-arm/arch-iop3xx/iq31244.h | 36 + include/asm-arm/arch-iop3xx/iq80321.h | 32 +- include/asm-arm/arch-iop3xx/iq80331.h | 38 + include/asm-arm/arch-iop3xx/irqs.h | 18 +- include/asm-arm/arch-iop3xx/param.h | 2 +- include/asm-arm/arch-iop3xx/serial.h | 53 +- include/asm-arm/arch-iop3xx/system.h | 10 +- include/asm-arm/arch-iop3xx/timex.h | 16 +- include/asm-arm/arch-iop3xx/uncompress.h | 36 +- include/asm-arm/arch-iop3xx/vmalloc.h | 1 + include/asm-arm/arch-ixp2000/dma.h | 18 + include/asm-arm/arch-ixp2000/enp2611.h | 28 + include/asm-arm/arch-ixp2000/gpio.h | 55 + include/asm-arm/arch-ixp2000/hardware.h | 44 + include/asm-arm/arch-ixp2000/io.h | 150 + include/asm-arm/arch-ixp2000/irq.h | 13 + include/asm-arm/arch-ixp2000/irqs.h | 190 + include/asm-arm/arch-ixp2000/ixdp2x00.h | 93 + include/asm-arm/arch-ixp2000/ixdp2x01.h | 53 + include/asm-arm/arch-ixp2000/ixp2000-regs.h | 337 + include/asm-arm/arch-ixp2000/memory.h | 34 + include/asm-arm/arch-ixp2000/param.h | 3 + include/asm-arm/arch-ixp2000/platform.h | 165 + include/asm-arm/arch-ixp2000/serial.h | 27 + include/asm-arm/arch-ixp2000/system.h | 35 + include/asm-arm/arch-ixp2000/timex.h | 13 + include/asm-arm/arch-ixp2000/uncompress.h | 52 + include/asm-arm/arch-ixp2000/vmalloc.h | 23 + include/asm-arm/arch-ixp4xx/io.h | 14 +- include/asm-arm/arch-ixp4xx/uncompress.h | 2 +- include/asm-arm/arch-l7200/param.h | 2 +- include/asm-arm/arch-l7200/uncompress.h | 2 +- include/asm-arm/arch-lh7a40x/uncompress.h | 2 +- include/asm-arm/arch-omap/board-h2.h | 10 + include/asm-arm/arch-omap/board-h3.h | 84 +- include/asm-arm/arch-omap/board-innovator.h | 119 +- include/asm-arm/arch-omap/board-perseus2.h | 57 +- include/asm-arm/arch-omap/board.h | 51 +- include/asm-arm/arch-omap/fpga.h | 159 +- include/asm-arm/arch-omap/mcbsp.h | 253 + include/asm-arm/arch-omap/omap1510.h | 19 +- include/asm-arm/arch-omap/omap1610.h | 9 + include/asm-arm/arch-omap/omap5912.h | 9 + include/asm-arm/arch-omap/omap730.h | 6 - include/asm-arm/arch-omap/tps65010.h | 80 + include/asm-arm/arch-omap/usb.h | 108 + include/asm-arm/arch-pxa/mmc.h | 19 + include/asm-arm/arch-pxa/serial.h | 1 + include/asm-arm/arch-pxa/system.h | 1 + include/asm-arm/arch-pxa/uncompress.h | 2 +- include/asm-arm/arch-rpc/io.h | 38 +- include/asm-arm/arch-s3c2410/bast-cpld.h | 35 +- include/asm-arm/arch-s3c2410/bast-irq.h | 3 +- include/asm-arm/arch-s3c2410/dma.h | 299 +- include/asm-arm/arch-s3c2410/hardware.h | 35 + include/asm-arm/arch-s3c2410/nand.h | 48 + include/asm-arm/arch-s3c2410/param.h | 2 +- include/asm-arm/arch-s3c2410/regs-clock.h | 58 +- include/asm-arm/arch-s3c2410/regs-dsc.h | 183 + include/asm-arm/arch-s3c2410/regs-gpio.h | 31 +- include/asm-arm/arch-s3c2410/regs-gpioj.h | 100 + include/asm-arm/arch-s3c2410/regs-iic.h | 50 + include/asm-arm/arch-s3c2410/regs-mem.h | 190 + include/asm-arm/arch-s3c2410/regs-nand.h | 43 + include/asm-arm/arch-s3c2410/regs-sdi.h | 112 + include/asm-arm/arch-s3c2410/regs-spi.h | 56 + include/asm-arm/arch-s3c2410/regs-timer.h | 2 + include/asm-arm/arch-s3c2410/regs-udc.h | 162 + include/asm-arm/arch-s3c2410/uncompress.h | 2 +- include/asm-arm/arch-s3c2410/usb-control.h | 45 + include/asm-arm/arch-sa1100/uncompress.h | 2 +- include/asm-arm/arch-shark/uncompress.h | 2 +- include/asm-arm/bitops.h | 2 +- include/asm-arm/bug.h | 14 +- include/asm-arm/elf.h | 27 + include/asm-arm/hardirq.h | 48 +- include/asm-arm/hardware.h | 9 - include/asm-arm/hardware/amba_clcd.h | 259 + include/asm-arm/hardware/clock.h | 8 +- include/asm-arm/io.h | 2 + include/asm-arm/mach/mmc.h | 16 + include/asm-arm/mach/pci.h | 8 +- include/asm-arm/mach/time.h | 7 +- include/asm-arm/param.h | 11 +- include/asm-arm/ptrace.h | 6 + include/asm-arm/semaphore.h | 30 +- include/asm-arm/socket.h | 16 - include/asm-arm26/bug.h | 17 +- include/asm-arm26/hardirq.h | 35 - include/asm-arm26/ptrace.h | 1 + include/asm-arm26/semaphore.h | 37 +- include/asm-arm26/socket.h | 16 - include/asm-cris/arch-v10/ptrace.h | 1 + include/asm-cris/bug.h | 19 +- include/asm-cris/hardirq.h | 36 - include/asm-cris/socket.h | 15 - include/asm-generic/bug.h | 34 + include/asm-generic/ide_iops.h | 8 +- include/asm-generic/iomap.h | 63 + include/asm-generic/local.h | 2 +- include/asm-generic/uaccess.h | 26 + include/asm-h8300/bug.h | 19 +- include/asm-h8300/ptrace.h | 1 + include/asm-h8300/socket.h | 16 - include/asm-i386/.cvsignore | 1 + include/asm-i386/apicdef.h | 1 + include/asm-i386/bug.h | 14 +- include/asm-i386/hardirq.h | 42 +- include/asm-i386/io.h | 65 +- include/asm-i386/irq.h | 5 + include/asm-i386/kdebug.h | 50 + include/asm-i386/kexec.h | 25 + include/asm-i386/kprobes.h | 60 + include/asm-i386/linkage.h | 4 + include/asm-i386/mach-bigsmp/mach_ipi.h | 2 +- include/asm-i386/mach-default/do_timer.h | 2 +- include/asm-i386/mach-default/mach_ipi.h | 4 +- include/asm-i386/mach-numaq/mach_ipi.h | 2 +- include/asm-i386/mach-summit/mach_ipi.h | 2 +- include/asm-i386/mach-visws/do_timer.h | 2 +- include/asm-i386/module.h | 12 + include/asm-i386/mtrr.h | 2 - include/asm-i386/processor.h | 8 +- include/asm-i386/ptrace.h | 5 + include/asm-i386/rwlock.h | 32 +- include/asm-i386/segment.h | 2 + include/asm-i386/semaphore.h | 34 +- include/asm-i386/socket.h | 16 - include/asm-i386/thread_info.h | 5 +- include/asm-i386/topology.h | 2 +- include/asm-ia64/bug.h | 13 +- include/asm-ia64/compat.h | 8 +- include/asm-ia64/elf.h | 6 +- include/asm-ia64/hardirq.h | 41 +- include/asm-ia64/hw_irq.h | 2 - include/asm-ia64/io.h | 85 +- include/asm-ia64/mca.h | 7 + include/asm-ia64/mca_asm.h | 3 - include/asm-ia64/mmu_context.h | 51 +- include/asm-ia64/mmzone.h | 6 - include/asm-ia64/numa.h | 2 +- include/asm-ia64/ptrace.h | 9 + include/asm-ia64/sal.h | 12 +- include/asm-ia64/semaphore.h | 30 +- include/asm-ia64/siginfo.h | 2 +- include/asm-ia64/sn/l1.h | 36 + include/asm-ia64/sn/shub_mmr.h | 199 + include/asm-ia64/sn/sn2/shubio.h | 2 +- include/asm-ia64/sn/sn2/sn_hwperf.h | 218 + include/asm-ia64/sn/xtalk/xtalk.h | 2 +- include/asm-ia64/sn/xtalk/xwidget.h | 2 +- include/asm-ia64/socket.h | 16 - include/asm-ia64/tlbflush.h | 7 +- include/asm-m32r/a.out.h | 28 + include/asm-m32r/addrspace.h | 58 + include/asm-m32r/assembler.h | 212 + include/asm-m32r/atomic.h | 305 + include/asm-m32r/bitops.h | 716 + include/asm-m32r/bug.h | 22 + include/asm-m32r/bugs.h | 21 + include/asm-m32r/byteorder.h | 19 + include/asm-m32r/cache.h | 12 + include/asm-m32r/cachectl.h | 26 + include/asm-m32r/cacheflush.h | 68 + include/asm-m32r/checksum.h | 206 + include/asm-m32r/current.h | 18 + include/asm-m32r/delay.h | 28 + include/asm-m32r/div64.h | 38 + include/asm-m32r/dma-mapping.h | 23 + include/asm-m32r/dma.h | 14 + include/asm-m32r/elf.h | 172 + include/asm-m32r/errno.h | 9 + include/asm-m32r/fcntl.h | 92 + include/asm-m32r/flat.h | 145 + include/asm-m32r/hardirq.h | 65 + include/asm-m32r/hdreg.h | 1 + include/asm-m32r/hw_irq.h | 9 + include/asm-m32r/ide.h | 82 + include/asm-m32r/io.h | 216 + include/asm-m32r/ioctl.h | 78 + include/asm-m32r/ioctls.h | 88 + include/asm-m32r/ipc.h | 35 + include/asm-m32r/ipcbuf.h | 33 + include/asm-m32r/irq.h | 86 + include/asm-m32r/kmap_types.h | 34 + include/asm-m32r/linkage.h | 7 + include/asm-m32r/local.h | 6 + include/asm-m32r/m32102.h | 266 + include/asm-m32r/m32102peri.h | 468 + include/asm-m32r/m32700ut/m32700ut_lan.h | 107 + include/asm-m32r/m32700ut/m32700ut_lcd.h | 59 + include/asm-m32r/m32700ut/m32700ut_pld.h | 265 + include/asm-m32r/m32r.h | 134 + include/asm-m32r/m32r_mp_fpga.h | 313 + include/asm-m32r/mappi2/mappi2_pld.h | 151 + include/asm-m32r/mc146818rtc.h | 32 + include/asm-m32r/mman.h | 45 + include/asm-m32r/mmu.h | 35 + include/asm-m32r/mmu_context.h | 169 + include/asm-m32r/mmzone.h | 80 + include/asm-m32r/module.h | 13 + include/asm-m32r/msgbuf.h | 35 + include/asm-m32r/namei.h | 21 + include/asm-m32r/numnodes.h | 15 + include/asm-m32r/opsput/opsput_lan.h | 56 + include/asm-m32r/opsput/opsput_lcd.h | 59 + include/asm-m32r/opsput/opsput_pld.h | 259 + include/asm-m32r/page.h | 112 + include/asm-m32r/param.h | 27 + include/asm-m32r/pci.h | 10 + include/asm-m32r/percpu.h | 6 + include/asm-m32r/pgalloc.h | 87 + include/asm-m32r/pgtable-2level.h | 77 + include/asm-m32r/pgtable.h | 422 + include/asm-m32r/poll.h | 31 + include/asm-m32r/posix_types.h | 126 + include/asm-m32r/processor.h | 157 + include/asm-m32r/ptrace.h | 165 + include/asm-m32r/resource.h | 55 + include/asm-m32r/rtc.h | 70 + include/asm-m32r/scatterlist.h | 18 + include/asm-m32r/sections.h | 8 + include/asm-m32r/segment.h | 14 + include/asm-m32r/semaphore.h | 214 + include/asm-m32r/sembuf.h | 29 + include/asm-m32r/serial.h | 151 + include/asm-m32r/setup.h | 33 + include/asm-m32r/shmbuf.h | 46 + include/asm-m32r/shmparam.h | 8 + include/asm-m32r/sigcontext.h | 50 + include/asm-m32r/siginfo.h | 8 + include/asm-m32r/signal.h | 200 + include/asm-m32r/smp.h | 119 + include/asm-m32r/socket.h | 50 + include/asm-m32r/sockios.h | 14 + include/asm-m32r/spinlock.h | 382 + include/asm-m32r/stat.h | 91 + include/asm-m32r/statfs.h | 6 + include/asm-m32r/string.h | 15 + include/asm-m32r/syscall.h | 11 + include/asm-m32r/system.h | 301 + include/asm-m32r/termbits.h | 175 + include/asm-m32r/termios.h | 109 + include/asm-m32r/thread_info.h | 149 + include/asm-m32r/timex.h | 36 + include/asm-m32r/tlb.h | 20 + include/asm-m32r/tlbflush.h | 102 + include/asm-m32r/topology.h | 53 + include/asm-m32r/types.h | 64 + include/asm-m32r/uaccess.h | 752 + include/asm-m32r/ucontext.h | 14 + include/asm-m32r/unaligned.h | 25 + include/asm-m32r/unistd.h | 483 + include/asm-m32r/user.h | 59 + include/asm-m32r/vga.h | 22 + include/asm-m32r/xor.h | 8 + include/asm-m68k/bug.h | 17 +- include/asm-m68k/hardirq.h | 36 - include/asm-m68k/ptrace.h | 1 + include/asm-m68k/semaphore.h | 33 +- include/asm-m68k/socket.h | 16 - include/asm-m68k/system.h | 2 +- include/asm-m68knommu/bug.h | 22 +- include/asm-m68knommu/hardirq.h | 46 - include/asm-m68knommu/ptrace.h | 1 + include/asm-m68knommu/semaphore.h | 33 +- include/asm-mips/bug.h | 10 +- include/asm-mips/dma-mapping.h | 1 - include/asm-mips/hardirq.h | 40 - include/asm-mips/siginfo.h | 2 +- include/asm-mips/socket.h | 41 +- include/asm-parisc/bug.h | 27 +- include/asm-parisc/hardirq.h | 42 - include/asm-parisc/ptrace.h | 1 + include/asm-parisc/semaphore.h | 31 +- include/asm-parisc/socket.h | 14 - include/asm-ppc/8253pit.h | 10 + include/asm-ppc/bug.h | 7 +- include/asm-ppc/checksum.h | 2 +- include/asm-ppc/fsl_ocp.h | 2 +- include/asm-ppc/hardirq.h | 44 - include/asm-ppc/kexec.h | 26 + include/asm-ppc/mpc52xx.h | 374 +- include/asm-ppc/mpc52xx_psc.h | 162 +- include/asm-ppc/open_pic.h | 2 +- include/asm-ppc/residual.h | 16 + include/asm-ppc/semaphore.h | 36 +- include/asm-ppc/socket.h | 16 - include/asm-ppc/thread_info.h | 10 - include/asm-ppc64/8253pit.h | 10 + include/asm-ppc64/btext.h | 19 +- include/asm-ppc64/bug.h | 8 +- include/asm-ppc64/checksum.h | 4 +- include/asm-ppc64/dma-mapping.h | 1 - include/asm-ppc64/hvcall.h | 8 +- include/asm-ppc64/hvconsole.h | 18 +- include/asm-ppc64/hvcserver.h | 25 +- include/asm-ppc64/iSeries/iSeries_io.h | 18 +- include/asm-ppc64/lmb.h | 10 +- include/asm-ppc64/memory.h | 10 - include/asm-ppc64/pci-bridge.h | 9 +- include/asm-ppc64/plpar_wrappers.h | 109 + include/asm-ppc64/semaphore.h | 36 +- include/asm-ppc64/socket.h | 16 - include/asm-ppc64/tlb.h | 17 +- include/asm-s390/bug.h | 19 +- include/asm-s390/dma-mapping.h | 13 +- include/asm-s390/hardirq.h | 43 +- include/asm-s390/idals.h | 2 +- include/asm-s390/irq.h | 2 +- include/asm-s390/lowcore.h | 8 +- include/asm-s390/ptrace.h | 1 + include/asm-s390/socket.h | 16 - include/asm-s390/timer.h | 2 - include/asm-sh/bug.h | 17 +- include/asm-sh/cacheflush.h | 14 +- include/asm-sh/hardirq.h | 41 +- include/asm-sh/ptrace.h | 9 + include/asm-sh/semaphore.h | 35 +- include/asm-sh/socket.h | 16 - include/asm-sh64/cacheflush.h | 14 +- include/asm-sh64/dma-mapping.h | 1 - include/asm-sh64/ptrace.h | 1 + include/asm-sh64/semaphore.h | 35 +- include/asm-sparc/dma-mapping.h | 1 - include/asm-sparc/ptrace.h | 1 + include/asm-sparc/sigcontext.h | 14 - include/asm-sparc/socket.h | 16 - include/asm-sparc/vaddrs.h | 4 +- include/asm-sparc64/delay.h | 69 +- include/asm-sparc64/dma.h | 2 +- include/asm-sparc64/ebus.h | 2 +- include/asm-sparc64/elf.h | 4 +- include/asm-sparc64/kdebug.h | 49 +- include/asm-sparc64/kprobes.h | 24 + include/asm-sparc64/parport.h | 6 +- include/asm-sparc64/pbm.h | 9 + include/asm-sparc64/ptrace.h | 5 + include/asm-sparc64/sigcontext.h | 12 - include/asm-sparc64/socket.h | 16 - include/asm-sparc64/string.h | 109 +- include/asm-sparc64/ttable.h | 6 + include/asm-sparc64/vga.h | 3 + include/asm-um/ptrace-generic.h | 2 + include/asm-v850/bug.h | 12 +- include/asm-v850/hardirq.h | 40 - include/asm-v850/ptrace.h | 1 + include/asm-v850/socket.h | 16 - include/asm-x86_64/apic.h | 28 +- include/asm-x86_64/apicdef.h | 2 +- include/asm-x86_64/atomic.h | 160 + include/asm-x86_64/bug.h | 25 +- include/asm-x86_64/cpufeature.h | 15 +- include/asm-x86_64/crash.h | 75 + include/asm-x86_64/desc.h | 4 +- include/asm-x86_64/dma-mapping.h | 134 +- include/asm-x86_64/elf.h | 7 + include/asm-x86_64/hardirq.h | 43 +- include/asm-x86_64/kexec.h | 25 + include/asm-x86_64/mtrr.h | 2 - include/asm-x86_64/pci.h | 253 +- include/asm-x86_64/proto.h | 4 +- include/asm-x86_64/socket.h | 16 - include/asm-x86_64/swiotlb.h | 36 + include/asm-x86_64/system.h | 8 +- include/linux/.cvsignore | 3 + include/linux/adfs_fs.h | 16 +- include/linux/adfs_fs_i.h | 2 +- include/linux/adfs_fs_sb.h | 2 +- include/linux/affs_hardblocks.h | 14 +- include/linux/amigaffs.h | 112 +- include/linux/arcdevice.h | 2 +- include/linux/atalk.h | 29 +- include/linux/byteorder/big_endian.h | 118 +- include/linux/byteorder/generic.h | 8 +- include/linux/byteorder/little_endian.h | 118 +- include/linux/ckrm-io.h | 1 + include/linux/ckrm_classqueue.h | 4 +- include/linux/ckrm_sched.h | 21 +- include/linux/coda.h | 10 +- include/linux/cyclades.h | 10 +- include/linux/dqblk_xfs.h | 63 +- include/linux/efs_dir.h | 4 +- include/linux/efs_fs.h | 4 +- include/linux/efs_fs_i.h | 24 +- include/linux/efs_fs_sb.h | 52 +- include/linux/efs_vh.h | 20 +- include/linux/err.h | 4 +- include/linux/fs.h | 9 + include/linux/gen_stats.h | 62 + include/linux/genhd.h | 111 +- include/linux/hardirq.h | 46 + include/linux/hdreg.h | 5 +- include/linux/hiddev.h | 8 +- include/linux/highuid.h | 8 +- include/linux/hpet.h | 4 +- include/linux/i2c-algo-pca.h | 17 + include/linux/i2c-vid.h | 5 +- include/linux/if_ether.h | 14 +- include/linux/if_fddi.h | 36 +- include/linux/if_tr.h | 9 + include/linux/if_vlan.h | 12 +- include/linux/input.h | 2 + include/linux/ioctl32.h | 25 +- include/linux/ip6_tunnel.h | 2 + include/linux/ipv6.h | 11 +- include/linux/isicom.h | 1 - include/linux/kexec.h | 56 + include/linux/kmod.h | 1 + include/linux/kprobes.h | 134 + include/linux/kref.h | 14 +- include/linux/linkage.h | 4 + include/linux/list.h | 42 +- include/linux/lockd/bind.h | 2 +- include/linux/lockd/lockd.h | 23 +- include/linux/mempolicy.h | 3 - include/linux/mii.h | 3 +- include/linux/mm.h | 2 +- include/linux/mman.h | 3 + include/linux/mmc/card.h | 92 + include/linux/mmc/host.h | 108 + include/linux/mmc/mmc.h | 98 + include/linux/mmc/protocol.h | 203 + include/linux/mmtimer.h | 56 + include/linux/msdos_fs.h | 57 +- include/linux/mv643xx.h | 1039 + include/linux/ncp.h | 92 +- include/linux/ncp_fs_i.h | 8 +- include/linux/ncp_no.h | 26 +- include/linux/netfilter_bridge/ebt_802_3.h | 9 + include/linux/netfilter_ipv4/ip_conntrack.h | 25 +- .../linux/netfilter_ipv4/ip_conntrack_core.h | 32 +- .../linux/netfilter_ipv4/ip_conntrack_ftp.h | 5 - .../linux/netfilter_ipv4/ip_conntrack_irc.h | 5 - .../netfilter_ipv4/ip_conntrack_protocol.h | 43 +- .../linux/netfilter_ipv4/ip_conntrack_sctp.h | 25 + .../linux/netfilter_ipv4/ip_conntrack_tcp.h | 37 +- .../linux/netfilter_ipv4/ip_conntrack_tuple.h | 22 +- include/linux/netfilter_ipv4/ip_nat.h | 15 +- include/linux/netfilter_ipv4/ip_nat_core.h | 6 - include/linux/netfilter_ipv4/ip_nat_helper.h | 8 +- .../linux/netfilter_ipv4/ip_nat_protocol.h | 16 +- include/linux/netfilter_ipv4/ipt_comment.h | 10 + include/linux/netfilter_ipv4/ipt_sctp.h | 107 + include/linux/netfilter_ipv6.h | 2 + include/linux/netfilter_ipv6/ip6t_physdev.h | 24 + include/linux/nfs.h | 17 + include/linux/nfs4.h | 62 +- include/linux/nfs4_acl.h | 59 + include/linux/nfs_fs_i.h | 4 +- include/linux/nfs_fs_sb.h | 1 + include/linux/nfs_page.h | 37 +- include/linux/nfs_xdr.h | 76 +- include/linux/nls.h | 25 + include/linux/node.h | 1 - include/linux/notifier.h | 4 + include/linux/pci-acpi.h | 61 + include/linux/percpu.h | 26 +- include/linux/pfkeyv2.h | 1 + include/linux/pid.h | 43 +- include/linux/prio_tree.h | 12 + include/linux/profile.h | 58 +- include/linux/raid/md.h | 2 - include/linux/raid/md_k.h | 11 +- include/linux/raid/raid10.h | 103 + include/linux/ramfs.h | 11 + include/linux/rbtree.h | 1 + include/linux/reboot.h | 2 + include/linux/romfs_fs.h | 18 +- include/linux/skbuff.h | 14 + include/linux/socket.h | 4 + include/linux/stddef.h | 6 + include/linux/sunrpc/auth_gss.h | 2 - include/linux/sunrpc/gss_spkm3.h | 61 + include/linux/sunrpc/sched.h | 2 + include/linux/sunrpc/svc.h | 10 + include/linux/sysv_fs.h | 131 +- include/linux/tc_act/tc_gact.h | 34 + include/linux/timex.h | 121 +- include/linux/topology.h | 2 +- include/linux/udf_fs_i.h | 2 +- include/linux/ufs_fs.h | 539 +- include/linux/ufs_fs_i.h | 4 +- include/linux/umem.h | 14 +- include/linux/usb_ch9.h | 8 +- include/linux/usb_otg.h | 118 + include/linux/vs_base.h | 50 +- include/linux/vs_cvirt.h | 67 +- include/linux/vs_limit.h | 75 +- include/linux/vs_memory.h | 110 +- include/linux/vs_socket.h | 8 +- include/linux/vserver.h | 4 - include/linux/vserver/cvirt.h | 79 +- include/linux/vserver/debug.h | 49 +- include/linux/vserver/legacy.h | 6 +- include/linux/vserver/limit.h | 23 +- include/linux/vserver/namespace.h | 7 +- include/linux/vserver/signal.h | 10 +- include/linux/vserver/switch.h | 28 +- include/linux/vserver/xid.h | 4 +- include/linux/workqueue.h | 2 + include/math-emu/op-common.h | 6 +- include/media/id.h | 1 - include/media/tuner.h | 17 + include/net/af_unix.h | 7 +- include/net/dn_neigh.h | 1 - include/net/gen_stats.h | 45 + include/net/if_inet6.h | 2 + include/net/inet_ecn.h | 43 +- include/net/ip_fib.h | 39 +- include/net/ipcomp.h | 3 +- include/net/llc_pdu.h | 10 +- include/net/neighbour.h | 100 +- include/net/pkt_act.h | 13 +- include/net/tc_act/tc_gact.h | 17 + include/net/tcp_ecn.h | 2 +- include/rxrpc/connection.h | 12 +- include/rxrpc/packet.h | 26 +- include/rxrpc/peer.h | 2 +- include/rxrpc/rxrpc.h | 9 +- include/rxrpc/transport.h | 2 - include/rxrpc/types.h | 2 + include/scsi/scsi_driver.h | 1 + include/sound/asequencer.h | 4 +- include/sound/control.h | 3 +- include/sound/driver.h | 2 - include/sound/es1688.h | 2 - include/sound/info.h | 5 +- include/sound/memalloc.h | 21 +- include/sound/pcm-indirect.h | 177 + include/sound/pcm.h | 58 +- include/sound/pcm_oss.h | 10 +- include/sound/seq_kernel.h | 7 +- include/sound/soundfont.h | 1 + include/sound/vx_core.h | 8 + include/video/epson1355.h | 64 + include/video/tdfx.h | 1 + include/video/vga.h | 46 +- init/Kconfig | 12 + kernel/.cvsignore | 2 + kernel/Makefile | 1 + kernel/audit.c | 15 +- kernel/capability.c | 6 +- kernel/ckrm/Makefile | 2 +- kernel/ckrm/ckrm.c | 31 +- kernel/ckrm/ckrm_cpu_class.c | 56 +- kernel/ckrm/ckrm_cpu_monitor.c | 9 +- kernel/ckrm/ckrm_laq.c | 495 - kernel/ckrm/ckrm_listenaq.c | 12 +- kernel/ckrm/ckrmutils.c | 26 +- kernel/ckrm_classqueue.c | 2 +- kernel/ckrm_sched.c | 9 +- kernel/exec_domain.c | 121 +- kernel/kexec.c | 640 + kernel/kprobes.c | 149 + kernel/module-verify-sig.c | 442 + kernel/module-verify.c | 367 +- kernel/module-verify.h | 30 +- kernel/power/Kconfig | 42 +- kernel/power/console.c | 3 + kernel/power/disk.c | 85 +- kernel/power/main.c | 17 +- kernel/power/power.h | 19 +- kernel/profile.c | 241 +- kernel/ptrace.c | 63 +- kernel/resource.c | 22 +- kernel/sched.c | 190 +- kernel/softirq.c | 7 +- kernel/spinlock.c | 308 + kernel/sys.c | 21 + kernel/vserver/Kconfig | 10 + kernel/vserver/cvirt.c | 48 +- kernel/vserver/helper.c | 10 +- kernel/vserver/legacy.c | 10 +- kernel/vserver/limit.c | 16 +- kernel/vserver/namespace.c | 54 +- kernel/vserver/proc.c | 89 +- kernel/vserver/sched.c | 72 +- kernel/vserver/signal.c | 51 +- kernel/vserver/switch.c | 16 +- lib/.cvsignore | 2 + lib/Kconfig.debug | 100 + lib/iomap.c | 206 + lib/kref.c | 29 +- lib/zlib_inflate/Makefile | 2 +- lib/zlib_inflate/infblock.c | 2 +- lib/zlib_inflate/inflate.c | 143 - lib/zlib_inflate/inflate_sync.c | 148 + lib/zlib_inflate/inftrees.h | 1 + mm/mempolicy.c | 80 +- mm/prio_tree.c | 69 +- mm/tiny-shmem.c | 124 + net/8021q/vlan.c | 2 +- net/appletalk/Makefile | 3 +- net/appletalk/atalk_proc.c | 13 - net/appletalk/sysctl_net_atalk.c | 18 +- net/atm/common.h | 11 - net/atm/ipcommon.h | 3 - net/atm/lec.h | 1 - net/ax25/ax25_in.c | 1 - net/bluetooth/bnep/core.c | 2 +- net/bridge/br_input.c | 62 +- net/bridge/br_netfilter.c | 447 +- net/bridge/netfilter/ebt_802_3.c | 2 +- net/bridge/netfilter/ebt_among.c | 61 +- net/bridge/netfilter/ebt_arp.c | 55 +- net/bridge/netfilter/ebt_arpreply.c | 34 +- net/bridge/netfilter/ebt_dnat.c | 3 +- net/bridge/netfilter/ebt_ip.c | 33 +- net/bridge/netfilter/ebt_log.c | 66 +- net/bridge/netfilter/ebt_redirect.c | 7 +- net/bridge/netfilter/ebt_snat.c | 3 +- net/bridge/netfilter/ebt_stp.c | 22 +- net/bridge/netfilter/ebt_vlan.c | 16 +- net/core/dv.c | 7 +- net/core/filter.c | 21 +- net/core/gen_estimator.c | 204 + net/core/gen_stats.c | 132 + net/core/neighbour.c | 886 +- net/core/scm.c | 4 +- net/core/utils.c | 112 +- net/decnet/dn_neigh.c | 225 +- net/ipv4/fib_lookup.h | 34 + net/ipv4/fib_rules.c | 25 +- net/ipv4/fib_semantics.c | 407 +- net/ipv4/ipvs/ip_vs_core.c | 118 +- net/ipv4/ipvs/ip_vs_proto.c | 42 +- net/ipv4/ipvs/ip_vs_proto_ah.c | 9 +- net/ipv4/ipvs/ip_vs_proto_esp.c | 9 +- net/ipv4/ipvs/ip_vs_proto_icmp.c | 35 +- net/ipv4/ipvs/ip_vs_proto_tcp.c | 44 +- net/ipv4/ipvs/ip_vs_proto_udp.c | 42 +- net/ipv4/netfilter/Kconfig | 45 + net/ipv4/netfilter/Makefile | 8 + net/ipv4/netfilter/ip_conntrack_amanda.c | 2 +- net/ipv4/netfilter/ip_conntrack_core.c | 42 +- net/ipv4/netfilter/ip_conntrack_ftp.c | 46 +- net/ipv4/netfilter/ip_conntrack_irc.c | 47 +- .../netfilter/ip_conntrack_proto_generic.c | 4 +- net/ipv4/netfilter/ip_conntrack_proto_icmp.c | 2 +- net/ipv4/netfilter/ip_conntrack_proto_sctp.c | 656 + net/ipv4/netfilter/ip_conntrack_proto_tcp.c | 2 +- net/ipv4/netfilter/ip_conntrack_proto_udp.c | 7 +- net/ipv4/netfilter/ip_conntrack_standalone.c | 17 +- net/ipv4/netfilter/ip_conntrack_tftp.c | 20 +- net/ipv4/netfilter/ip_nat_core.c | 6 +- net/ipv4/netfilter/ip_nat_ftp.c | 52 +- net/ipv4/netfilter/ip_nat_helper.c | 30 +- net/ipv4/netfilter/ip_nat_irc.c | 45 +- net/ipv4/netfilter/ip_nat_proto_icmp.c | 2 +- net/ipv4/netfilter/ip_nat_proto_tcp.c | 2 +- net/ipv4/netfilter/ip_nat_proto_udp.c | 2 +- net/ipv4/netfilter/ip_nat_proto_unknown.c | 4 +- net/ipv4/netfilter/ip_nat_rule.c | 3 +- net/ipv4/netfilter/ip_nat_tftp.c | 35 +- net/ipv4/netfilter/ip_queue.c | 2 + net/ipv4/netfilter/ipchains_core.c | 44 +- net/ipv4/netfilter/ipfwadm_core.c | 44 +- net/ipv4/netfilter/ipt_ECN.c | 29 +- net/ipv4/netfilter/ipt_NOTRACK.c | 3 +- net/ipv4/netfilter/ipt_ah.c | 11 +- net/ipv4/netfilter/ipt_comment.c | 59 + net/ipv4/netfilter/ipt_conntrack.c | 2 +- net/ipv4/netfilter/ipt_ecn.c | 17 +- net/ipv4/netfilter/ipt_esp.c | 11 +- net/ipv4/netfilter/ipt_mac.c | 2 +- net/ipv4/netfilter/ipt_multiport.c | 19 +- net/ipv4/netfilter/ipt_recent.c | 11 +- net/ipv4/netfilter/ipt_sctp.c | 203 + net/ipv4/netfilter/ipt_state.c | 4 +- net/ipv4/netfilter/ipt_tcpmss.c | 30 +- net/ipv4/netfilter/iptable_filter.c | 3 +- net/ipv4/tcp_diag.c | 77 +- net/ipv4/tcp_ipv4.c | 3 +- net/ipv4/udp.c | 80 +- net/ipv4/xfrm4_input.c | 5 +- net/ipv4/xfrm4_output.c | 6 +- net/ipv6/Kconfig | 13 + net/ipv6/datagram.c | 13 +- net/ipv6/exthdrs_core.c | 27 +- net/ipv6/ip6_fib.c | 29 +- net/ipv6/netfilter/Kconfig | 9 + net/ipv6/netfilter/Makefile | 1 + net/ipv6/netfilter/ip6t_eui64.c | 6 +- net/ipv6/netfilter/ip6t_mac.c | 2 +- net/ipv6/netfilter/ip6t_physdev.c | 136 + net/ipv6/netfilter/ip6table_filter.c | 3 +- net/ipv6/xfrm6_input.c | 21 +- net/ipv6/xfrm6_output.c | 16 +- net/ipv6/xfrm6_tunnel.c | 72 +- net/irda/irlan/irlan_common.c | 5 +- net/irda/irlan/irlan_eth.c | 10 +- net/irda/irqueue.c | 8 +- net/llc/llc_input.c | 2 +- net/llc/llc_output.c | 7 +- net/rxrpc/main.c | 2 +- net/rxrpc/peer.c | 4 +- net/sched/act_api.c | 10 +- net/sched/gact.c | 265 + net/sched/sch_dsmark.c | 2 - net/sched/sch_netem.c | 810 +- net/sched/sch_sfq.c | 1 - net/sched/sch_teql.c | 3 +- net/sunrpc/auth_gss/gss_spkm3_mech.c | 296 + net/sunrpc/auth_gss/gss_spkm3_seal.c | 132 + net/sunrpc/auth_gss/gss_spkm3_token.c | 266 + net/sunrpc/auth_gss/gss_spkm3_unseal.c | 128 + net/sunrpc/sched.c | 4 +- net/sunrpc/sunrpc_syms.c | 1 + net/sunrpc/svc.c | 9 +- net/sunrpc/svcauth.c | 62 +- net/sunrpc/svcauth_unix.c | 32 +- net/unix/af_unix.c | 39 +- scripts/.cvsignore | 4 + scripts/Makefile.build | 121 +- scripts/Makefile.clean | 44 +- scripts/Makefile.host | 155 + scripts/Makefile.lib | 52 +- scripts/basic/.cvsignore | 3 + scripts/basic/Makefile | 4 +- scripts/binoffset.c | 12 +- scripts/genksyms/Makefile | 4 +- scripts/genksyms/genksyms.c | 15 + scripts/kconfig/.cvsignore | 5 + scripts/kconfig/confdata.c | 17 +- scripts/kconfig/gconf.c | 5 +- scripts/kconfig/lkc.h | 3 - scripts/kconfig/menu.c | 1 - scripts/kconfig/zconf.tab.c_shipped | 2 + scripts/kconfig/zconf.y | 2 + scripts/kernel-2.6-planetlab.spec | 2 +- scripts/lxdialog/.cvsignore | 1 + scripts/lxdialog/Makefile | 4 +- scripts/lxdialog/menubox.c | 9 + scripts/mksysmap | 44 + scripts/mod/.cvsignore | 3 + scripts/mod/Makefile | 4 +- scripts/mod/modpost.c | 36 +- scripts/mod/modpost.h | 2 + scripts/mod/sumversion.c | 4 + scripts/modsign/mod-extract.c | 929 +- scripts/modsign/modsign.sh | 48 +- scripts/namespace.pl | 449 + scripts/package/Makefile | 6 +- scripts/package/builddeb | 2 +- scripts/package/mkspec | 20 +- scripts/patch-kernel | 113 +- scripts/reference_discarded.pl | 1 + scripts/ver_linux | 7 +- security/Kconfig | 4 +- security/security.c | 4 +- security/selinux/ss/avtab.c | 12 +- security/selinux/ss/avtab.h | 2 + security/selinux/ss/policydb.h | 1 - sound/core/device.c | 4 +- sound/core/hwdep.c | 40 +- sound/core/info.c | 63 +- sound/core/info_oss.c | 12 - sound/core/memory.c | 76 +- sound/core/oss/mixer_oss.c | 53 +- sound/core/oss/pcm_plugin.h | 2 +- sound/core/pcm.c | 23 +- sound/core/pcm_timer.c | 13 +- sound/core/seq/instr/ainstr_fm.c | 2 - sound/core/seq/instr/ainstr_gf1.c | 4 +- sound/core/seq/instr/ainstr_iw.c | 8 +- sound/core/seq/instr/ainstr_simple.c | 2 - sound/core/seq/oss/seq_oss_ioctl.c | 74 +- sound/core/seq/oss/seq_oss_midi.c | 2 +- sound/core/seq/oss/seq_oss_readq.c | 49 +- sound/core/seq/oss/seq_oss_readq.h | 9 +- sound/core/seq/oss/seq_oss_rw.c | 116 +- sound/core/seq/oss/seq_oss_synth.c | 8 +- sound/core/seq/oss/seq_oss_timer.c | 4 +- sound/core/seq/oss/seq_oss_writeq.c | 2 +- sound/core/seq/seq_device.c | 14 +- sound/core/seq/seq_fifo.c | 2 +- sound/core/seq/seq_instr.c | 11 +- sound/core/seq/seq_memory.c | 6 +- sound/core/seq/seq_midi_emul.c | 2 - sound/core/seq/seq_midi_event.c | 2 +- sound/core/seq/seq_ports.c | 4 +- sound/core/seq/seq_prioq.c | 2 +- sound/core/seq/seq_queue.c | 2 +- sound/core/seq/seq_timer.c | 2 +- sound/core/seq/seq_virmidi.c | 46 +- sound/core/sgbuf.c | 11 +- sound/drivers/mpu401/mpu401_uart.c | 22 +- sound/drivers/opl3/opl3_midi.c | 14 +- sound/drivers/opl3/opl3_synth.c | 6 +- sound/drivers/opl4/Makefile | 2 +- sound/drivers/opl4/opl4_lib.c | 59 +- sound/drivers/opl4/opl4_local.h | 1 - sound/drivers/opl4/opl4_mixer.c | 12 +- sound/drivers/opl4/opl4_proc.c | 8 +- sound/drivers/opl4/opl4_synth.c | 35 +- sound/drivers/vx/vx_core.c | 11 +- sound/drivers/vx/vx_hwdep.c | 4 +- sound/drivers/vx/vx_mixer.c | 53 +- sound/drivers/vx/vx_pcm.c | 37 +- sound/drivers/vx/vx_uer.c | 15 +- sound/i2c/cs8427.c | 26 +- sound/i2c/i2c.c | 10 +- sound/i2c/l3/uda1341.c | 24 +- sound/i2c/other/ak4117.c | 10 +- sound/i2c/other/ak4xxx-adda.c | 12 +- sound/i2c/tea6330t.c | 14 +- sound/isa/ad1816a/ad1816a_lib.c | 18 +- sound/isa/cs423x/cs4236_lib.c | 26 +- sound/isa/es1688/es1688_lib.c | 15 +- sound/isa/gus/Makefile | 6 +- sound/isa/gus/gus_dram.c | 10 +- sound/isa/gus/gus_instr.c | 18 +- sound/isa/gus/gus_irq.c | 4 +- sound/isa/gus/gus_main.c | 10 +- sound/isa/gus/gus_mem.c | 2 +- sound/isa/gus/gus_mem_proc.c | 12 +- sound/isa/gus/gus_mixer.c | 10 +- sound/isa/gus/gus_pcm.c | 38 +- sound/isa/gus/gus_synth.c | 2 +- sound/isa/gus/gus_timer.c | 6 +- sound/isa/gus/gus_uart.c | 12 +- sound/isa/sb/emu8000.c | 18 +- sound/isa/sb/emu8000_callback.c | 20 +- sound/isa/sb/emu8000_local.h | 2 - sound/isa/sb/emu8000_pcm.c | 4 +- sound/isa/sb/emu8000_synth.c | 1 - sound/isa/sb/sb16_csp.c | 17 +- sound/isa/sb/sb16_main.c | 18 +- sound/isa/sb/sb8_main.c | 2 - sound/isa/sb/sb8_midi.c | 16 +- sound/isa/sb/sb_common.c | 9 +- sound/isa/sb/sb_mixer.c | 2 - sound/oss/ad1848.h | 8 +- sound/oss/ad1889.h | 6 +- sound/oss/ali5455.c | 1 - sound/oss/au1000.c | 2 +- sound/oss/btaudio.c | 4 +- sound/oss/cs4232.c | 46 +- sound/oss/cs4232.h | 2 - sound/oss/cs46xx.c | 13 +- sound/oss/dmasound/Kconfig | 2 +- sound/oss/emu10k1/midi.c | 3 +- sound/oss/forte.c | 1 - sound/oss/gus_card.c | 24 +- sound/oss/harmony.c | 138 +- sound/oss/ite8172.c | 2 +- sound/oss/mad16.c | 220 +- sound/oss/mpu401.h | 2 +- sound/oss/msnd.h | 9 +- sound/oss/msnd_pinnacle.c | 42 +- sound/oss/nec_vrc5477.c | 1 - sound/oss/opl3sa.c | 59 +- sound/oss/rme96xx.c | 2 +- sound/oss/sb_common.c | 24 +- sound/oss/sgalaxy.c | 42 +- sound/oss/swarm_cs4297a.c | 2 +- sound/oss/trix.c | 221 +- sound/pci/Makefile | 2 + sound/pci/ac97/ac97_id.h | 7 + sound/pci/ac97/ac97_patch.h | 1 + sound/pci/ac97/ak4531_codec.c | 26 +- sound/pci/atiixp_modem.c | 1351 + sound/pci/au88x0/au88x0_mixer.c | 13 +- sound/pci/au88x0/au88x0_mpu401.c | 2 +- sound/pci/au88x0/au88x0_xtalk.c | 6 + sound/pci/au88x0/au88x0_xtalk.h | 3 +- sound/pci/cs46xx/cs46xx_lib.h | 2 - sound/pci/cs46xx/dsp_spos.c | 41 +- sound/pci/emu10k1/emu10k1_callback.c | 16 +- sound/pci/emu10k1/emu10k1_patch.c | 4 +- sound/pci/emu10k1/emu10k1_synth.c | 4 +- sound/pci/emu10k1/emupcm.c | 321 +- sound/pci/emu10k1/emuproc.c | 126 +- sound/pci/emu10k1/io.c | 4 +- sound/pci/emu10k1/irq.c | 2 +- sound/pci/emu10k1/memory.c | 14 +- sound/pci/ice1712/ak4xxx.c | 1 - sound/pci/ice1712/ews.h | 4 +- sound/pci/ice1712/pontis.c | 849 + sound/pci/ice1712/pontis.h | 33 + sound/pci/ice1712/vt1720_mobo.c | 106 + sound/pci/ice1712/vt1720_mobo.h | 37 + sound/pci/mixart/mixart.h | 1 - sound/pci/mixart/mixart_core.c | 4 +- sound/pci/mixart/mixart_hwdep.c | 4 +- sound/pci/mixart/mixart_mixer.c | 2 - sound/pci/trident/trident_memory.c | 14 +- sound/pci/trident/trident_synth.c | 33 +- sound/pci/vx222/vx222.h | 2 +- sound/pci/vx222/vx222_ops.c | 2 - sound/pcmcia/pdaudiocf/pdaudiocf_pcm.c | 4 +- sound/pcmcia/vx/vx_entry.c | 10 +- sound/pcmcia/vx/vxp_mixer.c | 2 - sound/pcmcia/vx/vxp_ops.c | 2 - sound/ppc/Kconfig | 5 +- sound/ppc/Makefile | 2 +- sound/ppc/awacs.c | 35 +- sound/ppc/beep.c | 262 + sound/ppc/burgundy.c | 6 +- sound/ppc/daca.c | 6 +- sound/synth/emux/emux.c | 6 +- sound/synth/emux/emux_effect.c | 2 +- sound/synth/emux/emux_hwdep.c | 2 +- sound/synth/emux/emux_nrpn.c | 18 +- sound/synth/emux/emux_proc.c | 2 +- sound/synth/emux/emux_seq.c | 20 +- sound/synth/emux/emux_synth.c | 12 +- sound/synth/emux/soundfont.c | 60 +- sound/synth/util_mem.c | 2 +- sound/usb/Kconfig | 10 + sound/usb/Makefile | 8 +- sound/usb/usbmidi.c | 86 +- sound/usb/usbmixer_maps.c | 2 +- sound/usb/usx2y/Makefile | 3 + sound/usb/usx2y/usX2Yhwdep.c | 282 + sound/usb/usx2y/usX2Yhwdep.h | 6 + sound/usb/usx2y/usbus428ctldefs.h | 108 + sound/usb/usx2y/usbusx2y.c | 434 + sound/usb/usx2y/usbusx2y.h | 61 + sound/usb/usx2y/usbusx2yaudio.c | 1027 + sound/usb/usx2y/usx2y.h | 49 + usr/.cvsignore | 3 + usr/Makefile | 2 +- 2498 files changed, 621028 insertions(+), 29771 deletions(-) create mode 100644 .cvsignore create mode 100644 Documentation/ManagementStyle create mode 100644 Documentation/RCU/RTFP.txt create mode 100644 Documentation/RCU/UP.txt create mode 100644 Documentation/RCU/arrayRCU.txt create mode 100644 Documentation/RCU/checklist.txt create mode 100644 Documentation/RCU/listRCU.txt create mode 100644 Documentation/RCU/rcu.txt create mode 100644 Documentation/arm/IXP2000 create mode 100644 Documentation/arm/Samsung-S3C24XX/EB2410ITX.txt create mode 100644 Documentation/arm/Samsung-S3C24XX/GPIO.txt create mode 100644 Documentation/arm/Samsung-S3C24XX/Overview.txt create mode 100644 Documentation/ckrm/cpusched create mode 100644 Documentation/i2o/README create mode 100644 Documentation/i2o/ioctl create mode 100644 Documentation/networking/README.ipw2100 create mode 100644 Documentation/networking/README.ipw2200 create mode 100644 Documentation/networking/gen_stats.txt create mode 100644 Documentation/sched-stats.txt create mode 100644 Documentation/scsi/megaraid.txt create mode 100644 Documentation/time_interpolators.txt create mode 100644 Documentation/tty.txt create mode 100644 TAGS create mode 100644 arch/alpha/Kconfig.debug create mode 100644 arch/alpha/kernel/io.c create mode 100644 arch/arm/Kconfig.debug create mode 100644 arch/arm/boot/compressed/big-endian.S create mode 100644 arch/arm/configs/enp2611_defconfig create mode 100644 arch/arm/configs/ep80219_defconfig create mode 100644 arch/arm/configs/h7201_defconfig create mode 100644 arch/arm/configs/h7202_defconfig create mode 100644 arch/arm/configs/iq31244_defconfig create mode 100644 arch/arm/configs/iq80331_defconfig create mode 100644 arch/arm/configs/ixdp2400_defconfig create mode 100644 arch/arm/configs/ixdp2401_defconfig create mode 100644 arch/arm/configs/ixdp2800_defconfig create mode 100644 arch/arm/configs/ixdp2801_defconfig create mode 100644 arch/arm/configs/mx1ads_defconfig create mode 100644 arch/arm/kernel/iwmmxt.S create mode 100644 arch/arm/mach-h720x/Kconfig create mode 100644 arch/arm/mach-h720x/Makefile create mode 100644 arch/arm/mach-h720x/common.c create mode 100644 arch/arm/mach-h720x/cpu-h7201.c create mode 100644 arch/arm/mach-h720x/cpu-h7202.c create mode 100644 arch/arm/mach-h720x/h7201-eval.c create mode 100644 arch/arm/mach-h720x/h7202-eval.c create mode 100644 arch/arm/mach-imx/Kconfig create mode 100644 arch/arm/mach-imx/Makefile create mode 100644 arch/arm/mach-imx/dma.c create mode 100644 arch/arm/mach-imx/generic.c create mode 100644 arch/arm/mach-imx/generic.h create mode 100644 arch/arm/mach-imx/irq.c create mode 100644 arch/arm/mach-imx/leds-mx1ads.c create mode 100644 arch/arm/mach-imx/leds.c create mode 100644 arch/arm/mach-imx/leds.h create mode 100644 arch/arm/mach-imx/mx1ads.c create mode 100644 arch/arm/mach-imx/time.c create mode 100644 arch/arm/mach-iop3xx/common.c create mode 100644 arch/arm/mach-iop3xx/iop321-mm.c create mode 100644 arch/arm/mach-iop3xx/iop321-setup.c create mode 100644 arch/arm/mach-iop3xx/iop331-irq.c create mode 100644 arch/arm/mach-iop3xx/iop331-mm.c create mode 100644 arch/arm/mach-iop3xx/iop331-pci.c create mode 100644 arch/arm/mach-iop3xx/iop331-setup.c create mode 100644 arch/arm/mach-iop3xx/iop331-time.c create mode 100644 arch/arm/mach-iop3xx/iq31244-mm.c create mode 100644 arch/arm/mach-iop3xx/iq31244-pci.c create mode 100644 arch/arm/mach-iop3xx/iq80321-mm.c create mode 100644 arch/arm/mach-iop3xx/iq80331-mm.c create mode 100644 arch/arm/mach-iop3xx/iq80331-pci.c create mode 100644 arch/arm/mach-ixp2000/Kconfig create mode 100644 arch/arm/mach-ixp2000/Makefile create mode 100644 arch/arm/mach-ixp2000/core.c create mode 100644 arch/arm/mach-ixp2000/enp2611.c create mode 100644 arch/arm/mach-ixp2000/ixdp2400.c create mode 100644 arch/arm/mach-ixp2000/ixdp2800.c create mode 100644 arch/arm/mach-ixp2000/ixdp2x00.c create mode 100644 arch/arm/mach-ixp2000/ixdp2x01.c create mode 100644 arch/arm/mach-ixp2000/pci.c create mode 100644 arch/arm/mach-omap/board-h2.c create mode 100644 arch/arm/mach-omap/board-h3.c create mode 100644 arch/arm/mach-omap/leds-h2p2-debug.c create mode 100644 arch/arm/mach-omap/mcbsp.c create mode 100644 arch/arm/mach-omap/usb.c create mode 100644 arch/arm/mach-s3c2410/clock.c create mode 100644 arch/arm/mach-s3c2410/clock.h create mode 100644 arch/arm/mach-s3c2410/cpu.c create mode 100644 arch/arm/mach-s3c2410/cpu.h create mode 100644 arch/arm/mach-s3c2410/devs.c create mode 100644 arch/arm/mach-s3c2410/devs.h create mode 100644 arch/arm/mach-s3c2410/dma.c create mode 100644 arch/arm/mach-s3c2410/s3c2440-dsc.c create mode 100644 arch/arm/mach-s3c2410/s3c2440.c create mode 100644 arch/arm/mach-s3c2410/s3c2440.h create mode 100644 arch/arm/mach-s3c2410/usb-simtec.c create mode 100644 arch/arm/mach-s3c2410/usb-simtec.h create mode 100644 arch/arm/mm/flush.c create mode 100644 arch/arm26/Kconfig.debug create mode 100644 arch/cris/Kconfig.debug create mode 100644 arch/h8300/Kconfig.debug create mode 100644 arch/i386/Kconfig.debug create mode 100644 arch/i386/Kconfig.debug~ create mode 100644 arch/i386/boot/.cvsignore create mode 100644 arch/i386/boot/compressed/.cvsignore create mode 100644 arch/i386/boot/tools/.cvsignore create mode 100644 arch/i386/kernel/.cvsignore create mode 100644 arch/i386/kernel/cpu/intel_cacheinfo.c create mode 100644 arch/i386/kernel/kprobes.c create mode 100644 arch/i386/kernel/machine_kexec.c create mode 100644 arch/i386/kernel/relocate_kernel.S create mode 100644 arch/i386/kernel/vsyscall.lds.S create mode 100644 arch/ia64/Kconfig.debug create mode 100644 arch/ia64/configs/bigsur_defconfig create mode 100644 arch/ia64/configs/tiger_defconfig create mode 100644 arch/ia64/kernel/mca_drv.c create mode 100644 arch/ia64/kernel/mca_drv.h create mode 100644 arch/ia64/kernel/mca_drv_asm.S create mode 100644 arch/ia64/oprofile/perfmon.c create mode 100644 arch/ia64/sn/include/ioerror.h create mode 100644 arch/ia64/sn/include/pci/pcibr_provider.h create mode 100644 arch/ia64/sn/include/pci/pcibus_provider_defs.h create mode 100644 arch/ia64/sn/include/pci/pcidev.h create mode 100644 arch/ia64/sn/include/pci/pic.h create mode 100644 arch/ia64/sn/include/pci/tiocp.h create mode 100644 arch/ia64/sn/include/shub.h create mode 100644 arch/ia64/sn/include/shubio.h create mode 100644 arch/ia64/sn/include/tio.h create mode 100644 arch/ia64/sn/include/xtalk/hubdev.h create mode 100644 arch/ia64/sn/include/xtalk/xbow.h create mode 100644 arch/ia64/sn/include/xtalk/xwidgetdev.h create mode 100644 arch/ia64/sn/kernel/bte_error.c create mode 100644 arch/ia64/sn/kernel/huberror.c create mode 100644 arch/ia64/sn/kernel/io_init.c create mode 100644 arch/ia64/sn/kernel/iomv.c create mode 100644 arch/ia64/sn/kernel/klconflib.c create mode 100644 arch/ia64/sn/kernel/sn2/sn_hwperf.c create mode 100644 arch/ia64/sn/pci/Makefile create mode 100644 arch/ia64/sn/pci/pci_dma.c create mode 100644 arch/ia64/sn/pci/pcibr/Makefile create mode 100644 arch/ia64/sn/pci/pcibr/pcibr_ate.c create mode 100644 arch/ia64/sn/pci/pcibr/pcibr_dma.c create mode 100644 arch/ia64/sn/pci/pcibr/pcibr_provider.c create mode 100644 arch/ia64/sn/pci/pcibr/pcibr_reg.c create mode 100644 arch/m32r/Kconfig create mode 100644 arch/m32r/Makefile create mode 100644 arch/m32r/boot/Makefile create mode 100644 arch/m32r/boot/compressed/Makefile create mode 100644 arch/m32r/boot/compressed/boot.h create mode 100644 arch/m32r/boot/compressed/head.S create mode 100644 arch/m32r/boot/compressed/install.sh create mode 100644 arch/m32r/boot/compressed/m32r_sio.c create mode 100644 arch/m32r/boot/compressed/misc.c create mode 100644 arch/m32r/boot/compressed/vmlinux.lds.S create mode 100644 arch/m32r/boot/compressed/vmlinux.scr create mode 100644 arch/m32r/boot/setup.S create mode 100644 arch/m32r/defconfig create mode 100644 arch/m32r/kernel/Makefile create mode 100644 arch/m32r/kernel/align.c create mode 100644 arch/m32r/kernel/entry.S create mode 100644 arch/m32r/kernel/head.S create mode 100644 arch/m32r/kernel/init_task.c create mode 100644 arch/m32r/kernel/io_m32700ut.c create mode 100644 arch/m32r/kernel/io_mappi.c create mode 100644 arch/m32r/kernel/io_mappi2.c create mode 100644 arch/m32r/kernel/io_oaks32r.c create mode 100644 arch/m32r/kernel/io_opsput.c create mode 100644 arch/m32r/kernel/io_usrv.c create mode 100644 arch/m32r/kernel/irq.c create mode 100644 arch/m32r/kernel/m32r_ksyms.c create mode 100644 arch/m32r/kernel/module.c create mode 100644 arch/m32r/kernel/process.c create mode 100644 arch/m32r/kernel/ptrace.c create mode 100644 arch/m32r/kernel/semaphore.c create mode 100644 arch/m32r/kernel/setup.c create mode 100644 arch/m32r/kernel/setup_m32700ut.c create mode 100644 arch/m32r/kernel/setup_mappi.c create mode 100644 arch/m32r/kernel/setup_mappi2.c create mode 100644 arch/m32r/kernel/setup_oaks32r.c create mode 100644 arch/m32r/kernel/setup_opsput.c create mode 100644 arch/m32r/kernel/setup_usrv.c create mode 100644 arch/m32r/kernel/signal.c create mode 100644 arch/m32r/kernel/smp.c create mode 100644 arch/m32r/kernel/smpboot.c create mode 100644 arch/m32r/kernel/sys_m32r.c create mode 100644 arch/m32r/kernel/time.c create mode 100644 arch/m32r/kernel/traps.c create mode 100644 arch/m32r/kernel/vmlinux.lds.S create mode 100644 arch/m32r/lib/Makefile create mode 100644 arch/m32r/lib/ashxdi3.S create mode 100644 arch/m32r/lib/checksum.S create mode 100644 arch/m32r/lib/csum_partial_copy.c create mode 100644 arch/m32r/lib/delay.c create mode 100644 arch/m32r/lib/getuser.S create mode 100644 arch/m32r/lib/memcpy.S create mode 100644 arch/m32r/lib/memset.S create mode 100644 arch/m32r/lib/putuser.S create mode 100644 arch/m32r/lib/strlen.S create mode 100644 arch/m32r/lib/usercopy.c create mode 100644 arch/m32r/m32700ut/defconfig.m32700ut.smp create mode 100644 arch/m32r/m32700ut/defconfig.m32700ut.up create mode 100644 arch/m32r/m32700ut/dot.gdbinit_200MHz_16MB create mode 100644 arch/m32r/m32700ut/dot.gdbinit_300MHz_32MB create mode 100644 arch/m32r/mappi/defconfig.nommu create mode 100644 arch/m32r/mappi/defconfig.smp create mode 100644 arch/m32r/mappi/defconfig.up create mode 100644 arch/m32r/mappi/dot.gdbinit create mode 100644 arch/m32r/mappi/dot.gdbinit.nommu create mode 100644 arch/m32r/mappi/dot.gdbinit.smp create mode 100644 arch/m32r/mm/Makefile create mode 100644 arch/m32r/mm/cache.c create mode 100644 arch/m32r/mm/discontig.c create mode 100644 arch/m32r/mm/extable.c create mode 100644 arch/m32r/mm/fault-nommu.c create mode 100644 arch/m32r/mm/fault.c create mode 100644 arch/m32r/mm/init.c create mode 100644 arch/m32r/mm/ioremap-nommu.c create mode 100644 arch/m32r/mm/ioremap.c create mode 100644 arch/m32r/mm/mmu.S create mode 100644 arch/m32r/mm/page.S create mode 100644 arch/m32r/oaks32r/defconfig.nommu create mode 100644 arch/m32r/oaks32r/dot.gdbinit.nommu create mode 100644 arch/m32r/oprofile/Kconfig create mode 100644 arch/m32r/oprofile/Makefile create mode 100644 arch/m32r/oprofile/init.c create mode 100644 arch/m32r/opsput/defconfig.opsput create mode 100644 arch/m32r/opsput/dot.gdbinit create mode 100644 arch/m68k/Kconfig.debug create mode 100644 arch/m68knommu/Kconfig.debug create mode 100644 arch/mips/Kconfig.debug create mode 100644 arch/parisc/Kconfig.debug create mode 100644 arch/ppc/Kconfig.debug create mode 100644 arch/ppc/boot/include/serial.h create mode 100644 arch/ppc/boot/simple/chrpmap.c create mode 100644 arch/ppc/boot/simple/pibs.c create mode 100644 arch/ppc/boot/simple/prepmap.c create mode 100644 arch/ppc/kernel/head_booke.h create mode 100644 arch/ppc/kernel/machine_kexec.c create mode 100644 arch/ppc/kernel/relocate_kernel.S create mode 100644 arch/ppc/platforms/lopec.c create mode 100644 arch/ppc/platforms/lopec.h create mode 100644 arch/ppc/platforms/mvme5100.c create mode 100644 arch/ppc/platforms/pq2ads.c create mode 100644 arch/ppc/syslib/m8xx_wdt.c create mode 100644 arch/ppc/syslib/m8xx_wdt.h create mode 100644 arch/ppc64/Kconfig.debug create mode 100644 arch/ppc64/kernel/iomap.c create mode 100644 arch/ppc64/kernel/pSeries_setup.c create mode 100644 arch/ppc64/kernel/prom_init.c create mode 100644 arch/ppc64/kernel/u3_iommu.c create mode 100644 arch/ppc64/mm/hash_native.c create mode 100644 arch/ppc64/mm/stab.c create mode 100644 arch/s390/Kconfig.debug create mode 100644 arch/s390/kernel/irq.c create mode 100644 arch/sh/Kconfig.debug create mode 100644 arch/sh64/Kconfig.debug create mode 100644 arch/sparc/Kconfig.debug create mode 100644 arch/sparc64/Kconfig.debug create mode 100644 arch/sparc64/kernel/kprobes.c create mode 100644 arch/sparc64/lib/U1copy_from_user.S create mode 100644 arch/sparc64/lib/U1copy_to_user.S create mode 100644 arch/sparc64/lib/U1memcpy.S create mode 100644 arch/sparc64/lib/U3patch.S create mode 100644 arch/sparc64/lib/copy_in_user.S create mode 100644 arch/sparc64/lib/delay.c create mode 100644 arch/sparc64/lib/iomap.c create mode 100644 arch/sparc64/lib/memmove.S create mode 100644 arch/sparc64/lib/user_fixup.c create mode 100644 arch/um/Kconfig.debug create mode 100644 arch/um/kernel/dyn.lds.S create mode 100644 arch/um/kernel/main.c create mode 100644 arch/um/kernel/uml.lds.S create mode 100644 arch/v850/Kconfig.debug create mode 100644 arch/x86_64/Kconfig.debug create mode 100644 arch/x86_64/kernel/machine_kexec.c create mode 100644 arch/x86_64/kernel/relocate_kernel.S create mode 100644 arch/x86_64/lib/bitops.c create mode 100644 arch/x86_64/pci/Makefile-BUS create mode 100644 arch/x86_64/pci/k8-bus.c create mode 100644 configs/kernel-2.6.9-i586-smp.config create mode 100644 configs/kernel-2.6.9-i586.config create mode 100644 configs/kernel-2.6.9-i686-smp.config create mode 100644 configs/kernel-2.6.9-i686.config create mode 100644 crypto/wp512.c create mode 100644 drivers/acpi/motherboard.c create mode 100644 drivers/acpi/sleep/wakeup.c delete mode 100644 drivers/block/cfq-iosched-orig.c create mode 100644 drivers/block/ub.c create mode 100644 drivers/char/.cvsignore create mode 100644 drivers/char/drm/drm_core.h create mode 100644 drivers/char/drm/i915.h create mode 100644 drivers/char/drm/i915_dma.c create mode 100644 drivers/char/drm/i915_drm.h create mode 100644 drivers/char/drm/i915_drv.c create mode 100644 drivers/char/drm/i915_drv.h create mode 100644 drivers/char/drm/i915_irq.c create mode 100644 drivers/char/drm/i915_mem.c create mode 100644 drivers/char/hvsi.c create mode 100644 drivers/char/ipmi/ipmi_poweroff.c create mode 100644 drivers/char/mmtimer.c create mode 100644 drivers/char/snsc.c create mode 100644 drivers/char/snsc.h create mode 100644 drivers/char/watchdog/mpc8xx_wdt.c create mode 100644 drivers/cpufreq/cpufreq_ondemand.c create mode 100644 drivers/i2c/algos/i2c-algo-pca.c create mode 100644 drivers/i2c/algos/i2c-algo-pca.h create mode 100644 drivers/i2c/busses/i2c-ixp2000.c create mode 100644 drivers/i2c/busses/i2c-mpc.c create mode 100644 drivers/i2c/busses/i2c-pca-isa.c create mode 100644 drivers/i2c/chips/isp1301_omap.c create mode 100644 drivers/i2c/chips/smsc47m1.c create mode 100644 drivers/i2c/i2c-sensor-detect.c create mode 100644 drivers/i2c/i2c-sensor-vid.c create mode 100644 drivers/ide/arm/bast-ide.c create mode 100644 drivers/ide/pci/delkin_cb.c create mode 100644 drivers/ide/pci/it8212.c create mode 100644 drivers/input/serio/serio_raw.c create mode 100644 drivers/md/raid10.c create mode 100644 drivers/message/i2o/debug.c create mode 100644 drivers/message/i2o/device.c create mode 100644 drivers/message/i2o/driver.c create mode 100644 drivers/message/i2o/exec-osm.c create mode 100644 drivers/message/i2o/i2o_block.h create mode 100644 drivers/message/i2o/iop.c create mode 100644 drivers/message/i2o/pci.c create mode 100644 drivers/mmc/Kconfig create mode 100644 drivers/mmc/Makefile create mode 100644 drivers/mmc/mmc.c create mode 100644 drivers/mmc/mmc.h create mode 100644 drivers/mmc/mmc_block.c create mode 100644 drivers/mmc/mmc_queue.c create mode 100644 drivers/mmc/mmc_queue.h create mode 100644 drivers/mmc/mmc_sysfs.c create mode 100644 drivers/mmc/mmci.c create mode 100644 drivers/mmc/mmci.h create mode 100644 drivers/mmc/pxamci.c create mode 100644 drivers/mmc/pxamci.h create mode 100644 drivers/mtd/maps/ixp2000.c create mode 100644 drivers/net/gt64240eth.h create mode 100644 drivers/net/mv643xx_eth.c create mode 100644 drivers/net/mv643xx_eth.h create mode 100644 drivers/net/wireless/ieee80211/LICENSE create mode 100644 drivers/net/wireless/ieee80211/Makefile create mode 100644 drivers/net/wireless/ieee80211/ieee80211.h create mode 100644 drivers/net/wireless/ieee80211/ieee80211_crypt.c create mode 100644 drivers/net/wireless/ieee80211/ieee80211_crypt.h create mode 100644 drivers/net/wireless/ieee80211/ieee80211_crypt_ccmp.c create mode 100644 drivers/net/wireless/ieee80211/ieee80211_crypt_tkip.c create mode 100644 drivers/net/wireless/ieee80211/ieee80211_crypt_wep.c create mode 100644 drivers/net/wireless/ieee80211/ieee80211_module.c create mode 100644 drivers/net/wireless/ieee80211/ieee80211_rx.c create mode 100644 drivers/net/wireless/ieee80211/ieee80211_tx.c create mode 100644 drivers/net/wireless/ieee80211/ieee80211_wx.c create mode 100644 drivers/net/wireless/ipw2100/LICENSE create mode 100644 drivers/net/wireless/ipw2100/Makefile create mode 100644 drivers/net/wireless/ipw2100/ipw2100.c create mode 100644 drivers/net/wireless/ipw2100/ipw2100.h create mode 100644 drivers/net/wireless/ipw2200/LICENSE create mode 100644 drivers/net/wireless/ipw2200/Makefile create mode 100644 drivers/net/wireless/ipw2200/ipw2200.c create mode 100644 drivers/net/wireless/ipw2200/ipw2200.h create mode 100644 drivers/pci/.cvsignore create mode 100644 drivers/pci/hotplug/acpiphp_ibm.c create mode 100644 drivers/pci/pci-acpi.c create mode 100644 drivers/scsi/ahci.c create mode 100644 drivers/scsi/aic7xxx/.cvsignore create mode 100644 drivers/scsi/ibmvscsi/Makefile create mode 100644 drivers/scsi/ibmvscsi/ibmvscsi.c create mode 100644 drivers/scsi/ibmvscsi/ibmvscsi.h create mode 100644 drivers/scsi/ibmvscsi/iseries_vscsi.c create mode 100644 drivers/scsi/ibmvscsi/rpa_vscsi.c create mode 100644 drivers/scsi/ibmvscsi/srp.h create mode 100644 drivers/scsi/ibmvscsi/viosrp.h create mode 100644 drivers/scsi/lpfc/Makefile create mode 100644 drivers/scsi/lpfc/lpfc.h create mode 100644 drivers/scsi/lpfc/lpfc_compat.h create mode 100644 drivers/scsi/lpfc/lpfc_crtn.h create mode 100644 drivers/scsi/lpfc/lpfc_ct.c create mode 100644 drivers/scsi/lpfc/lpfc_disc.h create mode 100644 drivers/scsi/lpfc/lpfc_els.c create mode 100644 drivers/scsi/lpfc/lpfc_fcp.c create mode 100644 drivers/scsi/lpfc/lpfc_fcp.h create mode 100644 drivers/scsi/lpfc/lpfc_hbadisc.c create mode 100644 drivers/scsi/lpfc/lpfc_hw.h create mode 100644 drivers/scsi/lpfc/lpfc_init.c create mode 100644 drivers/scsi/lpfc/lpfc_logmsg.h create mode 100644 drivers/scsi/lpfc/lpfc_mbox.c create mode 100644 drivers/scsi/lpfc/lpfc_mem.c create mode 100644 drivers/scsi/lpfc/lpfc_mem.h create mode 100644 drivers/scsi/lpfc/lpfc_nportdisc.c create mode 100644 drivers/scsi/lpfc/lpfc_scsi.h create mode 100644 drivers/scsi/lpfc/lpfc_scsiport.c create mode 100644 drivers/scsi/lpfc/lpfc_sli.c create mode 100644 drivers/scsi/lpfc/lpfc_sli.h create mode 100644 drivers/scsi/lpfc/lpfc_version.h create mode 100644 drivers/scsi/megaraid/Kconfig.megaraid create mode 100644 drivers/scsi/megaraid/Makefile create mode 100644 drivers/scsi/megaraid/mbox_defs.h create mode 100644 drivers/scsi/megaraid/mega_common.h create mode 100644 drivers/scsi/megaraid/megaraid_ioctl.h create mode 100644 drivers/scsi/megaraid/megaraid_mbox.c create mode 100644 drivers/scsi/megaraid/megaraid_mbox.h create mode 100644 drivers/scsi/megaraid/megaraid_mm.c create mode 100644 drivers/scsi/megaraid/megaraid_mm.h create mode 100644 drivers/scsi/sata_uli.c create mode 100644 drivers/serial/icom.c create mode 100644 drivers/serial/icom.h create mode 100644 drivers/usb/atm/Kconfig create mode 100644 drivers/usb/atm/Makefile create mode 100644 drivers/usb/atm/speedtch.c create mode 100644 drivers/usb/atm/usb_atm.c create mode 100644 drivers/usb/atm/usb_atm.h create mode 100644 drivers/usb/core/otg_whitelist.h create mode 100644 drivers/usb/gadget/lh7a40x_udc.c create mode 100644 drivers/usb/gadget/lh7a40x_udc.h create mode 100644 drivers/usb/gadget/omap_udc.c create mode 100644 drivers/usb/gadget/omap_udc.h create mode 100644 drivers/usb/media/pwc/ChangeLog create mode 100644 drivers/usb/media/pwc/Makefile create mode 100644 drivers/usb/media/pwc/philips.txt create mode 100644 drivers/usb/media/pwc/pwc-ctrl.c create mode 100644 drivers/usb/media/pwc/pwc-dec1.c create mode 100644 drivers/usb/media/pwc/pwc-dec1.h create mode 100644 drivers/usb/media/pwc/pwc-dec23.c create mode 100644 drivers/usb/media/pwc/pwc-dec23.h create mode 100644 drivers/usb/media/pwc/pwc-if.c create mode 100644 drivers/usb/media/pwc/pwc-ioctl.h create mode 100644 drivers/usb/media/pwc/pwc-kiara.c create mode 100644 drivers/usb/media/pwc/pwc-kiara.h create mode 100644 drivers/usb/media/pwc/pwc-misc.c create mode 100644 drivers/usb/media/pwc/pwc-nala.h create mode 100644 drivers/usb/media/pwc/pwc-timon.c create mode 100644 drivers/usb/media/pwc/pwc-timon.h create mode 100644 drivers/usb/media/pwc/pwc-uncompress.c create mode 100644 drivers/usb/media/pwc/pwc-uncompress.h create mode 100644 drivers/usb/media/pwc/pwc.h create mode 100644 drivers/usb/media/sn9c102_pas202bcb.c create mode 100644 drivers/video/amba-clcd.c create mode 100644 drivers/w1/ds_w1_bridge.c create mode 100644 drivers/w1/dscore.c create mode 100644 drivers/w1/dscore.h create mode 100644 drivers/w1/w1_smem.c create mode 100644 fs/nfs/callback.c create mode 100644 fs/nfs/callback.h create mode 100644 fs/nfs/callback_proc.c create mode 100644 fs/nfs/callback_xdr.c create mode 100644 fs/nfs/delegation.c create mode 100644 fs/nfs/delegation.h create mode 100644 fs/nfsd/nfs4acl.c create mode 100644 fs/ntfs/bitmap.c create mode 100644 fs/ntfs/bitmap.h create mode 100644 fs/ntfs/lcnalloc.c create mode 100644 fs/ntfs/lcnalloc.h create mode 100644 fs/xfs/linux-2.6/xfs_ioctl32.c create mode 100644 fs/xfs/linux-2.6/xfs_ioctl32.h create mode 100644 include/.cvsignore create mode 100644 include/asm-alpha/io_trivial.h create mode 100644 include/asm-arm/arch-h720x/boards.h create mode 100644 include/asm-arm/arch-h720x/dma.h create mode 100644 include/asm-arm/arch-h720x/h7201-regs.h create mode 100644 include/asm-arm/arch-h720x/h7202-regs.h create mode 100644 include/asm-arm/arch-h720x/hardware.h create mode 100644 include/asm-arm/arch-h720x/io.h create mode 100644 include/asm-arm/arch-h720x/irq.h create mode 100644 include/asm-arm/arch-h720x/irqs.h create mode 100644 include/asm-arm/arch-h720x/memory.h create mode 100644 include/asm-arm/arch-h720x/param.h create mode 100644 include/asm-arm/arch-h720x/serial.h create mode 100644 include/asm-arm/arch-h720x/system.h create mode 100644 include/asm-arm/arch-h720x/timex.h create mode 100644 include/asm-arm/arch-h720x/uncompress.h create mode 100644 include/asm-arm/arch-h720x/vmalloc.h create mode 100644 include/asm-arm/arch-imx/dma.h create mode 100644 include/asm-arm/arch-imx/hardware.h create mode 100644 include/asm-arm/arch-imx/imx-regs.h create mode 100644 include/asm-arm/arch-imx/io.h create mode 100644 include/asm-arm/arch-imx/irq.h create mode 100644 include/asm-arm/arch-imx/irqs.h create mode 100644 include/asm-arm/arch-imx/memory.h create mode 100644 include/asm-arm/arch-imx/mx1ads.h create mode 100644 include/asm-arm/arch-imx/param.h create mode 100644 include/asm-arm/arch-imx/serial.h create mode 100644 include/asm-arm/arch-imx/system.h create mode 100644 include/asm-arm/arch-imx/timex.h create mode 100644 include/asm-arm/arch-imx/uncompress.h create mode 100644 include/asm-arm/arch-imx/vmalloc.h create mode 100644 include/asm-arm/arch-iop3xx/iop331-irqs.h create mode 100644 include/asm-arm/arch-iop3xx/iop331.h create mode 100644 include/asm-arm/arch-iop3xx/iq31244.h create mode 100644 include/asm-arm/arch-iop3xx/iq80331.h create mode 100644 include/asm-arm/arch-ixp2000/dma.h create mode 100644 include/asm-arm/arch-ixp2000/enp2611.h create mode 100644 include/asm-arm/arch-ixp2000/gpio.h create mode 100644 include/asm-arm/arch-ixp2000/hardware.h create mode 100644 include/asm-arm/arch-ixp2000/io.h create mode 100644 include/asm-arm/arch-ixp2000/irq.h create mode 100644 include/asm-arm/arch-ixp2000/irqs.h create mode 100644 include/asm-arm/arch-ixp2000/ixdp2x00.h create mode 100644 include/asm-arm/arch-ixp2000/ixdp2x01.h create mode 100644 include/asm-arm/arch-ixp2000/ixp2000-regs.h create mode 100644 include/asm-arm/arch-ixp2000/memory.h create mode 100644 include/asm-arm/arch-ixp2000/param.h create mode 100644 include/asm-arm/arch-ixp2000/platform.h create mode 100644 include/asm-arm/arch-ixp2000/serial.h create mode 100644 include/asm-arm/arch-ixp2000/system.h create mode 100644 include/asm-arm/arch-ixp2000/timex.h create mode 100644 include/asm-arm/arch-ixp2000/uncompress.h create mode 100644 include/asm-arm/arch-ixp2000/vmalloc.h create mode 100644 include/asm-arm/arch-omap/mcbsp.h create mode 100644 include/asm-arm/arch-omap/tps65010.h create mode 100644 include/asm-arm/arch-omap/usb.h create mode 100644 include/asm-arm/arch-pxa/mmc.h create mode 100644 include/asm-arm/arch-s3c2410/nand.h create mode 100644 include/asm-arm/arch-s3c2410/regs-dsc.h create mode 100644 include/asm-arm/arch-s3c2410/regs-gpioj.h create mode 100644 include/asm-arm/arch-s3c2410/regs-iic.h create mode 100644 include/asm-arm/arch-s3c2410/regs-mem.h create mode 100644 include/asm-arm/arch-s3c2410/regs-nand.h create mode 100644 include/asm-arm/arch-s3c2410/regs-sdi.h create mode 100644 include/asm-arm/arch-s3c2410/regs-spi.h create mode 100644 include/asm-arm/arch-s3c2410/regs-udc.h create mode 100644 include/asm-arm/arch-s3c2410/usb-control.h create mode 100644 include/asm-arm/hardware/amba_clcd.h create mode 100644 include/asm-arm/mach/mmc.h create mode 100644 include/asm-generic/bug.h create mode 100644 include/asm-generic/iomap.h create mode 100644 include/asm-generic/uaccess.h create mode 100644 include/asm-i386/.cvsignore create mode 100644 include/asm-i386/kdebug.h create mode 100644 include/asm-i386/kexec.h create mode 100644 include/asm-i386/kprobes.h create mode 100644 include/asm-ia64/sn/l1.h create mode 100644 include/asm-ia64/sn/shub_mmr.h create mode 100644 include/asm-ia64/sn/sn2/sn_hwperf.h create mode 100644 include/asm-m32r/a.out.h create mode 100644 include/asm-m32r/addrspace.h create mode 100644 include/asm-m32r/assembler.h create mode 100644 include/asm-m32r/atomic.h create mode 100644 include/asm-m32r/bitops.h create mode 100644 include/asm-m32r/bug.h create mode 100644 include/asm-m32r/bugs.h create mode 100644 include/asm-m32r/byteorder.h create mode 100644 include/asm-m32r/cache.h create mode 100644 include/asm-m32r/cachectl.h create mode 100644 include/asm-m32r/cacheflush.h create mode 100644 include/asm-m32r/checksum.h create mode 100644 include/asm-m32r/current.h create mode 100644 include/asm-m32r/delay.h create mode 100644 include/asm-m32r/div64.h create mode 100644 include/asm-m32r/dma-mapping.h create mode 100644 include/asm-m32r/dma.h create mode 100644 include/asm-m32r/elf.h create mode 100644 include/asm-m32r/errno.h create mode 100644 include/asm-m32r/fcntl.h create mode 100644 include/asm-m32r/flat.h create mode 100644 include/asm-m32r/hardirq.h create mode 100644 include/asm-m32r/hdreg.h create mode 100644 include/asm-m32r/hw_irq.h create mode 100644 include/asm-m32r/ide.h create mode 100644 include/asm-m32r/io.h create mode 100644 include/asm-m32r/ioctl.h create mode 100644 include/asm-m32r/ioctls.h create mode 100644 include/asm-m32r/ipc.h create mode 100644 include/asm-m32r/ipcbuf.h create mode 100644 include/asm-m32r/irq.h create mode 100644 include/asm-m32r/kmap_types.h create mode 100644 include/asm-m32r/linkage.h create mode 100644 include/asm-m32r/local.h create mode 100644 include/asm-m32r/m32102.h create mode 100644 include/asm-m32r/m32102peri.h create mode 100644 include/asm-m32r/m32700ut/m32700ut_lan.h create mode 100644 include/asm-m32r/m32700ut/m32700ut_lcd.h create mode 100644 include/asm-m32r/m32700ut/m32700ut_pld.h create mode 100644 include/asm-m32r/m32r.h create mode 100644 include/asm-m32r/m32r_mp_fpga.h create mode 100644 include/asm-m32r/mappi2/mappi2_pld.h create mode 100644 include/asm-m32r/mc146818rtc.h create mode 100644 include/asm-m32r/mman.h create mode 100644 include/asm-m32r/mmu.h create mode 100644 include/asm-m32r/mmu_context.h create mode 100644 include/asm-m32r/mmzone.h create mode 100644 include/asm-m32r/module.h create mode 100644 include/asm-m32r/msgbuf.h create mode 100644 include/asm-m32r/namei.h create mode 100644 include/asm-m32r/numnodes.h create mode 100644 include/asm-m32r/opsput/opsput_lan.h create mode 100644 include/asm-m32r/opsput/opsput_lcd.h create mode 100644 include/asm-m32r/opsput/opsput_pld.h create mode 100644 include/asm-m32r/page.h create mode 100644 include/asm-m32r/param.h create mode 100644 include/asm-m32r/pci.h create mode 100644 include/asm-m32r/percpu.h create mode 100644 include/asm-m32r/pgalloc.h create mode 100644 include/asm-m32r/pgtable-2level.h create mode 100644 include/asm-m32r/pgtable.h create mode 100644 include/asm-m32r/poll.h create mode 100644 include/asm-m32r/posix_types.h create mode 100644 include/asm-m32r/processor.h create mode 100644 include/asm-m32r/ptrace.h create mode 100644 include/asm-m32r/resource.h create mode 100644 include/asm-m32r/rtc.h create mode 100644 include/asm-m32r/scatterlist.h create mode 100644 include/asm-m32r/sections.h create mode 100644 include/asm-m32r/segment.h create mode 100644 include/asm-m32r/semaphore.h create mode 100644 include/asm-m32r/sembuf.h create mode 100644 include/asm-m32r/serial.h create mode 100644 include/asm-m32r/setup.h create mode 100644 include/asm-m32r/shmbuf.h create mode 100644 include/asm-m32r/shmparam.h create mode 100644 include/asm-m32r/sigcontext.h create mode 100644 include/asm-m32r/siginfo.h create mode 100644 include/asm-m32r/signal.h create mode 100644 include/asm-m32r/smp.h create mode 100644 include/asm-m32r/socket.h create mode 100644 include/asm-m32r/sockios.h create mode 100644 include/asm-m32r/spinlock.h create mode 100644 include/asm-m32r/stat.h create mode 100644 include/asm-m32r/statfs.h create mode 100644 include/asm-m32r/string.h create mode 100644 include/asm-m32r/syscall.h create mode 100644 include/asm-m32r/system.h create mode 100644 include/asm-m32r/termbits.h create mode 100644 include/asm-m32r/termios.h create mode 100644 include/asm-m32r/thread_info.h create mode 100644 include/asm-m32r/timex.h create mode 100644 include/asm-m32r/tlb.h create mode 100644 include/asm-m32r/tlbflush.h create mode 100644 include/asm-m32r/topology.h create mode 100644 include/asm-m32r/types.h create mode 100644 include/asm-m32r/uaccess.h create mode 100644 include/asm-m32r/ucontext.h create mode 100644 include/asm-m32r/unaligned.h create mode 100644 include/asm-m32r/unistd.h create mode 100644 include/asm-m32r/user.h create mode 100644 include/asm-m32r/vga.h create mode 100644 include/asm-m32r/xor.h create mode 100644 include/asm-ppc/8253pit.h create mode 100644 include/asm-ppc/kexec.h create mode 100644 include/asm-ppc64/8253pit.h create mode 100644 include/asm-ppc64/plpar_wrappers.h create mode 100644 include/asm-sparc64/kprobes.h create mode 100644 include/asm-x86_64/crash.h create mode 100644 include/asm-x86_64/kexec.h create mode 100644 include/asm-x86_64/swiotlb.h create mode 100644 include/linux/.cvsignore create mode 100644 include/linux/gen_stats.h create mode 100644 include/linux/hardirq.h create mode 100644 include/linux/i2c-algo-pca.h create mode 100644 include/linux/kexec.h create mode 100644 include/linux/kprobes.h create mode 100644 include/linux/mmc/card.h create mode 100644 include/linux/mmc/host.h create mode 100644 include/linux/mmc/mmc.h create mode 100644 include/linux/mmc/protocol.h create mode 100644 include/linux/mmtimer.h create mode 100644 include/linux/mv643xx.h create mode 100644 include/linux/netfilter_ipv4/ip_conntrack_sctp.h create mode 100644 include/linux/netfilter_ipv4/ipt_comment.h create mode 100644 include/linux/netfilter_ipv4/ipt_sctp.h create mode 100644 include/linux/netfilter_ipv6/ip6t_physdev.h create mode 100644 include/linux/nfs4_acl.h create mode 100644 include/linux/pci-acpi.h create mode 100644 include/linux/raid/raid10.h create mode 100644 include/linux/ramfs.h create mode 100644 include/linux/sunrpc/gss_spkm3.h create mode 100644 include/linux/tc_act/tc_gact.h create mode 100644 include/linux/usb_otg.h create mode 100644 include/net/gen_stats.h create mode 100644 include/net/tc_act/tc_gact.h create mode 100644 include/sound/pcm-indirect.h create mode 100644 include/video/epson1355.h create mode 100644 kernel/.cvsignore delete mode 100644 kernel/ckrm/ckrm_laq.c create mode 100644 kernel/kexec.c create mode 100644 kernel/kprobes.c create mode 100644 kernel/module-verify-sig.c create mode 100644 kernel/spinlock.c create mode 100644 lib/.cvsignore create mode 100644 lib/Kconfig.debug create mode 100644 lib/iomap.c create mode 100644 lib/zlib_inflate/inflate_sync.c create mode 100644 mm/tiny-shmem.c create mode 100644 net/core/gen_estimator.c create mode 100644 net/core/gen_stats.c create mode 100644 net/ipv4/fib_lookup.h create mode 100644 net/ipv4/netfilter/ip_conntrack_proto_sctp.c create mode 100644 net/ipv4/netfilter/ipt_comment.c create mode 100644 net/ipv4/netfilter/ipt_sctp.c create mode 100644 net/ipv6/netfilter/ip6t_physdev.c create mode 100644 net/sched/gact.c create mode 100644 net/sunrpc/auth_gss/gss_spkm3_mech.c create mode 100644 net/sunrpc/auth_gss/gss_spkm3_seal.c create mode 100644 net/sunrpc/auth_gss/gss_spkm3_token.c create mode 100644 net/sunrpc/auth_gss/gss_spkm3_unseal.c create mode 100644 scripts/.cvsignore create mode 100644 scripts/Makefile.host create mode 100644 scripts/basic/.cvsignore create mode 100644 scripts/kconfig/.cvsignore create mode 100644 scripts/lxdialog/.cvsignore create mode 100644 scripts/mksysmap create mode 100644 scripts/mod/.cvsignore create mode 100644 scripts/namespace.pl create mode 100644 sound/pci/atiixp_modem.c create mode 100644 sound/pci/ice1712/pontis.c create mode 100644 sound/pci/ice1712/pontis.h create mode 100644 sound/pci/ice1712/vt1720_mobo.c create mode 100644 sound/pci/ice1712/vt1720_mobo.h create mode 100644 sound/ppc/beep.c create mode 100644 sound/usb/usx2y/Makefile create mode 100644 sound/usb/usx2y/usX2Yhwdep.c create mode 100644 sound/usb/usx2y/usX2Yhwdep.h create mode 100644 sound/usb/usx2y/usbus428ctldefs.h create mode 100644 sound/usb/usx2y/usbusx2y.c create mode 100644 sound/usb/usx2y/usbusx2y.h create mode 100644 sound/usb/usx2y/usbusx2yaudio.c create mode 100644 sound/usb/usx2y/usx2y.h create mode 100644 usr/.cvsignore diff --git a/.cvsignore b/.cvsignore new file mode 100644 index 000000000..5e7d07457 --- /dev/null +++ b/.cvsignore @@ -0,0 +1,13 @@ +.config +.tmp_System.map +.tmp_kallsyms1.S +.tmp_kallsyms2.S +.tmp_kallsyms3.S +.tmp_versions +.tmp_vmlinux1 +.tmp_vmlinux2 +.tmp_vmlinux3 +.version +Module.symvers +System.map +vmlinux diff --git a/Documentation/CodingStyle b/Documentation/CodingStyle index 7b256c12a..f25b3953f 100644 --- a/Documentation/CodingStyle +++ b/Documentation/CodingStyle @@ -356,10 +356,10 @@ Generally, inline functions are preferable to macros resembling functions. Macros with multiple statements should be enclosed in a do - while block: -#define macrofun(a,b,c) \ +#define macrofun(a, b, c) \ do { \ if (a == 5) \ - do_this(b,c); \ + do_this(b, c); \ } while (0) Things to avoid when using macros: diff --git a/Documentation/DocBook/Makefile b/Documentation/DocBook/Makefile index bf212c021..9a23eab00 100644 --- a/Documentation/DocBook/Makefile +++ b/Documentation/DocBook/Makefile @@ -185,9 +185,7 @@ clean-files := $(DOCBOOKS) \ $(patsubst %.sgml, %.9, $(DOCBOOKS)) \ $(C-procfs-example) -ifneq ($(wildcard $(patsubst %.html,%,$(HTML))),) -clean-rule := rm -rf $(wildcard $(patsubst %.html,%,$(HTML))) -endif +clean-dirs := $(patsubst %.sgml,%,$(DOCBOOKS)) #man put files in man subdir - traverse down subdir- := man/ diff --git a/Documentation/DocBook/gadget.tmpl b/Documentation/DocBook/gadget.tmpl index f07e73a41..0c28a966c 100644 --- a/Documentation/DocBook/gadget.tmpl +++ b/Documentation/DocBook/gadget.tmpl @@ -2,8 +2,8 @@ USB Gadget API for Linux - 02 June 2003 - 02 June 2003 + 20 August 2004 + 20 August 2004 @@ -34,7 +34,7 @@ - 2003 + 2003-2004 David Brownell @@ -73,9 +73,13 @@ a number of important problems, including: composite devices, and alternate interface settings. + USB "On-The-Go" (OTG) support, in conjunction + with updates to the Linux-USB host side. + Sharing data structures and API models with the - Linux-USB host side API. This looks forward to USB "On-The-Go" - (OTG) and similar more-symmetric frameworks. + Linux-USB host side API. This helps the OTG support, and + looks forward to more-symmetric frameworks (where the same + I/O model is used by both host and device side drivers). Minimalist, so it's easier to support new device controller hardware. I/O processing doesn't imply large @@ -153,6 +157,7 @@ with the lowest level (which directly handles hardware). the SA-11x0 or PXA-25x UDC (found within many PDAs), and a variety of other products. + @@ -162,10 +167,14 @@ with the lowest level (which directly handles hardware). The lower boundary of this driver implements hardware-neutral USB functions, using calls to the controller driver. Because such hardware varies widely in capabilities and restrictions, - the gadget driver is normally configured at compile time + and is used in embedded environments where space is at a premium, + the gadget driver is often configured at compile time to work with endpoints supported by one particular controller. Gadget drivers may be portable to several different controllers, using conditional compilation. + (Recent kernels substantially simplify the work involved in + supporting new hardware, by autoconfiguring + endpoints automatically for many bulk-oriented drivers.) Gadget driver responsibilities include: @@ -178,8 +187,9 @@ with the lowest level (which directly handles hardware). altsettings, including enabling and configuring endpoints handling life cycle events, such as managing - bindings - to hardware, and disconnection from the USB host. + bindings to hardware, + USB suspend/resume, remote wakeup, + and disconnection from the USB host. managing IN and OUT transfers on all currently enabled endpoints @@ -244,15 +254,38 @@ with the lowest level (which directly handles hardware). -Over time, reusable utilities should evolve to help make some -gadget driver tasks simpler. An example of particular interest +OTG-capable systems will also need to include a standard Linux-USB +host side stack, +with usbcore, +one or more Host Controller Drivers (HCDs), +USB Device Drivers to support +the OTG "Targeted Peripheral List", +and so forth. +There will also be an OTG Controller Driver, +which is visible to gadget and device driver developers only indirectly. +That helps the host and device side USB controllers implement the +two new OTG protocols (HNP and SRP). +Roles switch (host to peripheral, or vice versa) using HNP +during USB suspend processing, and SRP can be viewed as a +more battery-friendly kind of device wakeup protocol. + + +Over time, reusable utilities are evolving to help make some +gadget driver tasks simpler. +For example, building configuration descriptors from vectors of +descriptors for the configurations interfaces and endpoints is +now automated, and many drivers now use autoconfiguration to +choose hardware endpoints and initialize their descriptors. + +A potential example of particular interest is code implementing standard USB-IF protocols for HID, networking, storage, or audio classes. Some developers are interested in KDB or KGDB hooks, to let target hardware be remotely debugged. Most such USB protocol code doesn't need to be hardware-specific, any more than network protocols like X11, HTTP, or NFS are. -Such interface drivers might be combined, to support composite devices. +Such gadget-side interface drivers should eventually be combined, +to implement composite devices. @@ -284,7 +317,7 @@ data types and functions are described here. However, docproc does not understand all the C constructs that are used, so some relevant information is likely omitted from what you are reading. -One example of such information is several per-request flags. +One example of such information is endpoint autoconfiguration. You'll have to read the header file, and use example source code (such as that for "Gadget Zero"), to fully understand the API. @@ -292,7 +325,7 @@ code (such as that for "Gadget Zero"), to fully understand the API. The part of the API implementing some basic driver capabilities is specific to the version of the Linux kernel that's in use. -The 2.5 kernel includes a driver model +The 2.6 kernel includes a driver model framework that has no analogue on earlier kernels; so those parts of the gadget API are not fully portable. (They are implemented on 2.4 kernels, but in a different way.) @@ -308,17 +341,21 @@ There are significant hardware features, such as device-to-device DMA that would be added using hardware-specific APIs. -This API expects drivers to use conditional compilation to handle -endpoint capabilities of different hardware. -Those tend to have arbitrary restrictions, relating to +This API allows drivers to use conditional compilation to handle +endpoint capabilities of different hardware, but doesn't require that. +Hardware tends to have arbitrary restrictions, relating to transfer types, addressing, packet sizes, buffering, and availability. As a rule, such differences only matter for "endpoint zero" logic that handles device configuration and management. -The API only supports limited run-time +The API supports limited run-time detection of capabilities, through naming conventions for endpoints. -Although a gadget driver could scan the endpoints available to it and -choose to map those capabilities onto driver functionality in some way, -few drivers will want to reconfigure themselves at run-time. +Many drivers will be able to at least partially autoconfigure +themselves. +In particular, driver init sections will often have endpoint +autoconfiguration logic that scans the hardware's list of endpoints +to find ones matching the driver requirements +(relying on those conventions), to eliminate some of the most +common reasons for conditional compilation. Like the Linux-USB host side API, this API exposes @@ -355,10 +392,14 @@ and so on. At this point the device is logically in the USB ch9 initial state ("attached"), drawing no power and not usable (since it does not yet support enumeration). +Any host should not see the device, since it's not +activated the data line pullup used by the host to +detect a device, even if VBUS power is available. Register a gadget driver that implements some higher level -device function. That will then bind() to a usb_gadget. +device function. That will then bind() to a usb_gadget, which +activates the data line pullup sometime after detecting VBUS. The hardware driver can now start enumerating. @@ -373,6 +414,8 @@ based both on what the bus interface hardware provides and on the functionality being implemented. That can involve alternate settings or configurations, unless the hardware prevents such operation. +For OTG devices, each configuration descriptor includes +an OTG descriptor. The gadget driver handles the last step of enumeration, @@ -381,13 +424,18 @@ It enables all endpoints used in that configuration, with all interfaces in their default settings. That involves using a list of the hardware's endpoints, enabling each endpoint according to its descriptor. +It may also involve using usb_gadget_vbus_draw +to let more power be drawn from VBUS, as allowed by that configuration. +For OTG devices, setting a configuration may also involve reporting +HNP capabilities through a user interface. Do real work and perform data transfers, possibly involving changes to interface settings or switching to new configurations, until the device is disconnect()ed from the host. Queue any number of transfer requests to each endpoint. -The drivers then go back to step 3 (above). +It may be suspended and resumed several times before being disconnected. +On disconnect, the drivers go back to step 3 (above). When the gadget driver module is being unloaded, @@ -399,7 +447,9 @@ driver be unloaded. Drivers will normally be arranged so that just loading the gadget driver module (or statically linking it into a Linux kernel) -allows the peripheral device to be enumerated. +allows the peripheral device to be enumerated, but some drivers +will defer enumeration until some higher level component (like +a user mode daemon) enables it. Note that at this lowest level there are no policies about how ep0 configuration logic is implemented, except that it should obey USB specifications. @@ -410,6 +460,18 @@ or understanding that composite devices might happen to be built by integrating reusable components. +Note that the lifecycle above can be slightly different +for OTG devices. +Other than providing an additional OTG descriptor in each +configuration, only the HNP-related differences are particularly +visible to driver code. +They involve reporting requirements during the SET_CONFIGURATION +request, and the option to invoke HNP during some suspend callbacks. +Also, SRP changes the semantics of +usb_gadget_wakeup +slightly. + + USB 2.0 Chapter 9 Types and Constants @@ -418,9 +480,9 @@ be built by integrating reusable components. rely on common USB structures and constants defined in the <linux/usb_ch9.h> -header file, which is standard in Linux 2.5 kernels. +header file, which is standard in Linux 2.6 kernels. These are the same types and constants used by host -side drivers. +side drivers (and usbcore). !Iinclude/linux/usb_ch9.h @@ -451,26 +513,38 @@ USB peripheral controller drivers. The core API is sufficient for writing a USB Gadget Driver, but some optional utilities are provided to simplify common tasks. +These utilities include endpoint autoconfiguration. !Edrivers/usb/gadget/usbstring.c !Edrivers/usb/gadget/config.c + Peripheral Controller Drivers -The first hardware supporting this API is the NetChip 2280 +The first hardware supporting this API was the NetChip 2280 controller, which supports USB 2.0 high speed and is based on PCI. This is the net2280 driver module. -The driver supports Linux kernel versions 2.4 and 2.5; +The driver supports Linux kernel versions 2.4 and 2.6; contact NetChip Technologies for development boards and product information. -For users of Intel's PXA 2xx series processors, -a pxa2xx_udc driver is available. +Other hardware working in the "gadget" framework includes: +Intel's PXA 25x and IXP42x series processors +(pxa2xx_udc), +Toshiba TC86c001 "Goku-S" (goku_udc), +Renesas SH7705/7727 (sh_udc), +MediaQ 11xx (mq11xx_udc), +Hynix HMS30C7202 (h7202_udc), +National 9303/4 (n9604_udc), +Texas Instruments OMAP (omap_udc), +Sharp LH7A40x (lh7a40x_udc), +and more. +Most of those are full speed controllers. At this writing, there are people at work on drivers in @@ -526,6 +600,15 @@ subset of CDC Ethernet. to avoid creating problems.) +Support for Microsoft's RNDIS +protocol has been contributed by Pengutronix and Auerswald GmbH. +This is like CDC Ethernet, but it runs on more slightly USB hardware +(but less than the CDC subset). +However, its main claim to fame is being able to connect directly to +recent versions of Windows, using drivers that Microsoft bundles +and supports, making it much simpler to network with Windows. + + There is also support for user mode gadget drivers, using gadgetfs. This provides a User Mode API that presents @@ -535,6 +618,10 @@ Familiar tools like GDB and pthreads can be used to develop and debug user mode drivers, so that once a robust controller driver is available many applications for it won't require new kernel mode software. +Linux 2.6 Async I/O (AIO) +support is available, so that user mode software +can stream data with only slightly more overhead +than a kernel driver. There's a USB Mass Storage class driver, which provides @@ -548,6 +635,16 @@ storage class specification, using transparent SCSI commands to access the data from the backing store. +There's a "serial line" driver, useful for TTY style +operation over USB. +The latest version of that driver supports CDC ACM style +operation, like a USB modem, and so on most hardware it can +interoperate easily with MS-Windows. +One interesting use of that driver is in boot firmware (like a BIOS), +which can sometimes use that model with very small systems without +real serial lines. + + Support for other kinds of gadget is expected to be developed and contributed over time, as this driver framework evolves. @@ -555,6 +652,96 @@ over time, as this driver framework evolves. +USB On-The-GO (OTG) + +USB OTG support on Linux 2.6 was initially developed +by Texas Instruments for +OMAP 16xx and 17xx +series processors. +Other OTG systems should work in similar ways, but the +hardware level details could be very different. + + +Systems need specialized hardware support to implement OTG, +notably including a special Mini-AB jack +and associated transciever to support Dual-Role +operation: +they can act either as a host, using the standard +Linux-USB host side driver stack, +or as a peripheral, using this "gadget" framework. +To do that, the system software relies on small additions +to those programming interfaces, +and on a new internal component (here called an "OTG Controller") +affecting which driver stack connects to the OTG port. +In each role, the system can re-use the existing pool of +hardware-neutral drivers, layered on top of the controller +driver interfaces (usb_bus or +usb_gadget). +Such drivers need at most minor changes, and most of the calls +added to support OTG can also benefit non-OTG products. + + + + Gadget drivers test the is_otg + flag, and use it to determine whether or not to include + an OTG descriptor in each of their configurations. + + Gadget drivers may need changes to support the + two new OTG protocols, exposed in new gadget attributes + such as b_hnp_enable flag. + HNP support should be reported through a user interface + (two LEDs could suffice), and is triggered in some cases + when the host suspends the peripheral. + SRP support can be user-initiated just like remote wakeup, + probably by pressing the same button. + + On the host side, USB device drivers need + to be taught to trigger HNP at appropriate moments, using + usb_suspend_device(). + That also conserves battery power, which is useful even + for non-OTG configurations. + + Also on the host side, a driver must support the + OTG "Targeted Peripheral List". That's just a whitelist, + used to reject peripherals not supported with a given + Linux OTG host. + This whitelist is product-specific; + each product must modify otg_whitelist.h + to match its interoperability specification. + + + Non-OTG Linux hosts, like PCs and workstations, + normally have some solution for adding drivers, so that + peripherals that aren't recognized can eventually be supported. + That approach is unreasonable for consumer products that may + never have their firmware upgraded, and where it's usually + unrealistic to expect traditional PC/workstation/server kinds + of support model to work. + For example, it's often impractical to change device firmware + once the product has been distributed, so driver bugs can't + normally be fixed if they're found after shipment. + + + + +Additional changes are needed below those hardware-neutral +usb_bus and usb_gadget +driver interfaces; those aren't discussed here in any detail. +Those affect the hardware-specific code for each USB Host or Peripheral +controller, and how the HCD initializes (since OTG can be active only +on a single port). +They also involve what may be called an OTG Controller +Driver, managing the OTG transceiver and the OTG state +machine logic as well as much of the root hub behavior for the +OTG port. +The OTG controller driver needs to activate and deactivate USB +controllers depending on the relevant device role. +Some related changes were needed inside usbcore, so that it +can identify OTG-capable devices and respond appropriately +to HNP or SRP protocols. + + + Slave NPU + * | + * | + * P2P + * | + * + * Bus 1 | + * <--+------+---------+---------+------+--> + * | | | | | + * | | | | | + * ... Dev PMC Media Eth0 Eth1 ... + * + * The master controlls all but Eth1, which is controlled by the + * slave. What this measn is that the both the master and the slave + * have to scan the bus, but only one of them can enumerate the bus. + * In addition, after the bus is scaned, each kernel must remove + * the device(s) it does not control from the PCI dev list otherwise + * a driver on each NPU will try to manage it and we will have horrible + * conflicts. Oh..and the slave NPU needs to see the master NPU + * for Intel's drivers to work properly. Closed source drivers... + * + * The way we deal with this is fairly simple but ugly: + * + * 1) Let master scan and enumerate the bus completely. + * 2) Master deletes Eth1 from device list. + * 3) Slave scans bus and then deletes all but Eth1 (Eth0 on slave) + * from device list. + * 4) Find HW designers and LART them. + * + * The boards also do not do normal PCI IRQ routing, or any sort of + * sensical swizzling, so we just need to check where on the bus a + * device sits and figure out to which CPLD pin the interrupt is routed. + * See ixdp2[48]00.c files. + * + *************************************************************************/ +void ixdp2x00_slave_pci_postinit(void) +{ + struct pci_dev *dev; + + /* + * Remove PMC device is there is one + */ + if((dev = pci_find_slot(1, IXDP2X00_PMC_DEVFN))) + pci_remove_bus_device(dev); + + dev = pci_find_slot(0, IXDP2X00_21555_DEVFN); + pci_remove_bus_device(dev); +} + +/************************************************************************** + * IXDP2x00 Machine Setup + *************************************************************************/ +static struct flash_platform_data ixdp2x00_platform_data = { + .map_name = "cfi_probe", + .width = 1, +}; + +static struct ixp2000_flash_data ixdp2x00_flash_data = { + .platform_data = &ixdp2x00_platform_data, + .nr_banks = 1 +}; + +static struct resource ixdp2x00_flash_resource = { + .start = 0xc4000000, + .end = 0xc4000000 + 0x00ffffff, + .flags = IORESOURCE_MEM, +}; + +static struct platform_device ixdp2x00_flash = { + .name = "IXP2000-Flash", + .id = 0, + .dev = { + .platform_data = &ixdp2x00_flash_data, + }, + .num_resources = 1, + .resource = &ixdp2x00_flash_resource, +}; + +static struct ixp2000_i2c_pins ixdp2x00_i2c_gpio_pins = { + .sda_pin = IXDP2X00_GPIO_SDA, + .scl_pin = IXDP2X00_GPIO_SCL, +}; + +static struct platform_device ixdp2x00_i2c_controller = { + .name = "IXP2000-I2C", + .id = 0, + .dev = { + .platform_data = &ixdp2x00_i2c_gpio_pins, + }, + .num_resources = 0 +}; + +static struct platform_device *ixdp2x00_devices[] __initdata = { + &ixdp2x00_flash, + &ixdp2x00_i2c_controller +}; + +void __init ixdp2x00_init_machine(void) +{ + gpio_line_set(IXDP2X00_GPIO_I2C_ENABLE, 1); + gpio_line_config(IXDP2X00_GPIO_I2C_ENABLE, GPIO_OUT); + + platform_add_devices(ixdp2x00_devices, ARRAY_SIZE(ixdp2x00_devices)); +} + diff --git a/arch/arm/mach-ixp2000/ixdp2x01.c b/arch/arm/mach-ixp2000/ixdp2x01.c new file mode 100644 index 000000000..176114aad --- /dev/null +++ b/arch/arm/mach-ixp2000/ixdp2x01.c @@ -0,0 +1,381 @@ +/* + * arch/arm/mach-ixp2000/ixdp2x01.c + * + * Code common to Intel IXDP2401 and IXDP2801 platforms + * + * Original Author: Andrzej Mialwoski + * Maintainer: Deepak Saxena + * + * Copyright (C) 2002-2003 Intel Corp. + * Copyright (C) 2003-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. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +/************************************************************************* + * IXDP2x01 IRQ Handling + *************************************************************************/ +static void ixdp2x01_irq_mask(unsigned int irq) +{ + *IXDP2X01_INT_MASK_SET_REG = IXP2000_BOARD_IRQ_MASK(irq); +} + +static void ixdp2x01_irq_unmask(unsigned int irq) +{ + *IXDP2X01_INT_MASK_CLR_REG = IXP2000_BOARD_IRQ_MASK(irq); +} + +static u32 valid_irq_mask; + +static void ixdp2x01_irq_handler(unsigned int irq, struct irqdesc *desc, struct pt_regs *regs) +{ + u32 ex_interrupt; + int i; + + desc->chip->mask(irq); + + ex_interrupt = *IXDP2X01_INT_STAT_REG & valid_irq_mask; + + if (!ex_interrupt) { + printk(KERN_ERR "Spurious IXDP2X01 CPLD interrupt!\n"); + return; + } + + for (i = 0; i < IXP2000_BOARD_IRQS; i++) { + if (ex_interrupt & (1 << i)) { + struct irqdesc *cpld_desc; + int cpld_irq = IXP2000_BOARD_IRQ(0) + i; + cpld_desc = irq_desc + cpld_irq; + cpld_desc->handle(cpld_irq, cpld_desc, regs); + } + } + + desc->chip->unmask(irq); +} + +static struct irqchip ixdp2x01_irq_chip = { + .mask = ixdp2x01_irq_mask, + .ack = ixdp2x01_irq_mask, + .unmask = ixdp2x01_irq_unmask +}; + +/* + * We only do anything if we are the master NPU on the board. + * The slave NPU only has the ethernet chip going directly to + * the PCIB interrupt input. + */ +void __init ixdp2x01_init_irq(void) +{ + int irq = 0; + + /* initialize chip specific interrupts */ + ixp2000_init_irq(); + + if (machine_is_ixdp2401()) + valid_irq_mask = IXDP2401_VALID_IRQ_MASK; + else + valid_irq_mask = IXDP2801_VALID_IRQ_MASK; + + /* Mask all interrupts from CPLD, disable simulation */ + *IXDP2X01_INT_MASK_SET_REG = 0xffffffff; + *IXDP2X01_INT_SIM_REG = 0; + + for (irq = NR_IXP2000_IRQS; irq < NR_IXDP2X01_IRQS; irq++) { + if (irq & valid_irq_mask) { + set_irq_chip(irq, &ixdp2x01_irq_chip); + set_irq_handler(irq, do_level_IRQ); + set_irq_flags(irq, IRQF_VALID); + } else { + set_irq_flags(irq, 0); + } + } + + /* Hook into PCI interrupts */ + set_irq_chained_handler(IRQ_IXP2000_PCIB, &ixdp2x01_irq_handler); +} + + +/************************************************************************* + * IXDP2x01 memory map and serial ports + *************************************************************************/ +static struct map_desc ixdp2x01_io_desc __initdata = { + .virtual = IXDP2X01_VIRT_CPLD_BASE, + .physical = IXDP2X01_PHYS_CPLD_BASE, + .length = IXDP2X01_CPLD_REGION_SIZE, + .type = MT_DEVICE +}; + +static struct uart_port ixdp2x01_serial_ports[2] = { + { + .membase = (char *)(IXDP2X01_UART1_VIRT_BASE), + .mapbase = (unsigned long)IXDP2X01_UART1_PHYS_BASE, + .irq = IRQ_IXDP2X01_UART1, + .flags = UPF_SKIP_TEST, + .iotype = UPIO_MEM32, + .regshift = 2, + .uartclk = IXDP2X01_UART_CLK, + .line = 1, + .type = PORT_16550A, + .fifosize = 16 + }, { + .membase = (char *)(IXDP2X01_UART2_VIRT_BASE), + .mapbase = (unsigned long)IXDP2X01_UART2_PHYS_BASE, + .irq = IRQ_IXDP2X01_UART2, + .flags = UPF_SKIP_TEST, + .iotype = UPIO_MEM32, + .regshift = 2, + .uartclk = IXDP2X01_UART_CLK, + .line = 2, + .type = PORT_16550A, + .fifosize = 16 + }, +}; + +static void __init ixdp2x01_map_io(void) +{ + ixp2000_map_io(); + + iotable_init(&ixdp2x01_io_desc, 1); + + early_serial_setup(&ixdp2x01_serial_ports[0]); + early_serial_setup(&ixdp2x01_serial_ports[1]); +} + + +/************************************************************************* + * IXDP2x01 timer tick configuration + *************************************************************************/ +static unsigned int ixdp2x01_clock; + +static int __init ixdp2x01_clock_setup(char *str) +{ + ixdp2x01_clock = simple_strtoul(str, NULL, 10); + + return 1; +} + +__setup("ixdp2x01_clock=", ixdp2x01_clock_setup); + +static void __init ixdp2x01_init_time(void) +{ + if (!ixdp2x01_clock) + ixdp2x01_clock = 50000000; + + ixp2000_init_time(ixdp2x01_clock); +} + +/************************************************************************* + * IXDP2x01 PCI + *************************************************************************/ +void __init ixdp2x01_pci_preinit(void) +{ + ixp2000_reg_write(IXP2000_PCI_ADDR_EXT, 0x00000000); + ixp2000_pci_preinit(); +} + +#define DEVPIN(dev, pin) ((pin) | ((dev) << 3)) + +static int __init ixdp2x01_pci_map_irq(struct pci_dev *dev, u8 slot, u8 pin) +{ + u8 bus = dev->bus->number; + u32 devpin = DEVPIN(PCI_SLOT(dev->devfn), pin); + struct pci_bus *tmp_bus = dev->bus; + + /* Primary bus, no interrupts here */ + if (bus == 0) { + return -1; + } + + /* Lookup first leaf in bus tree */ + while ((tmp_bus->parent != NULL) && (tmp_bus->parent->parent != NULL)) { + tmp_bus = tmp_bus->parent; + } + + /* Select between known bridges */ + switch (tmp_bus->self->devfn | (tmp_bus->self->bus->number << 8)) { + /* Device is located after first MB bridge */ + case 0x0008: + if (tmp_bus == dev->bus) { + /* Device is located directy after first MB bridge */ + switch (devpin) { + case DEVPIN(1, 1): /* Onboard 82546 ch 0 */ + if (machine_is_ixdp2401()) + return IRQ_IXDP2401_INTA_82546; + return -1; + case DEVPIN(1, 2): /* Onboard 82546 ch 1 */ + if (machine_is_ixdp2401()) + return IRQ_IXDP2401_INTB_82546; + return -1; + case DEVPIN(0, 1): /* PMC INTA# */ + return IRQ_IXDP2X01_SPCI_PMC_INTA; + case DEVPIN(0, 2): /* PMC INTB# */ + return IRQ_IXDP2X01_SPCI_PMC_INTB; + case DEVPIN(0, 3): /* PMC INTC# */ + return IRQ_IXDP2X01_SPCI_PMC_INTC; + case DEVPIN(0, 4): /* PMC INTD# */ + return IRQ_IXDP2X01_SPCI_PMC_INTD; + } + } + break; + case 0x0010: + if (tmp_bus == dev->bus) { + /* Device is located directy after second MB bridge */ + /* Secondary bus of second bridge */ + switch (devpin) { + case DEVPIN(0, 1): /* DB#0 */ + return IRQ_IXDP2X01_SPCI_DB_0; + case DEVPIN(1, 1): /* DB#1 */ + return IRQ_IXDP2X01_SPCI_DB_1; + } + } else { + /* Device is located indirectly after second MB bridge */ + /* Not supported now */ + } + break; + } + + return -1; +} + + +static int ixdp2x01_pci_setup(int nr, struct pci_sys_data *sys) +{ + sys->mem_offset = 0xe0000000; + + if (machine_is_ixdp2801()) + sys->mem_offset -= ((*IXP2000_PCI_ADDR_EXT & 0xE000) << 16); + + return ixp2000_pci_setup(nr, sys); +} + +struct hw_pci ixdp2x01_pci __initdata = { + .nr_controllers = 1, + .setup = ixdp2x01_pci_setup, + .preinit = ixdp2x01_pci_preinit, + .scan = ixp2000_pci_scan_bus, + .map_irq = ixdp2x01_pci_map_irq, +}; + +int __init ixdp2x01_pci_init(void) +{ + + pci_common_init(&ixdp2x01_pci); + return 0; +} + +subsys_initcall(ixdp2x01_pci_init); + +/************************************************************************* + * IXDP2x01 Machine Intialization + *************************************************************************/ +static struct flash_platform_data ixdp2x01_flash_platform_data = { + .map_name = "cfi_probe", + .width = 1, +}; + +static unsigned long ixdp2x01_flash_bank_setup(unsigned long ofs) +{ + *IXDP2X01_CPLD_FLASH_REG = + ((ofs >> IXDP2X01_FLASH_WINDOW_BITS) | IXDP2X01_CPLD_FLASH_INTERN); + return (ofs & IXDP2X01_FLASH_WINDOW_MASK); +} + +static struct ixp2000_flash_data ixdp2x01_flash_data = { + .platform_data = &ixdp2x01_flash_platform_data, + .bank_setup = ixdp2x01_flash_bank_setup +}; + +static struct resource ixdp2x01_flash_resource = { + .start = 0xc4000000, + .end = 0xc4000000 + 0x01ffffff, + .flags = IORESOURCE_MEM, +}; + +static struct platform_device ixdp2x01_flash = { + .name = "IXP2000-Flash", + .id = 0, + .dev = { + .platform_data = &ixdp2x01_flash_data, + }, + .num_resources = 1, + .resource = &ixdp2x01_flash_resource, +}; + +static struct platform_device *ixdp2x01_devices[] __initdata = { + &ixdp2x01_flash +}; + +static void __init ixdp2x01_init_machine(void) +{ + *IXDP2X01_CPLD_FLASH_REG = + (IXDP2X01_CPLD_FLASH_BANK_MASK | IXDP2X01_CPLD_FLASH_INTERN); + + ixdp2x01_flash_data.nr_banks = + ((*IXDP2X01_CPLD_FLASH_REG & IXDP2X01_CPLD_FLASH_BANK_MASK) + 1); + + platform_add_devices(ixdp2x01_devices, ARRAY_SIZE(ixdp2x01_devices)); +} + + +#ifdef CONFIG_ARCH_IXDP2401 +MACHINE_START(IXDP2401, "Intel IXDP2401 Development Platform") + MAINTAINER("MontaVista Software, Inc.") + BOOT_MEM(0x00000000, IXP2000_UART_PHYS_BASE, IXP2000_UART_VIRT_BASE) + BOOT_PARAMS(0x00000100) + MAPIO(ixdp2x01_map_io) + INITIRQ(ixdp2x01_init_irq) + INITTIME(ixdp2x01_init_time) + INIT_MACHINE(ixdp2x01_init_machine) +MACHINE_END +#endif + +#ifdef CONFIG_ARCH_IXDP2801 +MACHINE_START(IXDP2801, "Intel IXDP2801 Development Platform") + MAINTAINER("MontaVista Software, Inc.") + BOOT_MEM(0x00000000, IXP2000_UART_PHYS_BASE, IXP2000_UART_VIRT_BASE) + BOOT_PARAMS(0x00000100) + MAPIO(ixdp2x01_map_io) + INITIRQ(ixdp2x01_init_irq) + INITTIME(ixdp2x01_init_time) + INIT_MACHINE(ixdp2x01_init_machine) +MACHINE_END +#endif + + diff --git a/arch/arm/mach-ixp2000/pci.c b/arch/arm/mach-ixp2000/pci.c new file mode 100644 index 000000000..d08cebe00 --- /dev/null +++ b/arch/arm/mach-ixp2000/pci.c @@ -0,0 +1,233 @@ +/* + * arch/arm/mach-ixp2000/pci.c + * + * PCI routines for IXDP2400/IXDP2800 boards + * + * Original Author: Naeem Afzal + * Maintained by: Deepak Saxena + * + * Copyright 2002 Intel Corp. + * Copyright (C) 2003-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. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include + +static int pci_master_aborts = 0; + +static int clear_master_aborts(void); + +static u32 * +ixp2000_pci_config_addr(unsigned int bus_nr, unsigned int devfn, int where) +{ + u32 *paddress; + + if (PCI_SLOT(devfn) > 7) + return 0; + + /* Must be dword aligned */ + where &= ~3; + + /* + * For top bus, generate type 0, else type 1 + */ + if (!bus_nr) { + /* only bits[23:16] are used for IDSEL */ + paddress = (u32 *) (IXP2000_PCI_CFG0_VIRT_BASE + | (1 << (PCI_SLOT(devfn) + 16)) + | (PCI_FUNC(devfn) << 8) | where); + } else { + paddress = (u32 *) (IXP2000_PCI_CFG1_VIRT_BASE + | (bus_nr << 16) + | (PCI_SLOT(devfn) << 11) + | (PCI_FUNC(devfn) << 8) | where); + } + + return paddress; +} + +/* + * Mask table, bits to mask for quantity of size 1, 2 or 4 bytes. + * 0 and 3 are not valid indexes... + */ +static u32 bytemask[] = { + /*0*/ 0, + /*1*/ 0xff, + /*2*/ 0xffff, + /*3*/ 0, + /*4*/ 0xffffffff, +}; + + +int ixp2000_pci_read_config(struct pci_bus *bus, unsigned int devfn, int where, + int size, u32 *value) +{ + u32 n; + u32 *addr; + + n = where % 4; + + addr = ixp2000_pci_config_addr(bus->number, devfn, where); + if (!addr) + return PCIBIOS_DEVICE_NOT_FOUND; + + pci_master_aborts = 0; + *value = (*addr >> (8*n)) & bytemask[size]; + if (pci_master_aborts) { + pci_master_aborts = 0; + *value = 0xffffffff; + return PCIBIOS_DEVICE_NOT_FOUND; + } + + return PCIBIOS_SUCCESSFUL; +} + +/* + * We don't do error checks by callling clear_master_aborts() b/c the + * assumption is that the caller did a read first to make sure a device + * exists. + */ +int ixp2000_pci_write_config(struct pci_bus *bus, unsigned int devfn, int where, + int size, u32 value) +{ + u32 mask; + u32 *addr; + u32 temp; + + mask = ~(bytemask[size] << ((where % 0x4) * 8)); + addr = ixp2000_pci_config_addr(bus->number, devfn, where); + if (!addr) + return PCIBIOS_DEVICE_NOT_FOUND; + temp = (u32) (value) << ((where % 0x4) * 8); + *addr = (*addr & mask) | temp; + + clear_master_aborts(); + + return PCIBIOS_SUCCESSFUL; +} + + +static struct pci_ops ixp2000_pci_ops = { + .read = ixp2000_pci_read_config, + .write = ixp2000_pci_write_config +}; + +struct pci_bus *ixp2000_pci_scan_bus(int nr, struct pci_sys_data *sysdata) +{ + return pci_scan_bus(sysdata->busnr, &ixp2000_pci_ops, sysdata); +} + + +int ixp2000_pci_abort_handler(unsigned long addr, unsigned int fsr, struct pt_regs *regs) +{ + + volatile u32 temp; + + pci_master_aborts = 1; + + cli(); + temp = *(IXP2000_PCI_CONTROL); + if (temp & ((1 << 8) | (1 << 5))) { + *(IXP2000_PCI_CONTROL) = temp; + } + + temp = *(IXP2000_PCI_CMDSTAT); + if (temp & (1 << 29)) { + while (temp & (1 << 29)) { + *(IXP2000_PCI_CMDSTAT) = temp; + temp = *(IXP2000_PCI_CMDSTAT); + } + } + sti(); + + /* + * If it was an imprecise abort, then we need to correct the + * return address to be _after_ the instruction. + */ + if (fsr & (1 << 10)) + regs->ARM_pc += 4; + + return 0; +} + +int +clear_master_aborts(void) +{ + volatile u32 temp; + + cli(); + temp = *(IXP2000_PCI_CONTROL); + if (temp & ((1 << 8) | (1 << 5))) { + *(IXP2000_PCI_CONTROL) = temp; + } + + temp = *(IXP2000_PCI_CMDSTAT); + if (temp & (1 << 29)) { + while (temp & (1 << 29)) { + *(IXP2000_PCI_CMDSTAT) = temp; + temp = *(IXP2000_PCI_CMDSTAT); + } + } + sti(); + + return 0; +} + +void __init +ixp2000_pci_preinit(void) +{ + hook_fault_code(16+6, ixp2000_pci_abort_handler, SIGBUS, + "PCI config cycle to non-existent device"); +} + + +/* + * IXP2000 systems often have large resource requirements, so we just + * use our own resource space. + */ +static struct resource ixp2000_pci_mem_space = { + .start = 0x00000000, + .end = 0xffffffff, + .flags = IORESOURCE_MEM, + .name = "PCI Mem Space" +}; + +static struct resource ixp2000_pci_io_space = { + .start = 0x00000000, + .end = 0xffffffff, + .flags = IORESOURCE_IO, + .name = "PCI I/O Space" +}; + +int ixp2000_pci_setup(int nr, struct pci_sys_data *sys) +{ + if (nr >= 1) + return 0; + + sys->resource[0] = &ixp2000_pci_io_space; + sys->resource[1] = &ixp2000_pci_mem_space; + sys->resource[2] = NULL; + + return 1; +} + diff --git a/arch/arm/mach-ixp4xx/common.c b/arch/arm/mach-ixp4xx/common.c index a4edbe0eb..24ffa3238 100644 --- a/arch/arm/mach-ixp4xx/common.c +++ b/arch/arm/mach-ixp4xx/common.c @@ -207,7 +207,7 @@ void __init ixp4xx_init_irq(void) static unsigned volatile last_jiffy_time; -#define CLOCK_TICKS_PER_USEC (CLOCK_TICK_RATE / USEC_PER_SEC) +#define CLOCK_TICKS_PER_USEC ((CLOCK_TICK_RATE + USEC_PER_SEC/2) / USEC_PER_SEC) /* IRQs are disabled before entering here from do_gettimeofday() */ static unsigned long ixp4xx_gettimeoffset(void) diff --git a/arch/arm/mach-lh7a40x/time.c b/arch/arm/mach-lh7a40x/time.c index 61ada24c7..f4943cacb 100644 --- a/arch/arm/mach-lh7a40x/time.c +++ b/arch/arm/mach-lh7a40x/time.c @@ -22,12 +22,12 @@ #include #if HZ < 100 -# define TIMER_CONTROL TIMER_CONTROL1 -# define TIMER_LOAD TIMER_LOAD1 +# define TIMER_CONTROL TIMER_CONTROL2 +# define TIMER_LOAD TIMER_LOAD2 # define TIMER_CONSTANT (508469/HZ) # define TIMER_MODE (TIMER_C_ENABLE | TIMER_C_PERIODIC | TIMER_C_508KHZ) -# define TIMER_EOI TIMER_EOI1 -# define TIMER_IRQ IRQ_T1UI +# define TIMER_EOI TIMER_EOI2 +# define TIMER_IRQ IRQ_T2UI #else # define TIMER_CONTROL TIMER_CONTROL3 # define TIMER_LOAD TIMER_LOAD3 diff --git a/arch/arm/mach-omap/board-h2.c b/arch/arm/mach-omap/board-h2.c new file mode 100644 index 000000000..1441089d9 --- /dev/null +++ b/arch/arm/mach-omap/board-h2.c @@ -0,0 +1,115 @@ +/* + * linux/arch/arm/mach-omap/board-h2.c + * + * Board specific inits for OMAP-1610 H2 + * + * Copyright (C) 2001 RidgeRun, Inc. + * Author: Greg Lonnon + * + * Copyright (C) 2002 MontaVista Software, Inc. + * + * Separated FPGA interrupts from innovator1510.c and cleaned up for 2.6 + * Copyright (C) 2004 Nokia Corporation by Tony Lindrgen + * + * H2 specific changes and cleanup + * Copyright (C) 2004 Nokia Corporation by Imre Deak + * + * This program is free software; you can 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 +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include + +#include "common.h" + +extern void __init omap_init_time(void); + +static struct map_desc h2_io_desc[] __initdata = { +{ OMAP1610_ETHR_BASE, OMAP1610_ETHR_START, OMAP1610_ETHR_SIZE,MT_DEVICE }, +{ OMAP1610_NOR_FLASH_BASE, OMAP1610_NOR_FLASH_START, OMAP1610_NOR_FLASH_SIZE, + MT_DEVICE }, +}; + +static struct resource h2_smc91x_resources[] = { + [0] = { + .start = OMAP1610_ETHR_START, /* Physical */ + .end = OMAP1610_ETHR_START + SZ_4K, + .flags = IORESOURCE_MEM, + }, + [1] = { + .start = 0, /* Really GPIO 0 */ + .end = 0, + .flags = IORESOURCE_IRQ, + }, +}; + +static struct platform_device h2_smc91x_device = { + .name = "smc91x", + .id = 0, + .num_resources = ARRAY_SIZE(h2_smc91x_resources), + .resource = h2_smc91x_resources, +}; + +static struct platform_device *h2_devices[] __initdata = { + &h2_smc91x_device, +}; + +void h2_init_irq(void) +{ + omap_init_irq(); +} + +static struct omap_usb_config h2_usb_config __initdata = { + /* usb1 has a Mini-AB port and external isp1301 transceiver */ + .otg = 2, + +#ifdef CONFIG_USB_GADGET_OMAP + .hmc_mode = 19, // 0:host(off) 1:dev|otg 2:disabled + // .hmc_mode = 21, // 0:host(off) 1:dev(loopback) 2:host(loopback) +#elif defined(CONFIG_USB_OHCI_HCD) || defined(CONFIG_USB_OHCI_HCD_MODULE) + /* NONSTANDARD CABLE NEEDED (B-to-Mini-B) */ + .hmc_mode = 20, // 1:dev|otg(off) 1:host 2:disabled +#endif + + .pins[1] = 3, +}; + +static struct omap_board_config_kernel h2_config[] = { + { OMAP_TAG_USB, &h2_usb_config }, +}; + +static void __init h2_init(void) +{ + platform_add_devices(h2_devices, ARRAY_SIZE(h2_devices)); + omap_board_config = h2_config; + omap_board_config_size = ARRAY_SIZE(h2_config); +} + +static void __init h2_map_io(void) +{ + omap_map_io(); + iotable_init(h2_io_desc, ARRAY_SIZE(h2_io_desc)); +} + +MACHINE_START(OMAP_H2, "TI-H2") + MAINTAINER("Imre Deak ") + BOOT_MEM(0x10000000, 0xfff00000, 0xfef00000) + BOOT_PARAMS(0x10000100) + MAPIO(h2_map_io) + INITIRQ(h2_init_irq) + INIT_MACHINE(h2_init) + INITTIME(omap_init_time) +MACHINE_END diff --git a/arch/arm/mach-omap/board-h3.c b/arch/arm/mach-omap/board-h3.c new file mode 100644 index 000000000..d573b0cdf --- /dev/null +++ b/arch/arm/mach-omap/board-h3.c @@ -0,0 +1,90 @@ +/* + * linux/arch/arm/mach-omap/board-h3.c + * + * This file contains OMAP1710 H3 specific code. + * + * Copyright (C) 2004 Texas Instruments, Inc. + * Copyright (C) 2002 MontaVista Software, Inc. + * Copyright (C) 2001 RidgeRun, Inc. + * Author: RidgeRun, Inc. + * Greg Lonnon (glonnon@ridgerun.com) or info@ridgerun.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 +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include "common.h" + +extern void __init omap_init_time(void); + +void h3_init_irq(void) +{ + omap_init_irq(); +} + +static struct resource smc91x_resources[] = { + [0] = { + .start = OMAP1710_ETHR_START, /* Physical */ + .end = OMAP1710_ETHR_START + OMAP1710_ETHR_SIZE, + .flags = IORESOURCE_MEM, + }, + [1] = { + .start = 0, + .end = 0, + .flags = IORESOURCE_IRQ, + }, +}; + +static struct platform_device smc91x_device = { + .name = "smc91x", + .id = 0, + .num_resources = ARRAY_SIZE(smc91x_resources), + .resource = smc91x_resources, +}; + +static struct platform_device *devices[] __initdata = { + &smc91x_device, +}; + +static void __init h3_init(void) +{ + (void) platform_add_devices(devices, ARRAY_SIZE(devices)); +} + +static struct map_desc h3_io_desc[] __initdata = { +{ OMAP1710_ETHR_BASE, OMAP1710_ETHR_START, OMAP1710_ETHR_SIZE, MT_DEVICE }, +{ OMAP_NOR_FLASH_BASE, OMAP_NOR_FLASH_START, OMAP_NOR_FLASH_SIZE, MT_DEVICE }, +}; + +static void __init h3_map_io(void) +{ + omap_map_io(); + iotable_init(h3_io_desc, ARRAY_SIZE(h3_io_desc)); +} + +MACHINE_START(OMAP_H3, "TI OMAP1710 H3 board") + MAINTAINER("Texas Instruments, Inc.") + BOOT_MEM(0x10000000, 0xfff00000, 0xfef00000) + BOOT_PARAMS(0x10000100) + MAPIO(h3_map_io) + INITIRQ(h3_init_irq) + INIT_MACHINE(h3_init) + INITTIME(omap_init_time) +MACHINE_END diff --git a/arch/arm/mach-omap/clocks.c b/arch/arm/mach-omap/clocks.c index bda7c6f38..b50785659 100644 --- a/arch/arm/mach-omap/clocks.c +++ b/arch/arm/mach-omap/clocks.c @@ -36,6 +36,8 @@ #include #include +extern void start_mputimer1(unsigned long load_val); + /* Input clock in MHz */ static unsigned int source_clock = 12; @@ -239,7 +241,7 @@ int ck_auto_unclock = 1; int ck_debug = 0; #define CK_MAX_PLL_FREQ OMAP_CK_MAX_RATE -static __u8 ck_valid_table[CK_MAX_PLL_FREQ / 8 + 1]; +static __u32 ck_valid_table[CK_MAX_PLL_FREQ / 32 + 1]; static __u8 ck_lookup_table[CK_MAX_PLL_FREQ]; int @@ -615,11 +617,11 @@ __ck_make_lookup_table(void) int __init init_ck(void) { - const struct omap_clock_info *info; + const struct omap_clock_config *info; int crystal_type = 0; /* Default 12 MHz */ __ck_make_lookup_table(); - info = omap_get_per_info(OMAP_TAG_CLOCK, struct omap_clock_info); + info = omap_get_config(OMAP_TAG_CLOCK, struct omap_clock_config); if (info != NULL) { if (!cpu_is_omap1510()) crystal_type = info->system_clock_type; @@ -645,7 +647,8 @@ init_ck(void) #elif defined(CONFIG_OMAP_ARM_182MHZ) && defined(CONFIG_ARCH_OMAP730) omap_writew(0x250E, ARM_CKCTL); omap_writew(0x2710, DPLL_CTL); -#elif defined(CONFIG_OMAP_ARM_192MHZ) && (defined(CONFIG_ARCH_OMAP1610) || defined(CONFIG_ARCH_OMAP5912)) +#elif defined(CONFIG_OMAP_ARM_192MHZ) && (defined(CONFIG_ARCH_OMAP1610) || defined(CONFIG_ARCH_OMAP5912) \ + || defined(CONFIG_ARCH_OMAP1710)) omap_writew(0x150f, ARM_CKCTL); if (crystal_type == 2) { source_clock = 13; /* MHz */ diff --git a/arch/arm/mach-omap/common.c b/arch/arm/mach-omap/common.c index 43aaa1c6c..17d82a7f3 100644 --- a/arch/arm/mach-omap/common.c +++ b/arch/arm/mach-omap/common.c @@ -104,7 +104,7 @@ static struct map_desc omap1510_io_desc[] __initdata = { }; #endif -#ifdef CONFIG_ARCH_OMAP1610 +#if defined(CONFIG_ARCH_OMAP1610) || defined(CONFIG_ARCH_OMAP1710) static struct map_desc omap1610_io_desc[] __initdata = { { OMAP1610_DSP_BASE, OMAP1610_DSP_START, OMAP1610_DSP_SIZE, MT_DEVICE }, { OMAP1610_DSPREG_BASE, OMAP1610_DSPREG_START, OMAP1610_DSPREG_SIZE, MT_DEVICE }, @@ -147,8 +147,8 @@ static void __init _omap_map_io(void) iotable_init(omap1510_io_desc, ARRAY_SIZE(omap1510_io_desc)); } #endif -#ifdef CONFIG_ARCH_OMAP1610 - if (cpu_is_omap1610()) { +#if defined(CONFIG_ARCH_OMAP1610) || defined(CONFIG_ARCH_OMAP1710) + if (cpu_is_omap1610() || cpu_is_omap1710()) { iotable_init(omap1610_io_desc, ARRAY_SIZE(omap1610_io_desc)); } #endif @@ -181,13 +181,18 @@ void omap_map_io(void) extern int omap_bootloader_tag_len; extern u8 omap_bootloader_tag[]; -const void *__omap_get_per_info(u16 tag, size_t len) +struct omap_board_config_kernel *omap_board_config; +int omap_board_config_size = 0; + +const void *__omap_get_config(u16 tag, size_t len) { - struct omap_board_info_entry *info = NULL; + struct omap_board_config_entry *info = NULL; + struct omap_board_config_kernel *kinfo = NULL; + int i; #ifdef CONFIG_OMAP_BOOT_TAG if (omap_bootloader_tag_len > 4) - info = (struct omap_board_info_entry *) omap_bootloader_tag; + info = (struct omap_board_config_entry *) omap_bootloader_tag; while (info != NULL) { u8 *next; @@ -198,26 +203,38 @@ const void *__omap_get_per_info(u16 tag, size_t len) if (next >= omap_bootloader_tag + omap_bootloader_tag_len) info = NULL; else - info = (struct omap_board_info_entry *) next; + info = (struct omap_board_config_entry *) next; + } + if (info != NULL) { + /* Check the length as a lame attempt to check for + * binary inconsistancy. */ + if (info->len != len) { + printk(KERN_ERR "OMAP peripheral config: Length mismatch with tag %x (want %d, got %d)\n", + tag, len, info->len); + return NULL; + } + return info->data; } #endif - if (info == NULL) - return NULL; - if (info->len != len) { - printk(KERN_ERR "OMAP per_info: Length mismatch with tag %x (want %d, got %d)\n", - tag, len, info->len); - return NULL; + /* Try to find the config from the board-specific structures + * in the kernel. */ + for (i = 0; i < omap_board_config_size; i++) { + if (omap_board_config[i].tag == tag) { + kinfo = &omap_board_config[i]; + break; + } } - - return info->data; + if (kinfo == NULL) + return NULL; + return kinfo->data; } -EXPORT_SYMBOL(__omap_get_per_info); +EXPORT_SYMBOL(__omap_get_config); static int __init omap_add_serial_console(void) { - const struct omap_uart_info *info; + const struct omap_uart_config *info; - info = omap_get_per_info(OMAP_TAG_UART, struct omap_uart_info); + info = omap_get_config(OMAP_TAG_UART, struct omap_uart_config); if (info != NULL && info->console_uart) { static char speed[11], *opt = NULL; diff --git a/arch/arm/mach-omap/fpga.c b/arch/arm/mach-omap/fpga.c index d9246b1dc..f0ce5c12a 100644 --- a/arch/arm/mach-omap/fpga.c +++ b/arch/arm/mach-omap/fpga.c @@ -31,19 +31,9 @@ #include #include -unsigned char fpga_read(int reg) -{ - return __raw_readb(reg); -} - -void fpga_write(unsigned char val, int reg) -{ - __raw_writeb(val, reg); -} - static void fpga_mask_irq(unsigned int irq) { - irq -= IH_FPGA_BASE; + irq -= OMAP1510_IH_FPGA_BASE; if (irq < 8) __raw_writeb((__raw_readb(OMAP1510_FPGA_IMR_LO) @@ -76,7 +66,7 @@ static void fpga_ack_irq(unsigned int irq) static void fpga_unmask_irq(unsigned int irq) { - irq -= IH_FPGA_BASE; + irq -= OMAP1510_IH_FPGA_BASE; if (irq < 8) __raw_writeb((__raw_readb(OMAP1510_FPGA_IMR_LO) | (1 << irq)), @@ -114,8 +104,8 @@ void innovator_fpga_IRQ_demux(unsigned int irq, struct irqdesc *desc, break; } - for (fpga_irq = IH_FPGA_BASE; - (fpga_irq < (IH_FPGA_BASE + NR_FPGA_IRQS)) && stat; + for (fpga_irq = OMAP1510_IH_FPGA_BASE; + (fpga_irq < (OMAP1510_IH_FPGA_BASE + NR_FPGA_IRQS)) && stat; fpga_irq++, stat >>= 1) { if (stat & 1) { d = irq_desc + fpga_irq; @@ -162,7 +152,7 @@ static struct irqchip omap_fpga_irq = { * interrupts at the interrupt controller via disable_irq/enable_irq * could pose a problem. */ -void fpga_init_irq(void) +void omap1510_fpga_init_irq(void) { int i; @@ -170,9 +160,9 @@ void fpga_init_irq(void) __raw_writeb(0, OMAP1510_FPGA_IMR_HI); __raw_writeb(0, INNOVATOR_FPGA_IMR2); - for (i = IH_FPGA_BASE; i < (IH_FPGA_BASE + NR_FPGA_IRQS); i++) { + for (i = OMAP1510_IH_FPGA_BASE; i < (OMAP1510_IH_FPGA_BASE + NR_FPGA_IRQS); i++) { - if (i == INT_FPGA_TS) { + if (i == OMAP1510_INT_FPGA_TS) { /* * The touchscreen interrupt is level-sensitive, so * we'll use the regular mask_ack routine for it. @@ -201,9 +191,7 @@ void fpga_init_irq(void) omap_request_gpio(13); omap_set_gpio_direction(13, 1); omap_set_gpio_edge_ctrl(13, OMAP_GPIO_RISING_EDGE); - set_irq_chained_handler(INT_FPGA, innovator_fpga_IRQ_demux); + set_irq_chained_handler(OMAP1510_INT_FPGA, innovator_fpga_IRQ_demux); } -EXPORT_SYMBOL(fpga_init_irq); -EXPORT_SYMBOL(fpga_read); -EXPORT_SYMBOL(fpga_write); +EXPORT_SYMBOL(omap1510_fpga_init_irq); diff --git a/arch/arm/mach-omap/leds-h2p2-debug.c b/arch/arm/mach-omap/leds-h2p2-debug.c new file mode 100644 index 000000000..8ff27af0b --- /dev/null +++ b/arch/arm/mach-omap/leds-h2p2-debug.c @@ -0,0 +1,104 @@ +/* + * linux/arch/arm/mach-omap/leds-h2p2-debug.c + * + * Copyright 2003 by Texas Instruments Incorporated + * + */ +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include + +#include "leds.h" + +void h2p2_dbg_leds_event(led_event_t evt) +{ + unsigned long flags; + static unsigned long hw_led_state = 0; + + local_irq_save(flags); + + switch (evt) { + case led_start: + hw_led_state |= H2P2_DBG_FPGA_LED_STARTSTOP; + break; + + case led_stop: + hw_led_state &= ~H2P2_DBG_FPGA_LED_STARTSTOP; + break; + + case led_claim: + hw_led_state |= H2P2_DBG_FPGA_LED_CLAIMRELEASE; + break; + + case led_release: + hw_led_state &= ~H2P2_DBG_FPGA_LED_CLAIMRELEASE; + break; + +#ifdef CONFIG_LEDS_TIMER + case led_timer: + /* + * Toggle Timer LED + */ + if (hw_led_state & H2P2_DBG_FPGA_LED_TIMER) + hw_led_state &= ~H2P2_DBG_FPGA_LED_TIMER; + else + hw_led_state |= H2P2_DBG_FPGA_LED_TIMER; + break; +#endif + +#ifdef CONFIG_LEDS_CPU + case led_idle_start: + hw_led_state |= H2P2_DBG_FPGA_LED_IDLE; + break; + + case led_idle_end: + hw_led_state &= ~H2P2_DBG_FPGA_LED_IDLE; + break; +#endif + + case led_halted: + if (hw_led_state & H2P2_DBG_FPGA_LED_HALTED) + hw_led_state &= ~H2P2_DBG_FPGA_LED_HALTED; + else + hw_led_state |= H2P2_DBG_FPGA_LED_HALTED; + break; + + case led_green_on: + break; + + case led_green_off: + break; + + case led_amber_on: + break; + + case led_amber_off: + break; + + case led_red_on: + break; + + case led_red_off: + break; + + default: + break; + } + + + /* + * Actually burn the LEDs + */ + __raw_writew(~hw_led_state & 0xffff, H2P2_DBG_FPGA_LEDS); + + local_irq_restore(flags); +} diff --git a/arch/arm/mach-omap/leds.c b/arch/arm/mach-omap/leds.c index e3f378b4a..b6d0c7203 100644 --- a/arch/arm/mach-omap/leds.c +++ b/arch/arm/mach-omap/leds.c @@ -3,6 +3,7 @@ * * OMAP LEDs dispatcher */ +#include #include #include @@ -11,17 +12,17 @@ #include "leds.h" static int __init -omap1510_leds_init(void) +omap_leds_init(void) { if (machine_is_omap_innovator()) leds_event = innovator_leds_event; - else if (machine_is_omap_perseus2()) { - leds_event = perseus2_leds_event; + else if (machine_is_omap_h2() || machine_is_omap_perseus2()) { + leds_event = h2p2_dbg_leds_event; } leds_event(led_start); return 0; } -__initcall(omap1510_leds_init); +__initcall(omap_leds_init); diff --git a/arch/arm/mach-omap/leds.h b/arch/arm/mach-omap/leds.h index ba2eb59d1..7571e2d63 100644 --- a/arch/arm/mach-omap/leds.h +++ b/arch/arm/mach-omap/leds.h @@ -1,2 +1,2 @@ extern void innovator_leds_event(led_event_t evt); -extern void perseus2_leds_event(led_event_t evt); +extern void h2p2_dbg_leds_event(led_event_t evt); diff --git a/arch/arm/mach-omap/mcbsp.c b/arch/arm/mach-omap/mcbsp.c new file mode 100644 index 000000000..d334395f1 --- /dev/null +++ b/arch/arm/mach-omap/mcbsp.c @@ -0,0 +1,669 @@ +/* + * linux/arch/arm/omap/mcbsp.c + * + * Copyright (C) 2004 Nokia Corporation + * Author: Samuel Ortiz + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Multichannel mode not supported. + */ + +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include + +#ifdef CONFIG_MCBSP_DEBUG +#define DBG(x...) printk(x) +#else +#define DBG(x...) do { } while (0) +#endif + +struct omap_mcbsp { + u32 io_base; + u8 id; + u8 free; + omap_mcbsp_word_length rx_word_length; + omap_mcbsp_word_length tx_word_length; + + /* IRQ based TX/RX */ + int rx_irq; + int tx_irq; + + /* DMA stuff */ + u8 dma_rx_sync; + short dma_rx_lch; + u8 dma_tx_sync; + short dma_tx_lch; + + /* Completion queues */ + struct completion tx_irq_completion; + struct completion rx_irq_completion; + struct completion tx_dma_completion; + struct completion rx_dma_completion; + + spinlock_t lock; +}; + +static struct omap_mcbsp mcbsp[OMAP_MAX_MCBSP_COUNT]; + + +static void omap_mcbsp_dump_reg(u8 id) +{ + DBG("**** MCBSP%d regs ****\n", mcbsp[id].id); + DBG("DRR2: 0x%04x\n", OMAP_MCBSP_READ(mcbsp[id].io_base, DRR2)); + DBG("DRR1: 0x%04x\n", OMAP_MCBSP_READ(mcbsp[id].io_base, DRR1)); + DBG("DXR2: 0x%04x\n", OMAP_MCBSP_READ(mcbsp[id].io_base, DXR2)); + DBG("DXR1: 0x%04x\n", OMAP_MCBSP_READ(mcbsp[id].io_base, DXR1)); + DBG("SPCR2: 0x%04x\n", OMAP_MCBSP_READ(mcbsp[id].io_base, SPCR2)); + DBG("SPCR1: 0x%04x\n", OMAP_MCBSP_READ(mcbsp[id].io_base, SPCR1)); + DBG("RCR2: 0x%04x\n", OMAP_MCBSP_READ(mcbsp[id].io_base, RCR2)); + DBG("RCR1: 0x%04x\n", OMAP_MCBSP_READ(mcbsp[id].io_base, RCR1)); + DBG("XCR2: 0x%04x\n", OMAP_MCBSP_READ(mcbsp[id].io_base, XCR2)); + DBG("XCR1: 0x%04x\n", OMAP_MCBSP_READ(mcbsp[id].io_base, XCR1)); + DBG("SRGR2: 0x%04x\n", OMAP_MCBSP_READ(mcbsp[id].io_base, SRGR2)); + DBG("SRGR1: 0x%04x\n", OMAP_MCBSP_READ(mcbsp[id].io_base, SRGR1)); + DBG("PCR0: 0x%04x\n", OMAP_MCBSP_READ(mcbsp[id].io_base, PCR0)); + DBG("***********************\n"); +} + + +static irqreturn_t omap_mcbsp_tx_irq_handler(int irq, void *dev_id, struct pt_regs *regs) +{ + struct omap_mcbsp * mcbsp_tx = (struct omap_mcbsp *)(dev_id); + + DBG("TX IRQ callback : 0x%x\n", OMAP_MCBSP_READ(mcbsp_tx->io_base, SPCR2)); + + complete(&mcbsp_tx->tx_irq_completion); + return IRQ_HANDLED; +} + +static irqreturn_t omap_mcbsp_rx_irq_handler(int irq, void *dev_id, struct pt_regs *regs) +{ + struct omap_mcbsp * mcbsp_rx = (struct omap_mcbsp *)(dev_id); + + DBG("RX IRQ callback : 0x%x\n", OMAP_MCBSP_READ(mcbsp_rx->io_base, SPCR2)); + + complete(&mcbsp_rx->rx_irq_completion); + return IRQ_HANDLED; +} + + +static void omap_mcbsp_tx_dma_callback(int lch, u16 ch_status, void *data) +{ + struct omap_mcbsp * mcbsp_dma_tx = (struct omap_mcbsp *)(data); + + DBG("TX DMA callback : 0x%x\n", OMAP_MCBSP_READ(mcbsp_dma_tx->io_base, SPCR2)); + + /* We can free the channels */ + omap_free_dma(mcbsp_dma_tx->dma_tx_lch); + mcbsp_dma_tx->dma_tx_lch = -1; + + complete(&mcbsp_dma_tx->tx_dma_completion); +} + +static void omap_mcbsp_rx_dma_callback(int lch, u16 ch_status, void *data) +{ + struct omap_mcbsp * mcbsp_dma_rx = (struct omap_mcbsp *)(data); + + DBG("RX DMA callback : 0x%x\n", OMAP_MCBSP_READ(mcbsp_dma_rx->io_base, SPCR2)); + + /* We can free the channels */ + omap_free_dma(mcbsp_dma_rx->dma_rx_lch); + mcbsp_dma_rx->dma_rx_lch = -1; + + complete(&mcbsp_dma_rx->rx_dma_completion); +} + + +/* + * omap_mcbsp_config simply write a config to the + * appropriate McBSP. + * You either call this function or set the McBSP registers + * by yourself before calling omap_mcbsp_start(). + */ + +void omap_mcbsp_config(unsigned int id, const struct omap_mcbsp_reg_cfg * config) +{ + u32 io_base = mcbsp[id].io_base; + + DBG("OMAP-McBSP: McBSP%d io_base: 0x%8x\n", id+1, io_base); + + /* We write the given config */ + OMAP_MCBSP_WRITE(io_base, SPCR2, config->spcr2); + OMAP_MCBSP_WRITE(io_base, SPCR1, config->spcr1); + OMAP_MCBSP_WRITE(io_base, RCR2, config->rcr2); + OMAP_MCBSP_WRITE(io_base, RCR1, config->rcr1); + OMAP_MCBSP_WRITE(io_base, XCR2, config->xcr2); + OMAP_MCBSP_WRITE(io_base, XCR1, config->xcr1); + OMAP_MCBSP_WRITE(io_base, SRGR2, config->srgr2); + OMAP_MCBSP_WRITE(io_base, SRGR1, config->srgr1); + OMAP_MCBSP_WRITE(io_base, SRGR2, config->mcr2); + OMAP_MCBSP_WRITE(io_base, SRGR1, config->mcr1); + OMAP_MCBSP_WRITE(io_base, PCR0, config->pcr0); +} + + + +static int omap_mcbsp_check(unsigned int id) +{ + if (cpu_is_omap730()) { + if (id > OMAP_MAX_MCBSP_COUNT - 1) { + printk(KERN_ERR "OMAP-McBSP: McBSP%d doesn't exist\n", id + 1); + return -1; + } + return 0; + } + + if (cpu_is_omap1510() || cpu_is_omap1610() || cpu_is_omap1710()) { + if (id > OMAP_MAX_MCBSP_COUNT) { + printk(KERN_ERR "OMAP-McBSP: McBSP%d doesn't exist\n", id + 1); + return -1; + } + return 0; + } + + return -1; +} + +#define DSP_RSTCT2 0xe1008014 + +static void omap_mcbsp_dsp_request(void) +{ + if (cpu_is_omap1510() || cpu_is_omap1610() || cpu_is_omap1710()) { + omap_writew((omap_readw(ARM_RSTCT1) | (1 << 1) | (1 << 2)), + ARM_RSTCT1); + omap_writew((omap_readw(ARM_CKCTL) | 1 << EN_DSPCK), + ARM_CKCTL); + omap_writew((omap_readw(ARM_IDLECT2) | (1 << EN_APICK)), + ARM_IDLECT2); + + /* enable 12MHz clock to mcbsp 1 & 3 */ + __raw_writew(__raw_readw(DSP_IDLECT2) | (1 << EN_XORPCK), + DSP_IDLECT2); + __raw_writew(__raw_readw(DSP_RSTCT2) | 1 | 1 << 1, + DSP_RSTCT2); + } +} + +static void omap_mcbsp_dsp_free(void) +{ + /* Useless for now */ +} + + +int omap_mcbsp_request(unsigned int id) +{ + int err; + + if (omap_mcbsp_check(id) < 0) + return -EINVAL; + + /* + * On 1510, 1610 and 1710, McBSP1 and McBSP3 + * are DSP public peripherals. + */ + if (id == OMAP_MCBSP1 || id == OMAP_MCBSP3) + omap_mcbsp_dsp_request(); + + spin_lock(&mcbsp[id].lock); + if (!mcbsp[id].free) { + printk (KERN_ERR "OMAP-McBSP: McBSP%d is currently in use\n", id + 1); + spin_unlock(&mcbsp[id].lock); + return -1; + } + + mcbsp[id].free = 0; + spin_unlock(&mcbsp[id].lock); + + /* We need to get IRQs here */ + err = request_irq(mcbsp[id].tx_irq, omap_mcbsp_tx_irq_handler, 0, + "McBSP", + (void *) (&mcbsp[id])); + if (err != 0) { + printk(KERN_ERR "OMAP-McBSP: Unable to request TX IRQ %d for McBSP%d\n", + mcbsp[id].tx_irq, mcbsp[id].id); + return err; + } + + init_completion(&(mcbsp[id].tx_irq_completion)); + + + err = request_irq(mcbsp[id].rx_irq, omap_mcbsp_rx_irq_handler, 0, + "McBSP", + (void *) (&mcbsp[id])); + if (err != 0) { + printk(KERN_ERR "OMAP-McBSP: Unable to request RX IRQ %d for McBSP%d\n", + mcbsp[id].rx_irq, mcbsp[id].id); + free_irq(mcbsp[id].tx_irq, (void *) (&mcbsp[id])); + return err; + } + + init_completion(&(mcbsp[id].rx_irq_completion)); + return 0; + +} + +void omap_mcbsp_free(unsigned int id) +{ + if (omap_mcbsp_check(id) < 0) + return; + + if (id == OMAP_MCBSP1 || id == OMAP_MCBSP3) + omap_mcbsp_dsp_free(); + + spin_lock(&mcbsp[id].lock); + if (mcbsp[id].free) { + printk (KERN_ERR "OMAP-McBSP: McBSP%d was not reserved\n", id + 1); + spin_unlock(&mcbsp[id].lock); + return; + } + + mcbsp[id].free = 1; + spin_unlock(&mcbsp[id].lock); + + /* Free IRQs */ + free_irq(mcbsp[id].rx_irq, (void *) (&mcbsp[id])); + free_irq(mcbsp[id].tx_irq, (void *) (&mcbsp[id])); +} + +/* + * Here we start the McBSP, by enabling the sample + * generator, both transmitter and receivers, + * and the frame sync. + */ +void omap_mcbsp_start(unsigned int id) +{ + u32 io_base; + u16 w; + + if (omap_mcbsp_check(id) < 0) + return; + + io_base = mcbsp[id].io_base; + + mcbsp[id].rx_word_length = ((OMAP_MCBSP_READ(io_base, RCR1) >> 5) & 0x7); + mcbsp[id].tx_word_length = ((OMAP_MCBSP_READ(io_base, XCR1) >> 5) & 0x7); + + /* Start the sample generator */ + w = OMAP_MCBSP_READ(io_base, SPCR2); + OMAP_MCBSP_WRITE(io_base, SPCR2, w | (1 << 6)); + + /* Enable transmitter and receiver */ + w = OMAP_MCBSP_READ(io_base, SPCR2); + OMAP_MCBSP_WRITE(io_base, SPCR2, w | 1); + + w = OMAP_MCBSP_READ(io_base, SPCR1); + OMAP_MCBSP_WRITE(io_base, SPCR1, w | 1); + + udelay(100); + + /* Start frame sync */ + w = OMAP_MCBSP_READ(io_base, SPCR2); + OMAP_MCBSP_WRITE(io_base, SPCR2, w | (1 << 7)); + + /* Dump McBSP Regs */ + omap_mcbsp_dump_reg(id); + +} + +void omap_mcbsp_stop(unsigned int id) +{ + u32 io_base; + u16 w; + + if (omap_mcbsp_check(id) < 0) + return; + + io_base = mcbsp[id].io_base; + + /* Reset transmitter */ + w = OMAP_MCBSP_READ(io_base, SPCR2); + OMAP_MCBSP_WRITE(io_base, SPCR2, w & ~(1)); + + /* Reset receiver */ + w = OMAP_MCBSP_READ(io_base, SPCR1); + OMAP_MCBSP_WRITE(io_base, SPCR1, w & ~(1)); + + /* Reset the sample rate generator */ + w = OMAP_MCBSP_READ(io_base, SPCR2); + OMAP_MCBSP_WRITE(io_base, SPCR2, w & ~(1 << 6)); +} + + +/* + * IRQ based word transmission. + */ +void omap_mcbsp_xmit_word(unsigned int id, u32 word) +{ + u32 io_base; + omap_mcbsp_word_length word_length = mcbsp[id].tx_word_length; + + if (omap_mcbsp_check(id) < 0) + return; + + io_base = mcbsp[id].io_base; + + wait_for_completion(&(mcbsp[id].tx_irq_completion)); + + if (word_length > OMAP_MCBSP_WORD_16) + OMAP_MCBSP_WRITE(io_base, DXR2, word >> 16); + OMAP_MCBSP_WRITE(io_base, DXR1, word & 0xffff); +} + +u32 omap_mcbsp_recv_word(unsigned int id) +{ + u32 io_base; + u16 word_lsb, word_msb = 0; + omap_mcbsp_word_length word_length = mcbsp[id].rx_word_length; + + if (omap_mcbsp_check(id) < 0) + return -EINVAL; + + io_base = mcbsp[id].io_base; + + wait_for_completion(&(mcbsp[id].rx_irq_completion)); + + if (word_length > OMAP_MCBSP_WORD_16) + word_msb = OMAP_MCBSP_READ(io_base, DRR2); + word_lsb = OMAP_MCBSP_READ(io_base, DRR1); + + return (word_lsb | (word_msb << 16)); +} + + +/* + * Simple DMA based buffer rx/tx routines. + * Nothing fancy, just a single buffer tx/rx through DMA. + * The DMA resources are released once the transfer is done. + * For anything fancier, you should use your own customized DMA + * routines and callbacks. + */ +int omap_mcbsp_xmit_buffer(unsigned int id, dma_addr_t buffer, unsigned int length) +{ + int dma_tx_ch; + + if (omap_mcbsp_check(id) < 0) + return -EINVAL; + + if (omap_request_dma(mcbsp[id].dma_tx_sync, "McBSP TX", omap_mcbsp_tx_dma_callback, + &mcbsp[id], + &dma_tx_ch)) { + printk("OMAP-McBSP: Unable to request DMA channel for McBSP%d TX. Trying IRQ based TX\n", id+1); + return -EAGAIN; + } + mcbsp[id].dma_tx_lch = dma_tx_ch; + + DBG("TX DMA on channel %d\n", dma_tx_ch); + + init_completion(&(mcbsp[id].tx_dma_completion)); + + omap_set_dma_transfer_params(mcbsp[id].dma_tx_lch, + OMAP_DMA_DATA_TYPE_S16, + length >> 1, 1, + OMAP_DMA_SYNC_ELEMENT); + + omap_set_dma_dest_params(mcbsp[id].dma_tx_lch, + OMAP_DMA_PORT_TIPB, + OMAP_DMA_AMODE_CONSTANT, + mcbsp[id].io_base + OMAP_MCBSP_REG_DXR1); + + omap_set_dma_src_params(mcbsp[id].dma_tx_lch, + OMAP_DMA_PORT_EMIFF, + OMAP_DMA_AMODE_POST_INC, + buffer); + + omap_start_dma(mcbsp[id].dma_tx_lch); + wait_for_completion(&(mcbsp[id].tx_dma_completion)); + return 0; +} + + +int omap_mcbsp_recv_buffer(unsigned int id, dma_addr_t buffer, unsigned int length) +{ + int dma_rx_ch; + + if (omap_mcbsp_check(id) < 0) + return -EINVAL; + + if (omap_request_dma(mcbsp[id].dma_rx_sync, "McBSP RX", omap_mcbsp_rx_dma_callback, + &mcbsp[id], + &dma_rx_ch)) { + printk("Unable to request DMA channel for McBSP%d RX. Trying IRQ based RX\n", id+1); + return -EAGAIN; + } + mcbsp[id].dma_rx_lch = dma_rx_ch; + + DBG("RX DMA on channel %d\n", dma_rx_ch); + + init_completion(&(mcbsp[id].rx_dma_completion)); + + omap_set_dma_transfer_params(mcbsp[id].dma_rx_lch, + OMAP_DMA_DATA_TYPE_S16, + length >> 1, 1, + OMAP_DMA_SYNC_ELEMENT); + + omap_set_dma_src_params(mcbsp[id].dma_rx_lch, + OMAP_DMA_PORT_TIPB, + OMAP_DMA_AMODE_CONSTANT, + mcbsp[id].io_base + OMAP_MCBSP_REG_DRR1); + + omap_set_dma_dest_params(mcbsp[id].dma_rx_lch, + OMAP_DMA_PORT_EMIFF, + OMAP_DMA_AMODE_POST_INC, + buffer); + + omap_start_dma(mcbsp[id].dma_rx_lch); + wait_for_completion(&(mcbsp[id].rx_dma_completion)); + return 0; +} + + +/* + * SPI wrapper. + * Since SPI setup is much simpler than the generic McBSP one, + * this wrapper just need an omap_mcbsp_spi_cfg structure as an input. + * Once this is done, you can call omap_mcbsp_start(). + */ +void omap_mcbsp_set_spi_mode(unsigned int id, const struct omap_mcbsp_spi_cfg * spi_cfg) +{ + struct omap_mcbsp_reg_cfg mcbsp_cfg; + + if (omap_mcbsp_check(id) < 0) + return; + + memset(&mcbsp_cfg, 0, sizeof(struct omap_mcbsp_reg_cfg)); + + /* SPI has only one frame */ + mcbsp_cfg.rcr1 |= (RWDLEN1(spi_cfg->word_length) | RFRLEN1(0)); + mcbsp_cfg.xcr1 |= (XWDLEN1(spi_cfg->word_length) | XFRLEN1(0)); + + /* Clock stop mode */ + if (spi_cfg->clk_stp_mode == OMAP_MCBSP_CLK_STP_MODE_NO_DELAY) + mcbsp_cfg.spcr1 |= (1 << 12); + else + mcbsp_cfg.spcr1 |= (3 << 11); + + /* Set clock parities */ + if (spi_cfg->rx_clock_polarity == OMAP_MCBSP_CLK_RISING) + mcbsp_cfg.pcr0 |= CLKRP; + else + mcbsp_cfg.pcr0 &= ~CLKRP; + + if (spi_cfg->tx_clock_polarity == OMAP_MCBSP_CLK_RISING) + mcbsp_cfg.pcr0 &= ~CLKXP; + else + mcbsp_cfg.pcr0 |= CLKXP; + + /* Set SCLKME to 0 and CLKSM to 1 */ + mcbsp_cfg.pcr0 &= ~SCLKME; + mcbsp_cfg.srgr2 |= CLKSM; + + /* Set FSXP */ + if (spi_cfg->fsx_polarity == OMAP_MCBSP_FS_ACTIVE_HIGH) + mcbsp_cfg.pcr0 &= ~FSXP; + else + mcbsp_cfg.pcr0 |= FSXP; + + if (spi_cfg->spi_mode == OMAP_MCBSP_SPI_MASTER) { + mcbsp_cfg.pcr0 |= CLKXM; + mcbsp_cfg.srgr1 |= CLKGDV(spi_cfg->clk_div -1); + mcbsp_cfg.pcr0 |= FSXM; + mcbsp_cfg.srgr2 &= ~FSGM; + mcbsp_cfg.xcr2 |= XDATDLY(1); + mcbsp_cfg.rcr2 |= RDATDLY(1); + } + else { + mcbsp_cfg.pcr0 &= ~CLKXM; + mcbsp_cfg.srgr1 |= CLKGDV(1); + mcbsp_cfg.pcr0 &= ~FSXM; + mcbsp_cfg.xcr2 &= ~XDATDLY(3); + mcbsp_cfg.rcr2 &= ~RDATDLY(3); + } + + mcbsp_cfg.xcr2 &= ~XPHASE; + mcbsp_cfg.rcr2 &= ~RPHASE; + + omap_mcbsp_config(id, &mcbsp_cfg); +} + + +/* + * McBSP1 and McBSP3 are directly mapped on 1610 and 1510. + * 730 has only 2 McBSP, and both of them are MPU peripherals. + */ +struct omap_mcbsp_info { + u32 virt_base; + u8 dma_rx_sync, dma_tx_sync; + u16 rx_irq, tx_irq; +}; + +#ifdef CONFIG_ARCH_OMAP730 +static const struct omap_mcbsp_info mcbsp_730[] = { + [0] = { .virt_base = io_p2v(OMAP730_MCBSP1_BASE), + .dma_rx_sync = OMAP_DMA_MCBSP1_RX, + .dma_tx_sync = OMAP_DMA_MCBSP1_TX, + .rx_irq = INT_730_McBSP1RX, + .tx_irq = INT_730_McBSP1TX }, + [1] = { .virt_base = io_p2v(OMAP730_MCBSP2_BASE), + .dma_rx_sync = OMAP_DMA_MCBSP3_RX, + .dma_tx_sync = OMAP_DMA_MCBSP3_TX, + .rx_irq = INT_730_McBSP2RX, + .tx_irq = INT_730_McBSP2TX }, +}; +#endif + +#ifdef CONFIG_ARCH_OMAP1510 +static const struct omap_mcbsp_info mcbsp_1510[] = { + [0] = { .virt_base = OMAP1510_MCBSP1_BASE, + .dma_rx_sync = OMAP_DMA_MCBSP1_RX, + .dma_tx_sync = OMAP_DMA_MCBSP1_TX, + .rx_irq = INT_McBSP1RX, + .tx_irq = INT_McBSP1TX }, + [1] = { .virt_base = io_p2v(OMAP1510_MCBSP2_BASE), + .dma_rx_sync = OMAP_DMA_MCBSP2_RX, + .dma_tx_sync = OMAP_DMA_MCBSP2_TX, + .rx_irq = INT_1510_SPI_RX, + .tx_irq = INT_1510_SPI_TX }, + [2] = { .virt_base = OMAP1510_MCBSP3_BASE, + .dma_rx_sync = OMAP_DMA_MCBSP3_RX, + .dma_tx_sync = OMAP_DMA_MCBSP3_TX, + .rx_irq = INT_McBSP3RX, + .tx_irq = INT_McBSP3TX }, +}; +#endif + +#if defined(CONFIG_ARCH_OMAP1610) || defined(CONFIG_ARCH_OMAP1710) +static const struct omap_mcbsp_info mcbsp_1610[] = { + [0] = { .virt_base = OMAP1610_MCBSP1_BASE, + .dma_rx_sync = OMAP_DMA_MCBSP1_RX, + .dma_tx_sync = OMAP_DMA_MCBSP1_TX, + .rx_irq = INT_McBSP1RX, + .tx_irq = INT_McBSP1TX }, + [1] = { .virt_base = io_p2v(OMAP1610_MCBSP2_BASE), + .dma_rx_sync = OMAP_DMA_MCBSP2_RX, + .dma_tx_sync = OMAP_DMA_MCBSP2_TX, + .rx_irq = INT_1610_McBSP2_RX, + .tx_irq = INT_1610_McBSP2_TX }, + [2] = { .virt_base = OMAP1610_MCBSP3_BASE, + .dma_rx_sync = OMAP_DMA_MCBSP3_RX, + .dma_tx_sync = OMAP_DMA_MCBSP3_TX, + .rx_irq = INT_McBSP3RX, + .tx_irq = INT_McBSP3TX }, +}; +#endif + +static int __init omap_mcbsp_init(void) +{ + int mcbsp_count = 0, i; + static const struct omap_mcbsp_info *mcbsp_info; + + printk("Initializing OMAP McBSP system\n"); +#ifdef CONFIG_ARCH_OMAP730 + if (cpu_is_omap730()) { + mcbsp_info = mcbsp_730; + mcbsp_count = ARRAY_SIZE(mcbsp_730); + } +#endif +#ifdef CONFIG_ARCH_OMAP1510 + if (cpu_is_omap1510()) { + mcbsp_info = mcbsp_1510; + mcbsp_count = ARRAY_SIZE(mcbsp_1510); + } +#endif +#if defined(CONFIG_ARCH_OMAP1610) || defined(CONFIG_ARCH_OMAP1710) + if (cpu_is_omap1610() || cpu_is_omap1710()) { + mcbsp_info = mcbsp_1610; + mcbsp_count = ARRAY_SIZE(mcbsp_1610); + } +#endif + for (i = 0; i < OMAP_MAX_MCBSP_COUNT ; i++) { + if (i >= mcbsp_count) { + mcbsp[i].io_base = 0; + mcbsp[i].free = 0; + continue; + } + mcbsp[i].id = i + 1; + mcbsp[i].free = 1; + mcbsp[i].dma_tx_lch = -1; + mcbsp[i].dma_rx_lch = -1; + + mcbsp[i].io_base = mcbsp_info[i].virt_base; + mcbsp[i].tx_irq = mcbsp_info[i].tx_irq; + mcbsp[i].rx_irq = mcbsp_info[i].rx_irq; + mcbsp[i].dma_rx_sync = mcbsp_info[i].dma_rx_sync; + mcbsp[i].dma_tx_sync = mcbsp_info[i].dma_tx_sync; + spin_lock_init(&mcbsp[i].lock); + } + + return 0; +} + + +arch_initcall(omap_mcbsp_init); + +EXPORT_SYMBOL(omap_mcbsp_config); +EXPORT_SYMBOL(omap_mcbsp_request); +EXPORT_SYMBOL(omap_mcbsp_free); +EXPORT_SYMBOL(omap_mcbsp_start); +EXPORT_SYMBOL(omap_mcbsp_stop); +EXPORT_SYMBOL(omap_mcbsp_xmit_word); +EXPORT_SYMBOL(omap_mcbsp_recv_word); +EXPORT_SYMBOL(omap_mcbsp_xmit_buffer); +EXPORT_SYMBOL(omap_mcbsp_recv_buffer); +EXPORT_SYMBOL(omap_mcbsp_set_spi_mode); diff --git a/arch/arm/mach-omap/ocpi.c b/arch/arm/mach-omap/ocpi.c index 944c29418..93c7f4aca 100644 --- a/arch/arm/mach-omap/ocpi.c +++ b/arch/arm/mach-omap/ocpi.c @@ -59,8 +59,8 @@ int ocpi_enable(void) /* Make sure there's clock for OCPI */ -#ifdef CONFIG_ARCH_OMAP1610 - if (cpu_is_omap1610()) { +#if defined(CONFIG_ARCH_OMAP1610) || defined(CONFIG_ARCH_OMAP1710) + if (cpu_is_omap1610() || cpu_is_omap1710()) { val = omap_readl(OMAP1610_ARM_IDLECT3); val |= EN_OCPI_CK; val &= ~IDLOCPI_ARM; diff --git a/arch/arm/mach-omap/usb.c b/arch/arm/mach-omap/usb.c new file mode 100644 index 000000000..f3451d269 --- /dev/null +++ b/arch/arm/mach-omap/usb.c @@ -0,0 +1,541 @@ +/* + * arch/arm/mach-omap/usb.c -- platform level USB initialization + * + * Copyright (C) 2004 Texas Instruments, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#undef DEBUG + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include + +/* These routines should handle the standard chip-specific modes + * for usb0/1/2 ports, covering basic mux and transceiver setup. + * Call omap_usb_init() once, from INIT_MACHINE(). + * + * Some board-*.c files will need to set up additional mux options, + * like for suspend handling, vbus sensing, GPIOs, and the D+ pullup. + */ + +/* TESTED ON: + * - 1611B H2 (with usb1 mini-AB) + * - 1510 Innovator with built-in transceiver (custom cable feeding 5V VBUS) + * - 1710 custom development board using alternate pin group + */ + +/*-------------------------------------------------------------------------*/ + +#ifdef CONFIG_ARCH_OMAP_OTG + +static struct otg_transceiver *xceiv; + +/** + * otg_get_transceiver - find the (single) OTG transceiver driver + * + * Returns the transceiver driver, after getting a refcount to it; or + * null if there is no such transceiver. The caller is responsible for + * releasing that count. + */ +struct otg_transceiver *otg_get_transceiver(void) +{ + if (xceiv) + get_device(xceiv->dev); + return xceiv; +} +EXPORT_SYMBOL(otg_get_transceiver); + +int otg_set_transceiver(struct otg_transceiver *x) +{ + if (xceiv && x) + return -EBUSY; + xceiv = x; + return 0; +} +EXPORT_SYMBOL(otg_set_transceiver); + +#endif + +/*-------------------------------------------------------------------------*/ + +static u32 __init omap_usb0_init(unsigned nwires, unsigned is_device) +{ + u32 syscon1 = 0; + + if (nwires == 0) { + USB_TRANSCEIVER_CTRL_REG &= ~(1 << 3); + return 0; + } + + /* + * VP and VM are needed for all active usb0 configurations. + * USB0_VP and USB0_VM are always set on 1510, there's no muxing + * available for them. + */ + if (nwires >= 2 && !cpu_is_omap1510()) { + omap_cfg_reg(AA9_USB0_VP); + omap_cfg_reg(R9_USB0_VM); + } + + /* internal transceiver */ + if (nwires == 2) { + if (cpu_is_omap1510()) { + /* This works for OHCI on 1510-Innovator, nothing to mux */ + return 0; + } + +#if 0 + /* NOTE: host OR device mode for now, no OTG */ + USB_TRANSCEIVER_CTRL_REG &= ~(3 << 4); + if (is_device) { + omap_cfg_reg(W4_USB_PUEN); + omap_cfg_reg(R18_1510_USB_GPIO0); + // omap_cfg_reg(USB0_VBUS); + // omap_cfg_reg(USB0_PUEN); + // USB_TRANSCEIVER_CTRL_REG.CONF_USB0_PORT_R = 7 + // when USB0_PUEN is needed + } else /* host mode needs D+ and D- pulldowns */ + USB_TRANSCEIVER_CTRL_REG &= ~(3 << 1); + return 3 << 16; +#else + /* FIXME: 1610 needs to return the right value here */ + printk(KERN_ERR "usb0 internal transceiver, nyet\n"); + return 0; +#endif + } + + /* alternate pin config, external transceiver */ + omap_cfg_reg(V6_USB0_TXD); + omap_cfg_reg(W9_USB0_TXEN); + omap_cfg_reg(W5_USB0_SE0); + +#ifdef CONFIG_ARCH_OMAP_USB_SPEED + /* FIXME: there's good chance that pin V9 is used for MMC2 port cmddir */ + omap_cfg_reg(V9_USB0_SPEED); + // omap_cfg_reg(V9_USB0_SUSP); +#endif + + if (nwires != 3) + omap_cfg_reg(Y5_USB0_RCV); + + switch (nwires) { + case 3: + syscon1 = 2; + break; + case 4: + syscon1 = 1; + break; + case 6: + syscon1 = 3; + /* REVISIT: Is CONF_USB2_UNI_R only needed when nwires = 6? */ + USB_TRANSCEIVER_CTRL_REG |= CONF_USB2_UNI_R; + break; + default: + printk(KERN_ERR "illegal usb%d %d-wire transceiver\n", + 0, nwires); + } + return syscon1 << 16; +} + +static u32 __init omap_usb1_init(unsigned nwires) +{ + u32 syscon1 = 0; + + if (nwires != 6) + USB_TRANSCEIVER_CTRL_REG &= ~CONF_USB1_UNI_R; + if (nwires == 0) + return 0; + + /* external transceiver */ + omap_cfg_reg(USB1_TXD); + omap_cfg_reg(USB1_TXEN); + if (cpu_is_omap1510()) { + omap_cfg_reg(USB1_SEO); + omap_cfg_reg(USB1_SPEED); + // SUSP + } else if (cpu_is_omap1610() || cpu_is_omap5912() || cpu_is_omap1710()) { + omap_cfg_reg(W13_1610_USB1_SE0); + omap_cfg_reg(R13_1610_USB1_SPEED); + // SUSP + } else { + pr_debug("usb unrecognized\n"); + } + if (nwires != 3) + omap_cfg_reg(USB1_RCV); + + switch (nwires) { + case 3: + syscon1 = 2; + break; + case 4: + syscon1 = 1; + break; + case 6: + syscon1 = 3; + omap_cfg_reg(USB1_VP); + omap_cfg_reg(USB1_VM); + USB_TRANSCEIVER_CTRL_REG |= CONF_USB1_UNI_R; + break; + default: + printk(KERN_ERR "illegal usb%d %d-wire transceiver\n", + 1, nwires); + } + return syscon1 << 20; +} + +static u32 __init omap_usb2_init(unsigned nwires, unsigned alt_pingroup) +{ + u32 syscon1 = 0; + + if (alt_pingroup) + return 0; + if (nwires != 6) + USB_TRANSCEIVER_CTRL_REG &= ~CONF_USB2_UNI_R; + if (nwires == 0) + return 0; + + /* external transceiver */ + if (cpu_is_omap1510()) { + omap_cfg_reg(USB2_TXD); + omap_cfg_reg(USB2_TXEN); + omap_cfg_reg(USB2_SEO); + if (nwires != 3) + omap_cfg_reg(USB2_RCV); + } else if (cpu_is_omap1610() || cpu_is_omap5912() || cpu_is_omap1710()) { + omap_cfg_reg(V6_USB2_TXD); + omap_cfg_reg(W9_USB2_TXEN); + omap_cfg_reg(W5_USB2_SE0); + if (nwires != 3) + omap_cfg_reg(Y5_USB2_RCV); + } else { + pr_debug("usb unrecognized\n"); + } + // omap_cfg_reg(USB2_SUSP); + // FIXME omap_cfg_reg(USB2_SPEED); + + switch (nwires) { + case 3: + syscon1 = 2; + break; + case 4: + syscon1 = 1; + break; + case 6: + syscon1 = 3; + if (cpu_is_omap1510()) { + omap_cfg_reg(USB2_VP); + omap_cfg_reg(USB2_VM); + } else { + omap_cfg_reg(AA9_USB2_VP); + omap_cfg_reg(R9_USB2_VM); + } + USB_TRANSCEIVER_CTRL_REG |= CONF_USB2_UNI_R; + break; + default: + printk(KERN_ERR "illegal usb%d %d-wire transceiver\n", + 2, nwires); + } + return syscon1 << 24; +} + +/*-------------------------------------------------------------------------*/ + +#if defined(CONFIG_USB_GADGET_OMAP) || \ + defined(CONFIG_USB_OHCI_HCD) || defined(CONFIG_USB_OHCI_HCD_MODULE) || \ + (defined(CONFIG_USB_OTG) && defined(CONFIG_ARCH_OMAP_OTG)) +static void usb_release(struct device *dev) +{ + /* normally not freed */ +} +#endif + +#ifdef CONFIG_USB_GADGET_OMAP + +static struct resource udc_resources[] = { + /* order is significant! */ + { /* registers */ + .start = IO_ADDRESS(UDC_BASE), + .end = IO_ADDRESS(UDC_BASE + 0xff), + .flags = IORESOURCE_MEM, + }, { /* general IRQ */ + .start = IH2_BASE + 20, + .flags = IORESOURCE_IRQ, + }, { /* PIO IRQ */ + .start = IH2_BASE + 30, + .flags = IORESOURCE_IRQ, + }, { /* SOF IRQ */ + .start = IH2_BASE + 29, + .flags = IORESOURCE_IRQ, + }, +}; + +static u64 udc_dmamask = ~(u32)0; + +static struct platform_device udc_device = { + .name = "omap_udc", + .id = -1, + .dev = { + .release = usb_release, + .dma_mask = &udc_dmamask, + .coherent_dma_mask = 0xffffffff, + }, + .num_resources = ARRAY_SIZE(udc_resources), + .resource = udc_resources, +}; + +#endif + +#if defined(CONFIG_USB_OHCI_HCD) || defined(CONFIG_USB_OHCI_HCD_MODULE) + +/* The dmamask must be set for OHCI to work */ +static u64 ohci_dmamask = ~(u32)0; + +static struct resource ohci_resources[] = { + { + .start = IO_ADDRESS(OMAP_OHCI_BASE), + .end = IO_ADDRESS(OMAP_OHCI_BASE + 4096), + .flags = IORESOURCE_MEM, + }, + { + .start = INT_USB_HHC_1, + .flags = IORESOURCE_IRQ, + }, +}; + +static struct platform_device ohci_device = { + .name = "ohci", + .id = -1, + .dev = { + .release = usb_release, + .dma_mask = &ohci_dmamask, + .coherent_dma_mask = 0x0fffffff, + }, + .num_resources = ARRAY_SIZE(ohci_resources), + .resource = ohci_resources, +}; + +#endif + +#if defined(CONFIG_USB_OTG) && defined(CONFIG_ARCH_OMAP_OTG) + +static struct resource otg_resources[] = { + /* order is significant! */ + { + .start = IO_ADDRESS(OTG_BASE), + .end = IO_ADDRESS(OTG_BASE + 0xff), + .flags = IORESOURCE_MEM, + }, { + .start = IH2_BASE + 8, + .flags = IORESOURCE_IRQ, + }, +}; + +static struct platform_device otg_device = { + .name = "omap_otg", + .id = -1, + .dev = { + .release = usb_release, + }, + .num_resources = ARRAY_SIZE(otg_resources), + .resource = otg_resources, +}; + +#endif + +/*-------------------------------------------------------------------------*/ + +// FIXME correct answer depends on hmc_mode, +// as does any nonzero value for config->otg port number +#define is_usb0_device(config) 0 + +/*-------------------------------------------------------------------------*/ + +#ifdef CONFIG_ARCH_OMAP_OTG + +void __init +omap_otg_init(struct omap_usb_config *config) +{ + u32 syscon = OTG_SYSCON_1_REG & 0xffff; + int status; + int alt_pingroup = 0; + + /* NOTE: no bus or clock setup (yet?) */ + + syscon = OTG_SYSCON_1_REG & 0xffff; + if (!(syscon & OTG_RESET_DONE)) + pr_debug("USB resets not complete?\n"); + + // OTG_IRQ_EN_REG = 0; + + /* pin muxing and transceiver pinouts */ + if (config->pins[0] > 2) /* alt pingroup 2 */ + alt_pingroup = 1; + syscon |= omap_usb0_init(config->pins[0], is_usb0_device(config)); + syscon |= omap_usb1_init(config->pins[1]); + syscon |= omap_usb2_init(config->pins[2], alt_pingroup); + pr_debug("OTG_SYSCON_1_REG = %08x\n", syscon); + OTG_SYSCON_1_REG = syscon; + + syscon = config->hmc_mode; + syscon |= USBX_SYNCHRO | (4 << 16) /* B_ASE0_BRST */; + if (config->otg || config->register_host) + syscon |= UHOST_EN; +#ifdef CONFIG_USB_OTG + if (config->otg) + syscon |= OTG_EN; +#endif + pr_debug("OTG_SYSCON_2_REG = %08x\n", syscon); + OTG_SYSCON_2_REG = syscon; + + printk("USB: hmc %d", config->hmc_mode); + if (alt_pingroup) + printk(", usb2 alt %d wires", config->pins[2]); + else if (config->pins[0]) + printk(", usb0 %d wires%s", config->pins[2], + is_usb0_device(config) ? " (dev)" : ""); + if (config->pins[1]) + printk(", usb1 %d wires", config->pins[1]); + if (!alt_pingroup && config->pins[2]) + printk(", usb2 %d wires", config->pins[2]); + if (config->otg) + printk(", Mini-AB on usb%d", config->otg - 1); + printk("\n"); + + /* don't clock unused USB controllers */ + syscon = OTG_SYSCON_1_REG; + syscon |= HST_IDLE_EN|DEV_IDLE_EN|OTG_IDLE_EN; + +#ifdef CONFIG_USB_GADGET_OMAP + if (config->otg || config->register_dev) { + syscon &= ~DEV_IDLE_EN; + udc_device.dev.platform_data = config; + status = platform_device_register(&udc_device); + if (status) + pr_debug("can't register UDC device, %d\n", status); + } +#endif + +#if defined(CONFIG_USB_OHCI_HCD) || defined(CONFIG_USB_OHCI_HCD_MODULE) + if (config->otg || config->register_host) { + syscon &= ~HST_IDLE_EN; + ohci_device.dev.platform_data = config; + status = platform_device_register(&ohci_device); + if (status) + pr_debug("can't register OHCI device, %d\n", status); + } +#endif + +#ifdef CONFIG_USB_OTG + if (config->otg) { + syscon &= ~OTG_IDLE_EN; + if (cpu_is_omap730()) + otg_resources[1].start = INT_730_USB_OTG; + status = platform_device_register(&otg_device); + // ... + } +#endif + pr_debug("OTG_SYSCON_1_REG = %08x\n", syscon); + OTG_SYSCON_1_REG = syscon; + + status = 0; +} + +#else +static inline void omap_otg_init(struct omap_usb_config *config) {} +#endif + +/*-------------------------------------------------------------------------*/ + +#ifdef CONFIG_ARCH_OMAP1510 + +static void __init omap_1510_usb_init(struct omap_usb_config *config) +{ + int status; + unsigned int val; + + omap_usb0_init(config->pins[0], is_usb0_device(config)); + omap_usb1_init(config->pins[1]); + omap_usb2_init(config->pins[2], 0); + + val = omap_readl(MOD_CONF_CTRL_0) & ~(0x3f << 1); + val |= (config->hmc_mode << 1); + omap_writel(val, MOD_CONF_CTRL_0); + + // FIXME this has a UDC controller too + +#if defined(CONFIG_USB_OHCI_HCD) || defined(CONFIG_USB_OHCI_HCD_MODULE) + if (config->otg || config->register_host) { + ohci_device.dev.platform_data = config; + status = platform_device_register(&ohci_device); + if (status) + pr_debug("can't register OHCI device, %d\n", status); + } + // FIXME completely untested ... +#endif + +} + +#else +static inline void omap_1510_usb_init(struct omap_usb_config *config) {} +#endif + +/*-------------------------------------------------------------------------*/ + +static struct omap_usb_config platform_data; + +static int __init +omap_usb_init(void) +{ + const struct omap_usb_config *config; + + config = omap_get_config(OMAP_TAG_USB, struct omap_usb_config); + if (config == NULL) { + printk(KERN_ERR "USB: No board-specific platform config found\n"); + return -ENODEV; + } + platform_data = *config; + + if (cpu_is_omap730() + || cpu_is_omap1610() + || cpu_is_omap1710() + || cpu_is_omap5912()) + omap_otg_init(&platform_data); + else if (cpu_is_omap1510()) + omap_1510_usb_init(&platform_data); + else { + printk(KERN_ERR "USB: No init for your chip yet\n"); + return -ENODEV; + } + return 0; +} + +subsys_initcall(omap_usb_init); diff --git a/arch/arm/mach-pxa/irq.c b/arch/arm/mach-pxa/irq.c index c771e4376..a676a8657 100644 --- a/arch/arm/mach-pxa/irq.c +++ b/arch/arm/mach-pxa/irq.c @@ -20,39 +20,64 @@ #include #include #include +#include #include "generic.h" /* - * This is for IRQs known as PXA_IRQ([8...31]). + * This is for peripheral IRQs internal to the PXA chip. */ -static void pxa_mask_irq(unsigned int irq) +static void pxa_mask_low_irq(unsigned int irq) { ICMR &= ~(1 << (irq + PXA_IRQ_SKIP)); } -static void pxa_unmask_irq(unsigned int irq) +static void pxa_unmask_low_irq(unsigned int irq) { ICMR |= (1 << (irq + PXA_IRQ_SKIP)); } -static struct irqchip pxa_internal_chip = { - .ack = pxa_mask_irq, - .mask = pxa_mask_irq, - .unmask = pxa_unmask_irq, +static struct irqchip pxa_internal_chip_low = { + .ack = pxa_mask_low_irq, + .mask = pxa_mask_low_irq, + .unmask = pxa_unmask_low_irq, }; +#if PXA_INTERNAL_IRQS > 32 + +/* + * This is for the second set of internal IRQs as found on the PXA27x. + */ + +static void pxa_mask_high_irq(unsigned int irq) +{ + ICMR2 &= ~(1 << (irq - 32 + PXA_IRQ_SKIP)); +} + +static void pxa_unmask_high_irq(unsigned int irq) +{ + ICMR2 |= (1 << (irq - 32 + PXA_IRQ_SKIP)); +} + +static struct irqchip pxa_internal_chip_high = { + .ack = pxa_mask_high_irq, + .mask = pxa_mask_high_irq, + .unmask = pxa_unmask_high_irq, +}; + +#endif + /* * PXA GPIO edge detection for IRQs: * IRQs are generated on Falling-Edge, Rising-Edge, or both. * Use this instead of directly setting GRER/GFER. */ -static long GPIO_IRQ_rising_edge[3]; -static long GPIO_IRQ_falling_edge[3]; -static long GPIO_IRQ_mask[3]; +static long GPIO_IRQ_rising_edge[4]; +static long GPIO_IRQ_falling_edge[4]; +static long GPIO_IRQ_mask[4]; static int pxa_gpio_irq_type(unsigned int irq, unsigned int type) { @@ -106,13 +131,13 @@ static void pxa_ack_low_gpio(unsigned int irq) static struct irqchip pxa_low_gpio_chip = { .ack = pxa_ack_low_gpio, - .mask = pxa_mask_irq, - .unmask = pxa_unmask_irq, + .mask = pxa_mask_low_irq, + .unmask = pxa_unmask_low_irq, .type = pxa_gpio_irq_type, }; /* - * Demux handler for GPIO 2-80 edge detect interrupts + * Demux handler for GPIO>=2 edge detect interrupts */ static void pxa_gpio_demux_handler(unsigned int irq, struct irqdesc *desc, @@ -169,6 +194,23 @@ static void pxa_gpio_demux_handler(unsigned int irq, struct irqdesc *desc, } while (mask); loop = 1; } + +#if PXA_LAST_GPIO >= 96 + mask = GEDR3; + if (mask) { + GEDR3 = mask; + irq = IRQ_GPIO(96); + desc = irq_desc + irq; + do { + if (mask & 1) + desc->handle(irq, desc, regs); + irq++; + desc++; + mask >>= 1; + } while (mask); + loop = 1; + } +#endif } while (loop); } @@ -214,12 +256,25 @@ void __init pxa_init_irq(void) ICLR = 0; /* clear all GPIO edge detects */ - GFER0 = GFER1 = GFER2 = 0; - GRER0 = GRER1 = GRER2 = 0; + GFER0 = 0; + GFER1 = 0; + GFER2 = 0; + GRER0 = 0; + GRER1 = 0; + GRER2 = 0; GEDR0 = GEDR0; GEDR1 = GEDR1; GEDR2 = GEDR2; +#ifdef CONFIG_PXA27x + /* And similarly for the extra regs on the PXA27x */ + ICMR2 = 0; + ICLR2 = 0; + GFER3 = 0; + GRER3 = 0; + GEDR3 = GEDR3; +#endif + /* only unmasked interrupts kick us out of idle */ ICCR = 1; @@ -227,10 +282,18 @@ void __init pxa_init_irq(void) GPIO_IRQ_mask[0] = 3; for (irq = PXA_IRQ(PXA_IRQ_SKIP); irq <= PXA_IRQ(31); irq++) { - set_irq_chip(irq, &pxa_internal_chip); + set_irq_chip(irq, &pxa_internal_chip_low); + set_irq_handler(irq, do_level_IRQ); + set_irq_flags(irq, IRQF_VALID); + } + +#if PXA_INTERNAL_IRQS > 32 + for (irq = PXA_IRQ(32); irq < PXA_IRQ(PXA_INTERNAL_IRQS); irq++) { + set_irq_chip(irq, &pxa_internal_chip_high); set_irq_handler(irq, do_level_IRQ); set_irq_flags(irq, IRQF_VALID); } +#endif for (irq = IRQ_GPIO0; irq <= IRQ_GPIO1; irq++) { set_irq_chip(irq, &pxa_low_gpio_chip); @@ -238,13 +301,13 @@ void __init pxa_init_irq(void) set_irq_flags(irq, IRQF_VALID | IRQF_PROBE); } - for (irq = IRQ_GPIO(2); irq <= IRQ_GPIO(80); irq++) { + for (irq = IRQ_GPIO(2); irq <= IRQ_GPIO(PXA_LAST_GPIO); irq++) { set_irq_chip(irq, &pxa_muxed_gpio_chip); set_irq_handler(irq, do_edge_IRQ); set_irq_flags(irq, IRQF_VALID | IRQF_PROBE); } - /* Install handler for GPIO 2-80 edge detect interrupts */ - set_irq_chip(IRQ_GPIO_2_80, &pxa_internal_chip); - set_irq_chained_handler(IRQ_GPIO_2_80, pxa_gpio_demux_handler); + /* Install handler for GPIO>=2 edge detect interrupts */ + set_irq_chip(IRQ_GPIO_2_x, &pxa_internal_chip_low); + set_irq_chained_handler(IRQ_GPIO_2_x, pxa_gpio_demux_handler); } diff --git a/arch/arm/mach-pxa/leds-mainstone.c b/arch/arm/mach-pxa/leds-mainstone.c index 8403f6780..bbd3f87a9 100644 --- a/arch/arm/mach-pxa/leds-mainstone.c +++ b/arch/arm/mach-pxa/leds-mainstone.c @@ -17,6 +17,7 @@ #include #include +#include #include #include "leds.h" diff --git a/arch/arm/mach-pxa/mainstone.c b/arch/arm/mach-pxa/mainstone.c index 155b5950e..219fd4ecd 100644 --- a/arch/arm/mach-pxa/mainstone.c +++ b/arch/arm/mach-pxa/mainstone.c @@ -31,6 +31,7 @@ #include #include +#include #include #include diff --git a/arch/arm/mach-pxa/pxa25x.c b/arch/arm/mach-pxa/pxa25x.c index 41150917f..e887b7175 100644 --- a/arch/arm/mach-pxa/pxa25x.c +++ b/arch/arm/mach-pxa/pxa25x.c @@ -22,6 +22,7 @@ #include #include +#include #include "generic.h" diff --git a/arch/arm/mach-pxa/pxa27x.c b/arch/arm/mach-pxa/pxa27x.c index b1573c837..e5e97fef8 100644 --- a/arch/arm/mach-pxa/pxa27x.c +++ b/arch/arm/mach-pxa/pxa27x.c @@ -14,6 +14,7 @@ #include #include #include +#include #include #include diff --git a/arch/arm/mach-pxa/sleep.S b/arch/arm/mach-pxa/sleep.S index 189263939..be00614f5 100644 --- a/arch/arm/mach-pxa/sleep.S +++ b/arch/arm/mach-pxa/sleep.S @@ -16,6 +16,8 @@ #include #include +#include + .text /* @@ -64,6 +66,37 @@ ENTRY(pxa_cpu_suspend) @ prepare pointer to physical address 0 (virtual mapping in generic.c) mov r2, #UNCACHED_PHYS_0 + @ Intel PXA255 Specification Update notes problems + @ about suspending with PXBus operating above 133MHz + @ (see Errata 31, GPIO output signals, ... unpredictable in sleep + @ + @ We keep the change-down close to the actual suspend on SDRAM + @ as possible to eliminate messing about with the refresh clock + @ as the system will restore with the original speed settings + @ + @ Ben Dooks, 13-Sep-2004 + + ldr r6, =CCCR + ldr r8, [r6] @ keep original value for resume + + @ ensure x1 for run and turbo mode with memory clock + bic r7, r8, #CCCR_M_MASK | CCCR_N_MASK + orr r7, r7, #(1<<5) | (2<<7) + + @ check that the memory frequency is within limits + and r14, r7, #CCCR_L_MASK + teq r14, #1 + bicne r7, r7, #CCCR_L_MASK + orrne r7, r7, #1 @@ 99.53MHz + + @ get ready for the change + @ note, since we are making turbo=run, do not remove the turbo + @ as this may cause non-turbo mode on resume + mrc p14, 0, r0, c6, c0, 0 + bic r0, r0, #2 @ clear change bit + mcr p14, 0, r0, c6, c0, 0 + orr r0, r0, #2 @ initiate change bit + @ align execution to a cache line b 1f @@ -74,6 +107,13 @@ ENTRY(pxa_cpu_suspend) @ All needed values are now in registers. @ These last instructions should be in cache + @ initiate the frequency change... + str r7, [r6] + mcr p14, 0, r0, c6, c0, 0 + + @ restore the original cpu speed value for resume + str r8, [r6] + @ put SDRAM into self-refresh str r5, [r4] @@ -83,8 +123,7 @@ ENTRY(pxa_cpu_suspend) @ enter sleep mode mcr p14, 0, r1, c7, c0, 0 -20: nop - b 20b @ loop waiting for sleep +20: b 20b @ loop waiting for sleep /* * cpu_pxa_resume() diff --git a/arch/arm/mach-pxa/time.c b/arch/arm/mach-pxa/time.c index 7e8923563..a49fc0299 100644 --- a/arch/arm/mach-pxa/time.c +++ b/arch/arm/mach-pxa/time.c @@ -27,6 +27,7 @@ #include #include #include +#include static inline unsigned long pxa_get_rtc_time(void) diff --git a/arch/arm/mach-s3c2410/clock.c b/arch/arm/mach-s3c2410/clock.c new file mode 100644 index 000000000..a12ade71b --- /dev/null +++ b/arch/arm/mach-s3c2410/clock.c @@ -0,0 +1,310 @@ +/* linux/arch/arm/mach-s3c2410/clock.c + * + * Copyright (c) 2004 Simtec Electronics + * Ben Dooks + * + * S3C2410 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 +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include + +#include +#include + +#include "clock.h" + + +static LIST_HEAD(clocks); +static DECLARE_MUTEX(clocks_sem); + + +/* old functions */ + +void s3c2410_clk_enable(unsigned int clocks, unsigned int enable) +{ + unsigned long clkcon; + unsigned long flags; + + local_irq_save(flags); + + clkcon = __raw_readl(S3C2410_CLKCON); + clkcon &= ~clocks; + + if (enable) + clkcon |= clocks; + + __raw_writel(clkcon, S3C2410_CLKCON); + + local_irq_restore(flags); +} + + +/* Clock API calls */ + +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, list) { + if (strcmp(id, p->name) == 0 && + try_module_get(p->owner)) { + clk = p; + break; + } + } + up(&clocks_sem); + + return clk; +} + +void clk_put(struct clk *clk) +{ + module_put(clk->owner); +} + +int clk_enable(struct clk *clk) +{ + if (clk->ctrlbit != 0) + s3c2410_clk_enable(clk->ctrlbit, 1); + + return 0; +} + +void clk_disable(struct clk *clk) +{ + s3c2410_clk_enable(clk->ctrlbit, 0); +} + + +int clk_use(struct clk *clk) +{ + atomic_inc(&clk->used); + return 0; +} + + +void clk_unuse(struct clk *clk) +{ + atomic_dec(&clk->used); +} + +unsigned long clk_get_rate(struct clk *clk) +{ + if (clk->parent != NULL) + return clk->parent->rate; + + return clk->rate; +} + +long clk_round_rate(struct clk *clk, unsigned long rate) +{ + return rate; +} + +int clk_set_rate(struct clk *clk, unsigned long rate) +{ + return -EINVAL; +} + +struct clk *clk_get_parent(struct clk *clk) +{ + return clk->parent; +} + +EXPORT_SYMBOL(clk_get); +EXPORT_SYMBOL(clk_put); +EXPORT_SYMBOL(clk_enable); +EXPORT_SYMBOL(clk_disable); +EXPORT_SYMBOL(clk_use); +EXPORT_SYMBOL(clk_unuse); +EXPORT_SYMBOL(clk_get_rate); +EXPORT_SYMBOL(clk_round_rate); +EXPORT_SYMBOL(clk_set_rate); +EXPORT_SYMBOL(clk_get_parent); + +/* base clocks */ + +static struct clk clk_f = { + .name = "fclk", + .rate = 0, + .parent = NULL, + .ctrlbit = 0 +}; + +static struct clk clk_h = { + .name = "hclk", + .rate = 0, + .parent = NULL, + .ctrlbit = 0 +}; + +static struct clk clk_p = { + .name = "pclk", + .rate = 0, + .parent = NULL, + .ctrlbit = 0 +}; + +/* clock definitions */ + +static struct clk init_clocks[] = { + { .name = "nand", + .parent = &clk_h, + .ctrlbit = S3C2410_CLKCON_NAND + }, + { .name = "lcd", + .parent = &clk_h, + .ctrlbit = S3C2410_CLKCON_LCDC + }, + { .name = "usb-host", + .parent = &clk_h, + .ctrlbit = S3C2410_CLKCON_USBH + }, + { .name = "usb-device", + .parent = &clk_h, + .ctrlbit = S3C2410_CLKCON_USBD + }, + { .name = "timers", + .parent = &clk_p, + .ctrlbit = S3C2410_CLKCON_PWMT + }, + { .name = "sdi", + .parent = &clk_p, + .ctrlbit = S3C2410_CLKCON_SDI + }, + { .name = "uart0", + .parent = &clk_p, + .ctrlbit = S3C2410_CLKCON_UART0 + }, + { .name = "uart1", + .parent = &clk_p, + .ctrlbit = S3C2410_CLKCON_UART1 + }, + { .name = "uart2", + .parent = &clk_p, + .ctrlbit = S3C2410_CLKCON_UART2 + }, + { .name = "gpio", + .parent = &clk_p, + .ctrlbit = S3C2410_CLKCON_GPIO + }, + { .name = "rtc", + .parent = &clk_p, + .ctrlbit = S3C2410_CLKCON_RTC + }, + { .name = "adc", + .parent = &clk_p, + .ctrlbit = S3C2410_CLKCON_ADC + }, + { .name = "i2c", + .parent = &clk_p, + .ctrlbit = S3C2410_CLKCON_IIC + }, + { .name = "iis", + .parent = &clk_p, + .ctrlbit = S3C2410_CLKCON_IIS + }, + { .name = "spi", + .parent = &clk_p, + .ctrlbit = S3C2410_CLKCON_SPI + }, + { .name = "watchdog", + .parent = &clk_p, + .ctrlbit = 0 + } +}; + +/* initialise the clock system */ + +int s3c2410_register_clock(struct clk *clk) +{ + clk->owner = THIS_MODULE; + atomic_set(&clk->used, 0); + + /* add to the list of available clocks */ + + down(&clocks_sem); + list_add(&clk->list, &clocks); + up(&clocks_sem); + + return 0; +} + +/* initalise all the clocks */ + +static int __init s3c2410_init_clocks(void) +{ + struct clk *clkp = init_clocks; + int ptr; + int ret; + + printk(KERN_INFO "S3C2410 Clock control, (c) 2004 Simtec Electronics\n"); + + /* initialise the main system clocks */ + + clk_h.rate = s3c2410_hclk; + clk_p.rate = s3c2410_pclk; + clk_f.rate = s3c2410_fclk; + + /* set the enabled clocks to a minimal (known) state */ + __raw_writel(S3C2410_CLKCON_PWMT | S3C2410_CLKCON_UART0 | S3C2410_CLKCON_UART1 | S3C2410_CLKCON_UART2 | S3C2410_CLKCON_GPIO | S3C2410_CLKCON_RTC, S3C2410_CLKCON); + + /* register our clocks */ + + if (s3c2410_register_clock(&clk_f) < 0) + printk(KERN_ERR "failed to register cpu fclk\n"); + + if (s3c2410_register_clock(&clk_h) < 0) + printk(KERN_ERR "failed to register cpu hclk\n"); + + if (s3c2410_register_clock(&clk_p) < 0) + printk(KERN_ERR "failed to register cpu pclk\n"); + + for (ptr = 0; ptr < ARRAY_SIZE(init_clocks); ptr++, clkp++) { + ret = s3c2410_register_clock(clkp); + if (ret < 0) { + printk(KERN_ERR "Failed to register clock %s (%d)\n", + clkp->name, ret); + } + } + + return 0; +} + +arch_initcall(s3c2410_init_clocks); + diff --git a/arch/arm/mach-s3c2410/clock.h b/arch/arm/mach-s3c2410/clock.h new file mode 100644 index 000000000..4c7b94e87 --- /dev/null +++ b/arch/arm/mach-s3c2410/clock.h @@ -0,0 +1,20 @@ +/* + * linux/arch/arm/mach-s3c2410/clock.h + * + * Copyright (c) 2004 Simtec Electronics + * Written 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. +*/ + +struct clk { + struct list_head list; + struct module *owner; + struct clk *parent; + const char *name; + atomic_t used; + unsigned long rate; + unsigned long ctrlbit; +}; diff --git a/arch/arm/mach-s3c2410/cpu.c b/arch/arm/mach-s3c2410/cpu.c new file mode 100644 index 000000000..42eec7912 --- /dev/null +++ b/arch/arm/mach-s3c2410/cpu.c @@ -0,0 +1,152 @@ +/* linux/arch/arm/mach-s3c2410/cpu.c + * + * Copyright (c) 2004 Simtec Electronics + * Ben Dooks + * + * 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 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 +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include + +#include + +#include "cpu.h" +#include "s3c2410.h" +#include "s3c2440.h" + +struct cpu_table { + unsigned long idcode; + unsigned long idmask; + void (*map_io)(struct map_desc *mach_desc, int size); + int (*init)(void); + const char *name; +}; + +/* table of supported CPUs */ + +static const char name_s3c2410[] = "S3C2410"; +static const char name_s3c2440[] = "S3C2440"; +static const char name_s3c2410a[] = "S3C2410A"; +static const char name_s3c2440a[] = "S3C2440A"; + +static struct cpu_table cpu_ids[] __initdata = { + { + .idcode = 0x32410000, + .idmask = 0xffffffff, + .map_io = s3c2410_map_io, + .init = s3c2410_init, + .name = name_s3c2410 + }, + { + .idcode = 0x3241002, + .idmask = 0xffffffff, + .map_io = s3c2410_map_io, + .init = s3c2410_init, + .name = name_s3c2410a + }, + { + .idcode = 0x32440000, + .idmask = 0xffffffff, + .map_io = s3c2440_map_io, + .init = s3c2440_init, + .name = name_s3c2440 + }, + { + .idcode = 0x32440001, + .idmask = 0xffffffff, + .map_io = s3c2440_map_io, + .init = s3c2440_init, + .name = name_s3c2440a + } +}; + +/* minimal IO mapping */ + +static struct map_desc s3c_iodesc[] __initdata = { + IODESC_ENT(GPIO), + IODESC_ENT(IRQ), + IODESC_ENT(MEMCTRL), + IODESC_ENT(UART) +}; + + +static struct cpu_table * +s3c_lookup_cpu(unsigned long idcode) +{ + struct cpu_table *tab; + int count; + + tab = cpu_ids; + for (count = 0; count < ARRAY_SIZE(cpu_ids); count++) { + if ((idcode & tab->idmask) == tab->idcode) + return tab; + } + + return NULL; +} + +static struct cpu_table *cpu; + +void __init s3c24xx_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)); + + idcode = __raw_readl(S3C2410_GSTATUS1); + cpu = s3c_lookup_cpu(idcode); + + if (cpu == NULL) { + printk(KERN_ERR "Unknown CPU type 0x%08lx\n", idcode); + panic("Unknown S3C24XX CPU"); + } + + if (cpu->map_io == NULL || cpu->init == NULL) { + printk(KERN_ERR "CPU %s support not enabled\n", cpu->name); + panic("Unsupported S3C24XX CPU"); + } + + printk("CPU %s (id 0x%08lx)\n", cpu->name, idcode); + + (cpu->map_io)(mach_desc, size); +} + +static int __init s3c_arch_init(void) +{ + // do the correct init for cpu + + if (cpu == NULL) + panic("s3c_arch_init: NULL cpu\n"); + + return (cpu->init)(); +} + +arch_initcall(s3c_arch_init); diff --git a/arch/arm/mach-s3c2410/cpu.h b/arch/arm/mach-s3c2410/cpu.h new file mode 100644 index 000000000..b0053d76d --- /dev/null +++ b/arch/arm/mach-s3c2410/cpu.h @@ -0,0 +1,41 @@ +/* arch/arm/mach-s3c2410/cpu.h + * + * Copyright (c) 2004 Simtec Electronics + * Ben Dooks + * + * 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. + * + * Modifications: + * 24-Aug-2004 BJD Start of generic S3C24XX support +*/ + +#define IODESC_ENT(x) { S3C2410_VA_##x, S3C2410_PA_##x, S3C2410_SZ_##x, MT_DEVICE } + +#ifndef MHZ +#define MHZ (1000*1000) +#endif + +#define print_mhz(m) ((m) / MHZ), ((m / 1000) % 1000) + +#ifdef CONFIG_CPU_S3C2410 +extern int s3c2410_init(void); +extern void s3c2410_map_io(struct map_desc *mach_desc, int size); +#else +#define s3c2410_map_io NULL +#define s3c2410_init NULL +#endif + +#ifdef CONFIG_CPU_S3C2440 +extern int s3c2440_init(void); +extern void s3c2440_map_io(struct map_desc *mach_desc, int size); +#else +#define s3c2440_map_io NULL +#define s3c2440_init NULL +#endif + +extern void s3c24xx_init_io(struct map_desc *mach_desc, int size); + diff --git a/arch/arm/mach-s3c2410/devs.c b/arch/arm/mach-s3c2410/devs.c new file mode 100644 index 000000000..7cf560cae --- /dev/null +++ b/arch/arm/mach-s3c2410/devs.c @@ -0,0 +1,442 @@ +/* linux/arch/arm/mach-s3c2410/devs.c + * + * Copyright (c) 2004 Simtec Electronics + * Ben Dooks + * + * Base S3C2410 platform 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. + * + * Modifications: + * 29-Aug-2004 BJD Added timers 0 through 3 + * 29-Aug-2004 BJD Changed index of devices we only have one of to -1 + * 21-Aug-2004 BJD Added IRQ_TICK to RTC resources + * 18-Aug-2004 BJD Created initial version +*/ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include + +#include "devs.h" + +/* USB Host Controller */ + +static struct resource s3c_usb_resource[] = { + [0] = { + .start = S3C2410_PA_USBHOST, + .end = S3C2410_PA_USBHOST + S3C2410_SZ_USBHOST, + .flags = IORESOURCE_MEM, + }, + [1] = { + .start = IRQ_USBH, + .end = IRQ_USBH, + .flags = IORESOURCE_IRQ, + } +}; + +static u64 s3c_device_usb_dmamask = 0xffffffffUL; + +struct platform_device s3c_device_usb = { + .name = "s3c2410-ohci", + .id = -1, + .num_resources = ARRAY_SIZE(s3c_usb_resource), + .resource = s3c_usb_resource, + .dev = { + .dma_mask = &s3c_device_usb_dmamask, + .coherent_dma_mask = 0xffffffffUL + } +}; + +EXPORT_SYMBOL(s3c_device_usb); + +/* LCD Controller */ + +static struct resource s3c_lcd_resource[] = { + [0] = { + .start = S3C2410_PA_LCD, + .end = S3C2410_PA_LCD + S3C2410_SZ_LCD, + .flags = IORESOURCE_MEM, + }, + [1] = { + .start = IRQ_LCD, + .end = IRQ_LCD, + .flags = IORESOURCE_IRQ, + } + +}; + +static u64 s3c_device_lcd_dmamask = 0xffffffffUL; + +struct platform_device s3c_device_lcd = { + .name = "s3c2410-lcd", + .id = -1, + .num_resources = ARRAY_SIZE(s3c_lcd_resource), + .resource = s3c_lcd_resource, + .dev = { + .dma_mask = &s3c_device_lcd_dmamask, + .coherent_dma_mask = 0xffffffffUL + } +}; + +EXPORT_SYMBOL(s3c_device_lcd); + +/* NAND Controller */ + +static struct resource s3c_nand_resource[] = { + [0] = { + .start = S3C2410_PA_NAND, + .end = S3C2410_PA_NAND + S3C2410_SZ_NAND, + .flags = IORESOURCE_MEM, + } +}; + +struct platform_device s3c_device_nand = { + .name = "s3c2410-nand", + .id = -1, + .num_resources = ARRAY_SIZE(s3c_nand_resource), + .resource = s3c_nand_resource, +}; + +EXPORT_SYMBOL(s3c_device_nand); + +/* USB Device (Gadget)*/ + +static struct resource s3c_usbgadget_resource[] = { + [0] = { + .start = S3C2410_PA_USBDEV, + .end = S3C2410_PA_USBDEV + S3C2410_SZ_USBDEV, + .flags = IORESOURCE_MEM, + }, + [1] = { + .start = IRQ_USBD, + .end = IRQ_USBD, + .flags = IORESOURCE_IRQ, + } + +}; + +struct platform_device s3c_device_usbgadget = { + .name = "s3c2410-usbgadget", + .id = -1, + .num_resources = ARRAY_SIZE(s3c_usbgadget_resource), + .resource = s3c_usbgadget_resource, +}; + +EXPORT_SYMBOL(s3c_device_usbgadget); + +/* Watchdog */ + +static struct resource s3c_wdt_resource[] = { + [0] = { + .start = S3C2410_PA_WATCHDOG, + .end = S3C2410_PA_WATCHDOG + S3C2410_SZ_WATCHDOG, + .flags = IORESOURCE_MEM, + }, + [1] = { + .start = IRQ_WDT, + .end = IRQ_WDT, + .flags = IORESOURCE_IRQ, + } + +}; + +struct platform_device s3c_device_wdt = { + .name = "s3c2410-wdt", + .id = -1, + .num_resources = ARRAY_SIZE(s3c_wdt_resource), + .resource = s3c_wdt_resource, +}; + +EXPORT_SYMBOL(s3c_device_wdt); + +/* I2C */ + +static struct resource s3c_i2c_resource[] = { + [0] = { + .start = S3C2410_PA_IIC, + .end = S3C2410_PA_IIC + S3C2410_SZ_IIC, + .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[] = { + [0] = { + .start = S3C2410_PA_IIS, + .end = S3C2410_PA_IIS + S3C2410_SZ_IIS, + .flags = IORESOURCE_MEM, + } +}; + +static u64 s3c_device_iis_dmamask = 0xffffffffUL; + +struct platform_device s3c_device_iis = { + .name = "s3c2410-iis", + .id = -1, + .num_resources = ARRAY_SIZE(s3c_iis_resource), + .resource = s3c_iis_resource, + .dev = { + .dma_mask = &s3c_device_iis_dmamask, + .coherent_dma_mask = 0xffffffffUL + } +}; + +EXPORT_SYMBOL(s3c_device_iis); + +/* RTC */ + +static struct resource s3c_rtc_resource[] = { + [0] = { + .start = S3C2410_PA_RTC, + .end = S3C2410_PA_RTC + 0xff, + .flags = IORESOURCE_MEM, + }, + [1] = { + .start = IRQ_RTC, + .end = IRQ_RTC, + .flags = IORESOURCE_IRQ, + }, + [2] = { + .start = IRQ_TICK, + .end = IRQ_TICK, + .flags = IORESOURCE_IRQ + } +}; + +struct platform_device s3c_device_rtc = { + .name = "s3c2410-rtc", + .id = -1, + .num_resources = ARRAY_SIZE(s3c_rtc_resource), + .resource = s3c_rtc_resource, +}; + +EXPORT_SYMBOL(s3c_device_rtc); + +/* ADC */ + +static struct resource s3c_adc_resource[] = { + [0] = { + .start = S3C2410_PA_ADC, + .end = S3C2410_PA_ADC + S3C2410_SZ_ADC, + .flags = IORESOURCE_MEM, + }, + [1] = { + .start = IRQ_TC, + .end = IRQ_ADC, + .flags = IORESOURCE_IRQ, + } + +}; + +struct platform_device s3c_device_adc = { + .name = "s3c2410-adc", + .id = -1, + .num_resources = ARRAY_SIZE(s3c_adc_resource), + .resource = s3c_adc_resource, +}; + +/* SDI */ + +static struct resource s3c_sdi_resource[] = { + [0] = { + .start = S3C2410_PA_SDI, + .end = S3C2410_PA_SDI + S3C2410_SZ_SDI, + .flags = IORESOURCE_MEM, + }, + [1] = { + .start = IRQ_SDI, + .end = IRQ_SDI, + .flags = IORESOURCE_IRQ, + } + +}; + +struct platform_device s3c_device_sdi = { + .name = "s3c2410-sdi", + .id = -1, + .num_resources = ARRAY_SIZE(s3c_sdi_resource), + .resource = s3c_sdi_resource, +}; + +EXPORT_SYMBOL(s3c_device_sdi); + +/* SPI (0) */ + +static struct resource s3c_spi0_resource[] = { + [0] = { + .start = S3C2410_PA_SPI, + .end = S3C2410_PA_SPI + 0x1f, + .flags = IORESOURCE_MEM, + }, + [1] = { + .start = IRQ_SPI0, + .end = IRQ_SPI0, + .flags = IORESOURCE_IRQ, + } + +}; + +struct platform_device s3c_device_spi0 = { + .name = "s3c2410-spi", + .id = 0, + .num_resources = ARRAY_SIZE(s3c_spi0_resource), + .resource = s3c_spi0_resource, +}; + +EXPORT_SYMBOL(s3c_device_spi0); + +/* SPI (1) */ + +static struct resource s3c_spi1_resource[] = { + [0] = { + .start = S3C2410_PA_SPI + 0x20, + .end = S3C2410_PA_SPI + 0x20 + 0x1f, + .flags = IORESOURCE_MEM, + }, + [1] = { + .start = IRQ_SPI1, + .end = IRQ_SPI1, + .flags = IORESOURCE_IRQ, + } + +}; + +struct platform_device s3c_device_spi1 = { + .name = "s3c2410-spi", + .id = 1, + .num_resources = ARRAY_SIZE(s3c_spi1_resource), + .resource = s3c_spi1_resource, +}; + +EXPORT_SYMBOL(s3c_device_spi1); + +/* pwm timer blocks */ + +static struct resource s3c_timer0_resource[] = { + [0] = { + .start = S3C2410_PA_TIMER + 0x0C, + .end = S3C2410_PA_TIMER + 0x0C + 0xB, + .flags = IORESOURCE_MEM, + }, + [1] = { + .start = IRQ_TIMER0, + .end = IRQ_TIMER0, + .flags = IORESOURCE_IRQ, + } + +}; + +struct platform_device s3c_device_timer0 = { + .name = "s3c2410-timer", + .id = 0, + .num_resources = ARRAY_SIZE(s3c_timer0_resource), + .resource = s3c_timer0_resource, +}; + +EXPORT_SYMBOL(s3c_device_timer0); + +/* timer 1 */ + +static struct resource s3c_timer1_resource[] = { + [0] = { + .start = S3C2410_PA_TIMER + 0x18, + .end = S3C2410_PA_TIMER + 0x23, + .flags = IORESOURCE_MEM, + }, + [1] = { + .start = IRQ_TIMER1, + .end = IRQ_TIMER1, + .flags = IORESOURCE_IRQ, + } + +}; + +struct platform_device s3c_device_timer1 = { + .name = "s3c2410-timer", + .id = 1, + .num_resources = ARRAY_SIZE(s3c_timer1_resource), + .resource = s3c_timer1_resource, +}; + +EXPORT_SYMBOL(s3c_device_timer1); + +/* timer 2 */ + +static struct resource s3c_timer2_resource[] = { + [0] = { + .start = S3C2410_PA_TIMER + 0x24, + .end = S3C2410_PA_TIMER + 0x2F, + .flags = IORESOURCE_MEM, + }, + [1] = { + .start = IRQ_TIMER2, + .end = IRQ_TIMER2, + .flags = IORESOURCE_IRQ, + } + +}; + +struct platform_device s3c_device_timer2 = { + .name = "s3c2410-timer", + .id = 2, + .num_resources = ARRAY_SIZE(s3c_timer2_resource), + .resource = s3c_timer2_resource, +}; + +EXPORT_SYMBOL(s3c_device_timer2); + +/* timer 3 */ + +static struct resource s3c_timer3_resource[] = { + [0] = { + .start = S3C2410_PA_TIMER + 0x30, + .end = S3C2410_PA_TIMER + 0x3B, + .flags = IORESOURCE_MEM, + }, + [1] = { + .start = IRQ_TIMER3, + .end = IRQ_TIMER3, + .flags = IORESOURCE_IRQ, + } + +}; + +struct platform_device s3c_device_timer3 = { + .name = "s3c2410-timer", + .id = 3, + .num_resources = ARRAY_SIZE(s3c_timer3_resource), + .resource = s3c_timer3_resource, +}; + +EXPORT_SYMBOL(s3c_device_timer3); diff --git a/arch/arm/mach-s3c2410/devs.h b/arch/arm/mach-s3c2410/devs.h new file mode 100644 index 000000000..08a441659 --- /dev/null +++ b/arch/arm/mach-s3c2410/devs.h @@ -0,0 +1,36 @@ +/* arch/arm/mach-s3c2410/devs.h + * + * Copyright (c) 2004 Simtec Electronics + * Ben Dooks + * + * 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. + * + * Modifications: + * 18-Aug-2004 BJD Created initial version + * 27-Aug-2004 BJD Added timers 0 through 3 +*/ + +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_spi0; +extern struct platform_device s3c_device_spi1; + +extern struct platform_device s3c_device_nand; + +extern struct platform_device s3c_device_timer0; +extern struct platform_device s3c_device_timer1; +extern struct platform_device s3c_device_timer2; +extern struct platform_device s3c_device_timer3; + +extern struct platform_device s3c_device_usbgadget; diff --git a/arch/arm/mach-s3c2410/dma.c b/arch/arm/mach-s3c2410/dma.c new file mode 100644 index 000000000..819e5af16 --- /dev/null +++ b/arch/arm/mach-s3c2410/dma.c @@ -0,0 +1,1085 @@ +/* linux/arch/arm/mach-bast/dma.c + * + * (c) 2003,2004 Simtec Electronics + * Ben Dooks + * + * S3C2410 DMA core + * + * http://www.simtec.co.uk/products/EB2410ITX/ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Changelog: + * 08-Aug-2004 BJD Apply rmk's suggestions + * 21-Jul-2004 BJD Ported to linux 2.6 + * 12-Jul-2004 BJD Finished re-write and change of API + * 06-Jul-2004 BJD Rewrote dma code to try and cope with various problems + * 23-May-2003 BJD Created file + * 19-Aug-2003 BJD Cleanup, header fix, added URL + * + * This file is based on the Sangwook Lee/Samsung patches, re-written due + * to various ommisions from the code (such as flexible dma configuration) + * for use with the BAST system board. + * + * The re-write is pretty much complete, and should be good enough for any + * possible DMA function + */ + +#include + +#ifdef CONFIG_S3C2410_DMA_DEBUG +#define DEBUG +#endif + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include + +/* io map for dma */ +static void *dma_base; + +/* dma channel state information */ +s3c2410_dma_chan_t s3c2410_chans[S3C2410_DMA_CHANNELS]; + +/* debugging functions */ + +#define BUF_MAGIC (0xcafebabe) + +#define dmawarn(fmt...) printk(KERN_DEBUG fmt) + +#define dma_regaddr(chan, reg) ((chan)->regs + (reg)) + +#if 1 +#define dma_wrreg(chan, reg, val) writel((val), (chan)->regs + (reg)) +#else +static inline void +dma_wrreg(s3c2410_dma_chan_t *chan, int reg, unsigned long val) +{ + pr_debug("writing %08x to register %08x\n",(unsigned int)val,reg); + writel(val, dma_regaddr(chan, reg)); +} + +#endif + +#define dma_rdreg(chan, reg) readl((chan)->regs + (reg)) + +/* captured register state for debug */ + +struct s3c2410_dma_regstate { + unsigned long dcsrc; + unsigned long disrc; + unsigned long dstat; + unsigned long dcon; + unsigned long dmsktrig; +}; + +#ifdef CONFIG_S3C2410_DMA_DEBUG + +/* dmadbg_showregs + * + * simple debug routine to print the current state of the dma registers +*/ + +static void +dmadbg_capture(s3c2410_dma_chan_t *chan, struct s3c2410_dma_regstate *regs) +{ + regs->dcsrc = dma_rdreg(chan, S3C2410_DMA_DCSRC); + regs->disrc = dma_rdreg(chan, S3C2410_DMA_DISRC); + regs->dstat = dma_rdreg(chan, S3C2410_DMA_DSTAT); + regs->dcon = dma_rdreg(chan, S3C2410_DMA_DCON); + regs->dmsktrig = dma_rdreg(chan, S3C2410_DMA_DMASKTRIG); +} + +static void +dmadbg_showregs(const char *fname, int line, s3c2410_dma_chan_t *chan, + struct s3c2410_dma_regstate *regs) +{ + printk(KERN_DEBUG "dma%d: %s:%d: DCSRC=%08lx, DISRC=%08lx, DSTAT=%08lx DMT=%02lx, DCON=%08lx\n", + chan->number, fname, line, + regs->dcsrc, regs->disrc, regs->dstat, regs->dmsktrig, + regs->dcon); +} + +static void +dmadbg_showchan(const char *fname, int line, s3c2410_dma_chan_t *chan) +{ + struct s3c2410_dma_regstate state; + + dmadbg_capture(chan, &state); + + printk(KERN_DEBUG "dma%d: %s:%d: ls=%d, cur=%p, %p %p\n", + chan->number, fname, line, chan->load_state, + chan->curr, chan->next, chan->end); + + dmadbg_showregs(fname, line, chan, &state); +} + +#define dbg_showregs(chan) dmadbg_showregs(__FUNCTION__, __LINE__, (chan)) +#define dbg_showchan(chan) dmadbg_showchan(__FUNCTION__, __LINE__, (chan)) +#else +#define dbg_showregs(chan) do { } while(0) +#define dbg_showchan(chan) do { } while(0) +#endif /* CONFIG_S3C2410_DMA_DEBUG */ + +#define check_channel(chan) \ + do { if ((chan) >= S3C2410_DMA_CHANNELS) { \ + printk(KERN_ERR "%s: invalid channel %d\n", __FUNCTION__, (chan)); \ + return -EINVAL; \ + } } while(0) + + +/* s3c2410_dma_stats_timeout + * + * Update DMA stats from timeout info +*/ + +static void +s3c2410_dma_stats_timeout(s3c2410_dma_stats_t *stats, int val) +{ + if (stats == NULL) + return; + + if (val > stats->timeout_longest) + stats->timeout_longest = val; + if (val < stats->timeout_shortest) + stats->timeout_shortest = val; + + stats->timeout_avg += val; +} + +/* s3c2410_dma_waitforload + * + * wait for the DMA engine to load a buffer, and update the state accordingly +*/ + +static int +s3c2410_dma_waitforload(s3c2410_dma_chan_t *chan, int line) +{ + int timeout = chan->load_timeout; + int took; + + if (chan->load_state != S3C2410_DMALOAD_1LOADED) { + printk(KERN_ERR "dma%d: s3c2410_dma_waitforload() called in loadstate %d from line %d\n", chan->number, chan->load_state, line); + return 0; + } + + if (chan->stats != NULL) + chan->stats->loads++; + + while (--timeout > 0) { + if ((dma_rdreg(chan, S3C2410_DMA_DSTAT) << (32-20)) != 0) { + took = chan->load_timeout - timeout; + + s3c2410_dma_stats_timeout(chan->stats, took); + + switch (chan->load_state) { + case S3C2410_DMALOAD_1LOADED: + chan->load_state = S3C2410_DMALOAD_1RUNNING; + break; + + default: + printk(KERN_ERR "dma%d: unknown load_state in s3c2410_dma_waitforload() %d\n", chan->number, chan->load_state); + } + + return 1; + } + } + + if (chan->stats != NULL) { + chan->stats->timeout_failed++; + } + + return 0; +} + + + +/* s3c2410_dma_loadbuffer + * + * load a buffer, and update the channel state +*/ + +static inline int +s3c2410_dma_loadbuffer(s3c2410_dma_chan_t *chan, + s3c2410_dma_buf_t *buf) +{ + unsigned long reload; + + pr_debug("s3c2410_chan_loadbuffer: loading buff %p (0x%08lx,0x%06x)\n", + buf, (unsigned long)buf->data, buf->size); + + if (buf == NULL) { + dmawarn("buffer is NULL\n"); + return -EINVAL; + } + + /* check the state of the channel before we do anything */ + + if (chan->load_state == S3C2410_DMALOAD_1LOADED) { + dmawarn("load_state is S3C2410_DMALOAD_1LOADED\n"); + } + + if (chan->load_state == S3C2410_DMALOAD_1LOADED_1RUNNING) { + dmawarn("state is S3C2410_DMALOAD_1LOADED_1RUNNING\n"); + } + + /* it would seem sensible if we are the last buffer to not bother + * with the auto-reload bit, so that the DMA engine will not try + * and load another transfer after this one has finished... + */ + if (chan->load_state == S3C2410_DMALOAD_NONE) { + pr_debug("load_state is none, checking for noreload (next=%p)\n", + buf->next); + reload = (buf->next == NULL) ? S3C2410_DCON_NORELOAD : 0; + } else { + pr_debug("load_state is %d => autoreload\n", chan->load_state); + reload = S3C2410_DCON_AUTORELOAD; + } + + writel(buf->data, chan->addr_reg); + + dma_wrreg(chan, S3C2410_DMA_DCON, + chan->dcon | reload | (buf->size/chan->xfer_unit)); + + chan->next = buf->next; + + /* update the state of the channel */ + + switch (chan->load_state) { + case S3C2410_DMALOAD_NONE: + chan->load_state = S3C2410_DMALOAD_1LOADED; + break; + + case S3C2410_DMALOAD_1RUNNING: + chan->load_state = S3C2410_DMALOAD_1LOADED_1RUNNING; + break; + + default: + dmawarn("dmaload: unknown state %d in loadbuffer\n", + chan->load_state); + break; + } + + return 0; +} + +/* s3c2410_dma_call_op + * + * small routine to call the op routine with the given op if it has been + * registered +*/ + +static void +s3c2410_dma_call_op(s3c2410_dma_chan_t *chan, s3c2410_chan_op_t op) +{ + if (chan->op_fn != NULL) { + (chan->op_fn)(chan, op); + } +} + +/* s3c2410_dma_buffdone + * + * small wrapper to check if callback routine needs to be called, and + * if so, call it +*/ + +static inline void +s3c2410_dma_buffdone(s3c2410_dma_chan_t *chan, s3c2410_dma_buf_t *buf, + s3c2410_dma_buffresult_t result) +{ + pr_debug("callback_fn=%p, buf=%p, id=%p, size=%d, result=%d\n", + chan->callback_fn, buf, buf->id, buf->size, result); + + if (chan->callback_fn != NULL) { + (chan->callback_fn)(chan, buf->id, buf->size, result); + } +} + +/* s3c2410_dma_start + * + * start a dma channel going +*/ + +static int s3c2410_dma_start(s3c2410_dma_chan_t *chan) +{ + unsigned long tmp; + unsigned long flags; + + pr_debug("s3c2410_start_dma: channel=%d\n", chan->number); + + local_irq_save(flags); + + if (chan->state == S3C2410_DMA_RUNNING) { + pr_debug("s3c2410_start_dma: already running (%d)\n", chan->state); + local_irq_restore(flags); + return 0; + } + + chan->state = S3C2410_DMA_RUNNING; + + /* check wether there is anything to load, and if not, see + * if we can find anything to load + */ + + if (chan->load_state == S3C2410_DMALOAD_NONE) { + if (chan->next == NULL) { + printk(KERN_ERR "dma%d: channel has nothing loaded\n", + chan->number); + chan->state = S3C2410_DMA_IDLE; + local_irq_restore(flags); + return -EINVAL; + } + + s3c2410_dma_loadbuffer(chan, chan->next); + } + + dbg_showchan(chan); + + /* enable the channel */ + + if (!chan->irq_enabled) { + enable_irq(chan->irq); + chan->irq_enabled = 1; + } + + /* start the channel going */ + + tmp = dma_rdreg(chan, S3C2410_DMA_DMASKTRIG); + tmp &= ~S3C2410_DMASKTRIG_STOP; + tmp |= S3C2410_DMASKTRIG_ON; + dma_wrreg(chan, S3C2410_DMA_DMASKTRIG, tmp); + + pr_debug("wrote %08lx to DMASKTRIG\n", tmp); + +#if 0 + /* the dma buffer loads should take care of clearing the AUTO + * reloading feature */ + tmp = dma_rdreg(chan, S3C2410_DMA_DCON); + tmp &= ~S3C2410_DCON_NORELOAD; + dma_wrreg(chan, S3C2410_DMA_DCON, tmp); +#endif + + s3c2410_dma_call_op(chan, S3C2410_DMAOP_START); + + dbg_showchan(chan); + + local_irq_restore(flags); + return 0; +} + +/* s3c2410_dma_canload + * + * work out if we can queue another buffer into the DMA engine +*/ + +static int +s3c2410_dma_canload(s3c2410_dma_chan_t *chan) +{ + if (chan->load_state == S3C2410_DMALOAD_NONE || + chan->load_state == S3C2410_DMALOAD_1RUNNING) + return 1; + + return 0; +} + + +/* s3c2410_dma_enqueue + * + * queue an given buffer for dma transfer. + * + * id the device driver's id information for this buffer + * data the physical address of the buffer data + * size the size of the buffer in bytes + * + * If the channel is not running, then the flag S3C2410_DMAF_AUTOSTART + * is checked, and if set, the channel is started. If this flag isn't set, + * then an error will be returned. + * + * It is possible to queue more than one DMA buffer onto a channel at + * once, and the code will deal with the re-loading of the next buffer + * when necessary. +*/ + +int s3c2410_dma_enqueue(unsigned int channel, void *id, + dma_addr_t data, int size) +{ + s3c2410_dma_chan_t *chan = &s3c2410_chans[channel]; + s3c2410_dma_buf_t *buf; + unsigned long flags; + + check_channel(channel); + + pr_debug("%s: id=%p, data=%08x, size=%d\n", + __FUNCTION__, id, (unsigned int)data, size); + + buf = (s3c2410_dma_buf_t *)kmalloc(sizeof(*buf), GFP_ATOMIC); + if (buf == NULL) { + pr_debug("%s: out of memory (%d alloc)\n", + __FUNCTION__, sizeof(*buf)); + return -ENOMEM; + } + + pr_debug("%s: new buffer %p\n", __FUNCTION__, buf); + + //dbg_showchan(chan); + + buf->next = NULL; + buf->data = buf->ptr = data; + buf->size = size; + buf->id = id; + buf->magic = BUF_MAGIC; + + local_irq_save(flags); + + if (chan->curr == NULL) { + /* we've got nothing loaded... */ + pr_debug("%s: buffer %p queued onto empty channel\n", + __FUNCTION__, buf); + + chan->curr = buf; + chan->end = buf; + chan->next = NULL; + } else { + pr_debug("dma%d: %s: buffer %p queued onto non-empty channel\n", + chan->number, __FUNCTION__, buf); + + if (chan->end == NULL) + pr_debug("dma%d: %s: %p not empty, and chan->end==NULL?\n", + chan->number, __FUNCTION__, chan); + + chan->end->next = buf; + chan->end = buf; + } + + /* if necessary, update the next buffer field */ + if (chan->next == NULL) + chan->next = buf; + + /* check to see if we can load a buffer */ + if (chan->state == S3C2410_DMA_RUNNING) { + if (chan->load_state == S3C2410_DMALOAD_1LOADED && 1) { + if (s3c2410_dma_waitforload(chan, __LINE__) == 0) { + printk(KERN_ERR "dma%d: loadbuffer:" + "timeout loading buffer\n", + chan->number); + dbg_showchan(chan); + local_irq_restore(flags); + return -EINVAL; + } + } + + while (s3c2410_dma_canload(chan) && chan->next != NULL) { + s3c2410_dma_loadbuffer(chan, chan->next); + } + } else if (chan->state == S3C2410_DMA_IDLE) { + if (chan->flags & S3C2410_DMAF_AUTOSTART) { + s3c2410_dma_ctrl(chan->number, S3C2410_DMAOP_START); + } else { + printk(KERN_DEBUG "dma%d: cannot load onto stopped channel'n", chan->number); + local_irq_restore(flags); + return -EINVAL; + } + } + + local_irq_restore(flags); + return 0; +} + +static inline void +s3c2410_dma_freebuf(s3c2410_dma_buf_t *buf) +{ + int magicok = (buf->magic == BUF_MAGIC); + + buf->magic = -1; + + if (magicok) { + kfree(buf); + } else { + printk("s3c2410_dma_freebuf: buff %p with bad magic\n", buf); + } +} + +/* s3c2410_dma_lastxfer + * + * called when the system is out of buffers, to ensure that the channel + * is prepared for shutdown. +*/ + +static inline void +s3c2410_dma_lastxfer(s3c2410_dma_chan_t *chan) +{ + pr_debug("dma%d: s3c2410_dma_lastxfer: load_state %d\n", + chan->number, chan->load_state); + + switch (chan->load_state) { + case S3C2410_DMALOAD_NONE: + break; + + case S3C2410_DMALOAD_1LOADED: + if (s3c2410_dma_waitforload(chan, __LINE__) == 0) { + /* flag error? */ + printk(KERN_ERR "dma%d: timeout waiting for load\n", + chan->number); + return; + } + break; + + default: + pr_debug("dma%d: lastxfer: unhandled load_state %d with no next", + chan->number, chan->load_state); + return; + + } + + /* hopefully this'll shut the damned thing up after the transfer... */ + dma_wrreg(chan, S3C2410_DMA_DCON, chan->dcon | S3C2410_DCON_NORELOAD); +} + + +#define dmadbg2(x...) + +static irqreturn_t +s3c2410_dma_irq(int irq, void *devpw, struct pt_regs *regs) +{ + s3c2410_dma_chan_t *chan = (s3c2410_dma_chan_t *)devpw; + s3c2410_dma_buf_t *buf; + + buf = chan->curr; + + dbg_showchan(chan); + + /* modify the channel state */ + + switch (chan->load_state) { + case S3C2410_DMALOAD_1RUNNING: + /* TODO - if we are running only one buffer, we probably + * want to reload here, and then worry about the buffer + * callback */ + + chan->load_state = S3C2410_DMALOAD_NONE; + break; + + case S3C2410_DMALOAD_1LOADED: + /* iirc, we should go back to NONE loaded here, we + * had a buffer, and it was never verified as being + * loaded. + */ + + chan->load_state = S3C2410_DMALOAD_NONE; + break; + + case S3C2410_DMALOAD_1LOADED_1RUNNING: + /* we'll worry about checking to see if another buffer is + * ready after we've called back the owner. This should + * ensure we do not wait around too long for the DMA + * engine to start the next transfer + */ + + chan->load_state = S3C2410_DMALOAD_1LOADED; + break; + + case S3C2410_DMALOAD_NONE: + printk(KERN_ERR "dma%d: IRQ with no loaded buffer?\n", + chan->number); + break; + + default: + printk(KERN_ERR "dma%d: IRQ in invalid load_state %d\n", + chan->number, chan->load_state); + break; + } + + if (buf != NULL) { + /* update the chain to make sure that if we load any more + * buffers when we call the callback function, things should + * work properly */ + + chan->curr = buf->next; + buf->next = NULL; + + if (buf->magic != BUF_MAGIC) { + printk(KERN_ERR "dma%d: %s: buf %p incorrect magic\n", + chan->number, __FUNCTION__, buf); + return IRQ_HANDLED; + } + + s3c2410_dma_buffdone(chan, buf, S3C2410_RES_OK); + + /* free resouces */ + s3c2410_dma_freebuf(buf); + } else { + } + + if (chan->next != NULL) { + unsigned long flags; + + switch (chan->load_state) { + case S3C2410_DMALOAD_1RUNNING: + /* don't need to do anything for this state */ + break; + + case S3C2410_DMALOAD_NONE: + /* can load buffer immediately */ + break; + + case S3C2410_DMALOAD_1LOADED: + if (s3c2410_dma_waitforload(chan, __LINE__) == 0) { + /* flag error? */ + printk(KERN_ERR "dma%d: timeout waiting for load\n", + chan->number); + return IRQ_HANDLED; + } + + break; + + default: + printk(KERN_ERR "dma%d: unknown load_state in irq, %d\n", + chan->number, chan->load_state); + return IRQ_HANDLED; + } + + local_irq_save(flags); + s3c2410_dma_loadbuffer(chan, chan->next); + local_irq_restore(flags); + } else { + s3c2410_dma_lastxfer(chan); + + /* see if we can stop this channel.. */ + if (chan->load_state == S3C2410_DMALOAD_NONE) { + pr_debug("dma%d: end of transfer, stopping channel (%ld)\n", + chan->number, jiffies); + s3c2410_dma_ctrl(chan->number, S3C2410_DMAOP_STOP); + } + } + + return IRQ_HANDLED; +} + + + +/* s3c2410_request_dma + * + * get control of an dma channel +*/ + +int s3c2410_dma_request(unsigned int channel, s3c2410_dma_client_t *client, + void *dev) +{ + s3c2410_dma_chan_t *chan = &s3c2410_chans[channel]; + unsigned long flags; + int err; + + pr_debug("dma%d: s3c2410_request_dma: client=%s, dev=%p\n", + channel, client->name, dev); + + check_channel(channel); + + local_irq_save(flags); + + dbg_showchan(chan); + + if (chan->in_use) { + if (client != chan->client) { + printk(KERN_ERR "dma%d: already in use\n", channel); + local_irq_restore(flags); + return -EBUSY; + } else { + printk(KERN_ERR "dma%d: client already has channel\n", channel); + } + } + + chan->client = client; + chan->in_use = 1; + + if (!chan->irq_claimed) { + pr_debug("dma%d: %s : requesting irq %d\n", + channel, __FUNCTION__, chan->irq); + + err = request_irq(chan->irq, s3c2410_dma_irq, SA_INTERRUPT, + client->name, (void *)chan); + + if (err) { + chan->in_use = 0; + local_irq_restore(flags); + + printk(KERN_ERR "%s: cannot get IRQ %d for DMA %d\n", + client->name, chan->irq, chan->number); + return err; + } + + chan->irq_claimed = 1; + chan->irq_enabled = 1; + } + + local_irq_restore(flags); + + /* need to setup */ + + pr_debug("%s: channel initialised, %p\n", __FUNCTION__, chan); + + return 0; +} + +/* s3c2410_dma_free + * + * release the given channel back to the system, will stop and flush + * any outstanding transfers, and ensure the channel is ready for the + * next claimant. + * + * Note, although a warning is currently printed if the freeing client + * info is not the same as the registrant's client info, the free is still + * allowed to go through. +*/ + +int s3c2410_dma_free(dmach_t channel, s3c2410_dma_client_t *client) +{ + s3c2410_dma_chan_t *chan = &s3c2410_chans[channel]; + unsigned long flags; + + check_channel(channel); + + local_irq_save(flags); + + + if (chan->client != client) { + printk(KERN_WARNING "dma%d: possible free from different client (channel %p, passed %p)\n", + channel, chan->client, client); + } + + /* sort out stopping and freeing the channel */ + + if (chan->state != S3C2410_DMA_IDLE) { + pr_debug("%s: need to stop dma channel %p\n", + __FUNCTION__, chan); + + /* possibly flush the channel */ + s3c2410_dma_ctrl(channel, S3C2410_DMAOP_STOP); + } + + chan->client = NULL; + chan->in_use = 0; + + local_irq_restore(flags); + + return 0; +} + +static int s3c2410_dma_dostop(s3c2410_dma_chan_t *chan) +{ + unsigned long tmp; + unsigned long flags; + + pr_debug("%s:\n", __FUNCTION__); + + dbg_showchan(chan); + + local_irq_save(flags); + + s3c2410_dma_call_op(chan, S3C2410_DMAOP_STOP); + + tmp = dma_rdreg(chan, S3C2410_DMA_DMASKTRIG); + tmp |= S3C2410_DMASKTRIG_STOP; + dma_wrreg(chan, S3C2410_DMA_DMASKTRIG, tmp); + +#if 0 + /* should also clear interrupts, according to WinCE BSP */ + tmp = dma_rdreg(chan, S3C2410_DMA_DCON); + tmp |= S3C2410_DCON_NORELOAD; + dma_wrreg(chan, S3C2410_DMA_DCON, tmp); +#endif + + chan->state = S3C2410_DMA_IDLE; + chan->load_state = S3C2410_DMALOAD_NONE; + + local_irq_restore(flags); + + return 0; +} + +/* s3c2410_dma_flush + * + * stop the channel, and remove all current and pending transfers +*/ + +static int s3c2410_dma_flush(s3c2410_dma_chan_t *chan) +{ + s3c2410_dma_buf_t *buf, *next; + unsigned long flags; + + pr_debug("%s:\n", __FUNCTION__); + + local_irq_save(flags); + + if (chan->state != S3C2410_DMA_IDLE) { + pr_debug("%s: stopping channel...\n", __FUNCTION__ ); + s3c2410_dma_ctrl(chan->number, S3C2410_DMAOP_STOP); + } + + buf = chan->curr; + if (buf == NULL) + buf = chan->next; + + chan->curr = chan->next = chan->end = NULL; + + if (buf != NULL) { + for ( ; buf != NULL; buf = next) { + next = buf->next; + + pr_debug("%s: free buffer %p, next %p\n", + __FUNCTION__, buf, buf->next); + + s3c2410_dma_buffdone(chan, buf, S3C2410_RES_ABORT); + s3c2410_dma_freebuf(buf); + } + } + + local_irq_restore(flags); + + return 0; +} + + +int +s3c2410_dma_ctrl(dmach_t channel, s3c2410_chan_op_t op) +{ + s3c2410_dma_chan_t *chan = &s3c2410_chans[channel]; + + check_channel(channel); + + switch (op) { + case S3C2410_DMAOP_START: + return s3c2410_dma_start(chan); + + case S3C2410_DMAOP_STOP: + return s3c2410_dma_dostop(chan); + + case S3C2410_DMAOP_PAUSE: + return -ENOENT; + + case S3C2410_DMAOP_RESUME: + return -ENOENT; + + case S3C2410_DMAOP_FLUSH: + return s3c2410_dma_flush(chan); + + case S3C2410_DMAOP_TIMEOUT: + return 0; + + } + + return -ENOENT; /* unknown, don't bother */ +} + + +/* DMA configuration for each channel + * + * DISRCC -> source of the DMA (AHB,APB) + * DISRC -> source address of the DMA + * DIDSTC -> destination of the DMA (AHB,APD) + * DIDST -> destination address of the DMA +*/ + +/* s3c2410_dma_config + * + * xfersize: size of unit in bytes (1,2,4) + * dcon: base value of the DCONx register +*/ + +int s3c2410_dma_config(dmach_t channel, + int xferunit, + int dcon) +{ + s3c2410_dma_chan_t *chan = &s3c2410_chans[channel]; + + pr_debug("%s: chan=%d, xfer_unit=%d, dcon=%08x\n", + __FUNCTION__, channel, xferunit, dcon); + + check_channel(channel); + + switch (xferunit) { + case 1: + dcon |= S3C2410_DCON_BYTE; + break; + + case 2: + dcon |= S3C2410_DCON_HALFWORD; + break; + + case 4: + dcon |= S3C2410_DCON_WORD; + break; + + default: + pr_debug("%s: bad transfer size %d\n", __FUNCTION__, xferunit); + return -EINVAL; + } + + dcon |= S3C2410_DCON_HWTRIG; + dcon |= S3C2410_DCON_INTREQ; + + pr_debug("%s: dcon now %08x\n", __FUNCTION__, dcon); + + chan->dcon = dcon; + chan->xfer_unit = xferunit; + + return 0; +} + + +int s3c2410_dma_setflags(dmach_t channel, unsigned int flags) +{ + s3c2410_dma_chan_t *chan = &s3c2410_chans[channel]; + + check_channel(channel); + + pr_debug("%s: chan=%p, flags=%08x\n", __FUNCTION__, chan, flags); + + chan->flags = flags; + + return 0; +} + +/* do we need to protect the settings of the fields from + * irq? +*/ + +int s3c2410_dma_set_opfn(dmach_t channel, s3c2410_dma_opfn_t rtn) +{ + s3c2410_dma_chan_t *chan = &s3c2410_chans[channel]; + + check_channel(channel); + + pr_debug("%s: chan=%p, op rtn=%p\n", __FUNCTION__, chan, rtn); + + chan->op_fn = rtn; + + return 0; +} + +int s3c2410_dma_set_buffdone_fn(dmach_t channel, s3c2410_dma_cbfn_t rtn) +{ + s3c2410_dma_chan_t *chan = &s3c2410_chans[channel]; + + check_channel(channel); + + pr_debug("%s: chan=%p, callback rtn=%p\n", __FUNCTION__, chan, rtn); + + chan->callback_fn = rtn; + + return 0; +} + +/* s3c2410_dma_devconfig + * + * configure the dma source/destination hardware type and address + * + * source: S3C2410_DMASRC_HW: source is hardware + * S3C2410_DMASRC_MEM: source is memory + * + * hwcfg: the value for xxxSTCn register, + * bit 0: 0=increment pointer, 1=leave pointer + * bit 1: 0=soucre is AHB, 1=soucre is APB + * + * devaddr: physical address of the source +*/ + +int s3c2410_dma_devconfig(int channel, + s3c2410_dmasrc_t source, + int hwcfg, + unsigned long devaddr) +{ + s3c2410_dma_chan_t *chan = &s3c2410_chans[channel]; + + check_channel(channel); + + pr_debug("%s: source=%d, hwcfg=%08x, devaddr=%08lx\n", + __FUNCTION__, (int)source, hwcfg, devaddr); + + chan->source = source; + chan->dev_addr = devaddr; + + switch (source) { + case S3C2410_DMASRC_HW: + /* source is hardware */ + pr_debug("%s: hw source, devaddr=%08lx, hwcfg=%d\n", + __FUNCTION__, devaddr, hwcfg); + dma_wrreg(chan, S3C2410_DMA_DISRCC, hwcfg & 3); + dma_wrreg(chan, S3C2410_DMA_DISRC, devaddr); + dma_wrreg(chan, S3C2410_DMA_DIDSTC, (0<<1) | (0<<0)); + + chan->addr_reg = dma_regaddr(chan, S3C2410_DMA_DIDST); + return 0; + + case S3C2410_DMASRC_MEM: + /* source is memory */ + pr_debug( "%s: mem source, devaddr=%08lx, hwcfg=%d\n", + __FUNCTION__, devaddr, hwcfg); + dma_wrreg(chan, S3C2410_DMA_DISRCC, (0<<1) | (0<<0)); + dma_wrreg(chan, S3C2410_DMA_DIDST, devaddr); + dma_wrreg(chan, S3C2410_DMA_DIDSTC, hwcfg & 3); + + chan->addr_reg = dma_regaddr(chan, S3C2410_DMA_DISRC); + return 0; + } + + printk(KERN_ERR "dma%d: invalid source type (%d)\n", channel, source); + return -EINVAL; +} + +/* initialisation code */ + +static int __init s3c2410_init_dma(void) +{ + int channel; + s3c2410_dma_chan_t *cp; + + printk("S3C2410 DMA Driver, (c) 2003-2004 Simtec Electronics\n"); + + dma_base = ioremap(S3C2410_PA_DMA, 0x200); + if (dma_base == NULL) { + printk(KERN_ERR "dma failed to remap register block\n"); + return -ENOMEM; + } + + for (channel = 0; channel < S3C2410_DMA_CHANNELS; channel++) { + cp = &s3c2410_chans[channel]; + + memset(cp, 0, sizeof(s3c2410_dma_chan_t)); + + /* dma channel irqs are in order.. */ + cp->number = channel; + cp->irq = channel + IRQ_DMA0; + cp->regs = (unsigned long)dma_base + (channel*0x40); + + /* point current stats somewhere */ + cp->stats = &cp->stats_store; + cp->stats_store.timeout_shortest = LONG_MAX; + + /* basic channel configuration */ + + cp->load_timeout = 1<<18; + + printk("DMA channel %d at %08lx, irq %d\n", + cp->number, cp->regs, cp->irq); + } + + return 0; +} + +__initcall(s3c2410_init_dma); diff --git a/arch/arm/mach-s3c2410/gpio.c b/arch/arm/mach-s3c2410/gpio.c index 450b132af..8996725e9 100644 --- a/arch/arm/mach-s3c2410/gpio.c +++ b/arch/arm/mach-s3c2410/gpio.c @@ -19,6 +19,15 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * + * Changelog + * 13-Sep-2004 BJD Implemented change of MISCCR + * 14-Sep-2004 BJD Added getpin call + * 14-Sep-2004 BJD Fixed bug in setpin() call + * 30-Sep-2004 BJD Fixed cfgpin() mask bug + * 01-Oct-2004 BJD Added getcfg() to get pin configuration + * 01-Oct-2004 BJD Fixed mask bug in pullup() call + * 01-Oct-2004 BJD Added getirq() to turn pin into irqno + * 04-Oct-2004 BJD Added irq filter controls for GPIO */ @@ -36,23 +45,20 @@ void s3c2410_gpio_cfgpin(unsigned int pin, unsigned int function) { unsigned long base = S3C2410_GPIO_BASE(pin); - unsigned long shift = 1; - unsigned long mask = 3; + unsigned long mask; unsigned long con; unsigned long flags; if (pin < S3C2410_GPIO_BANKB) { - shift = 0; - mask = 1; + mask = 1 << S3C2410_GPIO_OFFSET(pin); + } else { + mask = 3 << S3C2410_GPIO_OFFSET(pin)*2; } - mask <<= S3C2410_GPIO_OFFSET(pin); - local_irq_save(flags); - con = __raw_readl(base + 0x00); - - con &= mask << shift; + con = __raw_readl(base + 0x00); + con &= ~mask; con |= function; __raw_writel(con, base + 0x00); @@ -60,6 +66,20 @@ void s3c2410_gpio_cfgpin(unsigned int pin, unsigned int function) local_irq_restore(flags); } +unsigned int s3c2410_gpio_getcfg(unsigned int pin) +{ + unsigned long base = S3C2410_GPIO_BASE(pin); + unsigned long mask; + + if (pin < S3C2410_GPIO_BANKB) { + mask = 1 << S3C2410_GPIO_OFFSET(pin); + } else { + mask = 3 << S3C2410_GPIO_OFFSET(pin)*2; + } + + return __raw_readl(base) & mask; +} + void s3c2410_gpio_pullup(unsigned int pin, unsigned int to) { unsigned long base = S3C2410_GPIO_BASE(pin); @@ -73,7 +93,7 @@ void s3c2410_gpio_pullup(unsigned int pin, unsigned int to) local_irq_save(flags); up = __raw_readl(base + 0x08); - up &= 1 << offs; + up &= ~(1L << offs); up |= to << offs; __raw_writel(up, base + 0x08); @@ -90,9 +110,85 @@ void s3c2410_gpio_setpin(unsigned int pin, unsigned int to) local_irq_save(flags); dat = __raw_readl(base + 0x04); - dat &= 1 << offs; + dat &= ~(1 << offs); dat |= to << offs; __raw_writel(dat, base + 0x04); local_irq_restore(flags); } + +unsigned int s3c2410_gpio_getpin(unsigned int pin) +{ + unsigned long base = S3C2410_GPIO_BASE(pin); + unsigned long offs = S3C2410_GPIO_OFFSET(pin); + + return __raw_readl(base + 0x04) & (1<< offs); +} + +unsigned int s3c2410_modify_misccr(unsigned int clear, unsigned int change) +{ + unsigned long flags; + unsigned long misccr; + + local_irq_save(flags); + misccr = __raw_readl(S3C2410_MISCCR); + misccr &= ~clear; + misccr ^= change; + __raw_writel(misccr, S3C2410_MISCCR); + local_irq_restore(flags); + + return misccr; +} + +int s3c2410_gpio_getirq(unsigned int pin) +{ + if (pin < S3C2410_GPF0 || pin > S3C2410_GPG15_EINT23) + return -1; /* not valid interrupts */ + + if (pin < S3C2410_GPG0 && pin > S3C2410_GPF7) + return -1; /* not valid pin */ + + if (pin < S3C2410_GPF4) + return (pin - S3C2410_GPF0) + IRQ_EINT0; + + if (pin < S3C2410_GPG0) + return (pin - S3C2410_GPF4) + IRQ_EINT4; + + return (pin - S3C2410_GPG0) + IRQ_EINT8; +} + +int s3c2410_gpio_irqfilter(unsigned int pin, unsigned int on, + unsigned int config) +{ + unsigned long reg = S3C2410_EINFLT0; + unsigned long flags; + unsigned long val; + + if (pin < S3C2410_GPG8 || pin > S3C2410_GPG15) + return -1; + + config &= 0xff; + + pin -= S3C2410_GPG8_EINT16; + reg += pin & ~3; + + local_irq_save(flags); + + /* update filter width and clock source */ + + val = __raw_readl(reg); + val &= ~(0xff << ((pin & 3) * 8)); + val |= config << ((pin & 3) * 8); + __raw_writel(val, reg); + + /* update filter enable */ + + val = __raw_readl(S3C2410_EXTINT2); + val &= ~(1 << ((pin * 4) + 3)); + val |= on << ((pin * 4) + 3); + __raw_writel(val, S3C2410_EXTINT2); + + local_irq_restore(flags); + + return 0; +} diff --git a/arch/arm/mach-s3c2410/irq.c b/arch/arm/mach-s3c2410/irq.c index 878b9740d..1951a0323 100644 --- a/arch/arm/mach-s3c2410/irq.c +++ b/arch/arm/mach-s3c2410/irq.c @@ -1,7 +1,7 @@ /* linux/arch/arm/mach-s3c2410/irq.c * - * Copyright (c) 2003 Simtec Electronics - * Ben Dooks + * Copyright (c) 2003,2004 Simtec Electronics + * Ben Dooks * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -17,7 +17,23 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * - */ + * Changelog: + * + * 22-Jul-2004 Ben Dooks + * Fixed compile warnings + * + * 22-Jul-2004 Roc Wu + * Fixed s3c_extirq_type + * + * 21-Jul-2004 Arnaud Patard (Rtp) + * Addition of ADC/TC demux + * + * 04-Oct-2004 Klaus Fetscher + * Fix for set_irq_type() on low EINT numbers + * + * 05-Oct-2004 Ben Dooks + * Tidy up KF's patch and sort out new release +*/ #include @@ -34,11 +50,8 @@ #include #include -#include +#include -#if 0 -#include -#endif #define irqdbf(x...) #define irqdbf2(x...) @@ -177,12 +190,84 @@ s3c_irqext_unmask(unsigned int irqno) s3c_irq_unmask((irqno <= (IRQ_EINT7 - EXTINT_OFF)) ? IRQ_EINT4t7 : IRQ_EINT8t23); } -/* todo - put type handler in here */ - static int s3c_irqext_type(unsigned int irq, unsigned int type) { - irqdbf("s3c_irqext_type: called for irq %d, type %d\n", irq, type); + unsigned long extint_reg; + unsigned long gpcon_reg; + unsigned long gpcon_offset, extint_offset; + unsigned long newvalue = 0, value; + + if ((irq >= IRQ_EINT0) && (irq <= IRQ_EINT3)) + { + gpcon_reg = S3C2410_GPFCON; + extint_reg = S3C2410_EXTINT0; + gpcon_offset = (irq - IRQ_EINT0) * 2; + extint_offset = (irq - IRQ_EINT0) * 4; + } + else if ((irq >= IRQ_EINT4) && (irq <= IRQ_EINT7)) + { + gpcon_reg = S3C2410_GPFCON; + extint_reg = S3C2410_EXTINT0; + gpcon_offset = (irq - (EXTINT_OFF)) * 2; + extint_offset = (irq - (EXTINT_OFF)) * 4; + } + else if ((irq >= IRQ_EINT8) && (irq <= IRQ_EINT15)) + { + gpcon_reg = S3C2410_GPGCON; + extint_reg = S3C2410_EXTINT1; + gpcon_offset = (irq - IRQ_EINT8) * 2; + extint_offset = (irq - IRQ_EINT8) * 4; + } + else if ((irq >= IRQ_EINT16) && (irq <= IRQ_EINT23)) + { + gpcon_reg = S3C2410_GPGCON; + extint_reg = S3C2410_EXTINT2; + gpcon_offset = (irq - IRQ_EINT8) * 2; + extint_offset = (irq - IRQ_EINT16) * 4; + } else + return -1; + + /* Set the GPIO to external interrupt mode */ + value = __raw_readl(gpcon_reg); + value = (value & ~(3 << gpcon_offset)) | (0x02 << gpcon_offset); + __raw_writel(value, gpcon_reg); + + /* Set the external interrupt to pointed trigger type */ + switch (type) + { + case IRQT_NOEDGE: + printk(KERN_WARNING "No edge setting!\n"); + break; + + case IRQT_RISING: + newvalue = S3C2410_EXTINT_RISEEDGE; + break; + + case IRQT_FALLING: + newvalue = S3C2410_EXTINT_FALLEDGE; + break; + + case IRQT_BOTHEDGE: + newvalue = S3C2410_EXTINT_BOTHEDGE; + break; + + case IRQT_LOW: + newvalue = S3C2410_EXTINT_LOWLEV; + break; + + case IRQT_HIGH: + newvalue = S3C2410_EXTINT_HILEV; + break; + + default: + printk(KERN_ERR "No such irq type %d", type); + return -1; + } + + value = __raw_readl(extint_reg); + value = (value & ~(7 << extint_offset)) | (newvalue << extint_offset); + __raw_writel(value, extint_reg); return 0; } @@ -194,6 +279,13 @@ static struct irqchip s3c_irqext_chip = { .type = s3c_irqext_type }; +static struct irqchip s3c_irq_eint0t4 = { + .ack = s3c_irq_ack, + .mask = s3c_irq_mask, + .unmask = s3c_irq_unmask, + .type = s3c_irqext_type +}; + /* mask values for the parent registers for each of the interrupt types */ #define INTMSK_UART0 (1UL << (IRQ_UART0 - IRQ_EINT0)) @@ -368,37 +460,37 @@ static struct irqchip s3c_irq_adc = { .ack = s3c_irq_adc_ack, }; -#if 0 -/* LCD (todo) */ - -static void -s3c_irq_lcd_mask(unsigned int irqno) +/* irq demux for adc */ +static void s3c_irq_demux_adc(unsigned int irq, + struct irqdesc *desc, + struct pt_regs *regs) { + unsigned int subsrc, submsk; + unsigned int offset = 9; + struct irqdesc *mydesc; -} - -static void -s3c_irq_lcd_unmask(unsigned int irqno) -{ + /* read the current pending interrupts, and the mask + * for what it is available */ -} + subsrc = __raw_readl(S3C2410_SUBSRCPND); + submsk = __raw_readl(S3C2410_INTSUBMSK); -static void -s3c_irq_lcd_ack(unsigned int irqno) -{ + subsrc &= ~submsk; + subsrc >>= offset; + subsrc &= 3; + if (subsrc != 0) { + if (subsrc & 1) { + mydesc = irq_desc + IRQ_TC; + mydesc->handle( IRQ_TC, mydesc, regs); + } + if (subsrc & 2) { + mydesc = irq_desc + IRQ_ADC; + mydesc->handle(IRQ_ADC, mydesc, regs); + } + } } -static struct irqchip s3c_irq_lcd = { - .mask = s3c_irq_lcd_mask, - .unmask = s3c_irq_lcd_unmask, - .ack = s3c_irq_lcd_ack, -}; -#endif - -/* irq demux */ - - static void s3c_irq_demux_uart(unsigned int start, struct pt_regs *regs) { @@ -466,11 +558,15 @@ s3c_irq_demux_uart2(unsigned int irq, s3c_irq_demux_uart(IRQ_S3CUART_RX2, regs); } - +/* s3c2410_init_irq + * + * Initialise S3C2410 IRQ system +*/ void __init s3c2410_init_irq(void) { unsigned long pend; + unsigned long last; int irqno; int i; @@ -478,48 +574,51 @@ void __init s3c2410_init_irq(void) /* first, clear all interrupts pending... */ + last = 0; for (i = 0; i < 4; i++) { pend = __raw_readl(S3C2410_EINTPEND); - if (pend == 0) + + if (pend == 0 || pend == last) break; + __raw_writel(pend, S3C2410_EINTPEND); printk("irq: clearing pending ext status %08x\n", (int)pend); + last = pend; } + last = 0; for (i = 0; i < 4; i++) { pend = __raw_readl(S3C2410_INTPND); - if (pend == 0) + + if (pend == 0 || pend == last) break; + __raw_writel(pend, S3C2410_SRCPND); __raw_writel(pend, S3C2410_INTPND); printk("irq: clearing pending status %08x\n", (int)pend); + last = pend; } + last = 0; for (i = 0; i < 4; i++) { pend = __raw_readl(S3C2410_SUBSRCPND); - if (pend == 0) + if (pend == 0 || pend == last) break; printk("irq: clearing subpending status %08x\n", (int)pend); __raw_writel(pend, S3C2410_SUBSRCPND); + last = pend; } /* register the main interrupts */ irqdbf("s3c2410_init_irq: registering s3c2410 interrupt handlers\n"); - for (irqno = IRQ_EINT0; irqno < IRQ_ADCPARENT; irqno++) { + for (irqno = IRQ_BATT_FLT; irqno <= IRQ_ADCPARENT; irqno++) { /* set all the s3c2410 internal irqs */ switch (irqno) { - - case IRQ_EINT4t7: - case IRQ_EINT8t23: - /* these are already dealt with, so should never - * appear */ - break; - /* deal with the special IRQs (cascaded) */ case IRQ_UART0: @@ -549,12 +648,18 @@ void __init s3c2410_init_irq(void) set_irq_chained_handler(IRQ_UART0, s3c_irq_demux_uart0); set_irq_chained_handler(IRQ_UART1, s3c_irq_demux_uart1); set_irq_chained_handler(IRQ_UART2, s3c_irq_demux_uart2); - //set_irq_chained_handler(IRQ_LCD, s3c_irq_demux_); - //set_irq_chained_handler(IRQ_ADCPARENT, s3c_irq_demux_); + set_irq_chained_handler(IRQ_ADCPARENT, s3c_irq_demux_adc); /* external interrupts */ + for (irqno = IRQ_EINT0; irqno <= IRQ_EINT3; irqno++) { + irqdbf("registering irq %d (ext int)\n", irqno); + set_irq_chip(irqno, &s3c_irq_eint0t4); + set_irq_handler(irqno, do_edge_IRQ); + set_irq_flags(irqno, IRQF_VALID); + } + for (irqno = IRQ_EINT4; irqno <= IRQ_EINT23; irqno++) { irqdbf("registering irq %d (extended s3c irq)\n", irqno); set_irq_chip(irqno, &s3c_irqext_chip); diff --git a/arch/arm/mach-s3c2410/mach-smdk2410.c b/arch/arm/mach-s3c2410/mach-smdk2410.c index bfadbd425..9f4286569 100644 --- a/arch/arm/mach-s3c2410/mach-smdk2410.c +++ b/arch/arm/mach-s3c2410/mach-smdk2410.c @@ -47,7 +47,8 @@ #include #include "s3c2410.h" - +#include "devs.h" +#include "cpu.h" static struct map_desc smdk2410_iodesc[] __initdata = { /* nothing here yet */ @@ -87,11 +88,24 @@ static struct s3c2410_uartcfg smdk2410_uartcfgs[] = { } }; +static struct platform_device *smdk2410_devices[] __initdata = { + &s3c_device_usb, + &s3c_device_lcd, + &s3c_device_wdt, + &s3c_device_i2c, + &s3c_device_iis, +}; + +static struct s3c2410_board smdk2410_board __initdata = { + .devices = smdk2410_devices, + .devices_count = ARRAY_SIZE(smdk2410_devices) +}; void __init smdk2410_map_io(void) { - s3c2410_map_io(smdk2410_iodesc, ARRAY_SIZE(smdk2410_iodesc)); - s3c2410_uartcfgs = smdk2410_uartcfgs; + s3c24xx_init_io(smdk2410_iodesc, ARRAY_SIZE(smdk2410_iodesc)); + s3c2410_init_uarts(smdk2410_uartcfgs, ARRAY_SIZE(smdk2410_uartcfgs)); + s3c2410_set_board(&smdk2410_board); } void __init smdk2410_init_irq(void) @@ -113,3 +127,5 @@ MACHINE_START(SMDK2410, "SMDK2410") /* @TODO: request a new identifier and switc INITIRQ(smdk2410_init_irq) INITTIME(smdk2410_init_time) MACHINE_END + + diff --git a/arch/arm/mach-s3c2410/s3c2440-dsc.c b/arch/arm/mach-s3c2410/s3c2440-dsc.c new file mode 100644 index 000000000..fb3078379 --- /dev/null +++ b/arch/arm/mach-s3c2410/s3c2440-dsc.c @@ -0,0 +1,57 @@ +/* linux/arch/arm/mach-s3c2410/s3c2440-dsc.c + * + * Copyright (c) 2004 Simtec Electronics + * Ben Dooks + * + * Samsung S3C2440 Drive Strength Control 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. + * + * Modifications: + * 29-Aug-2004 BJD Start of drive-strength control +*/ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include + +#include +#include + +#include "s3c2440.h" +#include "cpu.h" + +int s3c2440_set_dsc(unsigned int pin, unsigned int value) +{ + unsigned long base; + unsigned long val; + unsigned long flags; + unsigned long mask; + + base = (pin & S3C2440_SELECT_DSC1) ? S3C2440_DSC1 : S3C2440_DSC0; + mask = 3 << S3C2440_DSC_GETSHIFT(pin); + + local_irq_save(flags); + + val = __raw_readl(base); + val &= ~mask; + val |= value & mask; + __raw_writel(val, base); + + local_irq_restore(flags); + return 0; +} diff --git a/arch/arm/mach-s3c2410/s3c2440.c b/arch/arm/mach-s3c2410/s3c2440.c new file mode 100644 index 000000000..f4bb10c58 --- /dev/null +++ b/arch/arm/mach-s3c2410/s3c2440.c @@ -0,0 +1,192 @@ +/* linux/arch/arm/mach-s3c2410/s3c2440.c + * + * Copyright (c) 2004 Simtec Electronics + * Ben Dooks + * + * Samsung S3C2440 Mobile 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. + * + * Modifications: + * 24-Aug-2004 BJD Start of s3c2440 support +*/ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include + +#include +#include + +#include "s3c2440.h" +#include "cpu.h" + +int s3c2440_clock_tick_rate = 12*1000*1000; /* current timers at 12MHz */ + +/* clock info */ + +unsigned long s3c2440_baseclk = 12*1000*1000; /* assume base is 12MHz */ +unsigned long s3c2440_hdiv; + +unsigned long s3c2440_fclk; +unsigned long s3c2440_hclk; +unsigned long s3c2440_pclk; + +static struct map_desc s3c2440_iodesc[] __initdata = { + IODESC_ENT(USBHOST), + IODESC_ENT(CLKPWR), + IODESC_ENT(LCD), + IODESC_ENT(TIMER), + IODESC_ENT(ADC), +}; + +static struct resource s3c_uart0_resource[] = { + [0] = { + .start = S3C2410_PA_UART0, + .end = S3C2410_PA_UART0 + 0x3fff, + .flags = IORESOURCE_MEM, + }, + [1] = { + .start = IRQ_S3CUART_RX0, + .end = IRQ_S3CUART_ERR0, + .flags = IORESOURCE_IRQ, + } + +}; + +static struct resource s3c_uart1_resource[] = { + [0] = { + .start = S3C2410_PA_UART1, + .end = S3C2410_PA_UART1 + 0x3fff, + .flags = IORESOURCE_MEM, + }, + [1] = { + .start = IRQ_S3CUART_RX1, + .end = IRQ_S3CUART_ERR1, + .flags = IORESOURCE_IRQ, + } +}; + +static struct resource s3c_uart2_resource[] = { + [0] = { + .start = S3C2410_PA_UART2, + .end = S3C2410_PA_UART2 + 0x3fff, + .flags = IORESOURCE_MEM, + }, + [1] = { + .start = IRQ_S3CUART_RX2, + .end = IRQ_S3CUART_ERR2, + .flags = IORESOURCE_IRQ, + } +}; + +/* our uart devices */ + +static struct platform_device s3c_uart0 = { + .name = "s3c2440-uart", + .id = 0, + .num_resources = ARRAY_SIZE(s3c_uart0_resource), + .resource = s3c_uart0_resource, +}; + + +static struct platform_device s3c_uart1 = { + .name = "s3c2440-uart", + .id = 1, + .num_resources = ARRAY_SIZE(s3c_uart1_resource), + .resource = s3c_uart1_resource, +}; + +static struct platform_device s3c_uart2 = { + .name = "s3c2440-uart", + .id = 2, + .num_resources = ARRAY_SIZE(s3c_uart2_resource), + .resource = s3c_uart2_resource, +}; + +static struct platform_device *uart_devices[] __initdata = { + &s3c_uart0, + &s3c_uart1, + &s3c_uart2 +}; + +void __init s3c2440_map_io(struct map_desc *mach_desc, int size) +{ + unsigned long tmp; + unsigned long camdiv; + + /* register our io-tables */ + + iotable_init(s3c2440_iodesc, ARRAY_SIZE(s3c2440_iodesc)); + iotable_init(mach_desc, size); + + /* now we've got our machine bits initialised, work out what + * clocks we've got */ + + s3c2440_fclk = s3c2410_get_pll(__raw_readl(S3C2410_MPLLCON), + s3c2440_baseclk); + + tmp = __raw_readl(S3C2410_CLKDIVN); + camdiv = __raw_readl(S3C2440_CAMDIVN); + + /* work out clock scalings */ + + switch (tmp & S3C2440_CLKDIVN_HDIVN_MASK) { + case S3C2440_CLKDIVN_HDIVN_1: + s3c2440_hdiv = 1; + break; + + case S3C2440_CLKDIVN_HDIVN_2: + s3c2440_hdiv = 1; + break; + + case S3C2440_CLKDIVN_HDIVN_4_8: + s3c2440_hdiv = (camdiv & S3C2440_CAMDIVN_HCLK4_HALF) ? 8 : 4; + break; + + case S3C2440_CLKDIVN_HDIVN_3_6: + s3c2440_hdiv = (camdiv & S3C2440_CAMDIVN_HCLK4_HALF) ? 6 : 3; + break; + } + + s3c2440_hclk = s3c2440_fclk / s3c2440_hdiv; + s3c2440_pclk = s3c2440_hclk / ((tmp & S3C2440_CLKDIVN_PDIVN) ? 2 : 1); + + /* print brieft summary of clocks, etc */ + + printk("S3C2440: core %ld.%03ld MHz, memory %ld.%03ld MHz, peripheral %ld.%03ld MHz\n", + print_mhz(s3c2440_fclk), print_mhz(s3c2440_hclk), + print_mhz(s3c2440_pclk)); +} + + + +int __init s3c2440_init(void) +{ + int ret; + + printk("S3C2440: Initialising architecture\n"); + + ret = platform_add_devices(uart_devices, ARRAY_SIZE(uart_devices)); + if (ret) + return ret; + + // todo: board specific inits? + + return ret; +} + diff --git a/arch/arm/mach-s3c2410/s3c2440.h b/arch/arm/mach-s3c2410/s3c2440.h new file mode 100644 index 000000000..bf49a66d5 --- /dev/null +++ b/arch/arm/mach-s3c2410/s3c2440.h @@ -0,0 +1,18 @@ +/* arch/arm/mach-s3c2410/s3c2440.h + * + * Copyright (c) 2004 Simtec Electronics + * Ben Dooks + * + * Header file for s3c2440 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. + * + * Modifications: + * 24-Aug-2004 BJD Start of S3C2440 CPU support +*/ + +extern void s3c2440_init_irq(void); + +extern void s3c2440_init_time(void); diff --git a/arch/arm/mach-s3c2410/time.c b/arch/arm/mach-s3c2410/time.c index 62d89d98c..04ba5dfe0 100644 --- a/arch/arm/mach-s3c2410/time.c +++ b/arch/arm/mach-s3c2410/time.c @@ -31,14 +31,12 @@ #include #include #include +#include #include static unsigned long timer_startval; static unsigned long timer_ticks_usec; -#ifdef CONFIG_S3C2410_RTC -extern void s3c2410_rtc_check(); -#endif /* with an 12MHz clock, we get 12 ticks per-usec */ @@ -49,15 +47,30 @@ extern void s3c2410_rtc_check(); * 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 usec; + unsigned long irqpend; /* work out how many ticks have gone since last timer interrupt */ tdone = timer_startval - __raw_readl(S3C2410_TCNTO(4)); + /* 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 */ + + tdone = timer_startval - __raw_readl(S3C2410_TCNTO(4)); + tdone += 1<<16; + } + /* currently, tcnt is in 12MHz units, but this may change * for non-bast machines... */ diff --git a/arch/arm/mach-s3c2410/usb-simtec.c b/arch/arm/mach-s3c2410/usb-simtec.c new file mode 100644 index 000000000..b8f8ded4b --- /dev/null +++ b/arch/arm/mach-s3c2410/usb-simtec.c @@ -0,0 +1,123 @@ +/* linux/arch/arm/mach-s3c2410/usb-simtec.c + * + * Copyright (c) 2004 Simtec Electronics + * Ben Dooks + * + * http://www.simtec.co.uk/products/EB2410ITX/ + * + * Simtec BAST and Thorcom VR1000 USB port support 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. + * + * Modifications: + * 14-Sep-2004 BJD Created +*/ + +#define DEBUG + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "devs.h" +#include "usb-simtec.h" + +/* control power and monitor over-current events on various Simtec + * designed boards. +*/ + +static void +usb_simtec_powercontrol(int port, int to) +{ + pr_debug("usb_simtec_powercontrol(%d,%d)\n", port, to); + + if (port == 1) { + s3c2410_gpio_setpin(S3C2410_GPB4, to ? 0:1); + pr_debug("GPBDAT now %08x\n", __raw_readl(S3C2410_GPBDAT)); + } +} + +static irqreturn_t +usb_simtec_ocirq(int irq, void *pw, struct pt_regs *regs) +{ + struct s3c2410_hcd_info *info = (struct s3c2410_hcd_info *)pw; + + if (s3c2410_gpio_getpin(S3C2410_GPG10) == 0) { + pr_debug("usb_simtec: over-current irq (oc detected)\n"); + s3c2410_report_oc(info, 3); + } else { + pr_debug("usb_simtec: over-current irq (oc cleared)\n"); + } + + return IRQ_HANDLED; +} + +static void usb_simtec_enableoc(struct s3c2410_hcd_info *info, int on) +{ + int ret; + + if (on) { + pr_debug("claiming usb overccurent\n"); + ret = request_irq(IRQ_USBOC, usb_simtec_ocirq, SA_INTERRUPT, + "usb-oc", info); + if (ret != 0) { + printk(KERN_ERR "failed to request usb oc irq\n"); + } + + set_irq_type(IRQ_USBOC, IRQT_BOTHEDGE); + } else { + free_irq(IRQ_USBOC, NULL); + } +} + +static struct s3c2410_hcd_info usb_simtec_info = { + .port[0] = { + .flags = S3C_HCDFLG_USED + }, + .port[1] = { + .flags = S3C_HCDFLG_USED + }, + + .power_control = usb_simtec_powercontrol, + .enable_oc = usb_simtec_enableoc, +}; + + +int usb_simtec_init(void) +{ + printk("USB Power Control, (c) 2004 Simtec Electronics\n"); + s3c_device_usb.dev.platform_data = &usb_simtec_info; + + s3c2410_gpio_cfgpin(S3C2410_GPB4, S3C2410_GPB4_OUTP); + s3c2410_gpio_setpin(S3C2410_GPB4, 1); + + pr_debug("GPB: CON=%08x, DAT=%08x\n", + __raw_readl(S3C2410_GPBCON), __raw_readl(S3C2410_GPBDAT)); + + if (0) { + s3c2410_modify_misccr(S3C2410_MISCCR_USBHOST, + S3C2410_MISCCR_USBDEV); + } + + return 0; +} diff --git a/arch/arm/mach-s3c2410/usb-simtec.h b/arch/arm/mach-s3c2410/usb-simtec.h new file mode 100644 index 000000000..92c0cc83a --- /dev/null +++ b/arch/arm/mach-s3c2410/usb-simtec.h @@ -0,0 +1,19 @@ +/* linux/arch/arm/mach-s3c2410/usb-simtec.c + * + * Copyright (c) 2004 Simtec Electronics + * Ben Dooks + * + * http://www.simtec.co.uk/products/EB2410ITX/ + * + * Simtec BAST and Thorcom VR1000 USB port support 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. + * + * Modifications: + * 20-Aug-2004 BJD Created +*/ + +extern int usb_simtec_init(void); + diff --git a/arch/arm/mach-sa1100/pm.c b/arch/arm/mach-sa1100/pm.c index 2532b7f80..7120ec5aa 100644 --- a/arch/arm/mach-sa1100/pm.c +++ b/arch/arm/mach-sa1100/pm.c @@ -30,6 +30,7 @@ #include #include #include +#include extern void sa1100_cpu_suspend(void); extern void sa1100_cpu_resume(void); @@ -44,7 +45,7 @@ extern void sa1100_cpu_resume(void); */ enum { SLEEP_SAVE_SP = 0, - SLEEP_SAVE_OSCR, SLEEP_SAVE_OIER, + SLEEP_SAVE_OIER, SLEEP_SAVE_OSMR0, SLEEP_SAVE_OSMR1, SLEEP_SAVE_OSMR2, SLEEP_SAVE_OSMR3, SLEEP_SAVE_GPDR, SLEEP_SAVE_GAFR, @@ -58,18 +59,19 @@ enum { SLEEP_SAVE_SP = 0, static int sa11x0_pm_enter(u32 state) { - unsigned long sleep_save[SLEEP_SAVE_SIZE]; - unsigned long delta, gpio; + unsigned long gpio, sleep_save[SLEEP_SAVE_SIZE]; + struct timespec delta, rtc; if (state != PM_SUSPEND_MEM) return -EINVAL; /* preserve current time */ - delta = xtime.tv_sec - RCNR; + rtc.tv_sec = RCNR; + rtc.tv_nsec = 0; + save_time_delta(&delta, &rtc); gpio = GPLR; /* save vital registers */ - SAVE(OSCR); SAVE(OSMR0); SAVE(OSMR1); SAVE(OSMR2); @@ -131,11 +133,14 @@ static int sa11x0_pm_enter(u32 state) RESTORE(OSMR1); RESTORE(OSMR2); RESTORE(OSMR3); - RESTORE(OSCR); RESTORE(OIER); + /* OSMR0 is the system timer: make sure OSCR is sufficiently behind */ + OSCR = OSMR0 - LATCH; + /* restore current time */ - xtime.tv_sec = RCNR + delta; + rtc.tv_sec = RCNR; + restore_time_delta(&delta, &rtc); return 0; } diff --git a/arch/arm/mach-versatile/clock.c b/arch/arm/mach-versatile/clock.c index a13253692..24f12bdfb 100644 --- a/arch/arm/mach-versatile/clock.c +++ b/arch/arm/mach-versatile/clock.c @@ -87,8 +87,8 @@ int clk_set_rate(struct clk *clk, unsigned long rate) if (clk->setvco) { struct icst525_vco vco; - vco = icst525_khz_to_vco(clk->params, rate); - clk->rate = icst525_khz(clk->params, 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); diff --git a/arch/arm/mm/Makefile b/arch/arm/mm/Makefile index fc764e17e..ccf316c11 100644 --- a/arch/arm/mm/Makefile +++ b/arch/arm/mm/Makefile @@ -3,7 +3,8 @@ # obj-y := consistent.o extable.o fault-armv.o \ - fault.o init.o ioremap.o mmap.o mm-armv.o + fault.o flush.o init.o ioremap.o mmap.o \ + mm-armv.o obj-$(CONFIG_MODULES) += proc-syms.o diff --git a/arch/arm/mm/abort-ev5tj.S b/arch/arm/mm/abort-ev5tj.S index 922e05a4d..fd0584a92 100644 --- a/arch/arm/mm/abort-ev5tj.S +++ b/arch/arm/mm/abort-ev5tj.S @@ -23,7 +23,6 @@ ENTRY(v5tj_early_abort) mrc p15, 0, r0, c6, c0, 0 @ get FAR bic r1, r1, #1 << 11 | 1 << 10 @ clear bits 11 and 10 of FSR tst r3, #PSR_J_BIT @ Java? - orrne r1, r1, #1 << 11 @ always assume write movne pc, lr tst r3, #PSR_T_BIT @ Thumb? ldrneh r3, [r2] @ read aborted thumb instruction diff --git a/arch/arm/mm/cache-v3.S b/arch/arm/mm/cache-v3.S index 6659439f6..e1994788c 100644 --- a/arch/arm/mm/cache-v3.S +++ b/arch/arm/mm/cache-v3.S @@ -57,6 +57,19 @@ ENTRY(v3_flush_user_cache_range) * - end - virtual end address */ ENTRY(v3_coherent_kern_range) + /* FALLTHROUGH */ + +/* + * coherent_user_range(start, end) + * + * Ensure coherency between the Icache and the Dcache in the + * region described by start. If you have non-snooping + * Harvard caches, you need to implement this function. + * + * - start - virtual start address + * - end - virtual end address + */ +ENTRY(v3_coherent_user_range) mov pc, lr /* @@ -116,6 +129,7 @@ ENTRY(v3_cache_fns) .long v3_flush_user_cache_all .long v3_flush_user_cache_range .long v3_coherent_kern_range + .long v3_coherent_user_range .long v3_flush_kern_dcache_page .long v3_dma_inv_range .long v3_dma_clean_range diff --git a/arch/arm/mm/cache-v4.S b/arch/arm/mm/cache-v4.S index bbc822f16..b8ad5d58e 100644 --- a/arch/arm/mm/cache-v4.S +++ b/arch/arm/mm/cache-v4.S @@ -59,6 +59,19 @@ ENTRY(v4_flush_user_cache_range) * - end - virtual end address */ ENTRY(v4_coherent_kern_range) + /* FALLTHROUGH */ + +/* + * coherent_user_range(start, end) + * + * Ensure coherency between the Icache and the Dcache in the + * region described by start. If you have non-snooping + * Harvard caches, you need to implement this function. + * + * - start - virtual start address + * - end - virtual end address + */ +ENTRY(v4_coherent_user_range) mov pc, lr /* @@ -118,6 +131,7 @@ ENTRY(v4_cache_fns) .long v4_flush_user_cache_all .long v4_flush_user_cache_range .long v4_coherent_kern_range + .long v4_coherent_user_range .long v4_flush_kern_dcache_page .long v4_dma_inv_range .long v4_dma_clean_range diff --git a/arch/arm/mm/cache-v4wb.S b/arch/arm/mm/cache-v4wb.S index 4f7c918e6..5c4055b62 100644 --- a/arch/arm/mm/cache-v4wb.S +++ b/arch/arm/mm/cache-v4wb.S @@ -121,6 +121,19 @@ ENTRY(v4wb_flush_kern_dcache_page) * - end - virtual end address */ ENTRY(v4wb_coherent_kern_range) + /* fall through */ + +/* + * coherent_user_range(start, end) + * + * Ensure coherency between the Icache and the Dcache in the + * region described by start. If you have non-snooping + * Harvard caches, you need to implement this function. + * + * - start - virtual start address + * - end - virtual end address + */ +ENTRY(v4wb_coherent_user_range) bic r0, r0, #CACHE_DLINESIZE - 1 1: mcr p15, 0, r0, c7, c10, 1 @ clean D entry mcr p15, 0, r0, c7, c6, 1 @ invalidate D entry @@ -195,6 +208,7 @@ ENTRY(v4wb_cache_fns) .long v4wb_flush_user_cache_all .long v4wb_flush_user_cache_range .long v4wb_coherent_kern_range + .long v4wb_coherent_user_range .long v4wb_flush_kern_dcache_page .long v4wb_dma_inv_range .long v4wb_dma_clean_range diff --git a/arch/arm/mm/cache-v4wt.S b/arch/arm/mm/cache-v4wt.S index 61c9fc60f..9bcabd86c 100644 --- a/arch/arm/mm/cache-v4wt.S +++ b/arch/arm/mm/cache-v4wt.S @@ -97,6 +97,19 @@ ENTRY(v4wt_flush_user_cache_range) * - end - virtual end address */ ENTRY(v4wt_coherent_kern_range) + /* FALLTRHOUGH */ + +/* + * coherent_user_range(start, end) + * + * Ensure coherency between the Icache and the Dcache in the + * region described by start. If you have non-snooping + * Harvard caches, you need to implement this function. + * + * - start - virtual start address + * - end - virtual end address + */ +ENTRY(v4wt_coherent_user_range) bic r0, r0, #CACHE_DLINESIZE - 1 1: mcr p15, 0, r0, c7, c5, 1 @ invalidate I entry add r0, r0, #CACHE_DLINESIZE @@ -167,6 +180,7 @@ ENTRY(v4wt_cache_fns) .long v4wt_flush_user_cache_all .long v4wt_flush_user_cache_range .long v4wt_coherent_kern_range + .long v4wt_coherent_user_range .long v4wt_flush_kern_dcache_page .long v4wt_dma_inv_range .long v4wt_dma_clean_range diff --git a/arch/arm/mm/copypage-v6.c b/arch/arm/mm/copypage-v6.c index e6a139ee2..402dad21a 100644 --- a/arch/arm/mm/copypage-v6.c +++ b/arch/arm/mm/copypage-v6.c @@ -31,14 +31,46 @@ static spinlock_t v6_lock = SPIN_LOCK_UNLOCKED; #define DCACHE_COLOUR(vaddr) ((vaddr & (SHMLBA - 1)) >> PAGE_SHIFT) +/* + * Copy the user page. No aliasing to deal with so we can just + * attack the kernel's existing mapping of these pages. + */ +void v6_copy_user_page_nonaliasing(void *kto, const void *kfrom, unsigned long vaddr) +{ + copy_page(kto, kfrom); +} + +/* + * Clear the user page. No aliasing to deal with so we can just + * attack the kernel's existing mapping of this page. + */ +void v6_clear_user_page_nonaliasing(void *kaddr, unsigned long vaddr) +{ + clear_page(kaddr); +} + /* * Copy the page, taking account of the cache colour. */ -void v6_copy_user_page(void *kto, const void *kfrom, unsigned long vaddr) +void v6_copy_user_page_aliasing(void *kto, const void *kfrom, unsigned long vaddr) { unsigned int offset = DCACHE_COLOUR(vaddr); unsigned long from, to; + /* + * 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"); + + /* + * Now copy the page using the same cache colour as the + * pages ultimate destination. + */ spin_lock(&v6_lock); set_pte(from_pte + offset, pfn_pte(__pa(kfrom) >> PAGE_SHIFT, from_pgprot)); @@ -55,11 +87,30 @@ void v6_copy_user_page(void *kto, const void *kfrom, unsigned long vaddr) spin_unlock(&v6_lock); } -void v6_clear_user_page(void *kaddr, unsigned long vaddr) +/* + * Clear the user page. We need to deal with the aliasing issues, + * so remap the kernel page into the same cache colour as the user + * page. + */ +void v6_clear_user_page_aliasing(void *kaddr, unsigned long vaddr) { unsigned int offset = DCACHE_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"); + + /* + * Now clear the page using the same cache colour as + * the pages ultimate destination. + */ spin_lock(&v6_lock); set_pte(to_pte + offset, pfn_pte(__pa(kaddr) >> PAGE_SHIFT, to_pgprot)); @@ -70,26 +121,31 @@ void v6_clear_user_page(void *kaddr, unsigned long vaddr) } struct cpu_user_fns v6_user_fns __initdata = { - .cpu_clear_user_page = v6_clear_user_page, - .cpu_copy_user_page = v6_copy_user_page, + .cpu_clear_user_page = v6_clear_user_page_nonaliasing, + .cpu_copy_user_page = v6_copy_user_page_nonaliasing, }; static int __init v6_userpage_init(void) { - pgd_t *pgd; - pmd_t *pmd; - - pgd = pgd_offset_k(from_address); - pmd = pmd_alloc(&init_mm, pgd, from_address); - if (!pmd) - BUG(); - from_pte = pte_alloc_kernel(&init_mm, pmd, from_address); - if (!from_pte) - BUG(); - - to_pte = pte_alloc_kernel(&init_mm, pmd, to_address); - if (!to_pte) - BUG(); + if (cache_is_vipt_aliasing()) { + pgd_t *pgd; + pmd_t *pmd; + + pgd = pgd_offset_k(from_address); + pmd = pmd_alloc(&init_mm, pgd, from_address); + if (!pmd) + BUG(); + from_pte = pte_alloc_kernel(&init_mm, pmd, from_address); + if (!from_pte) + BUG(); + + to_pte = pte_alloc_kernel(&init_mm, pmd, to_address); + if (!to_pte) + BUG(); + + cpu_user.cpu_clear_user_page = v6_clear_user_page_aliasing; + cpu_user.cpu_copy_user_page = v6_copy_user_page_aliasing; + } return 0; } diff --git a/arch/arm/mm/flush.c b/arch/arm/mm/flush.c new file mode 100644 index 000000000..ff5f62ce4 --- /dev/null +++ b/arch/arm/mm/flush.c @@ -0,0 +1,94 @@ +/* + * linux/arch/arm/mm/flush.c + * + * Copyright (C) 1995-2002 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 +#include +#include + +#include +#include + +static void __flush_dcache_page(struct address_space *mapping, struct page *page) +{ + struct mm_struct *mm = current->active_mm; + struct vm_area_struct *mpnt; + struct prio_tree_iter iter; + pgoff_t pgoff; + + /* + * Writeback any data associated with the kernel mapping of this + * page. This ensures that data in the physical page is mutually + * coherent with the kernels mapping. + */ + __cpuc_flush_dcache_page(page_address(page)); + + /* + * If there's no mapping pointer here, then this page isn't + * visible to userspace yet, so there are no cache lines + * associated with any other aliases. + */ + if (!mapping) + return; + + /* + * There are possible user space mappings of this page: + * - VIVT cache: we need to also write back and invalidate all user + * data in the current VM view associated with this page. + * - aliasing VIPT: we only need to find one mapping of this page. + */ + pgoff = page->index << (PAGE_CACHE_SHIFT - PAGE_SHIFT); + + flush_dcache_mmap_lock(mapping); + vma_prio_tree_foreach(mpnt, &iter, &mapping->i_mmap, pgoff, pgoff) { + unsigned long offset; + + /* + * If this VMA is not in our MM, we can ignore it. + */ + if (mpnt->vm_mm != mm) + continue; + if (!(mpnt->vm_flags & VM_MAYSHARE)) + continue; + offset = (pgoff - mpnt->vm_pgoff) << PAGE_SHIFT; + flush_cache_page(mpnt, mpnt->vm_start + offset); + if (cache_is_vipt()) + break; + } + flush_dcache_mmap_unlock(mapping); +} + +/* + * Ensure cache coherency between kernel mapping and userspace mapping + * of this page. + * + * We have three cases to consider: + * - VIPT non-aliasing cache: fully coherent so nothing required. + * - VIVT: fully aliasing, so we need to handle every alias in our + * current VM view. + * - VIPT aliasing: need to handle one alias in our current VM view. + * + * If we need to handle aliasing: + * If the page only exists in the page cache and there are no user + * space mappings, we can be lazy and remember that we may have dirty + * kernel cache lines for later. Otherwise, we assume we have + * aliasing mappings. + */ +void flush_dcache_page(struct page *page) +{ + struct address_space *mapping = page_mapping(page); + + if (cache_is_vipt_nonaliasing()) + return; + + if (mapping && !mapping_mapped(mapping)) + set_bit(PG_dcache_dirty, &page->flags); + else + __flush_dcache_page(mapping, page); +} +EXPORT_SYMBOL(flush_dcache_page); diff --git a/arch/arm/mm/proc-arm1020.S b/arch/arm/mm/proc-arm1020.S index 7fe21a95d..1267ab586 100644 --- a/arch/arm/mm/proc-arm1020.S +++ b/arch/arm/mm/proc-arm1020.S @@ -196,6 +196,19 @@ ENTRY(arm1020_flush_user_cache_range) * - end - virtual end address */ ENTRY(arm1020_coherent_kern_range) + /* FALLTRHOUGH */ + +/* + * coherent_user_range(start, end) + * + * Ensure coherency between the Icache and the Dcache in the + * region described by start. If you have non-snooping + * Harvard caches, you need to implement this function. + * + * - start - virtual start address + * - end - virtual end address + */ +ENTRY(arm1020_coherent_user_range) mov ip, #0 bic r0, r0, #CACHE_DLINESIZE - 1 mcr p15, 0, ip, c7, c10, 4 @@ -317,6 +330,7 @@ ENTRY(arm1020_cache_fns) .long arm1020_flush_user_cache_all .long arm1020_flush_user_cache_range .long arm1020_coherent_kern_range + .long arm1020_coherent_user_range .long arm1020_flush_kern_dcache_page .long arm1020_dma_inv_range .long arm1020_dma_clean_range diff --git a/arch/arm/mm/proc-arm1020e.S b/arch/arm/mm/proc-arm1020e.S index 0332c3e69..947790dd3 100644 --- a/arch/arm/mm/proc-arm1020e.S +++ b/arch/arm/mm/proc-arm1020e.S @@ -193,6 +193,18 @@ ENTRY(arm1020e_flush_user_cache_range) * - end - virtual end address */ ENTRY(arm1020e_coherent_kern_range) + /* FALLTHROUGH */ +/* + * coherent_user_range(start, end) + * + * Ensure coherency between the Icache and the Dcache in the + * region described by start. If you have non-snooping + * Harvard caches, you need to implement this function. + * + * - start - virtual start address + * - end - virtual end address + */ +ENTRY(arm1020e_coherent_user_range) mov ip, #0 bic r0, r0, #CACHE_DLINESIZE - 1 1: @@ -304,6 +316,7 @@ ENTRY(arm1020e_cache_fns) .long arm1020e_flush_user_cache_all .long arm1020e_flush_user_cache_range .long arm1020e_coherent_kern_range + .long arm1020e_coherent_user_range .long arm1020e_flush_kern_dcache_page .long arm1020e_dma_inv_range .long arm1020e_dma_clean_range @@ -495,7 +508,7 @@ __arm1020e_proc_info: b __arm1020e_setup .long cpu_arch_name .long cpu_elf_name - .long HWCAP_SWP | HWCAP_HALF | HWCAP_THUMB + .long HWCAP_SWP | HWCAP_HALF | HWCAP_THUMB | HWCAP_EDSP .long cpu_arm1020e_name .long arm1020e_processor_functions .long v4wbi_tlb_fns diff --git a/arch/arm/mm/proc-arm1022.S b/arch/arm/mm/proc-arm1022.S index 35a5c2f00..7c8426384 100644 --- a/arch/arm/mm/proc-arm1022.S +++ b/arch/arm/mm/proc-arm1022.S @@ -180,6 +180,19 @@ ENTRY(arm1022_flush_user_cache_range) * - end - virtual end address */ ENTRY(arm1022_coherent_kern_range) + /* FALLTHROUGH */ + +/* + * coherent_user_range(start, end) + * + * Ensure coherency between the Icache and the Dcache in the + * region described by start. If you have non-snooping + * Harvard caches, you need to implement this function. + * + * - start - virtual start address + * - end - virtual end address + */ +ENTRY(arm1022_coherent_user_range) mov ip, #0 bic r0, r0, #CACHE_DLINESIZE - 1 1: @@ -291,6 +304,7 @@ ENTRY(arm1022_cache_fns) .long arm1022_flush_user_cache_all .long arm1022_flush_user_cache_range .long arm1022_coherent_kern_range + .long arm1022_coherent_user_range .long arm1022_flush_kern_dcache_page .long arm1022_dma_inv_range .long arm1022_dma_clean_range @@ -475,7 +489,7 @@ __arm1022_proc_info: b __arm1022_setup .long cpu_arch_name .long cpu_elf_name - .long HWCAP_SWP | HWCAP_HALF | HWCAP_THUMB + .long HWCAP_SWP | HWCAP_HALF | HWCAP_THUMB | HWCAP_EDSP .long cpu_arm1022_name .long arm1022_processor_functions .long v4wbi_tlb_fns diff --git a/arch/arm/mm/proc-arm1026.S b/arch/arm/mm/proc-arm1026.S index 0009f2d27..38a06cb1e 100644 --- a/arch/arm/mm/proc-arm1026.S +++ b/arch/arm/mm/proc-arm1026.S @@ -175,6 +175,18 @@ ENTRY(arm1026_flush_user_cache_range) * - end - virtual end address */ ENTRY(arm1026_coherent_kern_range) + /* FALLTHROUGH */ +/* + * coherent_user_range(start, end) + * + * Ensure coherency between the Icache and the Dcache in the + * region described by start. If you have non-snooping + * Harvard caches, you need to implement this function. + * + * - start - virtual start address + * - end - virtual end address + */ +ENTRY(arm1026_coherent_user_range) mov ip, #0 bic r0, r0, #CACHE_DLINESIZE - 1 1: @@ -286,6 +298,7 @@ ENTRY(arm1026_cache_fns) .long arm1026_flush_user_cache_all .long arm1026_flush_user_cache_range .long arm1026_coherent_kern_range + .long arm1026_coherent_user_range .long arm1026_flush_kern_dcache_page .long arm1026_dma_inv_range .long arm1026_dma_clean_range @@ -471,7 +484,7 @@ __arm1026_proc_info: b __arm1026_setup .long cpu_arch_name .long cpu_elf_name - .long HWCAP_SWP | HWCAP_HALF | HWCAP_THUMB | HWCAP_FAST_MULT + .long HWCAP_SWP|HWCAP_HALF|HWCAP_THUMB|HWCAP_FAST_MULT|HWCAP_EDSP|HWCAP_JAVA .long cpu_arm1026_name .long arm1026_processor_functions .long v4wbi_tlb_fns diff --git a/arch/arm/mm/proc-arm920.S b/arch/arm/mm/proc-arm920.S index 3922a8213..8c9204a7c 100644 --- a/arch/arm/mm/proc-arm920.S +++ b/arch/arm/mm/proc-arm920.S @@ -182,6 +182,19 @@ ENTRY(arm920_flush_user_cache_range) * - end - virtual end address */ ENTRY(arm920_coherent_kern_range) + /* FALLTHROUGH */ + +/* + * coherent_user_range(start, end) + * + * Ensure coherency between the Icache and the Dcache in the + * region described by start, end. If you have non-snooping + * Harvard caches, you need to implement this function. + * + * - start - virtual start address + * - end - virtual end address + */ +ENTRY(arm920_coherent_user_range) bic r0, r0, #CACHE_DLINESIZE - 1 1: mcr p15, 0, r0, c7, c10, 1 @ clean D entry mcr p15, 0, r0, c7, c5, 1 @ invalidate I entry @@ -277,6 +290,7 @@ ENTRY(arm920_cache_fns) .long arm920_flush_user_cache_all .long arm920_flush_user_cache_range .long arm920_coherent_kern_range + .long arm920_coherent_user_range .long arm920_flush_kern_dcache_page .long arm920_dma_inv_range .long arm920_dma_clean_range diff --git a/arch/arm/mm/proc-arm922.S b/arch/arm/mm/proc-arm922.S index 86065117c..13e65cb8a 100644 --- a/arch/arm/mm/proc-arm922.S +++ b/arch/arm/mm/proc-arm922.S @@ -184,6 +184,19 @@ ENTRY(arm922_flush_user_cache_range) * - end - virtual end address */ ENTRY(arm922_coherent_kern_range) + /* FALLTHROUGH */ + +/* + * coherent_user_range(start, end) + * + * Ensure coherency between the Icache and the Dcache in the + * region described by start, end. If you have non-snooping + * Harvard caches, you need to implement this function. + * + * - start - virtual start address + * - end - virtual end address + */ +ENTRY(arm922_coherent_user_range) bic r0, r0, #CACHE_DLINESIZE - 1 1: mcr p15, 0, r0, c7, c10, 1 @ clean D entry mcr p15, 0, r0, c7, c5, 1 @ invalidate I entry @@ -279,6 +292,7 @@ ENTRY(arm922_cache_fns) .long arm922_flush_user_cache_all .long arm922_flush_user_cache_range .long arm922_coherent_kern_range + .long arm922_coherent_user_range .long arm922_flush_kern_dcache_page .long arm922_dma_inv_range .long arm922_dma_clean_range diff --git a/arch/arm/mm/proc-arm925.S b/arch/arm/mm/proc-arm925.S index 917eb5e16..e8b3ff1fb 100644 --- a/arch/arm/mm/proc-arm925.S +++ b/arch/arm/mm/proc-arm925.S @@ -225,6 +225,19 @@ ENTRY(arm925_flush_user_cache_range) * - end - virtual end address */ ENTRY(arm925_coherent_kern_range) + /* FALLTHROUGH */ + +/* + * coherent_user_range(start, end) + * + * Ensure coherency between the Icache and the Dcache in the + * region described by start, end. If you have non-snooping + * Harvard caches, you need to implement this function. + * + * - start - virtual start address + * - end - virtual end address + */ +ENTRY(arm925_coherent_user_range) bic r0, r0, #CACHE_DLINESIZE - 1 1: mcr p15, 0, r0, c7, c10, 1 @ clean D entry mcr p15, 0, r0, c7, c5, 1 @ invalidate I entry @@ -329,6 +342,7 @@ ENTRY(arm925_cache_fns) .long arm925_flush_user_cache_all .long arm925_flush_user_cache_range .long arm925_coherent_kern_range + .long arm925_coherent_user_range .long arm925_flush_kern_dcache_page .long arm925_dma_inv_range .long arm925_dma_clean_range diff --git a/arch/arm/mm/proc-arm926.S b/arch/arm/mm/proc-arm926.S index 9b098e05c..5631c8bbd 100644 --- a/arch/arm/mm/proc-arm926.S +++ b/arch/arm/mm/proc-arm926.S @@ -185,6 +185,19 @@ ENTRY(arm926_flush_user_cache_range) * - end - virtual end address */ ENTRY(arm926_coherent_kern_range) + /* FALLTHROUGH */ + +/* + * coherent_user_range(start, end) + * + * Ensure coherency between the Icache and the Dcache in the + * region described by start, end. If you have non-snooping + * Harvard caches, you need to implement this function. + * + * - start - virtual start address + * - end - virtual end address + */ +ENTRY(arm926_coherent_user_range) bic r0, r0, #CACHE_DLINESIZE - 1 1: mcr p15, 0, r0, c7, c10, 1 @ clean D entry mcr p15, 0, r0, c7, c5, 1 @ invalidate I entry @@ -289,6 +302,7 @@ ENTRY(arm926_cache_fns) .long arm926_flush_user_cache_all .long arm926_flush_user_cache_range .long arm926_coherent_kern_range + .long arm926_coherent_user_range .long arm926_flush_kern_dcache_page .long arm926_dma_inv_range .long arm926_dma_clean_range @@ -473,7 +487,7 @@ __arm926_proc_info: b __arm926_setup .long cpu_arch_name .long cpu_elf_name - .long HWCAP_SWP | HWCAP_HALF | HWCAP_THUMB | HWCAP_FAST_MULT | HWCAP_JAVA + .long HWCAP_SWP|HWCAP_HALF|HWCAP_THUMB|HWCAP_FAST_MULT|HWCAP_EDSP|HWCAP_JAVA .long cpu_arm926_name .long arm926_processor_functions .long v4wbi_tlb_fns diff --git a/arch/arm/mm/proc-syms.c b/arch/arm/mm/proc-syms.c index 8df666f8d..6c5f0fe57 100644 --- a/arch/arm/mm/proc-syms.c +++ b/arch/arm/mm/proc-syms.c @@ -22,10 +22,10 @@ EXPORT_SYMBOL(processor); #endif #ifndef MULTI_CACHE -EXPORT_SYMBOL_NOVERS(__cpuc_flush_kern_all); -EXPORT_SYMBOL_NOVERS(__cpuc_flush_user_all); -EXPORT_SYMBOL_NOVERS(__cpuc_flush_user_range); -EXPORT_SYMBOL_NOVERS(__cpuc_coherent_kern_range); +EXPORT_SYMBOL(__cpuc_flush_kern_all); +EXPORT_SYMBOL(__cpuc_flush_user_all); +EXPORT_SYMBOL(__cpuc_flush_user_range); +EXPORT_SYMBOL(__cpuc_coherent_kern_range); #else EXPORT_SYMBOL(cpu_cache); #endif diff --git a/arch/arm/mm/proc-v6.S b/arch/arm/mm/proc-v6.S index c22cc3601..0a4ff2624 100644 --- a/arch/arm/mm/proc-v6.S +++ b/arch/arm/mm/proc-v6.S @@ -189,12 +189,10 @@ cpu_v6_name: * - cache type register is implemented */ __v6_setup: - mrc p15, 0, r10, c0, c0, 1 @ read cache type register - tst r10, #1 << 24 @ Harvard cache? mov r10, #0 - mcrne p15, 0, r10, c7, c14, 0 @ clean+invalidate D cache - mcrne p15, 0, r10, c7, c5, 0 @ invalidate I cache - mcreq p15, 0, r10, c7, c15, 0 @ clean+invalidate cache + mcr p15, 0, r10, c7, c14, 0 @ clean+invalidate D cache + mcr p15, 0, r10, c7, c5, 0 @ invalidate I cache + mcr p15, 0, r10, c7, c15, 0 @ clean+invalidate cache mcr p15, 0, r10, c7, c10, 4 @ drain write buffer mcr p15, 0, r10, c8, c7, 0 @ invalidate I + D TLBs mcr p15, 0, r10, c2, c0, 2 @ TTB control register @@ -258,7 +256,7 @@ __v6_proc_info: b __v6_setup .long cpu_arch_name .long cpu_elf_name - .long HWCAP_SWP | HWCAP_HALF | HWCAP_FAST_MULT | HWCAP_VFP + .long HWCAP_SWP|HWCAP_HALF|HWCAP_THUMB|HWCAP_FAST_MULT|HWCAP_VFP|HWCAP_EDSP|HWCAP_JAVA .long cpu_v6_name .long v6_processor_functions .long v6wbi_tlb_fns diff --git a/arch/arm/nwfpe/fpa11_cpdt.c b/arch/arm/nwfpe/fpa11_cpdt.c index 2ad457558..95fb63fa9 100644 --- a/arch/arm/nwfpe/fpa11_cpdt.c +++ b/arch/arm/nwfpe/fpa11_cpdt.c @@ -42,8 +42,13 @@ static inline void loadDouble(const unsigned int Fn, const unsigned int __user * unsigned int *p; p = (unsigned int *) &fpa11->fpreg[Fn].fDouble; fpa11->fType[Fn] = typeDouble; +#ifdef __ARMEB__ + get_user(p[0], &pMem[0]); /* sign & exponent */ + get_user(p[1], &pMem[1]); +#else get_user(p[0], &pMem[1]); get_user(p[1], &pMem[0]); /* sign & exponent */ +#endif } #ifdef CONFIG_FPE_NWFPE_XP @@ -140,8 +145,13 @@ static inline void storeDouble(const unsigned int Fn, unsigned int __user *pMem) val.f = fpa11->fpreg[Fn].fDouble; } +#ifdef __ARMEB__ + put_user(val.i[0], &pMem[0]); /* msw */ + put_user(val.i[1], &pMem[1]); /* lsw */ +#else put_user(val.i[1], &pMem[0]); /* msw */ put_user(val.i[0], &pMem[1]); /* lsw */ +#endif } #ifdef CONFIG_FPE_NWFPE_XP diff --git a/arch/arm/oprofile/op_model_xscale.c b/arch/arm/oprofile/op_model_xscale.c index 284874be3..447653df1 100644 --- a/arch/arm/oprofile/op_model_xscale.c +++ b/arch/arm/oprofile/op_model_xscale.c @@ -7,7 +7,7 @@ * @remark Copyright 2004 Dave Jiang * @remark Copyright 2004 Intel Corporation * @remark Copyright 2004 Zwane Mwaikambo - * @remark Copyright 2004 Oprofile Authors + * @remark Copyright 2004 OProfile Authors * * @remark Read the file COPYING * @@ -30,6 +30,7 @@ #define PMN_RESET 0x002 /* Reset event counters */ #define CCNT_RESET 0x004 /* Reset clock counter */ #define PMU_RESET (CCNT_RESET | PMN_RESET) +#define PMU_CNT64 0x008 /* Make CCNT count every 64th cycle */ /* TODO do runtime detection */ #ifdef CONFIG_ARCH_IOP310 @@ -125,12 +126,15 @@ static struct pmu_type *pmu; static void write_pmnc(u32 val) { - /* upper 4bits and 7, 11 are write-as-0 */ - val &= 0xffff77f; - if (pmu->id == PMU_XSC1) + if (pmu->id == PMU_XSC1) { + /* upper 4bits and 7, 11 are write-as-0 */ + val &= 0xffff77f; __asm__ __volatile__ ("mcr p14, 0, %0, c0, c0, 0" : : "r" (val)); - else + } else { + /* bits 4-23 are write-as-0, 24-31 are write ignored */ + val &= 0xf; __asm__ __volatile__ ("mcr p14, 0, %0, c0, c1, 0" : : "r" (val)); + } } static u32 read_pmnc(void) @@ -139,8 +143,11 @@ static u32 read_pmnc(void) if (pmu->id == PMU_XSC1) __asm__ __volatile__ ("mrc p14, 0, %0, c0, c0, 0" : "=r" (val)); - else + else { __asm__ __volatile__ ("mrc p14, 0, %0, c0, c1, 0" : "=r" (val)); + /* bits 1-2 and 4-23 are read-unpredictable */ + val &= 0xff000009; + } return val; } @@ -249,7 +256,7 @@ static int xscale_setup_ctrs(void) int i; for (i = CCNT; i < MAX_COUNTERS; i++) { - if (counter_config[i].event) + if (counter_config[i].enabled) continue; counter_config[i].event = EVT_UNUSED; @@ -336,7 +343,7 @@ static void inline __xsc2_check_ctrs(void) static irqreturn_t xscale_pmu_interrupt(int irq, void *arg, struct pt_regs *regs) { - unsigned long eip = instruction_pointer(regs); + unsigned long pc = profile_pc(regs); int i, is_kernel = !user_mode(regs); u32 pmnc; @@ -350,7 +357,7 @@ static irqreturn_t xscale_pmu_interrupt(int irq, void *arg, struct pt_regs *regs continue; write_counter(i, -(u32)results[i].reset_counter); - oprofile_add_sample(eip, is_kernel, i, smp_processor_id()); + oprofile_add_sample(pc, is_kernel, i, smp_processor_id()); results[i].ovf--; } @@ -386,8 +393,10 @@ static int xscale_pmu_start(void) if (pmu->id == PMU_XSC1) pmnc |= pmu->int_enable; - else + else { __asm__ __volatile__ ("mcr p14, 0, %0, c4, c1, 0" : : "r" (pmu->int_enable)); + pmnc &= ~PMU_CNT64; + } pmnc |= PMU_ENABLE; write_pmnc(pmnc); diff --git a/arch/arm26/Kconfig.debug b/arch/arm26/Kconfig.debug new file mode 100644 index 000000000..e2c920dc1 --- /dev/null +++ b/arch/arm26/Kconfig.debug @@ -0,0 +1,60 @@ +menu "Kernel hacking" + +source "lib/Kconfig.debug" + +# RMK wants arm kernels compiled with frame pointers so hardwire this to y. +# If you know what you are doing and are willing to live without stack +# traces, you can get a slightly smaller kernel by setting this option to +# n, but then RMK will have to kill you ;). +config FRAME_POINTER + bool + default y + help + If you say N here, the resulting kernel will be slightly smaller and + faster. However, when a problem occurs with the kernel, the + information that is reported is severely limited. Most people + should say Y here. + +config DEBUG_USER + bool "Verbose user fault messages" + help + When a user program crashes due to an exception, the kernel can + print a brief message explaining what the problem was. This is + sometimes helpful for debugging but serves no purpose on a + production system. Most people should say N here. + +config DEBUG_WAITQ + bool "Wait queue debugging" + depends on DEBUG_KERNEL + +config DEBUG_ERRORS + bool "Verbose kernel error messages" + depends on DEBUG_KERNEL + help + This option controls verbose debugging information which can be + printed when the kernel detects an internal error. This debugging + information is useful to kernel hackers when tracking down problems, + but mostly meaningless to other people. It's safe to say Y unless + you are concerned with the code size or don't want to see these + messages. + +config DEBUG_INFO + bool "Include GDB debugging information in kernel binary" + help + Say Y here to include source-level debugging information in the + `vmlinux' binary image. This is handy if you want to use gdb or + addr2line to debug the kernel. It has no impact on the in-memory + footprint of the running kernel but it can increase the amount of + time and disk space needed for compilation of the kernel. If in + doubt say N. + +# These options are only for real kernel hackers who want to get their hands dirty. +config DEBUG_LL + bool "Kernel low-level debugging functions" + depends on DEBUG_KERNEL + help + Say Y here to include definitions of printascii, printchar, printhex + in the kernel. This is helpful if you are debugging code that + executes before the console is initialized. + +endmenu diff --git a/arch/arm26/Makefile b/arch/arm26/Makefile index 3b0294a8d..631c20aef 100644 --- a/arch/arm26/Makefile +++ b/arch/arm26/Makefile @@ -8,8 +8,7 @@ # Copyright (C) 1995-2001 by Russell King LDFLAGS_vmlinux :=-p -X -LDFLAGS_BLOB :=--format binary -AFLAGS_vmlinux.lds.o = -DTEXTADDR=$(TEXTADDR) -DDATAADDR=$(DATAADDR) +CPPFLAGS_vmlinux.lds = -DTEXTADDR=$(TEXTADDR) -DDATAADDR=$(DATAADDR) OBJCOPYFLAGS :=-O binary -R .note -R .comment -S GZFLAGS :=-9 @@ -28,7 +27,6 @@ CFLAGS +=-mapcs-26 -mcpu=arm3 -mshort-load-bytes -msoft-float -Wa,-mno-fpu -Uar AFLAGS +=-mapcs-26 -mcpu=arm3 -mno-fpu -msoft-float -Wa,-mno-fpu head-y := arch/arm26/machine/head.o arch/arm26/kernel/init_task.o -LDFLAGS_BLOB += --oformat elf32-littlearm ifeq ($(CONFIG_XIP_KERNEL),y) TEXTADDR := 0x03880000 diff --git a/arch/arm26/boot/Makefile b/arch/arm26/boot/Makefile index 3d992e597..004dd81f9 100644 --- a/arch/arm26/boot/Makefile +++ b/arch/arm26/boot/Makefile @@ -67,12 +67,12 @@ initrd: install: $(obj)/Image $(CONFIG_SHELL) $(obj)/install.sh \ - $(VERSION).$(PATCHLEVEL).$(SUBLEVEL)$(EXTRAVERSION) \ + $(KERNELRELEASE) \ $(obj)/Image System.map "$(INSTALL_PATH)" zinstall: $(obj)/zImage $(CONFIG_SHELL) $(obj)/install.sh \ - $(VERSION).$(PATCHLEVEL).$(SUBLEVEL)$(EXTRAVERSION) \ + $(KERNELRELEASE) \ $(obj)/zImage System.map "$(INSTALL_PATH)" subdir- := compressed diff --git a/arch/arm26/kernel/Makefile b/arch/arm26/kernel/Makefile index 7e10b557c..e2e5503f7 100644 --- a/arch/arm26/kernel/Makefile +++ b/arch/arm26/kernel/Makefile @@ -14,5 +14,5 @@ obj-y := compat.o dma.o entry.o irq.o \ obj-$(CONFIG_FIQ) += fiq.o obj-$(CONFIG_MODULES) += armksyms.o -extra-y := init_task.o vmlinux.lds.s +extra-y := init_task.o vmlinux.lds diff --git a/arch/arm26/kernel/armksyms.c b/arch/arm26/kernel/armksyms.c index c540f22f9..bd6c30669 100644 --- a/arch/arm26/kernel/armksyms.c +++ b/arch/arm26/kernel/armksyms.c @@ -95,7 +95,7 @@ EXPORT_SYMBOL(ret_from_exception); EXPORT_SYMBOL(kd_mksound); #endif -EXPORT_SYMBOL_NOVERS(__do_softirq); +EXPORT_SYMBOL(__do_softirq); /* platform dependent support */ EXPORT_SYMBOL(dump_thread); @@ -125,71 +125,71 @@ EXPORT_SYMBOL(__csum_ipv6_magic); /* io */ #ifndef __raw_readsb -EXPORT_SYMBOL_NOVERS(__raw_readsb); +EXPORT_SYMBOL(__raw_readsb); #endif #ifndef __raw_readsw -EXPORT_SYMBOL_NOVERS(__raw_readsw); +EXPORT_SYMBOL(__raw_readsw); #endif #ifndef __raw_readsl -EXPORT_SYMBOL_NOVERS(__raw_readsl); +EXPORT_SYMBOL(__raw_readsl); #endif #ifndef __raw_writesb -EXPORT_SYMBOL_NOVERS(__raw_writesb); +EXPORT_SYMBOL(__raw_writesb); #endif #ifndef __raw_writesw -EXPORT_SYMBOL_NOVERS(__raw_writesw); +EXPORT_SYMBOL(__raw_writesw); #endif #ifndef __raw_writesl -EXPORT_SYMBOL_NOVERS(__raw_writesl); +EXPORT_SYMBOL(__raw_writesl); #endif /* string / mem functions */ -EXPORT_SYMBOL_NOVERS(strcpy); -EXPORT_SYMBOL_NOVERS(strncpy); -EXPORT_SYMBOL_NOVERS(strcat); -EXPORT_SYMBOL_NOVERS(strncat); -EXPORT_SYMBOL_NOVERS(strcmp); -EXPORT_SYMBOL_NOVERS(strncmp); -EXPORT_SYMBOL_NOVERS(strchr); -EXPORT_SYMBOL_NOVERS(strlen); -EXPORT_SYMBOL_NOVERS(strnlen); -EXPORT_SYMBOL_NOVERS(strpbrk); -EXPORT_SYMBOL_NOVERS(strrchr); -EXPORT_SYMBOL_NOVERS(strstr); -EXPORT_SYMBOL_NOVERS(memset); -EXPORT_SYMBOL_NOVERS(memcpy); -EXPORT_SYMBOL_NOVERS(memmove); -EXPORT_SYMBOL_NOVERS(memcmp); -EXPORT_SYMBOL_NOVERS(memscan); -EXPORT_SYMBOL_NOVERS(__memzero); +EXPORT_SYMBOL(strcpy); +EXPORT_SYMBOL(strncpy); +EXPORT_SYMBOL(strcat); +EXPORT_SYMBOL(strncat); +EXPORT_SYMBOL(strcmp); +EXPORT_SYMBOL(strncmp); +EXPORT_SYMBOL(strchr); +EXPORT_SYMBOL(strlen); +EXPORT_SYMBOL(strnlen); +EXPORT_SYMBOL(strpbrk); +EXPORT_SYMBOL(strrchr); +EXPORT_SYMBOL(strstr); +EXPORT_SYMBOL(memset); +EXPORT_SYMBOL(memcpy); +EXPORT_SYMBOL(memmove); +EXPORT_SYMBOL(memcmp); +EXPORT_SYMBOL(memscan); +EXPORT_SYMBOL(__memzero); /* user mem (segment) */ EXPORT_SYMBOL(uaccess_kernel); EXPORT_SYMBOL(uaccess_user); -EXPORT_SYMBOL_NOVERS(__get_user_1); -EXPORT_SYMBOL_NOVERS(__get_user_2); -EXPORT_SYMBOL_NOVERS(__get_user_4); -EXPORT_SYMBOL_NOVERS(__get_user_8); +EXPORT_SYMBOL(__get_user_1); +EXPORT_SYMBOL(__get_user_2); +EXPORT_SYMBOL(__get_user_4); +EXPORT_SYMBOL(__get_user_8); -EXPORT_SYMBOL_NOVERS(__put_user_1); -EXPORT_SYMBOL_NOVERS(__put_user_2); -EXPORT_SYMBOL_NOVERS(__put_user_4); -EXPORT_SYMBOL_NOVERS(__put_user_8); +EXPORT_SYMBOL(__put_user_1); +EXPORT_SYMBOL(__put_user_2); +EXPORT_SYMBOL(__put_user_4); +EXPORT_SYMBOL(__put_user_8); /* gcc lib functions */ -EXPORT_SYMBOL_NOVERS(__ashldi3); -EXPORT_SYMBOL_NOVERS(__ashrdi3); -EXPORT_SYMBOL_NOVERS(__divsi3); -EXPORT_SYMBOL_NOVERS(__lshrdi3); -EXPORT_SYMBOL_NOVERS(__modsi3); -EXPORT_SYMBOL_NOVERS(__muldi3); -EXPORT_SYMBOL_NOVERS(__ucmpdi2); -EXPORT_SYMBOL_NOVERS(__udivdi3); -EXPORT_SYMBOL_NOVERS(__umoddi3); -EXPORT_SYMBOL_NOVERS(__udivmoddi4); -EXPORT_SYMBOL_NOVERS(__udivsi3); -EXPORT_SYMBOL_NOVERS(__umodsi3); +EXPORT_SYMBOL(__ashldi3); +EXPORT_SYMBOL(__ashrdi3); +EXPORT_SYMBOL(__divsi3); +EXPORT_SYMBOL(__lshrdi3); +EXPORT_SYMBOL(__modsi3); +EXPORT_SYMBOL(__muldi3); +EXPORT_SYMBOL(__ucmpdi2); +EXPORT_SYMBOL(__udivdi3); +EXPORT_SYMBOL(__umoddi3); +EXPORT_SYMBOL(__udivmoddi4); +EXPORT_SYMBOL(__udivsi3); +EXPORT_SYMBOL(__umodsi3); /* bitops */ EXPORT_SYMBOL(_set_bit_le); @@ -214,10 +214,10 @@ EXPORT_SYMBOL(sys_exit); EXPORT_SYMBOL(sys_wait4); /* semaphores */ -EXPORT_SYMBOL_NOVERS(__down_failed); -EXPORT_SYMBOL_NOVERS(__down_interruptible_failed); -EXPORT_SYMBOL_NOVERS(__down_trylock_failed); -EXPORT_SYMBOL_NOVERS(__up_wakeup); +EXPORT_SYMBOL(__down_failed); +EXPORT_SYMBOL(__down_interruptible_failed); +EXPORT_SYMBOL(__down_trylock_failed); +EXPORT_SYMBOL(__up_wakeup); EXPORT_SYMBOL(get_wchan); diff --git a/arch/arm26/kernel/signal.c b/arch/arm26/kernel/signal.c index 16cf7db5c..bf05f1537 100644 --- a/arch/arm26/kernel/signal.c +++ b/arch/arm26/kernel/signal.c @@ -465,9 +465,7 @@ handle_signal(unsigned long sig, siginfo_t *info, sigset_t *oldset, return; } - if (sig == SIGSEGV) - ka->sa.sa_handler = SIG_DFL; - force_sig(SIGSEGV, tsk); + force_sigsegv(sig, tsk); } /* diff --git a/arch/arm26/kernel/sys_arm.c b/arch/arm26/kernel/sys_arm.c index 0d1cdd503..431f9f85e 100644 --- a/arch/arm26/kernel/sys_arm.c +++ b/arch/arm26/kernel/sys_arm.c @@ -256,7 +256,7 @@ asmlinkage int sys_clone(unsigned long clone_flags, unsigned long newsp, struct if (!newsp) newsp = regs->ARM_sp; - return do_fork(clone_flags & ~CLONE_IDLETASK, newsp, regs, 0, NULL, NULL); + return do_fork(clone_flags, newsp, regs, 0, NULL, NULL); } asmlinkage int sys_vfork(struct pt_regs *regs) diff --git a/arch/arm26/kernel/time.c b/arch/arm26/kernel/time.c index af2905a1f..dbbc21404 100644 --- a/arch/arm26/kernel/time.c +++ b/arch/arm26/kernel/time.c @@ -67,28 +67,6 @@ static unsigned long dummy_gettimeoffset(void) */ unsigned long (*gettimeoffset)(void) = dummy_gettimeoffset; -/* - * Handle kernel profile stuff... - */ -static inline void do_profile(struct pt_regs *regs) -{ - if (!user_mode(regs) && - prof_buffer && - current->pid) { - unsigned long pc = instruction_pointer(regs); - extern int _stext; - - pc -= (unsigned long)&_stext; - - pc >>= prof_shift; - - if (pc >= prof_len) - pc = prof_len - 1; - - prof_buffer[pc] += 1; - } -} - static unsigned long next_rtc_update; /* @@ -189,7 +167,7 @@ static irqreturn_t timer_interrupt(int irq, void *dev_id, struct pt_regs *regs) { do_timer(regs); do_set_rtc(); //FIME - EVERY timer IRQ? - do_profile(regs); + profile_tick(CPU_PROFILING, regs); return IRQ_HANDLED; //FIXME - is this right? } diff --git a/arch/arm26/kernel/vmlinux-arm26-xip.lds.in b/arch/arm26/kernel/vmlinux-arm26-xip.lds.in index 61eedf0bc..cbbda2766 100644 --- a/arch/arm26/kernel/vmlinux-arm26-xip.lds.in +++ b/arch/arm26/kernel/vmlinux-arm26-xip.lds.in @@ -34,9 +34,6 @@ SECTIONS __early_begin = .; *(__early_param) __early_end = .; - __start___param = .; - *(__param) - __stop___param = .; __initcall_start = .; *(.initcall1.init) *(.initcall2.init) diff --git a/arch/arm26/kernel/vmlinux-arm26.lds.in b/arch/arm26/kernel/vmlinux-arm26.lds.in index 2393f3805..128eb9195 100644 --- a/arch/arm26/kernel/vmlinux-arm26.lds.in +++ b/arch/arm26/kernel/vmlinux-arm26.lds.in @@ -35,9 +35,6 @@ SECTIONS __early_begin = .; *(__early_param) __early_end = .; - __start___param = .; - *(__param) - __stop___param = .; __initcall_start = .; *(.initcall1.init) *(.initcall2.init) diff --git a/arch/cris/Kconfig.debug b/arch/cris/Kconfig.debug new file mode 100644 index 000000000..9864aade2 --- /dev/null +++ b/arch/cris/Kconfig.debug @@ -0,0 +1,28 @@ +menu "Kernel hacking" + +source "lib/Kconfig.debug" + +#bool 'Debug kmalloc/kfree' CONFIG_DEBUG_MALLOC +config PROFILE + bool "Kernel profiling support" + +config PROFILE_SHIFT + int "Profile shift count" + depends on PROFILE + default "2" + +config ETRAX_KGDB + bool "Use kernel GDB debugger" + ---help--- + The CRIS version of gdb can be used to remotely debug a running + Linux kernel via the serial debug port. Provided you have gdb-cris + installed, run gdb-cris vmlinux, then type + + (gdb) set remotebaud 115200 <- kgdb uses 115200 as default + (gdb) target remote /dev/ttyS0 <- maybe you use another port + + This should connect you to your booted kernel (or boot it now if you + didn't before). The kernel halts when it boots, waiting for gdb if + this option is turned on! + +endmenu diff --git a/arch/cris/arch-v10/mm/init.c b/arch/cris/arch-v10/mm/init.c index df03dea5b..720b9c156 100644 --- a/arch/cris/arch-v10/mm/init.c +++ b/arch/cris/arch-v10/mm/init.c @@ -183,7 +183,7 @@ paging_init(void) * mem_map page array. */ - free_area_init_node(0, &contig_page_data, 0, zones_size, PAGE_OFFSET >> PAGE_SHIFT, 0); + free_area_init_node(0, &contig_page_data, zones_size, PAGE_OFFSET >> PAGE_SHIFT, 0); mem_map = contig_page_data.node_mem_map; } diff --git a/arch/cris/arch-v10/vmlinux.lds.S b/arch/cris/arch-v10/vmlinux.lds.S index 6b73a2c0d..9e134c688 100644 --- a/arch/cris/arch-v10/vmlinux.lds.S +++ b/arch/cris/arch-v10/vmlinux.lds.S @@ -64,9 +64,6 @@ SECTIONS __setup_start = .; .init.setup : { *(.init.setup) } __setup_end = .; - __start___param = .; - __param : { *(__param) } - __stop___param = .; .initcall.init : { __initcall_start = .; *(.initcall1.init); diff --git a/arch/cris/kernel/crisksyms.c b/arch/cris/kernel/crisksyms.c index 6ded633f8..7141bbecd 100644 --- a/arch/cris/kernel/crisksyms.c +++ b/arch/cris/kernel/crisksyms.c @@ -16,7 +16,6 @@ #include #include #include -#include #include #include #include @@ -91,8 +90,8 @@ EXPORT_SYMBOL(prepare_rx_descriptor); #undef memset extern void * memset(void *, int, __kernel_size_t); extern void * memcpy(void *, const void *, __kernel_size_t); -EXPORT_SYMBOL_NOVERS(memcpy); -EXPORT_SYMBOL_NOVERS(memset); +EXPORT_SYMBOL(memcpy); +EXPORT_SYMBOL(memset); #ifdef CONFIG_ETRAX_FAST_TIMER /* Fast timer functions */ diff --git a/arch/h8300/Kconfig.debug b/arch/h8300/Kconfig.debug new file mode 100644 index 000000000..55034d08a --- /dev/null +++ b/arch/h8300/Kconfig.debug @@ -0,0 +1,68 @@ +menu "Kernel hacking" + +source "lib/Kconfig.debug" + +config FULLDEBUG + bool "Full Symbolic/Source Debugging support" + help + Enable debugging symbols on kernel build. + +config HIGHPROFILE + bool "Use fast second timer for profiling" + help + Use a fast secondary clock to produce profiling information. + +config NO_KERNEL_MSG + bool "Suppress Kernel BUG Messages" + help + Do not output any debug BUG messages within the kernel. + +config GDB_MAGICPRINT + bool "Message Output for GDB MagicPrint service" + depends on (H8300H_SIM || H8S_SIM) + help + kernel messages output useing MagicPrint service from GDB + +config SYSCALL_PRINT + bool "SystemCall trace print" + help + outout history of systemcall + +config GDB_DEBUG + bool "Use gdb stub" + depends on (!H8300H_SIM && !H8S_SIM) + help + gdb stub exception support + +config CONFIG_SH_STANDARD_BIOS + bool "Use gdb protocol serial console" + depends on (!H8300H_SIM && !H8S_SIM) + help + serial console output using GDB protocol. + Require eCos/RedBoot + +config DEFAULT_CMDLINE + bool "Use buildin commandline" + default n + help + buildin kernel commandline enabled. + +config KERNEL_COMMAND + string "Buildin commmand string" + depends on DEFAULT_CMDLINE + help + buildin kernel commandline strings. + +config BLKDEV_RESERVE + bool "BLKDEV Reserved Memory" + default n + help + Reserved BLKDEV area. + +config CONFIG_BLKDEV_RESERVE_ADDRESS + hex 'start address' + depends on BLKDEV_RESERVE + help + BLKDEV start address. + +endmenu diff --git a/arch/h8300/kernel/asm-offsets.c b/arch/h8300/kernel/asm-offsets.c index 5e50c0893..b78b82ad2 100644 --- a/arch/h8300/kernel/asm-offsets.c +++ b/arch/h8300/kernel/asm-offsets.c @@ -12,9 +12,9 @@ #include #include #include +#include #include #include -#include #include #define DEFINE(sym, val) \ diff --git a/arch/h8300/kernel/ints.c b/arch/h8300/kernel/ints.c index 7246e29ea..edb3c4170 100644 --- a/arch/h8300/kernel/ints.c +++ b/arch/h8300/kernel/ints.c @@ -22,13 +22,13 @@ #include #include #include +#include #include #include #include #include #include -#include #include /* diff --git a/arch/h8300/kernel/time.c b/arch/h8300/kernel/time.c index 87e284901..0b293d652 100644 --- a/arch/h8300/kernel/time.c +++ b/arch/h8300/kernel/time.c @@ -36,24 +36,6 @@ u64 jiffies_64; EXPORT_SYMBOL(jiffies_64); -static inline void do_profile (unsigned long pc) -{ - if (prof_buffer && current->pid) { - extern int _stext; - pc -= (unsigned long) &_stext; - pc >>= prof_shift; - if (pc < prof_len) - ++prof_buffer[pc]; - else - /* - * Don't ignore out-of-bounds PC values silently, - * put them into the last histogram slot, so if - * present, they will show up as a sharp peak. - */ - ++prof_buffer[prof_len-1]; - } -} - /* * timer_interrupt() needs to keep up the real-time clock, * as well as call the "do_timer()" routine every clocktick @@ -64,10 +46,7 @@ static void timer_interrupt(int irq, void *dummy, struct pt_regs * regs) platform_timer_eoi(); do_timer(regs); - - if (!user_mode(regs)) - do_profile(regs->pc); - + profile_tick(CPU_PROFILING, regs); } void time_init(void) diff --git a/arch/h8300/lib/checksum.c b/arch/h8300/lib/checksum.c index 6691d0b93..5aa688d92 100644 --- a/arch/h8300/lib/checksum.c +++ b/arch/h8300/lib/checksum.c @@ -32,6 +32,7 @@ of the assembly has to go. */ #include +#include static inline unsigned short from32to16(unsigned long x) { diff --git a/arch/h8300/platform/h8s/ints.c b/arch/h8300/platform/h8s/ints.c index bbefac403..5441cdd12 100644 --- a/arch/h8300/platform/h8s/ints.c +++ b/arch/h8300/platform/h8s/ints.c @@ -22,6 +22,7 @@ #include #include #include +#include #include #include @@ -29,7 +30,6 @@ #include #include #include -#include #include #include diff --git a/arch/i386/Kconfig b/arch/i386/Kconfig index 15b003b50..3a3ba7fec 100644 --- a/arch/i386/Kconfig +++ b/arch/i386/Kconfig @@ -926,6 +926,74 @@ config REGPARM generate incorrect output with certain kernel constructs when -mregparm=3 is used. +config IRQSTACKS + bool "Use separate IRQ stacks" + help + If you say Y here the kernel will use a separate IRQ stack on each + cpu to handle interrupts. + +config STACK_SIZE_SHIFT + int "Kernel stack size (12 => 4KB, 13 => 8KB, 14 => 16KB)" + range 12 14 + default 12 if IRQSTACKS + default 13 + help + Select kernel stack size. 4KB stacks are best as they let + the system scale further. Use 8KB stacks if you have an + experimental kernel where a stack overlow with a 4KB stack + might occur. Use 16KB stacks if you want to safely support + Windows device drivers using either Linuxant or ndiswrapper. + +config STACK_WARN + int "Print stack trace when stack grows beyond specified bytes" + default 4096 if IRQSTACKS + default 4096 + help + The kernel will print a stack trace when the current stack exceeds + the specified size. + +config X86_STACK_CHECK + bool "Check for stack overflows" + default n + help + Say Y here to have the kernel attempt to detect when the per-task + kernel stack overflows. + + Some older versions of gcc don't handle the -p option correctly. + Kernprof is affected by the same problem, which is described here: + http://oss.sgi.com/projects/kernprof/faq.html#Q9 + + Basically, if you get oopses in __free_pages_ok during boot when + you have this turned on, you need to fix gcc. The Redhat 2.96 + version and gcc-3.x seem to work. + + If not debugging a stack overflow problem, say N + +config STACK_PANIC + int "Panic when stack approaches with specified bytes of the stack limit" + depends on X86_STACK_CHECK + default 512 if IRQSTACKS + default 512 + help + Panic if the stack grows to within specified byte range. + +config KEXEC + bool "kexec system call (EXPERIMENTAL)" + depends on EXPERIMENTAL + help + kexec is a system call that implements the ability to shutdown your + current kernel, and to start another kernel. It is like a reboot + but it is indepedent of the system firmware. And like a reboot + you can start any kernel with it, not just Linux. + + The name comes from the similiarity to the exec system call. + + It is an ongoing process to be certain the hardware in a machine + is properly shutdown, so do not be surprised if this code does not + initially work for you. It may help to enable device hotplugging + support. As of this writing the exact hardware interface is + strongly in flux, so no good recommendation can be made. + endmenu diff --git a/arch/i386/Kconfig.debug b/arch/i386/Kconfig.debug new file mode 100644 index 000000000..cf069b721 --- /dev/null +++ b/arch/i386/Kconfig.debug @@ -0,0 +1,80 @@ +menu "Kernel hacking" + +source "lib/Kconfig.debug" + +config EARLY_PRINTK + bool "Early printk" if EMBEDDED + default y + help + Write kernel log output directly into the VGA buffer or to a serial + port. + + This is useful for kernel debugging when your machine crashes very + early before the console code is initialized. For normal operation + it is not recommended because it looks ugly and doesn't cooperate + with klogd/syslogd or the X server. You should normally N here, + unless you want to debug such a crash. + +config DEBUG_STACKOVERFLOW + bool "Check for stack overflows" + depends on DEBUG_KERNEL + +config KPROBES + bool "Kprobes" + depends on DEBUG_KERNEL + help + Kprobes allows you to trap at almost any kernel address and + execute a callback function. register_kprobe() establishes + a probepoint and specifies the callback. Kprobes is useful + for kernel debugging, non-intrusive instrumentation and testing. + If in doubt, say "N". + +config DEBUG_STACK_USAGE + bool "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_PAGEALLOC + bool "Page alloc debugging" + depends on DEBUG_KERNEL + 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 4KSTACKS + bool "Use 4Kb for kernel stacks instead of 8Kb" + help + If you say Y here the kernel will use a 4Kb stacksize for the + kernel stack attached to each process/thread. This facilitates + running more threads on a system and also reduces the pressure + on the VM subsystem for higher order allocations. This option + will also use IRQ stacks to compensate for the reduced stackspace. + +config SCHEDSTATS + bool "Collect scheduler statistics" + depends on DEBUG_KERNEL && PROC_FS + help + If you say Y here, additional code will be inserted into the + scheduler and related routines to collect statistics about + scheduler behavior and provide them in /proc/schedstat. These + stats may be useful for both tuning and debugging the scheduler + If you aren't debugging the scheduler or trying to tune a specific + application, you can say N to avoid the very slight overhead + this adds. + +config X86_FIND_SMP_CONFIG + bool + depends on X86_LOCAL_APIC || X86_VOYAGER + default y + +config X86_MPPARSE + bool + depends on X86_LOCAL_APIC && !X86_VISWS + default y + +endmenu diff --git a/arch/i386/Kconfig.debug~ b/arch/i386/Kconfig.debug~ new file mode 100644 index 000000000..a4bd1523e --- /dev/null +++ b/arch/i386/Kconfig.debug~ @@ -0,0 +1,71 @@ +menu "Kernel hacking" + +source "lib/Kconfig.debug" + +config EARLY_PRINTK + bool "Early printk" if EMBEDDED + default y + help + Write kernel log output directly into the VGA buffer or to a serial + port. + + This is useful for kernel debugging when your machine crashes very + early before the console code is initialized. For normal operation + it is not recommended because it looks ugly and doesn't cooperate + with klogd/syslogd or the X server. You should normally N here, + unless you want to debug such a crash. + +config DEBUG_STACKOVERFLOW + bool "Check for stack overflows" + depends on DEBUG_KERNEL + +config KPROBES + bool "Kprobes" + depends on DEBUG_KERNEL + help + Kprobes allows you to trap at almost any kernel address and + execute a callback function. register_kprobe() establishes + a probepoint and specifies the callback. Kprobes is useful + for kernel debugging, non-intrusive instrumentation and testing. + If in doubt, say "N". + +config DEBUG_STACK_USAGE + bool "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_PAGEALLOC + bool "Page alloc debugging" + depends on DEBUG_KERNEL + 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 SCHEDSTATS + bool "Collect scheduler statistics" + depends on DEBUG_KERNEL && PROC_FS + help + If you say Y here, additional code will be inserted into the + scheduler and related routines to collect statistics about + scheduler behavior and provide them in /proc/schedstat. These + stats may be useful for both tuning and debugging the scheduler + If you aren't debugging the scheduler or trying to tune a specific + application, you can say N to avoid the very slight overhead + this adds. + +config X86_FIND_SMP_CONFIG + bool + depends on X86_LOCAL_APIC || X86_VOYAGER + default y + +config X86_MPPARSE + bool + depends on X86_LOCAL_APIC && !X86_VISWS + default y + +endmenu diff --git a/arch/i386/boot/.cvsignore b/arch/i386/boot/.cvsignore new file mode 100644 index 000000000..2d8a3afa4 --- /dev/null +++ b/arch/i386/boot/.cvsignore @@ -0,0 +1,4 @@ +bootsect +bzImage +setup +vmlinux.bin diff --git a/arch/i386/boot/compressed/.cvsignore b/arch/i386/boot/compressed/.cvsignore new file mode 100644 index 000000000..96b1b0022 --- /dev/null +++ b/arch/i386/boot/compressed/.cvsignore @@ -0,0 +1,3 @@ +vmlinux +vmlinux.bin +vmlinux.bin.gz diff --git a/arch/i386/boot/compressed/misc.c b/arch/i386/boot/compressed/misc.c index fa6704523..874568330 100644 --- a/arch/i386/boot/compressed/misc.c +++ b/arch/i386/boot/compressed/misc.c @@ -380,3 +380,6 @@ asmlinkage int decompress_kernel(struct moveparams *mv, void *rmode) if (high_loaded) close_output_buffer_if_we_run_high(mv); return high_loaded; } + +/* We don't actually check for stack overflows this early. */ +__asm__(".globl mcount ; mcount: ret\n"); diff --git a/arch/i386/boot/tools/.cvsignore b/arch/i386/boot/tools/.cvsignore new file mode 100644 index 000000000..378eac25d --- /dev/null +++ b/arch/i386/boot/tools/.cvsignore @@ -0,0 +1 @@ +build diff --git a/arch/i386/defconfig b/arch/i386/defconfig index aed3bc298..ed2bbb54d 100644 --- a/arch/i386/defconfig +++ b/arch/i386/defconfig @@ -1221,7 +1221,7 @@ CONFIG_OPROFILE=y CONFIG_EARLY_PRINTK=y CONFIG_DEBUG_SPINLOCK_SLEEP=y # CONFIG_FRAME_POINTER is not set -CONFIG_4KSTACKS=y +# CONFIG_4KSTACKS is not set CONFIG_X86_FIND_SMP_CONFIG=y CONFIG_X86_MPPARSE=y diff --git a/arch/i386/kernel/.cvsignore b/arch/i386/kernel/.cvsignore new file mode 100644 index 000000000..21c28761b --- /dev/null +++ b/arch/i386/kernel/.cvsignore @@ -0,0 +1,2 @@ +asm-offsets.s +vmlinux.lds.s diff --git a/arch/i386/kernel/Makefile b/arch/i386/kernel/Makefile index a056d5068..ab1ef80d1 100644 --- a/arch/i386/kernel/Makefile +++ b/arch/i386/kernel/Makefile @@ -23,6 +23,7 @@ obj-$(CONFIG_X86_TRAMPOLINE) += trampoline.o obj-$(CONFIG_X86_MPPARSE) += mpparse.o obj-$(CONFIG_X86_LOCAL_APIC) += apic.o nmi.o obj-$(CONFIG_X86_IO_APIC) += io_apic.o +obj-$(CONFIG_KEXEC) += machine_kexec.o relocate_kernel.o obj-$(CONFIG_X86_NUMAQ) += numaq.o obj-$(CONFIG_X86_SUMMIT_NUMA) += summit.o obj-$(CONFIG_MODULES) += module.o diff --git a/arch/i386/kernel/apic.c b/arch/i386/kernel/apic.c index ecf2b632f..eb4d41628 100644 --- a/arch/i386/kernel/apic.c +++ b/arch/i386/kernel/apic.c @@ -193,6 +193,36 @@ void disconnect_bsp_APIC(void) outb(0x70, 0x22); outb(0x00, 0x23); } + else { + /* Go back to Virtual Wire compatibility mode */ + unsigned long value; + + /* For the spurious interrupt use vector F, and enable it */ + value = apic_read(APIC_SPIV); + value &= ~APIC_VECTOR_MASK; + value |= APIC_SPIV_APIC_ENABLED; + value |= 0xf; + apic_write_around(APIC_SPIV, value); + + /* For LVT0 make it edge triggered, active high, external and enabled */ + value = apic_read(APIC_LVT0); + value &= ~(APIC_MODE_MASK | APIC_SEND_PENDING | + APIC_INPUT_POLARITY | APIC_LVT_REMOTE_IRR | + APIC_LVT_LEVEL_TRIGGER | APIC_LVT_MASKED ); + value |= APIC_LVT_REMOTE_IRR | APIC_SEND_PENDING; + value = SET_APIC_DELIVERY_MODE(value, APIC_MODE_EXINT); + apic_write_around(APIC_LVT0, value); + + /* For LVT1 make it edge triggered, active high, nmi and enabled */ + value = apic_read(APIC_LVT1); + value &= ~( + APIC_MODE_MASK | APIC_SEND_PENDING | + APIC_INPUT_POLARITY | APIC_LVT_REMOTE_IRR | + APIC_LVT_LEVEL_TRIGGER | APIC_LVT_MASKED); + value |= APIC_LVT_REMOTE_IRR | APIC_SEND_PENDING; + value = SET_APIC_DELIVERY_MODE(value, APIC_MODE_NMI); + apic_write_around(APIC_LVT1, value); + } } void disable_local_APIC(void) diff --git a/arch/i386/kernel/asm-offsets.c b/arch/i386/kernel/asm-offsets.c index 43943f871..b03f579a6 100644 --- a/arch/i386/kernel/asm-offsets.c +++ b/arch/i386/kernel/asm-offsets.c @@ -7,11 +7,11 @@ #include #include #include +#include #include #include "sigframe.h" #include #include -#include #define DEFINE(sym, val) \ asm volatile("\n->" #sym " %0 " #val : : "i" (val)) diff --git a/arch/i386/kernel/cpu/centaur.c b/arch/i386/kernel/cpu/centaur.c index e85f4827e..394814e57 100644 --- a/arch/i386/kernel/cpu/centaur.c +++ b/arch/i386/kernel/cpu/centaur.c @@ -286,23 +286,20 @@ static void __init init_c3(struct cpuinfo_x86 *c) c->x86_capability[5] = cpuid_edx(0xC0000001); } - switch (c->x86_model) { - case 6 ... 8: /* Cyrix III family */ - rdmsr (MSR_VIA_FCR, lo, hi); - lo |= (1<<1 | 1<<7); /* Report CX8 & enable PGE */ - wrmsr (MSR_VIA_FCR, lo, hi); - - set_bit(X86_FEATURE_CX8, c->x86_capability); - set_bit(X86_FEATURE_3DNOW, c->x86_capability); + /* Cyrix III family needs CX8 & PGE explicity enabled. */ + if (c->x86_model >=6 && c->x86_model <= 9) { + rdmsr (MSR_VIA_FCR, lo, hi); + lo |= (1<<1 | 1<<7); + wrmsr (MSR_VIA_FCR, lo, hi); + set_bit(X86_FEATURE_CX8, c->x86_capability); + } - /* fall through */ + /* Before Nehemiah, the C3's had 3dNOW! */ + if (c->x86_model >=6 && c->x86_model <9) + set_bit(X86_FEATURE_3DNOW, c->x86_capability); - case 9: /* Nehemiah */ - default: - get_model_name(c); - display_cacheinfo(c); - break; - } + get_model_name(c); + display_cacheinfo(c); } static void __init init_centaur(struct cpuinfo_x86 *c) diff --git a/arch/i386/kernel/cpu/cpufreq/Kconfig b/arch/i386/kernel/cpu/cpufreq/Kconfig index d285dc1ba..5cd444214 100644 --- a/arch/i386/kernel/cpu/cpufreq/Kconfig +++ b/arch/i386/kernel/cpu/cpufreq/Kconfig @@ -88,6 +88,11 @@ config X86_POWERNOW_K7 If in doubt, say N. +config X86_POWERNOW_K7_ACPI + bool + depends on ((X86_POWERNOW_K7 = "m" && ACPI_PROCESSOR) || (X86_POWERNOW_K7 = "y" && ACPI_PROCESSOR = "y")) + default y + config X86_POWERNOW_K8 tristate "AMD Opteron/Athlon64 PowerNow!" depends on CPU_FREQ && EXPERIMENTAL @@ -98,6 +103,11 @@ config X86_POWERNOW_K8 If in doubt, say N. +config X86_POWERNOW_K8_ACPI + bool + depends on ((X86_POWERNOW_K8 = "m" && ACPI_PROCESSOR) || (X86_POWERNOW_K8 = "y" && ACPI_PROCESSOR = "y")) + default y + config X86_GX_SUSPMOD tristate "Cyrix MediaGX/NatSemi Geode Suspend Modulation" depends on CPU_FREQ diff --git a/arch/i386/kernel/cpu/cpufreq/acpi.c b/arch/i386/kernel/cpu/cpufreq/acpi.c index 8c056882d..4ba912733 100644 --- a/arch/i386/kernel/cpu/cpufreq/acpi.c +++ b/arch/i386/kernel/cpu/cpufreq/acpi.c @@ -108,13 +108,27 @@ acpi_processor_set_performance ( u32 value = 0; int i = 0; struct cpufreq_freqs cpufreq_freqs; + cpumask_t saved_mask; + int retval; ACPI_FUNCTION_TRACE("acpi_processor_set_performance"); + /* + * TBD: Use something other than set_cpus_allowed. + * As set_cpus_allowed is a bit racy, + * with any other set_cpus_allowed for this process. + */ + saved_mask = current->cpus_allowed; + set_cpus_allowed(current, cpumask_of_cpu(cpu)); + if (smp_processor_id() != cpu) { + return_VALUE(-EAGAIN); + } + if (state == data->acpi_data.state) { ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Already at target state (P%d)\n", state)); - return_VALUE(0); + retval = 0; + goto migrate_end; } ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Transitioning from P%d to P%d\n", @@ -144,7 +158,8 @@ acpi_processor_set_performance ( if (ret) { ACPI_DEBUG_PRINT((ACPI_DB_WARN, "Invalid port width 0x%04x\n", bit_width)); - return_VALUE(ret); + retval = ret; + goto migrate_end; } /* @@ -166,7 +181,8 @@ acpi_processor_set_performance ( if (ret) { ACPI_DEBUG_PRINT((ACPI_DB_WARN, "Invalid port width 0x%04x\n", bit_width)); - return_VALUE(ret); + retval = ret; + goto migrate_end; } if (value == (u32) data->acpi_data.states[state].status) break; @@ -183,7 +199,8 @@ acpi_processor_set_performance ( cpufreq_notify_transition(&cpufreq_freqs, CPUFREQ_PRECHANGE); cpufreq_notify_transition(&cpufreq_freqs, CPUFREQ_POSTCHANGE); ACPI_DEBUG_PRINT((ACPI_DB_WARN, "Transition failed\n")); - return_VALUE(-ENODEV); + retval = -ENODEV; + goto migrate_end; } ACPI_DEBUG_PRINT((ACPI_DB_INFO, @@ -192,7 +209,10 @@ acpi_processor_set_performance ( data->acpi_data.state = state; - return_VALUE(0); + retval = 0; +migrate_end: + set_cpus_allowed(current, saved_mask); + return_VALUE(retval); } @@ -266,6 +286,69 @@ acpi_cpufreq_guess_freq ( } + +/* + * acpi_processor_cpu_init_pdc_est - let BIOS know about the SMP capabilities + * of this driver + * @perf: processor-specific acpi_io_data struct + * @cpu: CPU being initialized + * + * To avoid issues with legacy OSes, some BIOSes require to be informed of + * the SMP capabilities of OS P-state driver. Here we set the bits in _PDC + * accordingly, for Enhanced Speedstep. Actual call to _PDC is done in + * driver/acpi/processor.c + */ +static void +acpi_processor_cpu_init_pdc_est( + struct acpi_processor_performance *perf, + unsigned int cpu, + struct acpi_object_list *obj_list + ) +{ + union acpi_object *obj; + u32 *buf; + struct cpuinfo_x86 *c = cpu_data + cpu; + ACPI_FUNCTION_TRACE("acpi_processor_cpu_init_pdc_est"); + + if (!cpu_has(c, X86_FEATURE_EST)) + return_VOID; + + /* Initialize pdc. It will be used later. */ + if (!obj_list) + return_VOID; + + if (!(obj_list->count && obj_list->pointer)) + return_VOID; + + obj = obj_list->pointer; + if ((obj->buffer.length == 12) && obj->buffer.pointer) { + buf = (u32 *)obj->buffer.pointer; + buf[0] = ACPI_PDC_REVISION_ID; + buf[1] = 1; + buf[2] = ACPI_PDC_EST_CAPABILITY_SMP; + perf->pdc = obj_list; + } + return_VOID; +} + + +/* CPU specific PDC initialization */ +static void +acpi_processor_cpu_init_pdc( + struct acpi_processor_performance *perf, + unsigned int cpu, + struct acpi_object_list *obj_list + ) +{ + struct cpuinfo_x86 *c = cpu_data + cpu; + ACPI_FUNCTION_TRACE("acpi_processor_cpu_init_pdc"); + perf->pdc = NULL; + if (cpu_has(c, X86_FEATURE_EST)) + acpi_processor_cpu_init_pdc_est(perf, cpu, obj_list); + return_VOID; +} + + static int acpi_cpufreq_cpu_init ( struct cpufreq_policy *policy) @@ -275,7 +358,14 @@ acpi_cpufreq_cpu_init ( struct cpufreq_acpi_io *data; unsigned int result = 0; + union acpi_object arg0 = {ACPI_TYPE_BUFFER}; + u32 arg0_buf[3]; + struct acpi_object_list arg_list = {1, &arg0}; + ACPI_FUNCTION_TRACE("acpi_cpufreq_cpu_init"); + /* setup arg_list for _PDC settings */ + arg0.buffer.length = 12; + arg0.buffer.pointer = (u8 *) arg0_buf; data = kmalloc(sizeof(struct cpufreq_acpi_io), GFP_KERNEL); if (!data) @@ -284,7 +374,10 @@ acpi_cpufreq_cpu_init ( acpi_io_data[cpu] = data; + acpi_processor_cpu_init_pdc(&data->acpi_data, cpu, &arg_list); result = acpi_processor_register_performance(&data->acpi_data, cpu); + data->acpi_data.pdc = NULL; + if (result) goto err_free; diff --git a/arch/i386/kernel/cpu/intel_cacheinfo.c b/arch/i386/kernel/cpu/intel_cacheinfo.c new file mode 100644 index 000000000..2bba6df81 --- /dev/null +++ b/arch/i386/kernel/cpu/intel_cacheinfo.c @@ -0,0 +1,140 @@ +#include +#include + +#define LVL_1_INST 1 +#define LVL_1_DATA 2 +#define LVL_2 3 +#define LVL_3 4 +#define LVL_TRACE 5 + +struct _cache_table +{ + unsigned char descriptor; + char cache_type; + short size; +}; + +/* all the cache descriptor types we care about (no TLB or trace cache entries) */ +static struct _cache_table cache_table[] __initdata = +{ + { 0x06, LVL_1_INST, 8 }, /* 4-way set assoc, 32 byte line size */ + { 0x08, LVL_1_INST, 16 }, /* 4-way set assoc, 32 byte line size */ + { 0x0a, LVL_1_DATA, 8 }, /* 2 way set assoc, 32 byte line size */ + { 0x0c, LVL_1_DATA, 16 }, /* 4-way set assoc, 32 byte line size */ + { 0x22, LVL_3, 512 }, /* 4-way set assoc, sectored cache, 64 byte line size */ + { 0x23, LVL_3, 1024 }, /* 8-way set assoc, sectored cache, 64 byte line size */ + { 0x25, LVL_3, 2048 }, /* 8-way set assoc, sectored cache, 64 byte line size */ + { 0x29, LVL_3, 4096 }, /* 8-way set assoc, sectored cache, 64 byte line size */ + { 0x2c, LVL_1_DATA, 32 }, /* 8-way set assoc, 64 byte line size */ + { 0x30, LVL_1_INST, 32 }, /* 8-way set assoc, 64 byte line size */ + { 0x39, LVL_2, 128 }, /* 4-way set assoc, sectored cache, 64 byte line size */ + { 0x3b, LVL_2, 128 }, /* 2-way set assoc, sectored cache, 64 byte line size */ + { 0x3c, LVL_2, 256 }, /* 4-way set assoc, sectored cache, 64 byte line size */ + { 0x41, LVL_2, 128 }, /* 4-way set assoc, 32 byte line size */ + { 0x42, LVL_2, 256 }, /* 4-way set assoc, 32 byte line size */ + { 0x43, LVL_2, 512 }, /* 4-way set assoc, 32 byte line size */ + { 0x44, LVL_2, 1024 }, /* 4-way set assoc, 32 byte line size */ + { 0x45, LVL_2, 2048 }, /* 4-way set assoc, 32 byte line size */ + { 0x60, LVL_1_DATA, 16 }, /* 8-way set assoc, sectored cache, 64 byte line size */ + { 0x66, LVL_1_DATA, 8 }, /* 4-way set assoc, sectored cache, 64 byte line size */ + { 0x67, LVL_1_DATA, 16 }, /* 4-way set assoc, sectored cache, 64 byte line size */ + { 0x68, LVL_1_DATA, 32 }, /* 4-way set assoc, sectored cache, 64 byte line size */ + { 0x70, LVL_TRACE, 12 }, /* 8-way set assoc */ + { 0x71, LVL_TRACE, 16 }, /* 8-way set assoc */ + { 0x72, LVL_TRACE, 32 }, /* 8-way set assoc */ + { 0x78, LVL_2, 1024 }, /* 4-way set assoc, 64 byte line size */ + { 0x79, LVL_2, 128 }, /* 8-way set assoc, sectored cache, 64 byte line size */ + { 0x7a, LVL_2, 256 }, /* 8-way set assoc, sectored cache, 64 byte line size */ + { 0x7b, LVL_2, 512 }, /* 8-way set assoc, sectored cache, 64 byte line size */ + { 0x7c, LVL_2, 1024 }, /* 8-way set assoc, sectored cache, 64 byte line size */ + { 0x7d, LVL_2, 2048 }, /* 8-way set assoc, 64 byte line size */ + { 0x7f, LVL_2, 512 }, /* 2-way set assoc, 64 byte line size */ + { 0x82, LVL_2, 256 }, /* 8-way set assoc, 32 byte line size */ + { 0x83, LVL_2, 512 }, /* 8-way set assoc, 32 byte line size */ + { 0x84, LVL_2, 1024 }, /* 8-way set assoc, 32 byte line size */ + { 0x85, LVL_2, 2048 }, /* 8-way set assoc, 32 byte line size */ + { 0x86, LVL_2, 512 }, /* 4-way set assoc, 64 byte line size */ + { 0x87, LVL_2, 1024 }, /* 8-way set assoc, 64 byte line size */ + { 0x00, 0, 0} +}; + +unsigned int init_intel_cacheinfo(struct cpuinfo_x86 *c) +{ + unsigned int trace = 0, l1i = 0, l1d = 0, l2 = 0, l3 = 0; /* Cache sizes */ + + if (c->cpuid_level > 1) { + /* supports eax=2 call */ + int i, j, n; + int regs[4]; + unsigned char *dp = (unsigned char *)regs; + + /* Number of times to iterate */ + n = cpuid_eax(2) & 0xFF; + + for ( i = 0 ; i < n ; i++ ) { + cpuid(2, ®s[0], ®s[1], ®s[2], ®s[3]); + + /* If bit 31 is set, this is an unknown format */ + for ( j = 0 ; j < 3 ; j++ ) { + if ( regs[j] < 0 ) regs[j] = 0; + } + + /* Byte 0 is level count, not a descriptor */ + for ( j = 1 ; j < 16 ; j++ ) { + unsigned char des = dp[j]; + unsigned char k = 0; + + /* look up this descriptor in the table */ + while (cache_table[k].descriptor != 0) + { + if (cache_table[k].descriptor == des) { + switch (cache_table[k].cache_type) { + case LVL_1_INST: + l1i += cache_table[k].size; + break; + case LVL_1_DATA: + l1d += cache_table[k].size; + break; + case LVL_2: + l2 += cache_table[k].size; + break; + case LVL_3: + l3 += cache_table[k].size; + break; + case LVL_TRACE: + trace += cache_table[k].size; + break; + } + + break; + } + + k++; + } + } + } + + if ( trace ) + printk (KERN_INFO "CPU: Trace cache: %dK uops", trace); + else if ( l1i ) + printk (KERN_INFO "CPU: L1 I cache: %dK", l1i); + if ( l1d ) + printk(", L1 D cache: %dK\n", l1d); + else + printk("\n"); + if ( l2 ) + printk(KERN_INFO "CPU: L2 cache: %dK\n", l2); + if ( l3 ) + printk(KERN_INFO "CPU: L3 cache: %dK\n", l3); + + /* + * This assumes the L3 cache is shared; it typically lives in + * the northbridge. The L1 caches are included by the L2 + * cache, and so should not be included for the purpose of + * SMP switching weights. + */ + c->x86_cache_size = l2 ? l2 : (l1i+l1d); + } + + return l2; +} diff --git a/arch/i386/kernel/cpu/mtrr/mtrr.h b/arch/i386/kernel/cpu/mtrr/mtrr.h index fee8b5ccc..0d10c115d 100644 --- a/arch/i386/kernel/cpu/mtrr/mtrr.h +++ b/arch/i386/kernel/cpu/mtrr/mtrr.h @@ -52,7 +52,6 @@ struct mtrr_ops { }; extern int generic_get_free_region(unsigned long base, unsigned long size); -extern void generic_init_secondary(void); extern int generic_validate_add_page(unsigned long base, unsigned long size, unsigned int type); diff --git a/arch/i386/kernel/entry.S b/arch/i386/kernel/entry.S index 3ac74183c..dfbade1b9 100644 --- a/arch/i386/kernel/entry.S +++ b/arch/i386/kernel/entry.S @@ -1029,8 +1029,55 @@ ENTRY(sys_call_table) .long sys_mq_timedreceive /* 280 */ .long sys_mq_notify .long sys_mq_getsetattr - .long sys_ni_syscall /* reserved for kexec */ + .long sys_kexec_load .long sys_ioprio_set .long sys_ioprio_get /* 285 */ syscall_table_size=(.-sys_call_table) + +#ifdef CONFIG_X86_STACK_CHECK +.data +.globl stack_overflowed +stack_overflowed: + .long 0 +.text + +ENTRY(mcount) +#warning stack check enabled + push %eax + movl $(THREAD_SIZE - 1),%eax + andl %esp,%eax + cmpl $STACK_WARN,%eax + jle 1f +2: + popl %eax + ret +1: + /* prevent infinite recursion from call to mcount from the + * stack_overflow function. Need to revisit this code for + * SMP based systems. + */ + lock; btsl $0,stack_overflowed + jc 2b + + /* prepare to jmp to stack_overflow directly, as if it were + * called directly by the caller of mcount. + */ + pushl %ebp + pushl %ebx + pushl %esi + pushl %edi + + call stack_overflow + /* Note that stack_overflow() will clear the stack_overflowed + * variable. + */ + + popl %edi + popl %esi + popl %ebx + popl %ebp + + popl %eax + ret +#endif diff --git a/arch/i386/kernel/i386_ksyms.c b/arch/i386/kernel/i386_ksyms.c index 5a50c536d..584982c3e 100644 --- a/arch/i386/kernel/i386_ksyms.c +++ b/arch/i386/kernel/i386_ksyms.c @@ -188,6 +188,12 @@ EXPORT_SYMBOL(atomic_dec_and_lock); EXPORT_SYMBOL(__PAGE_KERNEL); +#ifdef CONFIG_X86_STACK_CHECK +extern void mcount(void); +EXPORT_SYMBOL(mcount); +#endif + + #ifdef CONFIG_HIGHMEM EXPORT_SYMBOL(kmap); EXPORT_SYMBOL(kunmap); diff --git a/arch/i386/kernel/i8259.c b/arch/i386/kernel/i8259.c index 97653d20f..7141d27ec 100644 --- a/arch/i386/kernel/i8259.c +++ b/arch/i386/kernel/i8259.c @@ -244,9 +244,21 @@ static int i8259A_resume(struct sys_device *dev) return 0; } +static int i8259A_shutdown(struct sys_device *dev) +{ + /* Put the i8259A into a quiescent state that + * the kernel initialization code can get it + * out of. + */ + outb(0xff, 0x21); /* mask all of 8259A-1 */ + outb(0xff, 0xA1); /* mask all of 8259A-1 */ + return 0; +} + static struct sysdev_class i8259_sysdev_class = { set_kset_name("i8259"), .resume = i8259A_resume, + .shutdown = i8259A_shutdown, }; static struct sys_device device_i8259A = { diff --git a/arch/i386/kernel/init_task.c b/arch/i386/kernel/init_task.c index 7422d73ee..30cfd4085 100644 --- a/arch/i386/kernel/init_task.c +++ b/arch/i386/kernel/init_task.c @@ -29,6 +29,13 @@ union thread_union init_thread_union __attribute__((__section__(".data.init_task"))) = { INIT_THREAD_INFO(init_task, init_thread_union) }; +#ifdef CONFIG_X86_STACK_CHECK +union thread_union stack_overflow_stack + __attribute__((__section__(".data.init_task"))) = + { INIT_THREAD_INFO(init_task, stack_overflow_stack) }; +#endif + + /* * Initial task structure. * diff --git a/arch/i386/kernel/io_apic.c b/arch/i386/kernel/io_apic.c index 39af35d19..f600e6799 100644 --- a/arch/i386/kernel/io_apic.c +++ b/arch/i386/kernel/io_apic.c @@ -1604,11 +1604,42 @@ static void __init enable_IO_APIC(void) */ void disable_IO_APIC(void) { + int pin; /* * Clear the IO-APIC before rebooting: */ clear_IO_APIC(); + /* + * If the i82559 is routed through an IOAPIC + * Put that IOAPIC in virtual wire mode + * so legacy interrups can be delivered. + */ + pin = find_isa_irq_pin(0, mp_ExtINT); + if (pin != -1) { + struct IO_APIC_route_entry entry; + unsigned long flags; + + memset(&entry, 0, sizeof(entry)); + entry.mask = 0; /* Enabled */ + entry.trigger = 0; /* Edge */ + entry.irr = 0; + entry.polarity = 0; /* High */ + entry.delivery_status = 0; + entry.dest_mode = 0; /* Physical */ + entry.delivery_mode = 7; /* ExtInt */ + entry.vector = 0; + entry.dest.physical.physical_dest = 0; + + + /* + * Add it to the IO-APIC irq-routing table: + */ + spin_lock_irqsave(&ioapic_lock, flags); + io_apic_write(0, 0x11+2*pin, *(((int *)&entry)+1)); + io_apic_write(0, 0x10+2*pin, *(((int *)&entry)+0)); + spin_unlock_irqrestore(&ioapic_lock, flags); + } disconnect_bsp_APIC(); } diff --git a/arch/i386/kernel/ioport.c b/arch/i386/kernel/ioport.c index 3346657c3..8b2516039 100644 --- a/arch/i386/kernel/ioport.c +++ b/arch/i386/kernel/ioport.c @@ -56,6 +56,7 @@ static void set_bitmap(unsigned long *bitmap, unsigned int base, unsigned int ex */ asmlinkage long sys_ioperm(unsigned long from, unsigned long num, int turn_on) { + unsigned long i, max_long, bytes, bytes_updated; struct thread_struct * t = ¤t->thread; struct tss_struct * tss; unsigned long *bitmap; @@ -81,16 +82,37 @@ asmlinkage long sys_ioperm(unsigned long from, unsigned long num, int turn_on) /* * do it in the per-thread copy and in the TSS ... + * + * Disable preemption via get_cpu() - we must not switch away + * because the ->io_bitmap_max value must match the bitmap + * contents: */ + tss = &per_cpu(init_tss, get_cpu()); + set_bitmap(t->io_bitmap_ptr, from, num, !turn_on); - tss = init_tss + get_cpu(); - if (tss->io_bitmap_base == IO_BITMAP_OFFSET) { /* already active? */ - set_bitmap(tss->io_bitmap, from, num, !turn_on); - } else { - memcpy(tss->io_bitmap, t->io_bitmap_ptr, IO_BITMAP_BYTES); - tss->io_bitmap_base = IO_BITMAP_OFFSET; /* Activate it in the TSS */ - } + + /* + * Search for a (possibly new) maximum. This is simple and stupid, + * to keep it obviously correct: + */ + max_long = 0; + for (i = 0; i < IO_BITMAP_LONGS; i++) + if (t->io_bitmap_ptr[i] != ~0UL) + max_long = i; + + bytes = (max_long + 1) * sizeof(long); + bytes_updated = max(bytes, t->io_bitmap_max); + + t->io_bitmap_max = bytes; + + /* + * Sets the lazy trigger so that the next I/O operation will + * reload the correct bitmap. + */ + tss->io_bitmap_base = INVALID_IO_BITMAP_OFFSET_LAZY; + put_cpu(); + return 0; } diff --git a/arch/i386/kernel/irq.c b/arch/i386/kernel/irq.c index 22f7fc771..1c8bedaeb 100644 --- a/arch/i386/kernel/irq.c +++ b/arch/i386/kernel/irq.c @@ -76,8 +76,10 @@ static void register_irq_proc (unsigned int irq); /* * per-CPU IRQ handling stacks */ +#ifdef CONFIG_IRQSTACKS union irq_ctx *hardirq_ctx[NR_CPUS]; union irq_ctx *softirq_ctx[NR_CPUS]; +#endif /* * Special irq handlers. @@ -220,6 +222,9 @@ asmlinkage int handle_IRQ_event(unsigned int irq, int status = 1; /* Force the "do bottom halves" bit */ int retval = 0; + if (!(action->flags & SA_INTERRUPT)) + local_irq_enable(); + do { status |= action->flags; retval |= action->handler(irq, action->dev_id, regs); @@ -489,10 +494,12 @@ asmlinkage unsigned int do_IRQ(struct pt_regs regs) u32 *isp; union irq_ctx * curctx; union irq_ctx * irqctx; - +#ifdef CONFIG_IRQSTACKS curctx = (union irq_ctx *) current_thread_info(); irqctx = hardirq_ctx[smp_processor_id()]; - +#else + curctx = irqctx = (union irq_ctx *)0; +#endif spin_unlock(&desc->lock); /* @@ -536,7 +543,6 @@ asmlinkage unsigned int do_IRQ(struct pt_regs regs) break; desc->status &= ~IRQ_PENDING; } - desc->status &= ~IRQ_INPROGRESS; out: @@ -1095,6 +1101,7 @@ void init_irq_proc (void) } +#ifdef CONFIG_IRQSTACKS /* * These should really be __section__(".bss.page_aligned") as well, but * gcc's 3.0 and earlier don't handle that correctly. @@ -1174,3 +1181,4 @@ asmlinkage void do_softirq(void) } EXPORT_SYMBOL(do_softirq); +#endif diff --git a/arch/i386/kernel/kprobes.c b/arch/i386/kernel/kprobes.c new file mode 100644 index 000000000..4d066cc30 --- /dev/null +++ b/arch/i386/kernel/kprobes.c @@ -0,0 +1,349 @@ +/* + * Kernel Probes (KProbes) + * arch/i386/kernel/kprobes.c + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * Copyright (C) IBM Corporation, 2002, 2004 + * + * 2002-Oct Created by Vamsi Krishna S Kernel + * Probes initial implementation ( includes contributions from + * Rusty Russell). + * 2004-July Suparna Bhattacharya added jumper probes + * interface to access function arguments. + */ + +#include +#include +#include +#include +#include +#include + +/* kprobe_status settings */ +#define KPROBE_HIT_ACTIVE 0x00000001 +#define KPROBE_HIT_SS 0x00000002 + +static struct kprobe *current_kprobe; +static unsigned long kprobe_status, kprobe_old_eflags, kprobe_saved_eflags; +static struct pt_regs jprobe_saved_regs; +static long *jprobe_saved_esp; +/* copy of the kernel stack at the probe fire time */ +static kprobe_opcode_t jprobes_stack[MAX_STACK_SIZE]; + +/* + * returns non-zero if opcode modifies the interrupt flag. + */ +static inline int is_IF_modifier(kprobe_opcode_t opcode) +{ + switch (opcode) { + case 0xfa: /* cli */ + case 0xfb: /* sti */ + case 0xcf: /* iret/iretd */ + case 0x9d: /* popf/popfd */ + return 1; + } + return 0; +} + +void arch_prepare_kprobe(struct kprobe *p) +{ + memcpy(p->insn, p->addr, MAX_INSN_SIZE * sizeof(kprobe_opcode_t)); +} + +static inline void disarm_kprobe(struct kprobe *p, struct pt_regs *regs) +{ + *p->addr = p->opcode; + regs->eip = (unsigned long)p->addr; +} + +static inline void prepare_singlestep(struct kprobe *p, struct pt_regs *regs) +{ + regs->eflags |= TF_MASK; + regs->eflags &= ~IF_MASK; + regs->eip = (unsigned long)&p->insn; +} + +/* + * Interrupts are disabled on entry as trap3 is an interrupt gate and they + * remain disabled thorough out this function. + */ +static inline int kprobe_handler(struct pt_regs *regs) +{ + struct kprobe *p; + int ret = 0; + u8 *addr = (u8 *) (regs->eip - 1); + + /* We're in an interrupt, but this is clear and BUG()-safe. */ + preempt_disable(); + + /* Check we're not actually recursing */ + if (kprobe_running()) { + /* We *are* holding lock here, so this is safe. + Disarm the probe we just hit, and ignore it. */ + p = get_kprobe(addr); + if (p) { + disarm_kprobe(p, regs); + ret = 1; + } else { + p = current_kprobe; + if (p->break_handler && p->break_handler(p, regs)) { + goto ss_probe; + } + } + /* If it's not ours, can't be delete race, (we hold lock). */ + goto no_kprobe; + } + + lock_kprobes(); + p = get_kprobe(addr); + if (!p) { + unlock_kprobes(); + if (*addr != BREAKPOINT_INSTRUCTION) { + /* + * The breakpoint instruction was removed right + * after we hit it. Another cpu has removed + * either a probepoint or a debugger breakpoint + * at this address. In either case, no further + * handling of this interrupt is appropriate. + */ + ret = 1; + } + /* Not one of ours: let kernel handle it */ + goto no_kprobe; + } + + kprobe_status = KPROBE_HIT_ACTIVE; + current_kprobe = p; + kprobe_saved_eflags = kprobe_old_eflags + = (regs->eflags & (TF_MASK | IF_MASK)); + if (is_IF_modifier(p->opcode)) + kprobe_saved_eflags &= ~IF_MASK; + + if (p->pre_handler(p, regs)) { + /* handler has already set things up, so skip ss setup */ + return 1; + } + + ss_probe: + prepare_singlestep(p, regs); + kprobe_status = KPROBE_HIT_SS; + return 1; + + no_kprobe: + preempt_enable_no_resched(); + return ret; +} + +/* + * Called after single-stepping. p->addr is the address of the + * instruction whose first byte has been replaced by the "int 3" + * instruction. To avoid the SMP problems that can occur when we + * temporarily put back the original opcode to single-step, we + * single-stepped a copy of the instruction. The address of this + * copy is p->insn. + * + * This function prepares to return from the post-single-step + * interrupt. We have to fix up the stack as follows: + * + * 0) Except in the case of absolute or indirect jump or call instructions, + * the new eip is relative to the copied instruction. We need to make + * it relative to the original instruction. + * + * 1) If the single-stepped instruction was pushfl, then the TF and IF + * flags are set in the just-pushed eflags, and may need to be cleared. + * + * 2) If the single-stepped instruction was a call, the return address + * that is atop the stack is the address following the copied instruction. + * We need to make it the address following the original instruction. + */ +static void resume_execution(struct kprobe *p, struct pt_regs *regs) +{ + unsigned long *tos = (unsigned long *)®s->esp; + unsigned long next_eip = 0; + unsigned long copy_eip = (unsigned long)&p->insn; + unsigned long orig_eip = (unsigned long)p->addr; + + switch (p->insn[0]) { + case 0x9c: /* pushfl */ + *tos &= ~(TF_MASK | IF_MASK); + *tos |= kprobe_old_eflags; + break; + case 0xe8: /* call relative - Fix return addr */ + *tos = orig_eip + (*tos - copy_eip); + break; + case 0xff: + if ((p->insn[1] & 0x30) == 0x10) { + /* call absolute, indirect */ + /* Fix return addr; eip is correct. */ + next_eip = regs->eip; + *tos = orig_eip + (*tos - copy_eip); + } else if (((p->insn[1] & 0x31) == 0x20) || /* jmp near, absolute indirect */ + ((p->insn[1] & 0x31) == 0x21)) { /* jmp far, absolute indirect */ + /* eip is correct. */ + next_eip = regs->eip; + } + break; + case 0xea: /* jmp absolute -- eip is correct */ + next_eip = regs->eip; + break; + default: + break; + } + + regs->eflags &= ~TF_MASK; + if (next_eip) { + regs->eip = next_eip; + } else { + regs->eip = orig_eip + (regs->eip - copy_eip); + } +} + +/* + * Interrupts are disabled on entry as trap1 is an interrupt gate and they + * remain disabled thoroughout this function. And we hold kprobe lock. + */ +static inline int post_kprobe_handler(struct pt_regs *regs) +{ + if (!kprobe_running()) + return 0; + + if (current_kprobe->post_handler) + current_kprobe->post_handler(current_kprobe, regs, 0); + + resume_execution(current_kprobe, regs); + regs->eflags |= kprobe_saved_eflags; + + unlock_kprobes(); + preempt_enable_no_resched(); + + /* + * if somebody else is singlestepping across a probe point, eflags + * will have TF set, in which case, continue the remaining processing + * of do_debug, as if this is not a probe hit. + */ + if (regs->eflags & TF_MASK) + return 0; + + return 1; +} + +/* Interrupts disabled, kprobe_lock held. */ +static inline int kprobe_fault_handler(struct pt_regs *regs, int trapnr) +{ + if (current_kprobe->fault_handler + && current_kprobe->fault_handler(current_kprobe, regs, trapnr)) + return 1; + + if (kprobe_status & KPROBE_HIT_SS) { + resume_execution(current_kprobe, regs); + regs->eflags |= kprobe_old_eflags; + + unlock_kprobes(); + preempt_enable_no_resched(); + } + return 0; +} + +/* + * Wrapper routine to for handling exceptions. + */ +int kprobe_exceptions_notify(struct notifier_block *self, unsigned long val, + void *data) +{ + struct die_args *args = (struct die_args *)data; + switch (val) { + case DIE_INT3: + if (kprobe_handler(args->regs)) + return NOTIFY_STOP; + break; + case DIE_DEBUG: + if (post_kprobe_handler(args->regs)) + return NOTIFY_STOP; + break; + case DIE_GPF: + if (kprobe_running() && + kprobe_fault_handler(args->regs, args->trapnr)) + return NOTIFY_STOP; + break; + case DIE_PAGE_FAULT: + if (kprobe_running() && + kprobe_fault_handler(args->regs, args->trapnr)) + return NOTIFY_STOP; + break; + default: + break; + } + return NOTIFY_DONE; +} + +int setjmp_pre_handler(struct kprobe *p, struct pt_regs *regs) +{ + struct jprobe *jp = container_of(p, struct jprobe, kp); + unsigned long addr; + + jprobe_saved_regs = *regs; + jprobe_saved_esp = ®s->esp; + addr = (unsigned long)jprobe_saved_esp; + + /* + * TBD: As Linus pointed out, gcc assumes that the callee + * owns the argument space and could overwrite it, e.g. + * tailcall optimization. So, to be absolutely safe + * we also save and restore enough stack bytes to cover + * the argument area. + */ + memcpy(jprobes_stack, (kprobe_opcode_t *) addr, MIN_STACK_SIZE(addr)); + regs->eflags &= ~IF_MASK; + regs->eip = (unsigned long)(jp->entry); + return 1; +} + +void jprobe_return(void) +{ + preempt_enable_no_resched(); + asm volatile (" xchgl %%ebx,%%esp \n" + " int3 \n"::"b" + (jprobe_saved_esp):"memory"); +} +void jprobe_return_end(void) +{ +}; + +int longjmp_break_handler(struct kprobe *p, struct pt_regs *regs) +{ + u8 *addr = (u8 *) (regs->eip - 1); + unsigned long stack_addr = (unsigned long)jprobe_saved_esp; + struct jprobe *jp = container_of(p, struct jprobe, kp); + + if ((addr > (u8 *) jprobe_return) && (addr < (u8 *) jprobe_return_end)) { + if (®s->esp != jprobe_saved_esp) { + struct pt_regs *saved_regs = + container_of(jprobe_saved_esp, struct pt_regs, esp); + printk("current esp %p does not match saved esp %p\n", + ®s->esp, jprobe_saved_esp); + printk("Saved registers for jprobe %p\n", jp); + show_registers(saved_regs); + printk("Current registers\n"); + show_registers(regs); + BUG(); + } + *regs = jprobe_saved_regs; + memcpy((kprobe_opcode_t *) stack_addr, jprobes_stack, + MIN_STACK_SIZE(stack_addr)); + return 1; + } + return 0; +} diff --git a/arch/i386/kernel/machine_kexec.c b/arch/i386/kernel/machine_kexec.c new file mode 100644 index 000000000..3a9e878f8 --- /dev/null +++ b/arch/i386/kernel/machine_kexec.c @@ -0,0 +1,208 @@ +/* + * machine_kexec.c - handle transition of Linux booting another kernel + * Copyright (C) 2002-2004 Eric Biederman + * + * This source code is licensed under the GNU General Public License, + * Version 2. See the file COPYING for more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static inline unsigned long read_cr3(void) +{ + unsigned long cr3; + asm volatile("movl %%cr3,%0": "=r"(cr3)); + return cr3; +} + +#define PAGE_ALIGNED __attribute__ ((__aligned__(PAGE_SIZE))) + +#define L0_ATTR (_PAGE_PRESENT | _PAGE_RW | _PAGE_ACCESSED | _PAGE_DIRTY) +#define L1_ATTR (_PAGE_PRESENT | _PAGE_RW | _PAGE_ACCESSED | _PAGE_DIRTY) +#define L2_ATTR (_PAGE_PRESENT) + +#define LEVEL0_SIZE (1UL << 12UL) + +#ifndef CONFIG_X86_PAE +#define LEVEL1_SIZE (1UL << 22UL) +static u32 pgtable_level1[1024] PAGE_ALIGNED; + +static void identity_map_page(unsigned long address) +{ + unsigned long level1_index, level2_index; + u32 *pgtable_level2; + + /* Find the current page table */ + pgtable_level2 = __va(read_cr3()); + + /* Find the indexes of the physical address to identity map */ + level1_index = (address % LEVEL1_SIZE)/LEVEL0_SIZE; + level2_index = address / LEVEL1_SIZE; + + /* Identity map the page table entry */ + pgtable_level1[level1_index] = address | L0_ATTR; + pgtable_level2[level2_index] = __pa(pgtable_level1) | L1_ATTR; + + /* Flush the tlb so the new mapping takes effect. + * Global tlb entries are not flushed but that is not an issue. + */ + load_cr3(pgtable_level2); +} + +#else +#define LEVEL1_SIZE (1UL << 21UL) +#define LEVEL2_SIZE (1UL << 30UL) +static u64 pgtable_level1[512] PAGE_ALIGNED; +static u64 pgtable_level2[512] PAGE_ALIGNED; + +static void identity_map_page(unsigned long address) +{ + unsigned long level1_index, level2_index, level3_index; + u64 *pgtable_level3; + + /* Find the current page table */ + pgtable_level3 = __va(read_cr3()); + + /* Find the indexes of the physical address to identity map */ + level1_index = (address % LEVEL1_SIZE)/LEVEL0_SIZE; + level2_index = (address % LEVEL2_SIZE)/LEVEL1_SIZE; + level3_index = address / LEVEL2_SIZE; + + /* Identity map the page table entry */ + pgtable_level1[level1_index] = address | L0_ATTR; + pgtable_level2[level2_index] = __pa(pgtable_level1) | L1_ATTR; + set_64bit(&pgtable_level3[level3_index], __pa(pgtable_level2) | L2_ATTR); + + /* Flush the tlb so the new mapping takes effect. + * Global tlb entries are not flushed but that is not an issue. + */ + load_cr3(pgtable_level3); +} +#endif + + +static void set_idt(void *newidt, __u16 limit) +{ + unsigned char curidt[6]; + + /* ia32 supports unaliged loads & stores */ + (*(__u16 *)(curidt)) = limit; + (*(__u32 *)(curidt +2)) = (unsigned long)(newidt); + + __asm__ __volatile__ ( + "lidt %0\n" + : "=m" (curidt) + ); +}; + + +static void set_gdt(void *newgdt, __u16 limit) +{ + unsigned char curgdt[6]; + + /* ia32 supports unaligned loads & stores */ + (*(__u16 *)(curgdt)) = limit; + (*(__u32 *)(curgdt +2)) = (unsigned long)(newgdt); + + __asm__ __volatile__ ( + "lgdt %0\n" + : "=m" (curgdt) + ); +}; + +static void load_segments(void) +{ +#define __STR(X) #X +#define STR(X) __STR(X) + + __asm__ __volatile__ ( + "\tljmp $"STR(__KERNEL_CS)",$1f\n" + "\t1:\n" + "\tmovl $"STR(__KERNEL_DS)",%eax\n" + "\tmovl %eax,%ds\n" + "\tmovl %eax,%es\n" + "\tmovl %eax,%fs\n" + "\tmovl %eax,%gs\n" + "\tmovl %eax,%ss\n" + ); +#undef STR +#undef __STR +} + +typedef asmlinkage void (*relocate_new_kernel_t)( + unsigned long indirection_page, unsigned long reboot_code_buffer, + unsigned long start_address, unsigned int has_pae); + +const extern unsigned char relocate_new_kernel[]; +extern void relocate_new_kernel_end(void); +const extern unsigned int relocate_new_kernel_size; + +/* + * Do what every setup is needed on image and the + * reboot code buffer to allow us to avoid allocations + * later. Currently nothing. + */ +int machine_kexec_prepare(struct kimage *image) +{ + return 0; +} + +void machine_kexec_cleanup(struct kimage *image) +{ +} + +/* + * Do not allocate memory (or fail in any way) in machine_kexec(). + * We are past the point of no return, committed to rebooting now. + */ +void machine_kexec(struct kimage *image) +{ + unsigned long indirection_page; + unsigned long reboot_code_buffer; + relocate_new_kernel_t rnk; + + /* Interrupts aren't acceptable while we reboot */ + local_irq_disable(); + + /* Compute some offsets */ + reboot_code_buffer = page_to_pfn(image->control_code_page) << PAGE_SHIFT; + indirection_page = image->head & PAGE_MASK; + + /* Set up an identity mapping for the reboot_code_buffer */ + identity_map_page(reboot_code_buffer); + + /* copy it out */ + memcpy((void *)reboot_code_buffer, relocate_new_kernel, relocate_new_kernel_size); + + /* The segment registers are funny things, they are + * automatically loaded from a table, in memory wherever you + * set them to a specific selector, but this table is never + * accessed again you set the segment to a different selector. + * + * The more common model is are caches where the behide + * the scenes work is done, but is also dropped at arbitrary + * times. + * + * I take advantage of this here by force loading the + * segments, before I zap the gdt with an invalid value. + */ + load_segments(); + /* The gdt & idt are now invalid. + * If you want to load them you must set up your own idt & gdt. + */ + set_gdt(phys_to_virt(0),0); + set_idt(phys_to_virt(0),0); + + /* now call it */ + rnk = (relocate_new_kernel_t) reboot_code_buffer; + (*rnk)(indirection_page, reboot_code_buffer, image->start, cpu_has_pae); +} diff --git a/arch/i386/kernel/pci-dma.c b/arch/i386/kernel/pci-dma.c index 10dc6b79d..982fe8c2b 100644 --- a/arch/i386/kernel/pci-dma.c +++ b/arch/i386/kernel/pci-dma.c @@ -13,17 +13,40 @@ #include #include +struct dma_coherent_mem { + void *virt_base; + u32 device_base; + int size; + int flags; + unsigned long *bitmap; +}; + void *dma_alloc_coherent(struct device *dev, size_t size, dma_addr_t *dma_handle, int gfp) { void *ret; + struct dma_coherent_mem *mem = dev ? dev->dma_mem : NULL; + int order = get_order(size); /* ignore region specifiers */ gfp &= ~(__GFP_DMA | __GFP_HIGHMEM); + if (mem) { + int page = bitmap_find_free_region(mem->bitmap, mem->size, + order); + if (page >= 0) { + *dma_handle = mem->device_base + (page << PAGE_SHIFT); + ret = mem->virt_base + (page << PAGE_SHIFT); + memset(ret, 0, size); + return ret; + } + if (mem->flags & DMA_MEMORY_EXCLUSIVE) + return NULL; + } + if (dev == NULL || (dev->coherent_dma_mask < 0xffffffff)) gfp |= GFP_DMA; - ret = (void *)__get_free_pages(gfp, get_order(size)); + ret = (void *)__get_free_pages(gfp, order); if (ret != NULL) { memset(ret, 0, size); @@ -35,5 +58,89 @@ void *dma_alloc_coherent(struct device *dev, size_t size, void dma_free_coherent(struct device *dev, size_t size, void *vaddr, dma_addr_t dma_handle) { - free_pages((unsigned long)vaddr, get_order(size)); + struct dma_coherent_mem *mem = dev ? dev->dma_mem : NULL; + int order = get_order(size); + + if (mem && vaddr >= mem->virt_base && vaddr < (mem->virt_base + (mem->size << PAGE_SHIFT))) { + int page = (vaddr - mem->virt_base) >> PAGE_SHIFT; + + bitmap_release_region(mem->bitmap, page, order); + } else + free_pages((unsigned long)vaddr, order); +} + +int dma_declare_coherent_memory(struct device *dev, dma_addr_t bus_addr, + dma_addr_t device_addr, size_t size, int flags) +{ + void __iomem *mem_base; + int pages = size >> PAGE_SHIFT; + int bitmap_size = (pages + 31)/32; + + if ((flags & (DMA_MEMORY_MAP | DMA_MEMORY_IO)) == 0) + goto out; + if (!size) + goto out; + if (dev->dma_mem) + goto out; + + /* FIXME: this routine just ignores DMA_MEMORY_INCLUDES_CHILDREN */ + + mem_base = ioremap(bus_addr, size); + if (!mem_base) + goto out; + + dev->dma_mem = kmalloc(GFP_KERNEL, sizeof(struct dma_coherent_mem)); + if (!dev->dma_mem) + goto out; + memset(dev->dma_mem, 0, sizeof(struct dma_coherent_mem)); + dev->dma_mem->bitmap = kmalloc(GFP_KERNEL, bitmap_size); + if (!dev->dma_mem->bitmap) + goto free1_out; + memset(dev->dma_mem->bitmap, 0, bitmap_size); + + dev->dma_mem->virt_base = mem_base; + dev->dma_mem->device_base = device_addr; + dev->dma_mem->size = pages; + dev->dma_mem->flags = flags; + + if (flags & DMA_MEMORY_MAP) + return DMA_MEMORY_MAP; + + return DMA_MEMORY_IO; + + free1_out: + kfree(dev->dma_mem->bitmap); + out: + return 0; +} +EXPORT_SYMBOL(dma_declare_coherent_memory); + +void dma_release_declared_memory(struct device *dev) +{ + struct dma_coherent_mem *mem = dev->dma_mem; + + if(!mem) + return; + dev->dma_mem = NULL; + kfree(mem->bitmap); + kfree(mem); +} +EXPORT_SYMBOL(dma_release_declared_memory); + +void *dma_mark_declared_memory_occupied(struct device *dev, + dma_addr_t device_addr, size_t size) +{ + struct dma_coherent_mem *mem = dev->dma_mem; + int pages = (size + (device_addr & ~PAGE_MASK) + PAGE_SIZE - 1) >> PAGE_SHIFT; + int pos, err; + + if (!mem) + return ERR_PTR(-EINVAL); + + pos = (device_addr - mem->device_base) >> PAGE_SHIFT; + err = bitmap_allocate_region(mem->bitmap, pos, get_order(pages)); + if (err != 0) + return ERR_PTR(err); + return mem->virt_base + (pos << PAGE_SHIFT); } +EXPORT_SYMBOL(dma_mark_declared_memory_occupied); diff --git a/arch/i386/kernel/process.c b/arch/i386/kernel/process.c index 3093d1fc6..e8a01f2b5 100644 --- a/arch/i386/kernel/process.c +++ b/arch/i386/kernel/process.c @@ -219,6 +219,32 @@ static int __init idle_setup (char *str) __setup("idle=", idle_setup); +void stack_overflow(void) +{ + extern unsigned long stack_overflowed; + unsigned long esp = current_stack_pointer(); + int panicing = ((esp&(THREAD_SIZE-1)) <= STACK_PANIC); + + oops_in_progress = 1; + printk( "esp: 0x%lx masked: 0x%lx STACK_PANIC:0x%lx %d %d\n", + esp, (esp&(THREAD_SIZE-1)), STACK_PANIC, + (((esp&(THREAD_SIZE-1)) <= STACK_PANIC)), panicing); + show_trace(current,(void*)esp); + + if (panicing) + panic("stack overflow\n"); + + oops_in_progress = 0; + + /* Just let it happen once per task, as otherwise it goes nuts + * in printing stack traces. This means that I need to dump + * the stack_overflowed boolean into the task or thread_info + * structure. For now just turn it off all together. + */ + + /* stack_overflowed = 0; */ +} + void show_regs(struct pt_regs * regs) { unsigned long cr0 = 0L, cr2 = 0L, cr3 = 0L, cr4 = 0L; diff --git a/arch/i386/kernel/reboot.c b/arch/i386/kernel/reboot.c index e8d5cd3ab..85e89f94b 100644 --- a/arch/i386/kernel/reboot.c +++ b/arch/i386/kernel/reboot.c @@ -23,7 +23,6 @@ static int reboot_mode; int reboot_thru_bios; #ifdef CONFIG_SMP -int reboot_smp = 0; static int reboot_cpu = -1; /* shamelessly grabbed from lib/vsprintf.c for readability */ #define is_digit(c) ((c) >= '0' && (c) <= '9') @@ -85,33 +84,9 @@ static int __init set_bios_reboot(struct dmi_system_id *d) return 0; } -/* - * Some machines require the "reboot=s" commandline option, this quirk makes that automatic. - */ -static int __init set_smp_reboot(struct dmi_system_id *d) -{ -#ifdef CONFIG_SMP - if (!reboot_smp) { - reboot_smp = 1; - printk(KERN_INFO "%s series board detected. Selecting SMP-method for reboots.\n", d->ident); - } -#endif - return 0; -} - -/* - * Some machines require the "reboot=b,s" commandline option, this quirk makes that automatic. - */ -static int __init set_smp_bios_reboot(struct dmi_system_id *d) -{ - set_smp_reboot(d); - set_bios_reboot(d); - return 0; -} - static struct dmi_system_id __initdata reboot_dmi_table[] = { { /* Handle problems with rebooting on Dell 1300's */ - .callback = set_smp_bios_reboot, + .callback = set_bios_reboot, .ident = "Dell PowerEdge 1300", .matches = { DMI_MATCH(DMI_SYS_VENDOR, "Dell Computer Corporation"), @@ -294,41 +269,32 @@ void machine_real_restart(unsigned char *code, int length) : "i" ((void *) (0x1000 - sizeof (real_mode_switch) - 100))); } -void machine_restart(char * __unused) +void machine_shutdown(void) { #ifdef CONFIG_SMP - int cpuid; - - cpuid = GET_APIC_ID(apic_read(APIC_ID)); - - if (reboot_smp) { - - /* check to see if reboot_cpu is valid - if its not, default to the BSP */ - if ((reboot_cpu == -1) || - (reboot_cpu > (NR_CPUS -1)) || - !physid_isset(cpuid, phys_cpu_present_map)) - reboot_cpu = boot_cpu_physical_apicid; - - reboot_smp = 0; /* use this as a flag to only go through this once*/ - /* re-run this function on the other CPUs - it will fall though this section since we have - cleared reboot_smp, and do the reboot if it is the - correct CPU, otherwise it halts. */ - if (reboot_cpu != cpuid) - smp_call_function((void *)machine_restart , NULL, 1, 0); + int reboot_cpu_id; + + /* The boot cpu is always logical cpu 0 */ + reboot_cpu_id = 0; + + /* See if there has been given a command line override */ + if ((reboot_cpu_id != -1) && (reboot_cpu < NR_CPUS) && + cpu_isset(reboot_cpu, cpu_online_map)) { + reboot_cpu_id = reboot_cpu; } - /* if reboot_cpu is still -1, then we want a tradional reboot, - and if we are not running on the reboot_cpu,, halt */ - if ((reboot_cpu != -1) && (cpuid != reboot_cpu)) { - for (;;) - __asm__ __volatile__ ("hlt"); + /* Make certain the cpu I'm rebooting on is online */ + if (!cpu_isset(reboot_cpu_id, cpu_online_map)) { + reboot_cpu_id = smp_processor_id(); } - /* - * Stop all CPUs and turn off local APICs and the IO-APIC, so - * other OSs see a clean IRQ state. + + /* Make certain I only run on the appropriate processor */ + set_cpus_allowed(current, cpumask_of_cpu(reboot_cpu_id)); + + /* O.K. Now that I'm on the appropriate processor, stop + * all of the others, and disable their local APICs. */ + if (!netdump_mode) smp_send_stop(); #elif defined(CONFIG_X86_LOCAL_APIC) @@ -341,6 +307,11 @@ void machine_restart(char * __unused) #ifdef CONFIG_X86_IO_APIC disable_IO_APIC(); #endif +} + +void machine_restart(char * __unused) +{ + machine_shutdown(); if (!reboot_thru_bios) { if (efi_enabled) { diff --git a/arch/i386/kernel/relocate_kernel.S b/arch/i386/kernel/relocate_kernel.S new file mode 100644 index 000000000..54be4c2ae --- /dev/null +++ b/arch/i386/kernel/relocate_kernel.S @@ -0,0 +1,118 @@ +/* + * relocate_kernel.S - put the kernel image in place to boot + * Copyright (C) 2002-2004 Eric Biederman + * + * This source code is licensed under the GNU General Public License, + * Version 2. See the file COPYING for more details. + */ + +#include + + /* + * Must be relocatable PIC code callable as a C function, that once + * it starts can not use the previous processes stack. + */ + .globl relocate_new_kernel +relocate_new_kernel: + /* read the arguments and say goodbye to the stack */ + movl 4(%esp), %ebx /* indirection_page */ + movl 8(%esp), %ebp /* reboot_code_buffer */ + movl 12(%esp), %edx /* start address */ + movl 16(%esp), %ecx /* cpu_has_pae */ + + /* zero out flags, and disable interrupts */ + pushl $0 + popfl + + /* set a new stack at the bottom of our page... */ + lea 4096(%ebp), %esp + + /* store the parameters back on the stack */ + pushl %edx /* store the start address */ + + /* Set cr0 to a known state: + * 31 0 == Paging disabled + * 18 0 == Alignment check disabled + * 16 0 == Write protect disabled + * 3 0 == No task switch + * 2 0 == Don't do FP software emulation. + * 0 1 == Proctected mode enabled + */ + movl %cr0, %eax + andl $~((1<<31)|(1<<18)|(1<<16)|(1<<3)|(1<<2)), %eax + orl $(1<<0), %eax + movl %eax, %cr0 + + /* clear cr4 if applicable */ + testl %ecx, %ecx + jz 1f + /* Set cr4 to a known state: + * Setting everything to zero seems safe. + */ + movl %cr4, %eax + andl $0, %eax + movl %eax, %cr4 + + jmp 1f +1: + + /* Flush the TLB (needed?) */ + xorl %eax, %eax + movl %eax, %cr3 + + /* Do the copies */ + cld +0: /* top, read another word for the indirection page */ + movl %ebx, %ecx + movl (%ebx), %ecx + addl $4, %ebx + testl $0x1, %ecx /* is it a destination page */ + jz 1f + movl %ecx, %edi + andl $0xfffff000, %edi + jmp 0b +1: + testl $0x2, %ecx /* is it an indirection page */ + jz 1f + movl %ecx, %ebx + andl $0xfffff000, %ebx + jmp 0b +1: + testl $0x4, %ecx /* is it the done indicator */ + jz 1f + jmp 2f +1: + testl $0x8, %ecx /* is it the source indicator */ + jz 0b /* Ignore it otherwise */ + movl %ecx, %esi /* For every source page do a copy */ + andl $0xfffff000, %esi + + movl $1024, %ecx + rep ; movsl + jmp 0b + +2: + + /* To be certain of avoiding problems with self-modifying code + * I need to execute a serializing instruction here. + * So I flush the TLB, it's handy, and not processor dependent. + */ + xorl %eax, %eax + movl %eax, %cr3 + + /* set all of the registers to known values */ + /* leave %esp alone */ + + xorl %eax, %eax + xorl %ebx, %ebx + xorl %ecx, %ecx + xorl %edx, %edx + xorl %esi, %esi + xorl %edi, %edi + xorl %ebp, %ebp + ret +relocate_new_kernel_end: + + .globl relocate_new_kernel_size +relocate_new_kernel_size: + .long relocate_new_kernel_end - relocate_new_kernel diff --git a/arch/i386/kernel/time.c b/arch/i386/kernel/time.c index f8dcf5d68..a9711fc35 100644 --- a/arch/i386/kernel/time.c +++ b/arch/i386/kernel/time.c @@ -200,6 +200,18 @@ unsigned long long monotonic_clock(void) } EXPORT_SYMBOL(monotonic_clock); +#if defined(CONFIG_SMP) && defined(CONFIG_FRAME_POINTER) +unsigned long profile_pc(struct pt_regs *regs) +{ + unsigned long pc = instruction_pointer(regs); + + if (in_lock_functions(pc)) + return *(unsigned long *)(regs->ebp + 4); + + return pc; +} +EXPORT_SYMBOL(profile_pc); +#endif /* * timer_interrupt() needs to keep up the real-time clock, @@ -321,11 +333,12 @@ static int time_suspend(struct sys_device *dev, u32 state) static int time_resume(struct sys_device *dev) { + unsigned long flags; unsigned long sec = get_cmos_time() + clock_cmos_diff; - write_seqlock_irq(&xtime_lock); + write_seqlock_irqsave(&xtime_lock, flags); xtime.tv_sec = sec; xtime.tv_nsec = 0; - write_sequnlock_irq(&xtime_lock); + write_sequnlock_irqrestore(&xtime_lock, flags); return 0; } diff --git a/arch/i386/kernel/timers/timer_pit.c b/arch/i386/kernel/timers/timer_pit.c index 7ddbf1c76..a433cb046 100644 --- a/arch/i386/kernel/timers/timer_pit.c +++ b/arch/i386/kernel/timers/timer_pit.c @@ -7,6 +7,9 @@ #include #include #include +#include +#include +#include #include #include #include @@ -156,3 +159,44 @@ struct timer_opts timer_pit = { .monotonic_clock = monotonic_clock_pit, .delay = delay_pit, }; + +void setup_pit_timer(void) +{ + extern spinlock_t i8253_lock; + unsigned long flags; + + spin_lock_irqsave(&i8253_lock, flags); + outb_p(0x34,PIT_MODE); /* binary, mode 2, LSB/MSB, ch 0 */ + udelay(10); + outb_p(LATCH & 0xff , PIT_CH0); /* LSB */ + udelay(10); + outb(LATCH >> 8 , PIT_CH0); /* MSB */ + spin_unlock_irqrestore(&i8253_lock, flags); +} + +static int timer_resume(struct sys_device *dev) +{ + setup_pit_timer(); + return 0; +} + +static struct sysdev_class timer_sysclass = { + set_kset_name("timer"), + .resume = timer_resume, +}; + +static struct sys_device device_timer = { + .id = 0, + .cls = &timer_sysclass, +}; + +static int __init init_timer_sysfs(void) +{ + int error = sysdev_class_register(&timer_sysclass); + if (!error) + error = sysdev_register(&device_timer); + return error; +} + +device_initcall(init_timer_sysfs); + diff --git a/arch/i386/kernel/vsyscall.lds.S b/arch/i386/kernel/vsyscall.lds.S new file mode 100644 index 000000000..3a8329d65 --- /dev/null +++ b/arch/i386/kernel/vsyscall.lds.S @@ -0,0 +1,65 @@ +/* + * Linker script for vsyscall DSO. The vsyscall page is an ELF shared + * object prelinked to its virtual address, and with only one read-only + * segment (that fits in one page). This script controls its layout. + */ +#include + +SECTIONS +{ + . = VSYSCALL_BASE + SIZEOF_HEADERS; + + .hash : { *(.hash) } :text + .dynsym : { *(.dynsym) } + .dynstr : { *(.dynstr) } + .gnu.version : { *(.gnu.version) } + .gnu.version_d : { *(.gnu.version_d) } + .gnu.version_r : { *(.gnu.version_r) } + + /* This linker script is used both with -r and with -shared. + For the layouts to match, we need to skip more than enough + space for the dynamic symbol table et al. If this amount + is insufficient, ld -shared will barf. Just increase it here. */ + . = VSYSCALL_BASE + 0x400; + + .text : { *(.text) } :text =0x90909090 + + .eh_frame_hdr : { *(.eh_frame_hdr) } :text :eh_frame_hdr + .eh_frame : { KEEP (*(.eh_frame)) } :text + .dynamic : { *(.dynamic) } :text :dynamic + .useless : { + *(.got.plt) *(.got) + *(.data .data.* .gnu.linkonce.d.*) + *(.dynbss) + *(.bss .bss.* .gnu.linkonce.b.*) + } :text +} + +/* + * 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 */ + eh_frame_hdr 0x6474e550; /* PT_GNU_EH_FRAME, but ld doesn't match the name */ +} + +/* + * This controls what symbols we export from the DSO. + */ +VERSION +{ + LINUX_2.5 { + global: + __kernel_vsyscall; + __kernel_sigreturn; + __kernel_rt_sigreturn; + + local: *; + }; +} + +/* The ELF entry point can be used to set the AT_SYSINFO value. */ +ENTRY(__kernel_vsyscall); diff --git a/arch/i386/lib/mmx.c b/arch/i386/lib/mmx.c index d6fc84f37..01f8b1a2c 100644 --- a/arch/i386/lib/mmx.c +++ b/arch/i386/lib/mmx.c @@ -2,9 +2,9 @@ #include #include #include +#include #include -#include /* diff --git a/arch/i386/mach-es7000/es7000plat.c b/arch/i386/mach-es7000/es7000plat.c index 784576c53..1d1954a7f 100644 --- a/arch/i386/mach-es7000/es7000plat.c +++ b/arch/i386/mach-es7000/es7000plat.c @@ -51,27 +51,74 @@ struct mip_reg *host_reg; int mip_port; unsigned long mip_addr, host_addr; +#if defined(CONFIG_X86_IO_APIC) && (defined(CONFIG_ACPI_INTERPRETER) || defined(CONFIG_ACPI_BOOT)) +static unsigned long cycle_irqs = 0; +static unsigned long free_irqs = 0; +static int gsi_map[MAX_GSI_MAPSIZE] = { [0 ... MAX_GSI_MAPSIZE-1] = -1 }; + +/* + * GSI override for ES7000 platforms. + */ + +static int __init +es7000_gsi_override(int ioapic, int gsi) +{ + static int newgsi = 0; + + if (gsi_map[gsi] != -1) + gsi = gsi_map[gsi]; + else if (cycle_irqs ^ free_irqs) { + newgsi = find_next_bit(&cycle_irqs, IOAPIC_GSI_BOUND(0), newgsi); + __set_bit(newgsi, &free_irqs); + gsi_map[gsi] = newgsi; + gsi = newgsi; + newgsi++; + Dprintk("es7000_gsi_override: free_irqs = 0x%lx\n", free_irqs); + } + + return gsi; +} + static int __init es7000_rename_gsi(int ioapic, int gsi) { + static int initialized = 0; + int i; + + /* + * These should NEVER be true at this point but we'd rather be + * safe than sorry. + */ + if (acpi_disabled || acpi_pci_disabled || acpi_noirq) + return gsi; + if (ioapic) - return gsi; - else { - if (gsi == 0) - return 13; - if (gsi == 1) - return 16; - if (gsi == 4) - return 17; - if (gsi == 6) - return 18; - if (gsi == 7) - return 19; - if (gsi == 8) - return 20; - return gsi; - } + return gsi; + + if (!initialized) { + unsigned long tmp_irqs = 0; + + for (i = 0; i < nr_ioapic_registers[0]; i++) + __set_bit(mp_irqs[i].mpc_srcbusirq, &tmp_irqs); + + cycle_irqs = (~tmp_irqs & io_apic_irqs & ((1 << IOAPIC_GSI_BOUND(0)) - 1)); + + initialized = 1; + Dprintk("es7000_rename_gsi: cycle_irqs = 0x%lx\n", cycle_irqs); + } + + for (i = 0; i < nr_ioapic_registers[0]; i++) { + if (mp_irqs[i].mpc_srcbusirq == gsi) { + if (mp_irqs[i].mpc_dstirq == gsi) + return gsi; + else + return es7000_gsi_override(0, gsi); + } + } + + return gsi; } +#endif // (CONFIG_X86_IO_APIC) && (CONFIG_ACPI_INTERPRETER || CONFIG_ACPI_BOOT) /* * Parse the OEM Table @@ -193,7 +240,7 @@ find_unisys_acpi_oem_table(unsigned long *oem_addr, int *length) } } } - printk("ES7000: did not find Unisys ACPI OEM table!\n"); + Dprintk("ES7000: did not find Unisys ACPI OEM table!\n"); return -1; } @@ -237,7 +284,7 @@ es7000_mip_write(struct mip_reg *mip_reg) } status = ((unsigned long long)mip_reg->off_0 & - (unsigned long long)0xffff0000000000) >> 48; + (unsigned long long)0xffff0000000000ULL) >> 48; mip_reg->off_38 = ((unsigned long long)mip_reg->off_38 & (unsigned long long)~MIP_VALID); return status; diff --git a/arch/i386/mm/mmap.c b/arch/i386/mm/mmap.c index f88a6c88d..ec60c0b42 100644 --- a/arch/i386/mm/mmap.c +++ b/arch/i386/mm/mmap.c @@ -57,7 +57,8 @@ void arch_pick_mmap_layout(struct mm_struct *mm) * Fall back to the standard layout if the personality * bit is set, or if the expected stack growth is unlimited: */ - if (sysctl_legacy_va_layout || (current->personality & ADDR_COMPAT_LAYOUT) || + if (sysctl_legacy_va_layout || + (current->personality & ADDR_COMPAT_LAYOUT) || current->rlim[RLIMIT_STACK].rlim_cur == RLIM_INFINITY) { mm->mmap_base = TASK_UNMAPPED_BASE; mm->get_unmapped_area = arch_get_unmapped_area; @@ -65,7 +66,8 @@ void arch_pick_mmap_layout(struct mm_struct *mm) } else { mm->mmap_base = mmap_base(mm); mm->get_unmapped_area = arch_get_unmapped_area_topdown; - mm->get_unmapped_exec_area = arch_get_unmapped_exec_area; + if (current->flags & PF_RELOCEXEC) + mm->get_unmapped_exec_area = arch_get_unmapped_exec_area; mm->unmap_area = arch_unmap_area_topdown; } } diff --git a/arch/i386/oprofile/op_model_athlon.c b/arch/i386/oprofile/op_model_athlon.c index 5f6cc84ab..0d4e00f42 100644 --- a/arch/i386/oprofile/op_model_athlon.c +++ b/arch/i386/oprofile/op_model_athlon.c @@ -70,7 +70,7 @@ static void athlon_setup_ctrs(struct op_msrs const * const msrs) /* enable active counters */ for (i = 0; i < NUM_COUNTERS; ++i) { - if (counter_config[i].event) { + if (counter_config[i].enabled) { reset_value[i] = counter_config[i].count; CTR_WRITE(counter_config[i].count, msrs, i); @@ -96,7 +96,7 @@ static int athlon_check_ctrs(unsigned int const cpu, { unsigned int low, high; int i; - unsigned long eip = instruction_pointer(regs); + unsigned long eip = profile_pc(regs); int is_kernel = !user_mode(regs); for (i = 0 ; i < NUM_COUNTERS; ++i) { diff --git a/arch/i386/oprofile/op_model_ppro.c b/arch/i386/oprofile/op_model_ppro.c index 21578b26d..0cc80654d 100644 --- a/arch/i386/oprofile/op_model_ppro.c +++ b/arch/i386/oprofile/op_model_ppro.c @@ -67,7 +67,7 @@ static void ppro_setup_ctrs(struct op_msrs const * const msrs) /* enable active counters */ for (i = 0; i < NUM_COUNTERS; ++i) { - if (counter_config[i].event) { + if (counter_config[i].enabled) { reset_value[i] = counter_config[i].count; CTR_WRITE(counter_config[i].count, msrs, i); @@ -91,7 +91,7 @@ static int ppro_check_ctrs(unsigned int const cpu, { unsigned int low, high; int i; - unsigned long eip = instruction_pointer(regs); + unsigned long eip = profile_pc(regs); int is_kernel = !user_mode(regs); for (i = 0 ; i < NUM_COUNTERS; ++i) { diff --git a/arch/i386/pci/common.c b/arch/i386/pci/common.c index 383827d92..88c832a0e 100644 --- a/arch/i386/pci/common.c +++ b/arch/i386/pci/common.c @@ -70,7 +70,7 @@ static void __devinit pcibios_fixup_ghosts(struct pci_bus *b) int i; DBG("PCI: Scanning for ghost devices on bus %d\n", b->number); - for (ln=b->devices.next; ln != &b->devices; ln=ln->next) { + list_for_each(ln, &b->devices) { d = pci_dev_b(ln); if ((d->class >> 8) == PCI_CLASS_BRIDGE_HOST) seen_host_bridge++; diff --git a/arch/i386/pci/i386.c b/arch/i386/pci/i386.c index 2ebc7c672..ec859ad0e 100644 --- a/arch/i386/pci/i386.c +++ b/arch/i386/pci/i386.c @@ -164,7 +164,7 @@ static void __init pcibios_allocate_resources(int pass) } } -static void __init pcibios_assign_resources(void) +static int __init pcibios_assign_resources(void) { struct pci_dev *dev = NULL; int idx; @@ -204,6 +204,7 @@ static void __init pcibios_assign_resources(void) pci_assign_resource(dev, PCI_ROM_RESOURCE); } } + return 0; } void __init pcibios_resource_survey(void) @@ -212,9 +213,14 @@ void __init pcibios_resource_survey(void) pcibios_allocate_bus_resources(&pci_root_buses); pcibios_allocate_resources(0); pcibios_allocate_resources(1); - pcibios_assign_resources(); } +/** + * called in fs_initcall (one below subsys_initcall), + * give a chance for motherboard reserve resources + */ +fs_initcall(pcibios_assign_resources); + int pcibios_enable_resources(struct pci_dev *dev, int mask) { u16 cmd, old_cmd; diff --git a/arch/i386/pci/mmconfig.c b/arch/i386/pci/mmconfig.c index 0b95fc46d..f42c45305 100644 --- a/arch/i386/pci/mmconfig.c +++ b/arch/i386/pci/mmconfig.c @@ -1,3 +1,10 @@ +/* + * Copyright (C) 2004 Matthew Wilcox + * Copyright (C) 2004 Intel Corp. + * + * This code is released under the GNU General Public License version 2. + */ + /* * mmconfig.c - Low-level direct PCI config space access via MMCONFIG */ @@ -9,7 +16,7 @@ /* The physical address of the MMCONFIG aperture. Set from ACPI tables. */ u32 pci_mmcfg_base_addr; -#define mmcfg_virt_addr (fix_to_virt(FIX_PCIE_MCFG)) +#define mmcfg_virt_addr ((void __iomem *) fix_to_virt(FIX_PCIE_MCFG)) /* The base address of the last MMCONFIG device accessed */ static u32 mmcfg_last_accessed_device; diff --git a/arch/i386/pci/numa.c b/arch/i386/pci/numa.c index d77450067..c8046737b 100644 --- a/arch/i386/pci/numa.c +++ b/arch/i386/pci/numa.c @@ -100,10 +100,7 @@ static void __devinit pci_fixup_i450nx(struct pci_dev *d) } pcibios_last_bus = -1; } - -struct pci_fixup pcibios_fixups[] = { - { PCI_FIXUP_HEADER, PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82451NX, pci_fixup_i450nx }, -}; +DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82451NX, pci_fixup_i450nx); static int __init pci_numa_init(void) { diff --git a/arch/i386/pci/pcbios.c b/arch/i386/pci/pcbios.c index 700188d0f..50fc1f31e 100644 --- a/arch/i386/pci/pcbios.c +++ b/arch/i386/pci/pcbios.c @@ -365,7 +365,7 @@ void __devinit pcibios_sort(void) idx = found = 0; while (pci_bios_find_device(dev->vendor, dev->device, idx, &bus, &devfn) == PCIBIOS_SUCCESSFUL) { idx++; - for (ln=pci_devices.next; ln != &pci_devices; ln=ln->next) { + list_for_each(ln, &pci_devices) { d = pci_dev_g(ln); if (d->bus->number == bus && d->devfn == devfn) { list_del(&d->global_list); diff --git a/arch/i386/power/Makefile b/arch/i386/power/Makefile index 2e1c9ab34..8cfa4e8a7 100644 --- a/arch/i386/power/Makefile +++ b/arch/i386/power/Makefile @@ -1,3 +1,2 @@ obj-$(CONFIG_PM) += cpu.o -obj-$(CONFIG_PM_DISK) += pmdisk.o obj-$(CONFIG_SOFTWARE_SUSPEND) += swsusp.o diff --git a/arch/ia64/Kconfig.debug b/arch/ia64/Kconfig.debug new file mode 100644 index 000000000..19edc8b2c --- /dev/null +++ b/arch/ia64/Kconfig.debug @@ -0,0 +1,64 @@ +menu "Kernel hacking" + +source "lib/Kconfig.debug" + +choice + prompt "Physical memory granularity" + default IA64_GRANULE_64MB + +config IA64_GRANULE_16MB + bool "16MB" + help + IA-64 identity-mapped regions use a large page size called "granules". + + Select "16MB" for a small granule size. + Select "64MB" for a large granule size. This is the current default. + +config IA64_GRANULE_64MB + bool "64MB" + depends on !(IA64_GENERIC || IA64_HP_ZX1 || IA64_SGI_SN2) + +endchoice + +config IA64_PRINT_HAZARDS + bool "Print possible IA-64 dependency violations to console" + depends on DEBUG_KERNEL + help + Selecting this option prints more information for Illegal Dependency + Faults, that is, for Read-after-Write (RAW), Write-after-Write (WAW), + or Write-after-Read (WAR) violations. This option is ignored if you + are compiling for an Itanium A step processor + (CONFIG_ITANIUM_ASTEP_SPECIFIC). If you're unsure, select Y. + +config DISABLE_VHPT + bool "Disable VHPT" + depends on DEBUG_KERNEL + help + The Virtual Hash Page Table (VHPT) enhances virtual address + translation performance. Normally you want the VHPT active but you + can select this option to disable the VHPT for debugging. If you're + unsure, answer N. + +config IA64_DEBUG_CMPXCHG + bool "Turn on compare-and-exchange bug checking (slow!)" + depends on DEBUG_KERNEL + help + Selecting this option turns on bug checking for the IA-64 + compare-and-exchange instructions. This is slow! Itaniums + from step B3 or later don't have this problem. If you're unsure, + select N. + +config IA64_DEBUG_IRQ + bool "Turn on irq debug checks (slow!)" + depends on DEBUG_KERNEL + help + Selecting this option turns on bug checking for the IA-64 irq_save + and restore instructions. It's useful for tracking down spinlock + problems, but slow! If you're unsure, select N. + +config SYSVIPC_COMPAT + bool + depends on COMPAT && SYSVIPC + default y + +endmenu diff --git a/arch/ia64/configs/bigsur_defconfig b/arch/ia64/configs/bigsur_defconfig new file mode 100644 index 000000000..f16fd2e14 --- /dev/null +++ b/arch/ia64/configs/bigsur_defconfig @@ -0,0 +1,1132 @@ +# +# Automatically generated make config: don't edit +# Linux kernel version: 2.6.9-rc2 +# Tue Sep 28 13:26:53 2004 +# + +# +# Code maturity level options +# +CONFIG_EXPERIMENTAL=y +CONFIG_CLEAN_COMPILE=y + +# +# General setup +# +CONFIG_LOCALVERSION="" +CONFIG_SWAP=y +CONFIG_SYSVIPC=y +CONFIG_POSIX_MQUEUE=y +# CONFIG_BSD_PROCESS_ACCT is not set +CONFIG_SYSCTL=y +# CONFIG_AUDIT is not set +CONFIG_LOG_BUF_SHIFT=16 +CONFIG_HOTPLUG=y +# CONFIG_IKCONFIG is not set +# CONFIG_EMBEDDED is not set +CONFIG_KALLSYMS=y +# CONFIG_KALLSYMS_ALL is not set +# CONFIG_KALLSYMS_EXTRA_PASS is not set +CONFIG_FUTEX=y +CONFIG_EPOLL=y +CONFIG_IOSCHED_NOOP=y +CONFIG_IOSCHED_AS=y +CONFIG_IOSCHED_DEADLINE=y +CONFIG_IOSCHED_CFQ=y +# CONFIG_CC_OPTIMIZE_FOR_SIZE is not set +CONFIG_SHMEM=y +# CONFIG_TINY_SHMEM is not set + +# +# Loadable module support +# +CONFIG_MODULES=y +CONFIG_MODULE_UNLOAD=y +# CONFIG_MODULE_FORCE_UNLOAD is not set +CONFIG_OBSOLETE_MODPARM=y +# CONFIG_MODVERSIONS is not set +CONFIG_KMOD=y +CONFIG_STOP_MACHINE=y + +# +# Processor type and features +# +CONFIG_IA64=y +CONFIG_64BIT=y +CONFIG_MMU=y +CONFIG_RWSEM_XCHGADD_ALGORITHM=y +CONFIG_TIME_INTERPOLATION=y +CONFIG_EFI=y +CONFIG_GENERIC_IOMAP=y +# CONFIG_IA64_GENERIC is not set +CONFIG_IA64_DIG=y +# CONFIG_IA64_HP_ZX1 is not set +# CONFIG_IA64_SGI_SN2 is not set +# CONFIG_IA64_HP_SIM is not set +CONFIG_ITANIUM=y +# CONFIG_MCKINLEY is not set +# CONFIG_IA64_PAGE_SIZE_4KB is not set +# CONFIG_IA64_PAGE_SIZE_8KB is not set +CONFIG_IA64_PAGE_SIZE_16KB=y +# CONFIG_IA64_PAGE_SIZE_64KB is not set +CONFIG_IA64_BRL_EMU=y +# CONFIG_ITANIUM_BSTEP_SPECIFIC is not set +CONFIG_IA64_L1_CACHE_SHIFT=6 +# CONFIG_NUMA is not set +# CONFIG_VIRTUAL_MEM_MAP is not set +# CONFIG_IA64_CYCLONE is not set +CONFIG_IOSAPIC=y +CONFIG_FORCE_MAX_ZONEORDER=18 +CONFIG_SMP=y +CONFIG_NR_CPUS=2 +# CONFIG_HOTPLUG_CPU is not set +CONFIG_PREEMPT=y +CONFIG_HAVE_DEC_LOCK=y +CONFIG_IA32_SUPPORT=y +CONFIG_COMPAT=y +CONFIG_PERFMON=y +CONFIG_IA64_PALINFO=y + +# +# Firmware Drivers +# +CONFIG_EFI_VARS=y +CONFIG_EFI_PCDP=y +CONFIG_BINFMT_ELF=y +CONFIG_BINFMT_MISC=m + +# +# Power management and ACPI +# +CONFIG_PM=y +CONFIG_ACPI=y + +# +# ACPI (Advanced Configuration and Power Interface) Support +# +CONFIG_ACPI_BOOT=y +CONFIG_ACPI_INTERPRETER=y +CONFIG_ACPI_BUTTON=m +CONFIG_ACPI_FAN=m +CONFIG_ACPI_PROCESSOR=m +CONFIG_ACPI_THERMAL=m +# CONFIG_ACPI_DEBUG is not set +CONFIG_ACPI_BUS=y +CONFIG_ACPI_POWER=y +CONFIG_ACPI_PCI=y +CONFIG_ACPI_SYSTEM=y + +# +# Bus options (PCI, PCMCIA) +# +CONFIG_PCI=y +CONFIG_PCI_DOMAINS=y +# CONFIG_PCI_MSI is not set +CONFIG_PCI_LEGACY_PROC=y +CONFIG_PCI_NAMES=y + +# +# PCI Hotplug Support +# +# CONFIG_HOTPLUG_PCI is not set + +# +# PCMCIA/CardBus support +# +# CONFIG_PCMCIA is not set + +# +# Device Drivers +# + +# +# Generic Driver Options +# +CONFIG_STANDALONE=y +CONFIG_PREVENT_FIRMWARE_BUILD=y +# CONFIG_FW_LOADER is not set +# CONFIG_DEBUG_DRIVER is not set + +# +# Memory Technology Devices (MTD) +# +# CONFIG_MTD is not set + +# +# Parallel port support +# +# CONFIG_PARPORT is not set + +# +# Plug and Play support +# + +# +# Block devices +# +# CONFIG_BLK_CPQ_DA is not set +# CONFIG_BLK_CPQ_CISS_DA is not set +# CONFIG_BLK_DEV_DAC960 is not set +# CONFIG_BLK_DEV_UMEM is not set +CONFIG_BLK_DEV_LOOP=m +CONFIG_BLK_DEV_CRYPTOLOOP=m +CONFIG_BLK_DEV_NBD=m +# CONFIG_BLK_DEV_SX8 is not set +# CONFIG_BLK_DEV_UB is not set +CONFIG_BLK_DEV_RAM=m +CONFIG_BLK_DEV_RAM_SIZE=4096 + +# +# ATA/ATAPI/MFM/RLL support +# +CONFIG_IDE=m +CONFIG_BLK_DEV_IDE=m + +# +# Please see Documentation/ide.txt for help/info on IDE drives +# +# CONFIG_BLK_DEV_IDE_SATA is not set +CONFIG_BLK_DEV_IDEDISK=m +# CONFIG_IDEDISK_MULTI_MODE is not set +CONFIG_BLK_DEV_IDECD=m +# CONFIG_BLK_DEV_IDETAPE is not set +CONFIG_BLK_DEV_IDEFLOPPY=m +# CONFIG_BLK_DEV_IDESCSI is not set +# CONFIG_IDE_TASK_IOCTL is not set +# CONFIG_IDE_TASKFILE_IO is not set + +# +# IDE chipset support/bugfixes +# +CONFIG_IDE_GENERIC=m +CONFIG_BLK_DEV_IDEPCI=y +CONFIG_IDEPCI_SHARE_IRQ=y +# CONFIG_BLK_DEV_OFFBOARD is not set +CONFIG_BLK_DEV_GENERIC=m +# CONFIG_BLK_DEV_OPTI621 is not set +CONFIG_BLK_DEV_IDEDMA_PCI=y +# CONFIG_BLK_DEV_IDEDMA_FORCED is not set +CONFIG_IDEDMA_PCI_AUTO=y +# CONFIG_IDEDMA_ONLYDISK is not set +# CONFIG_BLK_DEV_AEC62XX is not set +# CONFIG_BLK_DEV_ALI15X3 is not set +# CONFIG_BLK_DEV_AMD74XX is not set +# CONFIG_BLK_DEV_CMD64X is not set +# CONFIG_BLK_DEV_TRIFLEX is not set +# CONFIG_BLK_DEV_CY82C693 is not set +# CONFIG_BLK_DEV_CS5520 is not set +# CONFIG_BLK_DEV_CS5530 is not set +# CONFIG_BLK_DEV_HPT34X is not set +# CONFIG_BLK_DEV_HPT366 is not set +# CONFIG_BLK_DEV_SC1200 is not set +CONFIG_BLK_DEV_PIIX=m +# CONFIG_BLK_DEV_NS87415 is not set +# CONFIG_BLK_DEV_PDC202XX_OLD is not set +# CONFIG_BLK_DEV_PDC202XX_NEW is not set +# CONFIG_BLK_DEV_SVWKS is not set +# CONFIG_BLK_DEV_SIIMAGE is not set +# CONFIG_BLK_DEV_SLC90E66 is not set +# CONFIG_BLK_DEV_TRM290 is not set +# CONFIG_BLK_DEV_VIA82CXXX is not set +# CONFIG_IDE_ARM is not set +CONFIG_BLK_DEV_IDEDMA=y +# CONFIG_IDEDMA_IVB is not set +CONFIG_IDEDMA_AUTO=y +# CONFIG_BLK_DEV_HD is not set + +# +# SCSI device support +# +CONFIG_SCSI=y +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 + +# +# Some SCSI devices (e.g. CD jukebox) support multiple LUNs +# +# CONFIG_SCSI_MULTI_LUN is not set +CONFIG_SCSI_CONSTANTS=y +CONFIG_SCSI_LOGGING=y + +# +# SCSI Transport Attributes +# +CONFIG_SCSI_SPI_ATTRS=m +# CONFIG_SCSI_FC_ATTRS is not set + +# +# SCSI low-level drivers +# +# CONFIG_BLK_DEV_3W_XXXX_RAID is not set +# CONFIG_SCSI_3W_9XXX is not set +# CONFIG_SCSI_ACARD is not set +# CONFIG_SCSI_AACRAID is not set +# CONFIG_SCSI_AIC7XXX is not set +# CONFIG_SCSI_AIC7XXX_OLD is not set +# CONFIG_SCSI_AIC79XX is not set +# CONFIG_MEGARAID_NEWGEN is not set +# CONFIG_MEGARAID_LEGACY is not set +# CONFIG_SCSI_SATA is not set +# CONFIG_SCSI_BUSLOGIC is not set +# CONFIG_SCSI_DMX3191D is not set +# CONFIG_SCSI_EATA is not set +# CONFIG_SCSI_EATA_PIO is not set +# CONFIG_SCSI_FUTURE_DOMAIN is not set +# CONFIG_SCSI_GDTH is not set +# CONFIG_SCSI_IPS is not set +# CONFIG_SCSI_INIA100 is not set +# CONFIG_SCSI_SYM53C8XX_2 is not set +# CONFIG_SCSI_IPR is not set +# CONFIG_SCSI_QLOGIC_ISP is not set +# CONFIG_SCSI_QLOGIC_FC is not set +CONFIG_SCSI_QLOGIC_1280=y +CONFIG_SCSI_QLA2XXX=y +# CONFIG_SCSI_QLA21XX is not set +# CONFIG_SCSI_QLA22XX is not set +# CONFIG_SCSI_QLA2300 is not set +# CONFIG_SCSI_QLA2322 is not set +# CONFIG_SCSI_QLA6312 is not set +# CONFIG_SCSI_QLA6322 is not set +# CONFIG_SCSI_DC395x is not set +# CONFIG_SCSI_DC390T is not set +# CONFIG_SCSI_DEBUG is not set + +# +# Multi-device support (RAID and LVM) +# +CONFIG_MD=y +CONFIG_BLK_DEV_MD=m +CONFIG_MD_LINEAR=m +CONFIG_MD_RAID0=m +CONFIG_MD_RAID1=m +CONFIG_MD_RAID10=m +CONFIG_MD_RAID5=m +CONFIG_MD_RAID6=m +CONFIG_MD_MULTIPATH=m +CONFIG_BLK_DEV_DM=m +CONFIG_DM_CRYPT=m +CONFIG_DM_SNAPSHOT=m +CONFIG_DM_MIRROR=m +CONFIG_DM_ZERO=m + +# +# Fusion MPT device support +# +# CONFIG_FUSION is not set + +# +# IEEE 1394 (FireWire) support +# +# CONFIG_IEEE1394 is not set + +# +# I2O device support +# +# CONFIG_I2O is not set + +# +# Networking support +# +CONFIG_NET=y + +# +# Networking options +# +CONFIG_PACKET=y +CONFIG_PACKET_MMAP=y +# CONFIG_NETLINK_DEV 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_PNP 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_TUNNEL is not set +# CONFIG_IPV6 is not set +# CONFIG_NETFILTER is not set + +# +# SCTP Configuration (EXPERIMENTAL) +# +# CONFIG_IP_SCTP 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_NET_DIVERT is not set +# CONFIG_ECONET is not set +# CONFIG_WAN_ROUTER is not set +# CONFIG_NET_HW_FLOWCONTROL is not set + +# +# QoS and/or fair queueing +# +# CONFIG_NET_SCHED is not set +# CONFIG_NET_CLS_ROUTE is not set + +# +# Network testing +# +# CONFIG_NET_PKTGEN is not set +# CONFIG_NETPOLL is not set +# CONFIG_NET_POLL_CONTROLLER is not set +# CONFIG_HAMRADIO is not set +# CONFIG_IRDA is not set +# CONFIG_BT is not set +CONFIG_NETDEVICES=y +CONFIG_DUMMY=y +# CONFIG_BONDING is not set +# CONFIG_EQUALIZER is not set +# CONFIG_TUN is not set + +# +# ARCnet devices +# +# CONFIG_ARCNET is not set + +# +# Ethernet (10 or 100Mbit) +# +CONFIG_NET_ETHERNET=y +CONFIG_MII=y +# CONFIG_HAPPYMEAL is not set +# CONFIG_SUNGEM is not set +# CONFIG_NET_VENDOR_3COM is not set + +# +# Tulip family network device support +# +# CONFIG_NET_TULIP is not set +# CONFIG_HP100 is not set +CONFIG_NET_PCI=y +# CONFIG_PCNET32 is not set +# CONFIG_AMD8111_ETH is not set +# CONFIG_ADAPTEC_STARFIRE is not set +# CONFIG_B44 is not set +# CONFIG_FORCEDETH is not set +# CONFIG_DGRS is not set +CONFIG_EEPRO100=y +# CONFIG_EEPRO100_PIO is not set +# CONFIG_E100 is not set +# CONFIG_FEALNX is not set +# CONFIG_NATSEMI is not set +# CONFIG_NE2K_PCI is not set +# CONFIG_8139CP is not set +# CONFIG_8139TOO is not set +# CONFIG_SIS900 is not set +# CONFIG_EPIC100 is not set +# CONFIG_SUNDANCE is not set +# CONFIG_VIA_RHINE is not set +# CONFIG_VIA_VELOCITY 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_SK98LIN is not set +# CONFIG_TIGON3 is not set + +# +# Ethernet (10000 Mbit) +# +# CONFIG_IXGB is not set +# CONFIG_S2IO is not set + +# +# Token Ring devices +# +# CONFIG_TR is not set + +# +# Wireless LAN (non-hamradio) +# +# CONFIG_NET_RADIO 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_NET_FC is not set +# CONFIG_SHAPER is not set +# CONFIG_NETCONSOLE is not set + +# +# ISDN subsystem +# +# CONFIG_ISDN is not set + +# +# Telephony Support +# +# CONFIG_PHONE is not set + +# +# Input device support +# +CONFIG_INPUT=y + +# +# 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_TSDEV is not set +CONFIG_INPUT_EVDEV=y +# CONFIG_INPUT_EVBUG is not set + +# +# Input I/O drivers +# +# CONFIG_GAMEPORT is not set +CONFIG_SOUND_GAMEPORT=y +CONFIG_SERIO=y +CONFIG_SERIO_I8042=y +CONFIG_SERIO_SERPORT=y +# CONFIG_SERIO_CT82C710 is not set +# CONFIG_SERIO_PCIPS2 is not set +# CONFIG_SERIO_RAW 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_INPUT_MOUSE=y +CONFIG_MOUSE_PS2=y +# CONFIG_MOUSE_SERIAL is not set +# CONFIG_MOUSE_VSXXXAA is not set +# CONFIG_INPUT_JOYSTICK is not set +# CONFIG_INPUT_TOUCHSCREEN is not set +# CONFIG_INPUT_MISC is not set + +# +# Character devices +# +CONFIG_VT=y +CONFIG_VT_CONSOLE=y +CONFIG_HW_CONSOLE=y +# CONFIG_SERIAL_NONSTANDARD is not set + +# +# Serial drivers +# +CONFIG_SERIAL_8250=y +CONFIG_SERIAL_8250_CONSOLE=y +# CONFIG_SERIAL_8250_ACPI is not set +CONFIG_SERIAL_8250_NR_UARTS=4 +CONFIG_SERIAL_8250_EXTENDED=y +CONFIG_SERIAL_8250_SHARE_IRQ=y +# CONFIG_SERIAL_8250_DETECT_IRQ is not set +# CONFIG_SERIAL_8250_MULTIPORT is not set +# CONFIG_SERIAL_8250_RSA is not set + +# +# Non-8250 serial port support +# +CONFIG_SERIAL_CORE=y +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 is not set +CONFIG_EFI_RTC=y +# CONFIG_DTLK is not set +# CONFIG_R3964 is not set +# CONFIG_APPLICOM is not set + +# +# Ftape, the floppy tape device driver +# +CONFIG_AGP=m +CONFIG_AGP_I460=m +CONFIG_DRM=y +# CONFIG_DRM_TDFX is not set +CONFIG_DRM_R128=m +# CONFIG_DRM_RADEON is not set +# CONFIG_DRM_MGA is not set +# CONFIG_DRM_SIS is not set +# CONFIG_RAW_DRIVER is not set +# CONFIG_HPET is not set + +# +# I2C support +# +CONFIG_I2C=y +CONFIG_I2C_CHARDEV=y + +# +# I2C Algorithms +# +CONFIG_I2C_ALGOBIT=y +# CONFIG_I2C_ALGOPCF is not set +# CONFIG_I2C_ALGOPCA is not set + +# +# I2C Hardware Bus support +# +# 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_I801 is not set +# CONFIG_I2C_I810 is not set +# CONFIG_I2C_ISA is not set +# CONFIG_I2C_NFORCE2 is not set +# CONFIG_I2C_PARPORT_LIGHT is not set +# CONFIG_I2C_PROSAVAGE is not set +# CONFIG_I2C_SAVAGE4 is not set +# CONFIG_SCx200_ACB is not set +# CONFIG_I2C_SIS5595 is not set +# CONFIG_I2C_SIS630 is not set +# CONFIG_I2C_SIS96X is not set +# CONFIG_I2C_VIA is not set +# CONFIG_I2C_VIAPRO is not set +# CONFIG_I2C_VOODOO3 is not set +# CONFIG_I2C_PCA_ISA is not set + +# +# Hardware Sensors Chip support +# +# CONFIG_I2C_SENSOR is not set +# CONFIG_SENSORS_ADM1021 is not set +# CONFIG_SENSORS_ADM1025 is not set +# CONFIG_SENSORS_ADM1031 is not set +# CONFIG_SENSORS_ASB100 is not set +# CONFIG_SENSORS_DS1621 is not set +# CONFIG_SENSORS_FSCHER is not set +# CONFIG_SENSORS_GL518SM is not set +# CONFIG_SENSORS_IT87 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_LM90 is not set +# CONFIG_SENSORS_MAX1619 is not set +# CONFIG_SENSORS_SMSC47M1 is not set +# CONFIG_SENSORS_VIA686A is not set +# CONFIG_SENSORS_W83781D is not set +# CONFIG_SENSORS_W83L785TS is not set +# CONFIG_SENSORS_W83627HF is not set + +# +# Other I2C Chip support +# +# CONFIG_SENSORS_EEPROM is not set +# CONFIG_SENSORS_PCF8574 is not set +# CONFIG_SENSORS_PCF8591 is not set +# CONFIG_SENSORS_RTC8564 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 + +# +# Dallas's 1-wire bus +# +# CONFIG_W1 is not set + +# +# Misc devices +# + +# +# Multimedia devices +# +# CONFIG_VIDEO_DEV is not set + +# +# Digital Video Broadcasting Devices +# +# CONFIG_DVB is not set + +# +# Graphics support +# +# CONFIG_FB is not set + +# +# Console display driver support +# +CONFIG_VGA_CONSOLE=y +CONFIG_DUMMY_CONSOLE=y + +# +# Sound +# +CONFIG_SOUND=m + +# +# Advanced Linux Sound Architecture +# +CONFIG_SND=m +CONFIG_SND_TIMER=m +CONFIG_SND_PCM=m +CONFIG_SND_HWDEP=m +CONFIG_SND_RAWMIDI=m +CONFIG_SND_SEQUENCER=m +# CONFIG_SND_SEQ_DUMMY is not set +CONFIG_SND_OSSEMUL=y +CONFIG_SND_MIXER_OSS=m +CONFIG_SND_PCM_OSS=m +# CONFIG_SND_SEQUENCER_OSS is not set +# CONFIG_SND_VERBOSE_PRINTK is not set +# CONFIG_SND_DEBUG is not set + +# +# Generic devices +# +CONFIG_SND_OPL3_LIB=m +# CONFIG_SND_DUMMY is not set +# CONFIG_SND_VIRMIDI is not set +# CONFIG_SND_MTPAV is not set +# CONFIG_SND_SERIAL_U16550 is not set +# CONFIG_SND_MPU401 is not set + +# +# PCI devices +# +CONFIG_SND_AC97_CODEC=m +# CONFIG_SND_ALI5451 is not set +# CONFIG_SND_ATIIXP is not set +# CONFIG_SND_ATIIXP_MODEM is not set +# CONFIG_SND_AU8810 is not set +# CONFIG_SND_AU8820 is not set +# CONFIG_SND_AU8830 is not set +# CONFIG_SND_AZT3328 is not set +# CONFIG_SND_BT87X is not set +# CONFIG_SND_CS46XX is not set +CONFIG_SND_CS4281=m +# CONFIG_SND_EMU10K1 is not set +# CONFIG_SND_KORG1212 is not set +# CONFIG_SND_MIXART is not set +# CONFIG_SND_NM256 is not set +# CONFIG_SND_RME32 is not set +# CONFIG_SND_RME96 is not set +# CONFIG_SND_RME9652 is not set +# CONFIG_SND_HDSP is not set +# CONFIG_SND_TRIDENT is not set +# CONFIG_SND_YMFPCI is not set +# CONFIG_SND_ALS4000 is not set +# CONFIG_SND_CMIPCI is not set +# CONFIG_SND_ENS1370 is not set +# CONFIG_SND_ENS1371 is not set +# CONFIG_SND_ES1938 is not set +# CONFIG_SND_ES1968 is not set +# CONFIG_SND_MAESTRO3 is not set +# CONFIG_SND_FM801 is not set +# CONFIG_SND_ICE1712 is not set +# CONFIG_SND_ICE1724 is not set +# CONFIG_SND_INTEL8X0 is not set +# CONFIG_SND_INTEL8X0M is not set +# CONFIG_SND_SONICVIBES is not set +# CONFIG_SND_VIA82XX is not set +# CONFIG_SND_VX222 is not set + +# +# ALSA USB devices +# +# CONFIG_SND_USB_AUDIO is not set +# CONFIG_SND_USB_USX2Y is not set + +# +# Open Sound System +# +# CONFIG_SOUND_PRIME is not set + +# +# USB support +# +CONFIG_USB=m +# CONFIG_USB_DEBUG is not set + +# +# Miscellaneous USB options +# +CONFIG_USB_DEVICEFS=y +# CONFIG_USB_BANDWIDTH is not set +# CONFIG_USB_DYNAMIC_MINORS is not set +# CONFIG_USB_SUSPEND is not set +# CONFIG_USB_OTG is not set + +# +# USB Host Controller Drivers +# +# CONFIG_USB_EHCI_HCD is not set +# CONFIG_USB_OHCI_HCD is not set +CONFIG_USB_UHCI_HCD=m + +# +# USB Device Class drivers +# +CONFIG_USB_AUDIO=m +CONFIG_USB_BLUETOOTH_TTY=m +CONFIG_USB_MIDI=m +CONFIG_USB_ACM=m +CONFIG_USB_PRINTER=m +CONFIG_USB_STORAGE=m +# CONFIG_USB_STORAGE_DEBUG is not set +# CONFIG_USB_STORAGE_RW_DETECT 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_HP8200e is not set +# CONFIG_USB_STORAGE_SDDR09 is not set +# CONFIG_USB_STORAGE_SDDR55 is not set +# CONFIG_USB_STORAGE_JUMPSHOT is not set + +# +# USB Human Interface Devices (HID) +# +CONFIG_USB_HID=m +CONFIG_USB_HIDINPUT=y +# CONFIG_HID_FF is not set +CONFIG_USB_HIDDEV=y + +# +# USB HID Boot Protocol drivers +# +# CONFIG_USB_KBD is not set +# CONFIG_USB_MOUSE is not set +# CONFIG_USB_AIPTEK is not set +# CONFIG_USB_WACOM is not set +# CONFIG_USB_KBTAB is not set +# CONFIG_USB_POWERMATE is not set +# CONFIG_USB_MTOUCH is not set +# CONFIG_USB_EGALAX is not set +# CONFIG_USB_XPAD is not set +# CONFIG_USB_ATI_REMOTE is not set + +# +# USB Imaging devices +# +# CONFIG_USB_MDC800 is not set +# CONFIG_USB_MICROTEK is not set +# CONFIG_USB_HPUSBSCSI is not set + +# +# USB Multimedia devices +# +# CONFIG_USB_DABUSB is not set + +# +# Video4Linux support is needed for USB Multimedia device support +# + +# +# USB Network adaptors +# +# 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 + +# +# USB port drivers +# + +# +# USB Serial Converter support +# +# CONFIG_USB_SERIAL is not set + +# +# USB Miscellaneous drivers +# +# CONFIG_USB_EMI62 is not set +# CONFIG_USB_EMI26 is not set +# CONFIG_USB_TIGL 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_LED is not set +# CONFIG_USB_CYTHERM is not set +# CONFIG_USB_PHIDGETSERVO is not set +# CONFIG_USB_TEST is not set + +# +# USB Gadget Support +# +# CONFIG_USB_GADGET is not set + +# +# File systems +# +CONFIG_EXT2_FS=y +# CONFIG_EXT2_FS_XATTR is not set +CONFIG_EXT3_FS=y +CONFIG_EXT3_FS_XATTR=y +# CONFIG_EXT3_FS_POSIX_ACL is not set +# CONFIG_EXT3_FS_SECURITY is not set +CONFIG_JBD=y +# CONFIG_JBD_DEBUG is not set +CONFIG_FS_MBCACHE=y +# CONFIG_REISERFS_FS is not set +# CONFIG_JFS_FS is not set +CONFIG_FS_POSIX_ACL=y +CONFIG_XFS_FS=y +# CONFIG_XFS_RT is not set +CONFIG_XFS_QUOTA=y +CONFIG_XFS_SECURITY=y +CONFIG_XFS_POSIX_ACL=y +# CONFIG_MINIX_FS is not set +# CONFIG_ROMFS_FS is not set +# CONFIG_QUOTA is not set +CONFIG_QUOTACTL=y +CONFIG_AUTOFS_FS=m +CONFIG_AUTOFS4_FS=m + +# +# CD-ROM/DVD Filesystems +# +CONFIG_ISO9660_FS=m +CONFIG_JOLIET=y +# CONFIG_ZISOFS is not set +CONFIG_UDF_FS=m +CONFIG_UDF_NLS=y + +# +# 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_KCORE=y +CONFIG_SYSFS=y +# CONFIG_DEVFS_FS is not set +CONFIG_DEVPTS_FS_XATTR=y +CONFIG_DEVPTS_FS_SECURITY=y +CONFIG_TMPFS=y +CONFIG_HUGETLBFS=y +CONFIG_HUGETLB_PAGE=y +CONFIG_RAMFS=y + +# +# 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_HPFS_FS is not set +# CONFIG_QNX4FS_FS is not set +# CONFIG_SYSV_FS is not set +# CONFIG_UFS_FS is not set + +# +# Network File Systems +# +CONFIG_NFS_FS=m +CONFIG_NFS_V3=y +CONFIG_NFS_V4=y +# CONFIG_NFS_DIRECTIO is not set +CONFIG_NFSD=m +CONFIG_NFSD_V3=y +CONFIG_NFSD_V4=y +CONFIG_NFSD_TCP=y +CONFIG_LOCKD=m +CONFIG_LOCKD_V4=y +CONFIG_EXPORTFS=m +CONFIG_SUNRPC=m +CONFIG_SUNRPC_GSS=m +CONFIG_RPCSEC_GSS_KRB5=m +# CONFIG_RPCSEC_GSS_SPKM3 is not set +# CONFIG_SMB_FS is not set +CONFIG_CIFS=m +CONFIG_CIFS_STATS=y +CONFIG_CIFS_XATTR=y +CONFIG_CIFS_POSIX=y +# CONFIG_NCP_FS is not set +# CONFIG_CODA_FS is not set +# CONFIG_AFS_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=y +# CONFIG_ULTRIX_PARTITION is not set +# CONFIG_SUN_PARTITION is not set +CONFIG_EFI_PARTITION=y + +# +# Native Language Support +# +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=m + +# +# Library routines +# +# CONFIG_CRC_CCITT is not set +CONFIG_CRC32=y +# CONFIG_LIBCRC32C is not set + +# +# Profiling support +# +CONFIG_PROFILING=y +CONFIG_OPROFILE=y + +# +# Kernel hacking +# +CONFIG_DEBUG_KERNEL=y +CONFIG_MAGIC_SYSRQ=y +# CONFIG_DEBUG_SLAB is not set +# CONFIG_DEBUG_SPINLOCK is not set +# CONFIG_DEBUG_SPINLOCK_SLEEP is not set +# CONFIG_DEBUG_INFO is not set +# CONFIG_IA64_GRANULE_16MB is not set +CONFIG_IA64_GRANULE_64MB=y +# CONFIG_IA64_PRINT_HAZARDS is not set +# CONFIG_DISABLE_VHPT is not set +# CONFIG_IA64_DEBUG_CMPXCHG is not set +# CONFIG_IA64_DEBUG_IRQ is not set +CONFIG_SYSVIPC_COMPAT=y + +# +# Security options +# +# CONFIG_SECURITY is not set + +# +# Cryptographic options +# +CONFIG_CRYPTO=y +# CONFIG_CRYPTO_HMAC is not set +# CONFIG_CRYPTO_NULL is not set +# CONFIG_CRYPTO_MD4 is not set +CONFIG_CRYPTO_MD5=y +# CONFIG_CRYPTO_SHA1 is not set +# CONFIG_CRYPTO_SHA256 is not set +# CONFIG_CRYPTO_SHA512 is not set +# CONFIG_CRYPTO_WHIRLPOOL is not set +CONFIG_CRYPTO_DES=y +# CONFIG_CRYPTO_BLOWFISH is not set +# CONFIG_CRYPTO_TWOFISH is not set +# CONFIG_CRYPTO_SERPENT is not set +# CONFIG_CRYPTO_AES is not set +# CONFIG_CRYPTO_CAST5 is not set +# CONFIG_CRYPTO_CAST6 is not set +# CONFIG_CRYPTO_TEA is not set +# CONFIG_CRYPTO_ARC4 is not set +# CONFIG_CRYPTO_KHAZAD is not set +# CONFIG_CRYPTO_DEFLATE is not set +# CONFIG_CRYPTO_MICHAEL_MIC is not set +# CONFIG_CRYPTO_CRC32C is not set +# CONFIG_CRYPTO_TEST is not set diff --git a/arch/ia64/configs/generic_defconfig b/arch/ia64/configs/generic_defconfig index e64003e02..a2684739b 100644 --- a/arch/ia64/configs/generic_defconfig +++ b/arch/ia64/configs/generic_defconfig @@ -1,5 +1,7 @@ # # Automatically generated make config: don't edit +# Linux kernel version: 2.6.9-rc1 +# Tue Aug 24 15:08:24 2004 # # @@ -126,6 +128,7 @@ CONFIG_PCI_NAMES=y CONFIG_HOTPLUG_PCI=m # CONFIG_HOTPLUG_PCI_FAKE is not set CONFIG_HOTPLUG_PCI_ACPI=m +# CONFIG_HOTPLUG_PCI_ACPI_IBM is not set # CONFIG_HOTPLUG_PCI_CPCI is not set # CONFIG_HOTPLUG_PCI_PCIE is not set # CONFIG_HOTPLUG_PCI_SHPC is not set @@ -172,6 +175,7 @@ CONFIG_BLK_DEV_LOOP=m CONFIG_BLK_DEV_CRYPTOLOOP=m CONFIG_BLK_DEV_NBD=m # CONFIG_BLK_DEV_SX8 is not set +# CONFIG_BLK_DEV_UB is not set CONFIG_BLK_DEV_RAM=m CONFIG_BLK_DEV_RAM_SIZE=4096 @@ -313,6 +317,7 @@ CONFIG_BLK_DEV_MD=m CONFIG_MD_LINEAR=m CONFIG_MD_RAID0=m CONFIG_MD_RAID1=m +# CONFIG_MD_RAID10 is not set CONFIG_MD_RAID5=m CONFIG_MD_RAID6=m CONFIG_MD_MULTIPATH=m @@ -365,6 +370,7 @@ CONFIG_SYN_COOKIES=y # CONFIG_INET_AH is not set # CONFIG_INET_ESP is not set # CONFIG_INET_IPCOMP is not set +# CONFIG_INET_TUNNEL is not set # CONFIG_IPV6 is not set # CONFIG_NETFILTER is not set @@ -575,6 +581,7 @@ CONFIG_SERIAL_NONSTANDARD=y # CONFIG_SYNCLINKMP is not set # CONFIG_N_HDLC is not set # CONFIG_STALDRV is not set +CONFIG_SGI_SNSC=y # # Serial drivers @@ -766,6 +773,7 @@ CONFIG_USB=m CONFIG_USB_DEVICEFS=y # CONFIG_USB_BANDWIDTH is not set # CONFIG_USB_DYNAMIC_MINORS is not set +# CONFIG_USB_SUSPEND is not set # # USB Host Controller Drivers @@ -918,6 +926,8 @@ CONFIG_UDF_NLS=y 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=m # CONFIG_NTFS_DEBUG is not set # CONFIG_NTFS_RW is not set @@ -945,7 +955,6 @@ CONFIG_RAMFS=y # CONFIG_BEFS_FS is not set # CONFIG_BFS_FS is not set # CONFIG_EFS_FS is not set -# CONFIG_JFFS2_COMPRESSION_OPTIONS is not set # CONFIG_CRAMFS is not set # CONFIG_VXFS_FS is not set # CONFIG_HPFS_FS is not set @@ -970,6 +979,7 @@ CONFIG_EXPORTFS=m CONFIG_SUNRPC=m CONFIG_SUNRPC_GSS=m CONFIG_RPCSEC_GSS_KRB5=m +# CONFIG_RPCSEC_GSS_SPKM3 is not set CONFIG_SMB_FS=m CONFIG_SMB_NLS_DEFAULT=y CONFIG_SMB_NLS_REMOTE="cp437" @@ -1006,7 +1016,7 @@ CONFIG_EFI_PARTITION=y # CONFIG_NLS=y CONFIG_NLS_DEFAULT="iso8859-1" -CONFIG_NLS_CODEPAGE_437=m +CONFIG_NLS_CODEPAGE_437=y CONFIG_NLS_CODEPAGE_737=m CONFIG_NLS_CODEPAGE_775=m CONFIG_NLS_CODEPAGE_850=m @@ -1030,7 +1040,7 @@ CONFIG_NLS_ISO8859_8=m CONFIG_NLS_CODEPAGE_1250=m CONFIG_NLS_CODEPAGE_1251=m # CONFIG_NLS_ASCII is not set -CONFIG_NLS_ISO8859_1=m +CONFIG_NLS_ISO8859_1=y CONFIG_NLS_ISO8859_2=m CONFIG_NLS_ISO8859_3=m CONFIG_NLS_ISO8859_4=m @@ -1067,18 +1077,18 @@ CONFIG_CRC32=y # # Kernel hacking # -CONFIG_IA64_GRANULE_16MB=y -# CONFIG_IA64_GRANULE_64MB is not set CONFIG_DEBUG_KERNEL=y -# CONFIG_IA64_PRINT_HAZARDS is not set -# CONFIG_DISABLE_VHPT is not set CONFIG_MAGIC_SYSRQ=y # CONFIG_DEBUG_SLAB is not set # CONFIG_DEBUG_SPINLOCK is not set # CONFIG_DEBUG_SPINLOCK_SLEEP is not set +# CONFIG_DEBUG_INFO is not set +CONFIG_IA64_GRANULE_16MB=y +# CONFIG_IA64_GRANULE_64MB is not set +# CONFIG_IA64_PRINT_HAZARDS is not set +# CONFIG_DISABLE_VHPT is not set # CONFIG_IA64_DEBUG_CMPXCHG is not set # CONFIG_IA64_DEBUG_IRQ is not set -# CONFIG_DEBUG_INFO is not set CONFIG_SYSVIPC_COMPAT=y # @@ -1101,11 +1111,12 @@ CONFIG_CRYPTO_DES=m # CONFIG_CRYPTO_BLOWFISH is not set # CONFIG_CRYPTO_TWOFISH is not set # CONFIG_CRYPTO_SERPENT is not set -# CONFIG_CRYPTO_AES_GENERIC is not set +# CONFIG_CRYPTO_AES is not set # CONFIG_CRYPTO_CAST5 is not set # CONFIG_CRYPTO_CAST6 is not set # CONFIG_CRYPTO_TEA is not set # CONFIG_CRYPTO_ARC4 is not set +# CONFIG_CRYPTO_KHAZAD is not set # CONFIG_CRYPTO_DEFLATE is not set # CONFIG_CRYPTO_MICHAEL_MIC is not set # CONFIG_CRYPTO_CRC32C is not set diff --git a/arch/ia64/configs/tiger_defconfig b/arch/ia64/configs/tiger_defconfig new file mode 100644 index 000000000..ce9412586 --- /dev/null +++ b/arch/ia64/configs/tiger_defconfig @@ -0,0 +1,1028 @@ +# +# Automatically generated make config: don't edit +# Linux kernel version: 2.6.9-rc2 +# Tue Sep 28 09:03:25 2004 +# + +# +# Code maturity level options +# +CONFIG_EXPERIMENTAL=y +CONFIG_CLEAN_COMPILE=y + +# +# General setup +# +CONFIG_LOCALVERSION="" +CONFIG_SWAP=y +CONFIG_SYSVIPC=y +CONFIG_POSIX_MQUEUE=y +# CONFIG_BSD_PROCESS_ACCT is not set +CONFIG_SYSCTL=y +# CONFIG_AUDIT is not set +CONFIG_LOG_BUF_SHIFT=20 +CONFIG_HOTPLUG=y +CONFIG_IKCONFIG=y +CONFIG_IKCONFIG_PROC=y +# CONFIG_EMBEDDED is not set +CONFIG_KALLSYMS=y +CONFIG_KALLSYMS_ALL=y +# CONFIG_KALLSYMS_EXTRA_PASS is not set +CONFIG_FUTEX=y +CONFIG_EPOLL=y +CONFIG_IOSCHED_NOOP=y +CONFIG_IOSCHED_AS=y +CONFIG_IOSCHED_DEADLINE=y +CONFIG_IOSCHED_CFQ=y +# CONFIG_CC_OPTIMIZE_FOR_SIZE is not set +CONFIG_SHMEM=y +# CONFIG_TINY_SHMEM is not set + +# +# Loadable module support +# +CONFIG_MODULES=y +CONFIG_MODULE_UNLOAD=y +# CONFIG_MODULE_FORCE_UNLOAD is not set +CONFIG_OBSOLETE_MODPARM=y +CONFIG_MODVERSIONS=y +CONFIG_KMOD=y +CONFIG_STOP_MACHINE=y + +# +# Processor type and features +# +CONFIG_IA64=y +CONFIG_64BIT=y +CONFIG_MMU=y +CONFIG_RWSEM_XCHGADD_ALGORITHM=y +CONFIG_TIME_INTERPOLATION=y +CONFIG_EFI=y +CONFIG_GENERIC_IOMAP=y +# CONFIG_IA64_GENERIC is not set +CONFIG_IA64_DIG=y +# CONFIG_IA64_HP_ZX1 is not set +# CONFIG_IA64_SGI_SN2 is not set +# CONFIG_IA64_HP_SIM is not set +# CONFIG_ITANIUM is not set +CONFIG_MCKINLEY=y +# CONFIG_IA64_PAGE_SIZE_4KB is not set +# CONFIG_IA64_PAGE_SIZE_8KB is not set +CONFIG_IA64_PAGE_SIZE_16KB=y +# CONFIG_IA64_PAGE_SIZE_64KB is not set +CONFIG_IA64_L1_CACHE_SHIFT=7 +# CONFIG_NUMA is not set +CONFIG_VIRTUAL_MEM_MAP=y +CONFIG_IA64_CYCLONE=y +CONFIG_IOSAPIC=y +CONFIG_FORCE_MAX_ZONEORDER=18 +CONFIG_SMP=y +CONFIG_NR_CPUS=4 +CONFIG_HOTPLUG_CPU=y +# CONFIG_PREEMPT is not set +CONFIG_HAVE_DEC_LOCK=y +CONFIG_IA32_SUPPORT=y +CONFIG_COMPAT=y +CONFIG_PERFMON=y +CONFIG_IA64_PALINFO=y + +# +# Firmware Drivers +# +CONFIG_EFI_VARS=y +CONFIG_EFI_PCDP=y +CONFIG_BINFMT_ELF=y +CONFIG_BINFMT_MISC=m + +# +# Power management and ACPI +# +CONFIG_PM=y +CONFIG_ACPI=y + +# +# ACPI (Advanced Configuration and Power Interface) Support +# +CONFIG_ACPI_BOOT=y +CONFIG_ACPI_INTERPRETER=y +CONFIG_ACPI_BUTTON=m +CONFIG_ACPI_FAN=m +CONFIG_ACPI_PROCESSOR=m +CONFIG_ACPI_THERMAL=m +# CONFIG_ACPI_DEBUG is not set +CONFIG_ACPI_BUS=y +CONFIG_ACPI_POWER=y +CONFIG_ACPI_PCI=y +CONFIG_ACPI_SYSTEM=y + +# +# Bus options (PCI, PCMCIA) +# +CONFIG_PCI=y +CONFIG_PCI_DOMAINS=y +# CONFIG_PCI_MSI is not set +CONFIG_PCI_LEGACY_PROC=y +CONFIG_PCI_NAMES=y + +# +# PCI Hotplug Support +# +CONFIG_HOTPLUG_PCI=m +# CONFIG_HOTPLUG_PCI_FAKE is not set +CONFIG_HOTPLUG_PCI_ACPI=m +# CONFIG_HOTPLUG_PCI_ACPI_IBM is not set +# CONFIG_HOTPLUG_PCI_CPCI is not set +# CONFIG_HOTPLUG_PCI_PCIE is not set +# CONFIG_HOTPLUG_PCI_SHPC is not set + +# +# PCMCIA/CardBus support +# +# CONFIG_PCMCIA is not set + +# +# Device Drivers +# + +# +# Generic Driver Options +# +CONFIG_STANDALONE=y +CONFIG_PREVENT_FIRMWARE_BUILD=y +# CONFIG_FW_LOADER is not set +# CONFIG_DEBUG_DRIVER is not set + +# +# Memory Technology Devices (MTD) +# +# CONFIG_MTD is not set + +# +# Parallel port support +# +# CONFIG_PARPORT is not set + +# +# Plug and Play support +# + +# +# Block devices +# +# CONFIG_BLK_CPQ_DA is not set +# CONFIG_BLK_CPQ_CISS_DA is not set +# CONFIG_BLK_DEV_DAC960 is not set +# CONFIG_BLK_DEV_UMEM is not set +CONFIG_BLK_DEV_LOOP=m +CONFIG_BLK_DEV_CRYPTOLOOP=m +CONFIG_BLK_DEV_NBD=m +# CONFIG_BLK_DEV_SX8 is not set +# CONFIG_BLK_DEV_UB is not set +CONFIG_BLK_DEV_RAM=m +CONFIG_BLK_DEV_RAM_SIZE=4096 + +# +# ATA/ATAPI/MFM/RLL support +# +CONFIG_IDE=y +CONFIG_BLK_DEV_IDE=y + +# +# Please see Documentation/ide.txt for help/info on IDE drives +# +# CONFIG_BLK_DEV_IDE_SATA is not set +CONFIG_BLK_DEV_IDEDISK=y +# CONFIG_IDEDISK_MULTI_MODE is not set +CONFIG_BLK_DEV_IDECD=y +# CONFIG_BLK_DEV_IDETAPE is not set +CONFIG_BLK_DEV_IDEFLOPPY=y +CONFIG_BLK_DEV_IDESCSI=m +# CONFIG_IDE_TASK_IOCTL is not set +# CONFIG_IDE_TASKFILE_IO is not set + +# +# IDE chipset support/bugfixes +# +CONFIG_IDE_GENERIC=y +CONFIG_BLK_DEV_IDEPCI=y +# CONFIG_IDEPCI_SHARE_IRQ is not set +# CONFIG_BLK_DEV_OFFBOARD is not set +CONFIG_BLK_DEV_GENERIC=y +# CONFIG_BLK_DEV_OPTI621 is not set +CONFIG_BLK_DEV_IDEDMA_PCI=y +# CONFIG_BLK_DEV_IDEDMA_FORCED is not set +CONFIG_IDEDMA_PCI_AUTO=y +# CONFIG_IDEDMA_ONLYDISK is not set +# CONFIG_BLK_DEV_AEC62XX is not set +# CONFIG_BLK_DEV_ALI15X3 is not set +# CONFIG_BLK_DEV_AMD74XX is not set +CONFIG_BLK_DEV_CMD64X=y +# CONFIG_BLK_DEV_TRIFLEX is not set +# CONFIG_BLK_DEV_CY82C693 is not set +# CONFIG_BLK_DEV_CS5520 is not set +# CONFIG_BLK_DEV_CS5530 is not set +# CONFIG_BLK_DEV_HPT34X is not set +# CONFIG_BLK_DEV_HPT366 is not set +# CONFIG_BLK_DEV_SC1200 is not set +CONFIG_BLK_DEV_PIIX=y +# CONFIG_BLK_DEV_NS87415 is not set +# CONFIG_BLK_DEV_PDC202XX_OLD is not set +# CONFIG_BLK_DEV_PDC202XX_NEW is not set +# CONFIG_BLK_DEV_SVWKS is not set +# CONFIG_BLK_DEV_SIIMAGE is not set +# CONFIG_BLK_DEV_SLC90E66 is not set +# CONFIG_BLK_DEV_TRM290 is not set +# CONFIG_BLK_DEV_VIA82CXXX is not set +# CONFIG_IDE_ARM is not set +CONFIG_BLK_DEV_IDEDMA=y +# CONFIG_IDEDMA_IVB is not set +CONFIG_IDEDMA_AUTO=y +# CONFIG_BLK_DEV_HD is not set + +# +# SCSI device support +# +CONFIG_SCSI=y +CONFIG_SCSI_PROC_FS=y + +# +# SCSI support type (disk, tape, CD-ROM) +# +CONFIG_BLK_DEV_SD=y +CONFIG_CHR_DEV_ST=m +# CONFIG_CHR_DEV_OSST is not set +CONFIG_BLK_DEV_SR=m +# CONFIG_BLK_DEV_SR_VENDOR is not set +CONFIG_CHR_DEV_SG=m + +# +# 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 + +# +# SCSI Transport Attributes +# +CONFIG_SCSI_SPI_ATTRS=y +CONFIG_SCSI_FC_ATTRS=y + +# +# SCSI low-level drivers +# +# CONFIG_BLK_DEV_3W_XXXX_RAID is not set +# CONFIG_SCSI_3W_9XXX is not set +# CONFIG_SCSI_ACARD is not set +# CONFIG_SCSI_AACRAID is not set +# CONFIG_SCSI_AIC7XXX is not set +# CONFIG_SCSI_AIC7XXX_OLD is not set +# CONFIG_SCSI_AIC79XX is not set +# CONFIG_MEGARAID_NEWGEN is not set +# CONFIG_MEGARAID_LEGACY is not set +# CONFIG_SCSI_SATA is not set +# CONFIG_SCSI_BUSLOGIC is not set +# CONFIG_SCSI_DMX3191D is not set +# CONFIG_SCSI_EATA is not set +# CONFIG_SCSI_EATA_PIO is not set +# CONFIG_SCSI_FUTURE_DOMAIN is not set +# CONFIG_SCSI_GDTH is not set +# CONFIG_SCSI_IPS is not set +# CONFIG_SCSI_INIA100 is not set +CONFIG_SCSI_SYM53C8XX_2=y +CONFIG_SCSI_SYM53C8XX_DMA_ADDRESSING_MODE=1 +CONFIG_SCSI_SYM53C8XX_DEFAULT_TAGS=16 +CONFIG_SCSI_SYM53C8XX_MAX_TAGS=64 +# CONFIG_SCSI_SYM53C8XX_IOMAPPED is not set +# CONFIG_SCSI_IPR is not set +# CONFIG_SCSI_QLOGIC_ISP is not set +CONFIG_SCSI_QLOGIC_FC=y +# CONFIG_SCSI_QLOGIC_FC_FIRMWARE is not set +CONFIG_SCSI_QLOGIC_1280=y +CONFIG_SCSI_QLA2XXX=y +CONFIG_SCSI_QLA21XX=m +CONFIG_SCSI_QLA22XX=m +CONFIG_SCSI_QLA2300=m +CONFIG_SCSI_QLA2322=m +# CONFIG_SCSI_QLA6312 is not set +# CONFIG_SCSI_QLA6322 is not set +# CONFIG_SCSI_DC395x is not set +# CONFIG_SCSI_DC390T is not set +# CONFIG_SCSI_DEBUG is not set + +# +# Multi-device support (RAID and LVM) +# +CONFIG_MD=y +CONFIG_BLK_DEV_MD=m +CONFIG_MD_LINEAR=m +CONFIG_MD_RAID0=m +CONFIG_MD_RAID1=m +# CONFIG_MD_RAID10 is not set +CONFIG_MD_RAID5=m +CONFIG_MD_RAID6=m +CONFIG_MD_MULTIPATH=m +CONFIG_BLK_DEV_DM=m +CONFIG_DM_CRYPT=m +CONFIG_DM_SNAPSHOT=m +CONFIG_DM_MIRROR=m +CONFIG_DM_ZERO=m + +# +# Fusion MPT device support +# +CONFIG_FUSION=y +CONFIG_FUSION_MAX_SGE=40 +# CONFIG_FUSION_CTL is not set + +# +# IEEE 1394 (FireWire) support +# +# CONFIG_IEEE1394 is not set + +# +# I2O device support +# +# CONFIG_I2O is not set + +# +# Networking support +# +CONFIG_NET=y + +# +# Networking options +# +CONFIG_PACKET=y +# CONFIG_PACKET_MMAP is not set +CONFIG_NETLINK_DEV=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_PNP is not set +# CONFIG_NET_IPIP is not set +# CONFIG_NET_IPGRE is not set +# CONFIG_IP_MROUTE is not set +CONFIG_ARPD=y +CONFIG_SYN_COOKIES=y +# CONFIG_INET_AH is not set +# CONFIG_INET_ESP is not set +# CONFIG_INET_IPCOMP is not set +# CONFIG_INET_TUNNEL is not set +# CONFIG_IPV6 is not set +# CONFIG_NETFILTER is not set + +# +# SCTP Configuration (EXPERIMENTAL) +# +# CONFIG_IP_SCTP 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_NET_DIVERT is not set +# CONFIG_ECONET is not set +# CONFIG_WAN_ROUTER is not set +# CONFIG_NET_HW_FLOWCONTROL is not set + +# +# QoS and/or fair queueing +# +# CONFIG_NET_SCHED is not set +# CONFIG_NET_CLS_ROUTE is not set + +# +# Network testing +# +# CONFIG_NET_PKTGEN is not set +CONFIG_NETPOLL=y +# CONFIG_NETPOLL_RX is not set +# CONFIG_NETPOLL_TRAP is not set +CONFIG_NET_POLL_CONTROLLER=y +# CONFIG_HAMRADIO is not set +# CONFIG_IRDA is not set +# CONFIG_BT is not set +CONFIG_NETDEVICES=y +CONFIG_DUMMY=m +# CONFIG_BONDING is not set +# CONFIG_EQUALIZER is not set +# CONFIG_TUN is not set +# CONFIG_ETHERTAP is not set + +# +# ARCnet devices +# +# CONFIG_ARCNET is not set + +# +# Ethernet (10 or 100Mbit) +# +CONFIG_NET_ETHERNET=y +CONFIG_MII=m +# CONFIG_HAPPYMEAL is not set +# CONFIG_SUNGEM is not set +# CONFIG_NET_VENDOR_3COM is not set + +# +# Tulip family network device support +# +CONFIG_NET_TULIP=y +# CONFIG_DE2104X is not set +CONFIG_TULIP=m +# CONFIG_TULIP_MWI is not set +# CONFIG_TULIP_MMIO is not set +# CONFIG_TULIP_NAPI is not set +# CONFIG_DE4X5 is not set +# CONFIG_WINBOND_840 is not set +# CONFIG_DM9102 is not set +# CONFIG_HP100 is not set +CONFIG_NET_PCI=y +# CONFIG_PCNET32 is not set +# CONFIG_AMD8111_ETH is not set +# CONFIG_ADAPTEC_STARFIRE is not set +# CONFIG_B44 is not set +# CONFIG_FORCEDETH is not set +# CONFIG_DGRS is not set +CONFIG_EEPRO100=m +# CONFIG_EEPRO100_PIO is not set +CONFIG_E100=m +# CONFIG_E100_NAPI is not set +# CONFIG_FEALNX is not set +# CONFIG_NATSEMI is not set +# CONFIG_NE2K_PCI is not set +# CONFIG_8139CP is not set +# CONFIG_8139TOO is not set +# CONFIG_SIS900 is not set +# CONFIG_EPIC100 is not set +# CONFIG_SUNDANCE is not set +# CONFIG_VIA_RHINE is not set +# CONFIG_VIA_VELOCITY is not set + +# +# Ethernet (1000 Mbit) +# +# CONFIG_ACENIC is not set +# CONFIG_DL2K is not set +CONFIG_E1000=y +# CONFIG_E1000_NAPI is not set +# CONFIG_NS83820 is not set +# CONFIG_HAMACHI is not set +# CONFIG_YELLOWFIN is not set +# CONFIG_R8169 is not set +# CONFIG_SK98LIN is not set +CONFIG_TIGON3=y + +# +# Ethernet (10000 Mbit) +# +# CONFIG_IXGB is not set +# CONFIG_S2IO is not set + +# +# Token Ring devices +# +# CONFIG_TR is not set + +# +# Wireless LAN (non-hamradio) +# +# CONFIG_NET_RADIO 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_NET_FC is not set +# CONFIG_SHAPER is not set +CONFIG_NETCONSOLE=y + +# +# ISDN subsystem +# +# CONFIG_ISDN is not set + +# +# Telephony Support +# +# CONFIG_PHONE is not set + +# +# Input device support +# +CONFIG_INPUT=y + +# +# 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_TSDEV is not set +# CONFIG_INPUT_EVDEV is not set +# CONFIG_INPUT_EVBUG is not set + +# +# Input I/O drivers +# +CONFIG_GAMEPORT=m +CONFIG_SOUND_GAMEPORT=m +# CONFIG_GAMEPORT_NS558 is not set +# CONFIG_GAMEPORT_L4 is not set +# CONFIG_GAMEPORT_EMU10K1 is not set +# CONFIG_GAMEPORT_VORTEX is not set +# CONFIG_GAMEPORT_FM801 is not set +# CONFIG_GAMEPORT_CS461x is not set +CONFIG_SERIO=y +CONFIG_SERIO_I8042=y +# CONFIG_SERIO_SERPORT is not set +# CONFIG_SERIO_CT82C710 is not set +# CONFIG_SERIO_PCIPS2 is not set +# CONFIG_SERIO_RAW 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_INPUT_MOUSE=y +CONFIG_MOUSE_PS2=y +# CONFIG_MOUSE_SERIAL is not set +# CONFIG_MOUSE_VSXXXAA is not set +# CONFIG_INPUT_JOYSTICK is not set +# CONFIG_INPUT_TOUCHSCREEN is not set +# CONFIG_INPUT_MISC is not set + +# +# Character devices +# +CONFIG_VT=y +CONFIG_VT_CONSOLE=y +CONFIG_HW_CONSOLE=y +CONFIG_SERIAL_NONSTANDARD=y +# CONFIG_ROCKETPORT is not set +# CONFIG_CYCLADES is not set +# CONFIG_SYNCLINK is not set +# CONFIG_SYNCLINKMP is not set +# CONFIG_N_HDLC is not set +# CONFIG_STALDRV is not set + +# +# Serial drivers +# +CONFIG_SERIAL_8250=y +CONFIG_SERIAL_8250_CONSOLE=y +CONFIG_SERIAL_8250_ACPI=y +CONFIG_SERIAL_8250_NR_UARTS=6 +CONFIG_SERIAL_8250_EXTENDED=y +CONFIG_SERIAL_8250_SHARE_IRQ=y +# CONFIG_SERIAL_8250_DETECT_IRQ is not set +# CONFIG_SERIAL_8250_MULTIPORT is not set +# CONFIG_SERIAL_8250_RSA is not set + +# +# Non-8250 serial port support +# +CONFIG_SERIAL_CORE=y +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 is not set +CONFIG_EFI_RTC=y +# CONFIG_DTLK is not set +# CONFIG_R3964 is not set +# CONFIG_APPLICOM is not set + +# +# Ftape, the floppy tape device driver +# +CONFIG_AGP=m +CONFIG_AGP_I460=m +CONFIG_DRM=y +CONFIG_DRM_TDFX=m +CONFIG_DRM_R128=m +CONFIG_DRM_RADEON=m +CONFIG_DRM_MGA=m +CONFIG_DRM_SIS=m +CONFIG_RAW_DRIVER=m +CONFIG_HPET=y +# CONFIG_HPET_RTC_IRQ is not set +CONFIG_HPET_MMAP=y +CONFIG_MAX_RAW_DEVS=256 + +# +# I2C support +# +# CONFIG_I2C is not set + +# +# Dallas's 1-wire bus +# +# CONFIG_W1 is not set + +# +# Misc devices +# + +# +# Multimedia devices +# +# CONFIG_VIDEO_DEV is not set + +# +# Digital Video Broadcasting Devices +# +# CONFIG_DVB is not set + +# +# Graphics support +# +# CONFIG_FB is not set + +# +# Console display driver support +# +CONFIG_VGA_CONSOLE=y +CONFIG_DUMMY_CONSOLE=y + +# +# Sound +# +# CONFIG_SOUND is not set + +# +# USB support +# +CONFIG_USB=y +# CONFIG_USB_DEBUG is not set + +# +# Miscellaneous USB options +# +CONFIG_USB_DEVICEFS=y +# CONFIG_USB_BANDWIDTH is not set +# CONFIG_USB_DYNAMIC_MINORS is not set +# CONFIG_USB_SUSPEND is not set +# CONFIG_USB_OTG is not set + +# +# USB Host Controller Drivers +# +CONFIG_USB_EHCI_HCD=m +# CONFIG_USB_EHCI_SPLIT_ISO is not set +# CONFIG_USB_EHCI_ROOT_HUB_TT is not set +CONFIG_USB_OHCI_HCD=m +CONFIG_USB_UHCI_HCD=y + +# +# USB Device Class drivers +# +# CONFIG_USB_BLUETOOTH_TTY is not set +# CONFIG_USB_ACM is not set +# CONFIG_USB_PRINTER is not set +CONFIG_USB_STORAGE=m +# CONFIG_USB_STORAGE_DEBUG is not set +# CONFIG_USB_STORAGE_RW_DETECT 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_HP8200e is not set +# CONFIG_USB_STORAGE_SDDR09 is not set +# CONFIG_USB_STORAGE_SDDR55 is not set +# CONFIG_USB_STORAGE_JUMPSHOT is not set + +# +# USB Human Interface Devices (HID) +# +CONFIG_USB_HID=y +CONFIG_USB_HIDINPUT=y +# CONFIG_HID_FF is not set +# CONFIG_USB_HIDDEV is not set +# CONFIG_USB_AIPTEK is not set +# CONFIG_USB_WACOM is not set +# CONFIG_USB_KBTAB is not set +# CONFIG_USB_POWERMATE is not set +# CONFIG_USB_MTOUCH is not set +# CONFIG_USB_EGALAX is not set +# CONFIG_USB_XPAD is not set +# CONFIG_USB_ATI_REMOTE is not set + +# +# USB Imaging devices +# +# CONFIG_USB_MDC800 is not set +# CONFIG_USB_MICROTEK is not set +# CONFIG_USB_HPUSBSCSI is not set + +# +# USB Multimedia devices +# +# CONFIG_USB_DABUSB is not set + +# +# Video4Linux support is needed for USB Multimedia device support +# + +# +# USB Network adaptors +# +# 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 + +# +# USB port drivers +# + +# +# USB Serial Converter support +# +# CONFIG_USB_SERIAL is not set + +# +# USB Miscellaneous drivers +# +# CONFIG_USB_EMI62 is not set +# CONFIG_USB_EMI26 is not set +# CONFIG_USB_TIGL 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_LED is not set +# CONFIG_USB_CYTHERM is not set +# CONFIG_USB_PHIDGETSERVO is not set +# CONFIG_USB_TEST is not set + +# +# USB Gadget Support +# +# CONFIG_USB_GADGET is not set + +# +# File systems +# +CONFIG_EXT2_FS=y +CONFIG_EXT2_FS_XATTR=y +CONFIG_EXT2_FS_POSIX_ACL=y +CONFIG_EXT2_FS_SECURITY=y +CONFIG_EXT3_FS=y +CONFIG_EXT3_FS_XATTR=y +CONFIG_EXT3_FS_POSIX_ACL=y +CONFIG_EXT3_FS_SECURITY=y +CONFIG_JBD=y +# CONFIG_JBD_DEBUG is not set +CONFIG_FS_MBCACHE=y +CONFIG_REISERFS_FS=y +# CONFIG_REISERFS_CHECK is not set +# CONFIG_REISERFS_PROC_INFO is not set +CONFIG_REISERFS_FS_XATTR=y +CONFIG_REISERFS_FS_POSIX_ACL=y +CONFIG_REISERFS_FS_SECURITY=y +# CONFIG_JFS_FS is not set +CONFIG_FS_POSIX_ACL=y +CONFIG_XFS_FS=y +# CONFIG_XFS_RT is not set +# CONFIG_XFS_QUOTA is not set +# CONFIG_XFS_SECURITY is not set +# CONFIG_XFS_POSIX_ACL is not set +# CONFIG_MINIX_FS is not set +# CONFIG_ROMFS_FS is not set +# CONFIG_QUOTA is not set +CONFIG_AUTOFS_FS=y +CONFIG_AUTOFS4_FS=y + +# +# CD-ROM/DVD Filesystems +# +CONFIG_ISO9660_FS=m +CONFIG_JOLIET=y +# CONFIG_ZISOFS is not set +CONFIG_UDF_FS=m +CONFIG_UDF_NLS=y + +# +# 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=m +# CONFIG_NTFS_DEBUG is not set +# CONFIG_NTFS_RW is not set + +# +# Pseudo filesystems +# +CONFIG_PROC_FS=y +CONFIG_PROC_KCORE=y +CONFIG_SYSFS=y +# CONFIG_DEVFS_FS is not set +# CONFIG_DEVPTS_FS_XATTR is not set +CONFIG_TMPFS=y +CONFIG_HUGETLBFS=y +CONFIG_HUGETLB_PAGE=y +CONFIG_RAMFS=y + +# +# 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_HPFS_FS is not set +# CONFIG_QNX4FS_FS is not set +# CONFIG_SYSV_FS is not set +# CONFIG_UFS_FS is not set + +# +# Network File Systems +# +CONFIG_NFS_FS=m +CONFIG_NFS_V3=y +CONFIG_NFS_V4=y +CONFIG_NFS_DIRECTIO=y +CONFIG_NFSD=m +CONFIG_NFSD_V3=y +CONFIG_NFSD_V4=y +CONFIG_NFSD_TCP=y +CONFIG_LOCKD=m +CONFIG_LOCKD_V4=y +CONFIG_EXPORTFS=m +CONFIG_SUNRPC=m +CONFIG_SUNRPC_GSS=m +CONFIG_RPCSEC_GSS_KRB5=m +# CONFIG_RPCSEC_GSS_SPKM3 is not set +CONFIG_SMB_FS=m +CONFIG_SMB_NLS_DEFAULT=y +CONFIG_SMB_NLS_REMOTE="cp437" +CONFIG_CIFS=m +# CONFIG_CIFS_STATS is not set +# CONFIG_CIFS_XATTR is not set +# CONFIG_CIFS_POSIX 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=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=y +# CONFIG_ULTRIX_PARTITION is not set +# CONFIG_SUN_PARTITION is not set +CONFIG_EFI_PARTITION=y + +# +# Native Language Support +# +CONFIG_NLS=y +CONFIG_NLS_DEFAULT="iso8859-1" +CONFIG_NLS_CODEPAGE_437=y +CONFIG_NLS_CODEPAGE_737=m +CONFIG_NLS_CODEPAGE_775=m +CONFIG_NLS_CODEPAGE_850=m +CONFIG_NLS_CODEPAGE_852=m +CONFIG_NLS_CODEPAGE_855=m +CONFIG_NLS_CODEPAGE_857=m +CONFIG_NLS_CODEPAGE_860=m +CONFIG_NLS_CODEPAGE_861=m +CONFIG_NLS_CODEPAGE_862=m +CONFIG_NLS_CODEPAGE_863=m +CONFIG_NLS_CODEPAGE_864=m +CONFIG_NLS_CODEPAGE_865=m +CONFIG_NLS_CODEPAGE_866=m +CONFIG_NLS_CODEPAGE_869=m +CONFIG_NLS_CODEPAGE_936=m +CONFIG_NLS_CODEPAGE_950=m +CONFIG_NLS_CODEPAGE_932=m +CONFIG_NLS_CODEPAGE_949=m +CONFIG_NLS_CODEPAGE_874=m +CONFIG_NLS_ISO8859_8=m +CONFIG_NLS_CODEPAGE_1250=m +CONFIG_NLS_CODEPAGE_1251=m +# CONFIG_NLS_ASCII is not set +CONFIG_NLS_ISO8859_1=y +CONFIG_NLS_ISO8859_2=m +CONFIG_NLS_ISO8859_3=m +CONFIG_NLS_ISO8859_4=m +CONFIG_NLS_ISO8859_5=m +CONFIG_NLS_ISO8859_6=m +CONFIG_NLS_ISO8859_7=m +CONFIG_NLS_ISO8859_9=m +CONFIG_NLS_ISO8859_13=m +CONFIG_NLS_ISO8859_14=m +CONFIG_NLS_ISO8859_15=m +CONFIG_NLS_KOI8_R=m +CONFIG_NLS_KOI8_U=m +CONFIG_NLS_UTF8=m + +# +# Library routines +# +# CONFIG_CRC_CCITT is not set +CONFIG_CRC32=y +# CONFIG_LIBCRC32C is not set + +# +# Profiling support +# +# CONFIG_PROFILING is not set + +# +# Kernel hacking +# +CONFIG_DEBUG_KERNEL=y +CONFIG_MAGIC_SYSRQ=y +# CONFIG_DEBUG_SLAB is not set +# CONFIG_DEBUG_SPINLOCK is not set +# CONFIG_DEBUG_SPINLOCK_SLEEP is not set +# CONFIG_DEBUG_INFO is not set +CONFIG_IA64_GRANULE_16MB=y +# CONFIG_IA64_GRANULE_64MB is not set +# CONFIG_IA64_PRINT_HAZARDS is not set +# CONFIG_DISABLE_VHPT is not set +# CONFIG_IA64_DEBUG_CMPXCHG is not set +# CONFIG_IA64_DEBUG_IRQ is not set +CONFIG_SYSVIPC_COMPAT=y + +# +# Security options +# +# CONFIG_SECURITY is not set + +# +# Cryptographic options +# +CONFIG_CRYPTO=y +# CONFIG_CRYPTO_HMAC is not set +# CONFIG_CRYPTO_NULL is not set +# CONFIG_CRYPTO_MD4 is not set +CONFIG_CRYPTO_MD5=m +# CONFIG_CRYPTO_SHA1 is not set +# CONFIG_CRYPTO_SHA256 is not set +# CONFIG_CRYPTO_SHA512 is not set +# CONFIG_CRYPTO_WHIRLPOOL is not set +CONFIG_CRYPTO_DES=m +# CONFIG_CRYPTO_BLOWFISH is not set +# CONFIG_CRYPTO_TWOFISH is not set +# CONFIG_CRYPTO_SERPENT is not set +# CONFIG_CRYPTO_AES is not set +# CONFIG_CRYPTO_CAST5 is not set +# CONFIG_CRYPTO_CAST6 is not set +# CONFIG_CRYPTO_TEA is not set +# CONFIG_CRYPTO_ARC4 is not set +# CONFIG_CRYPTO_KHAZAD is not set +# CONFIG_CRYPTO_DEFLATE is not set +# CONFIG_CRYPTO_MICHAEL_MIC is not set +# CONFIG_CRYPTO_CRC32C is not set +# CONFIG_CRYPTO_TEST is not set diff --git a/arch/ia64/configs/zx1_defconfig b/arch/ia64/configs/zx1_defconfig index e9eda7323..7ef08d376 100644 --- a/arch/ia64/configs/zx1_defconfig +++ b/arch/ia64/configs/zx1_defconfig @@ -1,5 +1,7 @@ # # Automatically generated make config: don't edit +# Linux kernel version: 2.6.9-rc2-aegl +# Mon Sep 27 19:03:13 2004 # # @@ -7,33 +9,45 @@ # CONFIG_EXPERIMENTAL=y # CONFIG_CLEAN_COMPILE is not set -CONFIG_STANDALONE=y CONFIG_BROKEN=y CONFIG_BROKEN_ON_SMP=y # # General setup # +CONFIG_LOCALVERSION="" CONFIG_SWAP=y CONFIG_SYSVIPC=y +# CONFIG_POSIX_MQUEUE is not set CONFIG_BSD_PROCESS_ACCT=y +# CONFIG_BSD_PROCESS_ACCT_V3 is not set CONFIG_SYSCTL=y +# CONFIG_AUDIT is not set CONFIG_LOG_BUF_SHIFT=17 CONFIG_HOTPLUG=y # CONFIG_IKCONFIG is not set # CONFIG_EMBEDDED is not set CONFIG_KALLSYMS=y +# CONFIG_KALLSYMS_ALL is not set +# CONFIG_KALLSYMS_EXTRA_PASS is not set CONFIG_FUTEX=y CONFIG_EPOLL=y CONFIG_IOSCHED_NOOP=y CONFIG_IOSCHED_AS=y CONFIG_IOSCHED_DEADLINE=y +CONFIG_IOSCHED_CFQ=y # CONFIG_CC_OPTIMIZE_FOR_SIZE is not set +CONFIG_SHMEM=y +# CONFIG_TINY_SHMEM is not set # # Loadable module support # -# CONFIG_MODULES is not set +CONFIG_MODULES=y +# CONFIG_MODULE_UNLOAD is not set +CONFIG_OBSOLETE_MODPARM=y +# CONFIG_MODVERSIONS is not set +# CONFIG_KMOD is not set # # Processor type and features @@ -44,54 +58,53 @@ CONFIG_MMU=y CONFIG_RWSEM_XCHGADD_ALGORITHM=y CONFIG_TIME_INTERPOLATION=y CONFIG_EFI=y -# CONFIG_ITANIUM is not set -CONFIG_MCKINLEY=y +CONFIG_GENERIC_IOMAP=y # CONFIG_IA64_GENERIC is not set # CONFIG_IA64_DIG is not set CONFIG_IA64_HP_ZX1=y # CONFIG_IA64_SGI_SN2 is not set # CONFIG_IA64_HP_SIM is not set +# CONFIG_ITANIUM is not set +CONFIG_MCKINLEY=y # CONFIG_IA64_PAGE_SIZE_4KB is not set # CONFIG_IA64_PAGE_SIZE_8KB is not set CONFIG_IA64_PAGE_SIZE_16KB=y # CONFIG_IA64_PAGE_SIZE_64KB is not set -CONFIG_ACPI=y -CONFIG_ACPI_INTERPRETER=y -CONFIG_ACPI_KERNEL_CONFIG=y CONFIG_IA64_L1_CACHE_SHIFT=7 -# CONFIG_MCKINLEY_ASTEP_SPECIFIC is not set # CONFIG_NUMA is not set CONFIG_VIRTUAL_MEM_MAP=y -CONFIG_IA64_MCA=y # CONFIG_IA64_CYCLONE is not set -CONFIG_PM=y CONFIG_IOSAPIC=y CONFIG_FORCE_MAX_ZONEORDER=18 -# CONFIG_HUGETLB_PAGE_SIZE_4GB is not set -# CONFIG_HUGETLB_PAGE_SIZE_1GB is not set -# CONFIG_HUGETLB_PAGE_SIZE_256MB is not set -CONFIG_HUGETLB_PAGE_SIZE_64MB=y -# CONFIG_HUGETLB_PAGE_SIZE_16MB is not set -# CONFIG_HUGETLB_PAGE_SIZE_4MB is not set -# CONFIG_HUGETLB_PAGE_SIZE_1MB is not set -# CONFIG_HUGETLB_PAGE_SIZE_256KB is not set -CONFIG_IA64_PAL_IDLE=y CONFIG_SMP=y CONFIG_NR_CPUS=16 +# CONFIG_HOTPLUG_CPU is not set # CONFIG_PREEMPT is not set CONFIG_HAVE_DEC_LOCK=y CONFIG_IA32_SUPPORT=y CONFIG_COMPAT=y CONFIG_PERFMON=y CONFIG_IA64_PALINFO=y + +# +# Firmware Drivers +# CONFIG_EFI_VARS=y +CONFIG_EFI_PCDP=y CONFIG_BINFMT_ELF=y CONFIG_BINFMT_MISC=y +# +# Power management and ACPI +# +CONFIG_PM=y +CONFIG_ACPI=y + # # ACPI (Advanced Configuration and Power Interface) Support # CONFIG_ACPI_BOOT=y +CONFIG_ACPI_INTERPRETER=y CONFIG_ACPI_BUTTON=y CONFIG_ACPI_FAN=y CONFIG_ACPI_PROCESSOR=y @@ -101,8 +114,13 @@ CONFIG_ACPI_BUS=y CONFIG_ACPI_POWER=y CONFIG_ACPI_PCI=y CONFIG_ACPI_SYSTEM=y + +# +# Bus options (PCI, PCMCIA) +# CONFIG_PCI=y CONFIG_PCI_DOMAINS=y +# CONFIG_PCI_MSI is not set CONFIG_PCI_LEGACY_PROC=y CONFIG_PCI_NAMES=y @@ -112,6 +130,7 @@ CONFIG_PCI_NAMES=y CONFIG_HOTPLUG_PCI=y # CONFIG_HOTPLUG_PCI_FAKE is not set CONFIG_HOTPLUG_PCI_ACPI=y +# CONFIG_HOTPLUG_PCI_ACPI_IBM is not set # CONFIG_HOTPLUG_PCI_CPCI is not set # CONFIG_HOTPLUG_PCI_PCIE is not set # CONFIG_HOTPLUG_PCI_SHPC is not set @@ -122,20 +141,27 @@ CONFIG_HOTPLUG_PCI_ACPI=y # CONFIG_PCMCIA is not set # -# Parallel port support +# Device Drivers # -# CONFIG_PARPORT is not set # # Generic Driver Options # +CONFIG_STANDALONE=y +CONFIG_PREVENT_FIRMWARE_BUILD=y # CONFIG_FW_LOADER is not set +# CONFIG_DEBUG_DRIVER is not set # # Memory Technology Devices (MTD) # # CONFIG_MTD is not set +# +# Parallel port support +# +# CONFIG_PARPORT is not set + # # Plug and Play support # @@ -143,7 +169,6 @@ CONFIG_HOTPLUG_PCI_ACPI=y # # Block devices # -# CONFIG_BLK_DEV_FD is not set # CONFIG_BLK_CPQ_DA is not set # CONFIG_BLK_CPQ_CISS_DA is not set # CONFIG_BLK_DEV_DAC960 is not set @@ -151,10 +176,11 @@ CONFIG_HOTPLUG_PCI_ACPI=y CONFIG_BLK_DEV_LOOP=y # CONFIG_BLK_DEV_CRYPTOLOOP is not set # CONFIG_BLK_DEV_NBD is not set +# CONFIG_BLK_DEV_SX8 is not set +# CONFIG_BLK_DEV_UB is not set CONFIG_BLK_DEV_RAM=y CONFIG_BLK_DEV_RAM_SIZE=4096 CONFIG_BLK_DEV_INITRD=y -# CONFIG_DCSSBLK is not set # # ATA/ATAPI/MFM/RLL support @@ -165,9 +191,9 @@ CONFIG_BLK_DEV_IDE=y # # Please see Documentation/ide.txt for help/info on IDE drives # +# CONFIG_BLK_DEV_IDE_SATA is not set CONFIG_BLK_DEV_IDEDISK=y # CONFIG_IDEDISK_MULTI_MODE is not set -# CONFIG_IDEDISK_STROKE is not set CONFIG_BLK_DEV_IDECD=y # CONFIG_BLK_DEV_IDETAPE is not set # CONFIG_BLK_DEV_IDEFLOPPY is not set @@ -187,7 +213,6 @@ CONFIG_BLK_DEV_GENERIC=y CONFIG_BLK_DEV_IDEDMA_PCI=y # CONFIG_BLK_DEV_IDEDMA_FORCED is not set # CONFIG_IDEDMA_PCI_AUTO is not set -CONFIG_BLK_DEV_ADMA=y # CONFIG_BLK_DEV_AEC62XX is not set # CONFIG_BLK_DEV_ALI15X3 is not set # CONFIG_BLK_DEV_AMD74XX is not set @@ -208,32 +233,12 @@ CONFIG_BLK_DEV_CMD64X=y # CONFIG_BLK_DEV_SLC90E66 is not set # CONFIG_BLK_DEV_TRM290 is not set # CONFIG_BLK_DEV_VIA82CXXX is not set +# CONFIG_IDE_ARM is not set CONFIG_BLK_DEV_IDEDMA=y # CONFIG_IDEDMA_IVB is not set # CONFIG_IDEDMA_AUTO is not set # CONFIG_BLK_DEV_HD is not set -# -# IEEE 1394 (FireWire) support -# -# CONFIG_IEEE1394 is not set - -# -# I2O device support -# - -# -# Multi-device support (RAID and LVM) -# -# CONFIG_MD is not set - -# -# Fusion MPT device support -# -CONFIG_FUSION=y -CONFIG_FUSION_BOOT=y -CONFIG_FUSION_MAX_SGE=40 - # # SCSI device support # @@ -254,21 +259,28 @@ CONFIG_CHR_DEV_SG=y # Some SCSI devices (e.g. CD jukebox) support multiple LUNs # CONFIG_SCSI_MULTI_LUN=y -CONFIG_SCSI_REPORT_LUNS=y CONFIG_SCSI_CONSTANTS=y CONFIG_SCSI_LOGGING=y +# +# SCSI Transport Attributes +# +CONFIG_SCSI_SPI_ATTRS=y +# CONFIG_SCSI_FC_ATTRS is not set + # # SCSI low-level drivers # # CONFIG_BLK_DEV_3W_XXXX_RAID is not set +# CONFIG_SCSI_3W_9XXX is not set # CONFIG_SCSI_ACARD is not set # CONFIG_SCSI_AACRAID is not set # CONFIG_SCSI_AIC7XXX is not set # CONFIG_SCSI_AIC7XXX_OLD is not set # CONFIG_SCSI_AIC79XX is not set # CONFIG_SCSI_ADVANSYS is not set -CONFIG_SCSI_MEGARAID=y +# CONFIG_MEGARAID_NEWGEN is not set +# CONFIG_MEGARAID_LEGACY is not set # CONFIG_SCSI_SATA is not set # CONFIG_SCSI_BUSLOGIC is not set # CONFIG_SCSI_CPQFCTS is not set @@ -285,6 +297,7 @@ CONFIG_SCSI_SYM53C8XX_DMA_ADDRESSING_MODE=1 CONFIG_SCSI_SYM53C8XX_DEFAULT_TAGS=16 CONFIG_SCSI_SYM53C8XX_MAX_TAGS=64 # CONFIG_SCSI_SYM53C8XX_IOMAPPED is not set +# CONFIG_SCSI_IPR is not set # CONFIG_SCSI_PCI2000 is not set # CONFIG_SCSI_PCI2220I is not set # CONFIG_SCSI_QLOGIC_ISP is not set @@ -301,6 +314,28 @@ CONFIG_SCSI_QLA2XXX=y # CONFIG_SCSI_DC390T is not set # CONFIG_SCSI_DEBUG is not set +# +# Multi-device support (RAID and LVM) +# +# CONFIG_MD is not set + +# +# Fusion MPT device support +# +CONFIG_FUSION=y +CONFIG_FUSION_MAX_SGE=40 +# CONFIG_FUSION_CTL is not set + +# +# IEEE 1394 (FireWire) support +# +# CONFIG_IEEE1394 is not set + +# +# I2O device support +# +# CONFIG_I2O is not set + # # Networking support # @@ -322,19 +357,17 @@ CONFIG_IP_MULTICAST=y # CONFIG_NET_IPGRE is not set # CONFIG_IP_MROUTE is not set # CONFIG_ARPD is not set -# CONFIG_INET_ECN 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_TUNNEL is not set # # IP: Virtual Server Configuration # # CONFIG_IP_VS is not set # CONFIG_IPV6 is not set -# CONFIG_DECNET is not set -# CONFIG_BRIDGE is not set CONFIG_NETFILTER=y # CONFIG_NETFILTER_DEBUG is not set @@ -353,10 +386,11 @@ CONFIG_IP_NF_ARPTABLES=y # # SCTP Configuration (EXPERIMENTAL) # -CONFIG_IPV6_SCTP__=y # CONFIG_IP_SCTP 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 @@ -371,21 +405,27 @@ CONFIG_IPV6_SCTP__=y # QoS and/or fair queueing # # CONFIG_NET_SCHED is not set +# CONFIG_NET_CLS_ROUTE is not set # # Network testing # # CONFIG_NET_PKTGEN is not set +# CONFIG_NETPOLL is not set +# CONFIG_NET_POLL_CONTROLLER is not set +# CONFIG_HAMRADIO is not set +# CONFIG_IRDA is not set +# CONFIG_BT is not set CONFIG_NETDEVICES=y +CONFIG_DUMMY=y +# CONFIG_BONDING is not set +# CONFIG_EQUALIZER is not set +# CONFIG_TUN is not set # # ARCnet devices # # CONFIG_ARCNET is not set -CONFIG_DUMMY=y -# CONFIG_BONDING is not set -# CONFIG_EQUALIZER is not set -# CONFIG_TUN is not set # # Ethernet (10 or 100Mbit) @@ -429,6 +469,7 @@ CONFIG_E100=y # CONFIG_EPIC100 is not set # CONFIG_SUNDANCE is not set # CONFIG_VIA_RHINE is not set +# CONFIG_VIA_VELOCITY is not set # # Ethernet (1000 Mbit) @@ -441,7 +482,6 @@ CONFIG_E1000=y # CONFIG_HAMACHI is not set # CONFIG_YELLOWFIN is not set # CONFIG_R8169 is not set -# CONFIG_SIS190 is not set # CONFIG_SK98LIN is not set CONFIG_TIGON3=y @@ -449,47 +489,39 @@ CONFIG_TIGON3=y # Ethernet (10000 Mbit) # # CONFIG_IXGB is not set -# CONFIG_FDDI is not set -# CONFIG_HIPPI is not set -# CONFIG_PPP is not set -# CONFIG_SLIP is not set - -# -# Wireless LAN (non-hamradio) -# -# CONFIG_NET_RADIO is not set +# CONFIG_S2IO is not set # # Token Ring devices # # CONFIG_TR is not set -# CONFIG_NET_FC is not set -# CONFIG_SHAPER is not set - -# -# Wan interfaces -# -# CONFIG_WAN is not set # -# Amateur Radio support +# Wireless LAN (non-hamradio) # -# CONFIG_HAMRADIO is not set +# CONFIG_NET_RADIO is not set # -# IrDA (infrared) support +# Wan interfaces # -# CONFIG_IRDA is not set +# 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_NET_FC is not set +# CONFIG_SHAPER is not set +# CONFIG_NETCONSOLE is not set # -# Bluetooth support +# ISDN subsystem # -# CONFIG_BT is not set +# CONFIG_ISDN is not set # -# ISDN subsystem +# Telephony Support # -# CONFIG_ISDN is not set +# CONFIG_PHONE is not set # # Input device support @@ -518,6 +550,7 @@ CONFIG_SERIO=y # CONFIG_SERIO_SERPORT is not set # CONFIG_SERIO_CT82C710 is not set # CONFIG_SERIO_PCIPS2 is not set +# CONFIG_SERIO_RAW is not set # # Input Device Drivers @@ -541,7 +574,6 @@ CONFIG_HW_CONSOLE=y # CONFIG_SERIAL_8250=y CONFIG_SERIAL_8250_CONSOLE=y -CONFIG_SERIAL_8250_HCDP=y CONFIG_SERIAL_8250_ACPI=y CONFIG_SERIAL_8250_NR_UARTS=8 CONFIG_SERIAL_8250_EXTENDED=y @@ -559,12 +591,6 @@ CONFIG_UNIX98_PTYS=y CONFIG_LEGACY_PTYS=y CONFIG_LEGACY_PTY_COUNT=256 -# -# Mice -# -# CONFIG_BUSMOUSE is not set -# CONFIG_QIC02_TAPE is not set - # # IPMI # @@ -575,8 +601,6 @@ CONFIG_LEGACY_PTY_COUNT=256 # # CONFIG_WATCHDOG is not set # CONFIG_HW_RANDOM is not set -# CONFIG_NVRAM is not set -# CONFIG_GEN_RTC is not set CONFIG_EFI_RTC=y # CONFIG_DTLK is not set # CONFIG_R3964 is not set @@ -585,7 +609,6 @@ CONFIG_EFI_RTC=y # # Ftape, the floppy tape device driver # -# CONFIG_FTAPE is not set CONFIG_AGP=y CONFIG_AGP_HP_ZX1=y CONFIG_DRM=y @@ -596,6 +619,7 @@ CONFIG_DRM_RADEON=y # CONFIG_DRM_MGA is not set # CONFIG_DRM_SIS is not set # CONFIG_RAW_DRIVER is not set +# CONFIG_HPET is not set # # I2C support @@ -608,11 +632,13 @@ CONFIG_I2C_CHARDEV=y # CONFIG_I2C_ALGOBIT=y CONFIG_I2C_ALGOPCF=y +# CONFIG_I2C_ALGOPCA is not set # # I2C Hardware Bus support # # 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 @@ -630,29 +656,55 @@ CONFIG_I2C_ALGOPCF=y # CONFIG_I2C_VIA is not set # CONFIG_I2C_VIAPRO is not set # CONFIG_I2C_VOODOO3 is not set +# CONFIG_I2C_PCA_ISA is not set # -# I2C Hardware Sensors Chip support +# Hardware Sensors Chip support # # CONFIG_I2C_SENSOR is not set # CONFIG_SENSORS_ADM1021 is not set +# CONFIG_SENSORS_ADM1025 is not set +# CONFIG_SENSORS_ADM1031 is not set # CONFIG_SENSORS_ASB100 is not set -# CONFIG_SENSORS_EEPROM is not set +# CONFIG_SENSORS_DS1621 is not set # CONFIG_SENSORS_FSCHER is not set # CONFIG_SENSORS_GL518SM is not set # CONFIG_SENSORS_IT87 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_LM90 is not set +# CONFIG_SENSORS_MAX1619 is not set +# CONFIG_SENSORS_SMSC47M1 is not set # CONFIG_SENSORS_VIA686A is not set # CONFIG_SENSORS_W83781D is not set # CONFIG_SENSORS_W83L785TS is not set +# CONFIG_SENSORS_W83627HF is not set + +# +# Other I2C Chip support +# +# CONFIG_SENSORS_EEPROM is not set +# CONFIG_SENSORS_PCF8574 is not set +# CONFIG_SENSORS_PCF8591 is not set +# CONFIG_SENSORS_RTC8564 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 +# +# Dallas's 1-wire bus +# +# CONFIG_W1 is not set + +# +# Misc devices +# + # # Multimedia devices # @@ -663,6 +715,167 @@ CONFIG_I2C_ALGOPCF=y # # CONFIG_DVB is not set +# +# Graphics support +# +CONFIG_FB=y +CONFIG_FB_MODE_HELPERS=y +# CONFIG_FB_CIRRUS is not set +# CONFIG_FB_PM2 is not set +# CONFIG_FB_CYBER2000 is not set +# CONFIG_FB_ASILIANT is not set +# CONFIG_FB_IMSTT is not set +# CONFIG_FB_RIVA is not set +# CONFIG_FB_MATROX is not set +# CONFIG_FB_RADEON_OLD is not set +CONFIG_FB_RADEON=y +CONFIG_FB_RADEON_I2C=y +CONFIG_FB_RADEON_DEBUG=y +# CONFIG_FB_ATY128 is not set +# CONFIG_FB_ATY is not set +# CONFIG_FB_SIS is not set +# CONFIG_FB_NEOMAGIC is not set +# CONFIG_FB_KYRO is not set +# CONFIG_FB_3DFX is not set +# CONFIG_FB_VOODOO1 is not set +# CONFIG_FB_TRIDENT is not set +# CONFIG_FB_PM3 is not set +# CONFIG_FB_VIRTUAL is not set + +# +# Console display driver support +# +CONFIG_VGA_CONSOLE=y +CONFIG_DUMMY_CONSOLE=y +CONFIG_FRAMEBUFFER_CONSOLE=y +# 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 + +# +# Sound +# +# CONFIG_SOUND is not set + +# +# USB support +# +CONFIG_USB=y +# CONFIG_USB_DEBUG is not set + +# +# Miscellaneous USB options +# +# CONFIG_USB_DEVICEFS is not set +CONFIG_USB_BANDWIDTH=y +# CONFIG_USB_DYNAMIC_MINORS is not set +# CONFIG_USB_SUSPEND is not set +# CONFIG_USB_OTG is not set + +# +# USB Host Controller Drivers +# +CONFIG_USB_EHCI_HCD=y +# CONFIG_USB_EHCI_SPLIT_ISO is not set +# CONFIG_USB_EHCI_ROOT_HUB_TT is not set +CONFIG_USB_OHCI_HCD=y +CONFIG_USB_UHCI_HCD=y + +# +# USB Device Class drivers +# +# CONFIG_USB_BLUETOOTH_TTY is not set +# CONFIG_USB_ACM is not set +# CONFIG_USB_PRINTER is not set +CONFIG_USB_STORAGE=y +# CONFIG_USB_STORAGE_DEBUG is not set +# CONFIG_USB_STORAGE_RW_DETECT 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_HP8200e is not set +# CONFIG_USB_STORAGE_SDDR09 is not set +# CONFIG_USB_STORAGE_SDDR55 is not set +# CONFIG_USB_STORAGE_JUMPSHOT is not set + +# +# USB Human Interface Devices (HID) +# +CONFIG_USB_HID=y +CONFIG_USB_HIDINPUT=y +# CONFIG_HID_FF is not set +CONFIG_USB_HIDDEV=y +# CONFIG_USB_AIPTEK is not set +# CONFIG_USB_WACOM is not set +# CONFIG_USB_KBTAB is not set +# CONFIG_USB_POWERMATE is not set +# CONFIG_USB_MTOUCH is not set +# CONFIG_USB_EGALAX is not set +# CONFIG_USB_XPAD is not set +# CONFIG_USB_ATI_REMOTE is not set + +# +# USB Imaging devices +# +# CONFIG_USB_MDC800 is not set +# CONFIG_USB_MICROTEK is not set +# CONFIG_USB_HPUSBSCSI is not set + +# +# USB Multimedia devices +# +# CONFIG_USB_DABUSB is not set + +# +# Video4Linux support is needed for USB Multimedia device support +# + +# +# USB Network adaptors +# +# 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 + +# +# USB port drivers +# + +# +# USB Serial Converter support +# +# CONFIG_USB_SERIAL is not set + +# +# USB Miscellaneous drivers +# +# CONFIG_USB_EMI62 is not set +# CONFIG_USB_EMI26 is not set +# CONFIG_USB_TIGL 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_LED is not set +# CONFIG_USB_CYTHERM is not set +# CONFIG_USB_PHIDGETSERVO is not set + +# +# USB Gadget Support +# +# CONFIG_USB_GADGET is not set + # # File systems # @@ -693,6 +906,7 @@ CONFIG_ISO9660_FS=y CONFIG_JOLIET=y # CONFIG_ZISOFS is not set CONFIG_UDF_FS=y +CONFIG_UDF_NLS=y # # DOS/FAT/NT Filesystems @@ -700,6 +914,8 @@ CONFIG_UDF_FS=y 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 # @@ -707,6 +923,7 @@ CONFIG_VFAT_FS=y # CONFIG_PROC_FS=y CONFIG_PROC_KCORE=y +CONFIG_SYSFS=y # CONFIG_DEVFS_FS is not set # CONFIG_DEVPTS_FS_XATTR is not set CONFIG_TMPFS=y @@ -747,11 +964,12 @@ CONFIG_LOCKD_V4=y CONFIG_EXPORTFS=y CONFIG_SUNRPC=y CONFIG_SUNRPC_GSS=y +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_INTERMEZZO_FS is not set # CONFIG_AFS_FS is not set # @@ -769,7 +987,6 @@ CONFIG_MSDOS_PARTITION=y # CONFIG_SOLARIS_X86_PARTITION is not set # CONFIG_UNIXWARE_DISKLABEL is not set # CONFIG_LDM_PARTITION is not set -# CONFIG_NEC98_PARTITION is not set # CONFIG_SGI_PARTITION is not set # CONFIG_ULTRIX_PARTITION is not set # CONFIG_SUN_PARTITION is not set @@ -803,6 +1020,7 @@ CONFIG_NLS_CODEPAGE_874=y CONFIG_NLS_ISO8859_8=y # CONFIG_NLS_CODEPAGE_1250 is not set CONFIG_NLS_CODEPAGE_1251=y +# CONFIG_NLS_ASCII is not set CONFIG_NLS_ISO8859_1=y CONFIG_NLS_ISO8859_2=y CONFIG_NLS_ISO8859_3=y @@ -818,227 +1036,12 @@ CONFIG_NLS_KOI8_R=y CONFIG_NLS_KOI8_U=y CONFIG_NLS_UTF8=y -# -# Graphics support -# -CONFIG_FB=y -# CONFIG_FB_CIRRUS is not set -# CONFIG_FB_PM2 is not set -# CONFIG_FB_CYBER2000 is not set -# CONFIG_FB_IMSTT is not set -# CONFIG_FB_RIVA is not set -# CONFIG_FB_MATROX is not set -# CONFIG_FB_RADEON_OLD is not set -CONFIG_FB_RADEON=y -CONFIG_FB_RADEON_I2C=y -CONFIG_FB_RADEON_DEBUG=y -# CONFIG_FB_ATY128 is not set -# CONFIG_FB_ATY is not set -# CONFIG_FB_SIS is not set -# CONFIG_FB_NEOMAGIC is not set -# CONFIG_FB_KYRO is not set -# CONFIG_FB_3DFX is not set -# CONFIG_FB_VOODOO1 is not set -# CONFIG_FB_TRIDENT is not set -# CONFIG_FB_PM3 is not set -# CONFIG_FB_VIRTUAL is not set - -# -# Console display driver support -# -CONFIG_VGA_CONSOLE=y -# CONFIG_MDA_CONSOLE is not set -CONFIG_DUMMY_CONSOLE=y -CONFIG_FRAMEBUFFER_CONSOLE=y -CONFIG_PCI_CONSOLE=y -# 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 - -# -# Sound -# -CONFIG_SOUND=y - -# -# Advanced Linux Sound Architecture -# -CONFIG_SND=y -CONFIG_SND_SEQUENCER=y -# CONFIG_SND_SEQ_DUMMY is not set -CONFIG_SND_OSSEMUL=y -CONFIG_SND_MIXER_OSS=y -CONFIG_SND_PCM_OSS=y -CONFIG_SND_SEQUENCER_OSS=y -# CONFIG_SND_VERBOSE_PRINTK is not set -# CONFIG_SND_DEBUG is not set - -# -# Generic devices -# -# CONFIG_SND_DUMMY is not set -# CONFIG_SND_VIRMIDI is not set -# CONFIG_SND_MTPAV is not set -# CONFIG_SND_SERIAL_U16550 is not set -# CONFIG_SND_MPU401 is not set - -# -# PCI devices -# -# CONFIG_SND_ALI5451 is not set -# CONFIG_SND_AZT3328 is not set -# CONFIG_SND_BT87X is not set -# CONFIG_SND_CS46XX is not set -# CONFIG_SND_CS4281 is not set -# CONFIG_SND_EMU10K1 is not set -# CONFIG_SND_KORG1212 is not set -# CONFIG_SND_NM256 is not set -# CONFIG_SND_RME32 is not set -# CONFIG_SND_RME96 is not set -# CONFIG_SND_RME9652 is not set -# CONFIG_SND_HDSP is not set -# CONFIG_SND_TRIDENT is not set -# CONFIG_SND_YMFPCI is not set -# CONFIG_SND_ALS4000 is not set -# CONFIG_SND_CMIPCI is not set -# CONFIG_SND_ENS1370 is not set -# CONFIG_SND_ENS1371 is not set -# CONFIG_SND_ES1938 is not set -# CONFIG_SND_ES1968 is not set -# CONFIG_SND_MAESTRO3 is not set -CONFIG_SND_FM801=y -# CONFIG_SND_ICE1712 is not set -# CONFIG_SND_ICE1724 is not set -# CONFIG_SND_INTEL8X0 is not set -# CONFIG_SND_SONICVIBES is not set -# CONFIG_SND_VIA82XX is not set -# CONFIG_SND_VX222 is not set - -# -# ALSA USB devices -# -# CONFIG_SND_USB_AUDIO is not set - -# -# Open Sound System -# -# CONFIG_SOUND_PRIME is not set - -# -# USB support -# -CONFIG_USB=y -# CONFIG_USB_DEBUG is not set - -# -# Miscellaneous USB options -# -# CONFIG_USB_DEVICEFS is not set -CONFIG_USB_BANDWIDTH=y -# CONFIG_USB_DYNAMIC_MINORS is not set - -# -# USB Host Controller Drivers -# -CONFIG_USB_EHCI_HCD=y -CONFIG_USB_OHCI_HCD=y -CONFIG_USB_UHCI_HCD=y - -# -# USB Device Class drivers -# -# CONFIG_USB_AUDIO is not set -# CONFIG_USB_BLUETOOTH_TTY is not set -# CONFIG_USB_MIDI is not set -# CONFIG_USB_ACM is not set -# CONFIG_USB_PRINTER is not set -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_HP8200e is not set -# CONFIG_USB_STORAGE_SDDR09 is not set -# CONFIG_USB_STORAGE_SDDR55 is not set -# CONFIG_USB_STORAGE_JUMPSHOT is not set - -# -# USB Human Interface Devices (HID) -# -CONFIG_USB_HID=y -CONFIG_USB_HIDINPUT=y -# CONFIG_HID_FF is not set -CONFIG_USB_HIDDEV=y -# CONFIG_USB_AIPTEK is not set -# CONFIG_USB_WACOM is not set -# CONFIG_USB_KBTAB is not set -# CONFIG_USB_POWERMATE is not set -# CONFIG_USB_XPAD is not set - -# -# USB Imaging devices -# -# CONFIG_USB_MDC800 is not set -# CONFIG_USB_MICROTEK is not set -# CONFIG_USB_HPUSBSCSI is not set - -# -# USB Multimedia devices -# -# CONFIG_USB_DABUSB is not set - -# -# Video4Linux support is needed for USB Multimedia device support -# - -# -# USB Network adaptors -# -# 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 - -# -# USB port drivers -# - -# -# USB Serial Converter support -# -# CONFIG_USB_SERIAL is not set - -# -# USB Miscellaneous drivers -# -# CONFIG_USB_EMI62 is not set -# CONFIG_USB_EMI26 is not set -# CONFIG_USB_TIGL 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_LED is not set - -# -# USB Gadget Support -# -# CONFIG_USB_GADGET is not set - # # Library routines # +# CONFIG_CRC_CCITT is not set CONFIG_CRC32=y +# CONFIG_LIBCRC32C is not set # # Profiling support @@ -1048,18 +1051,19 @@ CONFIG_CRC32=y # # Kernel hacking # -CONFIG_IA64_GRANULE_16MB=y -# CONFIG_IA64_GRANULE_64MB is not set CONFIG_DEBUG_KERNEL=y -CONFIG_IA64_PRINT_HAZARDS=y -# CONFIG_DISABLE_VHPT is not set CONFIG_MAGIC_SYSRQ=y # CONFIG_DEBUG_SLAB is not set # CONFIG_DEBUG_SPINLOCK is not set # CONFIG_DEBUG_SPINLOCK_SLEEP is not set +# CONFIG_DEBUG_INFO is not set +CONFIG_IA64_GRANULE_16MB=y +# CONFIG_IA64_GRANULE_64MB is not set +CONFIG_IA64_PRINT_HAZARDS=y +# CONFIG_DISABLE_VHPT is not set # CONFIG_IA64_DEBUG_CMPXCHG is not set # CONFIG_IA64_DEBUG_IRQ is not set -# CONFIG_DEBUG_INFO is not set +CONFIG_SYSVIPC_COMPAT=y # # Security options @@ -1069,4 +1073,26 @@ CONFIG_MAGIC_SYSRQ=y # # Cryptographic options # -# CONFIG_CRYPTO is not set +CONFIG_CRYPTO=y +# CONFIG_CRYPTO_HMAC is not set +# CONFIG_CRYPTO_NULL is not set +# CONFIG_CRYPTO_MD4 is not set +CONFIG_CRYPTO_MD5=y +# CONFIG_CRYPTO_SHA1 is not set +# CONFIG_CRYPTO_SHA256 is not set +# CONFIG_CRYPTO_SHA512 is not set +# CONFIG_CRYPTO_WHIRLPOOL is not set +CONFIG_CRYPTO_DES=y +# CONFIG_CRYPTO_BLOWFISH is not set +# CONFIG_CRYPTO_TWOFISH is not set +# CONFIG_CRYPTO_SERPENT is not set +# CONFIG_CRYPTO_AES is not set +# CONFIG_CRYPTO_CAST5 is not set +# CONFIG_CRYPTO_CAST6 is not set +# CONFIG_CRYPTO_TEA is not set +# CONFIG_CRYPTO_ARC4 is not set +# CONFIG_CRYPTO_KHAZAD is not set +# CONFIG_CRYPTO_DEFLATE is not set +# CONFIG_CRYPTO_MICHAEL_MIC is not set +# CONFIG_CRYPTO_CRC32C is not set +# CONFIG_CRYPTO_TEST is not set diff --git a/arch/ia64/hp/common/sba_iommu.c b/arch/ia64/hp/common/sba_iommu.c index 5333e6190..e50c446e0 100644 --- a/arch/ia64/hp/common/sba_iommu.c +++ b/arch/ia64/hp/common/sba_iommu.c @@ -191,7 +191,7 @@ static unsigned long iovp_shift; static unsigned long iovp_mask; struct ioc { - void *ioc_hpa; /* I/O MMU base address */ + void __iomem *ioc_hpa; /* I/O MMU base address */ char *res_map; /* resource map, bit == pdir entry */ u64 *pdir_base; /* physical base address */ unsigned long ibase; /* pdir IOV Space base */ @@ -203,6 +203,9 @@ struct ioc { /* clearing pdir to prevent races with allocations. */ unsigned int res_bitshift; /* from the RIGHT! */ unsigned int res_size; /* size of resource map in bytes */ +#ifdef CONFIG_NUMA + unsigned int node; /* node where this IOC lives */ +#endif #if DELAYED_RESOURCE_CNT > 0 spinlock_t saved_lock; /* may want to try to get this on a separate cacheline */ /* than res_lock for bigger systems. */ @@ -1057,7 +1060,24 @@ sba_alloc_coherent (struct device *dev, size_t size, dma_addr_t *dma_handle, int struct ioc *ioc; void *addr; + ioc = GET_IOC(dev); + ASSERT(ioc); + +#ifdef CONFIG_NUMA + { + struct page *page; + page = alloc_pages_node(ioc->node == MAX_NUMNODES ? + numa_node_id() : ioc->node, flags, + get_order(size)); + + if (unlikely(!page)) + return NULL; + + addr = page_address(page); + } +#else addr = (void *) __get_free_pages(flags, get_order(size)); +#endif if (unlikely(!addr)) return NULL; @@ -1081,8 +1101,6 @@ sba_alloc_coherent (struct device *dev, size_t size, dma_addr_t *dma_handle, int * If device can't bypass or bypass is disabled, pass the 32bit fake * device to map single to get an iova mapping. */ - ioc = GET_IOC(dev); - ASSERT(ioc); *dma_handle = sba_map_single(&ioc->sac_only_dev->dev, addr, size, 0); return addr; @@ -1135,7 +1153,7 @@ sba_fill_pdir( { struct scatterlist *dma_sg = startsg; /* pointer to current DMA */ int n_mappings = 0; - u64 *pdirp = 0; + u64 *pdirp = NULL; unsigned long dma_offset = 0; dma_sg--; @@ -1799,6 +1817,10 @@ ioc_show(struct seq_file *s, void *v) seq_printf(s, "Hewlett Packard %s IOC rev %d.%d\n", ioc->name, ((ioc->rev >> 4) & 0xF), (ioc->rev & 0xF)); +#ifdef CONFIG_NUMA + if (ioc->node != MAX_NUMNODES) + seq_printf(s, "NUMA node : %d\n", ioc->node); +#endif seq_printf(s, "IOVA size : %ld MB\n", ((ioc->pdir_size >> 3) * iovp_size)/(1024*1024)); seq_printf(s, "IOVA page size : %ld kb\n", iovp_size/1024); @@ -1853,7 +1875,7 @@ ioc_proc_init(void) { struct proc_dir_entry *dir, *entry; - dir = proc_mkdir("bus/mckinley", 0); + dir = proc_mkdir("bus/mckinley", NULL); if (!dir) return; @@ -1899,6 +1921,58 @@ sba_connect_bus(struct pci_bus *bus) printk(KERN_WARNING "No IOC for PCI Bus %04x:%02x in ACPI\n", pci_domain_nr(bus), bus->number); } +#ifdef CONFIG_NUMA +static void __init +sba_map_ioc_to_node(struct ioc *ioc, acpi_handle handle) +{ + struct acpi_buffer buffer = {ACPI_ALLOCATE_BUFFER, NULL}; + union acpi_object *obj; + acpi_handle phandle; + unsigned int node; + + ioc->node = MAX_NUMNODES; + + /* + * Check for a _PXM on this node first. We don't typically see + * one here, so we'll end up getting it from the parent. + */ + if (ACPI_FAILURE(acpi_evaluate_object(handle, "_PXM", NULL, &buffer))) { + if (ACPI_FAILURE(acpi_get_parent(handle, &phandle))) + return; + + /* Reset the acpi buffer */ + buffer.length = ACPI_ALLOCATE_BUFFER; + buffer.pointer = NULL; + + if (ACPI_FAILURE(acpi_evaluate_object(phandle, "_PXM", NULL, + &buffer))) + return; + } + + if (!buffer.length || !buffer.pointer) + return; + + obj = buffer.pointer; + + if (obj->type != ACPI_TYPE_INTEGER || + obj->integer.value >= MAX_PXM_DOMAINS) { + acpi_os_free(buffer.pointer); + return; + } + + node = pxm_to_nid_map[obj->integer.value]; + acpi_os_free(buffer.pointer); + + if (node >= MAX_NUMNODES || !node_online(node)) + return; + + ioc->node = node; + return; +} +#else +#define sba_map_ioc_to_node(ioc, handle) +#endif + static int __init acpi_sba_ioc_add(struct acpi_device *device) { @@ -1941,6 +2015,8 @@ acpi_sba_ioc_add(struct acpi_device *device) if (!ioc) return 1; + /* setup NUMA node association */ + sba_map_ioc_to_node(ioc, device->handle); return 0; } diff --git a/arch/ia64/hp/sim/simserial.c b/arch/ia64/hp/sim/simserial.c index 381277884..af4b7143d 100644 --- a/arch/ia64/hp/sim/simserial.c +++ b/arch/ia64/hp/sim/simserial.c @@ -50,10 +50,6 @@ #define _INLINE_ inline #endif -#ifndef MIN -#define MIN(a,b) ((a) < (b) ? (a) : (b)) -#endif - #define IRQ_T(info) ((info->flags & ASYNC_SHARE_IRQ) ? SA_SHIRQ : SA_INTERRUPT) #define SSC_GETCHAR 21 @@ -275,7 +271,7 @@ static _INLINE_ void transmit_chars(struct async_struct *info, int *intr_done) * Then from the beginning of the buffer until necessary */ - count = MIN(CIRC_CNT(info->xmit.head, info->xmit.tail, SERIAL_XMIT_SIZE), + count = min(CIRC_CNT(info->xmit.head, info->xmit.tail, SERIAL_XMIT_SIZE), SERIAL_XMIT_SIZE - info->xmit.tail); console->write(console, info->xmit.buf+info->xmit.tail, count); @@ -1055,7 +1051,7 @@ simrs_init (void) ia64_ssc_connect_irq(KEYBOARD_INTR, state->irq); } - printk(KERN_INFO "ttyS%02d at 0x%04lx (irq = %d) is a %s\n", + printk(KERN_INFO "ttyS%d at 0x%04lx (irq = %d) is a %s\n", state->line, state->port, state->irq, uart_config[state->type].name); diff --git a/arch/ia64/ia32/elfcore32.h b/arch/ia64/ia32/elfcore32.h index 36f8d42e3..b68534d18 100644 --- a/arch/ia64/ia32/elfcore32.h +++ b/arch/ia64/ia32/elfcore32.h @@ -103,11 +103,15 @@ static inline int elf_core_copy_task_fpregs(struct task_struct *tsk, struct pt_regs *regs, elf_fpregset_t *fpu) { struct ia32_user_i387_struct *fpstate = (void*)fpu; + mm_segment_t old_fs; if (!tsk->used_math) return 0; - - save_ia32_fpstate(tsk, fpstate); + + old_fs = get_fs(); + set_fs(KERNEL_DS); + save_ia32_fpstate(tsk, (struct ia32_user_i387_struct __user *) fpstate); + set_fs(old_fs); return 1; } @@ -117,11 +121,15 @@ static inline int elf_core_copy_task_xfpregs(struct task_struct *tsk, elf_fpxregset_t *xfpu) { struct ia32_user_fxsr_struct *fpxstate = (void*) xfpu; + mm_segment_t old_fs; if (!tsk->used_math) return 0; - save_ia32_fpxstate(tsk, fpxstate); + old_fs = get_fs(); + set_fs(KERNEL_DS); + save_ia32_fpxstate(tsk, (struct ia32_user_fxsr_struct __user *) fpxstate); + set_fs(old_fs); return 1; } diff --git a/arch/ia64/ia32/ia32_ldt.c b/arch/ia64/ia32/ia32_ldt.c index 4cc8242de..a152738c7 100644 --- a/arch/ia64/ia32/ia32_ldt.c +++ b/arch/ia64/ia32/ia32_ldt.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2001 Hewlett-Packard Co + * Copyright (C) 2001, 2004 Hewlett-Packard Co * David Mosberger-Tang * * Adapted from arch/i386/kernel/ldt.c @@ -17,25 +17,24 @@ #include "ia32priv.h" -#define P(p) ((void *) (unsigned long) (p)) - /* * read_ldt() is not really atomic - this is not a problem since synchronization of reads * and writes done to the LDT has to be assured by user-space anyway. Writes are atomic, * to protect the security checks done on new descriptors. */ static int -read_ldt (void *ptr, unsigned long bytecount) +read_ldt (void __user *ptr, unsigned long bytecount) { - char *src, *dst, buf[256]; /* temporary buffer (don't overflow kernel stack!) */ unsigned long bytes_left, n; + char __user *src, *dst; + char buf[256]; /* temporary buffer (don't overflow kernel stack!) */ if (bytecount > IA32_LDT_ENTRIES*IA32_LDT_ENTRY_SIZE) bytecount = IA32_LDT_ENTRIES*IA32_LDT_ENTRY_SIZE; bytes_left = bytecount; - src = (void *) IA32_LDT_OFFSET; + src = (void __user *) IA32_LDT_OFFSET; dst = ptr; while (bytes_left) { @@ -61,7 +60,7 @@ read_ldt (void *ptr, unsigned long bytecount) } static int -read_default_ldt (void * ptr, unsigned long bytecount) +read_default_ldt (void __user * ptr, unsigned long bytecount) { unsigned long size; int err; @@ -80,7 +79,7 @@ read_default_ldt (void * ptr, unsigned long bytecount) } static int -write_ldt (void * ptr, unsigned long bytecount, int oldmode) +write_ldt (void __user * ptr, unsigned long bytecount, int oldmode) { struct ia32_user_desc ldt_info; __u64 entry; @@ -120,7 +119,7 @@ write_ldt (void * ptr, unsigned long bytecount, int oldmode) * memory, but we still need to guard against out-of-memory, hence we must use * put_user(). */ - ret = __put_user(entry, (__u64 *) IA32_LDT_OFFSET + ldt_info.entry_number); + ret = __put_user(entry, (__u64 __user *) IA32_LDT_OFFSET + ldt_info.entry_number); ia32_load_segment_descriptors(current); return ret; } @@ -132,16 +131,16 @@ sys32_modify_ldt (int func, unsigned int ptr, unsigned int bytecount) switch (func) { case 0: - ret = read_ldt(P(ptr), bytecount); + ret = read_ldt(compat_ptr(ptr), bytecount); break; case 1: - ret = write_ldt(P(ptr), bytecount, 1); + ret = write_ldt(compat_ptr(ptr), bytecount, 1); break; case 2: - ret = read_default_ldt(P(ptr), bytecount); + ret = read_default_ldt(compat_ptr(ptr), bytecount); break; case 0x11: - ret = write_ldt(P(ptr), bytecount, 0); + ret = write_ldt(compat_ptr(ptr), bytecount, 0); break; } return ret; diff --git a/arch/ia64/kernel/acpi-ext.c b/arch/ia64/kernel/acpi-ext.c index b70f6de25..2623df5e2 100644 --- a/arch/ia64/kernel/acpi-ext.c +++ b/arch/ia64/kernel/acpi-ext.c @@ -62,7 +62,7 @@ acpi_find_vendor_resource(acpi_handle obj, struct acpi_vendor_descriptor *id, struct acpi_vendor_info info; info.descriptor = id; - info.data = 0; + info.data = NULL; acpi_walk_resources(obj, METHOD_NAME__CRS, acpi_vendor_resource_match, &info); if (!info.data) diff --git a/arch/ia64/kernel/asm-offsets.c b/arch/ia64/kernel/asm-offsets.c index f166571ec..b1ab994b2 100644 --- a/arch/ia64/kernel/asm-offsets.c +++ b/arch/ia64/kernel/asm-offsets.c @@ -34,6 +34,11 @@ void foo(void) BLANK(); + DEFINE(TI_FLAGS, offsetof(struct thread_info, flags)); + DEFINE(TI_PRE_COUNT, offsetof(struct thread_info, preempt_count)); + + BLANK(); + DEFINE(IA64_TASK_BLOCKED_OFFSET,offsetof (struct task_struct, blocked)); DEFINE(IA64_TASK_CLEAR_CHILD_TID_OFFSET,offsetof (struct task_struct, clear_child_tid)); DEFINE(IA64_TASK_GROUP_LEADER_OFFSET, offsetof (struct task_struct, group_leader)); @@ -188,18 +193,10 @@ void foo(void) DEFINE(IA64_CLONE_VM, CLONE_VM); BLANK(); - /* used by fsys_gettimeofday in arch/ia64/kernel/fsys.S */ - DEFINE(IA64_CPUINFO_ITM_DELTA_OFFSET, offsetof (struct cpuinfo_ia64, itm_delta)); - DEFINE(IA64_CPUINFO_ITM_NEXT_OFFSET, offsetof (struct cpuinfo_ia64, itm_next)); DEFINE(IA64_CPUINFO_NSEC_PER_CYC_OFFSET, offsetof (struct cpuinfo_ia64, nsec_per_cyc)); DEFINE(IA64_TIMESPEC_TV_NSEC_OFFSET, offsetof (struct timespec, tv_nsec)); - DEFINE(CLONE_IDLETASK_BIT, 12); -#if CLONE_IDLETASK != (1 << 12) -# error "CLONE_IDLETASK_BIT incorrect, please fix" -#endif - DEFINE(CLONE_SETTLS_BIT, 19); #if CLONE_SETTLS != (1<<19) # error "CLONE_SETTLS_BIT incorrect, please fix" @@ -207,5 +204,21 @@ void foo(void) BLANK(); DEFINE(IA64_MCA_TLB_INFO_SIZE, sizeof (struct ia64_mca_tlb_info)); + /* used by head.S */ + DEFINE(IA64_CPUINFO_NSEC_PER_CYC_OFFSET, offsetof (struct cpuinfo_ia64, nsec_per_cyc)); + BLANK(); + /* used by fsys_gettimeofday in arch/ia64/kernel/fsys.S */ + DEFINE(IA64_TIME_INTERPOLATOR_ADDRESS_OFFSET, offsetof (struct time_interpolator, addr)); + DEFINE(IA64_TIME_INTERPOLATOR_SOURCE_OFFSET, offsetof (struct time_interpolator, source)); + DEFINE(IA64_TIME_INTERPOLATOR_SHIFT_OFFSET, offsetof (struct time_interpolator, shift)); + DEFINE(IA64_TIME_INTERPOLATOR_NSEC_OFFSET, offsetof (struct time_interpolator, nsec_per_cyc)); + DEFINE(IA64_TIME_INTERPOLATOR_OFFSET_OFFSET, offsetof (struct time_interpolator, offset)); + DEFINE(IA64_TIME_INTERPOLATOR_LAST_CYCLE_OFFSET, offsetof (struct time_interpolator, last_cycle)); + DEFINE(IA64_TIME_INTERPOLATOR_LAST_COUNTER_OFFSET, offsetof (struct time_interpolator, last_counter)); + DEFINE(IA64_TIME_INTERPOLATOR_JITTER_OFFSET, offsetof (struct time_interpolator, jitter)); + DEFINE(IA64_TIME_SOURCE_CPU, TIME_SOURCE_CPU); + DEFINE(IA64_TIME_SOURCE_MMIO64, TIME_SOURCE_MMIO64); + DEFINE(IA64_TIME_SOURCE_MMIO32, TIME_SOURCE_MMIO32); + DEFINE(IA64_TIMESPEC_TV_NSEC_OFFSET, offsetof (struct timespec, tv_nsec)); } diff --git a/arch/ia64/kernel/cyclone.c b/arch/ia64/kernel/cyclone.c index 19300e958..f8261d3a9 100644 --- a/arch/ia64/kernel/cyclone.c +++ b/arch/ia64/kernel/cyclone.c @@ -1,6 +1,8 @@ +#include #include #include #include +#include /* IBM Summit (EXA) Cyclone counter code*/ #define CYCLONE_CBAR_ADDR 0xFEB00CD0 @@ -15,62 +17,10 @@ void __init cyclone_setup(void) use_cyclone = 1; } -static u32* volatile cyclone_timer; /* Cyclone MPMC0 register */ -static u32 last_update_cyclone; - -static unsigned long offset_base; - -static unsigned long get_offset_cyclone(void) -{ - u32 now; - unsigned long offset; - - /* Read the cyclone timer */ - now = readl(cyclone_timer); - /* .. relative to previous update*/ - offset = now - last_update_cyclone; - - /* convert cyclone ticks to nanoseconds */ - offset = (offset*NSEC_PER_SEC)/CYCLONE_TIMER_FREQ; - - /* our adjusted time in nanoseconds */ - return offset_base + offset; -} - -static void update_cyclone(long delta_nsec) -{ - u32 now; - unsigned long offset; - - /* Read the cyclone timer */ - now = readl(cyclone_timer); - /* .. relative to previous update*/ - offset = now - last_update_cyclone; - - /* convert cyclone ticks to nanoseconds */ - offset = (offset*NSEC_PER_SEC)/CYCLONE_TIMER_FREQ; - - offset += offset_base; - - /* Be careful about signed/unsigned comparisons here: */ - if (delta_nsec < 0 || (unsigned long) delta_nsec < offset) - offset_base = offset - delta_nsec; - else - offset_base = 0; - - last_update_cyclone = now; -} - -static void reset_cyclone(void) -{ - offset_base = 0; - last_update_cyclone = readl(cyclone_timer); -} struct time_interpolator cyclone_interpolator = { - .get_offset = get_offset_cyclone, - .update = update_cyclone, - .reset = reset_cyclone, + .source = TIME_SOURCE_MMIO32, + .shift = 32, .frequency = CYCLONE_TIMER_FREQ, .drift = -100, }; @@ -81,6 +31,7 @@ int __init init_cyclone_clock(void) u64 base; /* saved cyclone base address */ u64 offset; /* offset from pageaddr to cyclone_timer register */ int i; + u32* volatile cyclone_timer; /* Cyclone MPMC0 register */ if (!use_cyclone) return -ENODEV; @@ -148,7 +99,7 @@ int __init init_cyclone_clock(void) } } /* initialize last tick */ - last_update_cyclone = readl(cyclone_timer); + cyclone_interpolator.addr = cyclone_timer; register_time_interpolator(&cyclone_interpolator); return 0; diff --git a/arch/ia64/kernel/mca_drv.c b/arch/ia64/kernel/mca_drv.c new file mode 100644 index 000000000..90ed84b72 --- /dev/null +++ b/arch/ia64/kernel/mca_drv.c @@ -0,0 +1,639 @@ +/* + * File: mca_drv.c + * Purpose: Generic MCA handling layer + * + * Copyright (C) 2004 FUJITSU LIMITED + * Copyright (C) Hidetoshi Seto (seto.hidetoshi@jp.fujitsu.com) + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "mca_drv.h" + +/* max size of SAL error record (default) */ +static int sal_rec_max = 10000; + +/* from mca.c */ +static ia64_mca_sal_to_os_state_t *sal_to_os_handoff_state; +static ia64_mca_os_to_sal_state_t *os_to_sal_handoff_state; + +/* from mca_drv_asm.S */ +extern void *mca_handler_bhhook(void); + +static spinlock_t mca_bh_lock = SPIN_LOCK_UNLOCKED; + +typedef enum { + MCA_IS_LOCAL = 0, + MCA_IS_GLOBAL = 1 +} mca_type_t; + +#define MAX_PAGE_ISOLATE 32 + +static struct page *page_isolate[MAX_PAGE_ISOLATE]; +static int num_page_isolate = 0; + +typedef enum { + ISOLATE_NG = 0, + ISOLATE_OK = 1 +} isolate_status_t; + +/* + * This pool keeps pointers to the section part of SAL error record + */ +static struct { + slidx_list_t *buffer; /* section pointer list pool */ + int cur_idx; /* Current index of section pointer list pool */ + int max_idx; /* Maximum index of section pointer list pool */ +} slidx_pool; + +/** + * mca_page_isolate - isolate a poisoned page in order not to use it later + * @paddr: poisoned memory location + * + * Return value: + * ISOLATE_OK / ISOLATE_NG + */ + +static isolate_status_t +mca_page_isolate(unsigned long paddr) +{ + int i; + struct page *p; + + /* whether physical address is valid or not */ + if ( !ia64_phys_addr_valid(paddr) ) + return ISOLATE_NG; + + /* convert physical address to physical page number */ + p = pfn_to_page(paddr>>PAGE_SHIFT); + + /* check whether a page number have been already registered or not */ + for( i = 0; i < num_page_isolate; i++ ) + if( page_isolate[i] == p ) + return ISOLATE_OK; /* already listed */ + + /* limitation check */ + if( num_page_isolate == MAX_PAGE_ISOLATE ) + return ISOLATE_NG; + + /* kick pages having attribute 'SLAB' or 'Reserved' */ + if( PageSlab(p) || PageReserved(p) ) + return ISOLATE_NG; + + /* add attribute 'Reserved' and register the page */ + SetPageReserved(p); + page_isolate[num_page_isolate++] = p; + + return ISOLATE_OK; +} + +/** + * mca_hanlder_bh - Kill the process which occurred memory read error + * @paddr: poisoned address received from MCA Handler + */ + +void +mca_handler_bh(unsigned long paddr) +{ + printk(KERN_DEBUG "OS_MCA: process [pid: %d](%s) encounters MCA.\n", + current->pid, current->comm); + + spin_lock(&mca_bh_lock); + if (mca_page_isolate(paddr) == ISOLATE_OK) { + printk(KERN_DEBUG "Page isolation: ( %lx ) success.\n", paddr); + } else { + printk(KERN_DEBUG "Page isolation: ( %lx ) failure.\n", paddr); + } + spin_unlock(&mca_bh_lock); + + /* This process is about to be killed itself */ + force_sig(SIGKILL, current); + schedule(); +} + +/** + * mca_make_peidx - Make index of processor error section + * @slpi: pointer to record of processor error section + * @peidx: pointer to index of processor error section + */ + +static void +mca_make_peidx(sal_log_processor_info_t *slpi, peidx_table_t *peidx) +{ + /* + * calculate the start address of + * "struct cpuid_info" and "sal_processor_static_info_t". + */ + u64 total_check_num = slpi->valid.num_cache_check + + slpi->valid.num_tlb_check + + slpi->valid.num_bus_check + + slpi->valid.num_reg_file_check + + slpi->valid.num_ms_check; + u64 head_size = sizeof(sal_log_mod_error_info_t) * total_check_num + + sizeof(sal_log_processor_info_t); + u64 mid_size = slpi->valid.cpuid_info * sizeof(struct sal_cpuid_info); + + peidx_head(peidx) = slpi; + peidx_mid(peidx) = (struct sal_cpuid_info *) + (slpi->valid.cpuid_info ? ((char*)slpi + head_size) : NULL); + peidx_bottom(peidx) = (sal_processor_static_info_t *) + (slpi->valid.psi_static_struct ? + ((char*)slpi + head_size + mid_size) : NULL); +} + +/** + * mca_make_slidx - Make index of SAL error record + * @buffer: pointer to SAL error record + * @slidx: pointer to index of SAL error record + * + * Return value: + * 1 if record has platform error / 0 if not + */ +#define LOG_INDEX_ADD_SECT_PTR(sect, ptr) \ + { slidx_list_t *hl = &slidx_pool.buffer[slidx_pool.cur_idx]; \ + hl->hdr = ptr; \ + list_add(&hl->list, &(sect)); \ + slidx_pool.cur_idx = (slidx_pool.cur_idx + 1)%slidx_pool.max_idx; } + +static int +mca_make_slidx(void *buffer, slidx_table_t *slidx) +{ + int platform_err = 0; + int record_len = ((sal_log_record_header_t*)buffer)->len; + u32 ercd_pos; + int sects; + sal_log_section_hdr_t *sp; + + /* + * Initialize index referring current record + */ + INIT_LIST_HEAD(&(slidx->proc_err)); + INIT_LIST_HEAD(&(slidx->mem_dev_err)); + INIT_LIST_HEAD(&(slidx->sel_dev_err)); + INIT_LIST_HEAD(&(slidx->pci_bus_err)); + INIT_LIST_HEAD(&(slidx->smbios_dev_err)); + INIT_LIST_HEAD(&(slidx->pci_comp_err)); + INIT_LIST_HEAD(&(slidx->plat_specific_err)); + INIT_LIST_HEAD(&(slidx->host_ctlr_err)); + INIT_LIST_HEAD(&(slidx->plat_bus_err)); + INIT_LIST_HEAD(&(slidx->unsupported)); + + /* + * Extract a Record Header + */ + slidx->header = buffer; + + /* + * Extract each section records + * (arranged from "int ia64_log_platform_info_print()") + */ + for (ercd_pos = sizeof(sal_log_record_header_t), sects = 0; + ercd_pos < record_len; ercd_pos += sp->len, sects++) { + sp = (sal_log_section_hdr_t *)((char*)buffer + ercd_pos); + if (!efi_guidcmp(sp->guid, SAL_PROC_DEV_ERR_SECT_GUID)) { + LOG_INDEX_ADD_SECT_PTR(slidx->proc_err, sp); + } else if (!efi_guidcmp(sp->guid, SAL_PLAT_MEM_DEV_ERR_SECT_GUID)) { + platform_err = 1; + LOG_INDEX_ADD_SECT_PTR(slidx->mem_dev_err, sp); + } else if (!efi_guidcmp(sp->guid, SAL_PLAT_SEL_DEV_ERR_SECT_GUID)) { + platform_err = 1; + LOG_INDEX_ADD_SECT_PTR(slidx->sel_dev_err, sp); + } else if (!efi_guidcmp(sp->guid, SAL_PLAT_PCI_BUS_ERR_SECT_GUID)) { + platform_err = 1; + LOG_INDEX_ADD_SECT_PTR(slidx->pci_bus_err, sp); + } else if (!efi_guidcmp(sp->guid, SAL_PLAT_SMBIOS_DEV_ERR_SECT_GUID)) { + platform_err = 1; + LOG_INDEX_ADD_SECT_PTR(slidx->smbios_dev_err, sp); + } else if (!efi_guidcmp(sp->guid, SAL_PLAT_PCI_COMP_ERR_SECT_GUID)) { + platform_err = 1; + LOG_INDEX_ADD_SECT_PTR(slidx->pci_comp_err, sp); + } else if (!efi_guidcmp(sp->guid, SAL_PLAT_SPECIFIC_ERR_SECT_GUID)) { + platform_err = 1; + LOG_INDEX_ADD_SECT_PTR(slidx->plat_specific_err, sp); + } else if (!efi_guidcmp(sp->guid, SAL_PLAT_HOST_CTLR_ERR_SECT_GUID)) { + platform_err = 1; + LOG_INDEX_ADD_SECT_PTR(slidx->host_ctlr_err, sp); + } else if (!efi_guidcmp(sp->guid, SAL_PLAT_BUS_ERR_SECT_GUID)) { + platform_err = 1; + LOG_INDEX_ADD_SECT_PTR(slidx->plat_bus_err, sp); + } else { + LOG_INDEX_ADD_SECT_PTR(slidx->unsupported, sp); + } + } + slidx->n_sections = sects; + + return platform_err; +} + +/** + * init_record_index_pools - Initialize pool of lists for SAL record index + * + * Return value: + * 0 on Success / -ENOMEM on Failure + */ +static int +init_record_index_pools(void) +{ + int i; + int rec_max_size; /* Maximum size of SAL error records */ + int sect_min_size; /* Minimum size of SAL error sections */ + /* minimum size table of each section */ + static int sal_log_sect_min_sizes[] = { + sizeof(sal_log_processor_info_t) + sizeof(sal_processor_static_info_t), + sizeof(sal_log_mem_dev_err_info_t), + sizeof(sal_log_sel_dev_err_info_t), + sizeof(sal_log_pci_bus_err_info_t), + sizeof(sal_log_smbios_dev_err_info_t), + sizeof(sal_log_pci_comp_err_info_t), + sizeof(sal_log_plat_specific_err_info_t), + sizeof(sal_log_host_ctlr_err_info_t), + sizeof(sal_log_plat_bus_err_info_t), + }; + + /* + * MCA handler cannot allocate new memory on flight, + * so we preallocate enough memory to handle a SAL record. + * + * Initialize a handling set of slidx_pool: + * 1. Pick up the max size of SAL error records + * 2. Pick up the min size of SAL error sections + * 3. Allocate the pool as enough to 2 SAL records + * (now we can estimate the maxinum of section in a record.) + */ + + /* - 1 - */ + rec_max_size = sal_rec_max; + + /* - 2 - */ + sect_min_size = sal_log_sect_min_sizes[0]; + for (i = 1; i < sizeof sal_log_sect_min_sizes/sizeof(size_t); i++) + if (sect_min_size > sal_log_sect_min_sizes[i]) + sect_min_size = sal_log_sect_min_sizes[i]; + + /* - 3 - */ + slidx_pool.max_idx = (rec_max_size/sect_min_size) * 2 + 1; + slidx_pool.buffer = (slidx_list_t *) kmalloc(slidx_pool.max_idx * sizeof(slidx_list_t), GFP_KERNEL); + + return slidx_pool.buffer ? 0 : -ENOMEM; +} + + +/***************************************************************************** + * Recovery functions * + *****************************************************************************/ + +/** + * is_mca_global - Check whether this MCA is global or not + * @peidx: pointer of index of processor error section + * @pbci: pointer to pal_bus_check_info_t + * + * Return value: + * MCA_IS_LOCAL / MCA_IS_GLOBAL + */ + +static mca_type_t +is_mca_global(peidx_table_t *peidx, pal_bus_check_info_t *pbci) +{ + pal_processor_state_info_t *psp = (pal_processor_state_info_t*)peidx_psp(peidx); + + /* + * PAL can request a rendezvous, if the MCA has a global scope. + * If "rz_always" flag is set, SAL requests MCA rendezvous + * in spite of global MCA. + * Therefore it is local MCA when rendezvous has not been requested. + * Failed to rendezvous, the system must be down. + */ + switch (sal_to_os_handoff_state->imsto_rendez_state) { + case -1: /* SAL rendezvous unsuccessful */ + return MCA_IS_GLOBAL; + case 0: /* SAL rendezvous not required */ + return MCA_IS_LOCAL; + case 1: /* SAL rendezvous successful int */ + case 2: /* SAL rendezvous successful int with init */ + default: + break; + } + + /* + * If One or more Cache/TLB/Reg_File/Uarch_Check is here, + * it would be a local MCA. (i.e. processor internal error) + */ + if (psp->tc || psp->cc || psp->rc || psp->uc) + return MCA_IS_LOCAL; + + /* + * Bus_Check structure with Bus_Check.ib (internal bus error) flag set + * would be a global MCA. (e.g. a system bus address parity error) + */ + if (!pbci || pbci->ib) + return MCA_IS_GLOBAL; + + /* + * Bus_Check structure with Bus_Check.eb (external bus error) flag set + * could be either a local MCA or a global MCA. + * + * Referring Bus_Check.bsi: + * 0: Unknown/unclassified + * 1: BERR# + * 2: BINIT# + * 3: Hard Fail + * (FIXME: Are these SGI specific or generic bsi values?) + */ + if (pbci->eb) + switch (pbci->bsi) { + case 0: + /* e.g. a load from poisoned memory */ + return MCA_IS_LOCAL; + case 1: + case 2: + case 3: + return MCA_IS_GLOBAL; + } + + return MCA_IS_GLOBAL; +} + +/** + * recover_from_read_error - Try to recover the errors which type are "read"s. + * @slidx: pointer of index of SAL error record + * @peidx: pointer of index of processor error section + * @pbci: pointer of pal_bus_check_info + * + * Return value: + * 1 on Success / 0 on Failure + */ + +static int +recover_from_read_error(slidx_table_t *slidx, peidx_table_t *peidx, pal_bus_check_info_t *pbci) +{ + sal_log_mod_error_info_t *smei; + pal_min_state_area_t *pmsa; + struct ia64_psr *psr1, *psr2; + ia64_fptr_t *mca_hdlr_bh = (ia64_fptr_t*)mca_handler_bhhook; + + /* Is target address valid? */ + if (!pbci->tv) + return 0; + + /* + * cpu read or memory-mapped io read + * + * offending process affected process OS MCA do + * kernel mode kernel mode down system + * kernel mode user mode kill the process + * user mode kernel mode down system (*) + * user mode user mode kill the process + * + * (*) You could terminate offending user-mode process + * if (pbci->pv && pbci->pl != 0) *and* if you sure + * the process not have any locks of kernel. + */ + + psr1 =(struct ia64_psr *)&(peidx_minstate_area(peidx)->pmsa_ipsr); + + /* + * Check the privilege level of interrupted context. + * If it is user-mode, then terminate affected process. + */ + if (psr1->cpl != 0) { + smei = peidx_bus_check(peidx, 0); + if (smei->valid.target_identifier) { + /* + * setup for resume to bottom half of MCA, + * "mca_handler_bhhook" + */ + pmsa = (pal_min_state_area_t *)(sal_to_os_handoff_state->pal_min_state | (6ul<<61)); + /* pass to bhhook as 1st argument (gr8) */ + pmsa->pmsa_gr[8-1] = smei->target_identifier; + /* set interrupted return address (but no use) */ + pmsa->pmsa_br0 = pmsa->pmsa_iip; + /* change resume address to bottom half */ + pmsa->pmsa_iip = mca_hdlr_bh->fp; + pmsa->pmsa_gr[1-1] = mca_hdlr_bh->gp; + /* set cpl with kernel mode */ + psr2 = (struct ia64_psr *)&pmsa->pmsa_ipsr; + psr2->cpl = 0; + psr2->ri = 0; + + return 1; + } + + } + + return 0; +} + +/** + * recover_from_platform_error - Recover from platform error. + * @slidx: pointer of index of SAL error record + * @peidx: pointer of index of processor error section + * @pbci: pointer of pal_bus_check_info + * + * Return value: + * 1 on Success / 0 on Failure + */ + +static int +recover_from_platform_error(slidx_table_t *slidx, peidx_table_t *peidx, pal_bus_check_info_t *pbci) +{ + int status = 0; + pal_processor_state_info_t *psp = (pal_processor_state_info_t*)peidx_psp(peidx); + + if (psp->bc && pbci->eb && pbci->bsi == 0) { + switch(pbci->type) { + case 1: /* partial read */ + case 3: /* full line(cpu) read */ + case 9: /* I/O space read */ + status = recover_from_read_error(slidx, peidx, pbci); + break; + case 0: /* unknown */ + case 2: /* partial write */ + case 4: /* full line write */ + case 5: /* implicit or explicit write-back operation */ + case 6: /* snoop probe */ + case 7: /* incoming or outgoing ptc.g */ + case 8: /* write coalescing transactions */ + case 10: /* I/O space write */ + case 11: /* inter-processor interrupt message(IPI) */ + case 12: /* interrupt acknowledge or external task priority cycle */ + default: + break; + } + } + + return status; +} + +/** + * recover_from_processor_error + * @platform: whether there are some platform error section or not + * @slidx: pointer of index of SAL error record + * @peidx: pointer of index of processor error section + * @pbci: pointer of pal_bus_check_info + * + * Return value: + * 1 on Success / 0 on Failure + */ +/* + * Later we try to recover when below all conditions are satisfied. + * 1. Only one processor error section is exist. + * 2. BUS_CHECK is exist and the others are not exist.(Except TLB_CHECK) + * 3. The entry of BUS_CHECK_INFO is 1. + * 4. "External bus error" flag is set and the others are not set. + */ + +static int +recover_from_processor_error(int platform, slidx_table_t *slidx, peidx_table_t *peidx, pal_bus_check_info_t *pbci) +{ + pal_processor_state_info_t *psp = (pal_processor_state_info_t*)peidx_psp(peidx); + + /* + * We cannot recover errors with other than bus_check. + */ + if (psp->cc || psp->rc || psp->uc) + return 0; + + /* + * If there is no bus error, record is weird but we need not to recover. + */ + if (psp->bc == 0 || pbci == NULL) + return 1; + + /* + * Sorry, we cannot handle so many. + */ + if (peidx_bus_check_num(peidx) > 1) + return 0; + /* + * Well, here is only one bus error. + */ + if (pbci->ib || pbci->cc) + return 0; + if (pbci->eb && pbci->bsi > 0) + return 0; + if (psp->ci == 0) + return 0; + + /* + * This is a local MCA and estimated as recoverble external bus error. + * (e.g. a load from poisoned memory) + * This means "there are some platform errors". + */ + if (platform) + return recover_from_platform_error(slidx, peidx, pbci); + /* + * On account of strange SAL error record, we cannot recover. + */ + return 0; +} + +/** + * mca_try_to_recover - Try to recover from MCA + * @rec: pointer to a SAL error record + * + * Return value: + * 1 on Success / 0 on Failure + */ + +static int +mca_try_to_recover(void *rec, + ia64_mca_sal_to_os_state_t *sal_to_os_state, + ia64_mca_os_to_sal_state_t *os_to_sal_state) +{ + int platform_err; + int n_proc_err; + slidx_table_t slidx; + peidx_table_t peidx; + pal_bus_check_info_t pbci; + + /* handoff state from/to mca.c */ + sal_to_os_handoff_state = sal_to_os_state; + os_to_sal_handoff_state = os_to_sal_state; + + /* Make index of SAL error record */ + platform_err = mca_make_slidx(rec, &slidx); + + /* Count processor error sections */ + n_proc_err = slidx_count(&slidx, proc_err); + + /* Now, OS can recover when there is one processor error section */ + if (n_proc_err > 1) + return 0; + else if (n_proc_err == 0) { + /* Weird SAL record ... We need not to recover */ + + return 1; + } + + /* Make index of processor error section */ + mca_make_peidx((sal_log_processor_info_t*)slidx_first_entry(&slidx.proc_err)->hdr, &peidx); + + /* Extract Processor BUS_CHECK[0] */ + *((u64*)&pbci) = peidx_check_info(&peidx, bus_check, 0); + + /* Check whether MCA is global or not */ + if (is_mca_global(&peidx, &pbci)) + return 0; + + /* Try to recover a processor error */ + return recover_from_processor_error(platform_err, &slidx, &peidx, &pbci); +} + +/* + * ============================================================================= + */ + +int __init mca_external_handler_init(void) +{ + if (init_record_index_pools()) + return -ENOMEM; + + /* register external mca handlers */ + if (ia64_reg_MCA_extension(mca_try_to_recover)){ + printk(KERN_ERR "ia64_reg_MCA_extension failed.\n"); + kfree(slidx_pool.buffer); + return -EFAULT; + } + return 0; +} + +void __exit mca_external_handler_exit(void) +{ + /* unregister external mca handlers */ + ia64_unreg_MCA_extension(); + kfree(slidx_pool.buffer); +} + +module_init(mca_external_handler_init); +module_exit(mca_external_handler_exit); + +module_param(sal_rec_max, int, 0644); +MODULE_PARM_DESC(sal_rec_max, "Max size of SAL error record"); + +MODULE_DESCRIPTION("ia64 platform dependent mca handler driver"); +MODULE_LICENSE("GPL"); diff --git a/arch/ia64/kernel/mca_drv.h b/arch/ia64/kernel/mca_drv.h new file mode 100644 index 000000000..0227b761f --- /dev/null +++ b/arch/ia64/kernel/mca_drv.h @@ -0,0 +1,113 @@ +/* + * File: mca_drv.h + * Purpose: Define helpers for Generic MCA handling + * + * Copyright (C) 2004 FUJITSU LIMITED + * Copyright (C) Hidetoshi Seto (seto.hidetoshi@jp.fujitsu.com) + */ +/* + * Processor error section: + * + * +-sal_log_processor_info_t *info-------------+ + * | sal_log_section_hdr_t header; | + * | ... | + * | sal_log_mod_error_info_t info[0]; | + * +-+----------------+-------------------------+ + * | CACHE_CHECK | ^ num_cache_check v + * +----------------+ + * | TLB_CHECK | ^ num_tlb_check v + * +----------------+ + * | BUS_CHECK | ^ num_bus_check v + * +----------------+ + * | REG_FILE_CHECK | ^ num_reg_file_check v + * +----------------+ + * | MS_CHECK | ^ num_ms_check v + * +-struct cpuid_info *id----------------------+ + * | regs[5]; | + * | reserved; | + * +-sal_processor_static_info_t *regs----------+ + * | valid; | + * | ... | + * | fr[128]; | + * +--------------------------------------------+ + */ + +/* peidx: index of processor error section */ +typedef struct peidx_table { + sal_log_processor_info_t *info; + struct sal_cpuid_info *id; + sal_processor_static_info_t *regs; +} peidx_table_t; + +#define peidx_head(p) (((p)->info)) +#define peidx_mid(p) (((p)->id)) +#define peidx_bottom(p) (((p)->regs)) + +#define peidx_psp(p) (&(peidx_head(p)->proc_state_parameter)) +#define peidx_field_valid(p) (&(peidx_head(p)->valid)) +#define peidx_minstate_area(p) (&(peidx_bottom(p)->min_state_area)) + +#define peidx_cache_check_num(p) (peidx_head(p)->valid.num_cache_check) +#define peidx_tlb_check_num(p) (peidx_head(p)->valid.num_tlb_check) +#define peidx_bus_check_num(p) (peidx_head(p)->valid.num_bus_check) +#define peidx_reg_file_check_num(p) (peidx_head(p)->valid.num_reg_file_check) +#define peidx_ms_check_num(p) (peidx_head(p)->valid.num_ms_check) + +#define peidx_cache_check_idx(p, n) (n) +#define peidx_tlb_check_idx(p, n) (peidx_cache_check_idx(p, peidx_cache_check_num(p)) + n) +#define peidx_bus_check_idx(p, n) (peidx_tlb_check_idx(p, peidx_tlb_check_num(p)) + n) +#define peidx_reg_file_check_idx(p, n) (peidx_bus_check_idx(p, peidx_bus_check_num(p)) + n) +#define peidx_ms_check_idx(p, n) (peidx_reg_file_check_idx(p, peidx_reg_file_check_num(p)) + n) + +#define peidx_mod_error_info(p, name, n) \ +({ int __idx = peidx_##name##_idx(p, n); \ + sal_log_mod_error_info_t *__ret = NULL; \ + if (peidx_##name##_num(p) > n) /*BUG*/ \ + __ret = &(peidx_head(p)->info[__idx]); \ + __ret; }) + +#define peidx_cache_check(p, n) peidx_mod_error_info(p, cache_check, n) +#define peidx_tlb_check(p, n) peidx_mod_error_info(p, tlb_check, n) +#define peidx_bus_check(p, n) peidx_mod_error_info(p, bus_check, n) +#define peidx_reg_file_check(p, n) peidx_mod_error_info(p, reg_file_check, n) +#define peidx_ms_check(p, n) peidx_mod_error_info(p, ms_check, n) + +#define peidx_check_info(proc, name, n) \ +({ \ + sal_log_mod_error_info_t *__info = peidx_mod_error_info(proc, name, n);\ + u64 __temp = __info && __info->valid.check_info \ + ? __info->check_info : 0; \ + __temp; }) + +/* slidx: index of SAL log error record */ + +typedef struct slidx_list { + struct list_head list; + sal_log_section_hdr_t *hdr; +} slidx_list_t; + +typedef struct slidx_table { + sal_log_record_header_t *header; + int n_sections; /* # of section headers */ + struct list_head proc_err; + struct list_head mem_dev_err; + struct list_head sel_dev_err; + struct list_head pci_bus_err; + struct list_head smbios_dev_err; + struct list_head pci_comp_err; + struct list_head plat_specific_err; + struct list_head host_ctlr_err; + struct list_head plat_bus_err; + struct list_head unsupported; /* list of unsupported sections */ +} slidx_table_t; + +#define slidx_foreach_entry(pos, head) \ + list_for_each_entry(pos, head, list) +#define slidx_first_entry(head) \ + (((head)->next != (head)) ? list_entry((head)->next, typeof(slidx_list_t), list) : NULL) +#define slidx_count(slidx, sec) \ +({ int __count = 0; \ + slidx_list_t *__pos; \ + slidx_foreach_entry(__pos, &((slidx)->sec)) { __count++; }\ + __count; }) + diff --git a/arch/ia64/kernel/mca_drv_asm.S b/arch/ia64/kernel/mca_drv_asm.S new file mode 100644 index 000000000..bcfa05acc --- /dev/null +++ b/arch/ia64/kernel/mca_drv_asm.S @@ -0,0 +1,45 @@ +/* + * File: mca_drv_asm.S + * Purpose: Assembly portion of Generic MCA handling + * + * Copyright (C) 2004 FUJITSU LIMITED + * Copyright (C) Hidetoshi Seto (seto.hidetoshi@jp.fujitsu.com) + */ +#include +#include + +#include +#include + +GLOBAL_ENTRY(mca_handler_bhhook) + invala // clear RSE ? + ;; // + cover // + ;; // + clrrrb // + ;; + alloc r16=ar.pfs,0,2,1,0 // make a new frame + ;; + mov r13=IA64_KR(CURRENT) // current task pointer + ;; + adds r12=IA64_TASK_THREAD_KSP_OFFSET,r13 + ;; + ld8 r12=[r12] // stack pointer + ;; + mov loc0=r16 + movl loc1=mca_handler_bh // recovery C function + ;; + mov out0=r8 // poisoned address + mov b6=loc1 + ;; + mov loc1=rp + ;; + br.call.sptk.many rp=b6 // not return ... + ;; + mov ar.pfs=loc0 + mov rp=loc1 + ;; + mov r8=r0 + br.ret.sptk.many rp + ;; +END(mca_handler_bhhook) diff --git a/arch/ia64/kernel/minstate.h b/arch/ia64/kernel/minstate.h index 540301f6e..b1460dcc3 100644 --- a/arch/ia64/kernel/minstate.h +++ b/arch/ia64/kernel/minstate.h @@ -54,8 +54,7 @@ (pUStk) mov ar.rsc=0x3; /* set eager mode, pl 0, little-endian, loadrs=0 */ \ #define MINSTATE_END_SAVE_MIN_PHYS \ - or r12=r12,r14; /* make sp a kernel virtual address */ \ - or r13=r13,r14; /* make `current' a kernel virtual address */ \ + dep r12=-1,r12,61,3; /* make sp a kernel virtual address */ \ ;; #ifdef MINSTATE_VIRT @@ -65,7 +64,7 @@ #endif #ifdef MINSTATE_PHYS -# define MINSTATE_GET_CURRENT(reg) mov reg=IA64_KR(CURRENT);; dep reg=0,reg,61,3 +# define MINSTATE_GET_CURRENT(reg) mov reg=IA64_KR(CURRENT);; tpa reg=reg # define MINSTATE_START_SAVE_MIN MINSTATE_START_SAVE_MIN_PHYS # define MINSTATE_END_SAVE_MIN MINSTATE_END_SAVE_MIN_PHYS #endif @@ -172,7 +171,6 @@ ;; \ .mem.offset 0,0; st8.spill [r16]=r15,16; \ .mem.offset 8,0; st8.spill [r17]=r14,16; \ - dep r14=-1,r0,61,3; \ ;; \ .mem.offset 0,0; st8.spill [r16]=r2,16; \ .mem.offset 8,0; st8.spill [r17]=r3,16; \ diff --git a/arch/ia64/kernel/patch.c b/arch/ia64/kernel/patch.c index ef04f5df0..367804a60 100644 --- a/arch/ia64/kernel/patch.c +++ b/arch/ia64/kernel/patch.c @@ -65,21 +65,21 @@ void ia64_patch_imm64 (u64 insn_addr, u64 val) { ia64_patch(insn_addr, - 0x01fffefe000, ( ((val & 0x8000000000000000) >> 27) /* bit 63 -> 36 */ - | ((val & 0x0000000000200000) << 0) /* bit 21 -> 21 */ - | ((val & 0x00000000001f0000) << 6) /* bit 16 -> 22 */ - | ((val & 0x000000000000ff80) << 20) /* bit 7 -> 27 */ - | ((val & 0x000000000000007f) << 13) /* bit 0 -> 13 */)); - ia64_patch(insn_addr - 1, 0x1ffffffffff, val >> 22); + 0x01fffefe000UL, ( ((val & 0x8000000000000000UL) >> 27) /* bit 63 -> 36 */ + | ((val & 0x0000000000200000UL) << 0) /* bit 21 -> 21 */ + | ((val & 0x00000000001f0000UL) << 6) /* bit 16 -> 22 */ + | ((val & 0x000000000000ff80UL) << 20) /* bit 7 -> 27 */ + | ((val & 0x000000000000007fUL) << 13) /* bit 0 -> 13 */)); + ia64_patch(insn_addr - 1, 0x1ffffffffffUL, val >> 22); } void ia64_patch_imm60 (u64 insn_addr, u64 val) { ia64_patch(insn_addr, - 0x011ffffe000, ( ((val & 0x0800000000000000) >> 23) /* bit 59 -> 36 */ - | ((val & 0x00000000000fffff) << 13) /* bit 0 -> 13 */)); - ia64_patch(insn_addr - 1, 0x1fffffffffc, val >> 18); + 0x011ffffe000UL, ( ((val & 0x0800000000000000UL) >> 23) /* bit 59 -> 36 */ + | ((val & 0x00000000000fffffUL) << 13) /* bit 0 -> 13 */)); + ia64_patch(insn_addr - 1, 0x1fffffffffcUL, val >> 18); } /* @@ -130,10 +130,10 @@ ia64_patch_mckinley_e9 (unsigned long start, unsigned long end) while (offp < (s32 *) end) { wp = (u64 *) ia64_imva((char *) offp + *offp); - wp[0] = 0x0000000100000000; /* nop.m 0; nop.i 0; nop.i 0 */ - wp[1] = 0x0004000000000200; - wp[2] = 0x0000000100000011; /* nop.m 0; nop.i 0; br.ret.sptk.many b6 */ - wp[3] = 0x0084006880000200; + wp[0] = 0x0000000100000000UL; /* nop.m 0; nop.i 0; nop.i 0 */ + wp[1] = 0x0004000000000200UL; + wp[2] = 0x0000000100000011UL; /* nop.m 0; nop.i 0; br.ret.sptk.many b6 */ + wp[3] = 0x0084006880000200UL; ia64_fc(wp); ia64_fc(wp + 2); ++offp; } diff --git a/arch/ia64/kernel/salinfo.c b/arch/ia64/kernel/salinfo.c index a0420ae4d..500e1050c 100644 --- a/arch/ia64/kernel/salinfo.c +++ b/arch/ia64/kernel/salinfo.c @@ -268,7 +268,7 @@ salinfo_event_open(struct inode *inode, struct file *file) } static ssize_t -salinfo_event_read(struct file *file, char *buffer, size_t count, loff_t *ppos) +salinfo_event_read(struct file *file, char __user *buffer, size_t count, loff_t *ppos) { struct inode *inode = file->f_dentry->d_inode; struct proc_dir_entry *entry = PDE(inode); @@ -417,11 +417,16 @@ retry: if (!data->saved_num) call_on_cpu(cpu, salinfo_log_read_cpu, data); - data->state = data->log_size ? STATE_LOG_RECORD : STATE_NO_DATA; + if (!data->log_size) { + data->state = STATE_NO_DATA; + clear_bit(cpu, &data->cpu_event); + } else { + data->state = STATE_LOG_RECORD; + } } static ssize_t -salinfo_log_read(struct file *file, char *buffer, size_t count, loff_t *ppos) +salinfo_log_read(struct file *file, char __user *buffer, size_t count, loff_t *ppos) { struct inode *inode = file->f_dentry->d_inode; struct proc_dir_entry *entry = PDE(inode); @@ -478,7 +483,7 @@ salinfo_log_clear(struct salinfo_data *data, int cpu) } static ssize_t -salinfo_log_write(struct file *file, const char *buffer, size_t count, loff_t *ppos) +salinfo_log_write(struct file *file, const char __user *buffer, size_t count, loff_t *ppos) { struct inode *inode = file->f_dentry->d_inode; struct proc_dir_entry *entry = PDE(inode); diff --git a/arch/ia64/kernel/sigframe.h b/arch/ia64/kernel/sigframe.h index 55e527527..37b986cb8 100644 --- a/arch/ia64/kernel/sigframe.h +++ b/arch/ia64/kernel/sigframe.h @@ -17,7 +17,7 @@ struct sigframe { * End of architected state. */ - void *handler; /* pointer to the plabel of the signal handler */ + void __user *handler; /* pointer to the plabel of the signal handler */ struct siginfo info; struct sigcontext sc; }; diff --git a/arch/ia64/kernel/sys_ia64.c b/arch/ia64/kernel/sys_ia64.c index eddde6018..88d8b217f 100644 --- a/arch/ia64/kernel/sys_ia64.c +++ b/arch/ia64/kernel/sys_ia64.c @@ -93,7 +93,7 @@ sys_getpagesize (void) } asmlinkage unsigned long -ia64_shmat (int shmid, void *shmaddr, int shmflg) +ia64_shmat (int shmid, void __user *shmaddr, int shmflg) { unsigned long raddr; int retval; @@ -183,7 +183,7 @@ static inline unsigned long do_mmap2 (unsigned long addr, unsigned long len, int prot, int flags, int fd, unsigned long pgoff) { unsigned long roff; - struct file *file = 0; + struct file *file = NULL; flags &= ~(MAP_EXECUTABLE | MAP_DENYWRITE); if (!(flags & MAP_ANONYMOUS)) { diff --git a/arch/ia64/kernel/unaligned.c b/arch/ia64/kernel/unaligned.c index b1a68e436..46dad0d21 100644 --- a/arch/ia64/kernel/unaligned.c +++ b/arch/ia64/kernel/unaligned.c @@ -760,7 +760,7 @@ emulate_load_int (unsigned long ifa, load_store_t ld, struct pt_regs *regs) return -1; } /* this assumes little-endian byte-order: */ - if (copy_from_user(&val, (void *) ifa, len)) + if (copy_from_user(&val, (void __user *) ifa, len)) return -1; setreg(ld.r1, val, 0, regs); @@ -869,7 +869,7 @@ emulate_store_int (unsigned long ifa, load_store_t ld, struct pt_regs *regs) * * extract the value to be stored */ - getreg(ld.imm, &r2, 0, regs); + getreg(ld.imm, &r2, NULL, regs); /* * we rely on the macros in unaligned.h for now i.e., @@ -887,7 +887,7 @@ emulate_store_int (unsigned long ifa, load_store_t ld, struct pt_regs *regs) } /* this assumes little-endian byte-order: */ - if (copy_to_user((void *) ifa, &r2, len)) + if (copy_to_user((void __user *) ifa, &r2, len)) return -1; /* @@ -1036,8 +1036,8 @@ emulate_load_floatpair (unsigned long ifa, load_store_t ld, struct pt_regs *regs * This assumes little-endian byte-order. Note that there is no "ldfpe" * instruction: */ - if (copy_from_user(&fpr_init[0], (void *) ifa, len) - || copy_from_user(&fpr_init[1], (void *) (ifa + len), len)) + if (copy_from_user(&fpr_init[0], (void __user *) ifa, len) + || copy_from_user(&fpr_init[1], (void __user *) (ifa + len), len)) return -1; DPRINT("ld.r1=%d ld.imm=%d x6_sz=%d\n", ld.r1, ld.imm, ld.x6_sz); @@ -1138,7 +1138,7 @@ emulate_load_float (unsigned long ifa, load_store_t ld, struct pt_regs *regs) * See comments in ldX for descriptions on how the various loads are handled. */ if (ld.x6_op != 0x2) { - if (copy_from_user(&fpr_init, (void *) ifa, len)) + if (copy_from_user(&fpr_init, (void __user *) ifa, len)) return -1; DPRINT("ld.r1=%d x6_sz=%d\n", ld.r1, ld.x6_sz); @@ -1230,7 +1230,7 @@ emulate_store_float (unsigned long ifa, load_store_t ld, struct pt_regs *regs) DDUMP("fpr_init =", &fpr_init, len); DDUMP("fpr_final =", &fpr_final, len); - if (copy_to_user((void *) ifa, &fpr_final, len)) + if (copy_to_user((void __user *) ifa, &fpr_final, len)) return -1; /* @@ -1351,7 +1351,7 @@ ia64_handle_unaligned (unsigned long ifa, struct pt_regs *regs) DPRINT("iip=%lx ifa=%lx isr=%lx (ei=%d, sp=%d)\n", regs->cr_iip, ifa, regs->cr_ipsr, ipsr->ri, ipsr->it); - if (__copy_from_user(bundle, (void *) regs->cr_iip, 16)) + if (__copy_from_user(bundle, (void __user *) regs->cr_iip, 16)) goto failure; /* @@ -1496,7 +1496,7 @@ ia64_handle_unaligned (unsigned long ifa, struct pt_regs *regs) si.si_signo = SIGBUS; si.si_errno = 0; si.si_code = BUS_ADRALN; - si.si_addr = (void *) ifa; + si.si_addr = (void __user *) ifa; si.si_flags = 0; si.si_isr = 0; si.si_imm = 0; diff --git a/arch/ia64/kernel/vmlinux.lds.S b/arch/ia64/kernel/vmlinux.lds.S index 57912212b..b9f0db4c1 100644 --- a/arch/ia64/kernel/vmlinux.lds.S +++ b/arch/ia64/kernel/vmlinux.lds.S @@ -42,6 +42,7 @@ SECTIONS *(.text.ivt) *(.text) SCHED_TEXT + LOCK_TEXT *(.gnu.linkonce.t*) } .text2 : AT(ADDR(.text2) - LOAD_OFFSET) @@ -135,12 +136,6 @@ SECTIONS *(.init.setup) __setup_end = .; } - __param : AT(ADDR(__param) - LOAD_OFFSET) - { - __start___param = .; - *(__param) - __stop___param = .; - } .initcall.init : AT(ADDR(.initcall.init) - LOAD_OFFSET) { __initcall_start = .; diff --git a/arch/ia64/lib/csum_partial_copy.c b/arch/ia64/lib/csum_partial_copy.c index 269dcc2cb..ca7ba1e9c 100644 --- a/arch/ia64/lib/csum_partial_copy.c +++ b/arch/ia64/lib/csum_partial_copy.c @@ -1,7 +1,7 @@ /* * Network Checksum & Copy routine * - * Copyright (C) 1999, 2003 Hewlett-Packard Co + * Copyright (C) 1999, 2003-2004 Hewlett-Packard Co * Stephane Eranian * * Most of the code has been imported from Linux/Alpha @@ -105,7 +105,7 @@ out: extern unsigned long do_csum(const unsigned char *, long); static unsigned int -do_csum_partial_copy_from_user (const char *src, char *dst, int len, +do_csum_partial_copy_from_user (const char __user *src, char *dst, int len, unsigned int psum, int *errp) { unsigned long result; @@ -129,10 +129,10 @@ do_csum_partial_copy_from_user (const char *src, char *dst, int len, } unsigned int -csum_partial_copy_from_user(const char *src, char *dst, int len, - unsigned int sum, int *errp) +csum_partial_copy_from_user (const char __user *src, char *dst, int len, + unsigned int sum, int *errp) { - if (!access_ok(src, len, VERIFY_READ)) { + if (!access_ok(VERIFY_READ, src, len)) { *errp = -EFAULT; memset(dst, 0, len); return sum; @@ -142,7 +142,7 @@ csum_partial_copy_from_user(const char *src, char *dst, int len, } unsigned int -csum_partial_copy_nocheck(const char *src, char *dst, int len, unsigned int sum) +csum_partial_copy_nocheck(const char __user *src, char *dst, int len, unsigned int sum) { return do_csum_partial_copy_from_user(src, dst, len, sum, NULL); } diff --git a/arch/ia64/lib/io.c b/arch/ia64/lib/io.c index 90f0f3061..2bc5be82d 100644 --- a/arch/ia64/lib/io.c +++ b/arch/ia64/lib/io.c @@ -9,7 +9,7 @@ * This needs to be optimized. */ void -__ia64_memcpy_fromio (void *to, unsigned long from, long count) +__ia64_memcpy_fromio (void *to, volatile void __iomem *from, long count) { char *dst = to; @@ -25,7 +25,7 @@ EXPORT_SYMBOL(__ia64_memcpy_fromio); * This needs to be optimized. */ void -__ia64_memcpy_toio (unsigned long to, void *from, long count) +__ia64_memcpy_toio (volatile void __iomem *to, void *from, long count) { char *src = from; @@ -41,7 +41,7 @@ EXPORT_SYMBOL(__ia64_memcpy_toio); * This needs to be optimized. */ void -__ia64_memset_c_io (unsigned long dst, unsigned long c, long count) +__ia64_memset_c_io (volatile void __iomem *dst, unsigned long c, long count) { unsigned char ch = (char)(c & 0xff); @@ -111,49 +111,49 @@ __ia64_outl (unsigned int val, unsigned long port) } unsigned char -__ia64_readb (void *addr) +__ia64_readb (void __iomem *addr) { return ___ia64_readb (addr); } unsigned short -__ia64_readw (void *addr) +__ia64_readw (void __iomem *addr) { return ___ia64_readw (addr); } unsigned int -__ia64_readl (void *addr) +__ia64_readl (void __iomem *addr) { return ___ia64_readl (addr); } unsigned long -__ia64_readq (void *addr) +__ia64_readq (void __iomem *addr) { return ___ia64_readq (addr); } unsigned char -__ia64_readb_relaxed (void *addr) +__ia64_readb_relaxed (void __iomem *addr) { return ___ia64_readb (addr); } unsigned short -__ia64_readw_relaxed (void *addr) +__ia64_readw_relaxed (void __iomem *addr) { return ___ia64_readw (addr); } unsigned int -__ia64_readl_relaxed (void *addr) +__ia64_readl_relaxed (void __iomem *addr) { return ___ia64_readl (addr); } unsigned long -__ia64_readq_relaxed (void *addr) +__ia64_readq_relaxed (void __iomem *addr) { return ___ia64_readq (addr); } diff --git a/arch/ia64/lib/swiotlb.c b/arch/ia64/lib/swiotlb.c index 781e23e66..9d4486f87 100644 --- a/arch/ia64/lib/swiotlb.c +++ b/arch/ia64/lib/swiotlb.c @@ -11,6 +11,7 @@ * 03/05/07 davidm Switch from PCI-DMA to generic device DMA API. * 00/12/13 davidm Rename to swiotlb.c and add mark_clean() to avoid * unnecessary i-cache flushing. + * 04/07/.. ak Better overflow handling. Assorted fixes. */ #include @@ -20,6 +21,7 @@ #include #include #include +#include #include #include @@ -46,6 +48,8 @@ */ #define IO_TLB_SHIFT 11 +int swiotlb_force; + /* * Used to do a quick range check in swiotlb_unmap_single and swiotlb_sync_single_*, to see * if the memory was in fact allocated by this API. @@ -55,8 +59,16 @@ static char *io_tlb_start, *io_tlb_end; /* * The number of IO TLB blocks (in groups of 64) betweeen io_tlb_start and io_tlb_end. * This is command line adjustable via setup_io_tlb_npages. + * Default to 64MB. + */ +static unsigned long io_tlb_nslabs = 32768; + +/* + * When the IOMMU overflows we return a fallback buffer. This sets the size. */ -static unsigned long io_tlb_nslabs = 1024; +static unsigned long io_tlb_overflow = 32*1024; + +void *io_tlb_overflow_buffer; /* * This is a free list describing the number of free entries available from each index @@ -78,15 +90,19 @@ static spinlock_t io_tlb_lock = SPIN_LOCK_UNLOCKED; static int __init setup_io_tlb_npages (char *str) { - io_tlb_nslabs = simple_strtoul(str, NULL, 0) << (PAGE_SHIFT - IO_TLB_SHIFT); - - /* avoid tail segment of size < IO_TLB_SEGSIZE */ - io_tlb_nslabs = ALIGN(io_tlb_nslabs, IO_TLB_SEGSIZE); - + if (isdigit(*str)) { + io_tlb_nslabs = simple_strtoul(str, &str, 0) << (PAGE_SHIFT - IO_TLB_SHIFT); + /* avoid tail segment of size < IO_TLB_SEGSIZE */ + io_tlb_nslabs = ALIGN(io_tlb_nslabs, IO_TLB_SEGSIZE); + } + if (*str == ',') + ++str; + if (!strcmp(str, "force")) + swiotlb_force = 1; return 1; } __setup("swiotlb=", setup_io_tlb_npages); - +/* make io_tlb_overflow tunable too? */ /* * Statically reserve bounce buffer space and initialize bounce buffer data structures for @@ -102,7 +118,7 @@ swiotlb_init (void) */ io_tlb_start = alloc_bootmem_low_pages(io_tlb_nslabs * (1 << IO_TLB_SHIFT)); if (!io_tlb_start) - BUG(); + panic("Cannot allocate SWIOTLB buffer"); io_tlb_end = io_tlb_start + io_tlb_nslabs * (1 << IO_TLB_SHIFT); /* @@ -115,11 +131,23 @@ swiotlb_init (void) 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 *)); - - printk(KERN_INFO "Placing software IO TLB between 0x%p - 0x%p\n", - (void *) io_tlb_start, (void *) io_tlb_end); + + /* + * Get the overflow emergency buffer + */ + io_tlb_overflow_buffer = alloc_bootmem_low(io_tlb_overflow); + printk(KERN_INFO "Placing software IO TLB between 0x%lx - 0x%lx\n", + virt_to_phys(io_tlb_start), virt_to_phys(io_tlb_end)); } +static inline int address_needs_mapping(struct device *hwdev, dma_addr_t addr) +{ + dma_addr_t mask = 0xffffffff; + if (hwdev && hwdev->dma_mask) + mask = *hwdev->dma_mask; + return (addr & ~mask) != 0; +} + /* * Allocates bounce buffer and returns its kernel virtual address. */ @@ -184,11 +212,8 @@ map_single (struct device *hwdev, char *buffer, size_t size, int dir) index = 0; } while (index != wrap); - /* - * XXX What is a suitable recovery mechanism here? We cannot - * sleep because we are called from with in interrupts! - */ - panic("map_single: could not allocate software IO TLB (%ld bytes)", size); + spin_unlock_irqrestore(&io_tlb_lock, flags); + return NULL; } found: spin_unlock_irqrestore(&io_tlb_lock, flags); @@ -285,7 +310,7 @@ swiotlb_alloc_coherent (struct device *hwdev, size_t size, dma_addr_t *dma_handl memset(ret, 0, size); dev_addr = virt_to_phys(ret); - if (hwdev && hwdev->dma_mask && (dev_addr & ~*hwdev->dma_mask) != 0) + if (address_needs_mapping(hwdev,dev_addr)) panic("swiotlb_alloc_consistent: allocated memory is out of range for device"); *dma_handle = dev_addr; return ret; @@ -297,6 +322,28 @@ swiotlb_free_coherent (struct device *hwdev, size_t size, void *vaddr, dma_addr_ free_pages((unsigned long) vaddr, get_order(size)); } +static void swiotlb_full(struct device *dev, size_t size, int dir, int do_panic) +{ + /* + * Ran out of IOMMU space for this operation. This is very bad. + * Unfortunately the drivers cannot handle this operation properly. + * unless they check for pci_dma_mapping_error (most don't) + * When the mapping is small enough return a static buffer to limit + * the damage, or panic when the transfer is too big. + */ + + printk(KERN_ERR + "PCI-DMA: Out of SW-IOMMU space for %lu bytes at device %s\n", + size, dev ? dev->bus_id : "?"); + + if (size > io_tlb_overflow && do_panic) { + if (dir == PCI_DMA_FROMDEVICE || dir == PCI_DMA_BIDIRECTIONAL) + panic("PCI-DMA: Memory would be corrupted\n"); + if (dir == PCI_DMA_TODEVICE || dir == PCI_DMA_BIDIRECTIONAL) + panic("PCI-DMA: Random memory would be DMAed\n"); + } +} + /* * Map a single buffer of the indicated size for DMA in streaming mode. The PCI address * to use is returned. @@ -308,13 +355,14 @@ dma_addr_t swiotlb_map_single (struct device *hwdev, void *ptr, size_t size, int dir) { unsigned long dev_addr = virt_to_phys(ptr); + void *map; if (dir == DMA_NONE) BUG(); /* * Check if the PCI device can DMA to ptr... if so, just return ptr */ - if (hwdev && hwdev->dma_mask && (dev_addr & ~*hwdev->dma_mask) == 0) + if (!address_needs_mapping(hwdev, dev_addr) && !swiotlb_force) /* * Device is bit capable of DMA'ing to the buffer... just return the PCI * address of ptr @@ -324,12 +372,18 @@ swiotlb_map_single (struct device *hwdev, void *ptr, size_t size, int dir) /* * get a bounce buffer: */ - dev_addr = virt_to_phys(map_single(hwdev, ptr, size, dir)); + map = map_single(hwdev, ptr, size, dir); + if (!map) { + swiotlb_full(hwdev, size, dir, 1); + map = io_tlb_overflow_buffer; + } + + dev_addr = virt_to_phys(map); /* * Ensure that the address returned is DMA'ble: */ - if (hwdev && hwdev->dma_mask && (dev_addr & ~*hwdev->dma_mask) != 0) + if (address_needs_mapping(hwdev, dev_addr)) panic("map_single: bounce buffer is not DMA'ble"); return dev_addr; @@ -437,9 +491,17 @@ swiotlb_map_sg (struct device *hwdev, struct scatterlist *sg, int nelems, int di for (i = 0; i < nelems; i++, sg++) { addr = SG_ENT_VIRT_ADDRESS(sg); dev_addr = virt_to_phys(addr); - if (hwdev && hwdev->dma_mask && (dev_addr & ~*hwdev->dma_mask) != 0) - sg->dma_address = (dma_addr_t) map_single(hwdev, addr, sg->length, dir); - else + if (swiotlb_force || address_needs_mapping(hwdev, dev_addr)) { + sg->dma_address = (dma_addr_t) virt_to_phys(map_single(hwdev, addr, sg->length, dir)); + if (!sg->dma_address) { + /* Don't panic here, we expect pci_map_sg users + to do proper error handling. */ + swiotlb_full(hwdev, sg->length, dir, 0); + swiotlb_unmap_sg(hwdev, sg - i, i, dir); + sg[0].dma_length = 0; + return 0; + } + } else sg->dma_address = dev_addr; sg->dma_length = sg->length; } @@ -460,7 +522,7 @@ swiotlb_unmap_sg (struct device *hwdev, struct scatterlist *sg, int nelems, int for (i = 0; i < nelems; i++, sg++) if (sg->dma_address != SG_ENT_PHYS_ADDRESS(sg)) - unmap_single(hwdev, (void *) sg->dma_address, sg->dma_length, dir); + unmap_single(hwdev, (void *) phys_to_virt(sg->dma_address), sg->dma_length, dir); else if (dir == DMA_FROM_DEVICE) mark_clean(SG_ENT_VIRT_ADDRESS(sg), sg->dma_length); } @@ -501,7 +563,7 @@ swiotlb_sync_sg_for_device (struct device *hwdev, struct scatterlist *sg, int ne int swiotlb_dma_mapping_error (dma_addr_t dma_addr) { - return 0; + return (dma_addr == virt_to_phys(io_tlb_overflow_buffer)); } /* diff --git a/arch/ia64/mm/extable.c b/arch/ia64/mm/extable.c index 2353cc795..99edfed31 100644 --- a/arch/ia64/mm/extable.c +++ b/arch/ia64/mm/extable.c @@ -77,7 +77,7 @@ search_extable (const struct exception_table_entry *first, else last = mid - 1; } - return 0; + return NULL; } void diff --git a/arch/ia64/oprofile/Kconfig b/arch/ia64/oprofile/Kconfig index bfac82f3e..56e6f614b 100644 --- a/arch/ia64/oprofile/Kconfig +++ b/arch/ia64/oprofile/Kconfig @@ -16,6 +16,10 @@ config OPROFILE whole system, include the kernel, kernel modules, libraries, and applications. + Due to firmware bugs, you may need to use the "nohalt" boot + option if you're using OProfile with the hardware performance + counters. + If unsure, say N. endmenu diff --git a/arch/ia64/oprofile/Makefile b/arch/ia64/oprofile/Makefile index 06e7c81ea..29aeef4f6 100644 --- a/arch/ia64/oprofile/Makefile +++ b/arch/ia64/oprofile/Makefile @@ -7,3 +7,4 @@ DRIVER_OBJS := $(addprefix ../../../drivers/oprofile/, \ timer_int.o ) oprofile-y := $(DRIVER_OBJS) init.o +oprofile-$(CONFIG_PERFMON) += perfmon.o diff --git a/arch/ia64/oprofile/init.c b/arch/ia64/oprofile/init.c index 558561d6d..6d0c132b0 100644 --- a/arch/ia64/oprofile/init.c +++ b/arch/ia64/oprofile/init.c @@ -12,14 +12,21 @@ #include #include -extern void timer_init(struct oprofile_operations ** ops); +extern int perfmon_init(struct oprofile_operations ** ops); +extern void perfmon_exit(void); int __init oprofile_arch_init(struct oprofile_operations ** ops) { +#ifdef CONFIG_PERFMON + return perfmon_init(ops); +#endif return -ENODEV; } void oprofile_arch_exit(void) { +#ifdef CONFIG_PERFMON + perfmon_exit(); +#endif } diff --git a/arch/ia64/oprofile/perfmon.c b/arch/ia64/oprofile/perfmon.c new file mode 100644 index 000000000..bfc82ccf5 --- /dev/null +++ b/arch/ia64/oprofile/perfmon.c @@ -0,0 +1,105 @@ +/** + * @file perfmon.c + * + * @remark Copyright 2003 OProfile authors + * @remark Read the file COPYING + * + * @author John Levon + */ + +#include +#include +#include +#include +#include +#include +#include + +static int allow_ints; + +static int +perfmon_handler(struct task_struct *task, void *buf, pfm_ovfl_arg_t *arg, + struct pt_regs *regs, unsigned long stamp) +{ + int cpu = smp_processor_id(); + unsigned long eip = instruction_pointer(regs); + int event = arg->pmd_eventid; + + arg->ovfl_ctrl.bits.reset_ovfl_pmds = 1; + + /* the owner of the oprofile event buffer may have exited + * without perfmon being shutdown (e.g. SIGSEGV) + */ + if (allow_ints) + oprofile_add_sample(eip, !user_mode(regs), event, cpu); + return 0; +} + + +static int perfmon_start(void) +{ + allow_ints = 1; + return 0; +} + + +static void perfmon_stop(void) +{ + allow_ints = 0; +} + + +#define OPROFILE_FMT_UUID { \ + 0x77, 0x7a, 0x6e, 0x61, 0x20, 0x65, 0x73, 0x69, 0x74, 0x6e, 0x72, 0x20, 0x61, 0x65, 0x0a, 0x6c } + +static pfm_buffer_fmt_t oprofile_fmt = { + .fmt_name = "oprofile_format", + .fmt_uuid = OPROFILE_FMT_UUID, + .fmt_handler = perfmon_handler, +}; + + +static char * get_cpu_type(void) +{ + __u8 family = local_cpu_data->family; + + switch (family) { + case 0x07: + return "ia64/itanium"; + case 0x1f: + return "ia64/itanium2"; + default: + return "ia64/ia64"; + } +} + + +/* all the ops are handled via userspace for IA64 perfmon */ +static struct oprofile_operations perfmon_ops = { + .start = perfmon_start, + .stop = perfmon_stop, +}; + +static int using_perfmon; + +int perfmon_init(struct oprofile_operations ** ops) +{ + int ret = pfm_register_buffer_fmt(&oprofile_fmt); + if (ret) + return -ENODEV; + + perfmon_ops.cpu_type = get_cpu_type(); + *ops = &perfmon_ops; + using_perfmon = 1; + printk(KERN_INFO "oprofile: using perfmon.\n"); + return 0; +} + + +void perfmon_exit(void) +{ + if (!using_perfmon) + return; + + pfm_unregister_buffer_fmt(oprofile_fmt.fmt_uuid); +} diff --git a/arch/ia64/sn/include/ioerror.h b/arch/ia64/sn/include/ioerror.h new file mode 100644 index 000000000..e68f2b078 --- /dev/null +++ b/arch/ia64/sn/include/ioerror.h @@ -0,0 +1,81 @@ +/* + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file "COPYING" in the main directory of this archive + * for more details. + * + * Copyright (C) 1992 - 1997, 2000-2003 Silicon Graphics, Inc. All rights reserved. + */ +#ifndef _ASM_IA64_SN_IOERROR_H +#define _ASM_IA64_SN_IOERROR_H + +/* + * IO error structure. + * + * This structure would expand to hold the information retrieved from + * all IO related error registers. + * + * This structure is defined to hold all system specific + * information related to a single error. + * + * This serves a couple of purpose. + * - Error handling often involves translating one form of address to other + * form. So, instead of having different data structures at each level, + * we have a single structure, and the appropriate fields get filled in + * at each layer. + * - This provides a way to dump all error related information in any layer + * of erorr handling (debugging aid). + * + * A second possibility is to allow each layer to define its own error + * data structure, and fill in the proper fields. This has the advantage + * of isolating the layers. + * A big concern is the potential stack usage (and overflow), if each layer + * defines these structures on stack (assuming we don't want to do kmalloc. + * + * Any layer wishing to pass extra information to a layer next to it in + * error handling hierarchy, can do so as a separate parameter. + */ + +typedef struct io_error_s { + /* Bit fields indicating which structure fields are valid */ + union { + struct { + unsigned ievb_errortype:1; + unsigned ievb_widgetnum:1; + unsigned ievb_widgetdev:1; + unsigned ievb_srccpu:1; + unsigned ievb_srcnode:1; + unsigned ievb_errnode:1; + unsigned ievb_sysioaddr:1; + unsigned ievb_xtalkaddr:1; + unsigned ievb_busspace:1; + unsigned ievb_busaddr:1; + unsigned ievb_vaddr:1; + unsigned ievb_memaddr:1; + unsigned ievb_epc:1; + unsigned ievb_ef:1; + unsigned ievb_tnum:1; + } iev_b; + unsigned iev_a; + } ie_v; + + short ie_errortype; /* error type: extra info about error */ + short ie_widgetnum; /* Widget number that's in error */ + short ie_widgetdev; /* Device within widget in error */ + cpuid_t ie_srccpu; /* CPU on srcnode generating error */ + cnodeid_t ie_srcnode; /* Node which caused the error */ + cnodeid_t ie_errnode; /* Node where error was noticed */ + iopaddr_t ie_sysioaddr; /* Sys specific IO address */ + iopaddr_t ie_xtalkaddr; /* Xtalk (48bit) addr of Error */ + iopaddr_t ie_busspace; /* Bus specific address space */ + iopaddr_t ie_busaddr; /* Bus specific address */ + caddr_t ie_vaddr; /* Virtual address of error */ + iopaddr_t ie_memaddr; /* Physical memory address */ + caddr_t ie_epc; /* pc when error reported */ + caddr_t ie_ef; /* eframe when error reported */ + short ie_tnum; /* Xtalk TNUM field */ +} ioerror_t; + +#define IOERROR_INIT(e) do { (e)->ie_v.iev_a = 0; } while (0) +#define IOERROR_SETVALUE(e,f,v) do { (e)->ie_ ## f = (v); (e)->ie_v.iev_b.ievb_ ## f = 1; } while (0) + +#endif /* _ASM_IA64_SN_IOERROR_H */ diff --git a/arch/ia64/sn/include/pci/pcibr_provider.h b/arch/ia64/sn/include/pci/pcibr_provider.h new file mode 100644 index 000000000..b1f05ffec --- /dev/null +++ b/arch/ia64/sn/include/pci/pcibr_provider.h @@ -0,0 +1,149 @@ +/* + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file "COPYING" in the main directory of this archive + * for more details. + * + * Copyright (C) 1992-1997,2000-2004 Silicon Graphics, Inc. All rights reserved. + */ +#ifndef _ASM_IA64_SN_PCI_PCIBR_PROVIDER_H +#define _ASM_IA64_SN_PCI_PCIBR_PROVIDER_H + +/* Workarounds */ +#define PV907516 (1 << 1) /* TIOCP: Don't write the write buffer flush reg */ + +#define BUSTYPE_MASK 0x1 + +/* Macros given a pcibus structure */ +#define IS_PCIX(ps) ((ps)->pbi_bridge_mode & BUSTYPE_MASK) +#define IS_PCI_BRIDGE_ASIC(asic) (asic == PCIIO_ASIC_TYPE_PIC || \ + asic == PCIIO_ASIC_TYPE_TIOCP) +#define IS_PIC_SOFT(ps) (ps->pbi_bridge_type == PCIBR_BRIDGETYPE_PIC) + + +/* + * The different PCI Bridge types supported on the SGI Altix platforms + */ +#define PCIBR_BRIDGETYPE_UNKNOWN -1 +#define PCIBR_BRIDGETYPE_PIC 2 +#define PCIBR_BRIDGETYPE_TIOCP 3 + +/* + * Bridge 64bit Direct Map Attributes + */ +#define PCI64_ATTR_PREF (1ull << 59) +#define PCI64_ATTR_PREC (1ull << 58) +#define PCI64_ATTR_VIRTUAL (1ull << 57) +#define PCI64_ATTR_BAR (1ull << 56) +#define PCI64_ATTR_SWAP (1ull << 55) +#define PCI64_ATTR_VIRTUAL1 (1ull << 54) + +#define PCI32_LOCAL_BASE 0 +#define PCI32_MAPPED_BASE 0x40000000 +#define PCI32_DIRECT_BASE 0x80000000 + +#define IS_PCI32_MAPPED(x) ((uint64_t)(x) < PCI32_DIRECT_BASE && \ + (uint64_t)(x) >= PCI32_MAPPED_BASE) +#define IS_PCI32_DIRECT(x) ((uint64_t)(x) >= PCI32_MAPPED_BASE) + + +/* + * Bridge PMU Address Transaltion Entry Attibutes + */ +#define PCI32_ATE_V (0x1 << 0) +#define PCI32_ATE_CO (0x1 << 1) +#define PCI32_ATE_PREC (0x1 << 2) +#define PCI32_ATE_PREF (0x1 << 3) +#define PCI32_ATE_BAR (0x1 << 4) +#define PCI32_ATE_ADDR_SHFT 12 + +#define MINIMAL_ATES_REQUIRED(addr, size) \ + (IOPG(IOPGOFF(addr) + (size) - 1) == IOPG((size) - 1)) + +#define MINIMAL_ATE_FLAG(addr, size) \ + (MINIMAL_ATES_REQUIRED((uint64_t)addr, size) ? 1 : 0) + +/* bit 29 of the pci address is the SWAP bit */ +#define ATE_SWAPSHIFT 29 +#define ATE_SWAP_ON(x) ((x) |= (1 << ATE_SWAPSHIFT)) +#define ATE_SWAP_OFF(x) ((x) &= ~(1 << ATE_SWAPSHIFT)) + +/* + * I/O page size + */ +#if PAGE_SIZE < 16384 +#define IOPFNSHIFT 12 /* 4K per mapped page */ +#else +#define IOPFNSHIFT 14 /* 16K per mapped page */ +#endif + +#define IOPGSIZE (1 << IOPFNSHIFT) +#define IOPG(x) ((x) >> IOPFNSHIFT) +#define IOPGOFF(x) ((x) & (IOPGSIZE-1)) + +#define PCIBR_DEV_SWAP_DIR (1ull << 19) +#define PCIBR_CTRL_PAGE_SIZE (0x1 << 21) + +/* + * PMU resources. + */ +struct ate_resource{ + uint64_t *ate; + uint64_t num_ate; + uint64_t lowest_free_index; +}; + +struct pcibus_info { + struct pcibus_bussoft pbi_buscommon; /* common header */ + uint32_t pbi_moduleid; + short pbi_bridge_type; + short pbi_bridge_mode; + + struct ate_resource pbi_int_ate_resource; + uint64_t pbi_int_ate_size; + + uint64_t pbi_dir_xbase; + char pbi_hub_xid; + + uint64_t pbi_devreg[8]; + spinlock_t pbi_lock; + + uint32_t pbi_valid_devices; + uint32_t pbi_enabled_devices; +}; + +/* + * pcibus_info structure locking macros + */ +inline static unsigned long +pcibr_lock(struct pcibus_info *pcibus_info) +{ + unsigned long flag; + spin_lock_irqsave(&pcibus_info->pbi_lock, flag); + return(flag); +} +#define pcibr_unlock(pcibus_info, flag) spin_unlock_irqrestore(&pcibus_info->pbi_lock, flag) + +extern void *pcibr_bus_fixup(struct pcibus_bussoft *); +extern uint64_t pcibr_dma_map(struct pcidev_info *, unsigned long, size_t, unsigned int); +extern void pcibr_dma_unmap(struct pcidev_info *, dma_addr_t, int); + +/* + * prototypes for the bridge asic register access routines in pcibr_reg.c + */ +extern void pcireg_control_bit_clr(struct pcibus_info *, uint64_t); +extern void pcireg_control_bit_set(struct pcibus_info *, uint64_t); +extern uint64_t pcireg_tflush_get(struct pcibus_info *); +extern uint64_t pcireg_intr_status_get(struct pcibus_info *); +extern void pcireg_intr_enable_bit_clr(struct pcibus_info *, uint64_t); +extern void pcireg_intr_enable_bit_set(struct pcibus_info *, uint64_t); +extern void pcireg_intr_addr_addr_set(struct pcibus_info *, int, uint64_t); +extern void pcireg_force_intr_set(struct pcibus_info *, int); +extern uint64_t pcireg_wrb_flush_get(struct pcibus_info *, int); +extern void pcireg_int_ate_set(struct pcibus_info *, int, uint64_t); +extern uint64_t * pcireg_int_ate_addr(struct pcibus_info *, int); +extern void pcibr_force_interrupt(struct sn_irq_info *sn_irq_info); +extern void pcibr_change_devices_irq(struct sn_irq_info *sn_irq_info); +extern int pcibr_ate_alloc(struct pcibus_info *, int); +extern void pcibr_ate_free(struct pcibus_info *, int); +extern void ate_write(struct pcibus_info *, int, int, uint64_t); +#endif diff --git a/arch/ia64/sn/include/pci/pcibus_provider_defs.h b/arch/ia64/sn/include/pci/pcibus_provider_defs.h new file mode 100644 index 000000000..07065615b --- /dev/null +++ b/arch/ia64/sn/include/pci/pcibus_provider_defs.h @@ -0,0 +1,43 @@ +/* + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file "COPYING" in the main directory of this archive + * for more details. + * + * Copyright (C) 1992 - 1997, 2000-2004 Silicon Graphics, Inc. All rights reserved. + */ +#ifndef _ASM_IA64_SN_PCI_PCIBUS_PROVIDER_H +#define _ASM_IA64_SN_PCI_PCIBUS_PROVIDER_H + +/* + * SN pci asic types. Do not ever renumber these or reuse values. The + * values must agree with what prom thinks they are. + */ + +#define PCIIO_ASIC_TYPE_UNKNOWN 0 +#define PCIIO_ASIC_TYPE_PPB 1 +#define PCIIO_ASIC_TYPE_PIC 2 +#define PCIIO_ASIC_TYPE_TIOCP 3 + +/* + * Common pciio bus provider data. There should be one of these as the + * first field in any pciio based provider soft structure (e.g. pcibr_soft + * tioca_soft, etc). + */ + +struct pcibus_bussoft { + uint32_t bs_asic_type; /* chipset type */ + uint32_t bs_xid; /* xwidget id */ + uint64_t bs_persist_busnum; /* Persistent Bus Number */ + uint64_t bs_legacy_io; /* legacy io pio addr */ + uint64_t bs_legacy_mem; /* legacy mem pio addr */ + uint64_t bs_base; /* widget base */ + struct xwidget_info *bs_xwidget_info; +}; + +/* + * DMA mapping flags + */ + +#define SN_PCIDMA_CONSISTENT 0x0001 + +#endif /* _ASM_IA64_SN_PCI_PCIBUS_PROVIDER_H */ diff --git a/arch/ia64/sn/include/pci/pcidev.h b/arch/ia64/sn/include/pci/pcidev.h new file mode 100644 index 000000000..81eb95d3b --- /dev/null +++ b/arch/ia64/sn/include/pci/pcidev.h @@ -0,0 +1,54 @@ +/* + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file "COPYING" in the main directory of this archive + * for more details. + * + * Copyright (C) 1992 - 1997, 2000-2004 Silicon Graphics, Inc. All rights reserved. + */ +#ifndef _ASM_IA64_SN_PCI_PCIDEV_H +#define _ASM_IA64_SN_PCI_PCIDEV_H + +#include + +extern struct sn_irq_info **sn_irq; + +#define SN_PCIDEV_INFO(pci_dev) \ + ((struct pcidev_info *)(pci_dev)->sysdata) + +/* + * Given a pci_bus, return the sn pcibus_bussoft struct. Note that + * this only works for root busses, not for busses represented by PPB's. + */ + +#define SN_PCIBUS_BUSSOFT(pci_bus) \ + ((struct pcibus_bussoft *)(PCI_CONTROLLER((pci_bus))->platform_data)) + +/* + * Given a struct pci_dev, return the sn pcibus_bussoft struct. Note + * that this is not equivalent to SN_PCIBUS_BUSSOFT(pci_dev->bus) due + * due to possible PPB's in the path. + */ + +#define SN_PCIDEV_BUSSOFT(pci_dev) \ + (SN_PCIDEV_INFO(pci_dev)->pdi_host_pcidev_info->pdi_pcibus_info) + +#define PCIIO_BUS_NONE 255 /* bus 255 reserved */ +#define PCIIO_SLOT_NONE 255 +#define PCIIO_FUNC_NONE 255 +#define PCIIO_VENDOR_ID_NONE (-1) + +struct pcidev_info { + uint64_t pdi_pio_mapped_addr[7]; /* 6 BARs PLUS 1 ROM */ + uint64_t pdi_slot_host_handle; /* Bus and devfn Host pci_dev */ + + struct pcibus_bussoft *pdi_pcibus_info; /* Kernel common bus soft */ + struct pcidev_info *pdi_host_pcidev_info; /* Kernel Host pci_dev */ + struct pci_dev *pdi_linux_pcidev; /* Kernel pci_dev */ + + struct sn_irq_info *pdi_sn_irq_info; +}; + +extern void sn_irq_fixup(struct pci_dev *pci_dev, + struct sn_irq_info *sn_irq_info); + +#endif /* _ASM_IA64_SN_PCI_PCIDEV_H */ diff --git a/arch/ia64/sn/include/pci/pic.h b/arch/ia64/sn/include/pci/pic.h new file mode 100644 index 000000000..fd18acecb --- /dev/null +++ b/arch/ia64/sn/include/pci/pic.h @@ -0,0 +1,261 @@ +/* + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file "COPYING" in the main directory of this archive + * for more details. + * + * Copyright (C) 1992 - 1997, 2000-2003 Silicon Graphics, Inc. All rights reserved. + */ +#ifndef _ASM_IA64_SN_PCI_PIC_H +#define _ASM_IA64_SN_PCI_PIC_H + +/* + * PIC AS DEVICE ZERO + * ------------------ + * + * PIC handles PCI/X busses. PCI/X requires that the 'bridge' (i.e. PIC) + * be designated as 'device 0'. That is a departure from earlier SGI + * PCI bridges. Because of that we use config space 1 to access the + * config space of the first actual PCI device on the bus. + * Here's what the PIC manual says: + * + * The current PCI-X bus specification now defines that the parent + * hosts bus bridge (PIC for example) must be device 0 on bus 0. PIC + * reduced the total number of devices from 8 to 4 and removed the + * device registers and windows, now only supporting devices 0,1,2, and + * 3. PIC did leave all 8 configuration space windows. The reason was + * there was nothing to gain by removing them. Here in lies the problem. + * The device numbering we do using 0 through 3 is unrelated to the device + * numbering which PCI-X requires in configuration space. In the past we + * correlated Configs pace and our device space 0 <-> 0, 1 <-> 1, etc. + * PCI-X requires we start a 1, not 0 and currently the PX brick + * does associate our: + * + * device 0 with configuration space window 1, + * device 1 with configuration space window 2, + * device 2 with configuration space window 3, + * device 3 with configuration space window 4. + * + * The net effect is that all config space access are off-by-one with + * relation to other per-slot accesses on the PIC. + * Here is a table that shows some of that: + * + * Internal Slot# + * | + * | 0 1 2 3 + * ----------|--------------------------------------- + * config | 0x21000 0x22000 0x23000 0x24000 + * | + * even rrb | 0[0] n/a 1[0] n/a [] == implied even/odd + * | + * odd rrb | n/a 0[1] n/a 1[1] + * | + * int dev | 00 01 10 11 + * | + * ext slot# | 1 2 3 4 + * ----------|--------------------------------------- + */ + +#define PIC_ATE_TARGETID_SHFT 8 +#define PIC_HOST_INTR_ADDR 0x0000FFFFFFFFFFFFUL +#define PIC_PCI64_ATTR_TARG_SHFT 60 + + +/***************************************************************************** + *********************** PIC MMR structure mapping *************************** + *****************************************************************************/ + +/* NOTE: PIC WAR. PV#854697. PIC does not allow writes just to [31:0] + * of a 64-bit register. When writing PIC registers, always write the + * entire 64 bits. + */ + +struct pic { + + /* 0x000000-0x00FFFF -- Local Registers */ + + /* 0x000000-0x000057 -- Standard Widget Configuration */ + uint64_t p_wid_id; /* 0x000000 */ + uint64_t p_wid_stat; /* 0x000008 */ + uint64_t p_wid_err_upper; /* 0x000010 */ + uint64_t p_wid_err_lower; /* 0x000018 */ + #define p_wid_err p_wid_err_lower + uint64_t p_wid_control; /* 0x000020 */ + uint64_t p_wid_req_timeout; /* 0x000028 */ + uint64_t p_wid_int_upper; /* 0x000030 */ + uint64_t p_wid_int_lower; /* 0x000038 */ + #define p_wid_int p_wid_int_lower + uint64_t p_wid_err_cmdword; /* 0x000040 */ + uint64_t p_wid_llp; /* 0x000048 */ + uint64_t p_wid_tflush; /* 0x000050 */ + + /* 0x000058-0x00007F -- Bridge-specific Widget Configuration */ + uint64_t p_wid_aux_err; /* 0x000058 */ + uint64_t p_wid_resp_upper; /* 0x000060 */ + uint64_t p_wid_resp_lower; /* 0x000068 */ + #define p_wid_resp p_wid_resp_lower + uint64_t p_wid_tst_pin_ctrl; /* 0x000070 */ + uint64_t p_wid_addr_lkerr; /* 0x000078 */ + + /* 0x000080-0x00008F -- PMU & MAP */ + uint64_t p_dir_map; /* 0x000080 */ + uint64_t _pad_000088; /* 0x000088 */ + + /* 0x000090-0x00009F -- SSRAM */ + uint64_t p_map_fault; /* 0x000090 */ + uint64_t _pad_000098; /* 0x000098 */ + + /* 0x0000A0-0x0000AF -- Arbitration */ + uint64_t p_arb; /* 0x0000A0 */ + uint64_t _pad_0000A8; /* 0x0000A8 */ + + /* 0x0000B0-0x0000BF -- Number In A Can or ATE Parity Error */ + uint64_t p_ate_parity_err; /* 0x0000B0 */ + uint64_t _pad_0000B8; /* 0x0000B8 */ + + /* 0x0000C0-0x0000FF -- PCI/GIO */ + uint64_t p_bus_timeout; /* 0x0000C0 */ + uint64_t p_pci_cfg; /* 0x0000C8 */ + uint64_t p_pci_err_upper; /* 0x0000D0 */ + uint64_t p_pci_err_lower; /* 0x0000D8 */ + #define p_pci_err p_pci_err_lower + uint64_t _pad_0000E0[4]; /* 0x0000{E0..F8} */ + + /* 0x000100-0x0001FF -- Interrupt */ + uint64_t p_int_status; /* 0x000100 */ + uint64_t p_int_enable; /* 0x000108 */ + uint64_t p_int_rst_stat; /* 0x000110 */ + uint64_t p_int_mode; /* 0x000118 */ + uint64_t p_int_device; /* 0x000120 */ + uint64_t p_int_host_err; /* 0x000128 */ + uint64_t p_int_addr[8]; /* 0x0001{30,,,68} */ + uint64_t p_err_int_view; /* 0x000170 */ + uint64_t p_mult_int; /* 0x000178 */ + uint64_t p_force_always[8]; /* 0x0001{80,,,B8} */ + uint64_t p_force_pin[8]; /* 0x0001{C0,,,F8} */ + + /* 0x000200-0x000298 -- Device */ + uint64_t p_device[4]; /* 0x0002{00,,,18} */ + uint64_t _pad_000220[4]; /* 0x0002{20,,,38} */ + uint64_t p_wr_req_buf[4]; /* 0x0002{40,,,58} */ + uint64_t _pad_000260[4]; /* 0x0002{60,,,78} */ + uint64_t p_rrb_map[2]; /* 0x0002{80,,,88} */ + #define p_even_resp p_rrb_map[0] /* 0x000280 */ + #define p_odd_resp p_rrb_map[1] /* 0x000288 */ + uint64_t p_resp_status; /* 0x000290 */ + uint64_t p_resp_clear; /* 0x000298 */ + + uint64_t _pad_0002A0[12]; /* 0x0002{A0..F8} */ + + /* 0x000300-0x0003F8 -- Buffer Address Match Registers */ + struct { + uint64_t upper; /* 0x0003{00,,,F0} */ + uint64_t lower; /* 0x0003{08,,,F8} */ + } p_buf_addr_match[16]; + + /* 0x000400-0x0005FF -- Performance Monitor Registers (even only) */ + struct { + uint64_t flush_w_touch; /* 0x000{400,,,5C0} */ + uint64_t flush_wo_touch; /* 0x000{408,,,5C8} */ + uint64_t inflight; /* 0x000{410,,,5D0} */ + uint64_t prefetch; /* 0x000{418,,,5D8} */ + uint64_t total_pci_retry; /* 0x000{420,,,5E0} */ + uint64_t max_pci_retry; /* 0x000{428,,,5E8} */ + uint64_t max_latency; /* 0x000{430,,,5F0} */ + uint64_t clear_all; /* 0x000{438,,,5F8} */ + } p_buf_count[8]; + + + /* 0x000600-0x0009FF -- PCI/X registers */ + uint64_t p_pcix_bus_err_addr; /* 0x000600 */ + uint64_t p_pcix_bus_err_attr; /* 0x000608 */ + uint64_t p_pcix_bus_err_data; /* 0x000610 */ + uint64_t p_pcix_pio_split_addr; /* 0x000618 */ + uint64_t p_pcix_pio_split_attr; /* 0x000620 */ + uint64_t p_pcix_dma_req_err_attr; /* 0x000628 */ + uint64_t p_pcix_dma_req_err_addr; /* 0x000630 */ + uint64_t p_pcix_timeout; /* 0x000638 */ + + uint64_t _pad_000640[120]; /* 0x000{640,,,9F8} */ + + /* 0x000A00-0x000BFF -- PCI/X Read&Write Buffer */ + struct { + uint64_t p_buf_addr; /* 0x000{A00,,,AF0} */ + uint64_t p_buf_attr; /* 0X000{A08,,,AF8} */ + } p_pcix_read_buf_64[16]; + + struct { + uint64_t p_buf_addr; /* 0x000{B00,,,BE0} */ + uint64_t p_buf_attr; /* 0x000{B08,,,BE8} */ + uint64_t p_buf_valid; /* 0x000{B10,,,BF0} */ + uint64_t __pad1; /* 0x000{B18,,,BF8} */ + } p_pcix_write_buf_64[8]; + + /* End of Local Registers -- Start of Address Map space */ + + char _pad_000c00[0x010000 - 0x000c00]; + + /* 0x010000-0x011fff -- Internal ATE RAM (Auto Parity Generation) */ + uint64_t p_int_ate_ram[1024]; /* 0x010000-0x011fff */ + + /* 0x012000-0x013fff -- Internal ATE RAM (Manual Parity Generation) */ + uint64_t p_int_ate_ram_mp[1024]; /* 0x012000-0x013fff */ + + char _pad_014000[0x18000 - 0x014000]; + + /* 0x18000-0x197F8 -- PIC Write Request Ram */ + uint64_t p_wr_req_lower[256]; /* 0x18000 - 0x187F8 */ + uint64_t p_wr_req_upper[256]; /* 0x18800 - 0x18FF8 */ + uint64_t p_wr_req_parity[256]; /* 0x19000 - 0x197F8 */ + + char _pad_019800[0x20000 - 0x019800]; + + /* 0x020000-0x027FFF -- PCI Device Configuration Spaces */ + union { + uint8_t c[0x1000 / 1]; /* 0x02{0000,,,7FFF} */ + uint16_t s[0x1000 / 2]; /* 0x02{0000,,,7FFF} */ + uint32_t l[0x1000 / 4]; /* 0x02{0000,,,7FFF} */ + uint64_t d[0x1000 / 8]; /* 0x02{0000,,,7FFF} */ + union { + uint8_t c[0x100 / 1]; + uint16_t s[0x100 / 2]; + uint32_t l[0x100 / 4]; + uint64_t d[0x100 / 8]; + } f[8]; + } p_type0_cfg_dev[8]; /* 0x02{0000,,,7FFF} */ + + /* 0x028000-0x028FFF -- PCI Type 1 Configuration Space */ + union { + uint8_t c[0x1000 / 1]; /* 0x028000-0x029000 */ + uint16_t s[0x1000 / 2]; /* 0x028000-0x029000 */ + uint32_t l[0x1000 / 4]; /* 0x028000-0x029000 */ + uint64_t d[0x1000 / 8]; /* 0x028000-0x029000 */ + union { + uint8_t c[0x100 / 1]; + uint16_t s[0x100 / 2]; + uint32_t l[0x100 / 4]; + uint64_t d[0x100 / 8]; + } f[8]; + } p_type1_cfg; /* 0x028000-0x029000 */ + + char _pad_029000[0x030000-0x029000]; + + /* 0x030000-0x030007 -- PCI Interrupt Acknowledge Cycle */ + union { + uint8_t c[8 / 1]; + uint16_t s[8 / 2]; + uint32_t l[8 / 4]; + uint64_t d[8 / 8]; + } p_pci_iack; /* 0x030000-0x030007 */ + + char _pad_030007[0x040000-0x030008]; + + /* 0x040000-0x030007 -- PCIX Special Cycle */ + union { + uint8_t c[8 / 1]; + uint16_t s[8 / 2]; + uint32_t l[8 / 4]; + uint64_t d[8 / 8]; + } p_pcix_cycle; /* 0x040000-0x040007 */ +}; + +#endif /* _ASM_IA64_SN_PCI_PIC_H */ diff --git a/arch/ia64/sn/include/pci/tiocp.h b/arch/ia64/sn/include/pci/tiocp.h new file mode 100644 index 000000000..f07c83b2b --- /dev/null +++ b/arch/ia64/sn/include/pci/tiocp.h @@ -0,0 +1,256 @@ +/* + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file "COPYING" in the main directory of this archive + * for more details. + * + * Copyright (C) 2003-2004 Silicon Graphics, Inc. All rights reserved. + */ +#ifndef _ASM_IA64_SN_PCI_TIOCP_H +#define _ASM_IA64_SN_PCI_TIOCP_H + +#define TIOCP_HOST_INTR_ADDR 0x003FFFFFFFFFFFFFUL +#define TIOCP_PCI64_CMDTYPE_MEM (0x1ull << 60) + + +/***************************************************************************** + *********************** TIOCP MMR structure mapping *************************** + *****************************************************************************/ + +struct tiocp{ + + /* 0x000000-0x00FFFF -- Local Registers */ + + /* 0x000000-0x000057 -- (Legacy Widget Space) Configuration */ + uint64_t cp_id; /* 0x000000 */ + uint64_t cp_stat; /* 0x000008 */ + uint64_t cp_err_upper; /* 0x000010 */ + uint64_t cp_err_lower; /* 0x000018 */ + #define cp_err cp_err_lower + uint64_t cp_control; /* 0x000020 */ + uint64_t cp_req_timeout; /* 0x000028 */ + uint64_t cp_intr_upper; /* 0x000030 */ + uint64_t cp_intr_lower; /* 0x000038 */ + #define cp_intr cp_intr_lower + uint64_t cp_err_cmdword; /* 0x000040 */ + uint64_t _pad_000048; /* 0x000048 */ + uint64_t cp_tflush; /* 0x000050 */ + + /* 0x000058-0x00007F -- Bridge-specific Configuration */ + uint64_t cp_aux_err; /* 0x000058 */ + uint64_t cp_resp_upper; /* 0x000060 */ + uint64_t cp_resp_lower; /* 0x000068 */ + #define cp_resp cp_resp_lower + uint64_t cp_tst_pin_ctrl; /* 0x000070 */ + uint64_t cp_addr_lkerr; /* 0x000078 */ + + /* 0x000080-0x00008F -- PMU & MAP */ + uint64_t cp_dir_map; /* 0x000080 */ + uint64_t _pad_000088; /* 0x000088 */ + + /* 0x000090-0x00009F -- SSRAM */ + uint64_t cp_map_fault; /* 0x000090 */ + uint64_t _pad_000098; /* 0x000098 */ + + /* 0x0000A0-0x0000AF -- Arbitration */ + uint64_t cp_arb; /* 0x0000A0 */ + uint64_t _pad_0000A8; /* 0x0000A8 */ + + /* 0x0000B0-0x0000BF -- Number In A Can or ATE Parity Error */ + uint64_t cp_ate_parity_err; /* 0x0000B0 */ + uint64_t _pad_0000B8; /* 0x0000B8 */ + + /* 0x0000C0-0x0000FF -- PCI/GIO */ + uint64_t cp_bus_timeout; /* 0x0000C0 */ + uint64_t cp_pci_cfg; /* 0x0000C8 */ + uint64_t cp_pci_err_upper; /* 0x0000D0 */ + uint64_t cp_pci_err_lower; /* 0x0000D8 */ + #define cp_pci_err cp_pci_err_lower + uint64_t _pad_0000E0[4]; /* 0x0000{E0..F8} */ + + /* 0x000100-0x0001FF -- Interrupt */ + uint64_t cp_int_status; /* 0x000100 */ + uint64_t cp_int_enable; /* 0x000108 */ + uint64_t cp_int_rst_stat; /* 0x000110 */ + uint64_t cp_int_mode; /* 0x000118 */ + uint64_t cp_int_device; /* 0x000120 */ + uint64_t cp_int_host_err; /* 0x000128 */ + uint64_t cp_int_addr[8]; /* 0x0001{30,,,68} */ + uint64_t cp_err_int_view; /* 0x000170 */ + uint64_t cp_mult_int; /* 0x000178 */ + uint64_t cp_force_always[8]; /* 0x0001{80,,,B8} */ + uint64_t cp_force_pin[8]; /* 0x0001{C0,,,F8} */ + + /* 0x000200-0x000298 -- Device */ + uint64_t cp_device[4]; /* 0x0002{00,,,18} */ + uint64_t _pad_000220[4]; /* 0x0002{20,,,38} */ + uint64_t cp_wr_req_buf[4]; /* 0x0002{40,,,58} */ + uint64_t _pad_000260[4]; /* 0x0002{60,,,78} */ + uint64_t cp_rrb_map[2]; /* 0x0002{80,,,88} */ + #define cp_even_resp cp_rrb_map[0] /* 0x000280 */ + #define cp_odd_resp cp_rrb_map[1] /* 0x000288 */ + uint64_t cp_resp_status; /* 0x000290 */ + uint64_t cp_resp_clear; /* 0x000298 */ + + uint64_t _pad_0002A0[12]; /* 0x0002{A0..F8} */ + + /* 0x000300-0x0003F8 -- Buffer Address Match Registers */ + struct { + uint64_t upper; /* 0x0003{00,,,F0} */ + uint64_t lower; /* 0x0003{08,,,F8} */ + } cp_buf_addr_match[16]; + + /* 0x000400-0x0005FF -- Performance Monitor Registers (even only) */ + struct { + uint64_t flush_w_touch; /* 0x000{400,,,5C0} */ + uint64_t flush_wo_touch; /* 0x000{408,,,5C8} */ + uint64_t inflight; /* 0x000{410,,,5D0} */ + uint64_t prefetch; /* 0x000{418,,,5D8} */ + uint64_t total_pci_retry; /* 0x000{420,,,5E0} */ + uint64_t max_pci_retry; /* 0x000{428,,,5E8} */ + uint64_t max_latency; /* 0x000{430,,,5F0} */ + uint64_t clear_all; /* 0x000{438,,,5F8} */ + } cp_buf_count[8]; + + + /* 0x000600-0x0009FF -- PCI/X registers */ + uint64_t cp_pcix_bus_err_addr; /* 0x000600 */ + uint64_t cp_pcix_bus_err_attr; /* 0x000608 */ + uint64_t cp_pcix_bus_err_data; /* 0x000610 */ + uint64_t cp_pcix_pio_split_addr; /* 0x000618 */ + uint64_t cp_pcix_pio_split_attr; /* 0x000620 */ + uint64_t cp_pcix_dma_req_err_attr; /* 0x000628 */ + uint64_t cp_pcix_dma_req_err_addr; /* 0x000630 */ + uint64_t cp_pcix_timeout; /* 0x000638 */ + + uint64_t _pad_000640[24]; /* 0x000{640,,,6F8} */ + + /* 0x000700-0x000737 -- Debug Registers */ + uint64_t cp_ct_debug_ctl; /* 0x000700 */ + uint64_t cp_br_debug_ctl; /* 0x000708 */ + uint64_t cp_mux3_debug_ctl; /* 0x000710 */ + uint64_t cp_mux4_debug_ctl; /* 0x000718 */ + uint64_t cp_mux5_debug_ctl; /* 0x000720 */ + uint64_t cp_mux6_debug_ctl; /* 0x000728 */ + uint64_t cp_mux7_debug_ctl; /* 0x000730 */ + + uint64_t _pad_000738[89]; /* 0x000{738,,,9F8} */ + + /* 0x000A00-0x000BFF -- PCI/X Read&Write Buffer */ + struct { + uint64_t cp_buf_addr; /* 0x000{A00,,,AF0} */ + uint64_t cp_buf_attr; /* 0X000{A08,,,AF8} */ + } cp_pcix_read_buf_64[16]; + + struct { + uint64_t cp_buf_addr; /* 0x000{B00,,,BE0} */ + uint64_t cp_buf_attr; /* 0x000{B08,,,BE8} */ + uint64_t cp_buf_valid; /* 0x000{B10,,,BF0} */ + uint64_t __pad1; /* 0x000{B18,,,BF8} */ + } cp_pcix_write_buf_64[8]; + + /* End of Local Registers -- Start of Address Map space */ + + char _pad_000c00[0x010000 - 0x000c00]; + + /* 0x010000-0x011FF8 -- Internal ATE RAM (Auto Parity Generation) */ + uint64_t cp_int_ate_ram[1024]; /* 0x010000-0x011FF8 */ + + char _pad_012000[0x14000 - 0x012000]; + + /* 0x014000-0x015FF8 -- Internal ATE RAM (Manual Parity Generation) */ + uint64_t cp_int_ate_ram_mp[1024]; /* 0x014000-0x015FF8 */ + + char _pad_016000[0x18000 - 0x016000]; + + /* 0x18000-0x197F8 -- TIOCP Write Request Ram */ + uint64_t cp_wr_req_lower[256]; /* 0x18000 - 0x187F8 */ + uint64_t cp_wr_req_upper[256]; /* 0x18800 - 0x18FF8 */ + uint64_t cp_wr_req_parity[256]; /* 0x19000 - 0x197F8 */ + + char _pad_019800[0x1C000 - 0x019800]; + + /* 0x1C000-0x1EFF8 -- TIOCP Read Response Ram */ + uint64_t cp_rd_resp_lower[512]; /* 0x1C000 - 0x1CFF8 */ + uint64_t cp_rd_resp_upper[512]; /* 0x1D000 - 0x1DFF8 */ + uint64_t cp_rd_resp_parity[512]; /* 0x1E000 - 0x1EFF8 */ + + char _pad_01F000[0x20000 - 0x01F000]; + + /* 0x020000-0x021FFF -- Host Device (CP) Configuration Space (not used) */ + char _pad_020000[0x021000 - 0x20000]; + + /* 0x021000-0x027FFF -- PCI Device Configuration Spaces */ + union { + uint8_t c[0x1000 / 1]; /* 0x02{0000,,,7FFF} */ + uint16_t s[0x1000 / 2]; /* 0x02{0000,,,7FFF} */ + uint32_t l[0x1000 / 4]; /* 0x02{0000,,,7FFF} */ + uint64_t d[0x1000 / 8]; /* 0x02{0000,,,7FFF} */ + union { + uint8_t c[0x100 / 1]; + uint16_t s[0x100 / 2]; + uint32_t l[0x100 / 4]; + uint64_t d[0x100 / 8]; + } f[8]; + } cp_type0_cfg_dev[7]; /* 0x02{1000,,,7FFF} */ + + /* 0x028000-0x028FFF -- PCI Type 1 Configuration Space */ + union { + uint8_t c[0x1000 / 1]; /* 0x028000-0x029000 */ + uint16_t s[0x1000 / 2]; /* 0x028000-0x029000 */ + uint32_t l[0x1000 / 4]; /* 0x028000-0x029000 */ + uint64_t d[0x1000 / 8]; /* 0x028000-0x029000 */ + union { + uint8_t c[0x100 / 1]; + uint16_t s[0x100 / 2]; + uint32_t l[0x100 / 4]; + uint64_t d[0x100 / 8]; + } f[8]; + } cp_type1_cfg; /* 0x028000-0x029000 */ + + char _pad_029000[0x030000-0x029000]; + + /* 0x030000-0x030007 -- PCI Interrupt Acknowledge Cycle */ + union { + uint8_t c[8 / 1]; + uint16_t s[8 / 2]; + uint32_t l[8 / 4]; + uint64_t d[8 / 8]; + } cp_pci_iack; /* 0x030000-0x030007 */ + + char _pad_030007[0x040000-0x030008]; + + /* 0x040000-0x040007 -- PCIX Special Cycle */ + union { + uint8_t c[8 / 1]; + uint16_t s[8 / 2]; + uint32_t l[8 / 4]; + uint64_t d[8 / 8]; + } cp_pcix_cycle; /* 0x040000-0x040007 */ + + char _pad_040007[0x200000-0x040008]; + + /* 0x200000-0x7FFFFF -- PCI/GIO Device Spaces */ + union { + uint8_t c[0x100000 / 1]; + uint16_t s[0x100000 / 2]; + uint32_t l[0x100000 / 4]; + uint64_t d[0x100000 / 8]; + } cp_devio_raw[6]; /* 0x200000-0x7FFFFF */ + + #define cp_devio(n) cp_devio_raw[((n)<2)?(n*2):(n+2)] + + char _pad_800000[0xA00000-0x800000]; + + /* 0xA00000-0xBFFFFF -- PCI/GIO Device Spaces w/flush */ + union { + uint8_t c[0x100000 / 1]; + uint16_t s[0x100000 / 2]; + uint32_t l[0x100000 / 4]; + uint64_t d[0x100000 / 8]; + } cp_devio_raw_flush[6]; /* 0xA00000-0xBFFFFF */ + + #define cp_devio_flush(n) cp_devio_raw_flush[((n)<2)?(n*2):(n+2)] + +}; + +#endif /* _ASM_IA64_SN_PCI_TIOCP_H */ diff --git a/arch/ia64/sn/include/shub.h b/arch/ia64/sn/include/shub.h new file mode 100644 index 000000000..44e2bef89 --- /dev/null +++ b/arch/ia64/sn/include/shub.h @@ -0,0 +1,39 @@ +/* + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file "COPYING" in the main directory of this archive + * for more details. + * + * Copyright (c) 2001-2004 Silicon Graphics, Inc. All rights reserved. + */ + +#ifndef _ASM_IA64_SN_SHUB_H +#define _ASM_IA64_SN_SHUB_H + + +#define MD_MEM_BANKS 4 + + +/* + * Junk Bus Address Space + * The junk bus is used to access the PROM, LED's, and UART. It's + * accessed through the local block MMR space. The data path is + * 16 bits wide. This space requires address bits 31-27 to be set, and + * is further divided by address bits 26:15. + * The LED addresses are write-only. To read the LEDs, you need to use + * SH_JUNK_BUS_LED0-3, defined in shub_mmr.h + * + */ +#define SH_REAL_JUNK_BUS_LED0 0x7fed00000UL +#define SH_REAL_JUNK_BUS_LED1 0x7fed10000UL +#define SH_REAL_JUNK_BUS_LED2 0x7fed20000UL +#define SH_REAL_JUNK_BUS_LED3 0x7fed30000UL +#define SH_JUNK_BUS_UART0 0x7fed40000UL +#define SH_JUNK_BUS_UART1 0x7fed40008UL +#define SH_JUNK_BUS_UART2 0x7fed40010UL +#define SH_JUNK_BUS_UART3 0x7fed40018UL +#define SH_JUNK_BUS_UART4 0x7fed40020UL +#define SH_JUNK_BUS_UART5 0x7fed40028UL +#define SH_JUNK_BUS_UART6 0x7fed40030UL +#define SH_JUNK_BUS_UART7 0x7fed40038UL + +#endif /* _ASM_IA64_SN_SHUB_H */ diff --git a/arch/ia64/sn/include/shubio.h b/arch/ia64/sn/include/shubio.h new file mode 100644 index 000000000..fbd880e6b --- /dev/null +++ b/arch/ia64/sn/include/shubio.h @@ -0,0 +1,3476 @@ +/* + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file "COPYING" in the main directory of this archive + * for more details. + * + * Copyright (C) 1992 - 1997, 2000-2004 Silicon Graphics, Inc. All rights reserved. + */ + +#ifndef _ASM_IA64_SN_SHUBIO_H +#define _ASM_IA64_SN_SHUBIO_H + +#define HUB_WIDGET_ID_MAX 0xf +#define IIO_NUM_ITTES 7 +#define HUB_NUM_BIG_WINDOW (IIO_NUM_ITTES - 1) + +#define IIO_WID 0x00400000 /* Crosstalk Widget Identification */ + /* This register is also accessible from + * Crosstalk at address 0x0. */ +#define IIO_WSTAT 0x00400008 /* Crosstalk Widget Status */ +#define IIO_WCR 0x00400020 /* Crosstalk Widget Control Register */ +#define IIO_ILAPR 0x00400100 /* IO Local Access Protection Register */ +#define IIO_ILAPO 0x00400108 /* IO Local Access Protection Override */ +#define IIO_IOWA 0x00400110 /* IO Outbound Widget Access */ +#define IIO_IIWA 0x00400118 /* IO Inbound Widget Access */ +#define IIO_IIDEM 0x00400120 /* IO Inbound Device Error Mask */ +#define IIO_ILCSR 0x00400128 /* IO LLP Control and Status Register */ +#define IIO_ILLR 0x00400130 /* IO LLP Log Register */ +#define IIO_IIDSR 0x00400138 /* IO Interrupt Destination */ + +#define IIO_IGFX0 0x00400140 /* IO Graphics Node-Widget Map 0 */ +#define IIO_IGFX1 0x00400148 /* IO Graphics Node-Widget Map 1 */ + +#define IIO_ISCR0 0x00400150 /* IO Scratch Register 0 */ +#define IIO_ISCR1 0x00400158 /* IO Scratch Register 1 */ + +#define IIO_ITTE1 0x00400160 /* IO Translation Table Entry 1 */ +#define IIO_ITTE2 0x00400168 /* IO Translation Table Entry 2 */ +#define IIO_ITTE3 0x00400170 /* IO Translation Table Entry 3 */ +#define IIO_ITTE4 0x00400178 /* IO Translation Table Entry 4 */ +#define IIO_ITTE5 0x00400180 /* IO Translation Table Entry 5 */ +#define IIO_ITTE6 0x00400188 /* IO Translation Table Entry 6 */ +#define IIO_ITTE7 0x00400190 /* IO Translation Table Entry 7 */ + +#define IIO_IPRB0 0x00400198 /* IO PRB Entry 0 */ +#define IIO_IPRB8 0x004001A0 /* IO PRB Entry 8 */ +#define IIO_IPRB9 0x004001A8 /* IO PRB Entry 9 */ +#define IIO_IPRBA 0x004001B0 /* IO PRB Entry A */ +#define IIO_IPRBB 0x004001B8 /* IO PRB Entry B */ +#define IIO_IPRBC 0x004001C0 /* IO PRB Entry C */ +#define IIO_IPRBD 0x004001C8 /* IO PRB Entry D */ +#define IIO_IPRBE 0x004001D0 /* IO PRB Entry E */ +#define IIO_IPRBF 0x004001D8 /* IO PRB Entry F */ + +#define IIO_IXCC 0x004001E0 /* IO Crosstalk Credit Count Timeout */ +#define IIO_IMEM 0x004001E8 /* IO Miscellaneous Error Mask */ +#define IIO_IXTT 0x004001F0 /* IO Crosstalk Timeout Threshold */ +#define IIO_IECLR 0x004001F8 /* IO Error Clear Register */ +#define IIO_IBCR 0x00400200 /* IO BTE Control Register */ + +#define IIO_IXSM 0x00400208 /* IO Crosstalk Spurious Message */ +#define IIO_IXSS 0x00400210 /* IO Crosstalk Spurious Sideband */ + +#define IIO_ILCT 0x00400218 /* IO LLP Channel Test */ + +#define IIO_IIEPH1 0x00400220 /* IO Incoming Error Packet Header, Part 1 */ +#define IIO_IIEPH2 0x00400228 /* IO Incoming Error Packet Header, Part 2 */ + + +#define IIO_ISLAPR 0x00400230 /* IO SXB Local Access Protection Regster */ +#define IIO_ISLAPO 0x00400238 /* IO SXB Local Access Protection Override */ + +#define IIO_IWI 0x00400240 /* IO Wrapper Interrupt Register */ +#define IIO_IWEL 0x00400248 /* IO Wrapper Error Log Register */ +#define IIO_IWC 0x00400250 /* IO Wrapper Control Register */ +#define IIO_IWS 0x00400258 /* IO Wrapper Status Register */ +#define IIO_IWEIM 0x00400260 /* IO Wrapper Error Interrupt Masking Register */ + +#define IIO_IPCA 0x00400300 /* IO PRB Counter Adjust */ + +#define IIO_IPRTE0_A 0x00400308 /* IO PIO Read Address Table Entry 0, Part A */ +#define IIO_IPRTE1_A 0x00400310 /* IO PIO Read Address Table Entry 1, Part A */ +#define IIO_IPRTE2_A 0x00400318 /* IO PIO Read Address Table Entry 2, Part A */ +#define IIO_IPRTE3_A 0x00400320 /* IO PIO Read Address Table Entry 3, Part A */ +#define IIO_IPRTE4_A 0x00400328 /* IO PIO Read Address Table Entry 4, Part A */ +#define IIO_IPRTE5_A 0x00400330 /* IO PIO Read Address Table Entry 5, Part A */ +#define IIO_IPRTE6_A 0x00400338 /* IO PIO Read Address Table Entry 6, Part A */ +#define IIO_IPRTE7_A 0x00400340 /* IO PIO Read Address Table Entry 7, Part A */ + +#define IIO_IPRTE0_B 0x00400348 /* IO PIO Read Address Table Entry 0, Part B */ +#define IIO_IPRTE1_B 0x00400350 /* IO PIO Read Address Table Entry 1, Part B */ +#define IIO_IPRTE2_B 0x00400358 /* IO PIO Read Address Table Entry 2, Part B */ +#define IIO_IPRTE3_B 0x00400360 /* IO PIO Read Address Table Entry 3, Part B */ +#define IIO_IPRTE4_B 0x00400368 /* IO PIO Read Address Table Entry 4, Part B */ +#define IIO_IPRTE5_B 0x00400370 /* IO PIO Read Address Table Entry 5, Part B */ +#define IIO_IPRTE6_B 0x00400378 /* IO PIO Read Address Table Entry 6, Part B */ +#define IIO_IPRTE7_B 0x00400380 /* IO PIO Read Address Table Entry 7, Part B */ + +#define IIO_IPDR 0x00400388 /* IO PIO Deallocation Register */ +#define IIO_ICDR 0x00400390 /* IO CRB Entry Deallocation Register */ +#define IIO_IFDR 0x00400398 /* IO IOQ FIFO Depth Register */ +#define IIO_IIAP 0x004003A0 /* IO IIQ Arbitration Parameters */ +#define IIO_ICMR 0x004003A8 /* IO CRB Management Register */ +#define IIO_ICCR 0x004003B0 /* IO CRB Control Register */ +#define IIO_ICTO 0x004003B8 /* IO CRB Timeout */ +#define IIO_ICTP 0x004003C0 /* IO CRB Timeout Prescalar */ + +#define IIO_ICRB0_A 0x00400400 /* IO CRB Entry 0_A */ +#define IIO_ICRB0_B 0x00400408 /* IO CRB Entry 0_B */ +#define IIO_ICRB0_C 0x00400410 /* IO CRB Entry 0_C */ +#define IIO_ICRB0_D 0x00400418 /* IO CRB Entry 0_D */ +#define IIO_ICRB0_E 0x00400420 /* IO CRB Entry 0_E */ + +#define IIO_ICRB1_A 0x00400430 /* IO CRB Entry 1_A */ +#define IIO_ICRB1_B 0x00400438 /* IO CRB Entry 1_B */ +#define IIO_ICRB1_C 0x00400440 /* IO CRB Entry 1_C */ +#define IIO_ICRB1_D 0x00400448 /* IO CRB Entry 1_D */ +#define IIO_ICRB1_E 0x00400450 /* IO CRB Entry 1_E */ + +#define IIO_ICRB2_A 0x00400460 /* IO CRB Entry 2_A */ +#define IIO_ICRB2_B 0x00400468 /* IO CRB Entry 2_B */ +#define IIO_ICRB2_C 0x00400470 /* IO CRB Entry 2_C */ +#define IIO_ICRB2_D 0x00400478 /* IO CRB Entry 2_D */ +#define IIO_ICRB2_E 0x00400480 /* IO CRB Entry 2_E */ + +#define IIO_ICRB3_A 0x00400490 /* IO CRB Entry 3_A */ +#define IIO_ICRB3_B 0x00400498 /* IO CRB Entry 3_B */ +#define IIO_ICRB3_C 0x004004a0 /* IO CRB Entry 3_C */ +#define IIO_ICRB3_D 0x004004a8 /* IO CRB Entry 3_D */ +#define IIO_ICRB3_E 0x004004b0 /* IO CRB Entry 3_E */ + +#define IIO_ICRB4_A 0x004004c0 /* IO CRB Entry 4_A */ +#define IIO_ICRB4_B 0x004004c8 /* IO CRB Entry 4_B */ +#define IIO_ICRB4_C 0x004004d0 /* IO CRB Entry 4_C */ +#define IIO_ICRB4_D 0x004004d8 /* IO CRB Entry 4_D */ +#define IIO_ICRB4_E 0x004004e0 /* IO CRB Entry 4_E */ + +#define IIO_ICRB5_A 0x004004f0 /* IO CRB Entry 5_A */ +#define IIO_ICRB5_B 0x004004f8 /* IO CRB Entry 5_B */ +#define IIO_ICRB5_C 0x00400500 /* IO CRB Entry 5_C */ +#define IIO_ICRB5_D 0x00400508 /* IO CRB Entry 5_D */ +#define IIO_ICRB5_E 0x00400510 /* IO CRB Entry 5_E */ + +#define IIO_ICRB6_A 0x00400520 /* IO CRB Entry 6_A */ +#define IIO_ICRB6_B 0x00400528 /* IO CRB Entry 6_B */ +#define IIO_ICRB6_C 0x00400530 /* IO CRB Entry 6_C */ +#define IIO_ICRB6_D 0x00400538 /* IO CRB Entry 6_D */ +#define IIO_ICRB6_E 0x00400540 /* IO CRB Entry 6_E */ + +#define IIO_ICRB7_A 0x00400550 /* IO CRB Entry 7_A */ +#define IIO_ICRB7_B 0x00400558 /* IO CRB Entry 7_B */ +#define IIO_ICRB7_C 0x00400560 /* IO CRB Entry 7_C */ +#define IIO_ICRB7_D 0x00400568 /* IO CRB Entry 7_D */ +#define IIO_ICRB7_E 0x00400570 /* IO CRB Entry 7_E */ + +#define IIO_ICRB8_A 0x00400580 /* IO CRB Entry 8_A */ +#define IIO_ICRB8_B 0x00400588 /* IO CRB Entry 8_B */ +#define IIO_ICRB8_C 0x00400590 /* IO CRB Entry 8_C */ +#define IIO_ICRB8_D 0x00400598 /* IO CRB Entry 8_D */ +#define IIO_ICRB8_E 0x004005a0 /* IO CRB Entry 8_E */ + +#define IIO_ICRB9_A 0x004005b0 /* IO CRB Entry 9_A */ +#define IIO_ICRB9_B 0x004005b8 /* IO CRB Entry 9_B */ +#define IIO_ICRB9_C 0x004005c0 /* IO CRB Entry 9_C */ +#define IIO_ICRB9_D 0x004005c8 /* IO CRB Entry 9_D */ +#define IIO_ICRB9_E 0x004005d0 /* IO CRB Entry 9_E */ + +#define IIO_ICRBA_A 0x004005e0 /* IO CRB Entry A_A */ +#define IIO_ICRBA_B 0x004005e8 /* IO CRB Entry A_B */ +#define IIO_ICRBA_C 0x004005f0 /* IO CRB Entry A_C */ +#define IIO_ICRBA_D 0x004005f8 /* IO CRB Entry A_D */ +#define IIO_ICRBA_E 0x00400600 /* IO CRB Entry A_E */ + +#define IIO_ICRBB_A 0x00400610 /* IO CRB Entry B_A */ +#define IIO_ICRBB_B 0x00400618 /* IO CRB Entry B_B */ +#define IIO_ICRBB_C 0x00400620 /* IO CRB Entry B_C */ +#define IIO_ICRBB_D 0x00400628 /* IO CRB Entry B_D */ +#define IIO_ICRBB_E 0x00400630 /* IO CRB Entry B_E */ + +#define IIO_ICRBC_A 0x00400640 /* IO CRB Entry C_A */ +#define IIO_ICRBC_B 0x00400648 /* IO CRB Entry C_B */ +#define IIO_ICRBC_C 0x00400650 /* IO CRB Entry C_C */ +#define IIO_ICRBC_D 0x00400658 /* IO CRB Entry C_D */ +#define IIO_ICRBC_E 0x00400660 /* IO CRB Entry C_E */ + +#define IIO_ICRBD_A 0x00400670 /* IO CRB Entry D_A */ +#define IIO_ICRBD_B 0x00400678 /* IO CRB Entry D_B */ +#define IIO_ICRBD_C 0x00400680 /* IO CRB Entry D_C */ +#define IIO_ICRBD_D 0x00400688 /* IO CRB Entry D_D */ +#define IIO_ICRBD_E 0x00400690 /* IO CRB Entry D_E */ + +#define IIO_ICRBE_A 0x004006a0 /* IO CRB Entry E_A */ +#define IIO_ICRBE_B 0x004006a8 /* IO CRB Entry E_B */ +#define IIO_ICRBE_C 0x004006b0 /* IO CRB Entry E_C */ +#define IIO_ICRBE_D 0x004006b8 /* IO CRB Entry E_D */ +#define IIO_ICRBE_E 0x004006c0 /* IO CRB Entry E_E */ + +#define IIO_ICSML 0x00400700 /* IO CRB Spurious Message Low */ +#define IIO_ICSMM 0x00400708 /* IO CRB Spurious Message Middle */ +#define IIO_ICSMH 0x00400710 /* IO CRB Spurious Message High */ + +#define IIO_IDBSS 0x00400718 /* IO Debug Submenu Select */ + +#define IIO_IBLS0 0x00410000 /* IO BTE Length Status 0 */ +#define IIO_IBSA0 0x00410008 /* IO BTE Source Address 0 */ +#define IIO_IBDA0 0x00410010 /* IO BTE Destination Address 0 */ +#define IIO_IBCT0 0x00410018 /* IO BTE Control Terminate 0 */ +#define IIO_IBNA0 0x00410020 /* IO BTE Notification Address 0 */ +#define IIO_IBIA0 0x00410028 /* IO BTE Interrupt Address 0 */ +#define IIO_IBLS1 0x00420000 /* IO BTE Length Status 1 */ +#define IIO_IBSA1 0x00420008 /* IO BTE Source Address 1 */ +#define IIO_IBDA1 0x00420010 /* IO BTE Destination Address 1 */ +#define IIO_IBCT1 0x00420018 /* IO BTE Control Terminate 1 */ +#define IIO_IBNA1 0x00420020 /* IO BTE Notification Address 1 */ +#define IIO_IBIA1 0x00420028 /* IO BTE Interrupt Address 1 */ + +#define IIO_IPCR 0x00430000 /* IO Performance Control */ +#define IIO_IPPR 0x00430008 /* IO Performance Profiling */ + + +/************************************************************************ + * * + * Description: This register echoes some information from the * + * LB_REV_ID register. It is available through Crosstalk as described * + * above. The REV_NUM and MFG_NUM fields receive their values from * + * the REVISION and MANUFACTURER fields in the LB_REV_ID register. * + * The PART_NUM field's value is the Crosstalk device ID number that * + * Steve Miller assigned to the SHub chip. * + * * + ************************************************************************/ + +typedef union ii_wid_u { + uint64_t ii_wid_regval; + struct { + uint64_t w_rsvd_1 : 1; + uint64_t w_mfg_num : 11; + uint64_t w_part_num : 16; + uint64_t w_rev_num : 4; + uint64_t w_rsvd : 32; + } ii_wid_fld_s; +} ii_wid_u_t; + + +/************************************************************************ + * * + * The fields in this register are set upon detection of an error * + * and cleared by various mechanisms, as explained in the * + * description. * + * * + ************************************************************************/ + +typedef union ii_wstat_u { + uint64_t ii_wstat_regval; + struct { + uint64_t w_pending : 4; + uint64_t w_xt_crd_to : 1; + uint64_t w_xt_tail_to : 1; + uint64_t w_rsvd_3 : 3; + uint64_t w_tx_mx_rty : 1; + uint64_t w_rsvd_2 : 6; + uint64_t w_llp_tx_cnt : 8; + uint64_t w_rsvd_1 : 8; + uint64_t w_crazy : 1; + uint64_t w_rsvd : 31; + } ii_wstat_fld_s; +} ii_wstat_u_t; + + +/************************************************************************ + * * + * Description: This is a read-write enabled register. It controls * + * various aspects of the Crosstalk flow control. * + * * + ************************************************************************/ + +typedef union ii_wcr_u { + uint64_t ii_wcr_regval; + struct { + uint64_t w_wid : 4; + uint64_t w_tag : 1; + uint64_t w_rsvd_1 : 8; + uint64_t w_dst_crd : 3; + uint64_t w_f_bad_pkt : 1; + uint64_t w_dir_con : 1; + uint64_t w_e_thresh : 5; + uint64_t w_rsvd : 41; + } ii_wcr_fld_s; +} ii_wcr_u_t; + + +/************************************************************************ + * * + * Description: This register's value is a bit vector that guards * + * access to local registers within the II as well as to external * + * Crosstalk widgets. Each bit in the register corresponds to a * + * particular region in the system; a region consists of one, two or * + * four nodes (depending on the value of the REGION_SIZE field in the * + * LB_REV_ID register, which is documented in Section 8.3.1.1). The * + * protection provided by this register applies to PIO read * + * operations as well as PIO write operations. The II will perform a * + * PIO read or write request only if the bit for the requestor's * + * region is set; otherwise, the II will not perform the requested * + * operation and will return an error response. When a PIO read or * + * write request targets an external Crosstalk widget, then not only * + * must the bit for the requestor's region be set in the ILAPR, but * + * also the target widget's bit in the IOWA register must be set in * + * order for the II to perform the requested operation; otherwise, * + * the II will return an error response. Hence, the protection * + * provided by the IOWA register supplements the protection provided * + * by the ILAPR for requests that target external Crosstalk widgets. * + * This register itself can be accessed only by the nodes whose * + * region ID bits are enabled in this same register. It can also be * + * accessed through the IAlias space by the local processors. * + * The reset value of this register allows access by all nodes. * + * * + ************************************************************************/ + +typedef union ii_ilapr_u { + uint64_t ii_ilapr_regval; + struct { + uint64_t i_region : 64; + } ii_ilapr_fld_s; +} ii_ilapr_u_t; + + + + +/************************************************************************ + * * + * Description: A write to this register of the 64-bit value * + * "SGIrules" in ASCII, will cause the bit in the ILAPR register * + * corresponding to the region of the requestor to be set (allow * + * access). A write of any other value will be ignored. Access * + * protection for this register is "SGIrules". * + * This register can also be accessed through the IAlias space. * + * However, this access will not change the access permissions in the * + * ILAPR. * + * * + ************************************************************************/ + +typedef union ii_ilapo_u { + uint64_t ii_ilapo_regval; + struct { + uint64_t i_io_ovrride : 64; + } ii_ilapo_fld_s; +} ii_ilapo_u_t; + + + +/************************************************************************ + * * + * This register qualifies all the PIO and Graphics writes launched * + * from the SHUB towards a widget. * + * * + ************************************************************************/ + +typedef union ii_iowa_u { + uint64_t ii_iowa_regval; + struct { + uint64_t i_w0_oac : 1; + uint64_t i_rsvd_1 : 7; + uint64_t i_wx_oac : 8; + uint64_t i_rsvd : 48; + } ii_iowa_fld_s; +} ii_iowa_u_t; + + +/************************************************************************ + * * + * Description: This register qualifies all the requests launched * + * from a widget towards the Shub. This register is intended to be * + * used by software in case of misbehaving widgets. * + * * + * * + ************************************************************************/ + +typedef union ii_iiwa_u { + uint64_t ii_iiwa_regval; + struct { + uint64_t i_w0_iac : 1; + uint64_t i_rsvd_1 : 7; + uint64_t i_wx_iac : 8; + uint64_t i_rsvd : 48; + } ii_iiwa_fld_s; +} ii_iiwa_u_t; + + + +/************************************************************************ + * * + * Description: This register qualifies all the operations launched * + * from a widget towards the SHub. It allows individual access * + * control for up to 8 devices per widget. A device refers to * + * individual DMA master hosted by a widget. * + * The bits in each field of this register are cleared by the Shub * + * upon detection of an error which requires the device to be * + * disabled. These fields assume that 0=TNUM=7 (i.e., Bridge-centric * + * Crosstalk). Whether or not a device has access rights to this * + * Shub is determined by an AND of the device enable bit in the * + * appropriate field of this register and the corresponding bit in * + * the Wx_IAC field (for the widget which this device belongs to). * + * The bits in this field are set by writing a 1 to them. Incoming * + * replies from Crosstalk are not subject to this access control * + * mechanism. * + * * + ************************************************************************/ + +typedef union ii_iidem_u { + uint64_t ii_iidem_regval; + struct { + uint64_t i_w8_dxs : 8; + uint64_t i_w9_dxs : 8; + uint64_t i_wa_dxs : 8; + uint64_t i_wb_dxs : 8; + uint64_t i_wc_dxs : 8; + uint64_t i_wd_dxs : 8; + uint64_t i_we_dxs : 8; + uint64_t i_wf_dxs : 8; + } ii_iidem_fld_s; +} ii_iidem_u_t; + + +/************************************************************************ + * * + * This register contains the various programmable fields necessary * + * for controlling and observing the LLP signals. * + * * + ************************************************************************/ + +typedef union ii_ilcsr_u { + uint64_t ii_ilcsr_regval; + struct { + uint64_t i_nullto : 6; + uint64_t i_rsvd_4 : 2; + uint64_t i_wrmrst : 1; + uint64_t i_rsvd_3 : 1; + uint64_t i_llp_en : 1; + uint64_t i_bm8 : 1; + uint64_t i_llp_stat : 2; + uint64_t i_remote_power : 1; + uint64_t i_rsvd_2 : 1; + uint64_t i_maxrtry : 10; + uint64_t i_d_avail_sel : 2; + uint64_t i_rsvd_1 : 4; + uint64_t i_maxbrst : 10; + uint64_t i_rsvd : 22; + + } ii_ilcsr_fld_s; +} ii_ilcsr_u_t; + + +/************************************************************************ + * * + * This is simply a status registers that monitors the LLP error * + * rate. * + * * + ************************************************************************/ + +typedef union ii_illr_u { + uint64_t ii_illr_regval; + struct { + uint64_t i_sn_cnt : 16; + uint64_t i_cb_cnt : 16; + uint64_t i_rsvd : 32; + } ii_illr_fld_s; +} ii_illr_u_t; + + +/************************************************************************ + * * + * Description: All II-detected non-BTE error interrupts are * + * specified via this register. * + * NOTE: The PI interrupt register address is hardcoded in the II. If * + * PI_ID==0, then the II sends an interrupt request (Duplonet PWRI * + * packet) to address offset 0x0180_0090 within the local register * + * address space of PI0 on the node specified by the NODE field. If * + * PI_ID==1, then the II sends the interrupt request to address * + * offset 0x01A0_0090 within the local register address space of PI1 * + * on the node specified by the NODE field. * + * * + ************************************************************************/ + +typedef union ii_iidsr_u { + uint64_t ii_iidsr_regval; + struct { + uint64_t i_level : 8; + uint64_t i_pi_id : 1; + uint64_t i_node : 11; + uint64_t i_rsvd_3 : 4; + uint64_t i_enable : 1; + uint64_t i_rsvd_2 : 3; + uint64_t i_int_sent : 2; + uint64_t i_rsvd_1 : 2; + uint64_t i_pi0_forward_int : 1; + uint64_t i_pi1_forward_int : 1; + uint64_t i_rsvd : 30; + } ii_iidsr_fld_s; +} ii_iidsr_u_t; + + + +/************************************************************************ + * * + * There are two instances of this register. This register is used * + * for matching up the incoming responses from the graphics widget to * + * the processor that initiated the graphics operation. The * + * write-responses are converted to graphics credits and returned to * + * the processor so that the processor interface can manage the flow * + * control. * + * * + ************************************************************************/ + +typedef union ii_igfx0_u { + uint64_t ii_igfx0_regval; + struct { + uint64_t i_w_num : 4; + uint64_t i_pi_id : 1; + uint64_t i_n_num : 12; + uint64_t i_p_num : 1; + uint64_t i_rsvd : 46; + } ii_igfx0_fld_s; +} ii_igfx0_u_t; + + +/************************************************************************ + * * + * There are two instances of this register. This register is used * + * for matching up the incoming responses from the graphics widget to * + * the processor that initiated the graphics operation. The * + * write-responses are converted to graphics credits and returned to * + * the processor so that the processor interface can manage the flow * + * control. * + * * + ************************************************************************/ + +typedef union ii_igfx1_u { + uint64_t ii_igfx1_regval; + struct { + uint64_t i_w_num : 4; + uint64_t i_pi_id : 1; + uint64_t i_n_num : 12; + uint64_t i_p_num : 1; + uint64_t i_rsvd : 46; + } ii_igfx1_fld_s; +} ii_igfx1_u_t; + + +/************************************************************************ + * * + * There are two instances of this registers. These registers are * + * used as scratch registers for software use. * + * * + ************************************************************************/ + +typedef union ii_iscr0_u { + uint64_t ii_iscr0_regval; + struct { + uint64_t i_scratch : 64; + } ii_iscr0_fld_s; +} ii_iscr0_u_t; + + + +/************************************************************************ + * * + * There are two instances of this registers. These registers are * + * used as scratch registers for software use. * + * * + ************************************************************************/ + +typedef union ii_iscr1_u { + uint64_t ii_iscr1_regval; + struct { + uint64_t i_scratch : 64; + } ii_iscr1_fld_s; +} ii_iscr1_u_t; + + +/************************************************************************ + * * + * Description: There are seven instances of translation table entry * + * registers. Each register maps a Shub Big Window to a 48-bit * + * address on Crosstalk. * + * For M-mode (128 nodes, 8 GBytes/node), SysAD[31:29] (Big Window * + * number) are used to select one of these 7 registers. The Widget * + * number field is then derived from the W_NUM field for synthesizing * + * a Crosstalk packet. The 5 bits of OFFSET are concatenated with * + * SysAD[28:0] to form Crosstalk[33:0]. The upper Crosstalk[47:34] * + * are padded with zeros. Although the maximum Crosstalk space * + * addressable by the SHub is thus the lower 16 GBytes per widget * + * (M-mode), however only 7/32nds of this * + * space can be accessed. * + * For the N-mode (256 nodes, 4 GBytes/node), SysAD[30:28] (Big * + * Window number) are used to select one of these 7 registers. The * + * Widget number field is then derived from the W_NUM field for * + * synthesizing a Crosstalk packet. The 5 bits of OFFSET are * + * concatenated with SysAD[27:0] to form Crosstalk[33:0]. The IOSP * + * field is used as Crosstalk[47], and remainder of the Crosstalk * + * address bits (Crosstalk[46:34]) are always zero. While the maximum * + * Crosstalk space addressable by the Shub is thus the lower * + * 8-GBytes per widget (N-mode), only 7/32nds * + * of this space can be accessed. * + * * + ************************************************************************/ + +typedef union ii_itte1_u { + uint64_t ii_itte1_regval; + struct { + uint64_t i_offset : 5; + uint64_t i_rsvd_1 : 3; + uint64_t i_w_num : 4; + uint64_t i_iosp : 1; + uint64_t i_rsvd : 51; + } ii_itte1_fld_s; +} ii_itte1_u_t; + + +/************************************************************************ + * * + * Description: There are seven instances of translation table entry * + * registers. Each register maps a Shub Big Window to a 48-bit * + * address on Crosstalk. * + * For M-mode (128 nodes, 8 GBytes/node), SysAD[31:29] (Big Window * + * number) are used to select one of these 7 registers. The Widget * + * number field is then derived from the W_NUM field for synthesizing * + * a Crosstalk packet. The 5 bits of OFFSET are concatenated with * + * SysAD[28:0] to form Crosstalk[33:0]. The upper Crosstalk[47:34] * + * are padded with zeros. Although the maximum Crosstalk space * + * addressable by the Shub is thus the lower 16 GBytes per widget * + * (M-mode), however only 7/32nds of this * + * space can be accessed. * + * For the N-mode (256 nodes, 4 GBytes/node), SysAD[30:28] (Big * + * Window number) are used to select one of these 7 registers. The * + * Widget number field is then derived from the W_NUM field for * + * synthesizing a Crosstalk packet. The 5 bits of OFFSET are * + * concatenated with SysAD[27:0] to form Crosstalk[33:0]. The IOSP * + * field is used as Crosstalk[47], and remainder of the Crosstalk * + * address bits (Crosstalk[46:34]) are always zero. While the maximum * + * Crosstalk space addressable by the Shub is thus the lower * + * 8-GBytes per widget (N-mode), only 7/32nds * + * of this space can be accessed. * + * * + ************************************************************************/ + +typedef union ii_itte2_u { + uint64_t ii_itte2_regval; + struct { + uint64_t i_offset : 5; + uint64_t i_rsvd_1 : 3; + uint64_t i_w_num : 4; + uint64_t i_iosp : 1; + uint64_t i_rsvd : 51; + } ii_itte2_fld_s; +} ii_itte2_u_t; + + +/************************************************************************ + * * + * Description: There are seven instances of translation table entry * + * registers. Each register maps a Shub Big Window to a 48-bit * + * address on Crosstalk. * + * For M-mode (128 nodes, 8 GBytes/node), SysAD[31:29] (Big Window * + * number) are used to select one of these 7 registers. The Widget * + * number field is then derived from the W_NUM field for synthesizing * + * a Crosstalk packet. The 5 bits of OFFSET are concatenated with * + * SysAD[28:0] to form Crosstalk[33:0]. The upper Crosstalk[47:34] * + * are padded with zeros. Although the maximum Crosstalk space * + * addressable by the Shub is thus the lower 16 GBytes per widget * + * (M-mode), however only 7/32nds of this * + * space can be accessed. * + * For the N-mode (256 nodes, 4 GBytes/node), SysAD[30:28] (Big * + * Window number) are used to select one of these 7 registers. The * + * Widget number field is then derived from the W_NUM field for * + * synthesizing a Crosstalk packet. The 5 bits of OFFSET are * + * concatenated with SysAD[27:0] to form Crosstalk[33:0]. The IOSP * + * field is used as Crosstalk[47], and remainder of the Crosstalk * + * address bits (Crosstalk[46:34]) are always zero. While the maximum * + * Crosstalk space addressable by the SHub is thus the lower * + * 8-GBytes per widget (N-mode), only 7/32nds * + * of this space can be accessed. * + * * + ************************************************************************/ + +typedef union ii_itte3_u { + uint64_t ii_itte3_regval; + struct { + uint64_t i_offset : 5; + uint64_t i_rsvd_1 : 3; + uint64_t i_w_num : 4; + uint64_t i_iosp : 1; + uint64_t i_rsvd : 51; + } ii_itte3_fld_s; +} ii_itte3_u_t; + + +/************************************************************************ + * * + * Description: There are seven instances of translation table entry * + * registers. Each register maps a SHub Big Window to a 48-bit * + * address on Crosstalk. * + * For M-mode (128 nodes, 8 GBytes/node), SysAD[31:29] (Big Window * + * number) are used to select one of these 7 registers. The Widget * + * number field is then derived from the W_NUM field for synthesizing * + * a Crosstalk packet. The 5 bits of OFFSET are concatenated with * + * SysAD[28:0] to form Crosstalk[33:0]. The upper Crosstalk[47:34] * + * are padded with zeros. Although the maximum Crosstalk space * + * addressable by the SHub is thus the lower 16 GBytes per widget * + * (M-mode), however only 7/32nds of this * + * space can be accessed. * + * For the N-mode (256 nodes, 4 GBytes/node), SysAD[30:28] (Big * + * Window number) are used to select one of these 7 registers. The * + * Widget number field is then derived from the W_NUM field for * + * synthesizing a Crosstalk packet. The 5 bits of OFFSET are * + * concatenated with SysAD[27:0] to form Crosstalk[33:0]. The IOSP * + * field is used as Crosstalk[47], and remainder of the Crosstalk * + * address bits (Crosstalk[46:34]) are always zero. While the maximum * + * Crosstalk space addressable by the SHub is thus the lower * + * 8-GBytes per widget (N-mode), only 7/32nds * + * of this space can be accessed. * + * * + ************************************************************************/ + +typedef union ii_itte4_u { + uint64_t ii_itte4_regval; + struct { + uint64_t i_offset : 5; + uint64_t i_rsvd_1 : 3; + uint64_t i_w_num : 4; + uint64_t i_iosp : 1; + uint64_t i_rsvd : 51; + } ii_itte4_fld_s; +} ii_itte4_u_t; + + +/************************************************************************ + * * + * Description: There are seven instances of translation table entry * + * registers. Each register maps a SHub Big Window to a 48-bit * + * address on Crosstalk. * + * For M-mode (128 nodes, 8 GBytes/node), SysAD[31:29] (Big Window * + * number) are used to select one of these 7 registers. The Widget * + * number field is then derived from the W_NUM field for synthesizing * + * a Crosstalk packet. The 5 bits of OFFSET are concatenated with * + * SysAD[28:0] to form Crosstalk[33:0]. The upper Crosstalk[47:34] * + * are padded with zeros. Although the maximum Crosstalk space * + * addressable by the Shub is thus the lower 16 GBytes per widget * + * (M-mode), however only 7/32nds of this * + * space can be accessed. * + * For the N-mode (256 nodes, 4 GBytes/node), SysAD[30:28] (Big * + * Window number) are used to select one of these 7 registers. The * + * Widget number field is then derived from the W_NUM field for * + * synthesizing a Crosstalk packet. The 5 bits of OFFSET are * + * concatenated with SysAD[27:0] to form Crosstalk[33:0]. The IOSP * + * field is used as Crosstalk[47], and remainder of the Crosstalk * + * address bits (Crosstalk[46:34]) are always zero. While the maximum * + * Crosstalk space addressable by the Shub is thus the lower * + * 8-GBytes per widget (N-mode), only 7/32nds * + * of this space can be accessed. * + * * + ************************************************************************/ + +typedef union ii_itte5_u { + uint64_t ii_itte5_regval; + struct { + uint64_t i_offset : 5; + uint64_t i_rsvd_1 : 3; + uint64_t i_w_num : 4; + uint64_t i_iosp : 1; + uint64_t i_rsvd : 51; + } ii_itte5_fld_s; +} ii_itte5_u_t; + + +/************************************************************************ + * * + * Description: There are seven instances of translation table entry * + * registers. Each register maps a Shub Big Window to a 48-bit * + * address on Crosstalk. * + * For M-mode (128 nodes, 8 GBytes/node), SysAD[31:29] (Big Window * + * number) are used to select one of these 7 registers. The Widget * + * number field is then derived from the W_NUM field for synthesizing * + * a Crosstalk packet. The 5 bits of OFFSET are concatenated with * + * SysAD[28:0] to form Crosstalk[33:0]. The upper Crosstalk[47:34] * + * are padded with zeros. Although the maximum Crosstalk space * + * addressable by the Shub is thus the lower 16 GBytes per widget * + * (M-mode), however only 7/32nds of this * + * space can be accessed. * + * For the N-mode (256 nodes, 4 GBytes/node), SysAD[30:28] (Big * + * Window number) are used to select one of these 7 registers. The * + * Widget number field is then derived from the W_NUM field for * + * synthesizing a Crosstalk packet. The 5 bits of OFFSET are * + * concatenated with SysAD[27:0] to form Crosstalk[33:0]. The IOSP * + * field is used as Crosstalk[47], and remainder of the Crosstalk * + * address bits (Crosstalk[46:34]) are always zero. While the maximum * + * Crosstalk space addressable by the Shub is thus the lower * + * 8-GBytes per widget (N-mode), only 7/32nds * + * of this space can be accessed. * + * * + ************************************************************************/ + +typedef union ii_itte6_u { + uint64_t ii_itte6_regval; + struct { + uint64_t i_offset : 5; + uint64_t i_rsvd_1 : 3; + uint64_t i_w_num : 4; + uint64_t i_iosp : 1; + uint64_t i_rsvd : 51; + } ii_itte6_fld_s; +} ii_itte6_u_t; + + +/************************************************************************ + * * + * Description: There are seven instances of translation table entry * + * registers. Each register maps a Shub Big Window to a 48-bit * + * address on Crosstalk. * + * For M-mode (128 nodes, 8 GBytes/node), SysAD[31:29] (Big Window * + * number) are used to select one of these 7 registers. The Widget * + * number field is then derived from the W_NUM field for synthesizing * + * a Crosstalk packet. The 5 bits of OFFSET are concatenated with * + * SysAD[28:0] to form Crosstalk[33:0]. The upper Crosstalk[47:34] * + * are padded with zeros. Although the maximum Crosstalk space * + * addressable by the Shub is thus the lower 16 GBytes per widget * + * (M-mode), however only 7/32nds of this * + * space can be accessed. * + * For the N-mode (256 nodes, 4 GBytes/node), SysAD[30:28] (Big * + * Window number) are used to select one of these 7 registers. The * + * Widget number field is then derived from the W_NUM field for * + * synthesizing a Crosstalk packet. The 5 bits of OFFSET are * + * concatenated with SysAD[27:0] to form Crosstalk[33:0]. The IOSP * + * field is used as Crosstalk[47], and remainder of the Crosstalk * + * address bits (Crosstalk[46:34]) are always zero. While the maximum * + * Crosstalk space addressable by the SHub is thus the lower * + * 8-GBytes per widget (N-mode), only 7/32nds * + * of this space can be accessed. * + * * + ************************************************************************/ + +typedef union ii_itte7_u { + uint64_t ii_itte7_regval; + struct { + uint64_t i_offset : 5; + uint64_t i_rsvd_1 : 3; + uint64_t i_w_num : 4; + uint64_t i_iosp : 1; + uint64_t i_rsvd : 51; + } ii_itte7_fld_s; +} ii_itte7_u_t; + + +/************************************************************************ + * * + * Description: There are 9 instances of this register, one per * + * actual widget in this implementation of SHub and Crossbow. * + * Note: Crossbow only has ports for Widgets 8 through F, widget 0 * + * refers to Crossbow's internal space. * + * This register contains the state elements per widget that are * + * necessary to manage the PIO flow control on Crosstalk and on the * + * Router Network. See the PIO Flow Control chapter for a complete * + * description of this register * + * The SPUR_WR bit requires some explanation. When this register is * + * written, the new value of the C field is captured in an internal * + * register so the hardware can remember what the programmer wrote * + * into the credit counter. The SPUR_WR bit sets whenever the C field * + * increments above this stored value, which indicates that there * + * have been more responses received than requests sent. The SPUR_WR * + * bit cannot be cleared until a value is written to the IPRBx * + * register; the write will correct the C field and capture its new * + * value in the internal register. Even if IECLR[E_PRB_x] is set, the * + * SPUR_WR bit will persist if IPRBx hasn't yet been written. * + * . * + * * + ************************************************************************/ + +typedef union ii_iprb0_u { + uint64_t ii_iprb0_regval; + struct { + uint64_t i_c : 8; + uint64_t i_na : 14; + uint64_t i_rsvd_2 : 2; + uint64_t i_nb : 14; + uint64_t i_rsvd_1 : 2; + uint64_t i_m : 2; + uint64_t i_f : 1; + uint64_t i_of_cnt : 5; + uint64_t i_error : 1; + uint64_t i_rd_to : 1; + uint64_t i_spur_wr : 1; + uint64_t i_spur_rd : 1; + uint64_t i_rsvd : 11; + uint64_t i_mult_err : 1; + } ii_iprb0_fld_s; +} ii_iprb0_u_t; + + +/************************************************************************ + * * + * Description: There are 9 instances of this register, one per * + * actual widget in this implementation of SHub and Crossbow. * + * Note: Crossbow only has ports for Widgets 8 through F, widget 0 * + * refers to Crossbow's internal space. * + * This register contains the state elements per widget that are * + * necessary to manage the PIO flow control on Crosstalk and on the * + * Router Network. See the PIO Flow Control chapter for a complete * + * description of this register * + * The SPUR_WR bit requires some explanation. When this register is * + * written, the new value of the C field is captured in an internal * + * register so the hardware can remember what the programmer wrote * + * into the credit counter. The SPUR_WR bit sets whenever the C field * + * increments above this stored value, which indicates that there * + * have been more responses received than requests sent. The SPUR_WR * + * bit cannot be cleared until a value is written to the IPRBx * + * register; the write will correct the C field and capture its new * + * value in the internal register. Even if IECLR[E_PRB_x] is set, the * + * SPUR_WR bit will persist if IPRBx hasn't yet been written. * + * . * + * * + ************************************************************************/ + +typedef union ii_iprb8_u { + uint64_t ii_iprb8_regval; + struct { + uint64_t i_c : 8; + uint64_t i_na : 14; + uint64_t i_rsvd_2 : 2; + uint64_t i_nb : 14; + uint64_t i_rsvd_1 : 2; + uint64_t i_m : 2; + uint64_t i_f : 1; + uint64_t i_of_cnt : 5; + uint64_t i_error : 1; + uint64_t i_rd_to : 1; + uint64_t i_spur_wr : 1; + uint64_t i_spur_rd : 1; + uint64_t i_rsvd : 11; + uint64_t i_mult_err : 1; + } ii_iprb8_fld_s; +} ii_iprb8_u_t; + + +/************************************************************************ + * * + * Description: There are 9 instances of this register, one per * + * actual widget in this implementation of SHub and Crossbow. * + * Note: Crossbow only has ports for Widgets 8 through F, widget 0 * + * refers to Crossbow's internal space. * + * This register contains the state elements per widget that are * + * necessary to manage the PIO flow control on Crosstalk and on the * + * Router Network. See the PIO Flow Control chapter for a complete * + * description of this register * + * The SPUR_WR bit requires some explanation. When this register is * + * written, the new value of the C field is captured in an internal * + * register so the hardware can remember what the programmer wrote * + * into the credit counter. The SPUR_WR bit sets whenever the C field * + * increments above this stored value, which indicates that there * + * have been more responses received than requests sent. The SPUR_WR * + * bit cannot be cleared until a value is written to the IPRBx * + * register; the write will correct the C field and capture its new * + * value in the internal register. Even if IECLR[E_PRB_x] is set, the * + * SPUR_WR bit will persist if IPRBx hasn't yet been written. * + * . * + * * + ************************************************************************/ + +typedef union ii_iprb9_u { + uint64_t ii_iprb9_regval; + struct { + uint64_t i_c : 8; + uint64_t i_na : 14; + uint64_t i_rsvd_2 : 2; + uint64_t i_nb : 14; + uint64_t i_rsvd_1 : 2; + uint64_t i_m : 2; + uint64_t i_f : 1; + uint64_t i_of_cnt : 5; + uint64_t i_error : 1; + uint64_t i_rd_to : 1; + uint64_t i_spur_wr : 1; + uint64_t i_spur_rd : 1; + uint64_t i_rsvd : 11; + uint64_t i_mult_err : 1; + } ii_iprb9_fld_s; +} ii_iprb9_u_t; + + +/************************************************************************ + * * + * Description: There are 9 instances of this register, one per * + * actual widget in this implementation of SHub and Crossbow. * + * Note: Crossbow only has ports for Widgets 8 through F, widget 0 * + * refers to Crossbow's internal space. * + * This register contains the state elements per widget that are * + * necessary to manage the PIO flow control on Crosstalk and on the * + * Router Network. See the PIO Flow Control chapter for a complete * + * description of this register * + * The SPUR_WR bit requires some explanation. When this register is * + * written, the new value of the C field is captured in an internal * + * register so the hardware can remember what the programmer wrote * + * into the credit counter. The SPUR_WR bit sets whenever the C field * + * increments above this stored value, which indicates that there * + * have been more responses received than requests sent. The SPUR_WR * + * bit cannot be cleared until a value is written to the IPRBx * + * register; the write will correct the C field and capture its new * + * value in the internal register. Even if IECLR[E_PRB_x] is set, the * + * SPUR_WR bit will persist if IPRBx hasn't yet been written. * + * * + * * + ************************************************************************/ + +typedef union ii_iprba_u { + uint64_t ii_iprba_regval; + struct { + uint64_t i_c : 8; + uint64_t i_na : 14; + uint64_t i_rsvd_2 : 2; + uint64_t i_nb : 14; + uint64_t i_rsvd_1 : 2; + uint64_t i_m : 2; + uint64_t i_f : 1; + uint64_t i_of_cnt : 5; + uint64_t i_error : 1; + uint64_t i_rd_to : 1; + uint64_t i_spur_wr : 1; + uint64_t i_spur_rd : 1; + uint64_t i_rsvd : 11; + uint64_t i_mult_err : 1; + } ii_iprba_fld_s; +} ii_iprba_u_t; + + +/************************************************************************ + * * + * Description: There are 9 instances of this register, one per * + * actual widget in this implementation of SHub and Crossbow. * + * Note: Crossbow only has ports for Widgets 8 through F, widget 0 * + * refers to Crossbow's internal space. * + * This register contains the state elements per widget that are * + * necessary to manage the PIO flow control on Crosstalk and on the * + * Router Network. See the PIO Flow Control chapter for a complete * + * description of this register * + * The SPUR_WR bit requires some explanation. When this register is * + * written, the new value of the C field is captured in an internal * + * register so the hardware can remember what the programmer wrote * + * into the credit counter. The SPUR_WR bit sets whenever the C field * + * increments above this stored value, which indicates that there * + * have been more responses received than requests sent. The SPUR_WR * + * bit cannot be cleared until a value is written to the IPRBx * + * register; the write will correct the C field and capture its new * + * value in the internal register. Even if IECLR[E_PRB_x] is set, the * + * SPUR_WR bit will persist if IPRBx hasn't yet been written. * + * . * + * * + ************************************************************************/ + +typedef union ii_iprbb_u { + uint64_t ii_iprbb_regval; + struct { + uint64_t i_c : 8; + uint64_t i_na : 14; + uint64_t i_rsvd_2 : 2; + uint64_t i_nb : 14; + uint64_t i_rsvd_1 : 2; + uint64_t i_m : 2; + uint64_t i_f : 1; + uint64_t i_of_cnt : 5; + uint64_t i_error : 1; + uint64_t i_rd_to : 1; + uint64_t i_spur_wr : 1; + uint64_t i_spur_rd : 1; + uint64_t i_rsvd : 11; + uint64_t i_mult_err : 1; + } ii_iprbb_fld_s; +} ii_iprbb_u_t; + + +/************************************************************************ + * * + * Description: There are 9 instances of this register, one per * + * actual widget in this implementation of SHub and Crossbow. * + * Note: Crossbow only has ports for Widgets 8 through F, widget 0 * + * refers to Crossbow's internal space. * + * This register contains the state elements per widget that are * + * necessary to manage the PIO flow control on Crosstalk and on the * + * Router Network. See the PIO Flow Control chapter for a complete * + * description of this register * + * The SPUR_WR bit requires some explanation. When this register is * + * written, the new value of the C field is captured in an internal * + * register so the hardware can remember what the programmer wrote * + * into the credit counter. The SPUR_WR bit sets whenever the C field * + * increments above this stored value, which indicates that there * + * have been more responses received than requests sent. The SPUR_WR * + * bit cannot be cleared until a value is written to the IPRBx * + * register; the write will correct the C field and capture its new * + * value in the internal register. Even if IECLR[E_PRB_x] is set, the * + * SPUR_WR bit will persist if IPRBx hasn't yet been written. * + * . * + * * + ************************************************************************/ + +typedef union ii_iprbc_u { + uint64_t ii_iprbc_regval; + struct { + uint64_t i_c : 8; + uint64_t i_na : 14; + uint64_t i_rsvd_2 : 2; + uint64_t i_nb : 14; + uint64_t i_rsvd_1 : 2; + uint64_t i_m : 2; + uint64_t i_f : 1; + uint64_t i_of_cnt : 5; + uint64_t i_error : 1; + uint64_t i_rd_to : 1; + uint64_t i_spur_wr : 1; + uint64_t i_spur_rd : 1; + uint64_t i_rsvd : 11; + uint64_t i_mult_err : 1; + } ii_iprbc_fld_s; +} ii_iprbc_u_t; + + +/************************************************************************ + * * + * Description: There are 9 instances of this register, one per * + * actual widget in this implementation of SHub and Crossbow. * + * Note: Crossbow only has ports for Widgets 8 through F, widget 0 * + * refers to Crossbow's internal space. * + * This register contains the state elements per widget that are * + * necessary to manage the PIO flow control on Crosstalk and on the * + * Router Network. See the PIO Flow Control chapter for a complete * + * description of this register * + * The SPUR_WR bit requires some explanation. When this register is * + * written, the new value of the C field is captured in an internal * + * register so the hardware can remember what the programmer wrote * + * into the credit counter. The SPUR_WR bit sets whenever the C field * + * increments above this stored value, which indicates that there * + * have been more responses received than requests sent. The SPUR_WR * + * bit cannot be cleared until a value is written to the IPRBx * + * register; the write will correct the C field and capture its new * + * value in the internal register. Even if IECLR[E_PRB_x] is set, the * + * SPUR_WR bit will persist if IPRBx hasn't yet been written. * + * . * + * * + ************************************************************************/ + +typedef union ii_iprbd_u { + uint64_t ii_iprbd_regval; + struct { + uint64_t i_c : 8; + uint64_t i_na : 14; + uint64_t i_rsvd_2 : 2; + uint64_t i_nb : 14; + uint64_t i_rsvd_1 : 2; + uint64_t i_m : 2; + uint64_t i_f : 1; + uint64_t i_of_cnt : 5; + uint64_t i_error : 1; + uint64_t i_rd_to : 1; + uint64_t i_spur_wr : 1; + uint64_t i_spur_rd : 1; + uint64_t i_rsvd : 11; + uint64_t i_mult_err : 1; + } ii_iprbd_fld_s; +} ii_iprbd_u_t; + + +/************************************************************************ + * * + * Description: There are 9 instances of this register, one per * + * actual widget in this implementation of SHub and Crossbow. * + * Note: Crossbow only has ports for Widgets 8 through F, widget 0 * + * refers to Crossbow's internal space. * + * This register contains the state elements per widget that are * + * necessary to manage the PIO flow control on Crosstalk and on the * + * Router Network. See the PIO Flow Control chapter for a complete * + * description of this register * + * The SPUR_WR bit requires some explanation. When this register is * + * written, the new value of the C field is captured in an internal * + * register so the hardware can remember what the programmer wrote * + * into the credit counter. The SPUR_WR bit sets whenever the C field * + * increments above this stored value, which indicates that there * + * have been more responses received than requests sent. The SPUR_WR * + * bit cannot be cleared until a value is written to the IPRBx * + * register; the write will correct the C field and capture its new * + * value in the internal register. Even if IECLR[E_PRB_x] is set, the * + * SPUR_WR bit will persist if IPRBx hasn't yet been written. * + * . * + * * + ************************************************************************/ + +typedef union ii_iprbe_u { + uint64_t ii_iprbe_regval; + struct { + uint64_t i_c : 8; + uint64_t i_na : 14; + uint64_t i_rsvd_2 : 2; + uint64_t i_nb : 14; + uint64_t i_rsvd_1 : 2; + uint64_t i_m : 2; + uint64_t i_f : 1; + uint64_t i_of_cnt : 5; + uint64_t i_error : 1; + uint64_t i_rd_to : 1; + uint64_t i_spur_wr : 1; + uint64_t i_spur_rd : 1; + uint64_t i_rsvd : 11; + uint64_t i_mult_err : 1; + } ii_iprbe_fld_s; +} ii_iprbe_u_t; + + +/************************************************************************ + * * + * Description: There are 9 instances of this register, one per * + * actual widget in this implementation of Shub and Crossbow. * + * Note: Crossbow only has ports for Widgets 8 through F, widget 0 * + * refers to Crossbow's internal space. * + * This register contains the state elements per widget that are * + * necessary to manage the PIO flow control on Crosstalk and on the * + * Router Network. See the PIO Flow Control chapter for a complete * + * description of this register * + * The SPUR_WR bit requires some explanation. When this register is * + * written, the new value of the C field is captured in an internal * + * register so the hardware can remember what the programmer wrote * + * into the credit counter. The SPUR_WR bit sets whenever the C field * + * increments above this stored value, which indicates that there * + * have been more responses received than requests sent. The SPUR_WR * + * bit cannot be cleared until a value is written to the IPRBx * + * register; the write will correct the C field and capture its new * + * value in the internal register. Even if IECLR[E_PRB_x] is set, the * + * SPUR_WR bit will persist if IPRBx hasn't yet been written. * + * . * + * * + ************************************************************************/ + +typedef union ii_iprbf_u { + uint64_t ii_iprbf_regval; + struct { + uint64_t i_c : 8; + uint64_t i_na : 14; + uint64_t i_rsvd_2 : 2; + uint64_t i_nb : 14; + uint64_t i_rsvd_1 : 2; + uint64_t i_m : 2; + uint64_t i_f : 1; + uint64_t i_of_cnt : 5; + uint64_t i_error : 1; + uint64_t i_rd_to : 1; + uint64_t i_spur_wr : 1; + uint64_t i_spur_rd : 1; + uint64_t i_rsvd : 11; + uint64_t i_mult_err : 1; + } ii_iprbe_fld_s; +} ii_iprbf_u_t; + + +/************************************************************************ + * * + * This register specifies the timeout value to use for monitoring * + * Crosstalk credits which are used outbound to Crosstalk. An * + * internal counter called the Crosstalk Credit Timeout Counter * + * increments every 128 II clocks. The counter starts counting * + * anytime the credit count drops below a threshold, and resets to * + * zero (stops counting) anytime the credit count is at or above the * + * threshold. The threshold is 1 credit in direct connect mode and 2 * + * in Crossbow connect mode. When the internal Crosstalk Credit * + * Timeout Counter reaches the value programmed in this register, a * + * Crosstalk Credit Timeout has occurred. The internal counter is not * + * readable from software, and stops counting at its maximum value, * + * so it cannot cause more than one interrupt. * + * * + ************************************************************************/ + +typedef union ii_ixcc_u { + uint64_t ii_ixcc_regval; + struct { + uint64_t i_time_out : 26; + uint64_t i_rsvd : 38; + } ii_ixcc_fld_s; +} ii_ixcc_u_t; + + +/************************************************************************ + * * + * Description: This register qualifies all the PIO and DMA * + * operations launched from widget 0 towards the SHub. In * + * addition, it also qualifies accesses by the BTE streams. * + * The bits in each field of this register are cleared by the SHub * + * upon detection of an error which requires widget 0 or the BTE * + * streams to be terminated. Whether or not widget x has access * + * rights to this SHub is determined by an AND of the device * + * enable bit in the appropriate field of this register and bit 0 in * + * the Wx_IAC field. The bits in this field are set by writing a 1 to * + * them. Incoming replies from Crosstalk are not subject to this * + * access control mechanism. * + * * + ************************************************************************/ + +typedef union ii_imem_u { + uint64_t ii_imem_regval; + struct { + uint64_t i_w0_esd : 1; + uint64_t i_rsvd_3 : 3; + uint64_t i_b0_esd : 1; + uint64_t i_rsvd_2 : 3; + uint64_t i_b1_esd : 1; + uint64_t i_rsvd_1 : 3; + uint64_t i_clr_precise : 1; + uint64_t i_rsvd : 51; + } ii_imem_fld_s; +} ii_imem_u_t; + + + +/************************************************************************ + * * + * Description: This register specifies the timeout value to use for * + * monitoring Crosstalk tail flits coming into the Shub in the * + * TAIL_TO field. An internal counter associated with this register * + * is incremented every 128 II internal clocks (7 bits). The counter * + * starts counting anytime a header micropacket is received and stops * + * counting (and resets to zero) any time a micropacket with a Tail * + * bit is received. Once the counter reaches the threshold value * + * programmed in this register, it generates an interrupt to the * + * processor that is programmed into the IIDSR. The counter saturates * + * (does not roll over) at its maximum value, so it cannot cause * + * another interrupt until after it is cleared. * + * The register also contains the Read Response Timeout values. The * + * Prescalar is 23 bits, and counts II clocks. An internal counter * + * increments on every II clock and when it reaches the value in the * + * Prescalar field, all IPRTE registers with their valid bits set * + * have their Read Response timers bumped. Whenever any of them match * + * the value in the RRSP_TO field, a Read Response Timeout has * + * occurred, and error handling occurs as described in the Error * + * Handling section of this document. * + * * + ************************************************************************/ + +typedef union ii_ixtt_u { + uint64_t ii_ixtt_regval; + struct { + uint64_t i_tail_to : 26; + uint64_t i_rsvd_1 : 6; + uint64_t i_rrsp_ps : 23; + uint64_t i_rrsp_to : 5; + uint64_t i_rsvd : 4; + } ii_ixtt_fld_s; +} ii_ixtt_u_t; + + +/************************************************************************ + * * + * Writing a 1 to the fields of this register clears the appropriate * + * error bits in other areas of SHub. Note that when the * + * E_PRB_x bits are used to clear error bits in PRB registers, * + * SPUR_RD and SPUR_WR may persist, because they require additional * + * action to clear them. See the IPRBx and IXSS Register * + * specifications. * + * * + ************************************************************************/ + +typedef union ii_ieclr_u { + uint64_t ii_ieclr_regval; + struct { + uint64_t i_e_prb_0 : 1; + uint64_t i_rsvd : 7; + uint64_t i_e_prb_8 : 1; + uint64_t i_e_prb_9 : 1; + uint64_t i_e_prb_a : 1; + uint64_t i_e_prb_b : 1; + uint64_t i_e_prb_c : 1; + uint64_t i_e_prb_d : 1; + uint64_t i_e_prb_e : 1; + uint64_t i_e_prb_f : 1; + uint64_t i_e_crazy : 1; + uint64_t i_e_bte_0 : 1; + uint64_t i_e_bte_1 : 1; + uint64_t i_reserved_1 : 10; + uint64_t i_spur_rd_hdr : 1; + uint64_t i_cam_intr_to : 1; + uint64_t i_cam_overflow : 1; + uint64_t i_cam_read_miss : 1; + uint64_t i_ioq_rep_underflow : 1; + uint64_t i_ioq_req_underflow : 1; + uint64_t i_ioq_rep_overflow : 1; + uint64_t i_ioq_req_overflow : 1; + uint64_t i_iiq_rep_overflow : 1; + uint64_t i_iiq_req_overflow : 1; + uint64_t i_ii_xn_rep_cred_overflow : 1; + uint64_t i_ii_xn_req_cred_overflow : 1; + uint64_t i_ii_xn_invalid_cmd : 1; + uint64_t i_xn_ii_invalid_cmd : 1; + uint64_t i_reserved_2 : 21; + } ii_ieclr_fld_s; +} ii_ieclr_u_t; + + +/************************************************************************ + * * + * This register controls both BTEs. SOFT_RESET is intended for * + * recovery after an error. COUNT controls the total number of CRBs * + * that both BTEs (combined) can use, which affects total BTE * + * bandwidth. * + * * + ************************************************************************/ + +typedef union ii_ibcr_u { + uint64_t ii_ibcr_regval; + struct { + uint64_t i_count : 4; + uint64_t i_rsvd_1 : 4; + uint64_t i_soft_reset : 1; + uint64_t i_rsvd : 55; + } ii_ibcr_fld_s; +} ii_ibcr_u_t; + + +/************************************************************************ + * * + * This register contains the header of a spurious read response * + * received from Crosstalk. A spurious read response is defined as a * + * read response received by II from a widget for which (1) the SIDN * + * has a value between 1 and 7, inclusive (II never sends requests to * + * these widgets (2) there is no valid IPRTE register which * + * corresponds to the TNUM, or (3) the widget indicated in SIDN is * + * not the same as the widget recorded in the IPRTE register * + * referenced by the TNUM. If this condition is true, and if the * + * IXSS[VALID] bit is clear, then the header of the spurious read * + * response is capture in IXSM and IXSS, and IXSS[VALID] is set. The * + * errant header is thereby captured, and no further spurious read * + * respones are captured until IXSS[VALID] is cleared by setting the * + * appropriate bit in IECLR.Everytime a spurious read response is * + * detected, the SPUR_RD bit of the PRB corresponding to the incoming * + * message's SIDN field is set. This always happens, regarless of * + * whether a header is captured. The programmer should check * + * IXSM[SIDN] to determine which widget sent the spurious response, * + * because there may be more than one SPUR_RD bit set in the PRB * + * registers. The widget indicated by IXSM[SIDN] was the first * + * spurious read response to be received since the last time * + * IXSS[VALID] was clear. The SPUR_RD bit of the corresponding PRB * + * will be set. Any SPUR_RD bits in any other PRB registers indicate * + * spurious messages from other widets which were detected after the * + * header was captured.. * + * * + ************************************************************************/ + +typedef union ii_ixsm_u { + uint64_t ii_ixsm_regval; + struct { + uint64_t i_byte_en : 32; + uint64_t i_reserved : 1; + uint64_t i_tag : 3; + uint64_t i_alt_pactyp : 4; + uint64_t i_bo : 1; + uint64_t i_error : 1; + uint64_t i_vbpm : 1; + uint64_t i_gbr : 1; + uint64_t i_ds : 2; + uint64_t i_ct : 1; + uint64_t i_tnum : 5; + uint64_t i_pactyp : 4; + uint64_t i_sidn : 4; + uint64_t i_didn : 4; + } ii_ixsm_fld_s; +} ii_ixsm_u_t; + + +/************************************************************************ + * * + * This register contains the sideband bits of a spurious read * + * response received from Crosstalk. * + * * + ************************************************************************/ + +typedef union ii_ixss_u { + uint64_t ii_ixss_regval; + struct { + uint64_t i_sideband : 8; + uint64_t i_rsvd : 55; + uint64_t i_valid : 1; + } ii_ixss_fld_s; +} ii_ixss_u_t; + + +/************************************************************************ + * * + * This register enables software to access the II LLP's test port. * + * Refer to the LLP 2.5 documentation for an explanation of the test * + * port. Software can write to this register to program the values * + * for the control fields (TestErrCapture, TestClear, TestFlit, * + * TestMask and TestSeed). Similarly, software can read from this * + * register to obtain the values of the test port's status outputs * + * (TestCBerr, TestValid and TestData). * + * * + ************************************************************************/ + +typedef union ii_ilct_u { + uint64_t ii_ilct_regval; + struct { + uint64_t i_test_seed : 20; + uint64_t i_test_mask : 8; + uint64_t i_test_data : 20; + uint64_t i_test_valid : 1; + uint64_t i_test_cberr : 1; + uint64_t i_test_flit : 3; + uint64_t i_test_clear : 1; + uint64_t i_test_err_capture : 1; + uint64_t i_rsvd : 9; + } ii_ilct_fld_s; +} ii_ilct_u_t; + + +/************************************************************************ + * * + * If the II detects an illegal incoming Duplonet packet (request or * + * reply) when VALID==0 in the IIEPH1 register, then it saves the * + * contents of the packet's header flit in the IIEPH1 and IIEPH2 * + * registers, sets the VALID bit in IIEPH1, clears the OVERRUN bit, * + * and assigns a value to the ERR_TYPE field which indicates the * + * specific nature of the error. The II recognizes four different * + * types of errors: short request packets (ERR_TYPE==2), short reply * + * packets (ERR_TYPE==3), long request packets (ERR_TYPE==4) and long * + * reply packets (ERR_TYPE==5). The encodings for these types of * + * errors were chosen to be consistent with the same types of errors * + * indicated by the ERR_TYPE field in the LB_ERROR_HDR1 register (in * + * the LB unit). If the II detects an illegal incoming Duplonet * + * packet when VALID==1 in the IIEPH1 register, then it merely sets * + * the OVERRUN bit to indicate that a subsequent error has happened, * + * and does nothing further. * + * * + ************************************************************************/ + +typedef union ii_iieph1_u { + uint64_t ii_iieph1_regval; + struct { + uint64_t i_command : 7; + uint64_t i_rsvd_5 : 1; + uint64_t i_suppl : 14; + uint64_t i_rsvd_4 : 1; + uint64_t i_source : 14; + uint64_t i_rsvd_3 : 1; + uint64_t i_err_type : 4; + uint64_t i_rsvd_2 : 4; + uint64_t i_overrun : 1; + uint64_t i_rsvd_1 : 3; + uint64_t i_valid : 1; + uint64_t i_rsvd : 13; + } ii_iieph1_fld_s; +} ii_iieph1_u_t; + + +/************************************************************************ + * * + * This register holds the Address field from the header flit of an * + * incoming erroneous Duplonet packet, along with the tail bit which * + * accompanied this header flit. This register is essentially an * + * extension of IIEPH1. Two registers were necessary because the 64 * + * bits available in only a single register were insufficient to * + * capture the entire header flit of an erroneous packet. * + * * + ************************************************************************/ + +typedef union ii_iieph2_u { + uint64_t ii_iieph2_regval; + struct { + uint64_t i_rsvd_0 : 3; + uint64_t i_address : 47; + uint64_t i_rsvd_1 : 10; + uint64_t i_tail : 1; + uint64_t i_rsvd : 3; + } ii_iieph2_fld_s; +} ii_iieph2_u_t; + + +/******************************/ + + + +/************************************************************************ + * * + * This register's value is a bit vector that guards access from SXBs * + * to local registers within the II as well as to external Crosstalk * + * widgets * + * * + ************************************************************************/ + +typedef union ii_islapr_u { + uint64_t ii_islapr_regval; + struct { + uint64_t i_region : 64; + } ii_islapr_fld_s; +} ii_islapr_u_t; + + +/************************************************************************ + * * + * A write to this register of the 56-bit value "Pup+Bun" will cause * + * the bit in the ISLAPR register corresponding to the region of the * + * requestor to be set (access allowed). ( + * * + ************************************************************************/ + +typedef union ii_islapo_u { + uint64_t ii_islapo_regval; + struct { + uint64_t i_io_sbx_ovrride : 56; + uint64_t i_rsvd : 8; + } ii_islapo_fld_s; +} ii_islapo_u_t; + +/************************************************************************ + * * + * Determines how long the wrapper will wait aftr an interrupt is * + * initially issued from the II before it times out the outstanding * + * interrupt and drops it from the interrupt queue. * + * * + ************************************************************************/ + +typedef union ii_iwi_u { + uint64_t ii_iwi_regval; + struct { + uint64_t i_prescale : 24; + uint64_t i_rsvd : 8; + uint64_t i_timeout : 8; + uint64_t i_rsvd1 : 8; + uint64_t i_intrpt_retry_period : 8; + uint64_t i_rsvd2 : 8; + } ii_iwi_fld_s; +} ii_iwi_u_t; + +/************************************************************************ + * * + * Log errors which have occurred in the II wrapper. The errors are * + * cleared by writing to the IECLR register. * + * * + ************************************************************************/ + +typedef union ii_iwel_u { + uint64_t ii_iwel_regval; + struct { + uint64_t i_intr_timed_out : 1; + uint64_t i_rsvd : 7; + uint64_t i_cam_overflow : 1; + uint64_t i_cam_read_miss : 1; + uint64_t i_rsvd1 : 2; + uint64_t i_ioq_rep_underflow : 1; + uint64_t i_ioq_req_underflow : 1; + uint64_t i_ioq_rep_overflow : 1; + uint64_t i_ioq_req_overflow : 1; + uint64_t i_iiq_rep_overflow : 1; + uint64_t i_iiq_req_overflow : 1; + uint64_t i_rsvd2 : 6; + uint64_t i_ii_xn_rep_cred_over_under: 1; + uint64_t i_ii_xn_req_cred_over_under: 1; + uint64_t i_rsvd3 : 6; + uint64_t i_ii_xn_invalid_cmd : 1; + uint64_t i_xn_ii_invalid_cmd : 1; + uint64_t i_rsvd4 : 30; + } ii_iwel_fld_s; +} ii_iwel_u_t; + +/************************************************************************ + * * + * Controls the II wrapper. * + * * + ************************************************************************/ + +typedef union ii_iwc_u { + uint64_t ii_iwc_regval; + struct { + uint64_t i_dma_byte_swap : 1; + uint64_t i_rsvd : 3; + uint64_t i_cam_read_lines_reset : 1; + uint64_t i_rsvd1 : 3; + uint64_t i_ii_xn_cred_over_under_log: 1; + uint64_t i_rsvd2 : 19; + uint64_t i_xn_rep_iq_depth : 5; + uint64_t i_rsvd3 : 3; + uint64_t i_xn_req_iq_depth : 5; + uint64_t i_rsvd4 : 3; + uint64_t i_iiq_depth : 6; + uint64_t i_rsvd5 : 12; + uint64_t i_force_rep_cred : 1; + uint64_t i_force_req_cred : 1; + } ii_iwc_fld_s; +} ii_iwc_u_t; + +/************************************************************************ + * * + * Status in the II wrapper. * + * * + ************************************************************************/ + +typedef union ii_iws_u { + uint64_t ii_iws_regval; + struct { + uint64_t i_xn_rep_iq_credits : 5; + uint64_t i_rsvd : 3; + uint64_t i_xn_req_iq_credits : 5; + uint64_t i_rsvd1 : 51; + } ii_iws_fld_s; +} ii_iws_u_t; + +/************************************************************************ + * * + * Masks errors in the IWEL register. * + * * + ************************************************************************/ + +typedef union ii_iweim_u { + uint64_t ii_iweim_regval; + struct { + uint64_t i_intr_timed_out : 1; + uint64_t i_rsvd : 7; + uint64_t i_cam_overflow : 1; + uint64_t i_cam_read_miss : 1; + uint64_t i_rsvd1 : 2; + uint64_t i_ioq_rep_underflow : 1; + uint64_t i_ioq_req_underflow : 1; + uint64_t i_ioq_rep_overflow : 1; + uint64_t i_ioq_req_overflow : 1; + uint64_t i_iiq_rep_overflow : 1; + uint64_t i_iiq_req_overflow : 1; + uint64_t i_rsvd2 : 6; + uint64_t i_ii_xn_rep_cred_overflow : 1; + uint64_t i_ii_xn_req_cred_overflow : 1; + uint64_t i_rsvd3 : 6; + uint64_t i_ii_xn_invalid_cmd : 1; + uint64_t i_xn_ii_invalid_cmd : 1; + uint64_t i_rsvd4 : 30; + } ii_iweim_fld_s; +} ii_iweim_u_t; + + +/************************************************************************ + * * + * A write to this register causes a particular field in the * + * corresponding widget's PRB entry to be adjusted up or down by 1. * + * This counter should be used when recovering from error and reset * + * conditions. Note that software would be capable of causing * + * inadvertent overflow or underflow of these counters. * + * * + ************************************************************************/ + +typedef union ii_ipca_u { + uint64_t ii_ipca_regval; + struct { + uint64_t i_wid : 4; + uint64_t i_adjust : 1; + uint64_t i_rsvd_1 : 3; + uint64_t i_field : 2; + uint64_t i_rsvd : 54; + } ii_ipca_fld_s; +} ii_ipca_u_t; + + +/************************************************************************ + * * + * There are 8 instances of this register. This register contains * + * the information that the II has to remember once it has launched a * + * PIO Read operation. The contents are used to form the correct * + * Router Network packet and direct the Crosstalk reply to the * + * appropriate processor. * + * * + ************************************************************************/ + + +typedef union ii_iprte0a_u { + uint64_t ii_iprte0a_regval; + struct { + uint64_t i_rsvd_1 : 54; + uint64_t i_widget : 4; + uint64_t i_to_cnt : 5; + uint64_t i_vld : 1; + } ii_iprte0a_fld_s; +} ii_iprte0a_u_t; + + +/************************************************************************ + * * + * There are 8 instances of this register. This register contains * + * the information that the II has to remember once it has launched a * + * PIO Read operation. The contents are used to form the correct * + * Router Network packet and direct the Crosstalk reply to the * + * appropriate processor. * + * * + ************************************************************************/ + +typedef union ii_iprte1a_u { + uint64_t ii_iprte1a_regval; + struct { + uint64_t i_rsvd_1 : 54; + uint64_t i_widget : 4; + uint64_t i_to_cnt : 5; + uint64_t i_vld : 1; + } ii_iprte1a_fld_s; +} ii_iprte1a_u_t; + + +/************************************************************************ + * * + * There are 8 instances of this register. This register contains * + * the information that the II has to remember once it has launched a * + * PIO Read operation. The contents are used to form the correct * + * Router Network packet and direct the Crosstalk reply to the * + * appropriate processor. * + * * + ************************************************************************/ + +typedef union ii_iprte2a_u { + uint64_t ii_iprte2a_regval; + struct { + uint64_t i_rsvd_1 : 54; + uint64_t i_widget : 4; + uint64_t i_to_cnt : 5; + uint64_t i_vld : 1; + } ii_iprte2a_fld_s; +} ii_iprte2a_u_t; + + +/************************************************************************ + * * + * There are 8 instances of this register. This register contains * + * the information that the II has to remember once it has launched a * + * PIO Read operation. The contents are used to form the correct * + * Router Network packet and direct the Crosstalk reply to the * + * appropriate processor. * + * * + ************************************************************************/ + +typedef union ii_iprte3a_u { + uint64_t ii_iprte3a_regval; + struct { + uint64_t i_rsvd_1 : 54; + uint64_t i_widget : 4; + uint64_t i_to_cnt : 5; + uint64_t i_vld : 1; + } ii_iprte3a_fld_s; +} ii_iprte3a_u_t; + + +/************************************************************************ + * * + * There are 8 instances of this register. This register contains * + * the information that the II has to remember once it has launched a * + * PIO Read operation. The contents are used to form the correct * + * Router Network packet and direct the Crosstalk reply to the * + * appropriate processor. * + * * + ************************************************************************/ + +typedef union ii_iprte4a_u { + uint64_t ii_iprte4a_regval; + struct { + uint64_t i_rsvd_1 : 54; + uint64_t i_widget : 4; + uint64_t i_to_cnt : 5; + uint64_t i_vld : 1; + } ii_iprte4a_fld_s; +} ii_iprte4a_u_t; + + +/************************************************************************ + * * + * There are 8 instances of this register. This register contains * + * the information that the II has to remember once it has launched a * + * PIO Read operation. The contents are used to form the correct * + * Router Network packet and direct the Crosstalk reply to the * + * appropriate processor. * + * * + ************************************************************************/ + +typedef union ii_iprte5a_u { + uint64_t ii_iprte5a_regval; + struct { + uint64_t i_rsvd_1 : 54; + uint64_t i_widget : 4; + uint64_t i_to_cnt : 5; + uint64_t i_vld : 1; + } ii_iprte5a_fld_s; +} ii_iprte5a_u_t; + + +/************************************************************************ + * * + * There are 8 instances of this register. This register contains * + * the information that the II has to remember once it has launched a * + * PIO Read operation. The contents are used to form the correct * + * Router Network packet and direct the Crosstalk reply to the * + * appropriate processor. * + * * + ************************************************************************/ + +typedef union ii_iprte6a_u { + uint64_t ii_iprte6a_regval; + struct { + uint64_t i_rsvd_1 : 54; + uint64_t i_widget : 4; + uint64_t i_to_cnt : 5; + uint64_t i_vld : 1; + } ii_iprte6a_fld_s; +} ii_iprte6a_u_t; + + +/************************************************************************ + * * + * There are 8 instances of this register. This register contains * + * the information that the II has to remember once it has launched a * + * PIO Read operation. The contents are used to form the correct * + * Router Network packet and direct the Crosstalk reply to the * + * appropriate processor. * + * * + ************************************************************************/ + +typedef union ii_iprte7a_u { + uint64_t ii_iprte7a_regval; + struct { + uint64_t i_rsvd_1 : 54; + uint64_t i_widget : 4; + uint64_t i_to_cnt : 5; + uint64_t i_vld : 1; + } ii_iprtea7_fld_s; +} ii_iprte7a_u_t; + + + +/************************************************************************ + * * + * There are 8 instances of this register. This register contains * + * the information that the II has to remember once it has launched a * + * PIO Read operation. The contents are used to form the correct * + * Router Network packet and direct the Crosstalk reply to the * + * appropriate processor. * + * * + ************************************************************************/ + + +typedef union ii_iprte0b_u { + uint64_t ii_iprte0b_regval; + struct { + uint64_t i_rsvd_1 : 3; + uint64_t i_address : 47; + uint64_t i_init : 3; + uint64_t i_source : 11; + } ii_iprte0b_fld_s; +} ii_iprte0b_u_t; + + +/************************************************************************ + * * + * There are 8 instances of this register. This register contains * + * the information that the II has to remember once it has launched a * + * PIO Read operation. The contents are used to form the correct * + * Router Network packet and direct the Crosstalk reply to the * + * appropriate processor. * + * * + ************************************************************************/ + +typedef union ii_iprte1b_u { + uint64_t ii_iprte1b_regval; + struct { + uint64_t i_rsvd_1 : 3; + uint64_t i_address : 47; + uint64_t i_init : 3; + uint64_t i_source : 11; + } ii_iprte1b_fld_s; +} ii_iprte1b_u_t; + + +/************************************************************************ + * * + * There are 8 instances of this register. This register contains * + * the information that the II has to remember once it has launched a * + * PIO Read operation. The contents are used to form the correct * + * Router Network packet and direct the Crosstalk reply to the * + * appropriate processor. * + * * + ************************************************************************/ + +typedef union ii_iprte2b_u { + uint64_t ii_iprte2b_regval; + struct { + uint64_t i_rsvd_1 : 3; + uint64_t i_address : 47; + uint64_t i_init : 3; + uint64_t i_source : 11; + } ii_iprte2b_fld_s; +} ii_iprte2b_u_t; + + +/************************************************************************ + * * + * There are 8 instances of this register. This register contains * + * the information that the II has to remember once it has launched a * + * PIO Read operation. The contents are used to form the correct * + * Router Network packet and direct the Crosstalk reply to the * + * appropriate processor. * + * * + ************************************************************************/ + +typedef union ii_iprte3b_u { + uint64_t ii_iprte3b_regval; + struct { + uint64_t i_rsvd_1 : 3; + uint64_t i_address : 47; + uint64_t i_init : 3; + uint64_t i_source : 11; + } ii_iprte3b_fld_s; +} ii_iprte3b_u_t; + + +/************************************************************************ + * * + * There are 8 instances of this register. This register contains * + * the information that the II has to remember once it has launched a * + * PIO Read operation. The contents are used to form the correct * + * Router Network packet and direct the Crosstalk reply to the * + * appropriate processor. * + * * + ************************************************************************/ + +typedef union ii_iprte4b_u { + uint64_t ii_iprte4b_regval; + struct { + uint64_t i_rsvd_1 : 3; + uint64_t i_address : 47; + uint64_t i_init : 3; + uint64_t i_source : 11; + } ii_iprte4b_fld_s; +} ii_iprte4b_u_t; + + +/************************************************************************ + * * + * There are 8 instances of this register. This register contains * + * the information that the II has to remember once it has launched a * + * PIO Read operation. The contents are used to form the correct * + * Router Network packet and direct the Crosstalk reply to the * + * appropriate processor. * + * * + ************************************************************************/ + +typedef union ii_iprte5b_u { + uint64_t ii_iprte5b_regval; + struct { + uint64_t i_rsvd_1 : 3; + uint64_t i_address : 47; + uint64_t i_init : 3; + uint64_t i_source : 11; + } ii_iprte5b_fld_s; +} ii_iprte5b_u_t; + + +/************************************************************************ + * * + * There are 8 instances of this register. This register contains * + * the information that the II has to remember once it has launched a * + * PIO Read operation. The contents are used to form the correct * + * Router Network packet and direct the Crosstalk reply to the * + * appropriate processor. * + * * + ************************************************************************/ + +typedef union ii_iprte6b_u { + uint64_t ii_iprte6b_regval; + struct { + uint64_t i_rsvd_1 : 3; + uint64_t i_address : 47; + uint64_t i_init : 3; + uint64_t i_source : 11; + + } ii_iprte6b_fld_s; +} ii_iprte6b_u_t; + + +/************************************************************************ + * * + * There are 8 instances of this register. This register contains * + * the information that the II has to remember once it has launched a * + * PIO Read operation. The contents are used to form the correct * + * Router Network packet and direct the Crosstalk reply to the * + * appropriate processor. * + * * + ************************************************************************/ + +typedef union ii_iprte7b_u { + uint64_t ii_iprte7b_regval; + struct { + uint64_t i_rsvd_1 : 3; + uint64_t i_address : 47; + uint64_t i_init : 3; + uint64_t i_source : 11; + } ii_iprte7b_fld_s; +} ii_iprte7b_u_t; + + +/************************************************************************ + * * + * Description: SHub II contains a feature which did not exist in * + * the Hub which automatically cleans up after a Read Response * + * timeout, including deallocation of the IPRTE and recovery of IBuf * + * space. The inclusion of this register in SHub is for backward * + * compatibility * + * A write to this register causes an entry from the table of * + * outstanding PIO Read Requests to be freed and returned to the * + * stack of free entries. This register is used in handling the * + * timeout errors that result in a PIO Reply never returning from * + * Crosstalk. * + * Note that this register does not affect the contents of the IPRTE * + * registers. The Valid bits in those registers have to be * + * specifically turned off by software. * + * * + ************************************************************************/ + +typedef union ii_ipdr_u { + uint64_t ii_ipdr_regval; + struct { + uint64_t i_te : 3; + uint64_t i_rsvd_1 : 1; + uint64_t i_pnd : 1; + uint64_t i_init_rpcnt : 1; + uint64_t i_rsvd : 58; + } ii_ipdr_fld_s; +} ii_ipdr_u_t; + + +/************************************************************************ + * * + * A write to this register causes a CRB entry to be returned to the * + * queue of free CRBs. The entry should have previously been cleared * + * (mark bit) via backdoor access to the pertinent CRB entry. This * + * register is used in the last step of handling the errors that are * + * captured and marked in CRB entries. Briefly: 1) first error for * + * DMA write from a particular device, and first error for a * + * particular BTE stream, lead to a marked CRB entry, and processor * + * interrupt, 2) software reads the error information captured in the * + * CRB entry, and presumably takes some corrective action, 3) * + * software clears the mark bit, and finally 4) software writes to * + * the ICDR register to return the CRB entry to the list of free CRB * + * entries. * + * * + ************************************************************************/ + +typedef union ii_icdr_u { + uint64_t ii_icdr_regval; + struct { + uint64_t i_crb_num : 4; + uint64_t i_pnd : 1; + uint64_t i_rsvd : 59; + } ii_icdr_fld_s; +} ii_icdr_u_t; + + +/************************************************************************ + * * + * This register provides debug access to two FIFOs inside of II. * + * Both IOQ_MAX* fields of this register contain the instantaneous * + * depth (in units of the number of available entries) of the * + * associated IOQ FIFO. A read of this register will return the * + * number of free entries on each FIFO at the time of the read. So * + * when a FIFO is idle, the associated field contains the maximum * + * depth of the FIFO. This register is writable for debug reasons * + * and is intended to be written with the maximum desired FIFO depth * + * while the FIFO is idle. Software must assure that II is idle when * + * this register is written. If there are any active entries in any * + * of these FIFOs when this register is written, the results are * + * undefined. * + * * + ************************************************************************/ + +typedef union ii_ifdr_u { + uint64_t ii_ifdr_regval; + struct { + uint64_t i_ioq_max_rq : 7; + uint64_t i_set_ioq_rq : 1; + uint64_t i_ioq_max_rp : 7; + uint64_t i_set_ioq_rp : 1; + uint64_t i_rsvd : 48; + } ii_ifdr_fld_s; +} ii_ifdr_u_t; + + +/************************************************************************ + * * + * This register allows the II to become sluggish in removing * + * messages from its inbound queue (IIQ). This will cause messages to * + * back up in either virtual channel. Disabling the "molasses" mode * + * subsequently allows the II to be tested under stress. In the * + * sluggish ("Molasses") mode, the localized effects of congestion * + * can be observed. * + * * + ************************************************************************/ + +typedef union ii_iiap_u { + uint64_t ii_iiap_regval; + struct { + uint64_t i_rq_mls : 6; + uint64_t i_rsvd_1 : 2; + uint64_t i_rp_mls : 6; + uint64_t i_rsvd : 50; + } ii_iiap_fld_s; +} ii_iiap_u_t; + + +/************************************************************************ + * * + * This register allows several parameters of CRB operation to be * + * set. Note that writing to this register can have catastrophic side * + * effects, if the CRB is not quiescent, i.e. if the CRB is * + * processing protocol messages when the write occurs. * + * * + ************************************************************************/ + +typedef union ii_icmr_u { + uint64_t ii_icmr_regval; + struct { + uint64_t i_sp_msg : 1; + uint64_t i_rd_hdr : 1; + uint64_t i_rsvd_4 : 2; + uint64_t i_c_cnt : 4; + uint64_t i_rsvd_3 : 4; + uint64_t i_clr_rqpd : 1; + uint64_t i_clr_rppd : 1; + uint64_t i_rsvd_2 : 2; + uint64_t i_fc_cnt : 4; + uint64_t i_crb_vld : 15; + uint64_t i_crb_mark : 15; + uint64_t i_rsvd_1 : 2; + uint64_t i_precise : 1; + uint64_t i_rsvd : 11; + } ii_icmr_fld_s; +} ii_icmr_u_t; + + +/************************************************************************ + * * + * This register allows control of the table portion of the CRB * + * logic via software. Control operations from this register have * + * priority over all incoming Crosstalk or BTE requests. * + * * + ************************************************************************/ + +typedef union ii_iccr_u { + uint64_t ii_iccr_regval; + struct { + uint64_t i_crb_num : 4; + uint64_t i_rsvd_1 : 4; + uint64_t i_cmd : 8; + uint64_t i_pending : 1; + uint64_t i_rsvd : 47; + } ii_iccr_fld_s; +} ii_iccr_u_t; + + +/************************************************************************ + * * + * This register allows the maximum timeout value to be programmed. * + * * + ************************************************************************/ + +typedef union ii_icto_u { + uint64_t ii_icto_regval; + struct { + uint64_t i_timeout : 8; + uint64_t i_rsvd : 56; + } ii_icto_fld_s; +} ii_icto_u_t; + + +/************************************************************************ + * * + * This register allows the timeout prescalar to be programmed. An * + * internal counter is associated with this register. When the * + * internal counter reaches the value of the PRESCALE field, the * + * timer registers in all valid CRBs are incremented (CRBx_D[TIMEOUT] * + * field). The internal counter resets to zero, and then continues * + * counting. * + * * + ************************************************************************/ + +typedef union ii_ictp_u { + uint64_t ii_ictp_regval; + struct { + uint64_t i_prescale : 24; + uint64_t i_rsvd : 40; + } ii_ictp_fld_s; +} ii_ictp_u_t; + + +/************************************************************************ + * * + * Description: There are 15 CRB Entries (ICRB0 to ICRBE) that are * + * used for Crosstalk operations (both cacheline and partial * + * operations) or BTE/IO. Because the CRB entries are very wide, five * + * registers (_A to _E) are required to read and write each entry. * + * The CRB Entry registers can be conceptualized as rows and columns * + * (illustrated in the table above). Each row contains the 4 * + * registers required for a single CRB Entry. The first doubleword * + * (column) for each entry is labeled A, and the second doubleword * + * (higher address) is labeled B, the third doubleword is labeled C, * + * the fourth doubleword is labeled D and the fifth doubleword is * + * labeled E. All CRB entries have their addresses on a quarter * + * cacheline aligned boundary. * + * Upon reset, only the following fields are initialized: valid * + * (VLD), priority count, timeout, timeout valid, and context valid. * + * All other bits should be cleared by software before use (after * + * recovering any potential error state from before the reset). * + * The following four tables summarize the format for the four * + * registers that are used for each ICRB# Entry. * + * * + ************************************************************************/ + +typedef union ii_icrb0_a_u { + uint64_t ii_icrb0_a_regval; + struct { + uint64_t ia_iow : 1; + uint64_t ia_vld : 1; + uint64_t ia_addr : 47; + uint64_t ia_tnum : 5; + uint64_t ia_sidn : 4; + uint64_t ia_rsvd : 6; + } ii_icrb0_a_fld_s; +} ii_icrb0_a_u_t; + + +/************************************************************************ + * * + * Description: There are 15 CRB Entries (ICRB0 to ICRBE) that are * + * used for Crosstalk operations (both cacheline and partial * + * operations) or BTE/IO. Because the CRB entries are very wide, five * + * registers (_A to _E) are required to read and write each entry. * + * * + ************************************************************************/ + +typedef union ii_icrb0_b_u { + uint64_t ii_icrb0_b_regval; + struct { + uint64_t ib_xt_err : 1; + uint64_t ib_mark : 1; + uint64_t ib_ln_uce : 1; + uint64_t ib_errcode : 3; + uint64_t ib_error : 1; + uint64_t ib_stall__bte_1 : 1; + uint64_t ib_stall__bte_0 : 1; + uint64_t ib_stall__intr : 1; + uint64_t ib_stall_ib : 1; + uint64_t ib_intvn : 1; + uint64_t ib_wb : 1; + uint64_t ib_hold : 1; + uint64_t ib_ack : 1; + uint64_t ib_resp : 1; + uint64_t ib_ack_cnt : 11; + uint64_t ib_rsvd : 7; + uint64_t ib_exc : 5; + uint64_t ib_init : 3; + uint64_t ib_imsg : 8; + uint64_t ib_imsgtype : 2; + uint64_t ib_use_old : 1; + uint64_t ib_rsvd_1 : 11; + } ii_icrb0_b_fld_s; +} ii_icrb0_b_u_t; + + +/************************************************************************ + * * + * Description: There are 15 CRB Entries (ICRB0 to ICRBE) that are * + * used for Crosstalk operations (both cacheline and partial * + * operations) or BTE/IO. Because the CRB entries are very wide, five * + * registers (_A to _E) are required to read and write each entry. * + * * + ************************************************************************/ + +typedef union ii_icrb0_c_u { + uint64_t ii_icrb0_c_regval; + struct { + uint64_t ic_source : 15; + uint64_t ic_size : 2; + uint64_t ic_ct : 1; + uint64_t ic_bte_num : 1; + uint64_t ic_gbr : 1; + uint64_t ic_resprqd : 1; + uint64_t ic_bo : 1; + uint64_t ic_suppl : 15; + uint64_t ic_rsvd : 27; + } ii_icrb0_c_fld_s; +} ii_icrb0_c_u_t; + + +/************************************************************************ + * * + * Description: There are 15 CRB Entries (ICRB0 to ICRBE) that are * + * used for Crosstalk operations (both cacheline and partial * + * operations) or BTE/IO. Because the CRB entries are very wide, five * + * registers (_A to _E) are required to read and write each entry. * + * * + ************************************************************************/ + +typedef union ii_icrb0_d_u { + uint64_t ii_icrb0_d_regval; + struct { + uint64_t id_pa_be : 43; + uint64_t id_bte_op : 1; + uint64_t id_pr_psc : 4; + uint64_t id_pr_cnt : 4; + uint64_t id_sleep : 1; + uint64_t id_rsvd : 11; + } ii_icrb0_d_fld_s; +} ii_icrb0_d_u_t; + + +/************************************************************************ + * * + * Description: There are 15 CRB Entries (ICRB0 to ICRBE) that are * + * used for Crosstalk operations (both cacheline and partial * + * operations) or BTE/IO. Because the CRB entries are very wide, five * + * registers (_A to _E) are required to read and write each entry. * + * * + ************************************************************************/ + +typedef union ii_icrb0_e_u { + uint64_t ii_icrb0_e_regval; + struct { + uint64_t ie_timeout : 8; + uint64_t ie_context : 15; + uint64_t ie_rsvd : 1; + uint64_t ie_tvld : 1; + uint64_t ie_cvld : 1; + uint64_t ie_rsvd_0 : 38; + } ii_icrb0_e_fld_s; +} ii_icrb0_e_u_t; + + +/************************************************************************ + * * + * This register contains the lower 64 bits of the header of the * + * spurious message captured by II. Valid when the SP_MSG bit in ICMR * + * register is set. * + * * + ************************************************************************/ + +typedef union ii_icsml_u { + uint64_t ii_icsml_regval; + struct { + uint64_t i_tt_addr : 47; + uint64_t i_newsuppl_ex : 14; + uint64_t i_reserved : 2; + uint64_t i_overflow : 1; + } ii_icsml_fld_s; +} ii_icsml_u_t; + + +/************************************************************************ + * * + * This register contains the middle 64 bits of the header of the * + * spurious message captured by II. Valid when the SP_MSG bit in ICMR * + * register is set. * + * * + ************************************************************************/ + +typedef union ii_icsmm_u { + uint64_t ii_icsmm_regval; + struct { + uint64_t i_tt_ack_cnt : 11; + uint64_t i_reserved : 53; + } ii_icsmm_fld_s; +} ii_icsmm_u_t; + + +/************************************************************************ + * * + * This register contains the microscopic state, all the inputs to * + * the protocol table, captured with the spurious message. Valid when * + * the SP_MSG bit in the ICMR register is set. * + * * + ************************************************************************/ + +typedef union ii_icsmh_u { + uint64_t ii_icsmh_regval; + struct { + uint64_t i_tt_vld : 1; + uint64_t i_xerr : 1; + uint64_t i_ft_cwact_o : 1; + uint64_t i_ft_wact_o : 1; + uint64_t i_ft_active_o : 1; + uint64_t i_sync : 1; + uint64_t i_mnusg : 1; + uint64_t i_mnusz : 1; + uint64_t i_plusz : 1; + uint64_t i_plusg : 1; + uint64_t i_tt_exc : 5; + uint64_t i_tt_wb : 1; + uint64_t i_tt_hold : 1; + uint64_t i_tt_ack : 1; + uint64_t i_tt_resp : 1; + uint64_t i_tt_intvn : 1; + uint64_t i_g_stall_bte1 : 1; + uint64_t i_g_stall_bte0 : 1; + uint64_t i_g_stall_il : 1; + uint64_t i_g_stall_ib : 1; + uint64_t i_tt_imsg : 8; + uint64_t i_tt_imsgtype : 2; + uint64_t i_tt_use_old : 1; + uint64_t i_tt_respreqd : 1; + uint64_t i_tt_bte_num : 1; + uint64_t i_cbn : 1; + uint64_t i_match : 1; + uint64_t i_rpcnt_lt_34 : 1; + uint64_t i_rpcnt_ge_34 : 1; + uint64_t i_rpcnt_lt_18 : 1; + uint64_t i_rpcnt_ge_18 : 1; + uint64_t i_rpcnt_lt_2 : 1; + uint64_t i_rpcnt_ge_2 : 1; + uint64_t i_rqcnt_lt_18 : 1; + uint64_t i_rqcnt_ge_18 : 1; + uint64_t i_rqcnt_lt_2 : 1; + uint64_t i_rqcnt_ge_2 : 1; + uint64_t i_tt_device : 7; + uint64_t i_tt_init : 3; + uint64_t i_reserved : 5; + } ii_icsmh_fld_s; +} ii_icsmh_u_t; + + +/************************************************************************ + * * + * The Shub DEBUG unit provides a 3-bit selection signal to the * + * II core and a 3-bit selection signal to the fsbclk domain in the II * + * wrapper. * + * * + ************************************************************************/ + +typedef union ii_idbss_u { + uint64_t ii_idbss_regval; + struct { + uint64_t i_iioclk_core_submenu : 3; + uint64_t i_rsvd : 5; + uint64_t i_fsbclk_wrapper_submenu : 3; + uint64_t i_rsvd_1 : 5; + uint64_t i_iioclk_menu : 5; + uint64_t i_rsvd_2 : 43; + } ii_idbss_fld_s; +} ii_idbss_u_t; + + +/************************************************************************ + * * + * Description: This register is used to set up the length for a * + * transfer and then to monitor the progress of that transfer. This * + * register needs to be initialized before a transfer is started. A * + * legitimate write to this register will set the Busy bit, clear the * + * Error bit, and initialize the length to the value desired. * + * While the transfer is in progress, hardware will decrement the * + * length field with each successful block that is copied. Once the * + * transfer completes, hardware will clear the Busy bit. The length * + * field will also contain the number of cache lines left to be * + * transferred. * + * * + ************************************************************************/ + +typedef union ii_ibls0_u { + uint64_t ii_ibls0_regval; + struct { + uint64_t i_length : 16; + uint64_t i_error : 1; + uint64_t i_rsvd_1 : 3; + uint64_t i_busy : 1; + uint64_t i_rsvd : 43; + } ii_ibls0_fld_s; +} ii_ibls0_u_t; + + +/************************************************************************ + * * + * This register should be loaded before a transfer is started. The * + * address to be loaded in bits 39:0 is the 40-bit TRex+ physical * + * address as described in Section 1.3, Figure2 and Figure3. Since * + * the bottom 7 bits of the address are always taken to be zero, BTE * + * transfers are always cacheline-aligned. * + * * + ************************************************************************/ + +typedef union ii_ibsa0_u { + uint64_t ii_ibsa0_regval; + struct { + uint64_t i_rsvd_1 : 7; + uint64_t i_addr : 42; + uint64_t i_rsvd : 15; + } ii_ibsa0_fld_s; +} ii_ibsa0_u_t; + + +/************************************************************************ + * * + * This register should be loaded before a transfer is started. The * + * address to be loaded in bits 39:0 is the 40-bit TRex+ physical * + * address as described in Section 1.3, Figure2 and Figure3. Since * + * the bottom 7 bits of the address are always taken to be zero, BTE * + * transfers are always cacheline-aligned. * + * * + ************************************************************************/ + +typedef union ii_ibda0_u { + uint64_t ii_ibda0_regval; + struct { + uint64_t i_rsvd_1 : 7; + uint64_t i_addr : 42; + uint64_t i_rsvd : 15; + } ii_ibda0_fld_s; +} ii_ibda0_u_t; + + +/************************************************************************ + * * + * Writing to this register sets up the attributes of the transfer * + * and initiates the transfer operation. Reading this register has * + * the side effect of terminating any transfer in progress. Note: * + * stopping a transfer midstream could have an adverse impact on the * + * other BTE. If a BTE stream has to be stopped (due to error * + * handling for example), both BTE streams should be stopped and * + * their transfers discarded. * + * * + ************************************************************************/ + +typedef union ii_ibct0_u { + uint64_t ii_ibct0_regval; + struct { + uint64_t i_zerofill : 1; + uint64_t i_rsvd_2 : 3; + uint64_t i_notify : 1; + uint64_t i_rsvd_1 : 3; + uint64_t i_poison : 1; + uint64_t i_rsvd : 55; + } ii_ibct0_fld_s; +} ii_ibct0_u_t; + + +/************************************************************************ + * * + * This register contains the address to which the WINV is sent. * + * This address has to be cache line aligned. * + * * + ************************************************************************/ + +typedef union ii_ibna0_u { + uint64_t ii_ibna0_regval; + struct { + uint64_t i_rsvd_1 : 7; + uint64_t i_addr : 42; + uint64_t i_rsvd : 15; + } ii_ibna0_fld_s; +} ii_ibna0_u_t; + + +/************************************************************************ + * * + * This register contains the programmable level as well as the node * + * ID and PI unit of the processor to which the interrupt will be * + * sent. * + * * + ************************************************************************/ + +typedef union ii_ibia0_u { + uint64_t ii_ibia0_regval; + struct { + uint64_t i_rsvd_2 : 1; + uint64_t i_node_id : 11; + uint64_t i_rsvd_1 : 4; + uint64_t i_level : 7; + uint64_t i_rsvd : 41; + } ii_ibia0_fld_s; +} ii_ibia0_u_t; + + +/************************************************************************ + * * + * Description: This register is used to set up the length for a * + * transfer and then to monitor the progress of that transfer. This * + * register needs to be initialized before a transfer is started. A * + * legitimate write to this register will set the Busy bit, clear the * + * Error bit, and initialize the length to the value desired. * + * While the transfer is in progress, hardware will decrement the * + * length field with each successful block that is copied. Once the * + * transfer completes, hardware will clear the Busy bit. The length * + * field will also contain the number of cache lines left to be * + * transferred. * + * * + ************************************************************************/ + +typedef union ii_ibls1_u { + uint64_t ii_ibls1_regval; + struct { + uint64_t i_length : 16; + uint64_t i_error : 1; + uint64_t i_rsvd_1 : 3; + uint64_t i_busy : 1; + uint64_t i_rsvd : 43; + } ii_ibls1_fld_s; +} ii_ibls1_u_t; + + +/************************************************************************ + * * + * This register should be loaded before a transfer is started. The * + * address to be loaded in bits 39:0 is the 40-bit TRex+ physical * + * address as described in Section 1.3, Figure2 and Figure3. Since * + * the bottom 7 bits of the address are always taken to be zero, BTE * + * transfers are always cacheline-aligned. * + * * + ************************************************************************/ + +typedef union ii_ibsa1_u { + uint64_t ii_ibsa1_regval; + struct { + uint64_t i_rsvd_1 : 7; + uint64_t i_addr : 33; + uint64_t i_rsvd : 24; + } ii_ibsa1_fld_s; +} ii_ibsa1_u_t; + + +/************************************************************************ + * * + * This register should be loaded before a transfer is started. The * + * address to be loaded in bits 39:0 is the 40-bit TRex+ physical * + * address as described in Section 1.3, Figure2 and Figure3. Since * + * the bottom 7 bits of the address are always taken to be zero, BTE * + * transfers are always cacheline-aligned. * + * * + ************************************************************************/ + +typedef union ii_ibda1_u { + uint64_t ii_ibda1_regval; + struct { + uint64_t i_rsvd_1 : 7; + uint64_t i_addr : 33; + uint64_t i_rsvd : 24; + } ii_ibda1_fld_s; +} ii_ibda1_u_t; + + +/************************************************************************ + * * + * Writing to this register sets up the attributes of the transfer * + * and initiates the transfer operation. Reading this register has * + * the side effect of terminating any transfer in progress. Note: * + * stopping a transfer midstream could have an adverse impact on the * + * other BTE. If a BTE stream has to be stopped (due to error * + * handling for example), both BTE streams should be stopped and * + * their transfers discarded. * + * * + ************************************************************************/ + +typedef union ii_ibct1_u { + uint64_t ii_ibct1_regval; + struct { + uint64_t i_zerofill : 1; + uint64_t i_rsvd_2 : 3; + uint64_t i_notify : 1; + uint64_t i_rsvd_1 : 3; + uint64_t i_poison : 1; + uint64_t i_rsvd : 55; + } ii_ibct1_fld_s; +} ii_ibct1_u_t; + + +/************************************************************************ + * * + * This register contains the address to which the WINV is sent. * + * This address has to be cache line aligned. * + * * + ************************************************************************/ + +typedef union ii_ibna1_u { + uint64_t ii_ibna1_regval; + struct { + uint64_t i_rsvd_1 : 7; + uint64_t i_addr : 33; + uint64_t i_rsvd : 24; + } ii_ibna1_fld_s; +} ii_ibna1_u_t; + + +/************************************************************************ + * * + * This register contains the programmable level as well as the node * + * ID and PI unit of the processor to which the interrupt will be * + * sent. * + * * + ************************************************************************/ + +typedef union ii_ibia1_u { + uint64_t ii_ibia1_regval; + struct { + uint64_t i_pi_id : 1; + uint64_t i_node_id : 8; + uint64_t i_rsvd_1 : 7; + uint64_t i_level : 7; + uint64_t i_rsvd : 41; + } ii_ibia1_fld_s; +} ii_ibia1_u_t; + + +/************************************************************************ + * * + * This register defines the resources that feed information into * + * the two performance counters located in the IO Performance * + * Profiling Register. There are 17 different quantities that can be * + * measured. Given these 17 different options, the two performance * + * counters have 15 of them in common; menu selections 0 through 0xE * + * are identical for each performance counter. As for the other two * + * options, one is available from one performance counter and the * + * other is available from the other performance counter. Hence, the * + * II supports all 17*16=272 possible combinations of quantities to * + * measure. * + * * + ************************************************************************/ + +typedef union ii_ipcr_u { + uint64_t ii_ipcr_regval; + struct { + uint64_t i_ippr0_c : 4; + uint64_t i_ippr1_c : 4; + uint64_t i_icct : 8; + uint64_t i_rsvd : 48; + } ii_ipcr_fld_s; +} ii_ipcr_u_t; + + +/************************************************************************ + * * + * * + * * + ************************************************************************/ + +typedef union ii_ippr_u { + uint64_t ii_ippr_regval; + struct { + uint64_t i_ippr0 : 32; + uint64_t i_ippr1 : 32; + } ii_ippr_fld_s; +} ii_ippr_u_t; + + + +/************************************************************************** + * * + * The following defines which were not formed into structures are * + * probably indentical to another register, and the name of the * + * register is provided against each of these registers. This * + * information needs to be checked carefully * + * * + * IIO_ICRB1_A IIO_ICRB0_A * + * IIO_ICRB1_B IIO_ICRB0_B * + * IIO_ICRB1_C IIO_ICRB0_C * + * IIO_ICRB1_D IIO_ICRB0_D * + * IIO_ICRB1_E IIO_ICRB0_E * + * IIO_ICRB2_A IIO_ICRB0_A * + * IIO_ICRB2_B IIO_ICRB0_B * + * IIO_ICRB2_C IIO_ICRB0_C * + * IIO_ICRB2_D IIO_ICRB0_D * + * IIO_ICRB2_E IIO_ICRB0_E * + * IIO_ICRB3_A IIO_ICRB0_A * + * IIO_ICRB3_B IIO_ICRB0_B * + * IIO_ICRB3_C IIO_ICRB0_C * + * IIO_ICRB3_D IIO_ICRB0_D * + * IIO_ICRB3_E IIO_ICRB0_E * + * IIO_ICRB4_A IIO_ICRB0_A * + * IIO_ICRB4_B IIO_ICRB0_B * + * IIO_ICRB4_C IIO_ICRB0_C * + * IIO_ICRB4_D IIO_ICRB0_D * + * IIO_ICRB4_E IIO_ICRB0_E * + * IIO_ICRB5_A IIO_ICRB0_A * + * IIO_ICRB5_B IIO_ICRB0_B * + * IIO_ICRB5_C IIO_ICRB0_C * + * IIO_ICRB5_D IIO_ICRB0_D * + * IIO_ICRB5_E IIO_ICRB0_E * + * IIO_ICRB6_A IIO_ICRB0_A * + * IIO_ICRB6_B IIO_ICRB0_B * + * IIO_ICRB6_C IIO_ICRB0_C * + * IIO_ICRB6_D IIO_ICRB0_D * + * IIO_ICRB6_E IIO_ICRB0_E * + * IIO_ICRB7_A IIO_ICRB0_A * + * IIO_ICRB7_B IIO_ICRB0_B * + * IIO_ICRB7_C IIO_ICRB0_C * + * IIO_ICRB7_D IIO_ICRB0_D * + * IIO_ICRB7_E IIO_ICRB0_E * + * IIO_ICRB8_A IIO_ICRB0_A * + * IIO_ICRB8_B IIO_ICRB0_B * + * IIO_ICRB8_C IIO_ICRB0_C * + * IIO_ICRB8_D IIO_ICRB0_D * + * IIO_ICRB8_E IIO_ICRB0_E * + * IIO_ICRB9_A IIO_ICRB0_A * + * IIO_ICRB9_B IIO_ICRB0_B * + * IIO_ICRB9_C IIO_ICRB0_C * + * IIO_ICRB9_D IIO_ICRB0_D * + * IIO_ICRB9_E IIO_ICRB0_E * + * IIO_ICRBA_A IIO_ICRB0_A * + * IIO_ICRBA_B IIO_ICRB0_B * + * IIO_ICRBA_C IIO_ICRB0_C * + * IIO_ICRBA_D IIO_ICRB0_D * + * IIO_ICRBA_E IIO_ICRB0_E * + * IIO_ICRBB_A IIO_ICRB0_A * + * IIO_ICRBB_B IIO_ICRB0_B * + * IIO_ICRBB_C IIO_ICRB0_C * + * IIO_ICRBB_D IIO_ICRB0_D * + * IIO_ICRBB_E IIO_ICRB0_E * + * IIO_ICRBC_A IIO_ICRB0_A * + * IIO_ICRBC_B IIO_ICRB0_B * + * IIO_ICRBC_C IIO_ICRB0_C * + * IIO_ICRBC_D IIO_ICRB0_D * + * IIO_ICRBC_E IIO_ICRB0_E * + * IIO_ICRBD_A IIO_ICRB0_A * + * IIO_ICRBD_B IIO_ICRB0_B * + * IIO_ICRBD_C IIO_ICRB0_C * + * IIO_ICRBD_D IIO_ICRB0_D * + * IIO_ICRBD_E IIO_ICRB0_E * + * IIO_ICRBE_A IIO_ICRB0_A * + * IIO_ICRBE_B IIO_ICRB0_B * + * IIO_ICRBE_C IIO_ICRB0_C * + * IIO_ICRBE_D IIO_ICRB0_D * + * IIO_ICRBE_E IIO_ICRB0_E * + * * + **************************************************************************/ + + +/* + * Slightly friendlier names for some common registers. + */ +#define IIO_WIDGET IIO_WID /* Widget identification */ +#define IIO_WIDGET_STAT IIO_WSTAT /* Widget status register */ +#define IIO_WIDGET_CTRL IIO_WCR /* Widget control register */ +#define IIO_PROTECT IIO_ILAPR /* IO interface protection */ +#define IIO_PROTECT_OVRRD IIO_ILAPO /* IO protect override */ +#define IIO_OUTWIDGET_ACCESS IIO_IOWA /* Outbound widget access */ +#define IIO_INWIDGET_ACCESS IIO_IIWA /* Inbound widget access */ +#define IIO_INDEV_ERR_MASK IIO_IIDEM /* Inbound device error mask */ +#define IIO_LLP_CSR IIO_ILCSR /* LLP control and status */ +#define IIO_LLP_LOG IIO_ILLR /* LLP log */ +#define IIO_XTALKCC_TOUT IIO_IXCC /* Xtalk credit count timeout*/ +#define IIO_XTALKTT_TOUT IIO_IXTT /* Xtalk tail timeout */ +#define IIO_IO_ERR_CLR IIO_IECLR /* IO error clear */ +#define IIO_IGFX_0 IIO_IGFX0 +#define IIO_IGFX_1 IIO_IGFX1 +#define IIO_IBCT_0 IIO_IBCT0 +#define IIO_IBCT_1 IIO_IBCT1 +#define IIO_IBLS_0 IIO_IBLS0 +#define IIO_IBLS_1 IIO_IBLS1 +#define IIO_IBSA_0 IIO_IBSA0 +#define IIO_IBSA_1 IIO_IBSA1 +#define IIO_IBDA_0 IIO_IBDA0 +#define IIO_IBDA_1 IIO_IBDA1 +#define IIO_IBNA_0 IIO_IBNA0 +#define IIO_IBNA_1 IIO_IBNA1 +#define IIO_IBIA_0 IIO_IBIA0 +#define IIO_IBIA_1 IIO_IBIA1 +#define IIO_IOPRB_0 IIO_IPRB0 + +#define IIO_PRTE_A(_x) (IIO_IPRTE0_A + (8 * (_x))) +#define IIO_PRTE_B(_x) (IIO_IPRTE0_B + (8 * (_x))) +#define IIO_NUM_PRTES 8 /* Total number of PRB table entries */ +#define IIO_WIDPRTE_A(x) IIO_PRTE_A(((x) - 8)) /* widget ID to its PRTE num */ +#define IIO_WIDPRTE_B(x) IIO_PRTE_B(((x) - 8)) /* widget ID to its PRTE num */ + +#define IIO_NUM_IPRBS (9) + +#define IIO_LLP_CSR_IS_UP 0x00002000 +#define IIO_LLP_CSR_LLP_STAT_MASK 0x00003000 +#define IIO_LLP_CSR_LLP_STAT_SHFT 12 + +#define IIO_LLP_CB_MAX 0xffff /* in ILLR CB_CNT, Max Check Bit errors */ +#define IIO_LLP_SN_MAX 0xffff /* in ILLR SN_CNT, Max Sequence Number errors */ + +/* key to IIO_PROTECT_OVRRD */ +#define IIO_PROTECT_OVRRD_KEY 0x53474972756c6573ull /* "SGIrules" */ + +/* BTE register names */ +#define IIO_BTE_STAT_0 IIO_IBLS_0 /* Also BTE length/status 0 */ +#define IIO_BTE_SRC_0 IIO_IBSA_0 /* Also BTE source address 0 */ +#define IIO_BTE_DEST_0 IIO_IBDA_0 /* Also BTE dest. address 0 */ +#define IIO_BTE_CTRL_0 IIO_IBCT_0 /* Also BTE control/terminate 0 */ +#define IIO_BTE_NOTIFY_0 IIO_IBNA_0 /* Also BTE notification 0 */ +#define IIO_BTE_INT_0 IIO_IBIA_0 /* Also BTE interrupt 0 */ +#define IIO_BTE_OFF_0 0 /* Base offset from BTE 0 regs. */ +#define IIO_BTE_OFF_1 (IIO_IBLS_1 - IIO_IBLS_0) /* Offset from base to BTE 1 */ + +/* BTE register offsets from base */ +#define BTEOFF_STAT 0 +#define BTEOFF_SRC (IIO_BTE_SRC_0 - IIO_BTE_STAT_0) +#define BTEOFF_DEST (IIO_BTE_DEST_0 - IIO_BTE_STAT_0) +#define BTEOFF_CTRL (IIO_BTE_CTRL_0 - IIO_BTE_STAT_0) +#define BTEOFF_NOTIFY (IIO_BTE_NOTIFY_0 - IIO_BTE_STAT_0) +#define BTEOFF_INT (IIO_BTE_INT_0 - IIO_BTE_STAT_0) + + +/* names used in shub diags */ +#define IIO_BASE_BTE0 IIO_IBLS_0 +#define IIO_BASE_BTE1 IIO_IBLS_1 + +/* + * Macro which takes the widget number, and returns the + * IO PRB address of that widget. + * value _x is expected to be a widget number in the range + * 0, 8 - 0xF + */ +#define IIO_IOPRB(_x) (IIO_IOPRB_0 + ( ( (_x) < HUB_WIDGET_ID_MIN ? \ + (_x) : \ + (_x) - (HUB_WIDGET_ID_MIN-1)) << 3) ) + + +/* GFX Flow Control Node/Widget Register */ +#define IIO_IGFX_W_NUM_BITS 4 /* size of widget num field */ +#define IIO_IGFX_W_NUM_MASK ((1<> IIO_WSTAT_TXRETRY_SHFT) & \ + IIO_WSTAT_TXRETRY_MASK) + +/* Number of II perf. counters we can multiplex at once */ + +#define IO_PERF_SETS 32 + +/* Bit for the widget in inbound access register */ +#define IIO_IIWA_WIDGET(_w) ((uint64_t)(1ULL << _w)) +/* Bit for the widget in outbound access register */ +#define IIO_IOWA_WIDGET(_w) ((uint64_t)(1ULL << _w)) + +/* NOTE: The following define assumes that we are going to get + * widget numbers from 8 thru F and the device numbers within + * widget from 0 thru 7. + */ +#define IIO_IIDEM_WIDGETDEV_MASK(w, d) ((uint64_t)(1ULL << (8 * ((w) - 8) + (d)))) + +/* IO Interrupt Destination Register */ +#define IIO_IIDSR_SENT_SHIFT 28 +#define IIO_IIDSR_SENT_MASK 0x30000000 +#define IIO_IIDSR_ENB_SHIFT 24 +#define IIO_IIDSR_ENB_MASK 0x01000000 +#define IIO_IIDSR_NODE_SHIFT 9 +#define IIO_IIDSR_NODE_MASK 0x000ff700 +#define IIO_IIDSR_PI_ID_SHIFT 8 +#define IIO_IIDSR_PI_ID_MASK 0x00000100 +#define IIO_IIDSR_LVL_SHIFT 0 +#define IIO_IIDSR_LVL_MASK 0x000000ff + +/* Xtalk timeout threshhold register (IIO_IXTT) */ +#define IXTT_RRSP_TO_SHFT 55 /* read response timeout */ +#define IXTT_RRSP_TO_MASK (0x1FULL << IXTT_RRSP_TO_SHFT) +#define IXTT_RRSP_PS_SHFT 32 /* read responsed TO prescalar */ +#define IXTT_RRSP_PS_MASK (0x7FFFFFULL << IXTT_RRSP_PS_SHFT) +#define IXTT_TAIL_TO_SHFT 0 /* tail timeout counter threshold */ +#define IXTT_TAIL_TO_MASK (0x3FFFFFFULL << IXTT_TAIL_TO_SHFT) + +/* + * The IO LLP control status register and widget control register + */ + +typedef union hubii_wcr_u { + uint64_t wcr_reg_value; + struct { + uint64_t wcr_widget_id: 4, /* LLP crossbar credit */ + wcr_tag_mode: 1, /* Tag mode */ + wcr_rsvd1: 8, /* Reserved */ + wcr_xbar_crd: 3, /* LLP crossbar credit */ + wcr_f_bad_pkt: 1, /* Force bad llp pkt enable */ + wcr_dir_con: 1, /* widget direct connect */ + wcr_e_thresh: 5, /* elasticity threshold */ + wcr_rsvd: 41; /* unused */ + } wcr_fields_s; +} hubii_wcr_t; + +#define iwcr_dir_con wcr_fields_s.wcr_dir_con + +/* The structures below are defined to extract and modify the ii +performance registers */ + +/* io_perf_sel allows the caller to specify what tests will be + performed */ + +typedef union io_perf_sel { + uint64_t perf_sel_reg; + struct { + uint64_t perf_ippr0 : 4, + perf_ippr1 : 4, + perf_icct : 8, + perf_rsvd : 48; + } perf_sel_bits; +} io_perf_sel_t; + +/* io_perf_cnt is to extract the count from the shub registers. Due to + hardware problems there is only one counter, not two. */ + +typedef union io_perf_cnt { + uint64_t perf_cnt; + struct { + uint64_t perf_cnt : 20, + perf_rsvd2 : 12, + perf_rsvd1 : 32; + } perf_cnt_bits; + +} io_perf_cnt_t; + +typedef union iprte_a { + uint64_t entry; + struct { + uint64_t i_rsvd_1 : 3; + uint64_t i_addr : 38; + uint64_t i_init : 3; + uint64_t i_source : 8; + uint64_t i_rsvd : 2; + uint64_t i_widget : 4; + uint64_t i_to_cnt : 5; + uint64_t i_vld : 1; + } iprte_fields; +} iprte_a_t; + +#endif /* _ASM_IA64_SN_SHUBIO_H */ + diff --git a/arch/ia64/sn/include/tio.h b/arch/ia64/sn/include/tio.h new file mode 100644 index 000000000..0139124dd --- /dev/null +++ b/arch/ia64/sn/include/tio.h @@ -0,0 +1,37 @@ +/* + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file "COPYING" in the main directory of this archive + * for more details. + * + * Copyright (C) 2000-2004 Silicon Graphics, Inc. All rights reserved. + */ + +#ifndef _ASM_IA64_SN_TIO_H +#define _ASM_IA64_SN_TIO_H + +#define TIO_MMR_ADDR_MOD + +#define TIO_NODE_ID TIO_MMR_ADDR_MOD(0x0000000090060e80) + +#define TIO_ITTE_BASE 0xb0008800 /* base of translation table entries */ +#define TIO_ITTE(bigwin) (TIO_ITTE_BASE + 8*(bigwin)) + +#define TIO_ITTE_OFFSET_BITS 8 /* size of offset field */ +#define TIO_ITTE_OFFSET_MASK ((1<> TIO_BWIN_SIZE_BITS) & \ + TIO_ITTE_OFFSET_MASK) << TIO_ITTE_OFFSET_SHIFT) | \ + (((widget) & TIO_ITTE_WIDGET_MASK) << TIO_ITTE_WIDGET_SHIFT)) | \ + (( (valid) & TIO_ITTE_VALID_MASK) << TIO_ITTE_VALID_SHIFT)) + +#endif /* _ASM_IA64_SN_TIO_H */ diff --git a/arch/ia64/sn/include/xtalk/hubdev.h b/arch/ia64/sn/include/xtalk/hubdev.h new file mode 100644 index 000000000..868e7ecae --- /dev/null +++ b/arch/ia64/sn/include/xtalk/hubdev.h @@ -0,0 +1,67 @@ +/* + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file "COPYING" in the main directory of this archive + * for more details. + * + * Copyright (C) 1992 - 1997, 2000-2004 Silicon Graphics, Inc. All rights reserved. + */ +#ifndef _ASM_IA64_SN_XTALK_HUBDEV_H +#define _ASM_IA64_SN_XTALK_HUBDEV_H + +#define HUB_WIDGET_ID_MAX 0xf +#define DEV_PER_WIDGET (2*2*8) +#define IIO_ITTE_WIDGET_BITS 4 /* size of widget field */ +#define IIO_ITTE_WIDGET_MASK ((1<= XBOW_PORT_8 && (wid) <= XBOW_PORT_F) + +/* whether to use upper or lower arbitration register, given source widget id */ +#define XBOW_ARB_IS_UPPER(wid) ((wid) >= XBOW_PORT_8 && (wid) <= XBOW_PORT_B) +#define XBOW_ARB_IS_LOWER(wid) ((wid) >= XBOW_PORT_C && (wid) <= XBOW_PORT_F) + +/* offset of arbitration register, given source widget id */ +#define XBOW_ARB_OFF(wid) (XBOW_ARB_IS_UPPER(wid) ? 0x1c : 0x24) + +#define XBOW_WID_ID WIDGET_ID +#define XBOW_WID_STAT WIDGET_STATUS +#define XBOW_WID_ERR_UPPER WIDGET_ERR_UPPER_ADDR +#define XBOW_WID_ERR_LOWER WIDGET_ERR_LOWER_ADDR +#define XBOW_WID_CONTROL WIDGET_CONTROL +#define XBOW_WID_REQ_TO WIDGET_REQ_TIMEOUT +#define XBOW_WID_INT_UPPER WIDGET_INTDEST_UPPER_ADDR +#define XBOW_WID_INT_LOWER WIDGET_INTDEST_LOWER_ADDR +#define XBOW_WID_ERR_CMDWORD WIDGET_ERR_CMD_WORD +#define XBOW_WID_LLP WIDGET_LLP_CFG +#define XBOW_WID_STAT_CLR WIDGET_TFLUSH +#define XBOW_WID_ARB_RELOAD 0x5c +#define XBOW_WID_PERF_CTR_A 0x64 +#define XBOW_WID_PERF_CTR_B 0x6c +#define XBOW_WID_NIC 0x74 + +/* Xbridge only */ +#define XBOW_W0_RST_FNC 0x00007C +#define XBOW_L8_RST_FNC 0x000084 +#define XBOW_L9_RST_FNC 0x00008c +#define XBOW_LA_RST_FNC 0x000094 +#define XBOW_LB_RST_FNC 0x00009c +#define XBOW_LC_RST_FNC 0x0000a4 +#define XBOW_LD_RST_FNC 0x0000ac +#define XBOW_LE_RST_FNC 0x0000b4 +#define XBOW_LF_RST_FNC 0x0000bc +#define XBOW_RESET_FENCE(x) ((x) > 7 && (x) < 16) ? \ + (XBOW_W0_RST_FNC + ((x) - 7) * 8) : \ + ((x) == 0) ? XBOW_W0_RST_FNC : 0 +#define XBOW_LOCK 0x0000c4 +#define XBOW_LOCK_CLR 0x0000cc +/* End of Xbridge only */ + +/* used only in ide, but defined here within the reserved portion */ +/* of the widget0 address space (before 0xf4) */ +#define XBOW_WID_UNDEF 0xe4 + +/* xbow link register set base, legal value for x is 0x8..0xf */ +#define XB_LINK_BASE 0x100 +#define XB_LINK_OFFSET 0x40 +#define XB_LINK_REG_BASE(x) (XB_LINK_BASE + ((x) & (MAX_XBOW_PORTS - 1)) * XB_LINK_OFFSET) + +#define XB_LINK_IBUF_FLUSH(x) (XB_LINK_REG_BASE(x) + 0x4) +#define XB_LINK_CTRL(x) (XB_LINK_REG_BASE(x) + 0xc) +#define XB_LINK_STATUS(x) (XB_LINK_REG_BASE(x) + 0x14) +#define XB_LINK_ARB_UPPER(x) (XB_LINK_REG_BASE(x) + 0x1c) +#define XB_LINK_ARB_LOWER(x) (XB_LINK_REG_BASE(x) + 0x24) +#define XB_LINK_STATUS_CLR(x) (XB_LINK_REG_BASE(x) + 0x2c) +#define XB_LINK_RESET(x) (XB_LINK_REG_BASE(x) + 0x34) +#define XB_LINK_AUX_STATUS(x) (XB_LINK_REG_BASE(x) + 0x3c) + +/* link_control(x) */ +#define XB_CTRL_LINKALIVE_IE 0x80000000 /* link comes alive */ + /* reserved: 0x40000000 */ +#define XB_CTRL_PERF_CTR_MODE_MSK 0x30000000 /* perf counter mode */ +#define XB_CTRL_IBUF_LEVEL_MSK 0x0e000000 /* input packet buffer level */ +#define XB_CTRL_8BIT_MODE 0x01000000 /* force link into 8 bit mode */ +#define XB_CTRL_BAD_LLP_PKT 0x00800000 /* force bad LLP packet */ +#define XB_CTRL_WIDGET_CR_MSK 0x007c0000 /* LLP widget credit mask */ +#define XB_CTRL_WIDGET_CR_SHFT 18 /* LLP widget credit shift */ +#define XB_CTRL_ILLEGAL_DST_IE 0x00020000 /* illegal destination */ +#define XB_CTRL_OALLOC_IBUF_IE 0x00010000 /* overallocated input buffer */ + /* reserved: 0x0000fe00 */ +#define XB_CTRL_BNDWDTH_ALLOC_IE 0x00000100 /* bandwidth alloc */ +#define XB_CTRL_RCV_CNT_OFLOW_IE 0x00000080 /* rcv retry overflow */ +#define XB_CTRL_XMT_CNT_OFLOW_IE 0x00000040 /* xmt retry overflow */ +#define XB_CTRL_XMT_MAX_RTRY_IE 0x00000020 /* max transmit retry */ +#define XB_CTRL_RCV_IE 0x00000010 /* receive */ +#define XB_CTRL_XMT_RTRY_IE 0x00000008 /* transmit retry */ + /* reserved: 0x00000004 */ +#define XB_CTRL_MAXREQ_TOUT_IE 0x00000002 /* maximum request timeout */ +#define XB_CTRL_SRC_TOUT_IE 0x00000001 /* source timeout */ + +/* link_status(x) */ +#define XB_STAT_LINKALIVE XB_CTRL_LINKALIVE_IE + /* reserved: 0x7ff80000 */ +#define XB_STAT_MULTI_ERR 0x00040000 /* multi error */ +#define XB_STAT_ILLEGAL_DST_ERR XB_CTRL_ILLEGAL_DST_IE +#define XB_STAT_OALLOC_IBUF_ERR XB_CTRL_OALLOC_IBUF_IE +#define XB_STAT_BNDWDTH_ALLOC_ID_MSK 0x0000ff00 /* port bitmask */ +#define XB_STAT_RCV_CNT_OFLOW_ERR XB_CTRL_RCV_CNT_OFLOW_IE +#define XB_STAT_XMT_CNT_OFLOW_ERR XB_CTRL_XMT_CNT_OFLOW_IE +#define XB_STAT_XMT_MAX_RTRY_ERR XB_CTRL_XMT_MAX_RTRY_IE +#define XB_STAT_RCV_ERR XB_CTRL_RCV_IE +#define XB_STAT_XMT_RTRY_ERR XB_CTRL_XMT_RTRY_IE + /* reserved: 0x00000004 */ +#define XB_STAT_MAXREQ_TOUT_ERR XB_CTRL_MAXREQ_TOUT_IE +#define XB_STAT_SRC_TOUT_ERR XB_CTRL_SRC_TOUT_IE + +/* link_aux_status(x) */ +#define XB_AUX_STAT_RCV_CNT 0xff000000 +#define XB_AUX_STAT_XMT_CNT 0x00ff0000 +#define XB_AUX_STAT_TOUT_DST 0x0000ff00 +#define XB_AUX_LINKFAIL_RST_BAD 0x00000040 +#define XB_AUX_STAT_PRESENT 0x00000020 +#define XB_AUX_STAT_PORT_WIDTH 0x00000010 + /* reserved: 0x0000000f */ + +/* + * link_arb_upper/link_arb_lower(x), (reg) should be the link_arb_upper + * register if (x) is 0x8..0xb, link_arb_lower if (x) is 0xc..0xf + */ +#define XB_ARB_GBR_MSK 0x1f +#define XB_ARB_RR_MSK 0x7 +#define XB_ARB_GBR_SHFT(x) (((x) & 0x3) * 8) +#define XB_ARB_RR_SHFT(x) (((x) & 0x3) * 8 + 5) +#define XB_ARB_GBR_CNT(reg,x) ((reg) >> XB_ARB_GBR_SHFT(x) & XB_ARB_GBR_MSK) +#define XB_ARB_RR_CNT(reg,x) ((reg) >> XB_ARB_RR_SHFT(x) & XB_ARB_RR_MSK) + +/* XBOW_WID_STAT */ +#define XB_WID_STAT_LINK_INTR_SHFT (24) +#define XB_WID_STAT_LINK_INTR_MASK (0xFF << XB_WID_STAT_LINK_INTR_SHFT) +#define XB_WID_STAT_LINK_INTR(x) (0x1 << (((x)&7) + XB_WID_STAT_LINK_INTR_SHFT)) +#define XB_WID_STAT_WIDGET0_INTR 0x00800000 +#define XB_WID_STAT_SRCID_MASK 0x000003c0 /* Xbridge only */ +#define XB_WID_STAT_REG_ACC_ERR 0x00000020 +#define XB_WID_STAT_RECV_TOUT 0x00000010 /* Xbridge only */ +#define XB_WID_STAT_ARB_TOUT 0x00000008 /* Xbridge only */ +#define XB_WID_STAT_XTALK_ERR 0x00000004 +#define XB_WID_STAT_DST_TOUT 0x00000002 /* Xbridge only */ +#define XB_WID_STAT_MULTI_ERR 0x00000001 + +#define XB_WID_STAT_SRCID_SHFT 6 + +/* XBOW_WID_CONTROL */ +#define XB_WID_CTRL_REG_ACC_IE XB_WID_STAT_REG_ACC_ERR +#define XB_WID_CTRL_RECV_TOUT XB_WID_STAT_RECV_TOUT +#define XB_WID_CTRL_ARB_TOUT XB_WID_STAT_ARB_TOUT +#define XB_WID_CTRL_XTALK_IE XB_WID_STAT_XTALK_ERR + +/* XBOW_WID_INT_UPPER */ +/* defined in xwidget.h for WIDGET_INTDEST_UPPER_ADDR */ + +/* XBOW WIDGET part number, in the ID register */ +#define XBOW_WIDGET_PART_NUM 0x0 /* crossbow */ +#define XXBOW_WIDGET_PART_NUM 0xd000 /* Xbridge */ +#define XBOW_WIDGET_MFGR_NUM 0x0 +#define XXBOW_WIDGET_MFGR_NUM 0x0 +#define PXBOW_WIDGET_PART_NUM 0xd100 /* PIC */ + +#define XBOW_REV_1_0 0x1 /* xbow rev 1.0 is "1" */ +#define XBOW_REV_1_1 0x2 /* xbow rev 1.1 is "2" */ +#define XBOW_REV_1_2 0x3 /* xbow rev 1.2 is "3" */ +#define XBOW_REV_1_3 0x4 /* xbow rev 1.3 is "4" */ +#define XBOW_REV_2_0 0x5 /* xbow rev 2.0 is "5" */ + +#define XXBOW_PART_REV_1_0 (XXBOW_WIDGET_PART_NUM << 4 | 0x1 ) +#define XXBOW_PART_REV_2_0 (XXBOW_WIDGET_PART_NUM << 4 | 0x2 ) + +/* XBOW_WID_ARB_RELOAD */ +#define XBOW_WID_ARB_RELOAD_INT 0x3f /* GBR reload interval */ + +#define IS_XBRIDGE_XBOW(wid) \ + (XWIDGET_PART_NUM(wid) == XXBOW_WIDGET_PART_NUM && \ + XWIDGET_MFG_NUM(wid) == XXBOW_WIDGET_MFGR_NUM) + +#define IS_PIC_XBOW(wid) \ + (XWIDGET_PART_NUM(wid) == PXBOW_WIDGET_PART_NUM && \ + XWIDGET_MFG_NUM(wid) == XXBOW_WIDGET_MFGR_NUM) + +#define XBOW_WAR_ENABLED(pv, widid) ((1 << XWIDGET_REV_NUM(widid)) & pv) + +#endif /* _ASM_IA64_SN_XTALK_XBOW_H */ diff --git a/arch/ia64/sn/include/xtalk/xwidgetdev.h b/arch/ia64/sn/include/xtalk/xwidgetdev.h new file mode 100644 index 000000000..c5f4bc5cc --- /dev/null +++ b/arch/ia64/sn/include/xtalk/xwidgetdev.h @@ -0,0 +1,70 @@ +/* + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file "COPYING" in the main directory of this archive + * for more details. + * + * Copyright (C) 1992-1997,2000-2003 Silicon Graphics, Inc. All Rights Reserved. + */ +#ifndef _ASM_IA64_SN_XTALK_XWIDGET_H +#define _ASM_IA64_SN_XTALK_XWIDGET_H + +/* WIDGET_ID */ +#define WIDGET_REV_NUM 0xf0000000 +#define WIDGET_PART_NUM 0x0ffff000 +#define WIDGET_MFG_NUM 0x00000ffe +#define WIDGET_REV_NUM_SHFT 28 +#define WIDGET_PART_NUM_SHFT 12 +#define WIDGET_MFG_NUM_SHFT 1 + +#define XWIDGET_PART_NUM(widgetid) (((widgetid) & WIDGET_PART_NUM) >> WIDGET_PART_NUM_SHFT) +#define XWIDGET_REV_NUM(widgetid) (((widgetid) & WIDGET_REV_NUM) >> WIDGET_REV_NUM_SHFT) +#define XWIDGET_MFG_NUM(widgetid) (((widgetid) & WIDGET_MFG_NUM) >> WIDGET_MFG_NUM_SHFT) +#define XWIDGET_PART_REV_NUM(widgetid) ((XWIDGET_PART_NUM(widgetid) << 4) | \ + XWIDGET_REV_NUM(widgetid)) +#define XWIDGET_PART_REV_NUM_REV(partrev) (partrev & 0xf) + +/* widget configuration registers */ +struct widget_cfg{ + uint32_t w_id; /* 0x04 */ + uint32_t w_pad_0; /* 0x00 */ + uint32_t w_status; /* 0x0c */ + uint32_t w_pad_1; /* 0x08 */ + uint32_t w_err_upper_addr; /* 0x14 */ + uint32_t w_pad_2; /* 0x10 */ + uint32_t w_err_lower_addr; /* 0x1c */ + uint32_t w_pad_3; /* 0x18 */ + uint32_t w_control; /* 0x24 */ + uint32_t w_pad_4; /* 0x20 */ + uint32_t w_req_timeout; /* 0x2c */ + uint32_t w_pad_5; /* 0x28 */ + uint32_t w_intdest_upper_addr; /* 0x34 */ + uint32_t w_pad_6; /* 0x30 */ + uint32_t w_intdest_lower_addr; /* 0x3c */ + uint32_t w_pad_7; /* 0x38 */ + uint32_t w_err_cmd_word; /* 0x44 */ + uint32_t w_pad_8; /* 0x40 */ + uint32_t w_llp_cfg; /* 0x4c */ + uint32_t w_pad_9; /* 0x48 */ + uint32_t w_tflush; /* 0x54 */ + uint32_t w_pad_10; /* 0x50 */ +}; + +/* + * Crosstalk Widget Hardware Identification, as defined in the Crosstalk spec. + */ +struct xwidget_hwid{ + int mfg_num; + int rev_num; + int part_num; +}; + +struct xwidget_info{ + + struct xwidget_hwid xwi_hwid; /* Widget Identification */ + char xwi_masterxid; /* Hub's Widget Port Number */ + void *xwi_hubinfo; /* Hub's provider private info */ + uint64_t *xwi_hub_provider; /* prom provider functions */ + void *xwi_vertex; +}; + +#endif /* _ASM_IA64_SN_XTALK_XWIDGET_H */ diff --git a/arch/ia64/sn/kernel/bte_error.c b/arch/ia64/sn/kernel/bte_error.c new file mode 100644 index 000000000..3591c2cf1 --- /dev/null +++ b/arch/ia64/sn/kernel/bte_error.c @@ -0,0 +1,188 @@ +/* + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file "COPYING" in the main directory of this archive + * for more details. + * + * Copyright (c) 2000-2004 Silicon Graphics, Inc. All Rights Reserved. + */ + +#include +#include +#include "ioerror.h" +#include +#include "shubio.h" +#include +#include "xtalk/xwidgetdev.h" +#include "xtalk/hubdev.h" +#include + +/* + * Bte error handling is done in two parts. The first captures + * any crb related errors. Since there can be multiple crbs per + * interface and multiple interfaces active, we need to wait until + * all active crbs are completed. This is the first job of the + * second part error handler. When all bte related CRBs are cleanly + * completed, it resets the interfaces and gets them ready for new + * transfers to be queued. + */ + +void bte_error_handler(unsigned long); + +/* + * Wait until all BTE related CRBs are completed + * and then reset the interfaces. + */ +void bte_error_handler(unsigned long _nodepda) +{ + struct nodepda_s *err_nodepda = (struct nodepda_s *)_nodepda; + spinlock_t *recovery_lock = &err_nodepda->bte_recovery_lock; + struct timer_list *recovery_timer = &err_nodepda->bte_recovery_timer; + nasid_t nasid; + int i; + int valid_crbs; + unsigned long irq_flags; + volatile u64 *notify; + bte_result_t bh_error; + ii_imem_u_t imem; /* II IMEM Register */ + ii_icrb0_d_u_t icrbd; /* II CRB Register D */ + ii_ibcr_u_t ibcr; + ii_icmr_u_t icmr; + + BTE_PRINTK(("bte_error_handler(%p) - %d\n", err_nodepda, + smp_processor_id())); + + spin_lock_irqsave(recovery_lock, irq_flags); + + if ((err_nodepda->bte_if[0].bh_error == BTE_SUCCESS) && + (err_nodepda->bte_if[1].bh_error == BTE_SUCCESS)) { + BTE_PRINTK(("eh:%p:%d Nothing to do.\n", err_nodepda, + smp_processor_id())); + spin_unlock_irqrestore(recovery_lock, irq_flags); + return; + } + /* + * Lock all interfaces on this node to prevent new transfers + * from being queued. + */ + for (i = 0; i < BTES_PER_NODE; i++) { + if (err_nodepda->bte_if[i].cleanup_active) { + continue; + } + spin_lock(&err_nodepda->bte_if[i].spinlock); + BTE_PRINTK(("eh:%p:%d locked %d\n", err_nodepda, + smp_processor_id(), i)); + err_nodepda->bte_if[i].cleanup_active = 1; + } + + /* Determine information about our hub */ + nasid = cnodeid_to_nasid(err_nodepda->bte_if[0].bte_cnode); + + /* + * A BTE transfer can use multiple CRBs. We need to make sure + * that all the BTE CRBs are complete (or timed out) before + * attempting to clean up the error. Resetting the BTE while + * there are still BTE CRBs active will hang the BTE. + * We should look at all the CRBs to see if they are allocated + * to the BTE and see if they are still active. When none + * are active, we can continue with the cleanup. + * + * We also want to make sure that the local NI port is up. + * When a router resets the NI port can go down, while it + * goes through the LLP handshake, but then comes back up. + */ + icmr.ii_icmr_regval = REMOTE_HUB_L(nasid, IIO_ICMR); + if (icmr.ii_icmr_fld_s.i_crb_mark != 0) { + /* + * There are errors which still need to be cleaned up by + * hubiio_crb_error_handler + */ + mod_timer(recovery_timer, HZ * 5); + BTE_PRINTK(("eh:%p:%d Marked Giving up\n", err_nodepda, + smp_processor_id())); + spin_unlock_irqrestore(recovery_lock, irq_flags); + return; + } + if (icmr.ii_icmr_fld_s.i_crb_vld != 0) { + + valid_crbs = icmr.ii_icmr_fld_s.i_crb_vld; + + for (i = 0; i < IIO_NUM_CRBS; i++) { + if (!((1 << i) & valid_crbs)) { + /* This crb was not marked as valid, ignore */ + continue; + } + icrbd.ii_icrb0_d_regval = + REMOTE_HUB_L(nasid, IIO_ICRB_D(i)); + if (icrbd.d_bteop) { + mod_timer(recovery_timer, HZ * 5); + BTE_PRINTK(("eh:%p:%d Valid %d, Giving up\n", + err_nodepda, smp_processor_id(), + i)); + spin_unlock_irqrestore(recovery_lock, + irq_flags); + return; + } + } + } + + BTE_PRINTK(("eh:%p:%d Cleaning up\n", err_nodepda, smp_processor_id())); + /* Reenable both bte interfaces */ + imem.ii_imem_regval = REMOTE_HUB_L(nasid, IIO_IMEM); + imem.ii_imem_fld_s.i_b0_esd = imem.ii_imem_fld_s.i_b1_esd = 1; + REMOTE_HUB_S(nasid, IIO_IMEM, imem.ii_imem_regval); + + /* Reinitialize both BTE state machines. */ + ibcr.ii_ibcr_regval = REMOTE_HUB_L(nasid, IIO_IBCR); + ibcr.ii_ibcr_fld_s.i_soft_reset = 1; + REMOTE_HUB_S(nasid, IIO_IBCR, ibcr.ii_ibcr_regval); + + for (i = 0; i < BTES_PER_NODE; i++) { + bh_error = err_nodepda->bte_if[i].bh_error; + if (bh_error != BTE_SUCCESS) { + /* There is an error which needs to be notified */ + notify = err_nodepda->bte_if[i].most_rcnt_na; + BTE_PRINTK(("cnode %d bte %d error=0x%lx\n", + err_nodepda->bte_if[i].bte_cnode, + err_nodepda->bte_if[i].bte_num, + IBLS_ERROR | (u64) bh_error)); + *notify = IBLS_ERROR | bh_error; + err_nodepda->bte_if[i].bh_error = BTE_SUCCESS; + } + + err_nodepda->bte_if[i].cleanup_active = 0; + BTE_PRINTK(("eh:%p:%d Unlocked %d\n", err_nodepda, + smp_processor_id(), i)); + spin_unlock(&pda->cpu_bte_if[i]->spinlock); + } + + del_timer(recovery_timer); + + spin_unlock_irqrestore(recovery_lock, irq_flags); +} + +/* + * First part error handler. This is called whenever any error CRB interrupt + * is generated by the II. + */ +void +bte_crb_error_handler(cnodeid_t cnode, int btenum, + int crbnum, ioerror_t * ioe, int bteop) +{ + struct bteinfo_s *bte; + + + bte = &(NODEPDA(cnode)->bte_if[btenum]); + + /* + * The caller has already figured out the error type, we save that + * in the bte handle structure for the thread excercising the + * interface to consume. + */ + bte->bh_error = ioe->ie_errortype + BTEFAIL_OFFSET; + bte->bte_error_count++; + + BTE_PRINTK(("Got an error on cnode %d bte %d: HW error type 0x%x\n", + bte->bte_cnode, bte->bte_num, ioe->ie_errortype)); + bte_error_handler((unsigned long) NODEPDA(cnode)); +} + diff --git a/arch/ia64/sn/kernel/huberror.c b/arch/ia64/sn/kernel/huberror.c new file mode 100644 index 000000000..118060460 --- /dev/null +++ b/arch/ia64/sn/kernel/huberror.c @@ -0,0 +1,201 @@ +/* + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file "COPYING" in the main directory of this archive + * for more details. + * + * Copyright (C) 1992 - 1997, 2000,2002-2004 Silicon Graphics, Inc. All rights reserved. + */ + +#include +#include +#include +#include +#include +#include "ioerror.h" +#include +#include "shubio.h" +#include +#include "xtalk/xwidgetdev.h" +#include "xtalk/hubdev.h" +#include + +void hubiio_crb_error_handler(struct hubdev_info *hubdev_info); +extern void bte_crb_error_handler(cnodeid_t, int, int, ioerror_t *, + int); +static irqreturn_t hub_eint_handler(int irq, void *arg, struct pt_regs *ep) +{ + struct hubdev_info *hubdev_info; + struct ia64_sal_retval ret_stuff; + nasid_t nasid; + + ret_stuff.status = 0; + ret_stuff.v0 = 0; + hubdev_info = (struct hubdev_info *)arg; + nasid = hubdev_info->hdi_nasid; + SAL_CALL_NOLOCK(ret_stuff, SN_SAL_HUB_ERROR_INTERRUPT, + (u64) nasid, 0, 0, 0, 0, 0, 0); + + if ((int)ret_stuff.v0) + panic("hubii_eint_handler(): Fatal TIO Error"); + + if (!(nasid & 1)) /* Not a TIO, handle CRB errors */ + (void)hubiio_crb_error_handler(hubdev_info); + + return IRQ_HANDLED; +} + +/* + * Free the hub CRB "crbnum" which encountered an error. + * Assumption is, error handling was successfully done, + * and we now want to return the CRB back to Hub for normal usage. + * + * In order to free the CRB, all that's needed is to de-allocate it + * + * Assumption: + * No other processor is mucking around with the hub control register. + * So, upper layer has to single thread this. + */ +void hubiio_crb_free(struct hubdev_info *hubdev_info, int crbnum) +{ + ii_icrb0_b_u_t icrbb; + + /* + * The hardware does NOT clear the mark bit, so it must get cleared + * here to be sure the error is not processed twice. + */ + icrbb.ii_icrb0_b_regval = REMOTE_HUB_L(hubdev_info->hdi_nasid, + IIO_ICRB_B(crbnum)); + icrbb.b_mark = 0; + REMOTE_HUB_S(hubdev_info->hdi_nasid, IIO_ICRB_B(crbnum), + icrbb.ii_icrb0_b_regval); + /* + * Deallocate the register wait till hub indicates it's done. + */ + REMOTE_HUB_S(hubdev_info->hdi_nasid, IIO_ICDR, (IIO_ICDR_PND | crbnum)); + while (REMOTE_HUB_L(hubdev_info->hdi_nasid, IIO_ICDR) & IIO_ICDR_PND) + udelay(1); + +} + +/* + * hubiio_crb_error_handler + * + * This routine gets invoked when a hub gets an error + * interrupt. So, the routine is running in interrupt context + * at error interrupt level. + * Action: + * It's responsible for identifying ALL the CRBs that are marked + * with error, and process them. + * + * If you find the CRB that's marked with error, map this to the + * reason it caused error, and invoke appropriate error handler. + * + * XXX Be aware of the information in the context register. + * + * NOTE: + * Use REMOTE_HUB_* macro instead of LOCAL_HUB_* so that the interrupt + * handler can be run on any node. (not necessarily the node + * corresponding to the hub that encountered error). + */ + +void hubiio_crb_error_handler(struct hubdev_info *hubdev_info) +{ + nasid_t nasid; + ii_icrb0_a_u_t icrba; /* II CRB Register A */ + ii_icrb0_b_u_t icrbb; /* II CRB Register B */ + ii_icrb0_c_u_t icrbc; /* II CRB Register C */ + ii_icrb0_d_u_t icrbd; /* II CRB Register D */ + ii_icrb0_e_u_t icrbe; /* II CRB Register D */ + int i; + int num_errors = 0; /* Num of errors handled */ + ioerror_t ioerror; + + nasid = hubdev_info->hdi_nasid; + + /* + * XXX - Add locking for any recovery actions + */ + /* + * Scan through all CRBs in the Hub, and handle the errors + * in any of the CRBs marked. + */ + for (i = 0; i < IIO_NUM_CRBS; i++) { + /* Check this crb entry to see if it is in error. */ + icrbb.ii_icrb0_b_regval = REMOTE_HUB_L(nasid, IIO_ICRB_B(i)); + + if (icrbb.b_mark == 0) { + continue; + } + + icrba.ii_icrb0_a_regval = REMOTE_HUB_L(nasid, IIO_ICRB_A(i)); + + IOERROR_INIT(&ioerror); + + /* read other CRB error registers. */ + icrbc.ii_icrb0_c_regval = REMOTE_HUB_L(nasid, IIO_ICRB_C(i)); + icrbd.ii_icrb0_d_regval = REMOTE_HUB_L(nasid, IIO_ICRB_D(i)); + icrbe.ii_icrb0_e_regval = REMOTE_HUB_L(nasid, IIO_ICRB_E(i)); + + IOERROR_SETVALUE(&ioerror, errortype, icrbb.b_ecode); + + /* Check if this error is due to BTE operation, + * and handle it separately. + */ + if (icrbd.d_bteop || + ((icrbb.b_initiator == IIO_ICRB_INIT_BTE0 || + icrbb.b_initiator == IIO_ICRB_INIT_BTE1) && + (icrbb.b_imsgtype == IIO_ICRB_IMSGT_BTE || + icrbb.b_imsgtype == IIO_ICRB_IMSGT_SN1NET))) { + + int bte_num; + + if (icrbd.d_bteop) + bte_num = icrbc.c_btenum; + else /* b_initiator bit 2 gives BTE number */ + bte_num = (icrbb.b_initiator & 0x4) >> 2; + + hubiio_crb_free(hubdev_info, i); + + bte_crb_error_handler(nasid_to_cnodeid(nasid), bte_num, + i, &ioerror, icrbd.d_bteop); + num_errors++; + continue; + } + } +} + +/* + * Function : hub_error_init + * Purpose : initialize the error handling requirements for a given hub. + * Parameters : cnode, the compact nodeid. + * Assumptions : Called only once per hub, either by a local cpu. Or by a + * remote cpu, when this hub is headless.(cpuless) + * Returns : None + */ +void hub_error_init(struct hubdev_info *hubdev_info) +{ + if (request_irq(SGI_II_ERROR, (void *)hub_eint_handler, SA_SHIRQ, + "SN_hub_error", (void *)hubdev_info)) + printk("hub_error_init: Failed to request_irq for 0x%p\n", + hubdev_info); + return; +} + + +/* + * Function : ice_error_init + * Purpose : initialize the error handling requirements for a given tio. + * Parameters : cnode, the compact nodeid. + * Assumptions : Called only once per tio. + * Returns : None + */ +void ice_error_init(struct hubdev_info *hubdev_info) +{ + if (request_irq + (SGI_TIO_ERROR, (void *)hub_eint_handler, SA_SHIRQ, "SN_TIO_error", + (void *)hubdev_info)) + printk("ice_error_init: request_irq() error hubdev_info 0x%p\n", + hubdev_info); + return; +} + diff --git a/arch/ia64/sn/kernel/io_init.c b/arch/ia64/sn/kernel/io_init.c new file mode 100644 index 000000000..abb0f4283 --- /dev/null +++ b/arch/ia64/sn/kernel/io_init.c @@ -0,0 +1,410 @@ +/* + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file "COPYING" in the main directory of this archive + * for more details. + * + * Copyright (C) 1992 - 1997, 2000-2004 Silicon Graphics, Inc. All rights reserved. + */ + +#include +#include +#include +#include +#include "pci/pcibus_provider_defs.h" +#include "pci/pcidev.h" +#include "pci/pcibr_provider.h" +#include "xtalk/xwidgetdev.h" +#include +#include "xtalk/hubdev.h" +#include +#include + +char master_baseio_wid; +nasid_t master_nasid = INVALID_NASID; /* Partition Master */ + +struct slab_info { + struct hubdev_info hubdev; +}; + +struct brick { + moduleid_t id; /* Module ID of this module */ + struct slab_info slab_info[MAX_SLABS + 1]; +}; + +int sn_ioif_inited = 0; /* SN I/O infrastructure initialized? */ + +/* + * Retrieve the DMA Flush List given nasid. This list is needed + * to implement the WAR - Flush DMA data on PIO Reads. + */ +static inline uint64_t +sal_get_widget_dmaflush_list(u64 nasid, u64 widget_num, u64 address) +{ + + struct ia64_sal_retval ret_stuff; + ret_stuff.status = 0; + ret_stuff.v0 = 0; + + SAL_CALL_NOLOCK(ret_stuff, + (u64) SN_SAL_IOIF_GET_WIDGET_DMAFLUSH_LIST, + (u64) nasid, (u64) widget_num, (u64) address, 0, 0, 0, + 0); + return ret_stuff.v0; + +} + +/* + * Retrieve the hub device info structure for the given nasid. + */ +static inline uint64_t sal_get_hubdev_info(u64 handle, u64 address) +{ + + struct ia64_sal_retval ret_stuff; + ret_stuff.status = 0; + ret_stuff.v0 = 0; + + SAL_CALL_NOLOCK(ret_stuff, + (u64) SN_SAL_IOIF_GET_HUBDEV_INFO, + (u64) handle, (u64) address, 0, 0, 0, 0, 0); + return ret_stuff.v0; +} + +/* + * Retrieve the pci bus information given the bus number. + */ +static inline uint64_t sal_get_pcibus_info(u64 segment, u64 busnum, u64 address) +{ + + struct ia64_sal_retval ret_stuff; + ret_stuff.status = 0; + ret_stuff.v0 = 0; + + SAL_CALL_NOLOCK(ret_stuff, + (u64) SN_SAL_IOIF_GET_PCIBUS_INFO, + (u64) segment, (u64) busnum, (u64) address, 0, 0, 0, 0); + return ret_stuff.v0; +} + +/* + * Retrieve the pci device information given the bus and device|function number. + */ +static inline uint64_t +sal_get_pcidev_info(u64 segment, u64 bus_number, u64 devfn, u64 pci_dev, + u64 sn_irq_info) +{ + struct ia64_sal_retval ret_stuff; + ret_stuff.status = 0; + ret_stuff.v0 = 0; + + SAL_CALL_NOLOCK(ret_stuff, + (u64) SN_SAL_IOIF_GET_PCIDEV_INFO, + (u64) segment, (u64) bus_number, (u64) devfn, + (u64) pci_dev, + sn_irq_info, 0, 0); + return ret_stuff.v0; +} + +/* + * sn_alloc_pci_sysdata() - This routine allocates a pci controller + * which is expected as the pci_dev and pci_bus sysdata by the Linux + * PCI infrastructure. + */ +static inline struct pci_controller *sn_alloc_pci_sysdata(void) +{ + struct pci_controller *pci_sysdata; + + pci_sysdata = kmalloc(sizeof(*pci_sysdata), GFP_KERNEL); + if (!pci_sysdata) + BUG(); + + memset(pci_sysdata, 0, sizeof(*pci_sysdata)); + return pci_sysdata; +} + +/* + * sn_fixup_ionodes() - This routine initializes the HUB data strcuture for + * each node in the system. + */ +static void sn_fixup_ionodes(void) +{ + + struct sn_flush_device_list *sn_flush_device_list; + struct hubdev_info *hubdev; + uint64_t status; + uint64_t nasid; + int i, widget; + + for (i = 0; i < numionodes; i++) { + hubdev = (struct hubdev_info *)(NODEPDA(i)->pdinfo); + nasid = cnodeid_to_nasid(i); + status = sal_get_hubdev_info(nasid, (uint64_t) __pa(hubdev)); + if (status) + continue; + + for (widget = 0; widget <= HUB_WIDGET_ID_MAX; widget++) + hubdev->hdi_xwidget_info[widget].xwi_hubinfo = hubdev; + + if (!hubdev->hdi_flush_nasid_list.widget_p) + continue; + + hubdev->hdi_flush_nasid_list.widget_p = + kmalloc((HUB_WIDGET_ID_MAX + 1) * + sizeof(struct sn_flush_device_list *), GFP_KERNEL); + + memset(hubdev->hdi_flush_nasid_list.widget_p, 0x0, + (HUB_WIDGET_ID_MAX + 1) * + sizeof(struct sn_flush_device_list *)); + + for (widget = 0; widget <= HUB_WIDGET_ID_MAX; widget++) { + sn_flush_device_list = kmalloc(DEV_PER_WIDGET * + sizeof(struct + sn_flush_device_list), + GFP_KERNEL); + memset(sn_flush_device_list, 0x0, + DEV_PER_WIDGET * + sizeof(struct sn_flush_device_list)); + + status = + sal_get_widget_dmaflush_list(nasid, widget, + (uint64_t) + __pa + (sn_flush_device_list)); + if (status) { + kfree(sn_flush_device_list); + continue; + } + + hubdev->hdi_flush_nasid_list.widget_p[widget] = + sn_flush_device_list; + } + + if (!(i & 1)) + hub_error_init(hubdev); + else + ice_error_init(hubdev); + } + +} + +/* + * sn_pci_fixup_slot() - This routine sets up a slot's resources + * consistent with the Linux PCI abstraction layer. Resources acquired + * from our PCI provider include PIO maps to BAR space and interrupt + * objects. + */ +static void sn_pci_fixup_slot(struct pci_dev *dev) +{ + int idx; + int segment = 0; + uint64_t size; + struct sn_irq_info *sn_irq_info; + struct pci_dev *host_pci_dev; + int status = 0; + + SN_PCIDEV_INFO(dev) = kmalloc(sizeof(struct pcidev_info), GFP_KERNEL); + if (SN_PCIDEV_INFO(dev) <= 0) + BUG(); /* Cannot afford to run out of memory */ + memset(SN_PCIDEV_INFO(dev), 0, sizeof(struct pcidev_info)); + + sn_irq_info = kmalloc(sizeof(struct sn_irq_info), GFP_KERNEL); + if (sn_irq_info <= 0) + BUG(); /* Cannot afford to run out of memory */ + memset(sn_irq_info, 0, sizeof(struct sn_irq_info)); + + /* Call to retrieve pci device information needed by kernel. */ + status = sal_get_pcidev_info((u64) segment, (u64) dev->bus->number, + dev->devfn, + (u64) __pa(SN_PCIDEV_INFO(dev)), + (u64) __pa(sn_irq_info)); + if (status) + BUG(); /* Cannot get platform pci device information information */ + + /* Copy over PIO Mapped Addresses */ + for (idx = 0; idx <= PCI_ROM_RESOURCE; idx++) { + unsigned long start, end, addr; + + if (!SN_PCIDEV_INFO(dev)->pdi_pio_mapped_addr[idx]) + continue; + + start = dev->resource[idx].start; + end = dev->resource[idx].end; + size = end - start; + addr = SN_PCIDEV_INFO(dev)->pdi_pio_mapped_addr[idx]; + addr = ((addr << 4) >> 4) | __IA64_UNCACHED_OFFSET; + dev->resource[idx].start = addr; + dev->resource[idx].end = addr + size; + if (dev->resource[idx].flags & IORESOURCE_IO) + dev->resource[idx].parent = &ioport_resource; + else + dev->resource[idx].parent = &iomem_resource; + } + + /* set up host bus linkages */ + host_pci_dev = + pci_find_slot(SN_PCIDEV_INFO(dev)->pdi_slot_host_handle >> 32, + SN_PCIDEV_INFO(dev)-> + pdi_slot_host_handle & 0xffffffff); + SN_PCIDEV_INFO(dev)->pdi_host_pcidev_info = + SN_PCIDEV_INFO(host_pci_dev); + SN_PCIDEV_INFO(dev)->pdi_linux_pcidev = dev; + SN_PCIDEV_INFO(dev)->pdi_pcibus_info = SN_PCIBUS_BUSSOFT(dev->bus); + + /* Only set up IRQ stuff if this device has a host bus context */ + if (SN_PCIDEV_BUSSOFT(dev) && sn_irq_info->irq_irq) { + SN_PCIDEV_INFO(dev)->pdi_sn_irq_info = sn_irq_info; + dev->irq = SN_PCIDEV_INFO(dev)->pdi_sn_irq_info->irq_irq; + sn_irq_fixup(dev, sn_irq_info); + } +} + +/* + * sn_pci_controller_fixup() - This routine sets up a bus's resources + * consistent with the Linux PCI abstraction layer. + */ +static void sn_pci_controller_fixup(int segment, int busnum) +{ + int status = 0; + int nasid, cnode; + struct pci_bus *bus; + struct pci_controller *controller; + struct pcibus_bussoft *prom_bussoft_ptr; + struct hubdev_info *hubdev_info; + void *provider_soft; + + status = + sal_get_pcibus_info((u64) segment, (u64) busnum, + (u64) ia64_tpa(&prom_bussoft_ptr)); + if (status > 0) { + return; /* bus # does not exist */ + } + + prom_bussoft_ptr = __va(prom_bussoft_ptr); + controller = sn_alloc_pci_sysdata(); + /* controller non-zero is BUG'd in sn_alloc_pci_sysdata */ + + bus = pci_scan_bus(busnum, &pci_root_ops, controller); + if (bus == NULL) { + return; /* error, or bus already scanned */ + } + + /* + * Per-provider fixup. Copies the contents from prom to local + * area and links SN_PCIBUS_BUSSOFT(). + * + * Note: Provider is responsible for ensuring that prom_bussoft_ptr + * represents an asic-type that it can handle. + */ + + if (prom_bussoft_ptr->bs_asic_type == PCIIO_ASIC_TYPE_PPB) { + return; /* no further fixup necessary */ + } + + provider_soft = pcibr_bus_fixup(prom_bussoft_ptr); + if (provider_soft == NULL) { + return; /* fixup failed or not applicable */ + } + + /* + * Generic bus fixup goes here. Don't reference prom_bussoft_ptr + * after this point. + */ + + PCI_CONTROLLER(bus) = controller; + SN_PCIBUS_BUSSOFT(bus) = provider_soft; + + nasid = NASID_GET(SN_PCIBUS_BUSSOFT(bus)->bs_base); + cnode = nasid_to_cnodeid(nasid); + hubdev_info = (struct hubdev_info *)(NODEPDA(cnode)->pdinfo); + SN_PCIBUS_BUSSOFT(bus)->bs_xwidget_info = + &(hubdev_info->hdi_xwidget_info[SN_PCIBUS_BUSSOFT(bus)->bs_xid]); +} + +/* + * Ugly hack to get PCI setup until we have a proper ACPI namespace. + */ + +#define PCI_BUSES_TO_SCAN 256 + +static int __init sn_pci_init(void) +{ + int i = 0; + struct pci_dev *pci_dev = NULL; + extern void sn_init_cpei_timer(void); +#ifdef CONFIG_PROC_FS + extern void register_sn_procfs(void); +#endif + + if (!ia64_platform_is("sn2") || IS_RUNNING_ON_SIMULATOR()) + return 0; + + /* + * This is needed to avoid bounce limit checks in the blk layer + */ + ia64_max_iommu_merge_mask = ~PAGE_MASK; + sn_fixup_ionodes(); + sn_irq = kmalloc(sizeof(struct sn_irq_info *) * NR_IRQS, GFP_KERNEL); + if (sn_irq <= 0) + BUG(); /* Canno afford to run out of memory. */ + memset(sn_irq, 0, sizeof(struct sn_irq_info *) * NR_IRQS); + + sn_init_cpei_timer(); + +#ifdef CONFIG_PROC_FS + register_sn_procfs(); +#endif + + for (i = 0; i < PCI_BUSES_TO_SCAN; i++) { + sn_pci_controller_fixup(0, i); + } + + /* + * Generic Linux PCI Layer has created the pci_bus and pci_dev + * structures - time for us to add our SN PLatform specific + * information. + */ + + while ((pci_dev = + pci_find_device(PCI_ANY_ID, PCI_ANY_ID, pci_dev)) != NULL) { + sn_pci_fixup_slot(pci_dev); + } + + sn_ioif_inited = 1; /* sn I/O infrastructure now initialized */ + + return 0; +} + +/* + * hubdev_init_node() - Creates the HUB data structure and link them to it's + * own NODE specific data area. + */ +void hubdev_init_node(nodepda_t * npda, cnodeid_t node) +{ + + struct hubdev_info *hubdev_info; + + if (node >= numnodes) /* Headless/memless IO nodes */ + hubdev_info = + (struct hubdev_info *)alloc_bootmem_node(NODE_DATA(0), + sizeof(struct + hubdev_info)); + else + hubdev_info = + (struct hubdev_info *)alloc_bootmem_node(NODE_DATA(node), + sizeof(struct + hubdev_info)); + npda->pdinfo = (void *)hubdev_info; + +} + +geoid_t +cnodeid_get_geoid(cnodeid_t cnode) +{ + + struct hubdev_info *hubdev; + + hubdev = (struct hubdev_info *)(NODEPDA(cnode)->pdinfo); + return hubdev->hdi_geoid; + +} + +subsys_initcall(sn_pci_init); diff --git a/arch/ia64/sn/kernel/iomv.c b/arch/ia64/sn/kernel/iomv.c new file mode 100644 index 000000000..a98d6740a --- /dev/null +++ b/arch/ia64/sn/kernel/iomv.c @@ -0,0 +1,75 @@ +/* + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file "COPYING" in the main directory of this archive + * for more details. + * + * Copyright (C) 2000-2003 Silicon Graphics, Inc. All rights reserved. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +/** + * sn_io_addr - convert an in/out port to an i/o address + * @port: port to convert + * + * Legacy in/out instructions are converted to ld/st instructions + * on IA64. This routine will convert a port number into a valid + * SN i/o address. Used by sn_in*() and sn_out*(). + */ +void *sn_io_addr(unsigned long port) +{ + if (!IS_RUNNING_ON_SIMULATOR()) { + /* On sn2, legacy I/O ports don't point at anything */ + if (port < (64 * 1024)) + return NULL; + return ((void *)(port | __IA64_UNCACHED_OFFSET)); + } else { + /* but the simulator uses them... */ + unsigned long io_base; + unsigned long addr; + + /* + * word align port, but need more than 10 bits + * for accessing registers in bedrock local block + * (so we don't do port&0xfff) + */ + if ((port >= 0x1f0 && port <= 0x1f7) || + port == 0x3f6 || port == 0x3f7) { + io_base = (0xc000000fcc000000UL | + ((unsigned long)get_nasid() << 38)); + addr = io_base | ((port >> 2) << 12) | (port & 0xfff); + } else { + addr = __ia64_get_io_port_base() | ((port >> 2) << 2); + } + return (void *)addr; + } +} + +EXPORT_SYMBOL(sn_io_addr); + +/** + * __sn_mmiowb - I/O space memory barrier + * + * See include/asm-ia64/io.h and Documentation/DocBook/deviceiobook.tmpl + * for details. + * + * On SN2, we wait for the PIO_WRITE_STATUS SHub register to clear. + * See PV 871084 for details about the WAR about zero value. + * + */ +void __sn_mmiowb(void) +{ + while ((((volatile unsigned long)(*pda->pio_write_status_addr)) & + SH_PIO_WRITE_STATUS_0_PENDING_WRITE_COUNT_MASK) != + SH_PIO_WRITE_STATUS_0_PENDING_WRITE_COUNT_MASK) + cpu_relax(); +} + +EXPORT_SYMBOL(__sn_mmiowb); diff --git a/arch/ia64/sn/kernel/klconflib.c b/arch/ia64/sn/kernel/klconflib.c new file mode 100644 index 000000000..0f11a3299 --- /dev/null +++ b/arch/ia64/sn/kernel/klconflib.c @@ -0,0 +1,108 @@ +/* + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file "COPYING" in the main directory of this archive + * for more details. + * + * Copyright (C) 1992 - 1997, 2000-2004 Silicon Graphics, Inc. All rights reserved. + */ + +#include +#include +#include +#include +#include +#include +#include + +char brick_types[MAX_BRICK_TYPES + 1] = "cri.xdpn%#=vo^kjbf890123456789..."; +/* + * Format a module id for printing. + * + * There are three possible formats: + * + * MODULE_FORMAT_BRIEF is the brief 6-character format, including + * the actual brick-type as recorded in the + * moduleid_t, eg. 002c15 for a C-brick, or + * 101#17 for a PX-brick. + * + * MODULE_FORMAT_LONG is the hwgraph format, eg. rack/002/bay/15 + * of rack/101/bay/17 (note that the brick + * type does not appear in this format). + * + * MODULE_FORMAT_LCD is like MODULE_FORMAT_BRIEF, except that it + * ensures that the module id provided appears + * exactly as it would on the LCD display of + * the corresponding brick, eg. still 002c15 + * for a C-brick, but 101p17 for a PX-brick. + * + * maule (9/13/04): Removed top-level check for (fmt == MODULE_FORMAT_LCD) + * making MODULE_FORMAT_LCD equivalent to MODULE_FORMAT_BRIEF. It was + * decided that all callers should assume the returned string should be what + * is displayed on the brick L1 LCD. + */ +void +format_module_id(char *buffer, moduleid_t m, int fmt) +{ + int rack, position; + unsigned char brickchar; + + rack = MODULE_GET_RACK(m); + brickchar = MODULE_GET_BTCHAR(m); + + /* Be sure we use the same brick type character as displayed + * on the brick's LCD + */ + switch (brickchar) + { + case L1_BRICKTYPE_GA: + case L1_BRICKTYPE_OPUS_TIO: + brickchar = L1_BRICKTYPE_C; + break; + + case L1_BRICKTYPE_PX: + case L1_BRICKTYPE_PE: + case L1_BRICKTYPE_PA: + case L1_BRICKTYPE_SA: /* we can move this to the "I's" later + * if that makes more sense + */ + brickchar = L1_BRICKTYPE_P; + break; + + case L1_BRICKTYPE_IX: + case L1_BRICKTYPE_IA: + + brickchar = L1_BRICKTYPE_I; + break; + } + + position = MODULE_GET_BPOS(m); + + if ((fmt == MODULE_FORMAT_BRIEF) || (fmt == MODULE_FORMAT_LCD)) { + /* Brief module number format, eg. 002c15 */ + + /* Decompress the rack number */ + *buffer++ = '0' + RACK_GET_CLASS(rack); + *buffer++ = '0' + RACK_GET_GROUP(rack); + *buffer++ = '0' + RACK_GET_NUM(rack); + + /* Add the brick type */ + *buffer++ = brickchar; + } + else if (fmt == MODULE_FORMAT_LONG) { + /* Fuller hwgraph format, eg. rack/002/bay/15 */ + + strcpy(buffer, "rack" "/"); buffer += strlen(buffer); + + *buffer++ = '0' + RACK_GET_CLASS(rack); + *buffer++ = '0' + RACK_GET_GROUP(rack); + *buffer++ = '0' + RACK_GET_NUM(rack); + + strcpy(buffer, "/" "bay" "/"); buffer += strlen(buffer); + } + + /* Add the bay position, using at least two digits */ + if (position < 10) + *buffer++ = '0'; + sprintf(buffer, "%d", position); + +} diff --git a/arch/ia64/sn/kernel/sn2/Makefile b/arch/ia64/sn/kernel/sn2/Makefile index 0143b8182..170bde454 100644 --- a/arch/ia64/sn/kernel/sn2/Makefile +++ b/arch/ia64/sn/kernel/sn2/Makefile @@ -10,4 +10,4 @@ # obj-y += cache.o io.o ptc_deadlock.o sn2_smp.o sn_proc_fs.o \ - prominfo_proc.o timer.o timer_interrupt.o + prominfo_proc.o timer.o timer_interrupt.o sn_hwperf.o diff --git a/arch/ia64/sn/kernel/sn2/sn_hwperf.c b/arch/ia64/sn/kernel/sn2/sn_hwperf.c new file mode 100644 index 000000000..8a810d339 --- /dev/null +++ b/arch/ia64/sn/kernel/sn2/sn_hwperf.c @@ -0,0 +1,652 @@ +/* + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file "COPYING" in the main directory of this archive + * for more details. + * + * Copyright (C) 2004 Silicon Graphics, Inc. All rights reserved. + * + * SGI Altix topology and hardware performance monitoring API. + * Mark Goodwin . + * + * Creates /proc/sgi_sn/sn_topology (read-only) to export + * info about Altix nodes, routers, CPUs and NumaLink + * interconnection/topology. + * + * Also creates a dynamic misc device named "sn_hwperf" + * that supports an ioctl interface to call down into SAL + * to discover hw objects, topology and to read/write + * memory mapped registers, e.g. for performance monitoring. + * The "sn_hwperf" device is registered only after the procfs + * file is first opened, i.e. only if/when it's needed. + * + * This API is used by SGI Performance Co-Pilot and other + * tools, see http://oss.sgi.com/projects/pcp + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static void *sn_hwperf_salheap = NULL; +static int sn_hwperf_obj_cnt = 0; +static nasid_t sn_hwperf_master_nasid = INVALID_NASID; +static int sn_hwperf_init(void); +static DECLARE_MUTEX(sn_hwperf_init_mutex); + +static int sn_hwperf_enum_objects(int *nobj, struct sn_hwperf_object_info **ret) +{ + int e; + u64 sz; + struct sn_hwperf_object_info *objbuf = NULL; + + if ((e = sn_hwperf_init()) < 0) { + printk("sn_hwperf_init failed: err %d\n", e); + goto out; + } + + sz = sn_hwperf_obj_cnt * sizeof(struct sn_hwperf_object_info); + if ((objbuf = (struct sn_hwperf_object_info *) vmalloc(sz)) == NULL) { + printk("sn_hwperf_enum_objects: vmalloc(%d) failed\n", (int)sz); + e = -ENOMEM; + goto out; + } + + e = ia64_sn_hwperf_op(sn_hwperf_master_nasid, SN_HWPERF_ENUM_OBJECTS, + 0, sz, (u64) objbuf, 0, 0, NULL); + if (e != SN_HWPERF_OP_OK) { + e = -EINVAL; + vfree(objbuf); + } + +out: + *nobj = sn_hwperf_obj_cnt; + *ret = objbuf; + return e; +} + +static int sn_hwperf_geoid_to_cnode(char *location) +{ + int cnode; + int mod, slot, slab; + int cmod, cslot, cslab; + + if (sscanf(location, "%03dc%02d#%d", &mod, &slot, &slab) != 3) + return -1; + for (cnode = 0; cnode < numnodes; cnode++) { + /* XXX: need a better way than this ... */ + if (sscanf(NODEPDA(cnode)->hwg_node_name, + "hw/module/%03dc%02d/slab/%d", &cmod, &cslot, &cslab) == 3) { + if (mod == cmod && slot == cslot && slab == cslab) + break; + } + } + + return cnode < numnodes ? cnode : -1; +} + +static int sn_hwperf_obj_to_cnode(struct sn_hwperf_object_info * obj) +{ + if (!obj->sn_hwp_this_part) + return -1; + return sn_hwperf_geoid_to_cnode(obj->location); +} + +static int sn_hwperf_generic_ordinal(struct sn_hwperf_object_info *obj, + struct sn_hwperf_object_info *objs) +{ + int ordinal; + struct sn_hwperf_object_info *p; + + for (ordinal=0, p=objs; p != obj; p++) { + if (SN_HWPERF_FOREIGN(p)) + continue; + if (p->location[3] == obj->location[3]) + ordinal++; + } + + return ordinal; +} + +#ifndef MODULE_IOBRICK +/* this will be available when ioif TIO support is added */ +#define MODULE_IOBRICK (MODULE_OPUSBRICK+1) +#endif + +static const char *sn_hwperf_get_brickname(struct sn_hwperf_object_info *obj, + struct sn_hwperf_object_info *objs, int *ordinal) +{ + int i; + const char *objtype = NULL; + + for (i=0; i < MAX_BRICK_TYPES; i++) { + if (brick_types[i] != obj->location[3]) + continue; + switch (i) { + case MODULE_CBRICK: + objtype = "node"; + *ordinal = sn_hwperf_obj_to_cnode(obj); /* cnodeid */ + break; + + case MODULE_RBRICK: + objtype = "router"; + *ordinal = sn_hwperf_generic_ordinal(obj, objs); + break; + + case MODULE_IOBRICK: + objtype = "ionode"; + *ordinal = sn_hwperf_generic_ordinal(obj, objs); + break; + } + break; + } + + if (i == MAX_BRICK_TYPES) { + objtype = "other"; + *ordinal = sn_hwperf_generic_ordinal(obj, objs); + } + + return objtype; +} + +static int sn_topology_show(struct seq_file *s, void *d) +{ + int sz; + int pt; + int e; + int i; + int j; + const char *brickname; + int ordinal; + cpumask_t cpumask; + char slice; + struct cpuinfo_ia64 *c; + struct sn_hwperf_port_info *ptdata; + struct sn_hwperf_object_info *p; + struct sn_hwperf_object_info *obj = d; /* this object */ + struct sn_hwperf_object_info *objs = s->private; /* all objects */ + + if (obj == objs) { + seq_printf(s, "# sn_topology version 1\n"); + seq_printf(s, "# objtype ordinal location partition" + " [attribute value [, ...]]\n"); + } + + if (SN_HWPERF_FOREIGN(obj)) { + /* private in another partition: not interesting */ + return 0; + } + + for (i = 0; obj->name[i]; i++) { + if (obj->name[i] == ' ') + obj->name[i] = '_'; + } + + brickname = sn_hwperf_get_brickname(obj, objs, &ordinal); + seq_printf(s, "%s %d %s %s asic %s", brickname, ordinal, obj->location, + obj->sn_hwp_this_part ? "local" : "shared", obj->name); + + if (obj->location[3] != 'c') + seq_putc(s, '\n'); + else { + seq_printf(s, ", nasid 0x%x", cnodeid_to_nasid(ordinal)); + for (i=0; i < numnodes; i++) { + seq_printf(s, i ? ":%d" : ", dist %d", + node_distance(ordinal, i)); + } + seq_putc(s, '\n'); + + /* + * CPUs on this node + */ + 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( + cpuid_to_cnodeid(i), + cpuid_to_cnodeid(j))); + } + seq_putc(s, '\n'); + } + } + } + + if (obj->ports) { + /* + * numalink ports + */ + sz = obj->ports * sizeof(struct sn_hwperf_port_info); + if ((ptdata = vmalloc(sz)) == NULL) + return -ENOMEM; + e = ia64_sn_hwperf_op(sn_hwperf_master_nasid, + SN_HWPERF_ENUM_PORTS, obj->id, sz, + (u64) ptdata, 0, 0, NULL); + if (e != SN_HWPERF_OP_OK) + return -EINVAL; + for (ordinal=0, p=objs; p != obj; p++) { + if (!SN_HWPERF_FOREIGN(p)) + ordinal += p->ports; + } + for (pt = 0; pt < obj->ports; pt++) { + for (p = objs, i = 0; i < sn_hwperf_obj_cnt; i++, p++) { + if (ptdata[pt].conn_id == p->id) { + break; + } + } + if (i >= sn_hwperf_obj_cnt) + continue; + seq_printf(s, "numalink %d %s-%d", + ordinal+pt, obj->location, ptdata[pt].port); + + if (obj->sn_hwp_this_part && p->sn_hwp_this_part) + /* both ends local to this partition */ + seq_puts(s, " local"); + else if (!obj->sn_hwp_this_part && !p->sn_hwp_this_part) + /* both ends of the link in foreign partiton */ + seq_puts(s, " foreign"); + else + /* link straddles a partition */ + seq_puts(s, " shared"); + + /* + * Unlikely, but strictly should query the LLP config + * registers because an NL4R can be configured to run + * NL3 protocol, even when not talking to an NL3 router. + * Ditto for node-node. + */ + seq_printf(s, " endpoint %s-%d, protocol %s\n", + p->location, ptdata[pt].conn_port, + strcmp(obj->name, "NL3Router") == 0 || + strcmp(p->name, "NL3Router") == 0 ? + "LLP3" : "LLP4"); + } + vfree(ptdata); + } + + return 0; +} + +static void *sn_topology_start(struct seq_file *s, loff_t * pos) +{ + struct sn_hwperf_object_info *objs = s->private; + + if (*pos < sn_hwperf_obj_cnt) + return (void *)(objs + *pos); + + return NULL; +} + +static void *sn_topology_next(struct seq_file *s, void *v, loff_t * pos) +{ + ++*pos; + return sn_topology_start(s, pos); +} + +static void sn_topology_stop(struct seq_file *m, void *v) +{ + return; +} + +/* + * /proc/sgi_sn/sn_topology, read-only using seq_file + */ +static struct seq_operations sn_topology_seq_ops = { + .start = sn_topology_start, + .next = sn_topology_next, + .stop = sn_topology_stop, + .show = sn_topology_show +}; + +struct sn_hwperf_op_info { + u64 op; + struct sn_hwperf_ioctl_args *a; + void *p; + int *v0; + int ret; +}; + +static void sn_hwperf_call_sal(void *info) +{ + struct sn_hwperf_op_info *op_info = info; + int r; + + r = ia64_sn_hwperf_op(sn_hwperf_master_nasid, op_info->op, + op_info->a->arg, op_info->a->sz, + (u64) op_info->p, 0, 0, op_info->v0); + op_info->ret = r; +} + +static int sn_hwperf_op_cpu(struct sn_hwperf_op_info *op_info) +{ + u32 cpu; + u32 use_ipi; + int r = 0; + cpumask_t save_allowed; + + cpu = (op_info->a->arg & SN_HWPERF_ARG_CPU_MASK) >> 32; + use_ipi = op_info->a->arg & SN_HWPERF_ARG_USE_IPI_MASK; + op_info->a->arg &= SN_HWPERF_ARG_OBJID_MASK; + + if (cpu != SN_HWPERF_ARG_ANY_CPU) { + if (cpu >= num_online_cpus() || !cpu_online(cpu)) { + r = -EINVAL; + goto out; + } + } + + if (cpu == SN_HWPERF_ARG_ANY_CPU || cpu == get_cpu()) { + /* don't care, or already on correct cpu */ + sn_hwperf_call_sal(op_info); + } + else { + if (use_ipi) { + /* use an interprocessor interrupt to call SAL */ + smp_call_function_single(cpu, sn_hwperf_call_sal, + op_info, 1, 1); + } + else { + /* migrate the task before calling SAL */ + save_allowed = current->cpus_allowed; + set_cpus_allowed(current, cpumask_of_cpu(cpu)); + sn_hwperf_call_sal(op_info); + set_cpus_allowed(current, save_allowed); + } + } + r = op_info->ret; + +out: + return r; +} + +/* + * ioctl for "sn_hwperf" misc device + */ +static int +sn_hwperf_ioctl(struct inode *in, struct file *fp, u32 op, u64 arg) +{ + struct sn_hwperf_ioctl_args a; + struct cpuinfo_ia64 *cdata; + struct sn_hwperf_object_info *objs; + struct sn_hwperf_object_info *cpuobj; + struct sn_hwperf_op_info op_info; + void *p = NULL; + int nobj; + char slice; + int node; + int r; + int v0; + int i; + int j; + + unlock_kernel(); + + /* only user requests are allowed here */ + if ((op & SN_HWPERF_OP_MASK) < 10) { + r = -EINVAL; + goto error; + } + r = copy_from_user(&a, (const void *)arg, + sizeof(struct sn_hwperf_ioctl_args)); + if (r != 0) { + r = -EFAULT; + goto error; + } + + /* + * Allocate memory to hold a kernel copy of the user buffer. The + * buffer contents are either copied in or out (or both) of user + * space depending on the flags encoded in the requested operation. + */ + if (a.ptr) { + p = vmalloc(a.sz); + if (!p) { + r = -ENOMEM; + goto error; + } + } + + if (op & SN_HWPERF_OP_MEM_COPYIN) { + r = copy_from_user(p, (const void *)a.ptr, a.sz); + if (r != 0) { + r = -EFAULT; + goto error; + } + } + + switch (op) { + case SN_HWPERF_GET_CPU_INFO: + if (a.sz == sizeof(u64)) { + /* special case to get size needed */ + *(u64 *) p = (u64) num_online_cpus() * + sizeof(struct sn_hwperf_object_info); + } else + if (a.sz < num_online_cpus() * sizeof(struct sn_hwperf_object_info)) { + r = -ENOMEM; + goto error; + } else + if ((r = sn_hwperf_enum_objects(&nobj, &objs)) == 0) { + memset(p, 0, a.sz); + for (i = 0; i < nobj; i++) { + node = sn_hwperf_obj_to_cnode(objs + i); + for_each_online_cpu(j) { + if (node != cpu_to_node(j)) + continue; + cpuobj = (struct sn_hwperf_object_info *) p + j; + slice = 'a' + cpuid_to_slice(j); + cdata = cpu_data(j); + cpuobj->id = j; + snprintf(cpuobj->name, + sizeof(cpuobj->name), + "CPU %luMHz %s", + cdata->proc_freq / 1000000, + cdata->vendor); + snprintf(cpuobj->location, + sizeof(cpuobj->location), + "%s%c", objs[i].location, + slice); + } + } + + vfree(objs); + } + break; + + case SN_HWPERF_GET_NODE_NASID: + if (a.sz != sizeof(u64) || + (node = a.arg) < 0 || node >= numnodes) { + r = -EINVAL; + goto error; + } + *(u64 *)p = (u64)cnodeid_to_nasid(node); + break; + + case SN_HWPERF_GET_OBJ_NODE: + if (a.sz != sizeof(u64) || a.arg < 0) { + r = -EINVAL; + goto error; + } + if ((r = sn_hwperf_enum_objects(&nobj, &objs)) == 0) { + if (a.arg >= nobj) { + r = -EINVAL; + vfree(objs); + goto error; + } + if (objs[(i = a.arg)].id != a.arg) { + for (i = 0; i < nobj; i++) { + if (objs[i].id == a.arg) + break; + } + } + if (i == nobj) { + r = -EINVAL; + vfree(objs); + goto error; + } + *(u64 *)p = (u64)sn_hwperf_obj_to_cnode(objs + i); + vfree(objs); + } + break; + + case SN_HWPERF_GET_MMRS: + case SN_HWPERF_SET_MMRS: + case SN_HWPERF_OBJECT_DISTANCE: + op_info.p = p; + op_info.a = &a; + op_info.v0 = &v0; + op_info.op = op; + r = sn_hwperf_op_cpu(&op_info); + break; + + default: + /* all other ops are a direct SAL call */ + r = ia64_sn_hwperf_op(sn_hwperf_master_nasid, op, + a.arg, a.sz, (u64) p, 0, 0, &v0); + a.v0 = v0; + break; + } + + if (op & SN_HWPERF_OP_MEM_COPYOUT) { + r = copy_to_user((void *)a.ptr, p, a.sz); + if (r != 0) { + r = -EFAULT; + goto error; + } + } + +error: + if (p) + vfree(p); + + lock_kernel(); + return r; +} + +static struct file_operations sn_hwperf_fops = { + .ioctl = sn_hwperf_ioctl, +}; + +static struct miscdevice sn_hwperf_dev = { + MISC_DYNAMIC_MINOR, + "sn_hwperf", + &sn_hwperf_fops +}; + +static int sn_hwperf_init(void) +{ + u64 v; + int salr; + int e = 0; + + /* single threaded, once-only initialization */ + down(&sn_hwperf_init_mutex); + if (sn_hwperf_salheap) { + up(&sn_hwperf_init_mutex); + return e; + } + + /* + * The PROM code needs a fixed reference node. For convenience the + * same node as the console I/O is used. + */ + sn_hwperf_master_nasid = (nasid_t) ia64_sn_get_console_nasid(); + + /* + * Request the needed size and install the PROM scratch area. + * The PROM keeps various tracking bits in this memory area. + */ + salr = ia64_sn_hwperf_op(sn_hwperf_master_nasid, + (u64) SN_HWPERF_GET_HEAPSIZE, 0, + (u64) sizeof(u64), (u64) &v, 0, 0, NULL); + if (salr != SN_HWPERF_OP_OK) { + e = -EINVAL; + goto out; + } + + if ((sn_hwperf_salheap = vmalloc(v)) == NULL) { + e = -ENOMEM; + goto out; + } + salr = ia64_sn_hwperf_op(sn_hwperf_master_nasid, + SN_HWPERF_INSTALL_HEAP, 0, v, + (u64) sn_hwperf_salheap, 0, 0, NULL); + if (salr != SN_HWPERF_OP_OK) { + e = -EINVAL; + goto out; + } + + salr = ia64_sn_hwperf_op(sn_hwperf_master_nasid, + SN_HWPERF_OBJECT_COUNT, 0, + sizeof(u64), (u64) &v, 0, 0, NULL); + if (salr != SN_HWPERF_OP_OK) { + e = -EINVAL; + goto out; + } + sn_hwperf_obj_cnt = (int)v; + +out: + if (e < 0 && sn_hwperf_salheap) { + vfree(sn_hwperf_salheap); + sn_hwperf_salheap = NULL; + sn_hwperf_obj_cnt = 0; + } + + if (!e) { + /* + * Register a dynamic misc device for ioctl. Platforms + * supporting hotplug will create /dev/sn_hwperf, else + * user can to look up the minor number in /proc/misc. + */ + if ((e = misc_register(&sn_hwperf_dev)) != 0) { + printk(KERN_ERR "sn_hwperf_init: misc register " + "for \"sn_hwperf\" failed, err %d\n", e); + } + } + + up(&sn_hwperf_init_mutex); + return e; +} + +int sn_topology_open(struct inode *inode, struct file *file) +{ + int e; + struct seq_file *seq; + struct sn_hwperf_object_info *objbuf; + int nobj; + + if ((e = sn_hwperf_enum_objects(&nobj, &objbuf)) == 0) { + e = seq_open(file, &sn_topology_seq_ops); + seq = file->private_data; + seq->private = objbuf; + } + + return e; +} + +int sn_topology_release(struct inode *inode, struct file *file) +{ + struct seq_file *seq = file->private_data; + + if (seq->private) + vfree(seq->private); + return seq_release(inode, file); +} diff --git a/arch/ia64/sn/kernel/sn2/sn_proc_fs.c b/arch/ia64/sn/kernel/sn2/sn_proc_fs.c index 14840791a..55c1065e9 100644 --- a/arch/ia64/sn/kernel/sn2/sn_proc_fs.c +++ b/arch/ia64/sn/kernel/sn2/sn_proc_fs.c @@ -3,74 +3,48 @@ * License. See the file "COPYING" in the main directory of this archive * for more details. * - * Copyright (C) 2000-2003 Silicon Graphics, Inc. All rights reserved. + * Copyright (C) 2000-2004 Silicon Graphics, Inc. All rights reserved. */ #include #include #ifdef CONFIG_PROC_FS #include +#include #include #include - -static int partition_id_read_proc(char *page, char **start, off_t off, - int count, int *eof, void *data) { - - return sprintf(page, "%d\n", sn_local_partid()); +static int partition_id_show(struct seq_file *s, void *p) +{ + seq_printf(s, "%d\n", sn_local_partid()); + return 0; } -static struct proc_dir_entry * sgi_proc_dir; - -void -register_sn_partition_id(void) { - struct proc_dir_entry *entry; - - if (!sgi_proc_dir) { - sgi_proc_dir = proc_mkdir("sgi_sn", 0); - } - entry = create_proc_entry("partition_id", 0444, sgi_proc_dir); - if (entry) { - entry->nlink = 1; - entry->data = 0; - entry->read_proc = partition_id_read_proc; - entry->write_proc = NULL; - } +static int partition_id_open(struct inode *inode, struct file *file) +{ + return single_open(file, partition_id_show, NULL); } -static int -system_serial_number_read_proc(char *page, char **start, off_t off, - int count, int *eof, void *data) { - return sprintf(page, "%s\n", sn_system_serial_number()); +static int system_serial_number_show(struct seq_file *s, void *p) +{ + seq_printf(s, "%s\n", sn_system_serial_number()); + return 0; } -static int -licenseID_read_proc(char *page, char **start, off_t off, - int count, int *eof, void *data) { - return sprintf(page, "0x%lx\n",sn_partition_serial_number_val()); +static int system_serial_number_open(struct inode *inode, struct file *file) +{ + return single_open(file, system_serial_number_show, NULL); } -void -register_sn_serial_numbers(void) { - struct proc_dir_entry *entry; +static int licenseID_show(struct seq_file *s, void *p) +{ + seq_printf(s, "0x%lx\n", sn_partition_serial_number_val()); + return 0; +} - if (!sgi_proc_dir) { - sgi_proc_dir = proc_mkdir("sgi_sn", 0); - } - entry = create_proc_entry("system_serial_number", 0444, sgi_proc_dir); - if (entry) { - entry->nlink = 1; - entry->data = 0; - entry->read_proc = system_serial_number_read_proc; - entry->write_proc = NULL; - } - entry = create_proc_entry("licenseID", 0444, sgi_proc_dir); - if (entry) { - entry->nlink = 1; - entry->data = 0; - entry->read_proc = licenseID_read_proc; - entry->write_proc = NULL; - } +static int licenseID_open(struct inode *inode, struct file *file) +{ + return single_open(file, licenseID_show, NULL); } /* @@ -81,48 +55,90 @@ register_sn_serial_numbers(void) { */ int sn_force_interrupt_flag = 1; -static int -sn_force_interrupt_read_proc(char *page, char **start, off_t off, - int count, int *eof, void *data) { - if (sn_force_interrupt_flag) { - return sprintf(page, "Force interrupt is enabled\n"); - } - return sprintf(page, "Force interrupt is disabled\n"); +static int sn_force_interrupt_show(struct seq_file *s, void *p) +{ + seq_printf(s, "Force interrupt is %s\n", + sn_force_interrupt_flag ? "enabled" : "disabled"); + return 0; } -static int -sn_force_interrupt_write_proc(struct file *file, const char *buffer, - unsigned long count, void *data) +static ssize_t sn_force_interrupt_write_proc(struct file *file, + const __user char *buffer, size_t count, loff_t *data) { - if (*buffer == '0') { - sn_force_interrupt_flag = 0; - } else { - sn_force_interrupt_flag = 1; - } - return 1; + sn_force_interrupt_flag = (*buffer == '0') ? 0 : 1; + return count; } -void -register_sn_force_interrupt(void) { - struct proc_dir_entry *entry; +static int sn_force_interrupt_open(struct inode *inode, struct file *file) +{ + return single_open(file, sn_force_interrupt_show, NULL); +} - if (!sgi_proc_dir) { - sgi_proc_dir = proc_mkdir("sgi_sn", 0); - } - entry = create_proc_entry("sn_force_interrupt",0444, sgi_proc_dir); - if (entry) { - entry->nlink = 1; - entry->data = 0; - entry->read_proc = sn_force_interrupt_read_proc; - entry->write_proc = sn_force_interrupt_write_proc; +static int coherence_id_show(struct seq_file *s, void *p) +{ + seq_printf(s, "%d\n", cpuid_to_coherence_id(smp_processor_id())); + return 0; +} + +static int coherence_id_open(struct inode *inode, struct file *file) +{ + return single_open(file, coherence_id_show, NULL); +} + +static struct proc_dir_entry *sn_procfs_create_entry( + const char *name, struct proc_dir_entry *parent, + int (*openfunc)(struct inode *, struct file *), + int (*releasefunc)(struct inode *, struct file *)) +{ + struct proc_dir_entry *e = create_proc_entry(name, 0444, parent); + + if (e) { + e->proc_fops = (struct file_operations *)kmalloc( + sizeof(struct file_operations), GFP_KERNEL); + if (e->proc_fops) { + memset(e->proc_fops, 0, sizeof(struct file_operations)); + e->proc_fops->open = openfunc; + e->proc_fops->read = seq_read; + e->proc_fops->llseek = seq_lseek; + e->proc_fops->release = releasefunc; + } } + + return e; } -void -register_sn_procfs(void) { - register_sn_partition_id(); - register_sn_serial_numbers(); - register_sn_force_interrupt(); +/* /proc/sgi_sn/sn_topology uses seq_file, see sn_hwperf.c */ +extern int sn_topology_open(struct inode *, struct file *); +extern int sn_topology_release(struct inode *, struct file *); + +void register_sn_procfs(void) +{ + static struct proc_dir_entry *sgi_proc_dir = NULL; + struct proc_dir_entry *e; + + BUG_ON(sgi_proc_dir != NULL); + if (!(sgi_proc_dir = proc_mkdir("sgi_sn", 0))) + return; + + sn_procfs_create_entry("partition_id", sgi_proc_dir, + partition_id_open, single_release); + + sn_procfs_create_entry("system_serial_number", sgi_proc_dir, + system_serial_number_open, single_release); + + sn_procfs_create_entry("licenseID", sgi_proc_dir, + licenseID_open, single_release); + + e = sn_procfs_create_entry("sn_force_interrupt", sgi_proc_dir, + sn_force_interrupt_open, single_release); + if (e) + e->proc_fops->write = sn_force_interrupt_write_proc; + + sn_procfs_create_entry("coherence_id", sgi_proc_dir, + coherence_id_open, single_release); + + sn_procfs_create_entry("sn_topology", sgi_proc_dir, + sn_topology_open, sn_topology_release); } #endif /* CONFIG_PROC_FS */ diff --git a/arch/ia64/sn/kernel/sn2/timer.c b/arch/ia64/sn/kernel/sn2/timer.c index 57844feea..85f7d215a 100644 --- a/arch/ia64/sn/kernel/sn2/timer.c +++ b/arch/ia64/sn/kernel/sn2/timer.c @@ -20,57 +20,16 @@ extern unsigned long sn_rtc_cycles_per_second; -static volatile unsigned long last_wall_rtc; -static unsigned long rtc_offset; /* updated only when xtime write-lock is held! */ -static long rtc_nsecs_per_cycle; -static long rtc_per_timer_tick; - -static unsigned long -getoffset(void) -{ - return rtc_offset + (GET_RTC_COUNTER() - last_wall_rtc)*rtc_nsecs_per_cycle; -} - - -static void -update(long delta_nsec) -{ - unsigned long rtc_counter = GET_RTC_COUNTER(); - unsigned long offset = rtc_offset + (rtc_counter - last_wall_rtc)*rtc_nsecs_per_cycle; - - /* Be careful about signed/unsigned comparisons here: */ - if (delta_nsec < 0 || (unsigned long) delta_nsec < offset) - rtc_offset = offset - delta_nsec; - else - rtc_offset = 0; - last_wall_rtc = rtc_counter; -} - - -static void -reset(void) -{ - rtc_offset = 0; - last_wall_rtc = GET_RTC_COUNTER(); -} - - -static struct time_interpolator sn2_interpolator = { - .get_offset = getoffset, - .update = update, - .reset = reset -}; +static struct time_interpolator sn2_interpolator; void __init sn_timer_init(void) { sn2_interpolator.frequency = sn_rtc_cycles_per_second; sn2_interpolator.drift = -1; /* unknown */ + sn2_interpolator.shift = 10; /* RTC is 54 bits maximum shift is 10 */ + sn2_interpolator.addr = RTC_COUNTER_ADDR; + sn2_interpolator.source = TIME_SOURCE_MMIO64; register_time_interpolator(&sn2_interpolator); - - rtc_per_timer_tick = sn_rtc_cycles_per_second / HZ; - rtc_nsecs_per_cycle = 1000000000 / sn_rtc_cycles_per_second; - - last_wall_rtc = GET_RTC_COUNTER(); } diff --git a/arch/ia64/sn/pci/Makefile b/arch/ia64/sn/pci/Makefile new file mode 100644 index 000000000..b5dca0097 --- /dev/null +++ b/arch/ia64/sn/pci/Makefile @@ -0,0 +1,10 @@ +# +# This file is subject to the terms and conditions of the GNU General Public +# License. See the file "COPYING" in the main directory of this archive +# for more details. +# +# Copyright (C) 2000-2004 Silicon Graphics, Inc. All Rights Reserved. +# +# Makefile for the sn pci general routines. + +obj-y := pci_dma.o pcibr/ diff --git a/arch/ia64/sn/pci/pci_dma.c b/arch/ia64/sn/pci/pci_dma.c new file mode 100644 index 000000000..71f311dc0 --- /dev/null +++ b/arch/ia64/sn/pci/pci_dma.c @@ -0,0 +1,477 @@ +/* + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file "COPYING" in the main directory of this archive + * for more details. + * + * Copyright (C) 2000,2002-2004 Silicon Graphics, Inc. All rights reserved. + * + * Routines for PCI DMA mapping. See Documentation/DMA-mapping.txt for + * a description of how these routines should be used. + */ + +#include +#include +#include "pci/pcibus_provider_defs.h" +#include "pci/pcidev.h" +#include "pci/pcibr_provider.h" + +void sn_pci_unmap_sg(struct pci_dev *hwdev, struct scatterlist *sg, int nents, + int direction); + +/** + * sn_pci_alloc_consistent - allocate memory for coherent DMA + * @hwdev: device to allocate for + * @size: size of the region + * @dma_handle: DMA (bus) address + * + * pci_alloc_consistent() returns a pointer to a memory region suitable for + * coherent DMA traffic to/from a PCI device. On SN platforms, this means + * that @dma_handle will have the %PCIIO_DMA_CMD flag set. + * + * This interface is usually used for "command" streams (e.g. the command + * queue for a SCSI controller). See Documentation/DMA-mapping.txt for + * more information. + * + * Also known as platform_pci_alloc_consistent() by the IA64 machvec code. + */ +void *sn_pci_alloc_consistent(struct pci_dev *hwdev, size_t size, + dma_addr_t * dma_handle) +{ + void *cpuaddr; + unsigned long phys_addr; + struct pcidev_info *pcidev_info = SN_PCIDEV_INFO(hwdev); + struct pcibus_bussoft *bussoft = SN_PCIDEV_BUSSOFT(hwdev); + + if (bussoft == NULL) { + return NULL; + } + + if (! IS_PCI_BRIDGE_ASIC(bussoft->bs_asic_type)) { + return NULL; /* unsupported asic type */ + } + + /* + * Allocate the memory. + * FIXME: We should be doing alloc_pages_node for the node closest + * to the PCI device. + */ + if (!(cpuaddr = (void *)__get_free_pages(GFP_ATOMIC, get_order(size)))) + return NULL; + + memset(cpuaddr, 0x0, size); + + /* physical addr. of the memory we just got */ + phys_addr = __pa(cpuaddr); + + /* + * 64 bit address translations should never fail. + * 32 bit translations can fail if there are insufficient mapping + * resources. + */ + + *dma_handle = pcibr_dma_map(pcidev_info, phys_addr, size, SN_PCIDMA_CONSISTENT); + if (!*dma_handle) { + printk(KERN_ERR + "sn_pci_alloc_consistent(): failed *dma_handle = 0x%lx hwdev->dev.coherent_dma_mask = 0x%lx \n", + *dma_handle, hwdev->dev.coherent_dma_mask); + free_pages((unsigned long)cpuaddr, get_order(size)); + return NULL; + } + + return cpuaddr; +} + +/** + * sn_pci_free_consistent - free memory associated with coherent DMAable region + * @hwdev: device to free for + * @size: size to free + * @vaddr: kernel virtual address to free + * @dma_handle: DMA address associated with this region + * + * Frees the memory allocated by pci_alloc_consistent(). Also known + * as platform_pci_free_consistent() by the IA64 machvec code. + */ +void +sn_pci_free_consistent(struct pci_dev *hwdev, size_t size, void *vaddr, + dma_addr_t dma_handle) +{ + struct pcidev_info *pcidev_info = SN_PCIDEV_INFO(hwdev); + struct pcibus_bussoft *bussoft = SN_PCIDEV_BUSSOFT(hwdev); + + if (! bussoft) { + return; + } + + pcibr_dma_unmap(pcidev_info, dma_handle, 0); + free_pages((unsigned long)vaddr, get_order(size)); +} + +/** + * sn_pci_map_sg - map a scatter-gather list for DMA + * @hwdev: device to map for + * @sg: scatterlist to map + * @nents: number of entries + * @direction: direction of the DMA transaction + * + * Maps each entry of @sg for DMA. Also known as platform_pci_map_sg by the + * IA64 machvec code. + */ +int +sn_pci_map_sg(struct pci_dev *hwdev, struct scatterlist *sg, int nents, + int direction) +{ + + int i; + unsigned long phys_addr; + struct scatterlist *saved_sg = sg; + struct pcidev_info *pcidev_info = SN_PCIDEV_INFO(hwdev); + struct pcibus_bussoft *bussoft = SN_PCIDEV_BUSSOFT(hwdev); + + /* can't go anywhere w/o a direction in life */ + if (direction == PCI_DMA_NONE) + BUG(); + + if (! bussoft) { + return 0; + } + + /* SN cannot support DMA addresses smaller than 32 bits. */ + if (hwdev->dma_mask < 0x7fffffff) + return 0; + + /* + * Setup a DMA address for each entry in the + * scatterlist. + */ + for (i = 0; i < nents; i++, sg++) { + phys_addr = + __pa((unsigned long)page_address(sg->page) + sg->offset); + sg->dma_address = pcibr_dma_map(pcidev_info, phys_addr, sg->length, 0); + + if (!sg->dma_address) { + printk(KERN_ERR "sn_pci_map_sg: Unable to allocate " + "anymore page map entries.\n"); + /* + * We will need to free all previously allocated entries. + */ + if (i > 0) { + sn_pci_unmap_sg(hwdev, saved_sg, i, direction); + } + return (0); + } + + sg->dma_length = sg->length; + } + + return nents; + +} + +/** + * sn_pci_unmap_sg - unmap a scatter-gather list + * @hwdev: device to unmap + * @sg: scatterlist to unmap + * @nents: number of scatterlist entries + * @direction: DMA direction + * + * Unmap a set of streaming mode DMA translations. Again, cpu read rules + * concerning calls here are the same as for pci_unmap_single() below. Also + * known as sn_pci_unmap_sg() by the IA64 machvec code. + */ +void +sn_pci_unmap_sg(struct pci_dev *hwdev, struct scatterlist *sg, int nents, + int direction) +{ + int i; + struct pcidev_info *pcidev_info = SN_PCIDEV_INFO(hwdev); + struct pcibus_bussoft *bussoft = SN_PCIDEV_BUSSOFT(hwdev); + + /* can't go anywhere w/o a direction in life */ + if (direction == PCI_DMA_NONE) + BUG(); + + if (! bussoft) { + return; + } + + for (i = 0; i < nents; i++, sg++) { + pcibr_dma_unmap(pcidev_info, sg->dma_address, direction); + sg->dma_address = (dma_addr_t) NULL; + sg->dma_length = 0; + } +} + +/** + * sn_pci_map_single - map a single region for DMA + * @hwdev: device to map for + * @ptr: kernel virtual address of the region to map + * @size: size of the region + * @direction: DMA direction + * + * Map the region pointed to by @ptr for DMA and return the + * DMA address. Also known as platform_pci_map_single() by + * the IA64 machvec code. + * + * We map this to the one step pcibr_dmamap_trans interface rather than + * the two step pcibr_dmamap_alloc/pcibr_dmamap_addr because we have + * no way of saving the dmamap handle from the alloc to later free + * (which is pretty much unacceptable). + * + * TODO: simplify our interface; + * get rid of dev_desc and vhdl (seems redundant given a pci_dev); + * figure out how to save dmamap handle so can use two step. + */ +dma_addr_t +sn_pci_map_single(struct pci_dev *hwdev, void *ptr, size_t size, int direction) +{ + dma_addr_t dma_addr; + unsigned long phys_addr; + struct pcidev_info *pcidev_info = SN_PCIDEV_INFO(hwdev); + struct pcibus_bussoft *bussoft = SN_PCIDEV_BUSSOFT(hwdev); + + if (direction == PCI_DMA_NONE) + BUG(); + + if (bussoft == NULL) { + return 0; + } + + if (! IS_PCI_BRIDGE_ASIC(bussoft->bs_asic_type)) { + return 0; /* unsupported asic type */ + } + + /* SN cannot support DMA addresses smaller than 32 bits. */ + if (hwdev->dma_mask < 0x7fffffff) + return 0; + + /* + * Call our dmamap interface + */ + + phys_addr = __pa(ptr); + dma_addr = pcibr_dma_map(pcidev_info, phys_addr, size, 0); + if (!dma_addr) { + printk(KERN_ERR "pci_map_single: Unable to allocate anymore " + "page map entries.\n"); + return 0; + } + return ((dma_addr_t) dma_addr); +} + +/** + * sn_pci_dma_sync_single_* - make sure all DMAs or CPU accesses + * have completed + * @hwdev: device to sync + * @dma_handle: DMA address to sync + * @size: size of region + * @direction: DMA direction + * + * This routine is supposed to sync the DMA region specified + * by @dma_handle into the 'coherence domain'. We do not need to do + * anything on our platform. + */ +void +sn_pci_unmap_single(struct pci_dev *hwdev, dma_addr_t dma_addr, size_t size, + int direction) +{ + struct pcidev_info *pcidev_info = SN_PCIDEV_INFO(hwdev); + struct pcibus_bussoft *bussoft = SN_PCIDEV_BUSSOFT(hwdev); + + if (direction == PCI_DMA_NONE) + BUG(); + + if (bussoft == NULL) { + return; + } + + if (! IS_PCI_BRIDGE_ASIC(bussoft->bs_asic_type)) { + return; /* unsupported asic type */ + } + + pcibr_dma_unmap(pcidev_info, dma_addr, direction); +} + +/** + * sn_dma_supported - test a DMA mask + * @hwdev: device to test + * @mask: DMA mask to test + * + * Return whether the given PCI device DMA address mask can be supported + * properly. For example, if your device can only drive the low 24-bits + * during PCI bus mastering, then you would pass 0x00ffffff as the mask to + * this function. Of course, SN only supports devices that have 32 or more + * address bits when using the PMU. We could theoretically support <32 bit + * cards using direct mapping, but we'll worry about that later--on the off + * chance that someone actually wants to use such a card. + */ +int sn_pci_dma_supported(struct pci_dev *hwdev, u64 mask) +{ + if (mask < 0x7fffffff) + return 0; + return 1; +} + +/* + * New generic DMA routines just wrap sn2 PCI routines until we + * support other bus types (if ever). + */ + +int sn_dma_supported(struct device *dev, u64 mask) +{ + BUG_ON(dev->bus != &pci_bus_type); + + return sn_pci_dma_supported(to_pci_dev(dev), mask); +} + +EXPORT_SYMBOL(sn_dma_supported); + +int sn_dma_set_mask(struct device *dev, u64 dma_mask) +{ + BUG_ON(dev->bus != &pci_bus_type); + + if (!sn_dma_supported(dev, dma_mask)) + return 0; + + *dev->dma_mask = dma_mask; + return 1; +} + +EXPORT_SYMBOL(sn_dma_set_mask); + +void *sn_dma_alloc_coherent(struct device *dev, size_t size, + dma_addr_t * dma_handle, int flag) +{ + BUG_ON(dev->bus != &pci_bus_type); + + return sn_pci_alloc_consistent(to_pci_dev(dev), size, dma_handle); +} + +EXPORT_SYMBOL(sn_dma_alloc_coherent); + +void +sn_dma_free_coherent(struct device *dev, size_t size, void *cpu_addr, + dma_addr_t dma_handle) +{ + BUG_ON(dev->bus != &pci_bus_type); + + sn_pci_free_consistent(to_pci_dev(dev), size, cpu_addr, dma_handle); +} + +EXPORT_SYMBOL(sn_dma_free_coherent); + +dma_addr_t +sn_dma_map_single(struct device *dev, void *cpu_addr, size_t size, + int direction) +{ + BUG_ON(dev->bus != &pci_bus_type); + + return sn_pci_map_single(to_pci_dev(dev), cpu_addr, size, + (int)direction); +} + +EXPORT_SYMBOL(sn_dma_map_single); + +void +sn_dma_unmap_single(struct device *dev, dma_addr_t dma_addr, size_t size, + int direction) +{ + BUG_ON(dev->bus != &pci_bus_type); + + sn_pci_unmap_single(to_pci_dev(dev), dma_addr, size, (int)direction); +} + +EXPORT_SYMBOL(sn_dma_unmap_single); + +dma_addr_t +sn_dma_map_page(struct device *dev, struct page *page, + unsigned long offset, size_t size, int direction) +{ + BUG_ON(dev->bus != &pci_bus_type); + + return pci_map_page(to_pci_dev(dev), page, offset, size, + (int)direction); +} + +EXPORT_SYMBOL(sn_dma_map_page); + +void +sn_dma_unmap_page(struct device *dev, dma_addr_t dma_address, size_t size, + int direction) +{ + BUG_ON(dev->bus != &pci_bus_type); + + pci_unmap_page(to_pci_dev(dev), dma_address, size, (int)direction); +} + +EXPORT_SYMBOL(sn_dma_unmap_page); + +int +sn_dma_map_sg(struct device *dev, struct scatterlist *sg, int nents, + int direction) +{ + BUG_ON(dev->bus != &pci_bus_type); + + return sn_pci_map_sg(to_pci_dev(dev), sg, nents, (int)direction); +} + +EXPORT_SYMBOL(sn_dma_map_sg); + +void +sn_dma_unmap_sg(struct device *dev, struct scatterlist *sg, int nhwentries, + int direction) +{ + BUG_ON(dev->bus != &pci_bus_type); + + sn_pci_unmap_sg(to_pci_dev(dev), sg, nhwentries, (int)direction); +} + +EXPORT_SYMBOL(sn_dma_unmap_sg); + +void +sn_dma_sync_single_for_cpu(struct device *dev, dma_addr_t dma_handle, + size_t size, int direction) +{ + BUG_ON(dev->bus != &pci_bus_type); +} + +EXPORT_SYMBOL(sn_dma_sync_single_for_cpu); + +void +sn_dma_sync_single_for_device(struct device *dev, dma_addr_t dma_handle, + size_t size, int direction) +{ + BUG_ON(dev->bus != &pci_bus_type); +} + +EXPORT_SYMBOL(sn_dma_sync_single_for_device); + +void +sn_dma_sync_sg_for_cpu(struct device *dev, struct scatterlist *sg, int nelems, + int direction) +{ + BUG_ON(dev->bus != &pci_bus_type); +} + +EXPORT_SYMBOL(sn_dma_sync_sg_for_cpu); + +void +sn_dma_sync_sg_for_device(struct device *dev, struct scatterlist *sg, + int nelems, int direction) +{ + BUG_ON(dev->bus != &pci_bus_type); +} + +int sn_dma_mapping_error(dma_addr_t dma_addr) +{ + return 0; +} + +EXPORT_SYMBOL(sn_dma_sync_sg_for_device); +EXPORT_SYMBOL(sn_pci_unmap_single); +EXPORT_SYMBOL(sn_pci_map_single); +EXPORT_SYMBOL(sn_pci_map_sg); +EXPORT_SYMBOL(sn_pci_unmap_sg); +EXPORT_SYMBOL(sn_pci_alloc_consistent); +EXPORT_SYMBOL(sn_pci_free_consistent); +EXPORT_SYMBOL(sn_pci_dma_supported); +EXPORT_SYMBOL(sn_dma_mapping_error); diff --git a/arch/ia64/sn/pci/pcibr/Makefile b/arch/ia64/sn/pci/pcibr/Makefile new file mode 100644 index 000000000..1850c4a94 --- /dev/null +++ b/arch/ia64/sn/pci/pcibr/Makefile @@ -0,0 +1,11 @@ +# +# This file is subject to the terms and conditions of the GNU General Public +# License. See the file "COPYING" in the main directory of this archive +# for more details. +# +# Copyright (C) 2002-2004 Silicon Graphics, Inc. All Rights Reserved. +# +# Makefile for the sn2 io routines. + +obj-y += pcibr_dma.o pcibr_reg.o \ + pcibr_ate.o pcibr_provider.o diff --git a/arch/ia64/sn/pci/pcibr/pcibr_ate.c b/arch/ia64/sn/pci/pcibr/pcibr_ate.c new file mode 100644 index 000000000..9d6854666 --- /dev/null +++ b/arch/ia64/sn/pci/pcibr/pcibr_ate.c @@ -0,0 +1,188 @@ +/* + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file "COPYING" in the main directory of this archive + * for more details. + * + * Copyright (C) 2001-2004 Silicon Graphics, Inc. All rights reserved. + */ + +#include +#include +#include "pci/pcibus_provider_defs.h" +#include "pci/pcidev.h" +#include "pci/pcibr_provider.h" + +int pcibr_invalidate_ate = 0; /* by default don't invalidate ATE on free */ + +/* + * mark_ate: Mark the ate as either free or inuse. + */ +static void mark_ate(struct ate_resource *ate_resource, int start, int number, + uint64_t value) +{ + + uint64_t *ate = ate_resource->ate; + int index; + int length = 0; + + for (index = start; length < number; index++, length++) + ate[index] = value; + +} + +/* + * find_free_ate: Find the first free ate index starting from the given + * index for the desired consequtive count. + */ +static int find_free_ate(struct ate_resource *ate_resource, int start, + int count) +{ + + uint64_t *ate = ate_resource->ate; + int index; + int start_free; + + for (index = start; index < ate_resource->num_ate;) { + if (!ate[index]) { + int i; + int free; + free = 0; + start_free = index; /* Found start free ate */ + for (i = start_free; i < ate_resource->num_ate; i++) { + if (!ate[i]) { /* This is free */ + if (++free == count) + return start_free; + } else { + index = i + 1; + break; + } + } + } else + index++; /* Try next ate */ + } + + return -1; +} + +/* + * free_ate_resource: Free the requested number of ATEs. + */ +static inline void free_ate_resource(struct ate_resource *ate_resource, + int start) +{ + + mark_ate(ate_resource, start, ate_resource->ate[start], 0); + if ((ate_resource->lowest_free_index > start) || + (ate_resource->lowest_free_index < 0)) + ate_resource->lowest_free_index = start; + +} + +/* + * alloc_ate_resource: Allocate the requested number of ATEs. + */ +static inline int alloc_ate_resource(struct ate_resource *ate_resource, + int ate_needed) +{ + + int start_index; + + /* + * Check for ate exhaustion. + */ + if (ate_resource->lowest_free_index < 0) + return -1; + + /* + * Find the required number of free consequtive ates. + */ + start_index = + find_free_ate(ate_resource, ate_resource->lowest_free_index, + ate_needed); + if (start_index >= 0) + mark_ate(ate_resource, start_index, ate_needed, ate_needed); + + ate_resource->lowest_free_index = + find_free_ate(ate_resource, ate_resource->lowest_free_index, 1); + + return start_index; +} + +/* + * Allocate "count" contiguous Bridge Address Translation Entries + * on the specified bridge to be used for PCI to XTALK mappings. + * Indices in rm map range from 1..num_entries. Indicies returned + * to caller range from 0..num_entries-1. + * + * Return the start index on success, -1 on failure. + */ +int pcibr_ate_alloc(struct pcibus_info *pcibus_info, int count) +{ + int status = 0; + uint64_t flag; + + flag = pcibr_lock(pcibus_info); + status = alloc_ate_resource(&pcibus_info->pbi_int_ate_resource, count); + + if (status < 0) { + /* Failed to allocate */ + pcibr_unlock(pcibus_info, flag); + return -1; + } + + pcibr_unlock(pcibus_info, flag); + + return status; +} + +/* + * Setup an Address Translation Entry as specified. Use either the Bridge + * internal maps or the external map RAM, as appropriate. + */ +static inline uint64_t *pcibr_ate_addr(struct pcibus_info *pcibus_info, + int ate_index) +{ + if (ate_index < pcibus_info->pbi_int_ate_size) { + return pcireg_int_ate_addr(pcibus_info, ate_index); + } + panic("pcibr_ate_addr: invalid ate_index 0x%x", ate_index); +} + +/* + * Update the ate. + */ +void inline +ate_write(struct pcibus_info *pcibus_info, int ate_index, int count, + volatile uint64_t ate) +{ + while (count-- > 0) { + if (ate_index < pcibus_info->pbi_int_ate_size) { + pcireg_int_ate_set(pcibus_info, ate_index, ate); + } else { + panic("ate_write: invalid ate_index 0x%x", ate_index); + } + ate_index++; + ate += IOPGSIZE; + } + + pcireg_tflush_get(pcibus_info); /* wait until Bridge PIO complete */ +} + +void pcibr_ate_free(struct pcibus_info *pcibus_info, int index) +{ + + volatile uint64_t ate; + int count; + uint64_t flags; + + if (pcibr_invalidate_ate) { + /* For debugging purposes, clear the valid bit in the ATE */ + ate = *pcibr_ate_addr(pcibus_info, index); + count = pcibus_info->pbi_int_ate_resource.ate[index]; + ate_write(pcibus_info, index, count, (ate & ~PCI32_ATE_V)); + } + + flags = pcibr_lock(pcibus_info); + free_ate_resource(&pcibus_info->pbi_int_ate_resource, index); + pcibr_unlock(pcibus_info, flags); +} diff --git a/arch/ia64/sn/pci/pcibr/pcibr_dma.c b/arch/ia64/sn/pci/pcibr/pcibr_dma.c new file mode 100644 index 000000000..b1d66ac06 --- /dev/null +++ b/arch/ia64/sn/pci/pcibr/pcibr_dma.c @@ -0,0 +1,379 @@ +/* + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file "COPYING" in the main directory of this archive + * for more details. + * + * Copyright (C) 2001-2004 Silicon Graphics, Inc. All rights reserved. + */ + +#include +#include +#include +#include +#include "xtalk/xwidgetdev.h" +#include "xtalk/hubdev.h" +#include "pci/pcibus_provider_defs.h" +#include "pci/pcidev.h" +#include "pci/tiocp.h" +#include "pci/pic.h" +#include "pci/pcibr_provider.h" +#include "pci/tiocp.h" +#include "tio.h" +#include + +extern int sn_ioif_inited; + +/* ===================================================================== + * DMA MANAGEMENT + * + * The Bridge ASIC provides three methods of doing DMA: via a "direct map" + * register available in 32-bit PCI space (which selects a contiguous 2G + * address space on some other widget), via "direct" addressing via 64-bit + * PCI space (all destination information comes from the PCI address, + * including transfer attributes), and via a "mapped" region that allows + * a bunch of different small mappings to be established with the PMU. + * + * For efficiency, we most prefer to use the 32bit direct mapping facility, + * since it requires no resource allocations. The advantage of using the + * PMU over the 64-bit direct is that single-cycle PCI addressing can be + * used; the advantage of using 64-bit direct over PMU addressing is that + * we do not have to allocate entries in the PMU. + */ + +static uint64_t +pcibr_dmamap_ate32(struct pcidev_info *info, + uint64_t paddr, size_t req_size, uint64_t flags) +{ + + struct pcidev_info *pcidev_info = info->pdi_host_pcidev_info; + struct pcibus_info *pcibus_info = (struct pcibus_info *)pcidev_info-> + pdi_pcibus_info; + uint8_t internal_device = (PCI_SLOT(pcidev_info->pdi_host_pcidev_info-> + pdi_linux_pcidev->devfn)) - 1; + int ate_count; + int ate_index; + uint64_t ate_flags = flags | PCI32_ATE_V; + uint64_t ate; + uint64_t pci_addr; + uint64_t xio_addr; + uint64_t offset; + + /* PIC in PCI-X mode does not supports 32bit PageMap mode */ + if (IS_PIC_SOFT(pcibus_info) && IS_PCIX(pcibus_info)) { + return 0; + } + + /* Calculate the number of ATEs needed. */ + if (!(MINIMAL_ATE_FLAG(paddr, req_size))) { + ate_count = IOPG((IOPGSIZE - 1) /* worst case start offset */ + +req_size /* max mapping bytes */ + - 1) + 1; /* round UP */ + } else { /* assume requested target is page aligned */ + ate_count = IOPG(req_size /* max mapping bytes */ + - 1) + 1; /* round UP */ + } + + /* Get the number of ATEs required. */ + ate_index = pcibr_ate_alloc(pcibus_info, ate_count); + if (ate_index < 0) + return 0; + + /* In PCI-X mode, Prefetch not supported */ + if (IS_PCIX(pcibus_info)) + ate_flags &= ~(PCI32_ATE_PREF); + + xio_addr = + IS_PIC_SOFT(pcibus_info) ? PHYS_TO_DMA(paddr) : + PHYS_TO_TIODMA(paddr); + offset = IOPGOFF(xio_addr); + ate = ate_flags | (xio_addr - offset); + + /* If PIC, put the targetid in the ATE */ + if (IS_PIC_SOFT(pcibus_info)) { + ate |= (pcibus_info->pbi_hub_xid << PIC_ATE_TARGETID_SHFT); + } + ate_write(pcibus_info, ate_index, ate_count, ate); + + /* + * Set up the DMA mapped Address. + */ + pci_addr = PCI32_MAPPED_BASE + offset + IOPGSIZE * ate_index; + + /* + * If swap was set in device in pcibr_endian_set() + * we need to turn swapping on. + */ + if (pcibus_info->pbi_devreg[internal_device] & PCIBR_DEV_SWAP_DIR) + ATE_SWAP_ON(pci_addr); + + return pci_addr; +} + +static uint64_t +pcibr_dmatrans_direct64(struct pcidev_info * info, uint64_t paddr, + uint64_t dma_attributes) +{ + struct pcibus_info *pcibus_info = (struct pcibus_info *) + ((info->pdi_host_pcidev_info)->pdi_pcibus_info); + uint64_t pci_addr; + + /* Translate to Crosstalk View of Physical Address */ + pci_addr = (IS_PIC_SOFT(pcibus_info) ? PHYS_TO_DMA(paddr) : + PHYS_TO_TIODMA(paddr)) | dma_attributes; + + /* Handle Bus mode */ + if (IS_PCIX(pcibus_info)) + pci_addr &= ~PCI64_ATTR_PREF; + + /* Handle Bridge Chipset differences */ + if (IS_PIC_SOFT(pcibus_info)) { + pci_addr |= + ((uint64_t) pcibus_info-> + pbi_hub_xid << PIC_PCI64_ATTR_TARG_SHFT); + } else + pci_addr |= TIOCP_PCI64_CMDTYPE_MEM; + + /* If PCI mode, func zero uses VCHAN0, every other func uses VCHAN1 */ + if (!IS_PCIX(pcibus_info) && PCI_FUNC(info->pdi_linux_pcidev->devfn)) + pci_addr |= PCI64_ATTR_VIRTUAL; + + return pci_addr; + +} + +static uint64_t +pcibr_dmatrans_direct32(struct pcidev_info * info, + uint64_t paddr, size_t req_size, uint64_t flags) +{ + + struct pcidev_info *pcidev_info = info->pdi_host_pcidev_info; + struct pcibus_info *pcibus_info = (struct pcibus_info *)pcidev_info-> + pdi_pcibus_info; + uint64_t xio_addr; + + uint64_t xio_base; + uint64_t offset; + uint64_t endoff; + + if (IS_PCIX(pcibus_info)) { + return 0; + } + + xio_addr = IS_PIC_SOFT(pcibus_info) ? PHYS_TO_DMA(paddr) : + PHYS_TO_TIODMA(paddr); + + xio_base = pcibus_info->pbi_dir_xbase; + offset = xio_addr - xio_base; + endoff = req_size + offset; + if ((req_size > (1ULL << 31)) || /* Too Big */ + (xio_addr < xio_base) || /* Out of range for mappings */ + (endoff > (1ULL << 31))) { /* Too Big */ + return 0; + } + + return PCI32_DIRECT_BASE | offset; + +} + +/* + * Wrapper routine for free'ing DMA maps + * DMA mappings for Direct 64 and 32 do not have any DMA maps. + */ +void +pcibr_dma_unmap(struct pcidev_info *pcidev_info, dma_addr_t dma_handle, + int direction) +{ + struct pcibus_info *pcibus_info = (struct pcibus_info *)pcidev_info-> + pdi_pcibus_info; + + if (IS_PCI32_MAPPED(dma_handle)) { + int ate_index; + + ate_index = + IOPG((ATE_SWAP_OFF(dma_handle) - PCI32_MAPPED_BASE)); + pcibr_ate_free(pcibus_info, ate_index); + } +} + +/* + * On SN systems there is a race condition between a PIO read response and + * DMA's. In rare cases, the read response may beat the DMA, causing the + * driver to think that data in memory is complete and meaningful. This code + * eliminates that race. This routine is called by the PIO read routines + * after doing the read. For PIC this routine then forces a fake interrupt + * on another line, which is logically associated with the slot that the PIO + * is addressed to. It then spins while watching the memory location that + * the interrupt is targetted to. When the interrupt response arrives, we + * are sure that the DMA has landed in memory and it is safe for the driver + * to proceed. For TIOCP use the Device(x) Write Request Buffer Flush + * Bridge register since it ensures the data has entered the coherence domain, + * unlike the PIC Device(x) Write Request Buffer Flush register. + */ + +void sn_dma_flush(uint64_t addr) +{ + nasid_t nasid; + int is_tio; + int wid_num; + int i, j; + int bwin; + uint64_t flags; + struct hubdev_info *hubinfo; + volatile struct sn_flush_device_list *p; + struct sn_flush_nasid_entry *flush_nasid_list; + + if (!sn_ioif_inited) + return; + + nasid = NASID_GET(addr); + if (-1 == nasid_to_cnodeid(nasid)) + return; + + hubinfo = (NODEPDA(nasid_to_cnodeid(nasid)))->pdinfo; + + if (!hubinfo) { + BUG(); + } + is_tio = (nasid & 1); + if (is_tio) { + wid_num = TIO_SWIN_WIDGETNUM(addr); + bwin = TIO_BWIN_WINDOWNUM(addr); + } else { + wid_num = SWIN_WIDGETNUM(addr); + bwin = BWIN_WINDOWNUM(addr); + } + + flush_nasid_list = &hubinfo->hdi_flush_nasid_list; + if (flush_nasid_list->widget_p == NULL) + return; + if (bwin > 0) { + uint64_t itte = flush_nasid_list->iio_itte[bwin]; + + if (is_tio) { + wid_num = (itte >> TIO_ITTE_WIDGET_SHIFT) & + TIO_ITTE_WIDGET_MASK; + } else { + wid_num = (itte >> IIO_ITTE_WIDGET_SHIFT) & + IIO_ITTE_WIDGET_MASK; + } + } + if (flush_nasid_list->widget_p == NULL) + return; + if (flush_nasid_list->widget_p[wid_num] == NULL) + return; + p = &flush_nasid_list->widget_p[wid_num][0]; + + /* find a matching BAR */ + for (i = 0; i < DEV_PER_WIDGET; i++) { + for (j = 0; j < PCI_ROM_RESOURCE; j++) { + if (p->sfdl_bar_list[j].start == 0) + break; + if (addr >= p->sfdl_bar_list[j].start + && addr <= p->sfdl_bar_list[j].end) + break; + } + if (j < PCI_ROM_RESOURCE && p->sfdl_bar_list[j].start != 0) + break; + p++; + } + + /* if no matching BAR, return without doing anything. */ + if (i == DEV_PER_WIDGET) + return; + + /* + * For TIOCP use the Device(x) Write Request Buffer Flush Bridge + * register since it ensures the data has entered the coherence + * domain, unlike PIC + */ + if (is_tio) { + uint32_t tio_id = REMOTE_HUB_L(nasid, TIO_NODE_ID); + uint32_t revnum = XWIDGET_PART_REV_NUM(tio_id); + + /* TIOCP BRINGUP WAR (PV907516): Don't write buffer flush reg */ + if ((1 << XWIDGET_PART_REV_NUM_REV(revnum)) & PV907516) { + return; + } else { + pcireg_wrb_flush_get(p->sfdl_pcibus_info, + (p->sfdl_slot - 1)); + } + } else { + spin_lock_irqsave(&((struct sn_flush_device_list *)p)-> + sfdl_flush_lock, flags); + + p->sfdl_flush_value = 0; + + /* force an interrupt. */ + *(volatile uint32_t *)(p->sfdl_force_int_addr) = 1; + + /* wait for the interrupt to come back. */ + while (*(p->sfdl_flush_addr) != 0x10f) ; + + /* okay, everything is synched up. */ + spin_unlock_irqrestore((spinlock_t *)&p->sfdl_flush_lock, flags); + } + return; +} + +/* + * Wrapper DMA interface. Called from pci_dma.c routines. + */ + +uint64_t +pcibr_dma_map(struct pcidev_info * pcidev_info, unsigned long phys_addr, + size_t size, unsigned int flags) +{ + dma_addr_t dma_handle; + struct pci_dev *pcidev = pcidev_info->pdi_linux_pcidev; + + if (flags & SN_PCIDMA_CONSISTENT) { + /* sn_pci_alloc_consistent interfaces */ + if (pcidev->dev.coherent_dma_mask == ~0UL) { + dma_handle = + pcibr_dmatrans_direct64(pcidev_info, phys_addr, + PCI64_ATTR_BAR); + } else { + dma_handle = + (dma_addr_t) pcibr_dmamap_ate32(pcidev_info, + phys_addr, size, + PCI32_ATE_BAR); + } + } else { + /* map_sg/map_single interfaces */ + + /* SN cannot support DMA addresses smaller than 32 bits. */ + if (pcidev->dma_mask < 0x7fffffff) { + return 0; + } + + if (pcidev->dma_mask == ~0UL) { + /* + * Handle the most common case: 64 bit cards. This + * call should always succeed. + */ + + dma_handle = + pcibr_dmatrans_direct64(pcidev_info, phys_addr, + PCI64_ATTR_PREF); + } else { + /* Handle 32-63 bit cards via direct mapping */ + dma_handle = + pcibr_dmatrans_direct32(pcidev_info, phys_addr, + size, 0); + if (!dma_handle) { + /* + * It is a 32 bit card and we cannot do direct mapping, + * so we use an ATE. + */ + + dma_handle = + pcibr_dmamap_ate32(pcidev_info, phys_addr, + size, PCI32_ATE_PREF); + } + } + } + + return dma_handle; +} + +EXPORT_SYMBOL(sn_dma_flush); diff --git a/arch/ia64/sn/pci/pcibr/pcibr_provider.c b/arch/ia64/sn/pci/pcibr/pcibr_provider.c new file mode 100644 index 000000000..92bd278cf --- /dev/null +++ b/arch/ia64/sn/pci/pcibr/pcibr_provider.c @@ -0,0 +1,170 @@ +/* + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file "COPYING" in the main directory of this archive + * for more details. + * + * Copyright (C) 2001-2004 Silicon Graphics, Inc. All rights reserved. + */ + +#include +#include +#include +#include +#include "xtalk/xwidgetdev.h" +#include +#include "xtalk/hubdev.h" +#include "pci/pcibus_provider_defs.h" +#include "pci/pcidev.h" +#include "pci/pcibr_provider.h" +#include + + +static int sal_pcibr_error_interrupt(struct pcibus_info *soft) +{ + struct ia64_sal_retval ret_stuff; + uint64_t busnum; + int segment; + ret_stuff.status = 0; + ret_stuff.v0 = 0; + + segment = 0; + busnum = soft->pbi_buscommon.bs_persist_busnum; + SAL_CALL_NOLOCK(ret_stuff, + (u64) SN_SAL_IOIF_ERROR_INTERRUPT, + (u64) segment, (u64) busnum, 0, 0, 0, 0, 0); + + return (int)ret_stuff.v0; +} + +/* + * PCI Bridge Error interrupt handler. Gets invoked whenever a PCI + * bridge sends an error interrupt. + */ +static irqreturn_t +pcibr_error_intr_handler(int irq, void *arg, struct pt_regs *regs) +{ + struct pcibus_info *soft = (struct pcibus_info *)arg; + + if (sal_pcibr_error_interrupt(soft) < 0) { + panic("pcibr_error_intr_handler(): Fatal Bridge Error"); + } + return IRQ_HANDLED; +} + +void * +pcibr_bus_fixup(struct pcibus_bussoft *prom_bussoft) +{ + int nasid, cnode, j; + struct hubdev_info *hubdev_info; + struct pcibus_info *soft; + struct sn_flush_device_list *sn_flush_device_list; + + if (! IS_PCI_BRIDGE_ASIC(prom_bussoft->bs_asic_type)) { + return NULL; + } + + /* + * Allocate kernel bus soft and copy from prom. + */ + + soft = kmalloc(sizeof(struct pcibus_info), GFP_KERNEL); + if (!soft) { + return NULL; + } + + memcpy(soft, prom_bussoft, sizeof(struct pcibus_info)); + soft->pbi_buscommon.bs_base = + (((u64) soft->pbi_buscommon. + bs_base << 4) >> 4) | __IA64_UNCACHED_OFFSET; + + spin_lock_init(&soft->pbi_lock); + + /* + * register the bridge's error interrupt handler + */ + if (request_irq(SGI_PCIBR_ERROR, (void *)pcibr_error_intr_handler, + SA_SHIRQ, "PCIBR error", (void *)(soft))) { + printk(KERN_WARNING + "pcibr cannot allocate interrupt for error handler\n"); + } + + /* + * Update the Bridge with the "kernel" pagesize + */ + if (PAGE_SIZE < 16384) { + pcireg_control_bit_clr(soft, PCIBR_CTRL_PAGE_SIZE); + } else { + pcireg_control_bit_set(soft, PCIBR_CTRL_PAGE_SIZE); + } + + nasid = NASID_GET(soft->pbi_buscommon.bs_base); + cnode = nasid_to_cnodeid(nasid); + hubdev_info = (struct hubdev_info *)(NODEPDA(cnode)->pdinfo); + + if (hubdev_info->hdi_flush_nasid_list.widget_p) { + sn_flush_device_list = hubdev_info->hdi_flush_nasid_list. + widget_p[(int)soft->pbi_buscommon.bs_xid]; + if (sn_flush_device_list) { + for (j = 0; j < DEV_PER_WIDGET; + j++, sn_flush_device_list++) { + if (sn_flush_device_list->sfdl_slot == -1) + continue; + if (sn_flush_device_list-> + sfdl_persistent_busnum == + soft->pbi_buscommon.bs_persist_busnum) + sn_flush_device_list->sfdl_pcibus_info = + soft; + } + } + } + + /* Setup the PMU ATE map */ + soft->pbi_int_ate_resource.lowest_free_index = 0; + soft->pbi_int_ate_resource.ate = + kmalloc(soft->pbi_int_ate_size * sizeof(uint64_t), GFP_KERNEL); + memset(soft->pbi_int_ate_resource.ate, 0, + (soft->pbi_int_ate_size * sizeof(uint64_t))); + + return soft; +} + +void pcibr_force_interrupt(struct sn_irq_info *sn_irq_info) +{ + struct pcidev_info *pcidev_info; + struct pcibus_info *pcibus_info; + int bit = sn_irq_info->irq_int_bit; + + pcidev_info = (struct pcidev_info *)sn_irq_info->irq_pciioinfo; + if (pcidev_info) { + pcibus_info = + (struct pcibus_info *)pcidev_info->pdi_host_pcidev_info-> + pdi_pcibus_info; + pcireg_force_intr_set(pcibus_info, bit); + } +} + +void pcibr_change_devices_irq(struct sn_irq_info *sn_irq_info) +{ + struct pcidev_info *pcidev_info; + struct pcibus_info *pcibus_info; + int bit = sn_irq_info->irq_int_bit; + uint64_t xtalk_addr = sn_irq_info->irq_xtalkaddr; + + pcidev_info = (struct pcidev_info *)sn_irq_info->irq_pciioinfo; + if (pcidev_info) { + pcibus_info = + (struct pcibus_info *)pcidev_info->pdi_host_pcidev_info-> + pdi_pcibus_info; + + /* Disable the device's IRQ */ + pcireg_intr_enable_bit_clr(pcibus_info, bit); + + /* Change the device's IRQ */ + pcireg_intr_addr_addr_set(pcibus_info, bit, xtalk_addr); + + /* Re-enable the device's IRQ */ + pcireg_intr_enable_bit_set(pcibus_info, bit); + + pcibr_force_interrupt(sn_irq_info); + } +} diff --git a/arch/ia64/sn/pci/pcibr/pcibr_reg.c b/arch/ia64/sn/pci/pcibr/pcibr_reg.c new file mode 100644 index 000000000..74a74a7d2 --- /dev/null +++ b/arch/ia64/sn/pci/pcibr/pcibr_reg.c @@ -0,0 +1,282 @@ +/* + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file "COPYING" in the main directory of this archive + * for more details. + * + * Copyright (C) 2004 Silicon Graphics, Inc. All rights reserved. + */ + +#include +#include +#include "pci/pcibus_provider_defs.h" +#include "pci/pcidev.h" +#include "pci/tiocp.h" +#include "pci/pic.h" +#include "pci/pcibr_provider.h" + +union br_ptr { + struct tiocp tio; + struct pic pic; +}; + +/* + * Control Register Access -- Read/Write 0000_0020 + */ +void pcireg_control_bit_clr(struct pcibus_info *pcibus_info, uint64_t bits) +{ + union br_ptr *ptr = (union br_ptr *)pcibus_info->pbi_buscommon.bs_base; + + if (pcibus_info) { + switch (pcibus_info->pbi_bridge_type) { + case PCIBR_BRIDGETYPE_TIOCP: + ptr->tio.cp_control &= ~bits; + break; + case PCIBR_BRIDGETYPE_PIC: + ptr->pic.p_wid_control &= ~bits; + break; + default: + panic + ("pcireg_control_bit_clr: unknown bridgetype bridge 0x%p", + (void *)ptr); + } + } +} + +void pcireg_control_bit_set(struct pcibus_info *pcibus_info, uint64_t bits) +{ + union br_ptr *ptr = (union br_ptr *)pcibus_info->pbi_buscommon.bs_base; + + if (pcibus_info) { + switch (pcibus_info->pbi_bridge_type) { + case PCIBR_BRIDGETYPE_TIOCP: + ptr->tio.cp_control |= bits; + break; + case PCIBR_BRIDGETYPE_PIC: + ptr->pic.p_wid_control |= bits; + break; + default: + panic + ("pcireg_control_bit_set: unknown bridgetype bridge 0x%p", + (void *)ptr); + } + } +} + +/* + * PCI/PCIX Target Flush Register Access -- Read Only 0000_0050 + */ +uint64_t pcireg_tflush_get(struct pcibus_info *pcibus_info) +{ + union br_ptr *ptr = (union br_ptr *)pcibus_info->pbi_buscommon.bs_base; + uint64_t ret = 0; + + if (pcibus_info) { + switch (pcibus_info->pbi_bridge_type) { + case PCIBR_BRIDGETYPE_TIOCP: + ret = ptr->tio.cp_tflush; + break; + case PCIBR_BRIDGETYPE_PIC: + ret = ptr->pic.p_wid_tflush; + break; + default: + panic + ("pcireg_tflush_get: unknown bridgetype bridge 0x%p", + (void *)ptr); + } + } + + /* Read of the Target Flush should always return zero */ + if (ret != 0) + panic("pcireg_tflush_get:Target Flush failed\n"); + + return ret; +} + +/* + * Interrupt Status Register Access -- Read Only 0000_0100 + */ +uint64_t pcireg_intr_status_get(struct pcibus_info * pcibus_info) +{ + union br_ptr *ptr = (union br_ptr *)pcibus_info->pbi_buscommon.bs_base; + uint64_t ret = 0; + + if (pcibus_info) { + switch (pcibus_info->pbi_bridge_type) { + case PCIBR_BRIDGETYPE_TIOCP: + ret = ptr->tio.cp_int_status; + break; + case PCIBR_BRIDGETYPE_PIC: + ret = ptr->pic.p_int_status; + break; + default: + panic + ("pcireg_intr_status_get: unknown bridgetype bridge 0x%p", + (void *)ptr); + } + } + return ret; +} + +/* + * Interrupt Enable Register Access -- Read/Write 0000_0108 + */ +void pcireg_intr_enable_bit_clr(struct pcibus_info *pcibus_info, uint64_t bits) +{ + union br_ptr *ptr = (union br_ptr *)pcibus_info->pbi_buscommon.bs_base; + + if (pcibus_info) { + switch (pcibus_info->pbi_bridge_type) { + case PCIBR_BRIDGETYPE_TIOCP: + ptr->tio.cp_int_enable &= ~bits; + break; + case PCIBR_BRIDGETYPE_PIC: + ptr->pic.p_int_enable &= ~bits; + break; + default: + panic + ("pcireg_intr_enable_bit_clr: unknown bridgetype bridge 0x%p", + (void *)ptr); + } + } +} + +void pcireg_intr_enable_bit_set(struct pcibus_info *pcibus_info, uint64_t bits) +{ + union br_ptr *ptr = (union br_ptr *)pcibus_info->pbi_buscommon.bs_base; + + if (pcibus_info) { + switch (pcibus_info->pbi_bridge_type) { + case PCIBR_BRIDGETYPE_TIOCP: + ptr->tio.cp_int_enable |= bits; + break; + case PCIBR_BRIDGETYPE_PIC: + ptr->pic.p_int_enable |= bits; + break; + default: + panic + ("pcireg_intr_enable_bit_set: unknown bridgetype bridge 0x%p", + (void *)ptr); + } + } +} + +/* + * Intr Host Address Register (int_addr) -- Read/Write 0000_0130 - 0000_0168 + */ +void pcireg_intr_addr_addr_set(struct pcibus_info *pcibus_info, int int_n, + uint64_t addr) +{ + union br_ptr *ptr = (union br_ptr *)pcibus_info->pbi_buscommon.bs_base; + + if (pcibus_info) { + switch (pcibus_info->pbi_bridge_type) { + case PCIBR_BRIDGETYPE_TIOCP: + ptr->tio.cp_int_addr[int_n] &= ~TIOCP_HOST_INTR_ADDR; + ptr->tio.cp_int_addr[int_n] |= + (addr & TIOCP_HOST_INTR_ADDR); + break; + case PCIBR_BRIDGETYPE_PIC: + ptr->pic.p_int_addr[int_n] &= ~PIC_HOST_INTR_ADDR; + ptr->pic.p_int_addr[int_n] |= + (addr & PIC_HOST_INTR_ADDR); + break; + default: + panic + ("pcireg_intr_addr_addr_get: unknown bridgetype bridge 0x%p", + (void *)ptr); + } + } +} + +/* + * Force Interrupt Register Access -- Write Only 0000_01C0 - 0000_01F8 + */ +void pcireg_force_intr_set(struct pcibus_info *pcibus_info, int int_n) +{ + union br_ptr *ptr = (union br_ptr *)pcibus_info->pbi_buscommon.bs_base; + + if (pcibus_info) { + switch (pcibus_info->pbi_bridge_type) { + case PCIBR_BRIDGETYPE_TIOCP: + ptr->tio.cp_force_pin[int_n] = 1; + break; + case PCIBR_BRIDGETYPE_PIC: + ptr->pic.p_force_pin[int_n] = 1; + break; + default: + panic + ("pcireg_force_intr_set: unknown bridgetype bridge 0x%p", + (void *)ptr); + } + } +} + +/* + * Device(x) Write Buffer Flush Reg Access -- Read Only 0000_0240 - 0000_0258 + */ +uint64_t pcireg_wrb_flush_get(struct pcibus_info *pcibus_info, int device) +{ + union br_ptr *ptr = (union br_ptr *)pcibus_info->pbi_buscommon.bs_base; + uint64_t ret = 0; + + if (pcibus_info) { + switch (pcibus_info->pbi_bridge_type) { + case PCIBR_BRIDGETYPE_TIOCP: + ret = ptr->tio.cp_wr_req_buf[device]; + break; + case PCIBR_BRIDGETYPE_PIC: + ret = ptr->pic.p_wr_req_buf[device]; + break; + default: + panic("pcireg_wrb_flush_get: unknown bridgetype bridge 0x%p", (void *)ptr); + } + + } + /* Read of the Write Buffer Flush should always return zero */ + return ret; +} + +void pcireg_int_ate_set(struct pcibus_info *pcibus_info, int ate_index, + uint64_t val) +{ + union br_ptr *ptr = (union br_ptr *)pcibus_info->pbi_buscommon.bs_base; + + if (pcibus_info) { + switch (pcibus_info->pbi_bridge_type) { + case PCIBR_BRIDGETYPE_TIOCP: + ptr->tio.cp_int_ate_ram[ate_index] = (uint64_t) val; + break; + case PCIBR_BRIDGETYPE_PIC: + ptr->pic.p_int_ate_ram[ate_index] = (uint64_t) val; + break; + default: + panic + ("pcireg_int_ate_set: unknown bridgetype bridge 0x%p", + (void *)ptr); + } + } +} + +uint64_t *pcireg_int_ate_addr(struct pcibus_info *pcibus_info, int ate_index) +{ + union br_ptr *ptr = (union br_ptr *)pcibus_info->pbi_buscommon.bs_base; + uint64_t *ret = (uint64_t *) 0; + + if (pcibus_info) { + switch (pcibus_info->pbi_bridge_type) { + case PCIBR_BRIDGETYPE_TIOCP: + ret = + (uint64_t *) & (ptr->tio.cp_int_ate_ram[ate_index]); + break; + case PCIBR_BRIDGETYPE_PIC: + ret = + (uint64_t *) & (ptr->pic.p_int_ate_ram[ate_index]); + break; + default: + panic + ("pcireg_int_ate_addr: unknown bridgetype bridge 0x%p", + (void *)ptr); + } + } + return ret; +} diff --git a/arch/m32r/Kconfig b/arch/m32r/Kconfig new file mode 100644 index 000000000..bacc0b298 --- /dev/null +++ b/arch/m32r/Kconfig @@ -0,0 +1,441 @@ +# +# For a description of the syntax of this configuration file, +# see Documentation/kbuild/kconfig-language.txt. +# + +mainmenu "Linux Kernel Configuration" + +config M32R + bool + default y + +config SBUS + bool + +config UID16 + bool + default y + +config GENERIC_ISA_DMA + bool + default y + +source "init/Kconfig" + + +menu "Processor type and features" + +choice + prompt "Platform Type" + default PLAT_MAPPI + +config PLAT_MAPPI + bool "Mappi-I" + help + The Mappi-I is an FPGA board for SOC (System-On-a-Chip) prototyping. + You can operate a Linux system on this board by using an M32R + softmacro core, which is a fully-synthesizable functional model + described in Verilog-HDL. + + The Mappi-I board was the first platform, which had been used + to port and develop a Linux system for the M32R processor. + Currently, the Mappi-II, an heir to the Mappi-I, is available. + +config PLAT_USRV + bool "uServer" + +config PLAT_M32700UT + bool "M32700UT" + help + The M3T-M32700UT is an evaluation board based on uT-Engine + specification. This board has an M32700 (Chaos) evaluation chip. + You can say Y for SMP, because the M32700 is a single chip + multiprocessor. + +config PLAT_OPSPUT + bool "OPSPUT" + help + The OPSPUT is an evaluation board based on uT-Engine + specification. This board has a OPSP-REP chip. + +config PLAT_OAKS32R + bool "OAKS32R" + help + The OAKS32R is a tiny, inexpensive evaluation board. + Please note that if you say Y here and choose chip "M32102", + say N for MMU and select a no-MMU version kernel, otherwise + a kernel with MMU support will not work, because the M32102 + is a microcontroller for embedded systems and it has no MMU. + +config PLAT_MAPPI2 + bool "Mappi-II(M3A-ZA36/M3A-ZA52)" + +endchoice + +choice + prompt "Processor family" + default CHIP_M32700 + +config CHIP_M32700 + bool "M32700 (Chaos)" + +config CHIP_M32102 + bool "M32102" + +config CHIP_VDEC2 + bool "VDEC2" + +config CHIP_OPSP + bool "OPSP" + +endchoice + +config MMU + bool "Support for memory management hardware" + depends on CHIP_M32700 || CHIP_VDEC2 || CHIP_OPSP + default y + +config TLB_ENTRIES + int "TLB Entries" + depends on CHIP_M32700 || CHIP_VDEC2 || CHIP_OPSP + default 32 if CHIP_M32700 || CHIP_OPSP + default 16 if CHIP_VDEC2 + + +config ISA_M32R + bool + depends on CHIP_M32102 + default y + +config ISA_M32R2 + bool + depends on CHIP_M32700 || CHIP_VDEC2 || CHIP_OPSP + default y + +config ISA_DSP_LEVEL2 + bool + depends on CHIP_M32700 || CHIP_OPSP + default y + +config ISA_DUAL_ISSUE + bool + depends on CHIP_M32700 || CHIP_OPSP + default y + +config BUS_CLOCK + int "Bus Clock [Hz] (integer)" + default "70000000" if PLAT_MAPPI + default "25000000" if PLAT_USRV + default "50000000" if PLAT_M32700UT + default "50000000" if PLAT_OPSPUT + default "33333333" if PLAT_OAKS32R + default "20000000" if PLAT_MAPPI2 + +config TIMER_DIVIDE + int "Timer divider (integer)" + default "128" + +config CPU_LITTLE_ENDIAN + bool "Generate little endian code" + default n + +config MEMORY_START + hex "Physical memory start address (hex)" + default "08000000" if PLAT_MAPPI || PLAT_MAPPI2 + default "08000000" if PLAT_USRV + default "08000000" if PLAT_M32700UT + default "08000000" if PLAT_OPSPUT + default "01000000" if PLAT_OAKS32R + +config MEMORY_SIZE + hex "Physical memory size (hex)" + default "04000000" if PLAT_MAPPI || PLAT_MAPPI2 + default "02000000" if PLAT_USRV + default "01000000" if PLAT_M32700UT + default "01000000" if PLAT_OPSPUT + default "00800000" if PLAT_OAKS32R + +config NOHIGHMEM + bool + default y + +config DISCONTIGMEM + bool "Internal RAM Support" + depends on CHIP_M32700 || CHIP_M32102 || CHIP_VDEC2 || CHIP_OPSP + default y + +config IRAM_START + hex "Internal memory start address (hex)" + default "00f00000" + depends on (CHIP_M32700 || CHIP_M32102 || CHIP_VDEC2 || CHIP_OPSP) && DISCONTIGMEM + +config IRAM_SIZE + hex "Internal memory size (hex)" + depends on (CHIP_M32700 || CHIP_M32102 || CHIP_VDEC2 || CHIP_OPSP) && DISCONTIGMEM + default "00080000" if CHIP_M32700 + default "00010000" if CHIP_M32102 || CHIP_OPSP + default "00008000" if CHIP_VDEC2 + +# +# Define implied options from the CPU selection here +# + +config RWSEM_GENERIC_SPINLOCK + bool + depends on M32R + default y + +config RWSEM_XCHGADD_ALGORITHM + bool + default n + +config PREEMPT + bool "Preemptible Kernel" + help + This option reduces the latency of the kernel when reacting to + real-time or interactive events by allowing a low priority process to + be preempted even if it is in kernel mode executing a system call. + This allows applications to run more reliably even when the system is + under load. + + Say Y here if you are building a kernel for a desktop, embedded + or real-time system. Say N if you are unsure. + +config HAVE_DEC_LOCK + bool + depends on (SMP || PREEMPT) + default n + +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, like most personal computers, 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 many, but not all, + singleprocessor machines. On a singleprocessor machine, the kernel + will run faster if you say N here. + + People using multiprocessor machines who say Y here should also say + Y to "Enhanced Real Time Clock Support", below. The "Advanced Power + Management" code will be disabled if you say Y here. + + See also the , + and the SMP-HOWTO available at + . + + If you don't know what to do here, say N. + +config CHIP_M32700_TS1 + bool "Workaround code for the M32700 TS1 chip's bug" + depends on (CHIP_M32700 && SMP) + default n + +config NR_CPUS + int "Maximum number of CPUs (2-32)" + range 2 32 + depends on SMP + default "2" + help + This allows you to specify the maximum number of CPUs which this + kernel will support. The maximum supported value is 32 and the + minimum value which makes sense is 2. + + This is purely to save memory - each supported CPU adds + approximately eight kilobytes to the kernel image. + +# Common NUMA Features +config NUMA + bool "Numa Memory Allocation Support" + depends on SMP + default n + +# turning this on wastes a bunch of space. +# Summit needs it only when NUMA is on +config BOOT_IOREMAP + bool + depends on NUMA + default n + +endmenu + + +menu "Bus options (PCI, PCMCIA, EISA, MCA, ISA)" + +config PCI + bool "PCI support" + default n + help + Find out whether you have a PCI motherboard. PCI is the name of a + bus system, i.e. the way the CPU talks to the other stuff inside + your box. Other bus systems are ISA, EISA, MicroChannel (MCA) or + VESA. If you have PCI, say Y, otherwise N. + + The PCI-HOWTO, available from + , contains valuable + information about which PCI hardware does work under Linux and which + doesn't. + +choice + prompt "PCI access mode" + depends on PCI + default PCI_GOANY + +config PCI_GOBIOS + bool "BIOS" + ---help--- + On PCI systems, the BIOS can be used to detect the PCI devices and + determine their configuration. However, some old PCI motherboards + have BIOS bugs and may crash if this is done. Also, some embedded + PCI-based systems don't have any BIOS at all. Linux can also try to + detect the PCI hardware directly without using the BIOS. + + With this option, you can specify how Linux should detect the PCI + devices. If you choose "BIOS", the BIOS will be used, if you choose + "Direct", the BIOS won't be used, and if you choose "Any", the + kernel will try the direct access method and falls back to the BIOS + if that doesn't work. If unsure, go with the default, which is + "Any". + +config PCI_GODIRECT + bool "Direct" + +config PCI_GOANY + bool "Any" + +endchoice + +config PCI_BIOS + bool + depends on PCI && (PCI_GOBIOS || PCI_GOANY) + default y + +config PCI_DIRECT + bool + depends on PCI && (PCI_GODIRECT || PCI_GOANY) + default y + +source "drivers/pci/Kconfig" + +config ISA + bool "ISA support" + help + Find out whether you have ISA slots on your motherboard. ISA is the + name of a bus system, i.e. the way the CPU talks to the other stuff + inside your box. If you have ISA, say Y, otherwise N. + +source "drivers/pcmcia/Kconfig" + +source "drivers/pci/hotplug/Kconfig" + +endmenu + + +menu "Executable file formats" + +source "fs/Kconfig.binfmt" + +endmenu + +source "drivers/Kconfig" + +source "fs/Kconfig" + +source "arch/m32r/oprofile/Kconfig" + +menu "Kernel hacking" + +config DEBUG_KERNEL + bool "Kernel debugging" + help + Say Y here if you are developing drivers or trying to debug and + identify kernel problems. + +config DEBUG_STACKOVERFLOW + bool "Check for stack overflows" + depends on DEBUG_KERNEL + +config DEBUG_SLAB + bool "Debug memory allocations" + depends on DEBUG_KERNEL + help + Say Y here to have the kernel do limited verification on memory + allocation as well as poisoning memory on free to catch use of freed + memory. + +config DEBUG_IOVIRT + bool "Memory mapped I/O debugging" + depends on DEBUG_KERNEL + help + Say Y here to get warned whenever an attempt is made to do I/O on + obviously invalid addresses such as those generated when ioremap() + calls are forgotten. Memory mapped I/O will go through an extra + check to catch access to unmapped ISA addresses, an access method + that can still be used by old drivers that are being ported from + 2.0/2.2. + +config MAGIC_SYSRQ + bool "Magic SysRq key" + depends on DEBUG_KERNEL + help + If you say Y here, you will have some control over the system even + if the system crashes for example during kernel debugging (e.g., you + will be able to flush the buffer cache to disk, reboot the system + immediately or dump some status information). This is accomplished + by pressing various keys while holding SysRq (Alt+PrintScreen). It + also works on a serial console (on PC hardware at least), if you + send a BREAK and then within 5 seconds a command keypress. The + keys are documented in . Don't say Y + unless you really know what this hack does. + +config DEBUG_SPINLOCK + bool "Spinlock debugging" + depends on DEBUG_KERNEL + help + Say Y here and build SMP to catch missing spinlock initialization + and certain other kinds of spinlock errors commonly made. This is + best used in conjunction with the NMI watchdog so that spinlock + deadlocks are also debuggable. + +config DEBUG_PAGEALLOC + bool "Page alloc debugging" + depends on DEBUG_KERNEL + 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 DEBUG_INFO + bool "Compile the kernel with debug info" + depends on DEBUG_KERNEL + help + If you say Y here the resulting kernel image will include + debugging info resulting in a larger kernel image. + Say Y here only if you plan to use gdb to debug the kernel. + If you don't debug the kernel, you can say N. + +config DEBUG_SPINLOCK_SLEEP + bool "Sleep-inside-spinlock checking" + help + If you say Y here, various routines which may sleep will become very + noisy if they are called with a spinlock held. + +config FRAME_POINTER + bool "Compile the kernel with frame pointers" + help + If you say Y here the resulting kernel image will be slightly larger + and slower, but it will give very useful debugging information. + If you don't debug the kernel, you can say N, but we may not be able + to solve problems without frame pointers. + +endmenu + +source "security/Kconfig" + +source "crypto/Kconfig" + +source "lib/Kconfig" + diff --git a/arch/m32r/Makefile b/arch/m32r/Makefile new file mode 100644 index 000000000..63ea62a5d --- /dev/null +++ b/arch/m32r/Makefile @@ -0,0 +1,55 @@ +# +# m32r/Makefile +# + +LDFLAGS := +OBJCOPYFLAGS := -O binary -R .note -R .comment -S +LDFLAGS_vmlinux := -e startup_32 +LDFLAGS_BLOB := --format binary --oformat elf32-m32r + +CFLAGS += -pipe -fno-schedule-insns +CFLAGS_KERNEL += -mmodel=medium +CFLAGS_MODULE += -mmodel=large + +ifdef CONFIG_CHIP_VDEC2 +cflags-$(CONFIG_ISA_M32R2) += -DNO_FPU -Wa,-bitinst +aflags-$(CONFIG_ISA_M32R2) += -DNO_FPU -Wa,-bitinst +else +cflags-$(CONFIG_ISA_M32R2) += -DNO_FPU -m32r2 +aflags-$(CONFIG_ISA_M32R2) += -DNO_FPU -m32r2 +endif + +cflags-$(CONFIG_ISA_M32R) += -DNO_FPU +aflags-$(CONFIG_ISA_M32R) += -DNO_FPU -Wa,-no-bitinst + +CFLAGS += $(cflags-y) +AFLAGS += $(aflags-y) + +CHECKFLAGS := $(CHECK) -D__m32r__ + +head-y := arch/m32r/kernel/head.o arch/m32r/kernel/init_task.o + +LIBGCC := $(shell $(CC) $(CFLAGS) -print-libgcc-file-name) + +libs-y += arch/m32r/lib/ $(LIBGCC) +core-y += arch/m32r/kernel/ \ + arch/m32r/mm/ \ + arch/m32r/boot/ + +drivers-$(CONFIG_OPROFILE) += arch/m32r/oprofile/ + +boot := arch/m32r/boot + +.PHONY: zImage + +zImage: vmlinux + $(Q)$(MAKE) $(build)=$(boot) $(boot)/$@ + +compressed: zImage + +archclean: + $(Q)$(MAKE) $(clean)=$(boot) + +define archhelp + @echo ' zImage - Compressed kernel image (arch/m32r/boot/zImage)' +endef diff --git a/arch/m32r/boot/Makefile b/arch/m32r/boot/Makefile new file mode 100644 index 000000000..af2cef475 --- /dev/null +++ b/arch/m32r/boot/Makefile @@ -0,0 +1,19 @@ +# +# arch/m32r/boot/Makefile +# +# 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. + +targets := zImage +subdir- := compressed + +obj-y := setup.o + +$(obj)/zImage: $(obj)/compressed/vmlinux FORCE + $(call if_changed,objcopy) + @echo 'Kernel: $@ is ready' + +$(obj)/compressed/vmlinux: FORCE + $(Q)$(MAKE) $(build)=$(obj)/compressed $@ + diff --git a/arch/m32r/boot/compressed/Makefile b/arch/m32r/boot/compressed/Makefile new file mode 100644 index 000000000..a8f130d5e --- /dev/null +++ b/arch/m32r/boot/compressed/Makefile @@ -0,0 +1,38 @@ +# +# linux/arch/sh/boot/compressed/Makefile +# +# create a compressed vmlinux image from the original vmlinux +# + +targets := vmlinux vmlinux.bin vmlinux.bin.gz head.o misc.o \ + m32r-sio.o piggy.o vmlinux.lds +EXTRA_AFLAGS := -traditional + +OBJECTS = $(obj)/head.o $(obj)/misc.o $(obj)/m32r_sio.o + +# +# IMAGE_OFFSET is the load offset of the compression loader +# +#IMAGE_OFFSET := $(shell printf "0x%08x" $$[$(CONFIG_MEMORY_START)+0x2000]) +#IMAGE_OFFSET := $(shell printf "0x%08x" $$[$(CONFIG_MEMORY_START)+0x00400000]) + +LDFLAGS_vmlinux := -T + +$(obj)/vmlinux: $(obj)/vmlinux.lds $(OBJECTS) $(obj)/piggy.o FORCE + $(call if_changed,ld) + @: + +$(obj)/vmlinux.bin: vmlinux FORCE + $(call if_changed,objcopy) + +$(obj)/vmlinux.bin.gz: $(obj)/vmlinux.bin FORCE + $(call if_changed,gzip) + +$(obj)/vmlinux.lds: $(obj)/vmlinux.lds.S FORCE + $(CPP) $(EXTRA_AFLAGS) -C -P -I include $< >$@ + +LDFLAGS_piggy.o := -r --format binary --oformat elf32-m32r-linux -T +OBJCOPYFLAGS += -R .empty_zero_page + +$(obj)/piggy.o: $(obj)/vmlinux.scr $(obj)/vmlinux.bin.gz FORCE + $(call if_changed,ld) diff --git a/arch/m32r/boot/compressed/boot.h b/arch/m32r/boot/compressed/boot.h new file mode 100644 index 000000000..9272e38d1 --- /dev/null +++ b/arch/m32r/boot/compressed/boot.h @@ -0,0 +1,59 @@ +/* + * 1. load vmlinuz + * + * CONFIG_MEMORY_START +-----------------------+ + * | vmlinuz | + * +-----------------------+ + * 2. decompressed + * + * CONFIG_MEMORY_START +-----------------------+ + * | vmlinuz | + * +-----------------------+ + * | | + * BOOT_RELOC_ADDR +-----------------------+ + * | | + * KERNEL_DECOMPRESS_ADDR +-----------------------+ + * | vmlinux | + * +-----------------------+ + * + * 3. relocate copy & jump code + * + * CONFIG_MEMORY_START +-----------------------+ + * | vmlinuz | + * +-----------------------+ + * | | + * BOOT_RELOC_ADDR +-----------------------+ + * | boot(copy&jump) | + * KERNEL_DECOMPRESS_ADDR +-----------------------+ + * | vmlinux | + * +-----------------------+ + * + * 4. relocate decompressed kernel + * + * CONFIG_MEMORY_START +-----------------------+ + * | vmlinux | + * +-----------------------+ + * | | + * BOOT_RELOC_ADDR +-----------------------+ + * | boot(copy&jump) | + * KERNEL_DECOMPRESS_ADDR +-----------------------+ + * | | + * +-----------------------+ + * + */ +#ifdef __ASSEMBLY__ +#define __val(x) x +#else +#define __val(x) (x) +#endif + +#define DECOMPRESS_OFFSET_BASE __val(0x00900000) +#define BOOT_RELOC_SIZE __val(0x00001000) + +#define KERNEL_EXEC_ADDR __val(CONFIG_MEMORY_START) +#define KERNEL_DECOMPRESS_ADDR __val(CONFIG_MEMORY_START + \ + DECOMPRESS_OFFSET_BASE + BOOT_RELOC_SIZE) +#define KERNEL_ENTRY __val(CONFIG_MEMORY_START + 0x1000) + +#define BOOT_EXEC_ADDR __val(CONFIG_MEMORY_START) +#define BOOT_RELOC_ADDR __val(CONFIG_MEMORY_START + DECOMPRESS_OFFSET_BASE) diff --git a/arch/m32r/boot/compressed/head.S b/arch/m32r/boot/compressed/head.S new file mode 100644 index 000000000..28de481bd --- /dev/null +++ b/arch/m32r/boot/compressed/head.S @@ -0,0 +1,115 @@ +/* + * linux/arch/m32r/boot/compressed/head.S + * + * Copyright (c) 2001-2003 Hiroyuki Kondo, Hirokazu Takata, + * Hitoshi Yamamoto, Takeo Takahashi + * Copyright (c) 2004 Hirokazu Takata + */ + + .text +#include +#include +#include +#include +#include + + .global startup + __ALIGN +startup: + ldi r0, #0x0000 /* SPI, disable EI */ + mvtc r0, psw + +/* + * Clear BSS first so that there are no surprises... + */ +#ifdef CONFIG_ISA_DUAL_ISSUE + + LDIMM (r2, __bss_start) + LDIMM (r3, _end) + sub r3, r2 ; BSS size in bytes + ; R4 = BSS size in longwords (rounded down) + mv r4, r3 || ldi r1, #0 + srli r4, #4 || addi r2, #-4 + beqz r4, .Lendloop1 +.Lloop1: +#ifndef CONFIG_CHIP_M32310 + ; Touch memory for the no-write-allocating cache. + ld r0, @(4,r2) +#endif + st r1, @+r2 || addi r4, #-1 + st r1, @+r2 + st r1, @+r2 + st r1, @+r2 || cmpeq r1, r4 ; R4 = 0? + bnc .Lloop1 +.Lendloop1: + and3 r4, r3, #15 + addi r2, #4 + beqz r4, .Lendloop2 +.Lloop2: + stb r1, @r2 || addi r4, #-1 + addi r2, #1 + bnez r4, .Lloop2 +.Lendloop2: + +#else /* not CONFIG_ISA_DUAL_ISSUE */ + + LDIMM (r2, __bss_start) + LDIMM (r3, _end) + sub r3, r2 ; BSS size in bytes + mv r4, r3 + srli r4, #2 ; R4 = BSS size in longwords (rounded down) + ldi r1, #0 ; clear R1 for longwords store + addi r2, #-4 ; account for pre-inc store + beqz r4, .Lendloop1 ; any more to go? +.Lloop1: + st r1, @+r2 ; yep, zero out another longword + addi r4, #-1 ; decrement count + bnez r4, .Lloop1 ; go do some more +.Lendloop1: + and3 r4, r3, #3 ; get no. of remaining BSS bytes to clear + addi r2, #4 ; account for pre-inc store + beqz r4, .Lendloop2 ; any more to go? +.Lloop2: + stb r1, @r2 ; yep, zero out another byte + addi r2, #1 ; bump address + addi r4, #-1 ; decrement count + bnez r4, .Lloop2 ; go do some more +.Lendloop2: + +#endif /* not CONFIG_ISA_DUAL_ISSUE */ + + seth r0, #shigh(stack_start) + ld sp, @(r0, low(stack_start)) /* set stack point */ + +/* + * decompress the kernel + */ + bl decompress_kernel + +#if defined(CONFIG_CHIP_M32700) + /* Cache flush */ + ldi r0, -1 + ldi r1, 0xd0 ; invalidate i-cache, copy back d-cache + stb r1, @r0 +#else +#error "put your cache flush function, please" +#endif + seth r0, #high(CONFIG_MEMORY_START) + or3 r0, r0, #0x2000 + jmp r0 + + .balign 512 +fake_headers_as_bzImage: + .short 0 + .ascii "HdrS" + .short 0x0202 + .short 0 + .short 0 + .byte 0x00, 0x10 + .short 0 + .byte 0 + .byte 1 + .byte 0x00, 0x80 + .long 0 + .long 0 + diff --git a/arch/m32r/boot/compressed/install.sh b/arch/m32r/boot/compressed/install.sh new file mode 100644 index 000000000..6d72e9e72 --- /dev/null +++ b/arch/m32r/boot/compressed/install.sh @@ -0,0 +1,57 @@ +#!/bin/sh +# +# arch/sh/boot/install.sh +# +# This file is subject to the terms and conditions of the GNU General Public +# License. See the file "COPYING" in the main directory of this archive +# for more details. +# +# Copyright (C) 1995 by Linus Torvalds +# +# Adapted from code in arch/i386/boot/Makefile by H. Peter Anvin +# Adapted from code in arch/i386/boot/install.sh by Russell King +# Adapted from code in arch/arm/boot/install.sh by Stuart Menefy +# Adapted from code in arch/sh/boot/install.sh by Takeo Takahashi +# +# "make install" script for sh architecture +# +# Arguments: +# $1 - kernel version +# $2 - kernel image file +# $3 - kernel map file +# $4 - default install path (blank if root directory) +# + +# User may have a custom install script + +if [ -x /sbin/installkernel ]; then + exec /sbin/installkernel "$@" +fi + +if [ "$2" = "zImage" ]; then +# Compressed install + echo "Installing compressed kernel" + if [ -f $4/vmlinuz-$1 ]; then + mv $4/vmlinuz-$1 $4/vmlinuz.old + fi + + if [ -f $4/System.map-$1 ]; then + mv $4/System.map-$1 $4/System.old + fi + + cat $2 > $4/vmlinuz-$1 + cp $3 $4/System.map-$1 +else +# Normal install + echo "Installing normal kernel" + if [ -f $4/vmlinux-$1 ]; then + mv $4/vmlinux-$1 $4/vmlinux.old + fi + + if [ -f $4/System.map ]; then + mv $4/System.map $4/System.old + fi + + cat $2 > $4/vmlinux-$1 + cp $3 $4/System.map +fi diff --git a/arch/m32r/boot/compressed/m32r_sio.c b/arch/m32r/boot/compressed/m32r_sio.c new file mode 100644 index 000000000..469c4dce5 --- /dev/null +++ b/arch/m32r/boot/compressed/m32r_sio.c @@ -0,0 +1,53 @@ +/* + * arch/m32r/boot/compressed/m32r_sio.c + * + * 2003-02-12: Takeo Takahashi + * + */ + +#include +#include +#include + +void putc(char c); + +int puts(const char *s) +{ + char c; + while ((c = *s++)) putc(c); + return 0; +} + +#if defined(CONFIG_PLAT_M32700UT_Alpha) || defined(CONFIG_PLAT_M32700UT) +#define USE_FPGA_MAP 0 + +#if USE_FPGA_MAP +/* + * fpga configuration program uses MMU, and define map as same as + * M32104 uT-Engine board. + */ +#define BOOT_SIO0STS (volatile unsigned short *)(0x02c00000 + 0x20006) +#define BOOT_SIO0TXB (volatile unsigned short *)(0x02c00000 + 0x2000c) +#else +#undef PLD_BASE +#define PLD_BASE 0xa4c00000 +#define BOOT_SIO0STS PLD_ESIO0STS +#define BOOT_SIO0TXB PLD_ESIO0TXB +#endif + +void putc(char c) +{ + + while ((*BOOT_SIO0STS & 0x3) != 0x3) ; + if (c == '\n') { + *BOOT_SIO0TXB = '\r'; + while ((*BOOT_SIO0STS & 0x3) != 0x3) ; + } + *BOOT_SIO0TXB = c; +} +#else +void putc(char c) +{ + /* do nothing */ +} +#endif diff --git a/arch/m32r/boot/compressed/misc.c b/arch/m32r/boot/compressed/misc.c new file mode 100644 index 000000000..4661d38d9 --- /dev/null +++ b/arch/m32r/boot/compressed/misc.c @@ -0,0 +1,223 @@ +/* + * arch/m32r/boot/compressed/misc.c + * + * This is a collection of several routines from gzip-1.0.3 + * adapted for Linux. + * + * malloc by Hannu Savolainen 1993 and Matthias Urlichs 1994 + * + * Adapted for SH by Stuart Menefy, Aug 1999 + * + * Modified to use standard LinuxSH BIOS by Greg Banks 7Jul2000 + * + * 2003-02-12: Support M32R by Takeo Takahashi + * This is based on arch/sh/boot/compressed/misc.c. + */ + +#include +#include + +/* + * gzip declarations + */ + +#define OF(args) args +#define STATIC static + +#undef memset +#undef memcpy +#define memzero(s, n) memset ((s), 0, (n)) + +typedef unsigned char uch; +typedef unsigned short ush; +typedef unsigned long ulg; + +#define WSIZE 0x8000 /* Window size must be at least 32k, */ + /* and a power of two */ + +static uch *inbuf; /* input buffer */ +static uch window[WSIZE]; /* Sliding window buffer */ + +static unsigned insize; /* valid bytes in inbuf */ +static unsigned inptr; /* index of next byte to be processed in inbuf */ +static unsigned outcnt; /* bytes in output buffer */ + +/* gzip flag byte */ +#define ASCII_FLAG 0x01 /* bit 0 set: file probably ASCII text */ +#define CONTINUATION 0x02 /* bit 1 set: continuation of multi-part gzip file */ +#define EXTRA_FIELD 0x04 /* bit 2 set: extra field present */ +#define ORIG_NAME 0x08 /* bit 3 set: original file name present */ +#define COMMENT 0x10 /* bit 4 set: file comment present */ +#define ENCRYPTED 0x20 /* bit 5 set: file is encrypted */ +#define RESERVED 0xC0 /* bit 6,7: reserved */ + +#define get_byte() (inptr < insize ? inbuf[inptr++] : fill_inbuf()) + +/* Diagnostic functions */ +#ifdef DEBUG +# define Assert(cond,msg) {if(!(cond)) error(msg);} +# define Trace(x) fprintf x +# define Tracev(x) {if (verbose) fprintf x ;} +# define Tracevv(x) {if (verbose>1) fprintf x ;} +# define Tracec(c,x) {if (verbose && (c)) fprintf x ;} +# define Tracecv(c,x) {if (verbose>1 && (c)) fprintf x ;} +#else +# define Assert(cond,msg) +# define Trace(x) +# define Tracev(x) +# define Tracevv(x) +# define Tracec(c,x) +# define Tracecv(c,x) +#endif + +static int fill_inbuf(void); +static void flush_window(void); +static void error(char *m); +static void gzip_mark(void **); +static void gzip_release(void **); + +extern char input_data[]; +extern int input_len; + +static long bytes_out; +static uch *output_data; +static unsigned long output_ptr; + + +static void *malloc(int size); +static void free(void *where); +static void error(char *m); +static void gzip_mark(void **); +static void gzip_release(void **); + +extern int puts(const char *); + +extern int _text; /* Defined in vmlinux.lds.S */ +extern int _end; +static unsigned long free_mem_ptr; +static unsigned long free_mem_end_ptr; + +#define HEAP_SIZE 0x10000 + +#include "../../../../lib/inflate.c" + +static void *malloc(int size) +{ + void *p; + + if (size <0) error("Malloc error\n"); + if (free_mem_ptr == 0) error("Memory error\n"); + + free_mem_ptr = (free_mem_ptr + 3) & ~3; /* Align */ + + p = (void *)free_mem_ptr; + free_mem_ptr += size; + + if (free_mem_ptr >= free_mem_end_ptr) + error("\nOut of memory\n"); + + return p; +} + +static void free(void *where) +{ /* Don't care */ +} + +static void gzip_mark(void **ptr) +{ + *ptr = (void *) free_mem_ptr; +} + +static void gzip_release(void **ptr) +{ + free_mem_ptr = (long) *ptr; +} + +void* memset(void* s, int c, size_t n) +{ + int i; + char *ss = (char*)s; + + for (i=0;i> 8); + } + crc = c; + bytes_out += (ulg)outcnt; + output_ptr += (ulg)outcnt; + outcnt = 0; +} + +static void error(char *x) +{ + puts("\n\n"); + puts(x); + puts("\n\n -- System halted"); + + while(1); /* Halt */ +} + +#define STACK_SIZE (4096) +long user_stack [STACK_SIZE]; +long* stack_start = &user_stack[STACK_SIZE]; + +/* return decompressed size */ +long decompress_kernel(void) +{ + insize = 0; + inptr = 0; + bytes_out = 0; + outcnt = 0; + output_data = 0; + output_ptr = CONFIG_MEMORY_START + 0x2000; + free_mem_ptr = (unsigned long)&_end; + free_mem_end_ptr = free_mem_ptr + HEAP_SIZE; + + makecrc(); + puts("Uncompressing Linux... "); + gunzip(); + puts("Ok, booting the kernel.\n"); + return bytes_out; +} diff --git a/arch/m32r/boot/compressed/vmlinux.lds.S b/arch/m32r/boot/compressed/vmlinux.lds.S new file mode 100644 index 000000000..20a9e9450 --- /dev/null +++ b/arch/m32r/boot/compressed/vmlinux.lds.S @@ -0,0 +1,23 @@ +#include + +OUTPUT_ARCH(m32r) +ENTRY(startup) +SECTIONS +{ + . = CONFIG_MEMORY_START + 0x00400000; + + _text = .; + .text : { *(.text) } = 0 + .rodata : { *(.rodata) } + _etext = .; + + . = ALIGN(32) + (. & (32 - 1)); + .data : { *(.data) } + _edata = .; + + . = ALIGN(32 / 8); + __bss_start = .; + .bss : { *(.bss) } + . = ALIGN(32 / 8); + _end = . ; +} diff --git a/arch/m32r/boot/compressed/vmlinux.scr b/arch/m32r/boot/compressed/vmlinux.scr new file mode 100644 index 000000000..a08490360 --- /dev/null +++ b/arch/m32r/boot/compressed/vmlinux.scr @@ -0,0 +1,9 @@ +SECTIONS +{ + .data : { + input_len = .; + LONG(input_data_end - input_data) input_data = .; + *(.data) + input_data_end = .; + } +} diff --git a/arch/m32r/boot/setup.S b/arch/m32r/boot/setup.S new file mode 100644 index 000000000..9be591710 --- /dev/null +++ b/arch/m32r/boot/setup.S @@ -0,0 +1,162 @@ +/* + * linux/arch/m32r/boot/setup.S -- A setup code. + * + * Copyright (C) 2001, 2002 Hiroyuki Kondo, Hirokazu Takata, + * and Hitoshi Yamamoto + * + */ +/* $Id$ */ + +#include +#include +#include +#include + +#include +#include +#include +#include + +/* + * References to members of the boot_cpu_data structure. + */ + +#define CPU_PARAMS boot_cpu_data +#define M32R_MCICAR 0xfffffff0 +#define M32R_MCDCAR 0xfffffff4 +#define M32R_MCCR 0xfffffffc +#define M32R_BSCR0 0xffffffd2 + +;BSEL +#define BSEL0CR0 0x00ef5000 +#define BSEL0CR1 0x00ef5004 +#define BSEL1CR0 0x00ef5100 +#define BSEL1CR1 0x00ef5104 +#define BSEL0CR0_VAL 0x00000000 +#define BSEL0CR1_VAL 0x01200100 +#define BSEL1CR0_VAL 0x01018000 +#define BSEL1CR1_VAL 0x00200001 + +;SDRAMC +#define SDRAMC_SDRF0 0x00ef6000 +#define SDRAMC_SDRF1 0x00ef6004 +#define SDRAMC_SDIR0 0x00ef6008 +#define SDRAMC_SDIR1 0x00ef600c +#define SDRAMC_SD0ADR 0x00ef6020 +#define SDRAMC_SD0ER 0x00ef6024 +#define SDRAMC_SD0TR 0x00ef6028 +#define SDRAMC_SD0MOD 0x00ef602c +#define SDRAMC_SD1ADR 0x00ef6040 +#define SDRAMC_SD1ER 0x00ef6044 +#define SDRAMC_SD1TR 0x00ef6048 +#define SDRAMC_SD1MOD 0x00ef604c +#define SDRAM0 0x18000000 +#define SDRAM1 0x1c000000 + +/*------------------------------------------------------------------------ + * start up + */ + +/*------------------------------------------------------------------------ + * Kernel entry + */ + .section .boot, "ax" +ENTRY(boot) + +/* Set cache mode */ +#if defined(CONFIG_CHIP_XNUX2) + ldi r0, #-2 ;LDIMM (r0, M32R_MCCR) + ldi r1, #0x0101 ; cache on (with invalidation) +; ldi r1, #0x00 ; cache off + sth r1, @r0 +#elif defined(CONFIG_CHIP_M32700) || defined(CONFIG_CHIP_VDEC2) \ + || defined(CONFIG_CHIP_OPSP) + ldi r0, #-4 ;LDIMM (r0, M32R_MCCR) + ldi r1, #0x73 ; cache on (with invalidation) +; ldi r1, #0x00 ; cache off + st r1, @r0 +#else +#error unknown chip configuration +#endif + +#ifdef CONFIG_SMP + ;; if not BSP (CPU#0) goto AP_loop + seth r5, #shigh(M32R_CPUID_PORTL) + ld r5, @(low(M32R_CPUID_PORTL), r5) + bnez r5, AP_loop +#if !defined(CONFIG_PLAT_USRV) + ;; boot AP + ld24 r5, #0xeff2f8 ; IPICR7 + ldi r6, #0x2 ; IPI to CPU1 + st r6, @r5 +#endif +#endif + +/* + * Now, Jump to stext + * if with MMU, TLB on. + * if with no MMU, only jump. + */ + .global eit_vector +mmu_on: + LDIMM (r13, stext) +#ifdef CONFIG_MMU + bl init_tlb + LDIMM (r2, eit_vector) ; set EVB(cr5) + mvtc r2, cr5 + seth r0, #high(MMU_REG_BASE) ; Set MMU_REG_BASE higher + or3 r0, r0, #low(MMU_REG_BASE) ; Set MMU_REG_BASE lower + ldi r1, #0x01 + st r1, @(MATM_offset,r0) ; Set MATM (T bit ON) + ld r0, @(MATM_offset,r0) ; Check +#else + seth r0,#high(M32R_MCDCAR) + or3 r0,r0,#low(M32R_MCDCAR) + ld24 r1,#0x8080 + st r1,@r0 +#endif /* CONFIG_MMU */ + jmp r13 + nop + nop + +#ifdef CONFIG_SMP +/* + * AP wait loop + */ +ENTRY(AP_loop) + ;; disable interrupt + clrpsw #0x40 + ;; reset EVB + LDIMM (r4, _AP_RE) + seth r5, #high(__PAGE_OFFSET) + or3 r5, r5, #low(__PAGE_OFFSET) + not r5, r5 + and r4, r5 + mvtc r4, cr5 + ;; disable maskable interrupt + seth r4, #high(M32R_ICU_IMASK_PORTL) + or3 r4, r4, #low(M32R_ICU_IMASK_PORTL) + ldi r5, #0 + st r5, @r4 + ld r5, @r4 + ;; enable only IPI + setpsw #0x40 + ;; LOOOOOOOOOOOOOOP!!! + .fillinsn +2: + nop + nop + bra 2b + nop + nop + +#ifdef CONFIG_CHIP_M32700_TS1 + .global dcache_dummy + .balign 16, 0 +dcache_dummy: + .byte 16 +#endif /* CONFIG_CHIP_M32700_TS1 */ +#endif /* CONFIG_SMP */ + + .end + diff --git a/arch/m32r/defconfig b/arch/m32r/defconfig new file mode 100644 index 000000000..984701b73 --- /dev/null +++ b/arch/m32r/defconfig @@ -0,0 +1,635 @@ +# +# Automatically generated make config: don't edit +# +CONFIG_M32R=y +CONFIG_UID16=y +CONFIG_GENERIC_ISA_DMA=y + +# +# Code maturity level options +# +CONFIG_EXPERIMENTAL=y +CONFIG_CLEAN_COMPILE=y +CONFIG_BROKEN_ON_SMP=y + +# +# General setup +# +CONFIG_SWAP=y +CONFIG_SYSVIPC=y +# CONFIG_POSIX_MQUEUE is not set +CONFIG_BSD_PROCESS_ACCT=y +# CONFIG_BSD_PROCESS_ACCT_V3 is not set +CONFIG_SYSCTL=y +# CONFIG_AUDIT is not set +CONFIG_LOG_BUF_SHIFT=14 +CONFIG_HOTPLUG=y +CONFIG_IKCONFIG=y +# CONFIG_IKCONFIG_PROC is not set +CONFIG_EMBEDDED=y +# CONFIG_KALLSYMS is not set +# CONFIG_FUTEX is not set +# CONFIG_EPOLL is not set +CONFIG_IOSCHED_NOOP=y +# CONFIG_IOSCHED_AS is not set +CONFIG_IOSCHED_DEADLINE=y +CONFIG_IOSCHED_CFQ=y +# CONFIG_CC_OPTIMIZE_FOR_SIZE is not set + +# +# Loadable module support +# +CONFIG_MODULES=y +CONFIG_MODULE_UNLOAD=y +# CONFIG_MODULE_FORCE_UNLOAD is not set +CONFIG_OBSOLETE_MODPARM=y +# CONFIG_MODVERSIONS is not set +CONFIG_KMOD=y + +# +# Processor type and features +# +# CONFIG_PLAT_MAPPI is not set +# CONFIG_PLAT_USRV is not set +CONFIG_PLAT_M32700UT=y +# CONFIG_PLAT_OPSPUT is not set +# CONFIG_PLAT_OAKS32R is not set +# CONFIG_PLAT_MAPPI2 is not set +CONFIG_CHIP_M32700=y +# CONFIG_CHIP_M32102 is not set +# CONFIG_CHIP_VDEC2 is not set +# CONFIG_CHIP_OPSP is not set +CONFIG_MMU=y +CONFIG_TLB_ENTRIES=32 +CONFIG_ISA_M32R2=y +CONFIG_ISA_DSP_LEVEL2=y +CONFIG_ISA_DUAL_ISSUE=y +CONFIG_BUS_CLOCK=50000000 +CONFIG_TIMER_DIVIDE=128 +# CONFIG_CPU_LITTLE_ENDIAN is not set +CONFIG_MEMORY_START=0x08000000 +CONFIG_MEMORY_SIZE=0x01000000 +CONFIG_NOHIGHMEM=y +# CONFIG_DISCONTIGMEM is not set +CONFIG_RWSEM_GENERIC_SPINLOCK=y +# CONFIG_RWSEM_XCHGADD_ALGORITHM is not set +CONFIG_PREEMPT=y +# CONFIG_HAVE_DEC_LOCK is not set +# CONFIG_SMP is not set + +# +# M32R drivers +# +# CONFIG_M32RPCC is not set +CONFIG_M32R_CFC=y +CONFIG_M32700UT_CFC=y +CONFIG_CFC_NUM=1 +# CONFIG_MTD_M32R is not set +CONFIG_M32R_SMC91111=y +CONFIG_M32700UT_DS1302=y + +# +# Power management options (ACPI, APM) +# +# CONFIG_PM is not set + +# +# Bus options (PCI, PCMCIA, EISA, MCA, ISA) +# +# CONFIG_PCI is not set +# CONFIG_ISA is not set + +# +# PCMCIA/CardBus support +# +CONFIG_PCMCIA=y +# CONFIG_PCMCIA_DEBUG is not set +# CONFIG_TCIC is not set + +# +# PCI Hotplug Support +# + +# +# Executable file formats +# +CONFIG_BINFMT_ELF=y +# CONFIG_BINFMT_MISC is not set + +# +# Device Drivers +# + +# +# Generic Driver Options +# +CONFIG_STANDALONE=y +CONFIG_PREVENT_FIRMWARE_BUILD=y +# CONFIG_FW_LOADER is not set + +# +# Memory Technology Devices (MTD) +# +# CONFIG_MTD is not set + +# +# Parallel port support +# +# CONFIG_PARPORT is not set + +# +# Plug and Play support +# + +# +# Block devices +# +# CONFIG_BLK_DEV_FD is not set +CONFIG_BLK_DEV_LOOP=y +# CONFIG_BLK_DEV_CRYPTOLOOP is not set +CONFIG_BLK_DEV_NBD=y +CONFIG_BLK_DEV_RAM=y +CONFIG_BLK_DEV_RAM_SIZE=4096 +# CONFIG_BLK_DEV_INITRD is not set + +# +# ATA/ATAPI/MFM/RLL support +# +CONFIG_IDE=y +CONFIG_BLK_DEV_IDE=y + +# +# Please see Documentation/ide.txt for help/info on IDE drives +# +# CONFIG_BLK_DEV_IDE_SATA is not set +CONFIG_BLK_DEV_IDEDISK=y +# CONFIG_IDEDISK_MULTI_MODE is not set +CONFIG_BLK_DEV_IDECS=y +CONFIG_BLK_DEV_IDECD=m +# CONFIG_BLK_DEV_IDETAPE is not set +# CONFIG_BLK_DEV_IDEFLOPPY is not set +# CONFIG_BLK_DEV_IDESCSI is not set +# CONFIG_IDE_TASK_IOCTL is not set +# CONFIG_IDE_TASKFILE_IO is not set + +# +# IDE chipset support/bugfixes +# +CONFIG_IDE_GENERIC=y +# CONFIG_IDE_ARM is not set +# CONFIG_BLK_DEV_IDEDMA is not set +# CONFIG_IDEDMA_AUTO is not set +# CONFIG_BLK_DEV_HD is not set + +# +# SCSI device support +# +CONFIG_SCSI=m +CONFIG_SCSI_PROC_FS=y + +# +# SCSI support type (disk, tape, CD-ROM) +# +CONFIG_BLK_DEV_SD=m +# CONFIG_CHR_DEV_ST is not set +# CONFIG_CHR_DEV_OSST is not set +CONFIG_BLK_DEV_SR=m +# CONFIG_BLK_DEV_SR_VENDOR is not set +CONFIG_CHR_DEV_SG=m + +# +# Some SCSI devices (e.g. CD jukebox) support multiple LUNs +# +CONFIG_SCSI_MULTI_LUN=y +# CONFIG_SCSI_CONSTANTS is not set +# CONFIG_SCSI_LOGGING is not set + +# +# SCSI Transport Attributes +# +# CONFIG_SCSI_SPI_ATTRS is not set +# CONFIG_SCSI_FC_ATTRS is not set + +# +# SCSI low-level drivers +# +# CONFIG_SCSI_AIC7XXX_OLD is not set +# CONFIG_SCSI_SATA is not set +# CONFIG_SCSI_EATA_PIO is not set +# CONFIG_SCSI_DEBUG is not set + +# +# PCMCIA SCSI adapter support +# +# CONFIG_PCMCIA_AHA152X is not set +# CONFIG_PCMCIA_FDOMAIN is not set +# CONFIG_PCMCIA_NINJA_SCSI is not set +# CONFIG_PCMCIA_QLOGIC is not set +# CONFIG_PCMCIA_SYM53C500 is not set + +# +# Multi-device support (RAID and LVM) +# +# CONFIG_MD is not set + +# +# Fusion MPT device support +# + +# +# IEEE 1394 (FireWire) support +# + +# +# I2O device support +# + +# +# Networking support +# +CONFIG_NET=y + +# +# Networking options +# +CONFIG_PACKET=y +# CONFIG_PACKET_MMAP is not set +# CONFIG_NETLINK_DEV 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_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_IPV6 is not set +# CONFIG_NETFILTER is not set + +# +# SCTP Configuration (EXPERIMENTAL) +# +# CONFIG_IP_SCTP 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_NET_DIVERT is not set +# CONFIG_ECONET is not set +# CONFIG_WAN_ROUTER is not set +# CONFIG_NET_HW_FLOWCONTROL is not set + +# +# QoS and/or fair queueing +# +# CONFIG_NET_SCHED is not set +# CONFIG_NET_CLS_ROUTE is not set + +# +# Network testing +# +# CONFIG_NET_PKTGEN is not set +# CONFIG_NETPOLL is not set +# CONFIG_NET_POLL_CONTROLLER is not set +# CONFIG_HAMRADIO is not set +# CONFIG_IRDA is not set +# CONFIG_BT is not set +# CONFIG_NETDEVICES is not set + +# +# ISDN subsystem +# +# CONFIG_ISDN is not set + +# +# Telephony Support +# +# CONFIG_PHONE is not set + +# +# Input device support +# +CONFIG_INPUT=y + +# +# Userland interfaces +# +# CONFIG_INPUT_MOUSEDEV is not set +# CONFIG_INPUT_JOYDEV is not set +# CONFIG_INPUT_TSDEV is not set +# CONFIG_INPUT_EVDEV is not set +# CONFIG_INPUT_EVBUG is not set + +# +# Input I/O drivers +# +# CONFIG_GAMEPORT is not set +CONFIG_SOUND_GAMEPORT=y +CONFIG_SERIO=y +# CONFIG_SERIO_I8042 is not set +CONFIG_SERIO_SERPORT=y +# CONFIG_SERIO_CT82C710 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_TOUCHSCREEN is not set +# CONFIG_INPUT_MISC is not set + +# +# Character devices +# +# CONFIG_VT is not set +# CONFIG_SERIAL_NONSTANDARD is not set + +# +# Serial drivers +# +# CONFIG_SERIAL_8250 is not set + +# +# Non-8250 serial port support +# +CONFIG_SERIAL_CORE=y +CONFIG_SERIAL_CORE_CONSOLE=y +# CONFIG_SERIAL_M32R_SIO is not set +CONFIG_SERIAL_M32R_PLDSIO=y +CONFIG_SERIAL_M32R_PLDSIO_CONSOLE=y +CONFIG_UNIX98_PTYS=y +CONFIG_LEGACY_PTYS=y +CONFIG_LEGACY_PTY_COUNT=256 +# CONFIG_QIC02_TAPE is not set + +# +# IPMI +# +# CONFIG_IPMI_HANDLER is not set + +# +# Watchdog Cards +# +# CONFIG_WATCHDOG is not set +# CONFIG_RTC is not set +# CONFIG_GEN_RTC is not set +# CONFIG_DTLK is not set +# CONFIG_R3964 is not set + +# +# Ftape, the floppy tape device driver +# +# CONFIG_AGP is not set +# CONFIG_DRM is not set + +# +# PCMCIA character devices +# +# CONFIG_SYNCLINK_CS is not set +# CONFIG_RAW_DRIVER is not set + +# +# I2C support +# +# CONFIG_I2C is not set + +# +# Dallas's 1-wire bus +# +# CONFIG_W1 is not set + +# +# Misc devices +# + +# +# Multimedia devices +# +CONFIG_VIDEO_DEV=y + +# +# Video For Linux +# + +# +# Video Adapters +# +# CONFIG_VIDEO_CPIA is not set +CONFIG_M32R_AR=y +CONFIG_M32R_AR_VGA=y + +# +# Radio Adapters +# +# CONFIG_RADIO_MAESTRO is not set + +# +# Digital Video Broadcasting Devices +# +# CONFIG_DVB is not set + +# +# Graphics support +# +# CONFIG_FB is not set + +# +# Sound +# +# CONFIG_SOUND is not set + +# +# USB support +# + +# +# USB Gadget Support +# +# CONFIG_USB_GADGET is not set + +# +# File systems +# +CONFIG_EXT2_FS=y +# CONFIG_EXT2_FS_XATTR 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_JBD=m +CONFIG_JBD_DEBUG=y +CONFIG_FS_MBCACHE=y +CONFIG_REISERFS_FS=m +# CONFIG_REISERFS_CHECK is not set +# CONFIG_REISERFS_PROC_INFO is not set +# CONFIG_REISERFS_FS_XATTR is not set +# CONFIG_JFS_FS is not set +# CONFIG_XFS_FS is not set +# CONFIG_MINIX_FS is not set +# CONFIG_ROMFS_FS is not set +# CONFIG_QUOTA is not set +# CONFIG_AUTOFS_FS is not set +# CONFIG_AUTOFS4_FS is not set + +# +# CD-ROM/DVD Filesystems +# +CONFIG_ISO9660_FS=m +CONFIG_JOLIET=y +# CONFIG_ZISOFS is not set +CONFIG_UDF_FS=m +CONFIG_UDF_NLS=y + +# +# DOS/FAT/NT Filesystems +# +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 + +# +# Pseudo filesystems +# +CONFIG_PROC_FS=y +CONFIG_PROC_KCORE=y +CONFIG_SYSFS=y +CONFIG_DEVFS_FS=y +CONFIG_DEVFS_MOUNT=y +# CONFIG_DEVFS_DEBUG is not set +# CONFIG_DEVPTS_FS_XATTR is not set +CONFIG_TMPFS=y +# CONFIG_HUGETLB_PAGE is not set +CONFIG_RAMFS=y + +# +# 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_HPFS_FS is not set +# CONFIG_QNX4FS_FS is not set +# CONFIG_SYSV_FS is not set +# CONFIG_UFS_FS is not set + +# +# Network File Systems +# +CONFIG_NFS_FS=y +CONFIG_NFS_V3=y +# CONFIG_NFS_V4 is not set +# CONFIG_NFS_DIRECTIO is not set +# CONFIG_NFSD is not set +CONFIG_ROOT_NFS=y +CONFIG_LOCKD=y +CONFIG_LOCKD_V4=y +# CONFIG_EXPORTFS is not set +CONFIG_SUNRPC=y +# CONFIG_RPCSEC_GSS_KRB5 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 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 + +# +# Profiling support +# +# CONFIG_PROFILING is not set + +# +# Kernel hacking +# +# CONFIG_DEBUG_KERNEL is not set +# CONFIG_DEBUG_SPINLOCK_SLEEP is not set +# CONFIG_FRAME_POINTER is not set + +# +# Security options +# +# CONFIG_SECURITY is not set + +# +# Cryptographic options +# +# CONFIG_CRYPTO is not set + +# +# Library routines +# +# CONFIG_CRC_CCITT is not set +CONFIG_CRC32=y +# CONFIG_LIBCRC32C is not set diff --git a/arch/m32r/kernel/Makefile b/arch/m32r/kernel/Makefile new file mode 100644 index 000000000..cfd690bf6 --- /dev/null +++ b/arch/m32r/kernel/Makefile @@ -0,0 +1,20 @@ +# +# Makefile for the Linux/M32R kernel. +# + +extra-y := head.o init_task.o vmlinux.lds + +obj-y := process.o entry.o traps.o align.o irq.o setup.o time.o \ + m32r_ksyms.o sys_m32r.o semaphore.o signal.o ptrace.o + +obj-$(CONFIG_SMP) += smp.o smpboot.o +obj-$(CONFIG_PLAT_MAPPI) += setup_mappi.o io_mappi.o +obj-$(CONFIG_PLAT_MAPPI2) += setup_mappi2.o io_mappi2.o +obj-$(CONFIG_PLAT_USRV) += setup_usrv.o io_usrv.o +obj-$(CONFIG_PLAT_M32700UT) += setup_m32700ut.o io_m32700ut.o +obj-$(CONFIG_PLAT_OPSPUT) += setup_opsput.o io_opsput.o +obj-$(CONFIG_MODULES) += module.o +obj-$(CONFIG_PLAT_OAKS32R) += setup_oaks32r.o io_oaks32r.o + +EXTRA_AFLAGS := -traditional + diff --git a/arch/m32r/kernel/align.c b/arch/m32r/kernel/align.c new file mode 100644 index 000000000..48ec29714 --- /dev/null +++ b/arch/m32r/kernel/align.c @@ -0,0 +1,585 @@ +/* + * align.c - address exception handler for M32R + * + * Copyright (c) 2003 Hitoshi Yamamoto + */ + +#include +#include +#include + +static int get_reg(struct pt_regs *regs, int nr) +{ + int val; + + if (nr < 4) + val = *(unsigned long *)(®s->r0 + nr); + else if (nr < 7) + val = *(unsigned long *)(®s->r4 + (nr - 4)); + else if (nr < 13) + val = *(unsigned long *)(®s->r7 + (nr - 7)); + else + val = *(unsigned long *)(®s->fp + (nr - 13)); + + return val; +} + +static void set_reg(struct pt_regs *regs, int nr, int val) +{ + if (nr < 4) + *(unsigned long *)(®s->r0 + nr) = val; + else if (nr < 7) + *(unsigned long *)(®s->r4 + (nr - 4)) = val; + else if (nr < 13) + *(unsigned long *)(®s->r7 + (nr - 7)) = val; + else + *(unsigned long *)(®s->fp + (nr - 13)) = val; +} + +#define REG1(insn) (((insn) & 0x0f00) >> 8) +#define REG2(insn) ((insn) & 0x000f) +#define PSW_BC 0x100 + +/* O- instruction */ +#define ISA_LD1 0x20c0 /* ld Rdest, @Rsrc */ +#define ISA_LD2 0x20e0 /* ld Rdest, @Rsrc+ */ +#define ISA_LDH 0x20a0 /* ldh Rdest, @Rsrc */ +#define ISA_LDUH 0x20b0 /* lduh Rdest, @Rsrc */ +#define ISA_ST1 0x2040 /* st Rsrc1, @Rsrc2 */ +#define ISA_ST2 0x2060 /* st Rsrc1, @+Rsrc2 */ +#define ISA_ST3 0x2070 /* st Rsrc1, @-Rsrc2 */ +#define ISA_STH1 0x2020 /* sth Rsrc1, @Rsrc2 */ +#define ISA_STH2 0x2030 /* sth Rsrc1, @Rsrc2+ */ + +#ifdef CONFIG_ISA_DUAL_ISSUE + +/* OS instruction */ +#define ISA_ADD 0x00a0 /* add Rdest, Rsrc */ +#define ISA_ADDI 0x4000 /* addi Rdest, #imm8 */ +#define ISA_ADDX 0x0090 /* addx Rdest, Rsrc */ +#define ISA_AND 0x00c0 /* and Rdest, Rsrc */ +#define ISA_CMP 0x0040 /* cmp Rsrc1, Rsrc2 */ +#define ISA_CMPEQ 0x0060 /* cmpeq Rsrc1, Rsrc2 */ +#define ISA_CMPU 0x0050 /* cmpu Rsrc1, Rsrc2 */ +#define ISA_CMPZ 0x0070 /* cmpz Rsrc */ +#define ISA_LDI 0x6000 /* ldi Rdest, #imm8 */ +#define ISA_MV 0x1080 /* mv Rdest, Rsrc */ +#define ISA_NEG 0x0030 /* neg Rdest, Rsrc */ +#define ISA_NOP 0x7000 /* nop */ +#define ISA_NOT 0x00b0 /* not Rdest, Rsrc */ +#define ISA_OR 0x00e0 /* or Rdest, Rsrc */ +#define ISA_SUB 0x0020 /* sub Rdest, Rsrc */ +#define ISA_SUBX 0x0010 /* subx Rdest, Rsrc */ +#define ISA_XOR 0x00d0 /* xor Rdest, Rsrc */ + +/* -S instruction */ +#define ISA_MUL 0x1060 /* mul Rdest, Rsrc */ +#define ISA_MULLO_A0 0x3010 /* mullo Rsrc1, Rsrc2, A0 */ +#define ISA_MULLO_A1 0x3090 /* mullo Rsrc1, Rsrc2, A1 */ +#define ISA_MVFACMI_A0 0x50f2 /* mvfacmi Rdest, A0 */ +#define ISA_MVFACMI_A1 0x50f6 /* mvfacmi Rdest, A1 */ + +static int emu_addi(unsigned short insn, struct pt_regs *regs) +{ + char imm = (char)(insn & 0xff); + int dest = REG1(insn); + int val; + + val = get_reg(regs, dest); + val += imm; + set_reg(regs, dest, val); + + return 0; +} + +static int emu_ldi(unsigned short insn, struct pt_regs *regs) +{ + char imm = (char)(insn & 0xff); + + set_reg(regs, REG1(insn), (int)imm); + + return 0; +} + +static int emu_add(unsigned short insn, struct pt_regs *regs) +{ + int dest = REG1(insn); + int src = REG2(insn); + int val; + + val = get_reg(regs, dest); + val += get_reg(regs, src); + set_reg(regs, dest, val); + + return 0; +} + +static int emu_addx(unsigned short insn, struct pt_regs *regs) +{ + int dest = REG1(insn); + unsigned int val, tmp; + + val = regs->psw & PSW_BC ? 1 : 0; + tmp = get_reg(regs, dest); + val += tmp; + val += (unsigned int)get_reg(regs, REG2(insn)); + set_reg(regs, dest, val); + + /* C bit set */ + if (val < tmp) + regs->psw |= PSW_BC; + else + regs->psw &= ~(PSW_BC); + + return 0; +} + +static int emu_and(unsigned short insn, struct pt_regs *regs) +{ + int dest = REG1(insn); + int val; + + val = get_reg(regs, dest); + val &= get_reg(regs, REG2(insn)); + set_reg(regs, dest, val); + + return 0; +} + +static int emu_cmp(unsigned short insn, struct pt_regs *regs) +{ + if (get_reg(regs, REG1(insn)) < get_reg(regs, REG2(insn))) + regs->psw |= PSW_BC; + else + regs->psw &= ~(PSW_BC); + + return 0; +} + +static int emu_cmpeq(unsigned short insn, struct pt_regs *regs) +{ + if (get_reg(regs, REG1(insn)) == get_reg(regs, REG2(insn))) + regs->psw |= PSW_BC; + else + regs->psw &= ~(PSW_BC); + + return 0; +} + +static int emu_cmpu(unsigned short insn, struct pt_regs *regs) +{ + if ((unsigned int)get_reg(regs, REG1(insn)) + < (unsigned int)get_reg(regs, REG2(insn))) + regs->psw |= PSW_BC; + else + regs->psw &= ~(PSW_BC); + + return 0; +} + +static int emu_cmpz(unsigned short insn, struct pt_regs *regs) +{ + if (!get_reg(regs, REG2(insn))) + regs->psw |= PSW_BC; + else + regs->psw &= ~(PSW_BC); + + return 0; +} + +static int emu_mv(unsigned short insn, struct pt_regs *regs) +{ + int val; + + val = get_reg(regs, REG2(insn)); + set_reg(regs, REG1(insn), val); + + return 0; +} + +static int emu_neg(unsigned short insn, struct pt_regs *regs) +{ + int val; + + val = get_reg(regs, REG2(insn)); + set_reg(regs, REG1(insn), 0 - val); + + return 0; +} + +static int emu_not(unsigned short insn, struct pt_regs *regs) +{ + int val; + + val = get_reg(regs, REG2(insn)); + set_reg(regs, REG1(insn), ~val); + + return 0; +} + +static int emu_or(unsigned short insn, struct pt_regs *regs) +{ + int dest = REG1(insn); + int val; + + val = get_reg(regs, dest); + val |= get_reg(regs, REG2(insn)); + set_reg(regs, dest, val); + + return 0; +} + +static int emu_sub(unsigned short insn, struct pt_regs *regs) +{ + int dest = REG1(insn); + int val; + + val = get_reg(regs, dest); + val -= get_reg(regs, REG2(insn)); + set_reg(regs, dest, val); + + return 0; +} + +static int emu_subx(unsigned short insn, struct pt_regs *regs) +{ + int dest = REG1(insn); + unsigned int val, tmp; + + val = tmp = get_reg(regs, dest); + val -= (unsigned int)get_reg(regs, REG2(insn)); + val -= regs->psw & PSW_BC ? 1 : 0; + set_reg(regs, dest, val); + + /* C bit set */ + if (val > tmp) + regs->psw |= PSW_BC; + else + regs->psw &= ~(PSW_BC); + + return 0; +} + +static int emu_xor(unsigned short insn, struct pt_regs *regs) +{ + int dest = REG1(insn); + unsigned int val; + + val = (unsigned int)get_reg(regs, dest); + val ^= (unsigned int)get_reg(regs, REG2(insn)); + set_reg(regs, dest, val); + + return 0; +} + +static int emu_mul(unsigned short insn, struct pt_regs *regs) +{ + int dest = REG1(insn); + int reg1, reg2; + + reg1 = get_reg(regs, dest); + reg2 = get_reg(regs, REG2(insn)); + + __asm__ __volatile__ ( + "mul %0, %1; \n\t" + : "+r" (reg1) : "r" (reg2) + ); + + set_reg(regs, dest, reg1); + + return 0; +} + +static int emu_mullo_a0(unsigned short insn, struct pt_regs *regs) +{ + int reg1, reg2; + + reg1 = get_reg(regs, REG1(insn)); + reg2 = get_reg(regs, REG2(insn)); + + __asm__ __volatile__ ( + "mullo %0, %1, a0; \n\t" + "mvfachi %0, a0; \n\t" + "mvfaclo %1, a0; \n\t" + : "+r" (reg1), "+r" (reg2) + ); + + regs->acc0h = reg1; + regs->acc0l = reg2; + + return 0; +} + +static int emu_mullo_a1(unsigned short insn, struct pt_regs *regs) +{ + int reg1, reg2; + + reg1 = get_reg(regs, REG1(insn)); + reg2 = get_reg(regs, REG2(insn)); + + __asm__ __volatile__ ( + "mullo %0, %1, a0; \n\t" + "mvfachi %0, a0; \n\t" + "mvfaclo %1, a0; \n\t" + : "+r" (reg1), "+r" (reg2) + ); + + regs->acc1h = reg1; + regs->acc1l = reg2; + + return 0; +} + +static int emu_mvfacmi_a0(unsigned short insn, struct pt_regs *regs) +{ + unsigned long val; + + val = (regs->acc0h << 16) | (regs->acc0l >> 16); + set_reg(regs, REG1(insn), (int)val); + + return 0; +} + +static int emu_mvfacmi_a1(unsigned short insn, struct pt_regs *regs) +{ + unsigned long val; + + val = (regs->acc1h << 16) | (regs->acc1l >> 16); + set_reg(regs, REG1(insn), (int)val); + + return 0; +} + +static int emu_m32r2(unsigned short insn, struct pt_regs *regs) +{ + int res = -1; + + if ((insn & 0x7fff) == ISA_NOP) /* nop */ + return 0; + + switch(insn & 0x7000) { + case ISA_ADDI: /* addi Rdest, #imm8 */ + res = emu_addi(insn, regs); + break; + case ISA_LDI: /* ldi Rdest, #imm8 */ + res = emu_ldi(insn, regs); + break; + default: + break; + } + + if (!res) + return 0; + + switch(insn & 0x70f0) { + case ISA_ADD: /* add Rdest, Rsrc */ + res = emu_add(insn, regs); + break; + case ISA_ADDX: /* addx Rdest, Rsrc */ + res = emu_addx(insn, regs); + break; + case ISA_AND: /* and Rdest, Rsrc */ + res = emu_and(insn, regs); + break; + case ISA_CMP: /* cmp Rsrc1, Rsrc2 */ + res = emu_cmp(insn, regs); + break; + case ISA_CMPEQ: /* cmpeq Rsrc1, Rsrc2 */ + res = emu_cmpeq(insn, regs); + break; + case ISA_CMPU: /* cmpu Rsrc1, Rsrc2 */ + res = emu_cmpu(insn, regs); + break; + case ISA_CMPZ: /* cmpz Rsrc */ + res = emu_cmpz(insn, regs); + break; + case ISA_MV: /* mv Rdest, Rsrc */ + res = emu_mv(insn, regs); + break; + case ISA_NEG: /* neg Rdest, Rsrc */ + res = emu_neg(insn, regs); + break; + case ISA_NOT: /* not Rdest, Rsrc */ + res = emu_not(insn, regs); + break; + case ISA_OR: /* or Rdest, Rsrc */ + res = emu_or(insn, regs); + break; + case ISA_SUB: /* sub Rdest, Rsrc */ + res = emu_sub(insn, regs); + break; + case ISA_SUBX: /* subx Rdest, Rsrc */ + res = emu_subx(insn, regs); + break; + case ISA_XOR: /* xor Rdest, Rsrc */ + res = emu_xor(insn, regs); + break; + case ISA_MUL: /* mul Rdest, Rsrc */ + res = emu_mul(insn, regs); + break; + case ISA_MULLO_A0: /* mullo Rsrc1, Rsrc2 */ + res = emu_mullo_a0(insn, regs); + break; + case ISA_MULLO_A1: /* mullo Rsrc1, Rsrc2 */ + res = emu_mullo_a1(insn, regs); + break; + default: + break; + } + + if (!res) + return 0; + + switch(insn & 0x70ff) { + case ISA_MVFACMI_A0: /* mvfacmi Rdest */ + res = emu_mvfacmi_a0(insn, regs); + break; + case ISA_MVFACMI_A1: /* mvfacmi Rdest */ + res = emu_mvfacmi_a1(insn, regs); + break; + default: + break; + } + + return res; +} + +#endif /* CONFIG_ISA_DUAL_ISSUE */ + +/* + * ld : ?010 dest 1100 src + * 0010 dest 1110 src : ld Rdest, @Rsrc+ + * ldh : ?010 dest 1010 src + * lduh : ?010 dest 1011 src + * st : ?010 src1 0100 src2 + * 0010 src1 0110 src2 : st Rsrc1, @+Rsrc2 + * 0010 src1 0111 src2 : st Rsrc1, @-Rsrc2 + * sth : ?010 src1 0010 src2 + */ + +static int insn_check(unsigned long insn, struct pt_regs *regs, + unsigned char **ucp) +{ + int res = 0; + + /* + * 32bit insn + * ld Rdest, @(disp16, Rsrc) + * st Rdest, @(disp16, Rsrc) + */ + if (insn & 0x80000000) { /* 32bit insn */ + *ucp += (short)(insn & 0x0000ffff); + regs->bpc += 4; + } else { /* 16bit insn */ +#ifdef CONFIG_ISA_DUAL_ISSUE + /* parallel exec check */ + if (!(regs->bpc & 0x2) && insn & 0x8000) { + res = emu_m32r2((unsigned short)insn, regs); + regs->bpc += 4; + } else +#endif /* CONFIG_ISA_DUAL_ISSUE */ + regs->bpc += 2; + } + + return res; +} + +static int emu_ld(unsigned long insn32, struct pt_regs *regs) +{ + unsigned char *ucp; + unsigned long val; + unsigned short insn16; + int size, src; + + insn16 = insn32 >> 16; + src = REG2(insn16); + ucp = (unsigned char *)get_reg(regs, src); + + if (insn_check(insn32, regs, &ucp)) + return -1; + + size = insn16 & 0x0040 ? 4 : 2; + if (copy_from_user(&val, ucp, size)) + return -1; + + if (size == 2) + val >>= 16; + + /* ldh sign check */ + if ((insn16 & 0x00f0) == 0x00a0 && (val & 0x8000)) + val |= 0xffff0000; + + set_reg(regs, REG1(insn16), val); + + /* ld increment check */ + if ((insn16 & 0xf0f0) == ISA_LD2) /* ld Rdest, @Rsrc+ */ + set_reg(regs, src, (unsigned long)(ucp + 4)); + + return 0; +} + +static int emu_st(unsigned long insn32, struct pt_regs *regs) +{ + unsigned char *ucp; + unsigned long val; + unsigned short insn16; + int size, src2; + + insn16 = insn32 >> 16; + src2 = REG2(insn16); + + ucp = (unsigned char *)get_reg(regs, src2); + + if (insn_check(insn32, regs, &ucp)) + return -1; + + size = insn16 & 0x0040 ? 4 : 2; + val = get_reg(regs, REG1(insn16)); + if (size == 2) + val <<= 16; + + /* st inc/dec check */ + if ((insn16 & 0xf0e0) == 0x2060) { + if (insn16 & 0x0010) + ucp -= 4; + else + ucp += 4; + + set_reg(regs, src2, (unsigned long)ucp); + } + + if (copy_to_user(ucp, &val, size)) + return -1; + + /* sth inc check */ + if ((insn16 & 0xf0f0) == ISA_STH2) { + ucp += 2; + set_reg(regs, src2, (unsigned long)ucp); + } + + return 0; +} + +int handle_unaligned_access(unsigned long insn32, struct pt_regs *regs) +{ + unsigned short insn16; + int res; + + insn16 = insn32 >> 16; + + /* ld or st check */ + if ((insn16 & 0x7000) != 0x2000) + return -1; + + /* insn alignment check */ + if ((insn16 & 0x8000) && (regs->bpc & 3)) + return -1; + + if (insn16 & 0x0080) /* ld */ + res = emu_ld(insn32, regs); + else /* st */ + res = emu_st(insn32, regs); + + return res; +} + diff --git a/arch/m32r/kernel/entry.S b/arch/m32r/kernel/entry.S new file mode 100644 index 000000000..29b0d8d0c --- /dev/null +++ b/arch/m32r/kernel/entry.S @@ -0,0 +1,999 @@ +/* + * linux/arch/m32r/kernel/entry.S + * + * Copyright (c) 2001, 2002 Hirokazu Takata, Hitoshi Yamamoto, H. Kondo + * Copyright (c) 2003 Hitoshi Yamamoto + * Copyright (c) 2004 Hirokazu Takata + * + * Taken from i386 version. + * Copyright (C) 1991, 1992 Linus Torvalds + */ + +/* + * entry.S contains the system-call and fault low-level handling routines. + * This also contains the timer-interrupt handler, as well as all interrupts + * and faults that can result in a task-switch. + * + * NOTE: This code handles signal-recognition, which happens every time + * after a timer-interrupt and after each system call. + * + * Stack layout in 'ret_from_system_call': + * ptrace needs to have all regs on the stack. + * if the order here is changed, it needs to be + * updated in fork.c:copy_process, signal.c:do_signal, + * ptrace.c and ptrace.h + * + * M32Rx/M32R2 M32R + * @(sp) - r4 ditto + * @(0x04,sp) - r5 ditto + * @(0x08,sp) - r6 ditto + * @(0x0c,sp) - *pt_regs ditto + * @(0x10,sp) - r0 ditto + * @(0x14,sp) - r1 ditto + * @(0x18,sp) - r2 ditto + * @(0x1c,sp) - r3 ditto + * @(0x20,sp) - r7 ditto + * @(0x24,sp) - r8 ditto + * @(0x28,sp) - r9 ditto + * @(0x2c,sp) - r10 ditto + * @(0x30,sp) - r11 ditto + * @(0x34,sp) - r12 ditto + * @(0x38,sp) - syscall_nr ditto + * @(0x3c,sp) - acc0h @(0x3c,sp) - acch + * @(0x40,sp) - acc0l @(0x40,sp) - accl + * @(0x44,sp) - acc1h @(0x44,sp) - psw + * @(0x48,sp) - acc1l @(0x48,sp) - bpc + * @(0x4c,sp) - psw @(0x4c,sp) - bbpsw + * @(0x50,sp) - bpc @(0x50,sp) - bbpc + * @(0x54,sp) - bbpsw @(0x54,sp) - spu (cr3) + * @(0x58,sp) - bbpc @(0x58,sp) - fp (r13) + * @(0x5c,sp) - spu (cr3) @(0x5c,sp) - lr (r14) + * @(0x60,sp) - fp (r13) @(0x60,sp) - spi (cr12) + * @(0x64,sp) - lr (r14) @(0x64,sp) - orig_r0 + * @(0x68,sp) - spi (cr2) + * @(0x6c,sp) - orig_r0 + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#if !defined(CONFIG_MMU) +#define sys_madvise sys_ni_syscall +#define sys_readahead sys_ni_syscall +#define sys_mprotect sys_ni_syscall +#define sys_msync sys_ni_syscall +#define sys_mlock sys_ni_syscall +#define sys_munlock sys_ni_syscall +#define sys_mlockall sys_ni_syscall +#define sys_munlockall sys_ni_syscall +#define sys_mremap sys_ni_syscall +#define sys_mincore sys_ni_syscall +#endif /* CONFIG_MMU */ + +#define R4(reg) @reg +#define R5(reg) @(0x04,reg) +#define R6(reg) @(0x08,reg) +#define PTREGS(reg) @(0x0C,reg) +#define R0(reg) @(0x10,reg) +#define R1(reg) @(0x14,reg) +#define R2(reg) @(0x18,reg) +#define R3(reg) @(0x1C,reg) +#define R7(reg) @(0x20,reg) +#define R8(reg) @(0x24,reg) +#define R9(reg) @(0x28,reg) +#define R10(reg) @(0x2C,reg) +#define R11(reg) @(0x30,reg) +#define R12(reg) @(0x34,reg) +#define SYSCALL_NR(reg) @(0x38,reg) +#if defined(CONFIG_ISA_M32R2) && defined(CONFIG_ISA_DSP_LEVEL2) +#define ACC0H(reg) @(0x3C,reg) +#define ACC0L(reg) @(0x40,reg) +#define ACC1H(reg) @(0x44,reg) +#define ACC1L(reg) @(0x48,reg) +#define PSW(reg) @(0x4C,reg) +#define BPC(reg) @(0x50,reg) +#define BBPSW(reg) @(0x54,reg) +#define BBPC(reg) @(0x58,reg) +#define SPU(reg) @(0x5C,reg) +#define FP(reg) @(0x60,reg) /* FP = R13 */ +#define LR(reg) @(0x64,reg) +#define SP(reg) @(0x68,reg) +#define ORIG_R0(reg) @(0x6C,reg) +#elif defined(CONFIG_ISA_M32R2) || defined(CONFIG_ISA_M32R) +#define ACCH(reg) @(0x3C,reg) +#define ACCL(reg) @(0x40,reg) +#define PSW(reg) @(0x44,reg) +#define BPC(reg) @(0x48,reg) +#define BBPSW(reg) @(0x4C,reg) +#define BBPC(reg) @(0x50,reg) +#define SPU(reg) @(0x54,reg) +#define FP(reg) @(0x58,reg) /* FP = R13 */ +#define LR(reg) @(0x5C,reg) +#define SP(reg) @(0x60,reg) +#define ORIG_R0(reg) @(0x64,reg) +#else +#error unknown isa configuration +#endif + +CF_MASK = 0x00000001 +TF_MASK = 0x00000100 +IF_MASK = 0x00000200 +DF_MASK = 0x00000400 +NT_MASK = 0x00004000 +VM_MASK = 0x00020000 + +#ifdef CONFIG_PREEMPT +#define preempt_stop(x) CLI(x) +#else +#define preempt_stop(x) +#define resume_kernel restore_all +#endif + +ENTRY(ret_from_fork) + ld r0, @sp+ + bl schedule_tail + GET_THREAD_INFO(r8) + bra syscall_exit + +/* + * Return to user mode is not as complex as all this looks, + * but we want the default path for a system call return to + * go as quickly as possible which is why some of this is + * less clear than it otherwise should be. + */ + + ; userspace resumption stub bypassing syscall exit tracing + ALIGN +ret_from_exception: + preempt_stop(r4) +ret_from_intr: + ld r4, PSW(sp) +#ifdef CONFIG_ISA_M32R2 + and3 r4, r4, #0x8800 ; check BSM and BPM bits +#else + and3 r4, r4, #0x8000 ; check BSM bit +#endif + beqz r4, resume_kernel +ENTRY(resume_userspace) + CLI(r4) ; make sure we don't miss an interrupt + ; setting need_resched or sigpending + ; between sampling and the iret + GET_THREAD_INFO(r8) + ld r9, @(TI_FLAGS, r8) + and3 r4, r9, #_TIF_WORK_MASK ; is there any work to be done on + ; int/exception return? + bnez r4, work_pending + bra restore_all + +#ifdef CONFIG_PREEMPT +ENTRY(resume_kernel) + GET_THREAD_INFO(r8) + ld r9, @(TI_PRE_COUNT, r8) ; non-zero preempt_count ? + bnez r9, restore_all +need_resched: + ld r9, @(TI_FLAGS, r8) ; need_resched set ? + and3 r4, r9, #_TIF_NEED_RESCHED + beqz r4, restore_all + ld r4, PSW(sp) ; interrupts off (exception path) ? + and3 r4, r4, #0x4000 + beqz r4, restore_all + LDIMM (r4, PREEMPT_ACTIVE) + st r4, @(TI_PRE_COUNT, r8) + STI(r4) + bl schedule + ldi r4, #0 + st r4, @(TI_PRE_COUNT, r8) + CLI(r4) + bra need_resched +#endif + + ; system call handler stub +ENTRY(system_call) + SWITCH_TO_KERNEL_STACK + SAVE_ALL + STI(r4) ; Enable interrupt + st sp, PTREGS(sp) ; implicit pt_regs parameter + cmpui r7, #NR_syscalls + bnc syscall_badsys + st r7, SYSCALL_NR(sp) ; syscall_nr + ; system call tracing in operation + GET_THREAD_INFO(r8) + ld r9, @(TI_FLAGS, r8) + and3 r4, r9, #_TIF_SYSCALL_TRACE + bnez r4, syscall_trace_entry +syscall_call: + slli r7, #2 ; table jump for the system call + LDIMM (r4, sys_call_table) + add r7, r4 + ld r7, @r7 + jl r7 ; execute system call + st r0, R0(sp) ; save the return value +syscall_exit: + CLI(r4) ; make sure we don't miss an interrupt + ; setting need_resched or sigpending + ; between sampling and the iret + ld r9, @(TI_FLAGS, r8) + and3 r4, r9, #_TIF_ALLWORK_MASK ; current->work + bnez r4, syscall_exit_work +restore_all: + RESTORE_ALL + + # perform work that needs to be done immediately before resumption + # r9 : frags + ALIGN +work_pending: + and3 r4, r9, #_TIF_NEED_RESCHED + beqz r4, work_notifysig +work_resched: + bl schedule + CLI(r4) ; make sure we don't miss an interrupt + ; setting need_resched or sigpending + ; between sampling and the iret + ld r9, @(TI_FLAGS, r8) + and3 r4, r9, #_TIF_WORK_MASK ; is there any work to be done other + ; than syscall tracing? + beqz r4, restore_all + and3 r4, r4, #_TIF_NEED_RESCHED + bnez r4, work_resched + +work_notifysig: ; deal with pending signals and + ; notify-resume requests + mv r0, sp ; arg1 : struct pt_regs *regs + ldi r1, #0 ; arg2 : sigset_t *oldset + mv r2, r9 ; arg3 : __u32 thread_info_flags + bl do_notify_resume + bra restore_all + + ; perform syscall exit tracing + ALIGN +syscall_trace_entry: + ldi r4, #-ENOSYS + st r4, R0(sp) + bl do_syscall_trace + ld r0, ORIG_R0(sp) + ld r1, R1(sp) + ld r2, R2(sp) + ld r3, R3(sp) + ld r4, R4(sp) + ld r5, R5(sp) + ld r6, R6(sp) + ld r7, SYSCALL_NR(sp) + cmpui r7, #NR_syscalls + bc syscall_call + bra syscall_exit + + ; perform syscall exit tracing + ALIGN +syscall_exit_work: + ld r9, @(TI_FLAGS, r8) + and3 r4, r9, #_TIF_SYSCALL_TRACE + beqz r4, work_pending + STI(r4) ; could let do_syscall_trace() call + ; schedule() instead + bl do_syscall_trace + bra resume_userspace + + ALIGN +syscall_fault: + SAVE_ALL + GET_THREAD_INFO(r8) + ldi r4, #-EFAULT + st r4, R0(sp) + bra resume_userspace + + ALIGN +syscall_badsys: + ldi r4, #-ENOSYS + st r4, R0(sp) + bra resume_userspace + + .global eit_vector + + .equ ei_vec_table, eit_vector + 0x0200 + +/* + * EI handler routine + */ +ENTRY(ei_handler) +#if defined(CONFIG_CHIP_M32700) + SWITCH_TO_KERNEL_STACK + ; WORKAROUND: force to clear SM bit and use the kernel stack (SPI). +#endif + SAVE_ALL + mv r1, sp ; arg1(regs) +#if defined(CONFIG_CHIP_VDEC2) || defined(CONFIG_CHIP_XNUX2) \ + || defined(CONFIG_CHIP_M32700) || defined(CONFIG_CHIP_M32102) \ + || defined(CONFIG_CHIP_OPSP) + +; GET_ICU_STATUS; + seth r0, #shigh(M32R_ICU_ISTS_ADDR) + ld r0, @(low(M32R_ICU_ISTS_ADDR),r0) + st r0, @-sp +#if defined(CONFIG_SMP) + /* + * If IRQ == 0 --> Nothing to do, Not write IMASK + * If IRQ == IPI --> Do IPI handler, Not write IMASK + * If IRQ != 0, IPI --> Do do_IRQ(), Write IMASK + */ + slli r0, #4 + srli r0, #24 ; r0(irq_num<<2) + ;; IRQ exist check +#if defined(CONFIG_CHIP_M32700) + /* WORKAROUND: IMASK bug M32700-TS1, TS2 chip. */ + beqz r0, 3f ; if (!irq_num) goto exit +#else + beqz r0, 1f ; if (!irq_num) goto exit +#endif /* WORKAROUND */ + ;; IPI check + cmpi r0, #(M32R_IRQ_IPI0<<2) ; ISN < IPI0 check + bc 2f + cmpi r0, #((M32R_IRQ_IPI7+1)<<2) ; ISN > IPI7 check + bnc 2f + LDIMM (r2, ei_vec_table) + add r2, r0 + ld r2, @r2 + beqz r2, 1f ; if (no IPI handler) goto exit + mv r0, r1 ; arg0(regs) + jl r2 + .fillinsn +1: + addi sp, #4 + bra ret_to_intr +#if defined(CONFIG_CHIP_M32700) + /* WORKAROUND: IMASK bug M32700-TS1, TS2 chip. */ + .fillinsn +3: + ld24 r14, #0x00070000 + seth r0, #shigh(M32R_ICU_IMASK_ADDR) + st r14, @(low(M32R_ICU_IMASK_ADDR), r0) + addi sp, #4 + bra ret_to_intr +#endif /* WORKAROUND */ + ;; do_IRQ + .fillinsn +2: + srli r0, #2 +#if defined(CONFIG_PLAT_USRV) + add3 r2, r0, #-(M32R_IRQ_INT1) ; INT1# interrupt + bnez r2, 9f + ; read ICU status register of PLD + seth r0, #high(PLD_ICUISTS) + or3 r0, r0, #low(PLD_ICUISTS) + lduh r0, @r0 + slli r0, #21 + srli r0, #27 ; ISN + addi r0, #(M32700UT_PLD_IRQ_BASE) + .fillinsn +9: +#elif defined(CONFIG_PLAT_M32700UT) + add3 r2, r0, #-(M32R_IRQ_INT1) ; INT1# interrupt + bnez r2, check_int0 + ; read ICU status register of PLD + seth r0, #high(PLD_ICUISTS) + or3 r0, r0, #low(PLD_ICUISTS) + lduh r0, @r0 + slli r0, #21 + srli r0, #27 ; ISN + addi r0, #(M32700UT_PLD_IRQ_BASE) + bra check_end + .fillinsn +check_int0: + add3 r2, r0, #-(M32R_IRQ_INT0) ; INT0# interrupt + bnez r2, check_int2 + ; read ICU status of LAN-board + seth r0, #high(M32700UT_LAN_ICUISTS) + or3 r0, r0, #low(M32700UT_LAN_ICUISTS) + lduh r0, @r0 + slli r0, #21 + srli r0, #27 ; ISN + add3 r0, r0, #(M32700UT_LAN_PLD_IRQ_BASE) + bra check_end + .fillinsn +check_int2: + add3 r2, r0, #-(M32R_IRQ_INT2) ; INT2# interrupt + bnez r2, check_end + ; read ICU status of LCD-board + seth r0, #high(M32700UT_LCD_ICUISTS) + or3 r0, r0, #low(M32700UT_LCD_ICUISTS) + lduh r0, @r0 + slli r0, #21 + srli r0, #27 ; ISN + add3 r0, r0, #(M32700UT_LCD_PLD_IRQ_BASE) + bra check_end + .fillinsn +check_end: +#elif defined(CONFIG_PLAT_OPSPUT) + add3 r2, r0, #-(M32R_IRQ_INT1) ; INT1# interrupt + bnez r2, check_int0 + ; read ICU status register of PLD + seth r0, #high(PLD_ICUISTS) + or3 r0, r0, #low(PLD_ICUISTS) + lduh r0, @r0 + slli r0, #21 + srli r0, #27 ; ISN + addi r0, #(OPSPUT_PLD_IRQ_BASE) + bra check_end + .fillinsn +check_int0: + add3 r2, r0, #-(M32R_IRQ_INT0) ; INT0# interrupt + bnez r2, check_int2 + ; read ICU status of LAN-board + seth r0, #high(OPSPUT_LAN_ICUISTS) + or3 r0, r0, #low(OPSPUT_LAN_ICUISTS) + lduh r0, @r0 + slli r0, #21 + srli r0, #27 ; ISN + add3 r0, r0, #(OPSPUT_LAN_PLD_IRQ_BASE) + bra check_end + .fillinsn +check_int2: + add3 r2, r0, #-(M32R_IRQ_INT2) ; INT2# interrupt + bnez r2, check_end + ; read ICU status of LCD-board + seth r0, #high(OPSPUT_LCD_ICUISTS) + or3 r0, r0, #low(OPSPUT_LCD_ICUISTS) + lduh r0, @r0 + slli r0, #21 + srli r0, #27 ; ISN + add3 r0, r0, #(OPSPUT_LCD_PLD_IRQ_BASE) + bra check_end + .fillinsn +check_end: +#endif /* CONFIG_PLAT_OPSPUT */ + bl do_IRQ ; r0(irq), r1(regs) +#else /* not CONFIG_SMP */ + srli r0, #22 ; r0(irq) +#if defined(CONFIG_PLAT_USRV) + add3 r2, r0, #-(M32R_IRQ_INT1) ; INT1# interrupt + bnez r2, 1f + ; read ICU status register of PLD + seth r0, #high(PLD_ICUISTS) + or3 r0, r0, #low(PLD_ICUISTS) + lduh r0, @r0 + slli r0, #21 + srli r0, #27 ; ISN + addi r0, #(M32700UT_PLD_IRQ_BASE) + .fillinsn +1: +#elif defined(CONFIG_PLAT_M32700UT) + add3 r2, r0, #-(M32R_IRQ_INT1) ; INT1# interrupt + bnez r2, check_int0 + ; read ICU status register of PLD + seth r0, #high(PLD_ICUISTS) + or3 r0, r0, #low(PLD_ICUISTS) + lduh r0, @r0 + slli r0, #21 + srli r0, #27 ; ISN + addi r0, #(M32700UT_PLD_IRQ_BASE) + bra check_end + .fillinsn +check_int0: + add3 r2, r0, #-(M32R_IRQ_INT0) ; INT0# interrupt + bnez r2, check_int2 + ; read ICU status of LAN-board + seth r0, #high(M32700UT_LAN_ICUISTS) + or3 r0, r0, #low(M32700UT_LAN_ICUISTS) + lduh r0, @r0 + slli r0, #21 + srli r0, #27 ; ISN + add3 r0, r0, #(M32700UT_LAN_PLD_IRQ_BASE) + bra check_end + .fillinsn +check_int2: + add3 r2, r0, #-(M32R_IRQ_INT2) ; INT2# interrupt + bnez r2, check_end + ; read ICU status of LCD-board + seth r0, #high(M32700UT_LCD_ICUISTS) + or3 r0, r0, #low(M32700UT_LCD_ICUISTS) + lduh r0, @r0 + slli r0, #21 + srli r0, #27 ; ISN + add3 r0, r0, #(M32700UT_LCD_PLD_IRQ_BASE) + bra check_end + .fillinsn +check_end: +#elif defined(CONFIG_PLAT_OPSPUT) + add3 r2, r0, #-(M32R_IRQ_INT1) ; INT1# interrupt + bnez r2, check_int0 + ; read ICU status register of PLD + seth r0, #high(PLD_ICUISTS) + or3 r0, r0, #low(PLD_ICUISTS) + lduh r0, @r0 + slli r0, #21 + srli r0, #27 ; ISN + addi r0, #(OPSPUT_PLD_IRQ_BASE) + bra check_end + .fillinsn +check_int0: + add3 r2, r0, #-(M32R_IRQ_INT0) ; INT0# interrupt + bnez r2, check_int2 + ; read ICU status of LAN-board + seth r0, #high(OPSPUT_LAN_ICUISTS) + or3 r0, r0, #low(OPSPUT_LAN_ICUISTS) + lduh r0, @r0 + slli r0, #21 + srli r0, #27 ; ISN + add3 r0, r0, #(OPSPUT_LAN_PLD_IRQ_BASE) + bra check_end + .fillinsn +check_int2: + add3 r2, r0, #-(M32R_IRQ_INT2) ; INT2# interrupt + bnez r2, check_end + ; read ICU status of LCD-board + seth r0, #high(OPSPUT_LCD_ICUISTS) + or3 r0, r0, #low(OPSPUT_LCD_ICUISTS) + lduh r0, @r0 + slli r0, #21 + srli r0, #27 ; ISN + add3 r0, r0, #(OPSPUT_LCD_PLD_IRQ_BASE) + bra check_end + .fillinsn +check_end: +#endif /* CONFIG_PLAT_OPSPUT */ + bl do_IRQ +#endif /* CONFIG_SMP */ + ld r14, @sp+ + seth r0, #shigh(M32R_ICU_IMASK_ADDR) + st r14, @(low(M32R_ICU_IMASK_ADDR),r0) +#else +#error no chip configuration +#endif +ret_to_intr: + bra ret_from_intr + +/* + * Default EIT handler + */ + ALIGN +int_msg: + .asciz "Unknown interrupt\n" + .byte 0 + +ENTRY(default_eit_handler) + push r0 + mvfc r0, psw + push r1 + push r2 + push r3 + push r0 + LDIMM (r0, __KERNEL_DS) + mv r0, r1 + mv r0, r2 + LDIMM (r0, int_msg) + bl printk + pop r0 + pop r3 + pop r2 + pop r1 + mvtc r0, psw + pop r0 +infinit: + bra infinit + +#ifdef CONFIG_MMU +/* + * Access Exception handler + */ +ENTRY(ace_handler) + SWITCH_TO_KERNEL_STACK + SAVE_ALL + + seth r2, #shigh(MMU_REG_BASE) /* Check status register */ + ld r4, @(low(MESTS_offset),r2) + st r4, @(low(MESTS_offset),r2) + srl3 r1, r4, #4 +#ifdef CONFIG_CHIP_M32700 + and3 r1, r1, #0x0000ffff + ; WORKAROUND: ignore TME bit for the M32700(TS1). +#endif /* CONFIG_CHIP_M32700 */ + beqz r1, inst +oprand: + ld r2, @(low(MDEVA_offset),r2) ; set address + srli r2, #12 + slli r2, #12 + srli r1, #1 + bra 1f +inst: + and3 r1, r4, #2 + srli r1, #1 + or3 r1, r1, #8 + mvfc r2, bpc ; set address + .fillinsn +1: + mvfc r3, psw + mv r0, sp + and3 r3, r3, 0x800 + srli r3, #9 + or r1, r3 + /* + * do_page_fault(): + * r0 : struct pt_regs *regs + * r1 : unsigned long error-code + * r2 : unsigned long address + * error-code: + * +------+------+------+------+ + * | bit3 | bit2 | bit1 | bit0 | + * +------+------+------+------+ + * bit 3 == 0:means data, 1:means instruction + * bit 2 == 0:means kernel, 1:means user-mode + * bit 1 == 0:means read, 1:means write + * bit 0 == 0:means no page found 1:means protection fault + * + */ + bl do_page_fault + bra ret_from_intr +#endif /* CONFIG_MMU */ + + +ENTRY(alignment_check) +/* void alignment_check(int error_code) */ + SWITCH_TO_KERNEL_STACK + SAVE_ALL + ldi r1, #0x30 ; error_code + mv r0, sp ; pt_regs + bl do_alignment_check +error_code: + bra ret_from_exception + +ENTRY(rie_handler) +/* void rie_handler(int error_code) */ + SWITCH_TO_KERNEL_STACK + SAVE_ALL + mvfc r0, bpc + ld r1, @r0 + seth r0, #0xa0f0 + st r1, @r0 + ldi r1, #0x20 ; error_code + mv r0, sp ; pt_regs + bl do_rie_handler + bra error_code + +ENTRY(pie_handler) +/* void pie_handler(int error_code) */ + SWITCH_TO_KERNEL_STACK + SAVE_ALL + ldi r1, #0 ; error_code ; FIXME + mv r0, sp ; pt_regs + bl do_pie_handler + bra error_code + +ENTRY(debug_trap) + .global withdraw_debug_trap + /* void debug_trap(void) */ + SWITCH_TO_KERNEL_STACK + SAVE_ALL + mv r0, sp ; pt_regs + bl withdraw_debug_trap + ldi r1, #0 ; error_code + mv r0, sp ; pt_regs + bl do_debug_trap + bra error_code + + +/* Cache flushing handler */ +ENTRY(cache_flushing_handler) + .global _flush_cache_all + /* void _flush_cache_all(void); */ + SWITCH_TO_KERNEL_STACK + push r0 + push r1 + push r2 + push r3 + push r4 + push r5 + push r6 + push r7 + push lr + bl _flush_cache_all + pop lr + pop r7 + pop r6 + pop r5 + pop r4 + pop r3 + pop r2 + pop r1 + pop r0 + rte + +.data +ENTRY(sys_call_table) + .long sys_restart_syscall /* 0 - old "setup()" system call*/ + .long sys_exit + .long sys_fork + .long sys_read + .long sys_write + .long sys_open /* 5 */ + .long sys_close + .long sys_waitpid + .long sys_creat + .long sys_link + .long sys_unlink /* 10 */ + .long sys_execve + .long sys_chdir + .long sys_time + .long sys_mknod + .long sys_chmod /* 15 */ + .long sys_lchown + .long sys_ni_syscall /* old break syscall holder */ + .long sys_stat + .long sys_lseek + .long sys_getpid /* 20 */ + .long sys_mount + .long sys_oldumount + .long sys_setuid + .long sys_getuid + .long sys_stime /* 25 */ + .long sys_ptrace + .long sys_alarm + .long sys_fstat + .long sys_pause + .long sys_utime /* 30 */ + .long sys_cacheflush /* for M32R */ /* old stty syscall holder */ + .long sys_cachectl /* for M32R */ /* old gtty syscall holder */ + .long sys_access + .long sys_nice + .long sys_ni_syscall /* 35 - old ftime syscall holder */ + .long sys_sync + .long sys_kill + .long sys_rename + .long sys_mkdir + .long sys_rmdir /* 40 */ + .long sys_dup + .long sys_pipe + .long sys_times + .long sys_ni_syscall /* old prof syscall holder */ + .long sys_brk /* 45 */ + .long sys_setgid + .long sys_getgid + .long sys_signal + .long sys_geteuid + .long sys_getegid /* 50 */ + .long sys_acct + .long sys_umount /* recycled never used phys() */ + .long sys_ni_syscall /* old lock syscall holder */ + .long sys_ioctl + .long sys_fcntl /* 55 */ + .long sys_ni_syscall /* old mpx syscall holder */ + .long sys_setpgid + .long sys_ni_syscall /* old ulimit syscall holder */ + .long sys_ni_syscall /* sys_olduname */ + .long sys_umask /* 60 */ + .long sys_chroot + .long sys_ustat + .long sys_dup2 + .long sys_getppid + .long sys_getpgrp /* 65 */ + .long sys_setsid + .long sys_sigaction + .long sys_sgetmask + .long sys_ssetmask + .long sys_setreuid /* 70 */ + .long sys_setregid + .long sys_sigsuspend + .long sys_sigpending + .long sys_sethostname + .long sys_setrlimit /* 75 */ + .long sys_getrlimit + .long sys_getrusage + .long sys_gettimeofday + .long sys_settimeofday + .long sys_getgroups /* 80 */ + .long sys_setgroups + .long sys_ni_syscall /* sys_oldselect */ + .long sys_symlink + .long sys_lstat + .long sys_readlink /* 85 */ + .long sys_uselib + .long sys_swapon + .long sys_reboot + .long old_readdir + .long sys_ni_syscall /* 90 - old_mmap syscall holder */ + .long sys_munmap + .long sys_truncate + .long sys_ftruncate + .long sys_fchmod + .long sys_fchown /* 95 */ + .long sys_getpriority + .long sys_setpriority + .long sys_ni_syscall /* old profil syscall holder */ + .long sys_statfs + .long sys_fstatfs /* 100 */ + .long sys_ni_syscall /* ioperm */ + .long sys_socketcall + .long sys_syslog + .long sys_setitimer + .long sys_getitimer /* 105 */ + .long sys_newstat + .long sys_newlstat + .long sys_newfstat + .long sys_uname + .long sys_ni_syscall /* 110 - iopl */ + .long sys_vhangup + .long sys_ni_syscall /* for idle */ + .long sys_ni_syscall /* for vm86old */ + .long sys_wait4 + .long sys_swapoff /* 115 */ + .long sys_sysinfo + .long sys_ipc + .long sys_fsync + .long sys_sigreturn + .long sys_clone /* 120 */ + .long sys_setdomainname + .long sys_newuname + .long sys_ni_syscall /* sys_modify_ldt */ + .long sys_adjtimex + .long sys_mprotect /* 125 */ + .long sys_sigprocmask + .long sys_ni_syscall /* sys_create_module */ + .long sys_init_module + .long sys_delete_module + .long sys_ni_syscall /* 130 sys_get_kernel_syms */ + .long sys_quotactl + .long sys_getpgid + .long sys_fchdir + .long sys_bdflush + .long sys_sysfs /* 135 */ + .long sys_personality + .long sys_ni_syscall /* for afs_syscall */ + .long sys_setfsuid + .long sys_setfsgid + .long sys_llseek /* 140 */ + .long sys_getdents + .long sys_select + .long sys_flock + .long sys_msync + .long sys_readv /* 145 */ + .long sys_writev + .long sys_getsid + .long sys_fdatasync + .long sys_sysctl + .long sys_mlock /* 150 */ + .long sys_munlock + .long sys_mlockall + .long sys_munlockall + .long sys_sched_setparam + .long sys_sched_getparam /* 155 */ + .long sys_sched_setscheduler + .long sys_sched_getscheduler + .long sys_sched_yield + .long sys_sched_get_priority_max + .long sys_sched_get_priority_min /* 160 */ + .long sys_sched_rr_get_interval + .long sys_nanosleep + .long sys_mremap + .long sys_setresuid + .long sys_getresuid /* 165 */ + .long sys_tas /* vm86 */ + .long sys_ni_syscall /* sys_query_module */ + .long sys_poll + .long sys_nfsservctl + .long sys_setresgid /* 170 */ + .long sys_getresgid + .long sys_prctl + .long sys_rt_sigreturn + .long sys_rt_sigaction + .long sys_rt_sigprocmask /* 175 */ + .long sys_rt_sigpending + .long sys_rt_sigtimedwait + .long sys_rt_sigqueueinfo + .long sys_rt_sigsuspend + .long sys_pread64 /* 180 */ + .long sys_pwrite64 + .long sys_chown + .long sys_getcwd + .long sys_capget + .long sys_capset /* 185 */ + .long sys_sigaltstack + .long sys_sendfile + .long sys_ni_syscall /* streams1 */ + .long sys_ni_syscall /* streams2 */ + .long sys_vfork /* 190 */ + .long sys_getrlimit + .long sys_mmap2 + .long sys_truncate64 + .long sys_ftruncate64 + .long sys_stat64 /* 195 */ + .long sys_lstat64 + .long sys_fstat64 + .long sys_lchown + .long sys_getuid + .long sys_getgid /* 200 */ + .long sys_geteuid + .long sys_getegid + .long sys_setreuid + .long sys_setregid + .long sys_getgroups /* 205 */ + .long sys_setgroups + .long sys_fchown + .long sys_setresuid + .long sys_getresuid + .long sys_setresgid /* 210 */ + .long sys_getresgid + .long sys_chown + .long sys_setuid + .long sys_setgid + .long sys_setfsuid /* 215 */ + .long sys_setfsgid + .long sys_pivot_root + .long sys_mincore + .long sys_madvise + .long sys_getdents64 /* 220 */ + .long sys_fcntl64 + .long sys_ni_syscall /* reserved for TUX */ + .long sys_ni_syscall /* Reserved for Security */ + .long sys_gettid + .long sys_readahead /* 225 */ + .long sys_setxattr + .long sys_lsetxattr + .long sys_fsetxattr + .long sys_getxattr + .long sys_lgetxattr /* 230 */ + .long sys_fgetxattr + .long sys_listxattr + .long sys_llistxattr + .long sys_flistxattr + .long sys_removexattr /* 235 */ + .long sys_lremovexattr + .long sys_fremovexattr + .long sys_tkill + .long sys_sendfile64 + .long sys_futex /* 240 */ + .long sys_sched_setaffinity + .long sys_sched_getaffinity + .long sys_ni_syscall /* reserved for "set_thread_area" system call */ + .long sys_ni_syscall /* reserved for "get_thread_area" system call */ + .long sys_io_setup /* 245 */ + .long sys_io_destroy + .long sys_io_getevents + .long sys_io_submit + .long sys_io_cancel + .long sys_fadvise64 /* 250 */ + .long sys_ni_syscall + .long sys_exit_group + .long sys_lookup_dcookie + .long sys_epoll_create + .long sys_epoll_ctl /* 255 */ + .long sys_epoll_wait + .long sys_remap_file_pages + .long sys_set_tid_address + .long sys_timer_create + .long sys_timer_settime /* 260 */ + .long sys_timer_gettime + .long sys_timer_getoverrun + .long sys_timer_delete + .long sys_clock_settime + .long sys_clock_gettime /* 265 */ + .long sys_clock_getres + .long sys_clock_nanosleep + .long sys_statfs64 + .long sys_fstatfs64 + .long sys_tgkill /* 270 */ + .long sys_utimes + .long sys_fadvise64_64 + .long sys_ni_syscall /* Reserved for sys_vserver */ + .long sys_ni_syscall /* Reserved for sys_mbind */ + .long sys_ni_syscall /* Reserved for sys_get_mempolicy */ + .long sys_ni_syscall /* Reserved for sys_set_mempolicy */ + .long sys_mq_open + .long sys_mq_unlink + .long sys_mq_timedsend + .long sys_mq_timedreceive /* 280 */ + .long sys_mq_notify + .long sys_mq_getsetattr + .long sys_ni_syscall /* reserved for kexec */ + .long sys_waitid + +syscall_table_size=(.-sys_call_table) + diff --git a/arch/m32r/kernel/head.S b/arch/m32r/kernel/head.S new file mode 100644 index 000000000..3e8317399 --- /dev/null +++ b/arch/m32r/kernel/head.S @@ -0,0 +1,287 @@ +/* + * linux/arch/m32r/kernel/head.S + * + * M32R startup code. + * + * Copyright (c) 2001, 2002 Hiroyuki Kondo, Hirokazu Takata, + * Hitoshi Yamamoto + */ + +/* $Id$ */ + +#include +__INIT +__INITDATA + + .text +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * References to members of the boot_cpu_data structure. + */ + .text + .global start_kernel + .global __bss_start + .global _end +ENTRY(stext) +ENTRY(_stext) +ENTRY(startup_32) + /* Setup up the stack pointer */ + LDIMM (r0, spi_stack_top) + LDIMM (r1, spu_stack_top) + mvtc r0, spi + mvtc r1, spu + + /* Initilalize PSW */ + ldi r0, #0x0000 /* use SPI, disable EI */ + mvtc r0, psw + + /* Set up the stack pointer */ + LDIMM (r0, stack_start) + ld r0, @r0 + mvtc r0, spi + +/* + * Clear BSS first so that there are no surprises... + */ +#ifdef CONFIG_ISA_DUAL_ISSUE + + LDIMM (r2, __bss_start) + LDIMM (r3, _end) + sub r3, r2 ; BSS size in bytes + ; R4 = BSS size in longwords (rounded down) + mv r4, r3 || ldi r1, #0 + srli r4, #4 || addi r2, #-4 + beqz r4, .Lendloop1 +.Lloop1: +#ifndef CONFIG_CHIP_M32310 + ; Touch memory for the no-write-allocating cache. + ld r0, @(4,r2) +#endif + st r1, @+r2 || addi r4, #-1 + st r1, @+r2 + st r1, @+r2 + st r1, @+r2 || cmpeq r1, r4 ; R4 = 0? + bnc .Lloop1 +.Lendloop1: + and3 r4, r3, #15 + addi r2, #4 + beqz r4, .Lendloop2 +.Lloop2: + stb r1, @r2 || addi r4, #-1 + addi r2, #1 + bnez r4, .Lloop2 +.Lendloop2: + +#else /* not CONFIG_ISA_DUAL_ISSUE */ + + LDIMM (r2, __bss_start) + LDIMM (r3, _end) + sub r3, r2 ; BSS size in bytes + mv r4, r3 + srli r4, #2 ; R4 = BSS size in longwords (rounded down) + ldi r1, #0 ; clear R1 for longwords store + addi r2, #-4 ; account for pre-inc store + beqz r4, .Lendloop1 ; any more to go? +.Lloop1: + st r1, @+r2 ; yep, zero out another longword + addi r4, #-1 ; decrement count + bnez r4, .Lloop1 ; go do some more +.Lendloop1: + and3 r4, r3, #3 ; get no. of remaining BSS bytes to clear + addi r2, #4 ; account for pre-inc store + beqz r4, .Lendloop2 ; any more to go? +.Lloop2: + stb r1, @r2 ; yep, zero out another byte + addi r2, #1 ; bump address + addi r4, #-1 ; decrement count + bnez r4, .Lloop2 ; go do some more +.Lendloop2: + +#endif /* not CONFIG_ISA_DUAL_ISSUE */ + +#if 0 /* M32R_FIXME */ +/* + * Copy data segment from ROM to RAM. + */ + .global ROM_D, TOP_DATA, END_DATA + + LDIMM (r1, ROM_D) + LDIMM (r2, TOP_DATA) + LDIMM (r3, END_DATA) + addi r2, #-4 + addi r3, #-4 +loop1: + ld r0, @r1+ + st r0, @+r2 + cmp r2, r3 + bc loop1 +#endif /* 0 */ + +/* Jump to kernel */ + LDIMM (r2, start_kernel) + jl r2 + .fillinsn +1: + bra 1b ; main should never return here, but + ; just in case, we know what happens. + +#ifdef CONFIG_SMP +/* + * AP startup routine + */ + .text + .global eit_vector +ENTRY(startup_AP) +;; setup EVB + LDIMM (r4, eit_vector) + mvtc r4, cr5 + +;; enable MMU + LDIMM (r2, init_tlb) + jl r2 + seth r4, #high(MATM) + or3 r4, r4, #low(MATM) + ldi r5, #0x01 + st r5, @r4 ; Set MATM Reg(T bit ON) + ld r6, @r4 ; MATM Check + LDIMM (r5, 1f) + jmp r5 ; enable MMU + nop + .fillinsn +1: +;; ISN check + ld r6, @r4 ; MATM Check + seth r4, #high(M32R_ICU_ISTS_ADDR) + or3 r4, r4, #low(M32R_ICU_ISTS_ADDR) + ld r5, @r4 ; Read ISTSi reg. + mv r6, r5 + slli r5, #13 ; PIML check + srli r5, #13 ; + seth r4, #high(M32R_ICU_IMASK_ADDR) + or3 r4, r4, #low(M32R_ICU_IMASK_ADDR) + st r5, @r4 ; Write IMASKi reg. + slli r6, #4 ; ISN check + srli r6, #26 ; + seth r4, #high(M32R_IRQ_IPI5) + or3 r4, r4, #low(M32R_IRQ_IPI5) + bne r4, r6, 2f ; if (ISN != CPU_BOOT_IPI) goto sleep; + +;; check cpu_bootout_map and set cpu_bootin_map + LDIMM (r4, cpu_bootout_map) + ld r4, @r4 + seth r5, #high(M32R_CPUID_PORTL) + or3 r5, r5, #low(M32R_CPUID_PORTL) + ld r5, @r5 + ldi r6, #1 + sll r6, r5 + and r4, r6 + beqz r4, 2f + LDIMM (r4, cpu_bootin_map) + ld r5, @r4 + or r5, r6 + st r6, @r4 + +;; clear PSW + ldi r4, #0 + mvtc r4, psw + +;; setup SPI + LDIMM (r4, stack_start) + ld r4, @r4 + mvtc r4, spi + +;; setup BPC (start_secondary) + LDIMM (r4, start_secondary) + mvtc r4, bpc + + rte ; goto startup_secondary + nop + nop + + .fillinsn +2: + ;; disable MMU + seth r4, #high(MATM) + or3 r4, r4, #low(MATM) + ldi r5, #0 + st r5, @r4 ; Set MATM Reg(T bit OFF) + ld r6, @r4 ; MATM Check + LDIMM (r4, 3f) + seth r5, #high(__PAGE_OFFSET) + or3 r5, r5, #low(__PAGE_OFFSET) + not r5, r5 + and r4, r5 + jmp r4 ; disable MMU + nop + .fillinsn +3: + ;; SLEEP and wait IPI + LDIMM (r4, AP_loop) + seth r5, #high(__PAGE_OFFSET) + or3 r5, r5, #low(__PAGE_OFFSET) + not r5, r5 + and r4, r5 + jmp r4 + nop + nop +#endif /* CONFIG_SMP */ + +ENTRY(stack_start) + .long init_thread_union+8192 + .long __KERNEL_DS + +/* + * This is initialized to create a identity-mapping at 0-4M (for bootup + * purposes) and another mapping of the 0-4M area at virtual address + * PAGE_OFFSET. + */ + .text + +#define MOUNT_ROOT_RDONLY 1 +#define RAMDISK_FLAGS 0 ; 1024KB +#define ORIG_ROOT_DEV 0x0100 ; /dev/ram0 (major:01, minor:00) +#define LOADER_TYPE 1 ; (??? - non-zero value seems + ; to be needed to boot from initrd) + +#define COMMAND_LINE "" + + .section .empty_zero_page, "aw" +ENTRY(empty_zero_page) + .long MOUNT_ROOT_RDONLY /* offset: +0x00 */ + .long RAMDISK_FLAGS + .long ORIG_ROOT_DEV + .long LOADER_TYPE + .long 0 /* INITRD_START */ /* +0x10 */ + .long 0 /* INITRD_SIZE */ + .long 0 /* CPU_CLOCK */ + .long 0 /* BUS_CLOCK */ + .long 0 /* TIMER_DIVIDE */ /* +0x20 */ + .balign 256,0 + .asciz COMMAND_LINE + .byte 0 + .balign 4096,0,4096 + +/*------------------------------------------------------------------------ + * Stack area + */ + .section .spi + ALIGN + .global spi_stack_top + .zero 1024 +spi_stack_top: + + .section .spu + ALIGN + .global spu_stack_top + .zero 1024 +spu_stack_top: + + .end diff --git a/arch/m32r/kernel/init_task.c b/arch/m32r/kernel/init_task.c new file mode 100644 index 000000000..9e508fd9d --- /dev/null +++ b/arch/m32r/kernel/init_task.c @@ -0,0 +1,41 @@ +/* orig : i386 init_task.c */ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +static struct fs_struct init_fs = INIT_FS; +static struct files_struct init_files = INIT_FILES; +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); + +/* + * Initial thread structure. + * + * We need to make sure that this is 8192-byte aligned due to the + * way process stacks are handled. This is done by having a special + * "init_task" linker map entry.. + */ +union thread_union init_thread_union + __attribute__((__section__(".data.init_task"))) = + { INIT_THREAD_INFO(init_task) }; + +/* + * Initial task structure. + * + * All other task structs will be allocated on slabs in fork.c + */ +struct task_struct init_task = INIT_TASK(init_task); + +EXPORT_SYMBOL(init_task); + diff --git a/arch/m32r/kernel/io_m32700ut.c b/arch/m32r/kernel/io_m32700ut.c new file mode 100644 index 000000000..501b75581 --- /dev/null +++ b/arch/m32r/kernel/io_m32700ut.c @@ -0,0 +1,455 @@ +/* + * linux/arch/m32r/kernel/io_mappi.c + * + * Typical I/O routines for M32700UT board. + * + * Copyright (c) 2001, 2002 Hiroyuki Kondo, Hirokazu Takata, + * Hitoshi Yamamoto, Takeo Takahashi + * + * 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 +#include +#include +#include +#include + +#if defined(CONFIG_PCMCIA) && defined(CONFIG_M32R_CFC) +#include + +#define M32R_PCC_IOMAP_SIZE 0x1000 + +#define M32R_PCC_IOSTART0 0x1000 +#define M32R_PCC_IOEND0 (M32R_PCC_IOSTART0 + M32R_PCC_IOMAP_SIZE - 1) + +extern void pcc_ioread_byte(int, unsigned long, void *, size_t, size_t, int); +extern void pcc_ioread_word(int, unsigned long, void *, size_t, size_t, int); +extern void pcc_iowrite_byte(int, unsigned long, void *, size_t, size_t, int); +extern void pcc_iowrite_word(int, unsigned long, void *, size_t, size_t, int); +#endif /* CONFIG_PCMCIA && CONFIG_M32R_CFC */ + +#define PORT2ADDR(port) _port2addr(port) +#define PORT2ADDR_USB(port) _port2addr_usb(port) + +static __inline__ void *_port2addr(unsigned long port) +{ + return (void *)(port + NONCACHE_OFFSET); +} + +#if defined(CONFIG_IDE) && !defined(CONFIG_M32R_CFC) +static __inline__ void *__port2addr_ata(unsigned long port) +{ + static int dummy_reg; + + switch (port) { + case 0x1f0: return (void *)0xac002000; + case 0x1f1: return (void *)0xac012800; + case 0x1f2: return (void *)0xac012002; + case 0x1f3: return (void *)0xac012802; + case 0x1f4: return (void *)0xac012004; + case 0x1f5: return (void *)0xac012804; + case 0x1f6: return (void *)0xac012006; + case 0x1f7: return (void *)0xac012806; + case 0x3f6: return (void *)0xac01200e; + default: return (void *)&dummy_reg; + } +} +#endif + +/* + * M32700UT-LAN is located in the extended bus space + * from 0x10000000 to 0x13ffffff on physical address. + * The base address of LAN controller(LAN91C111) is 0x300. + */ +#define LAN_IOSTART 0x300 +#define LAN_IOEND 0x320 +static __inline__ void *_port2addr_ne(unsigned long port) +{ + return (void *)(port + NONCACHE_OFFSET + 0x10000000); +} +static __inline__ void *_port2addr_usb(unsigned long port) +{ + return (void *)((port & 0x0f) + NONCACHE_OFFSET + 0x10303000); +} + +static __inline__ void delay(void) +{ + __asm__ __volatile__ ("push r0; \n\t pop r0;" : : :"memory"); +} + +/* + * NIC I/O function + */ + +#define PORT2ADDR_NE(port) _port2addr_ne(port) + +static __inline__ unsigned char _ne_inb(void *portp) +{ + return *(volatile unsigned char *)portp; +} + +static __inline__ unsigned short _ne_inw(void *portp) +{ + return (unsigned short)le16_to_cpu(*(volatile unsigned short *)portp); +} + +static __inline__ void _ne_insb(void *portp, void * addr, unsigned long count) +{ + unsigned char *buf = (unsigned char *)addr; + + while (count--) *buf++ = _ne_inb(portp); +} + +static __inline__ void _ne_outb(unsigned char b, void *portp) +{ + *(volatile unsigned char *)portp = b; +} + +static __inline__ void _ne_outw(unsigned short w, void *portp) +{ + *(volatile unsigned short *)portp = cpu_to_le16(w); +} + +unsigned char _inb(unsigned long port) +{ + if (port >= LAN_IOSTART && port < LAN_IOEND) + return _ne_inb(PORT2ADDR_NE(port)); + +#if defined(CONFIG_IDE) && !defined(CONFIG_M32R_CFC) + else if ((port >= 0x1f0 && port <=0x1f7) || port == 0x3f6) { + return *(volatile unsigned char *)__port2addr_ata(port); + } +#endif +#if defined(CONFIG_PCMCIA) && defined(CONFIG_M32R_CFC) + else if (port >= M32R_PCC_IOSTART0 && port <= M32R_PCC_IOEND0) { + unsigned char b; + pcc_ioread_byte(0, port, &b, sizeof(b), 1, 0); + return b; + } else +#endif + + return *(volatile unsigned char *)PORT2ADDR(port); +} + +unsigned short _inw(unsigned long port) +{ + if (port >= LAN_IOSTART && port < LAN_IOEND) + return _ne_inw(PORT2ADDR_NE(port)); +#if defined(CONFIG_IDE) && !defined(CONFIG_M32R_CFC) + else if ((port >= 0x1f0 && port <=0x1f7) || port == 0x3f6) { + return *(volatile unsigned short *)__port2addr_ata(port); + } +#endif +#if defined(CONFIG_USB) + else if(port >= 0x340 && port < 0x3a0) + return *(volatile unsigned short *)PORT2ADDR_USB(port); +#endif +#if defined(CONFIG_PCMCIA) && defined(CONFIG_M32R_CFC) + else if (port >= M32R_PCC_IOSTART0 && port <= M32R_PCC_IOEND0) { + unsigned short w; + pcc_ioread_word(0, port, &w, sizeof(w), 1, 0); + return w; + } else +#endif + return *(volatile unsigned short *)PORT2ADDR(port); +} + +unsigned long _inl(unsigned long port) +{ +#if defined(CONFIG_PCMCIA) && defined(CONFIG_M32R_CFC) + if (port >= M32R_PCC_IOSTART0 && port <= M32R_PCC_IOEND0) { + unsigned long l; + pcc_ioread_word(0, port, &l, sizeof(l), 1, 0); + return l; + } else +#endif + return *(volatile unsigned long *)PORT2ADDR(port); +} + +unsigned char _inb_p(unsigned long port) +{ + unsigned char v; + + if (port >= LAN_IOSTART && port < LAN_IOEND) + v = _ne_inb(PORT2ADDR_NE(port)); + else +#if defined(CONFIG_IDE) && !defined(CONFIG_M32R_CFC) + if ((port >= 0x1f0 && port <=0x1f7) || port == 0x3f6) { + return *(volatile unsigned char *)__port2addr_ata(port); + } else +#endif +#if defined(CONFIG_PCMCIA) && defined(CONFIG_M32R_CFC) + if (port >= M32R_PCC_IOSTART0 && port <= M32R_PCC_IOEND0) { + unsigned char b; + pcc_ioread_byte(0, port, &b, sizeof(b), 1, 0); + return b; + } else +#endif + v = *(volatile unsigned char *)PORT2ADDR(port); + + delay(); + return (v); +} + +unsigned short _inw_p(unsigned long port) +{ + unsigned short v; + + if (port >= LAN_IOSTART && port < LAN_IOEND) + v = _ne_inw(PORT2ADDR_NE(port)); + else +#if defined(CONFIG_IDE) && !defined(CONFIG_M32R_CFC) + if ((port >= 0x1f0 && port <=0x1f7) || port == 0x3f6) { + return *(volatile unsigned short *)__port2addr_ata(port); + } else +#endif +#if defined(CONFIG_USB) + if(port >= 0x340 && port < 0x3a0) + return *(volatile unsigned short *)PORT2ADDR_USB(port); + else +#endif +#if defined(CONFIG_PCMCIA) && defined(CONFIG_M32R_CFC) + if (port >= M32R_PCC_IOSTART0 && port <= M32R_PCC_IOEND0) { + unsigned short w; + pcc_ioread_word(0, port, &w, sizeof(w), 1, 0); + return w; + } else +#endif + v = *(volatile unsigned short *)PORT2ADDR(port); + + delay(); + return (v); +} + +unsigned long _inl_p(unsigned long port) +{ + unsigned long v; + + v = *(volatile unsigned long *)PORT2ADDR(port); + delay(); + return (v); +} + +void _outb(unsigned char b, unsigned long port) +{ + if (port >= LAN_IOSTART && port < LAN_IOEND) + _ne_outb(b, PORT2ADDR_NE(port)); + else +#if defined(CONFIG_IDE) && !defined(CONFIG_M32R_CFC) + if ((port >= 0x1f0 && port <=0x1f7) || port == 0x3f6) { + *(volatile unsigned char *)__port2addr_ata(port) = b; + } else +#endif +#if defined(CONFIG_PCMCIA) && defined(CONFIG_M32R_CFC) + if (port >= M32R_PCC_IOSTART0 && port <= M32R_PCC_IOEND0) { + pcc_iowrite_byte(0, port, &b, sizeof(b), 1, 0); + } else +#endif + *(volatile unsigned char *)PORT2ADDR(port) = b; +} + +void _outw(unsigned short w, unsigned long port) +{ + if (port >= LAN_IOSTART && port < LAN_IOEND) + _ne_outw(w, PORT2ADDR_NE(port)); + else +#if defined(CONFIG_IDE) && !defined(CONFIG_M32R_CFC) + if ((port >= 0x1f0 && port <=0x1f7) || port == 0x3f6) { + *(volatile unsigned short *)__port2addr_ata(port) = w; + } else +#endif +#if defined(CONFIG_USB) + if(port >= 0x340 && port < 0x3a0) + *(volatile unsigned short *)PORT2ADDR_USB(port) = w; + else +#endif +#if defined(CONFIG_PCMCIA) && defined(CONFIG_M32R_CFC) + if (port >= M32R_PCC_IOSTART0 && port <= M32R_PCC_IOEND0) { + pcc_iowrite_word(0, port, &w, sizeof(w), 1, 0); + } else +#endif + *(volatile unsigned short *)PORT2ADDR(port) = w; +} + +void _outl(unsigned long l, unsigned long port) +{ +#if defined(CONFIG_PCMCIA) && defined(CONFIG_M32R_CFC) + if (port >= M32R_PCC_IOSTART0 && port <= M32R_PCC_IOEND0) { + pcc_iowrite_word(0, port, &l, sizeof(l), 1, 0); + } else +#endif + *(volatile unsigned long *)PORT2ADDR(port) = l; +} + +void _outb_p(unsigned char b, unsigned long port) +{ + if (port >= LAN_IOSTART && port < LAN_IOEND) + _ne_outb(b, PORT2ADDR_NE(port)); + else +#if defined(CONFIG_IDE) && !defined(CONFIG_M32R_CFC) + if ((port >= 0x1f0 && port <=0x1f7) || port == 0x3f6) { + *(volatile unsigned char *)__port2addr_ata(port) = b; + } else +#endif +#if defined(CONFIG_PCMCIA) && defined(CONFIG_M32R_CFC) + if (port >= M32R_PCC_IOSTART0 && port <= M32R_PCC_IOEND0) { + pcc_iowrite_byte(0, port, &b, sizeof(b), 1, 0); + } else +#endif + *(volatile unsigned char *)PORT2ADDR(port) = b; + + delay(); +} + +void _outw_p(unsigned short w, unsigned long port) +{ + if (port >= LAN_IOSTART && port < LAN_IOEND) + _ne_outw(w, PORT2ADDR_NE(port)); + else +#if defined(CONFIG_IDE) && !defined(CONFIG_M32R_CFC) + if ((port >= 0x1f0 && port <=0x1f7) || port == 0x3f6) { + *(volatile unsigned short *)__port2addr_ata(port) = w; + } else +#endif +#if defined(CONFIG_USB) + if(port >= 0x340 && port < 0x3a0) + *(volatile unsigned short *)PORT2ADDR_USB(port) = w; + else +#endif +#if defined(CONFIG_PCMCIA) && defined(CONFIG_M32R_CFC) + if (port >= M32R_PCC_IOSTART0 && port <= M32R_PCC_IOEND0) { + pcc_iowrite_word(0, port, &w, sizeof(w), 1, 0); + } else +#endif + *(volatile unsigned short *)PORT2ADDR(port) = w; + + delay(); +} + +void _outl_p(unsigned long l, unsigned long port) +{ + *(volatile unsigned long *)PORT2ADDR(port) = l; + delay(); +} + +void _insb(unsigned int port, void * addr, unsigned long count) +{ + if (port >= LAN_IOSTART && port < LAN_IOEND) + _ne_insb(PORT2ADDR_NE(port), addr, count); +#if defined(CONFIG_IDE) && !defined(CONFIG_M32R_CFC) + else if ((port >= 0x1f0 && port <=0x1f7) || port == 0x3f6) { + unsigned char *buf = addr; + unsigned char *portp = __port2addr_ata(port); + while(count--) *buf++ = *(volatile unsigned char *)portp; + } +#endif +#if defined(CONFIG_PCMCIA) && defined(CONFIG_M32R_CFC) + else if (port >= M32R_PCC_IOSTART0 && port <= M32R_PCC_IOEND0) { + pcc_ioread_byte(0, port, (void *)addr, sizeof(unsigned char), count, 1); + } +#endif + else { + unsigned char *buf = addr; + unsigned char *portp = PORT2ADDR(port); + while(count--) *buf++ = *(volatile unsigned char *)portp; + } +} + +void _insw(unsigned int port, void * addr, unsigned long count) +{ + unsigned short *buf = addr; + unsigned short *portp; + + if (port >= LAN_IOSTART && port < LAN_IOEND) { + /* + * This portion is only used by smc91111.c to read data + * from the DATA_REG. Do not swap the data. + */ + portp = PORT2ADDR_NE(port); + while (count--) *buf++ = *(volatile unsigned short *)portp; +#if defined(CONFIG_PCMCIA) && defined(CONFIG_M32R_CFC) + } else if (port >= M32R_PCC_IOSTART0 && port <= M32R_PCC_IOEND0) { + pcc_ioread_word(9, port, (void *)addr, sizeof(unsigned short), count, 1); +#endif +#if defined(CONFIG_IDE) && !defined(CONFIG_M32R_CFC) + } else if ((port >= 0x1f0 && port <=0x1f7) || port == 0x3f6) { + portp = __port2addr_ata(port); + while (count--) *buf++ = *(volatile unsigned short *)portp; +#endif + } else { + portp = PORT2ADDR(port); + while (count--) *buf++ = *(volatile unsigned short *)portp; + } +} + +void _insl(unsigned int port, void * addr, unsigned long count) +{ + unsigned long *buf = addr; + unsigned long *portp; + + portp = PORT2ADDR(port); + while (count--) *buf++ = *(volatile unsigned long *)portp; +} + +void _outsb(unsigned int port, const void * addr, unsigned long count) +{ + const unsigned char *buf = addr; + unsigned char *portp; + + if (port >= LAN_IOSTART && port < LAN_IOEND) { + portp = PORT2ADDR_NE(port); + while (count--) _ne_outb(*buf++, portp); +#if defined(CONFIG_IDE) && !defined(CONFIG_M32R_CFC) + } else if ((port >= 0x1f0 && port <=0x1f7) || port == 0x3f6) { + portp = __port2addr_ata(port); + while(count--) *(volatile unsigned char *)portp = *buf++; +#endif +#if defined(CONFIG_PCMCIA) && defined(CONFIG_M32R_CFC) + } else if (port >= M32R_PCC_IOSTART0 && port <= M32R_PCC_IOEND0) { + pcc_iowrite_byte(0, port, (void *)addr, sizeof(unsigned char), count, 1); +#endif + } else { + portp = PORT2ADDR(port); + while(count--) *(volatile unsigned char *)portp = *buf++; + } +} + +void _outsw(unsigned int port, const void * addr, unsigned long count) +{ + const unsigned short *buf = addr; + unsigned short *portp; + + if (port >= LAN_IOSTART && port < LAN_IOEND) { + /* + * This portion is only used by smc91111.c to write data + * into the DATA_REG. Do not swap the data. + */ + portp = PORT2ADDR_NE(port); + while(count--) *(volatile unsigned short *)portp = *buf++; +#if defined(CONFIG_IDE) && !defined(CONFIG_M32R_CFC) + } else if ((port >= 0x1f0 && port <=0x1f7) || port == 0x3f6) { + portp = __port2addr_ata(port); + while(count--) *(volatile unsigned short *)portp = *buf++; +#endif +#if defined(CONFIG_PCMCIA) && defined(CONFIG_M32R_CFC) + } else if (port >= M32R_PCC_IOSTART0 && port <= M32R_PCC_IOEND0) { + pcc_iowrite_word(9, port, (void *)addr, sizeof(unsigned short), count, 1); +#endif + } else { + portp = PORT2ADDR(port); + while(count--) *(volatile unsigned short *)portp = *buf++; + } +} + +void _outsl(unsigned int port, const void * addr, unsigned long count) +{ + const unsigned long *buf = addr; + unsigned char *portp; + + portp = PORT2ADDR(port); + while(count--) *(volatile unsigned long *)portp = *buf++; +} diff --git a/arch/m32r/kernel/io_mappi.c b/arch/m32r/kernel/io_mappi.c new file mode 100644 index 000000000..74aeca636 --- /dev/null +++ b/arch/m32r/kernel/io_mappi.c @@ -0,0 +1,368 @@ +/* + * linux/arch/m32r/kernel/io_mappi.c + * + * Typical I/O routines for Mappi board. + * + * Copyright (c) 2001, 2002 Hiroyuki Kondo, Hirokazu Takata, + * Hitoshi Yamamoto + */ + +/* $Id: io_mappi.c,v 1.9 2003/12/02 07:18:08 fujiwara Exp $ */ + +#include +#include +#include +#include +#include + +#if defined(CONFIG_PCMCIA) && defined(CONFIG_M32RPCC) +#include + +#define M32R_PCC_IOMAP_SIZE 0x1000 + +#define M32R_PCC_IOSTART0 0x1000 +#define M32R_PCC_IOEND0 (M32R_PCC_IOSTART0 + M32R_PCC_IOMAP_SIZE - 1) +#define M32R_PCC_IOSTART1 0x2000 +#define M32R_PCC_IOEND1 (M32R_PCC_IOSTART1 + M32R_PCC_IOMAP_SIZE - 1) + +extern void pcc_ioread(int, unsigned long, void *, size_t, size_t, int); +extern void pcc_iowrite(int, unsigned long, void *, size_t, size_t, int); +#endif /* CONFIG_PCMCIA && CONFIG_M32RPCC */ + +#define PORT2ADDR(port) _port2addr(port) + +static __inline__ void *_port2addr(unsigned long port) +{ + return (void *)(port + NONCACHE_OFFSET); +} + +static __inline__ void *_port2addr_ne(unsigned long port) +{ + return (void *)((port<<1) + NONCACHE_OFFSET + 0x0C000000); +} + +static __inline__ void delay(void) +{ + __asm__ __volatile__ ("push r0; \n\t pop r0;" : : :"memory"); +} + +/* + * NIC I/O function + */ + +#define PORT2ADDR_NE(port) _port2addr_ne(port) + +static __inline__ unsigned char _ne_inb(void *portp) +{ + return (unsigned char) *(volatile unsigned short *)portp; +} + +static __inline__ unsigned short _ne_inw(void *portp) +{ + unsigned short tmp; + + tmp = *(volatile unsigned short *)portp; + return le16_to_cpu(tmp); +} + +static __inline__ void _ne_outb(unsigned char b, void *portp) +{ + *(volatile unsigned short *)portp = (unsigned short)b; +} + +static __inline__ void _ne_outw(unsigned short w, void *portp) +{ + *(volatile unsigned short *)portp = cpu_to_le16(w); +} + +unsigned char _inb(unsigned long port) +{ + if (port >= 0x300 && port < 0x320) + return _ne_inb(PORT2ADDR_NE(port)); + else +#if defined(CONFIG_PCMCIA) && defined(CONFIG_M32RPCC) + if (port >= M32R_PCC_IOSTART0 && port <= M32R_PCC_IOEND0) { + unsigned char b; + pcc_ioread(0, port, &b, sizeof(b), 1, 0); + return b; + } else if (port >= M32R_PCC_IOSTART1 && port <= M32R_PCC_IOEND1) { + unsigned char b; + pcc_ioread(1, port, &b, sizeof(b), 1, 0); + return b; + } else +#endif + + return *(volatile unsigned char *)PORT2ADDR(port); +} + +unsigned short _inw(unsigned long port) +{ + if (port >= 0x300 && port < 0x320) + return _ne_inw(PORT2ADDR_NE(port)); + else +#if defined(CONFIG_PCMCIA) && defined(CONFIG_M32RPCC) + if (port >= M32R_PCC_IOSTART0 && port <= M32R_PCC_IOEND0) { + unsigned short w; + pcc_ioread(0, port, &w, sizeof(w), 1, 0); + return w; + } else if (port >= M32R_PCC_IOSTART1 && port <= M32R_PCC_IOEND1) { + unsigned short w; + pcc_ioread(1, port, &w, sizeof(w), 1, 0); + return w; + } else +#endif + return *(volatile unsigned short *)PORT2ADDR(port); +} + +unsigned long _inl(unsigned long port) +{ +#if defined(CONFIG_PCMCIA) && defined(CONFIG_M32RPCC) + if (port >= M32R_PCC_IOSTART0 && port <= M32R_PCC_IOEND0) { + unsigned long l; + pcc_ioread(0, port, &l, sizeof(l), 1, 0); + return l; + } else if (port >= M32R_PCC_IOSTART1 && port <= M32R_PCC_IOEND1) { + unsigned short l; + pcc_ioread(1, port, &l, sizeof(l), 1, 0); + return l; + } else +#endif + return *(volatile unsigned long *)PORT2ADDR(port); +} + +unsigned char _inb_p(unsigned long port) +{ + unsigned char v; + + if (port >= 0x300 && port < 0x320) + v = _ne_inb(PORT2ADDR_NE(port)); + else +#if defined(CONFIG_PCMCIA) && defined(CONFIG_M32RPCC) + if (port >= M32R_PCC_IOSTART0 && port <= M32R_PCC_IOEND0) { + unsigned char b; + pcc_ioread(0, port, &b, sizeof(b), 1, 0); + return b; + } else if (port >= M32R_PCC_IOSTART1 && port <= M32R_PCC_IOEND1) { + unsigned char b; + pcc_ioread(1, port, &b, sizeof(b), 1, 0); + return b; + } else +#endif + v = *(volatile unsigned char *)PORT2ADDR(port); + + delay(); + return (v); +} + +unsigned short _inw_p(unsigned long port) +{ + unsigned short v; + + if (port >= 0x300 && port < 0x320) + v = _ne_inw(PORT2ADDR_NE(port)); + else +#if defined(CONFIG_PCMCIA) && defined(CONFIG_M32RPCC) + if (port >= M32R_PCC_IOSTART0 && port <= M32R_PCC_IOEND0) { + unsigned short w; + pcc_ioread(0, port, &w, sizeof(w), 1, 0); + return w; + } else if (port >= M32R_PCC_IOSTART1 && port <= M32R_PCC_IOEND1) { + unsigned short w; + pcc_ioread(1, port, &w, sizeof(w), 1, 0); + return w; + } else +#endif + v = *(volatile unsigned short *)PORT2ADDR(port); + + delay(); + return (v); +} + +unsigned long _inl_p(unsigned long port) +{ + unsigned long v; + + v = *(volatile unsigned long *)PORT2ADDR(port); + delay(); + return (v); +} + +void _outb(unsigned char b, unsigned long port) +{ + if (port >= 0x300 && port < 0x320) + _ne_outb(b, PORT2ADDR_NE(port)); + else +#if defined(CONFIG_PCMCIA) && defined(CONFIG_M32RPCC) + if (port >= M32R_PCC_IOSTART0 && port <= M32R_PCC_IOEND0) { + pcc_iowrite(0, port, &b, sizeof(b), 1, 0); + } else if (port >= M32R_PCC_IOSTART1 && port <= M32R_PCC_IOEND1) { + pcc_iowrite(1, port, &b, sizeof(b), 1, 0); + } else +#endif + *(volatile unsigned char *)PORT2ADDR(port) = b; +} + +void _outw(unsigned short w, unsigned long port) +{ + if (port >= 0x300 && port < 0x320) + _ne_outw(w, PORT2ADDR_NE(port)); + else +#if defined(CONFIG_PCMCIA) && defined(CONFIG_M32RPCC) + if (port >= M32R_PCC_IOSTART0 && port <= M32R_PCC_IOEND0) { + pcc_iowrite(0, port, &w, sizeof(w), 1, 0); + } else if (port >= M32R_PCC_IOSTART1 && port <= M32R_PCC_IOEND1) { + pcc_iowrite(1, port, &w, sizeof(w), 1, 0); + } else +#endif + *(volatile unsigned short *)PORT2ADDR(port) = w; +} + +void _outl(unsigned long l, unsigned long port) +{ +#if defined(CONFIG_PCMCIA) && defined(CONFIG_M32RPCC) + if (port >= M32R_PCC_IOSTART0 && port <= M32R_PCC_IOEND0) { + pcc_iowrite(0, port, &l, sizeof(l), 1, 0); + } else if (port >= M32R_PCC_IOSTART1 && port <= M32R_PCC_IOEND1) { + pcc_iowrite(1, port, &l, sizeof(l), 1, 0); + } else +#endif + *(volatile unsigned long *)PORT2ADDR(port) = l; +} + +void _outb_p(unsigned char b, unsigned long port) +{ + if (port >= 0x300 && port < 0x320) + _ne_outb(b, PORT2ADDR_NE(port)); + else +#if defined(CONFIG_PCMCIA) && defined(CONFIG_M32RPCC) + if (port >= M32R_PCC_IOSTART0 && port <= M32R_PCC_IOEND0) { + pcc_iowrite(0, port, &b, sizeof(b), 1, 0); + } else if (port >= M32R_PCC_IOSTART1 && port <= M32R_PCC_IOEND1) { + pcc_iowrite(1, port, &b, sizeof(b), 1, 0); + } else +#endif + *(volatile unsigned char *)PORT2ADDR(port) = b; + + delay(); +} + +void _outw_p(unsigned short w, unsigned long port) +{ + if (port >= 0x300 && port < 0x320) + _ne_outw(w, PORT2ADDR_NE(port)); + else +#if defined(CONFIG_PCMCIA) && defined(CONFIG_M32RPCC) + if (port >= M32R_PCC_IOSTART0 && port <= M32R_PCC_IOEND0) { + pcc_iowrite(0, port, &w, sizeof(w), 1, 0); + } else if (port >= M32R_PCC_IOSTART1 && port <= M32R_PCC_IOEND1) { + pcc_iowrite(1, port, &w, sizeof(w), 1, 0); + } else +#endif + *(volatile unsigned short *)PORT2ADDR(port) = w; + + delay(); +} + +void _outl_p(unsigned long l, unsigned long port) +{ + *(volatile unsigned long *)PORT2ADDR(port) = l; + delay(); +} + +void _insb(unsigned int port, void * addr, unsigned long count) +{ + unsigned short *buf = addr; + unsigned short *portp; + + if (port >= 0x300 && port < 0x320){ + portp = PORT2ADDR_NE(port); + while(count--) *buf++ = *(volatile unsigned char *)portp; +#if defined(CONFIG_PCMCIA) && defined(CONFIG_M32RPCC) + } else if (port >= M32R_PCC_IOSTART0 && port <= M32R_PCC_IOEND0) { + pcc_ioread(0, port, (void *)addr, sizeof(unsigned char), count, 1); + } else if (port >= M32R_PCC_IOSTART1 && port <= M32R_PCC_IOEND1) { + pcc_ioread(1, port, (void *)addr, sizeof(unsigned char), count, 1); +#endif + } else { + portp = PORT2ADDR(port); + while(count--) *buf++ = *(volatile unsigned char *)portp; + } +} + +void _insw(unsigned int port, void * addr, unsigned long count) +{ + unsigned short *buf = addr; + unsigned short *portp; + + if (port >= 0x300 && port < 0x320) { + portp = PORT2ADDR_NE(port); + while (count--) *buf++ = _ne_inw(portp); +#if defined(CONFIG_PCMCIA) && defined(CONFIG_M32RPCC) + } else if (port >= M32R_PCC_IOSTART0 && port <= M32R_PCC_IOEND0) { + pcc_ioread(0, port, (void *)addr, sizeof(unsigned short), count, 1); + } else if (port >= M32R_PCC_IOSTART1 && port <= M32R_PCC_IOEND1) { + pcc_ioread(1, port, (void *)addr, sizeof(unsigned short), count, 1); +#endif + } else { + portp = PORT2ADDR(port); + while (count--) *buf++ = *(volatile unsigned short *)portp; + } +} + +void _insl(unsigned int port, void * addr, unsigned long count) +{ + unsigned long *buf = addr; + unsigned long *portp; + + portp = PORT2ADDR(port); + while (count--) *buf++ = *(volatile unsigned long *)portp; +} + +void _outsb(unsigned int port, const void * addr, unsigned long count) +{ + const unsigned char *buf = addr; + unsigned char *portp; + + if (port >= 0x300 && port < 0x320) { + portp = PORT2ADDR_NE(port); + while (count--) _ne_outb(*buf++, portp); +#if defined(CONFIG_PCMCIA) && defined(CONFIG_M32RPCC) + } else if (port >= M32R_PCC_IOSTART0 && port <= M32R_PCC_IOEND0) { + pcc_iowrite(0, port, (void *)addr, sizeof(unsigned char), count, 1); + } else if (port >= M32R_PCC_IOSTART1 && port <= M32R_PCC_IOEND1) { + pcc_iowrite(1, port, (void *)addr, sizeof(unsigned char), count, 1); +#endif + } else { + portp = PORT2ADDR(port); + while(count--) *(volatile unsigned char *)portp = *buf++; + } +} + +void _outsw(unsigned int port, const void * addr, unsigned long count) +{ + const unsigned short *buf = addr; + unsigned short *portp; + + if (port >= 0x300 && port < 0x320) { + portp = PORT2ADDR_NE(port); + while (count--) _ne_outw(*buf++, portp); +#if defined(CONFIG_PCMCIA) && defined(CONFIG_M32RPCC) + } else if (port >= M32R_PCC_IOSTART0 && port <= M32R_PCC_IOEND0) { + pcc_iowrite(0, port, (void *)addr, sizeof(unsigned short), count, 1); + } else if (port >= M32R_PCC_IOSTART1 && port <= M32R_PCC_IOEND1) { + pcc_iowrite(1, port, (void *)addr, sizeof(unsigned short), count, 1); +#endif + } else { + portp = PORT2ADDR(port); + while(count--) *(volatile unsigned short *)portp = *buf++; + } +} + +void _outsl(unsigned int port, const void * addr, unsigned long count) +{ + const unsigned long *buf = addr; + unsigned char *portp; + + portp = PORT2ADDR(port); + while(count--) *(volatile unsigned long *)portp = *buf++; +} diff --git a/arch/m32r/kernel/io_mappi2.c b/arch/m32r/kernel/io_mappi2.c new file mode 100644 index 000000000..7705fb0bb --- /dev/null +++ b/arch/m32r/kernel/io_mappi2.c @@ -0,0 +1,370 @@ +/* + * linux/arch/m32r/kernel/io_mappi2.c + * + * Typical I/O routines for Mappi2 board. + * + * Copyright (c) 2001-2003 Hiroyuki Kondo, Hirokazu Takata, + * Hitoshi Yamamoto, Mamoru Sakugawa + */ + +/* $Id:$ */ + +#include +#include +#include +#include + +#if defined(CONFIG_PCMCIA) && defined(CONFIG_M32R_CFC) +#include + +#define M32R_PCC_IOMAP_SIZE 0x1000 + +#define M32R_PCC_IOSTART0 0x1000 +#define M32R_PCC_IOEND0 (M32R_PCC_IOSTART0 + M32R_PCC_IOMAP_SIZE - 1) + +extern void pcc_ioread_byte(int, unsigned long, void *, size_t, size_t, int); +extern void pcc_ioread_word(int, unsigned long, void *, size_t, size_t, int); +extern void pcc_iowrite_byte(int, unsigned long, void *, size_t, size_t, int); +extern void pcc_iowrite_word(int, unsigned long, void *, size_t, size_t, int); +#endif /* CONFIG_PCMCIA && CONFIG_MAPPI2_CFC */ + +#define PORT2ADDR(port) _port2addr(port) +#define PORT2ADDR_NE(port) _port2addr_ne(port) +#define PORT2ADDR_USB(port) _port2addr_usb(port) + +static __inline__ void *_port2addr(unsigned long port) +{ + return (void *)(port + NONCACHE_OFFSET); +} + +#define LAN_IOSTART 0x300 +#define LAN_IOEND 0x320 +#ifdef CONFIG_CHIP_OPSP +static __inline__ void *_port2addr_ne(unsigned long port) +{ + return (void *)(port + NONCACHE_OFFSET + 0x10000000); +} +#else +static __inline__ void *_port2addr_ne(unsigned long port) +{ + return (void *)(port + NONCACHE_OFFSET + 0x04000000); +} +#endif +static __inline__ void *_port2addr_usb(unsigned long port) +{ + return (void *)(port + NONCACHE_OFFSET + 0x14000000); +} +static __inline__ void delay(void) +{ + __asm__ __volatile__ ("push r0; \n\t pop r0;" : : :"memory"); +} + +/* + * NIC I/O function + */ + +static __inline__ unsigned char _ne_inb(void *portp) +{ + return (unsigned char) *(volatile unsigned char *)portp; +} + +static __inline__ unsigned short _ne_inw(void *portp) +{ +#if 1 /* byte swap */ + unsigned short tmp,tmp2; + tmp = *(volatile unsigned short *)portp; + tmp2 = (tmp>>8|tmp<<8); + return tmp2; +#else + return *(volatile unsigned short *)portp; +#endif +} + +static __inline__ void _ne_insb(void *portp, void * addr, unsigned long count) +{ + unsigned short tmp; + unsigned char *buf = addr; + + tmp = *(volatile unsigned char *)portp; + while (count--) *buf++ = *(volatile unsigned char *)portp; +} + +static __inline__ void _ne_outb(unsigned char b, void *portp) +{ + *(volatile unsigned char *)portp = (unsigned char)b; +} + +static __inline__ void _ne_outw(unsigned short w, void *portp) +{ + *(volatile unsigned short *)portp = (w>>8|w<<8); +} + +unsigned char _inb(unsigned long port) +{ + if (port >= LAN_IOSTART && port < LAN_IOEND) + return _ne_inb(PORT2ADDR_NE(port)); +#if defined(CONFIG_PCMCIA) && defined(CONFIG_M32R_CFC) + else if (port >= M32R_PCC_IOSTART0 && port <= M32R_PCC_IOEND0) { + unsigned char b; + pcc_ioread_byte(0, port, &b, sizeof(b), 1, 0); + return b; + } else +#endif + + return *(volatile unsigned char *)PORT2ADDR(port); +} + +unsigned short _inw(unsigned long port) +{ + if (port >= LAN_IOSTART && port < LAN_IOEND) + return _ne_inw(PORT2ADDR_NE(port)); +#if defined(CONFIG_USB) + else if (port >= 0x340 && port < 0x3a0) + return *(volatile unsigned short *)PORT2ADDR_USB(port); +#endif + +#if defined(CONFIG_PCMCIA) && defined(CONFIG_M32R_CFC) + else if (port >= M32R_PCC_IOSTART0 && port <= M32R_PCC_IOEND0) { + unsigned short w; + pcc_ioread_word(0, port, &w, sizeof(w), 1, 0); + return w; + } else +#endif + return *(volatile unsigned short *)PORT2ADDR(port); +} + +unsigned long _inl(unsigned long port) +{ +#if defined(CONFIG_PCMCIA) && defined(CONFIG_M32R_CFC) + if (port >= M32R_PCC_IOSTART0 && port <= M32R_PCC_IOEND0) { + unsigned long l; + pcc_ioread_word(0, port, &l, sizeof(l), 1, 0); + return l; + } else +#endif + return *(volatile unsigned long *)PORT2ADDR(port); +} + +unsigned char _inb_p(unsigned long port) +{ + unsigned char v; + + if (port >= 0x300 && port < 0x320) + v = _ne_inb(PORT2ADDR_NE(port)); + else +#if defined(CONFIG_PCMCIA) && defined(CONFIG_M32R_CFC) + if (port >= M32R_PCC_IOSTART0 && port <= M32R_PCC_IOEND0) { + unsigned char b; + pcc_ioread_byte(0, port, &b, sizeof(b), 1, 0); + return b; + } else +#endif + v = *(volatile unsigned char *)PORT2ADDR(port); + + delay(); + return (v); +} + +unsigned short _inw_p(unsigned long port) +{ + unsigned short v; + + if (port >= 0x300 && port < 0x320) + v = _ne_inw(PORT2ADDR_NE(port)); + else +#if defined(CONFIG_USB) + if (port >= 0x340 && port < 0x3a0) + v = *(volatile unsigned short *)PORT2ADDR_USB(port); + else +#endif +#if defined(CONFIG_PCMCIA) && defined(CONFIG_M32R_CFC) + if (port >= M32R_PCC_IOSTART0 && port <= M32R_PCC_IOEND0) { + unsigned short w; + pcc_ioread_word(0, port, &w, sizeof(w), 1, 0); + return w; + } else +#endif + v = *(volatile unsigned short *)PORT2ADDR(port); + + delay(); + return (v); +} + +unsigned long _inl_p(unsigned long port) +{ + unsigned long v; + + v = *(volatile unsigned long *)PORT2ADDR(port); + delay(); + return (v); +} + +void _outb(unsigned char b, unsigned long port) +{ + if (port >= LAN_IOSTART && port < LAN_IOEND) + _ne_outb(b, PORT2ADDR_NE(port)); + else +#if defined(CONFIG_PCMCIA) && defined(CONFIG_M32R_CFC) + if (port >= M32R_PCC_IOSTART0 && port <= M32R_PCC_IOEND0) { + pcc_iowrite_byte(0, port, &b, sizeof(b), 1, 0); + } else +#endif + *(volatile unsigned char *)PORT2ADDR(port) = b; +} + +void _outw(unsigned short w, unsigned long port) +{ + if (port >= LAN_IOSTART && port < LAN_IOEND) + _ne_outw(w, PORT2ADDR_NE(port)); + else +#if defined(CONFIG_USB) + if (port >= 0x340 && port < 0x3a0) + *(volatile unsigned short *)PORT2ADDR_USB(port) = w; + else +#endif +#if defined(CONFIG_PCMCIA) && defined(CONFIG_M32R_CFC) + if (port >= M32R_PCC_IOSTART0 && port <= M32R_PCC_IOEND0) { + pcc_iowrite_word(0, port, &w, sizeof(w), 1, 0); + } else +#endif + *(volatile unsigned short *)PORT2ADDR(port) = w; +} + +void _outl(unsigned long l, unsigned long port) +{ +#if defined(CONFIG_PCMCIA) && defined(CONFIG_M32R_CFC) + if (port >= M32R_PCC_IOSTART0 && port <= M32R_PCC_IOEND0) { + pcc_iowrite_word(0, port, &l, sizeof(l), 1, 0); + } else +#endif + *(volatile unsigned long *)PORT2ADDR(port) = l; +} + +void _outb_p(unsigned char b, unsigned long port) +{ + if (port >= LAN_IOSTART && port < LAN_IOEND) + _ne_outb(b, PORT2ADDR_NE(port)); + else +#if defined(CONFIG_PCMCIA) && defined(CONFIG_M32R_CFC) + if (port >= M32R_PCC_IOSTART0 && port <= M32R_PCC_IOEND0) { + pcc_iowrite_byte(0, port, &b, sizeof(b), 1, 0); + } else +#endif + *(volatile unsigned char *)PORT2ADDR(port) = b; + + delay(); +} + +void _outw_p(unsigned short w, unsigned long port) +{ + if (port >= LAN_IOSTART && port < LAN_IOEND) + _ne_outw(w, PORT2ADDR_NE(port)); + else +#if defined(CONFIG_USB) + if (port >= 0x340 && port < 0x3a0) + *(volatile unsigned short *)PORT2ADDR_USB(port) = w; + else +#endif +#if defined(CONFIG_PCMCIA) && defined(CONFIG_M32R_CFC) + if (port >= M32R_PCC_IOSTART0 && port <= M32R_PCC_IOEND0) { + pcc_iowrite_word(0, port, &w, sizeof(w), 1, 0); + } else +#endif + *(volatile unsigned short *)PORT2ADDR(port) = w; + + delay(); +} + +void _outl_p(unsigned long l, unsigned long port) +{ + *(volatile unsigned long *)PORT2ADDR(port) = l; + delay(); +} + +void _insb(unsigned int port, void * addr, unsigned long count) +{ + if (port >= LAN_IOSTART && port < LAN_IOEND) + _ne_insb(PORT2ADDR_NE(port), addr, count); +#if defined(CONFIG_PCMCIA) && defined(CONFIG_M32R_CFC) + else if (port >= M32R_PCC_IOSTART0 && port <= M32R_PCC_IOEND0) { + pcc_ioread_byte(0, port, (void *)addr, sizeof(unsigned char), count, 1); + } +#endif + else { + unsigned char *buf = addr; + unsigned char *portp = PORT2ADDR(port); + while(count--) *buf++ = *(volatile unsigned char *)portp; + } +} + +void _insw(unsigned int port, void * addr, unsigned long count) +{ + unsigned short *buf = addr; + unsigned short *portp; + + if (port >= LAN_IOSTART && port < LAN_IOEND) + portp = PORT2ADDR_NE(port); + while (count--) *buf++ = *(volatile unsigned short *)portp; +#if defined(CONFIG_PCMCIA) && defined(CONFIG_M32R_CFC) + } else if (port >= M32R_PCC_IOSTART0 && port <= M32R_PCC_IOEND0) { + pcc_ioread_word(9, port, (void *)addr, sizeof(unsigned short), count, 1); +#endif + } else { + portp = PORT2ADDR(port); + while (count--) *buf++ = *(volatile unsigned short *)portp; + } +} + +void _insl(unsigned int port, void * addr, unsigned long count) +{ + unsigned long *buf = addr; + unsigned long *portp; + + portp = PORT2ADDR(port); + while (count--) *buf++ = *(volatile unsigned long *)portp; +} + +void _outsb(unsigned int port, const void * addr, unsigned long count) +{ + const unsigned char *buf = addr; + unsigned char *portp; + + if (port >= LAN_IOSTART && port < LAN_IOEND) + portp = PORT2ADDR_NE(port); + while (count--) _ne_outb(*buf++, portp); +#if defined(CONFIG_PCMCIA) && defined(CONFIG_M32R_CFC) + } else if (port >= M32R_PCC_IOSTART0 && port <= M32R_PCC_IOEND0) { + pcc_iowrite_byte(0, port, (void *)addr, sizeof(unsigned char), count, 1); +#endif + } else { + portp = PORT2ADDR(port); + while(count--) *(volatile unsigned char *)portp = *buf++; + } +} + +void _outsw(unsigned int port, const void * addr, unsigned long count) +{ + const unsigned short *buf = addr; + unsigned short *portp; + + if (port >= LAN_IOSTART && port < LAN_IOEND) + portp = PORT2ADDR_NE(port); + while (count--) *(volatile unsigned short *)portp = *buf++; +#if defined(CONFIG_PCMCIA) && defined(CONFIG_M32R_CFC) + } else if (port >= M32R_PCC_IOSTART0 && port <= M32R_PCC_IOEND0) { + pcc_iowrite_word(9, port, (void *)addr, sizeof(unsigned short), count, 1); +#endif + } else { + portp = PORT2ADDR(port); + while(count--) *(volatile unsigned short *)portp = *buf++; + } +} + +void _outsl(unsigned int port, const void * addr, unsigned long count) +{ + const unsigned long *buf = addr; + unsigned char *portp; + + portp = PORT2ADDR(port); + while(count--) *(volatile unsigned long *)portp = *buf++; +} diff --git a/arch/m32r/kernel/io_oaks32r.c b/arch/m32r/kernel/io_oaks32r.c new file mode 100644 index 000000000..7fcbf2047 --- /dev/null +++ b/arch/m32r/kernel/io_oaks32r.c @@ -0,0 +1,243 @@ +/* + * linux/arch/m32r/kernel/io_oaks32r.c + * + * Typical I/O routines for OAKS32R board. + * + * Copyright (c) 2001-2004 Hiroyuki Kondo, Hirokazu Takata, + * Hitoshi Yamamoto, Mamoru Sakugawa + */ + +/* $Id$ */ + +#include +#include +#include +#include + +#define PORT2ADDR(port) _port2addr(port) + +static __inline__ void *_port2addr(unsigned long port) +{ + return (void *)(port + NONCACHE_OFFSET); +} + +static __inline__ void *_port2addr_ne(unsigned long port) +{ + return (void *)((port<<1) + NONCACHE_OFFSET + 0x02000000); +} + +static __inline__ void delay(void) +{ + __asm__ __volatile__ ("push r0; \n\t pop r0;" : : :"memory"); +} + +/* + * NIC I/O function + */ + +#define PORT2ADDR_NE(port) _port2addr_ne(port) + +static __inline__ unsigned char _ne_inb(void *portp) +{ + return *(volatile unsigned char *)(portp+1); +} + +static __inline__ unsigned short _ne_inw(void *portp) +{ + unsigned short tmp; + + tmp = *(unsigned short *)(portp) & 0xff; + tmp |= *(unsigned short *)(portp+2) << 8; + return tmp; +} + +static __inline__ void _ne_insb(void *portp, void * addr, unsigned long count) +{ + unsigned char *buf = addr; + while (count--) *buf++ = *(volatile unsigned char *)(portp+1); +} + +static __inline__ void _ne_outb(unsigned char b, void *portp) +{ + *(volatile unsigned char *)(portp+1) = b; +} + +static __inline__ void _ne_outw(unsigned short w, void *portp) +{ + *(volatile unsigned short *)portp = (w >> 8); + *(volatile unsigned short *)(portp+2) = (w & 0xff); +} + +unsigned char _inb(unsigned long port) +{ + if (port >= 0x300 && port < 0x320) + return _ne_inb(PORT2ADDR_NE(port)); + + return *(volatile unsigned char *)PORT2ADDR(port); +} + +unsigned short _inw(unsigned long port) +{ + if (port >= 0x300 && port < 0x320) + return _ne_inw(PORT2ADDR_NE(port)); + + return *(volatile unsigned short *)PORT2ADDR(port); +} + +unsigned long _inl(unsigned long port) +{ + return *(volatile unsigned long *)PORT2ADDR(port); +} + +unsigned char _inb_p(unsigned long port) +{ + unsigned char v; + + if (port >= 0x300 && port < 0x320) + v = _ne_inb(PORT2ADDR_NE(port)); + else + v = *(volatile unsigned char *)PORT2ADDR(port); + + delay(); + return (v); +} + +unsigned short _inw_p(unsigned long port) +{ + unsigned short v; + + if (port >= 0x300 && port < 0x320) + v = _ne_inw(PORT2ADDR_NE(port)); + else + v = *(volatile unsigned short *)PORT2ADDR(port); + + delay(); + return (v); +} + +unsigned long _inl_p(unsigned long port) +{ + unsigned long v; + + v = *(volatile unsigned long *)PORT2ADDR(port); + delay(); + return (v); +} + +void _outb(unsigned char b, unsigned long port) +{ + if (port >= 0x300 && port < 0x320) + _ne_outb(b, PORT2ADDR_NE(port)); + else + *(volatile unsigned char *)PORT2ADDR(port) = b; +} + +void _outw(unsigned short w, unsigned long port) +{ + if (port >= 0x300 && port < 0x320) + _ne_outw(w, PORT2ADDR_NE(port)); + else + *(volatile unsigned short *)PORT2ADDR(port) = w; +} + +void _outl(unsigned long l, unsigned long port) +{ + *(volatile unsigned long *)PORT2ADDR(port) = l; +} + +void _outb_p(unsigned char b, unsigned long port) +{ + if (port >= 0x300 && port < 0x320) + _ne_outb(b, PORT2ADDR_NE(port)); + else + *(volatile unsigned char *)PORT2ADDR(port) = b; + + delay(); +} + +void _outw_p(unsigned short w, unsigned long port) +{ + if (port >= 0x300 && port < 0x320) + _ne_outw(w, PORT2ADDR_NE(port)); + else + *(volatile unsigned short *)PORT2ADDR(port) = w; + + delay(); +} + +void _outl_p(unsigned long l, unsigned long port) +{ + *(volatile unsigned long *)PORT2ADDR(port) = l; + delay(); +} + +void _insb(unsigned int port, void * addr, unsigned long count) +{ + if (port >= 0x300 && port < 0x320) + _ne_insb(PORT2ADDR_NE(port), addr, count); + else { + unsigned char *buf = addr; + unsigned char *portp = PORT2ADDR(port); + while(count--) *buf++ = *(volatile unsigned char *)portp; + } +} + +void _insw(unsigned int port, void * addr, unsigned long count) +{ + unsigned short *buf = addr; + unsigned short *portp; + + if (port >= 0x300 && port < 0x320) { + portp = PORT2ADDR_NE(port); + while (count--) *buf++ = _ne_inw(portp); + } else { + portp = PORT2ADDR(port); + while (count--) *buf++ = *(volatile unsigned short *)portp; + } +} + +void _insl(unsigned int port, void * addr, unsigned long count) +{ + unsigned long *buf = addr; + unsigned long *portp; + + portp = PORT2ADDR(port); + while (count--) *buf++ = *(volatile unsigned long *)portp; +} + +void _outsb(unsigned int port, const void * addr, unsigned long count) +{ + const unsigned char *buf = addr; + unsigned char *portp; + + if (port >= 0x300 && port < 0x320) { + portp = PORT2ADDR_NE(port); + while (count--) _ne_outb(*buf++, portp); + } else { + portp = PORT2ADDR(port); + while(count--) *(volatile unsigned char *)portp = *buf++; + } +} + +void _outsw(unsigned int port, const void * addr, unsigned long count) +{ + const unsigned short *buf = addr; + unsigned short *portp; + + if (port >= 0x300 && port < 0x320) { + portp = PORT2ADDR_NE(port); + while (count--) _ne_outw(*buf++, portp); + } else { + portp = PORT2ADDR(port); + while(count--) *(volatile unsigned short *)portp = *buf++; + } +} + +void _outsl(unsigned int port, const void * addr, unsigned long count) +{ + const unsigned long *buf = addr; + unsigned char *portp; + + portp = PORT2ADDR(port); + while(count--) *(volatile unsigned long *)portp = *buf++; +} diff --git a/arch/m32r/kernel/io_opsput.c b/arch/m32r/kernel/io_opsput.c new file mode 100644 index 000000000..642376e3b --- /dev/null +++ b/arch/m32r/kernel/io_opsput.c @@ -0,0 +1,377 @@ +/* + * linux/arch/m32r/kernel/io_mappi.c + * + * Typical I/O routines for OPSPUT board. + * + * Copyright (c) 2001, 2002 Hiroyuki Kondo, Hirokazu Takata, + * Hitoshi Yamamoto, Takeo Takahashi + * + * 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 +#include +#include +#include +#include + +#if defined(CONFIG_PCMCIA) && defined(CONFIG_M32R_CFC) +#include + +#define M32R_PCC_IOMAP_SIZE 0x1000 + +#define M32R_PCC_IOSTART0 0x1000 +#define M32R_PCC_IOEND0 (M32R_PCC_IOSTART0 + M32R_PCC_IOMAP_SIZE - 1) + +extern void pcc_ioread_byte(int, unsigned long, void *, size_t, size_t, int); +extern void pcc_ioread_word(int, unsigned long, void *, size_t, size_t, int); +extern void pcc_iowrite_byte(int, unsigned long, void *, size_t, size_t, int); +extern void pcc_iowrite_word(int, unsigned long, void *, size_t, size_t, int); +#endif /* CONFIG_PCMCIA && CONFIG_M32R_CFC */ + +#define PORT2ADDR(port) _port2addr(port) +#define PORT2ADDR_USB(port) _port2addr_usb(port) + +static __inline__ void *_port2addr(unsigned long port) +{ + return (void *)(port + NONCACHE_OFFSET); +} + +/* + * OPSPUT-LAN is located in the extended bus space + * from 0x10000000 to 0x13ffffff on physical address. + * The base address of LAN controller(LAN91C111) is 0x300. + */ +#define LAN_IOSTART 0x300 +#define LAN_IOEND 0x320 +static __inline__ void *_port2addr_ne(unsigned long port) +{ + return (void *)(port + NONCACHE_OFFSET + 0x10000000); +} +static __inline__ void *_port2addr_usb(unsigned long port) +{ + return (void *)((port & 0x0f) + NONCACHE_OFFSET + 0x10303000); +} + +static __inline__ void delay(void) +{ + __asm__ __volatile__ ("push r0; \n\t pop r0;" : : :"memory"); +} + +/* + * NIC I/O function + */ + +#define PORT2ADDR_NE(port) _port2addr_ne(port) + +static __inline__ unsigned char _ne_inb(void *portp) +{ + return *(volatile unsigned char *)portp; +} + +static __inline__ unsigned short _ne_inw(void *portp) +{ + return (unsigned short)le16_to_cpu(*(volatile unsigned short *)portp); +} + +static __inline__ void _ne_insb(void *portp, void * addr, unsigned long count) +{ + unsigned char *buf = (unsigned char *)addr; + + while (count--) *buf++ = _ne_inb(portp); +} + +static __inline__ void _ne_outb(unsigned char b, void *portp) +{ + *(volatile unsigned char *)portp = b; +} + +static __inline__ void _ne_outw(unsigned short w, void *portp) +{ + *(volatile unsigned short *)portp = cpu_to_le16(w); +} + +unsigned char _inb(unsigned long port) +{ + if (port >= LAN_IOSTART && port < LAN_IOEND) + return _ne_inb(PORT2ADDR_NE(port)); + +#if defined(CONFIG_PCMCIA) && defined(CONFIG_M32R_CFC) + else if (port >= M32R_PCC_IOSTART0 && port <= M32R_PCC_IOEND0) { + unsigned char b; + pcc_ioread_byte(0, port, &b, sizeof(b), 1, 0); + return b; + } else +#endif + + return *(volatile unsigned char *)PORT2ADDR(port); +} + +unsigned short _inw(unsigned long port) +{ + if (port >= LAN_IOSTART && port < LAN_IOEND) + return _ne_inw(PORT2ADDR_NE(port)); +#if defined(CONFIG_USB) + else if(port >= 0x340 && port < 0x3a0) + return *(volatile unsigned short *)PORT2ADDR_USB(port); +#endif + +#if defined(CONFIG_PCMCIA) && defined(CONFIG_M32R_CFC) + else if (port >= M32R_PCC_IOSTART0 && port <= M32R_PCC_IOEND0) { + unsigned short w; + pcc_ioread_word(0, port, &w, sizeof(w), 1, 0); + return w; + } else +#endif + return *(volatile unsigned short *)PORT2ADDR(port); +} + +unsigned long _inl(unsigned long port) +{ +#if defined(CONFIG_PCMCIA) && defined(CONFIG_M32R_CFC) + if (port >= M32R_PCC_IOSTART0 && port <= M32R_PCC_IOEND0) { + unsigned long l; + pcc_ioread_word(0, port, &l, sizeof(l), 1, 0); + return l; + } else +#endif + return *(volatile unsigned long *)PORT2ADDR(port); +} + +unsigned char _inb_p(unsigned long port) +{ + unsigned char v; + + if (port >= LAN_IOSTART && port < LAN_IOEND) + v = _ne_inb(PORT2ADDR_NE(port)); + else +#if defined(CONFIG_PCMCIA) && defined(CONFIG_M32R_CFC) + if (port >= M32R_PCC_IOSTART0 && port <= M32R_PCC_IOEND0) { + unsigned char b; + pcc_ioread_byte(0, port, &b, sizeof(b), 1, 0); + return b; + } else +#endif + v = *(volatile unsigned char *)PORT2ADDR(port); + + delay(); + return (v); +} + +unsigned short _inw_p(unsigned long port) +{ + unsigned short v; + + if (port >= LAN_IOSTART && port < LAN_IOEND) + v = _ne_inw(PORT2ADDR_NE(port)); + else +#if defined(CONFIG_USB) + if(port >= 0x340 && port < 0x3a0) + return *(volatile unsigned short *)PORT2ADDR_USB(port); + else +#endif + +#if defined(CONFIG_PCMCIA) && defined(CONFIG_M32R_CFC) + if (port >= M32R_PCC_IOSTART0 && port <= M32R_PCC_IOEND0) { + unsigned short w; + pcc_ioread_word(0, port, &w, sizeof(w), 1, 0); + return w; + } else +#endif + v = *(volatile unsigned short *)PORT2ADDR(port); + + delay(); + return (v); +} + +unsigned long _inl_p(unsigned long port) +{ + unsigned long v; + + v = *(volatile unsigned long *)PORT2ADDR(port); + delay(); + return (v); +} + +void _outb(unsigned char b, unsigned long port) +{ + if (port >= LAN_IOSTART && port < LAN_IOEND) + _ne_outb(b, PORT2ADDR_NE(port)); + else +#if defined(CONFIG_PCMCIA) && defined(CONFIG_M32R_CFC) + if (port >= M32R_PCC_IOSTART0 && port <= M32R_PCC_IOEND0) { + pcc_iowrite_byte(0, port, &b, sizeof(b), 1, 0); + } else +#endif + *(volatile unsigned char *)PORT2ADDR(port) = b; +} + +void _outw(unsigned short w, unsigned long port) +{ + if (port >= LAN_IOSTART && port < LAN_IOEND) + _ne_outw(w, PORT2ADDR_NE(port)); + else +#if defined(CONFIG_USB) + if(port >= 0x340 && port < 0x3a0) + *(volatile unsigned short *)PORT2ADDR_USB(port) = w; + else +#endif + +#if defined(CONFIG_PCMCIA) && defined(CONFIG_M32R_CFC) + if (port >= M32R_PCC_IOSTART0 && port <= M32R_PCC_IOEND0) { + pcc_iowrite_word(0, port, &w, sizeof(w), 1, 0); + } else +#endif + *(volatile unsigned short *)PORT2ADDR(port) = w; +} + +void _outl(unsigned long l, unsigned long port) +{ +#if defined(CONFIG_PCMCIA) && defined(CONFIG_M32R_CFC) + if (port >= M32R_PCC_IOSTART0 && port <= M32R_PCC_IOEND0) { + pcc_iowrite_word(0, port, &l, sizeof(l), 1, 0); + } else +#endif + *(volatile unsigned long *)PORT2ADDR(port) = l; +} + +void _outb_p(unsigned char b, unsigned long port) +{ + if (port >= LAN_IOSTART && port < LAN_IOEND) + _ne_outb(b, PORT2ADDR_NE(port)); + else +#if defined(CONFIG_PCMCIA) && defined(CONFIG_M32R_CFC) + if (port >= M32R_PCC_IOSTART0 && port <= M32R_PCC_IOEND0) { + pcc_iowrite_byte(0, port, &b, sizeof(b), 1, 0); + } else +#endif + *(volatile unsigned char *)PORT2ADDR(port) = b; + + delay(); +} + +void _outw_p(unsigned short w, unsigned long port) +{ + if (port >= LAN_IOSTART && port < LAN_IOEND) + _ne_outw(w, PORT2ADDR_NE(port)); + else +#if defined(CONFIG_USB) + if(port >= 0x340 && port < 0x3a0) + *(volatile unsigned short *)PORT2ADDR_USB(port) = w; + else +#endif + +#if defined(CONFIG_PCMCIA) && defined(CONFIG_M32R_CFC) + if (port >= M32R_PCC_IOSTART0 && port <= M32R_PCC_IOEND0) { + pcc_iowrite_word(0, port, &w, sizeof(w), 1, 0); + } else +#endif + *(volatile unsigned short *)PORT2ADDR(port) = w; + + delay(); +} + +void _outl_p(unsigned long l, unsigned long port) +{ + *(volatile unsigned long *)PORT2ADDR(port) = l; + delay(); +} + +void _insb(unsigned int port, void * addr, unsigned long count) +{ + if (port >= LAN_IOSTART && port < LAN_IOEND) + _ne_insb(PORT2ADDR_NE(port), addr, count); +#if defined(CONFIG_PCMCIA) && defined(CONFIG_M32R_CFC) + else if (port >= M32R_PCC_IOSTART0 && port <= M32R_PCC_IOEND0) { + pcc_ioread_byte(0, port, (void *)addr, sizeof(unsigned char), count, 1); + } +#endif + else { + unsigned char *buf = addr; + unsigned char *portp = PORT2ADDR(port); + while(count--) *buf++ = *(volatile unsigned char *)portp; + } +} + +void _insw(unsigned int port, void * addr, unsigned long count) +{ + unsigned short *buf = addr; + unsigned short *portp; + + if (port >= LAN_IOSTART && port < LAN_IOEND) { + /* + * This portion is only used by smc91111.c to read data + * from the DATA_REG. Do not swap the data. + */ + portp = PORT2ADDR_NE(port); + while (count--) *buf++ = *(volatile unsigned short *)portp; +#if defined(CONFIG_PCMCIA) && defined(CONFIG_M32R_CFC) + } else if (port >= M32R_PCC_IOSTART0 && port <= M32R_PCC_IOEND0) { + pcc_ioread_word(9, port, (void *)addr, sizeof(unsigned short), count, 1); +#endif + } else { + portp = PORT2ADDR(port); + while (count--) *buf++ = *(volatile unsigned short *)portp; + } +} + +void _insl(unsigned int port, void * addr, unsigned long count) +{ + unsigned long *buf = addr; + unsigned long *portp; + + portp = PORT2ADDR(port); + while (count--) *buf++ = *(volatile unsigned long *)portp; +} + +void _outsb(unsigned int port, const void * addr, unsigned long count) +{ + const unsigned char *buf = addr; + unsigned char *portp; + + if (port >= LAN_IOSTART && port < LAN_IOEND) { + portp = PORT2ADDR_NE(port); + while (count--) _ne_outb(*buf++, portp); +#if defined(CONFIG_PCMCIA) && defined(CONFIG_M32R_CFC) + } else if (port >= M32R_PCC_IOSTART0 && port <= M32R_PCC_IOEND0) { + pcc_iowrite_byte(0, port, (void *)addr, sizeof(unsigned char), count, 1); +#endif + } else { + portp = PORT2ADDR(port); + while(count--) *(volatile unsigned char *)portp = *buf++; + } +} + +void _outsw(unsigned int port, const void * addr, unsigned long count) +{ + const unsigned short *buf = addr; + unsigned short *portp; + + if (port >= LAN_IOSTART && port < LAN_IOEND) { + /* + * This portion is only used by smc91111.c to write data + * into the DATA_REG. Do not swap the data. + */ + portp = PORT2ADDR_NE(port); + while(count--) *(volatile unsigned short *)portp = *buf++; +#if defined(CONFIG_PCMCIA) && defined(CONFIG_M32R_CFC) + } else if (port >= M32R_PCC_IOSTART0 && port <= M32R_PCC_IOEND0) { + pcc_iowrite_word(9, port, (void *)addr, sizeof(unsigned short), count, 1); +#endif + } else { + portp = PORT2ADDR(port); + while(count--) *(volatile unsigned short *)portp = *buf++; + } +} + +void _outsl(unsigned int port, const void * addr, unsigned long count) +{ + const unsigned long *buf = addr; + unsigned char *portp; + + portp = PORT2ADDR(port); + while(count--) *(volatile unsigned long *)portp = *buf++; +} diff --git a/arch/m32r/kernel/io_usrv.c b/arch/m32r/kernel/io_usrv.c new file mode 100644 index 000000000..cc77ced9f --- /dev/null +++ b/arch/m32r/kernel/io_usrv.c @@ -0,0 +1,247 @@ +/* + * linux/arch/m32r/kernel/io_usrv.c + * + * Typical I/O routines for uServer board. + * + * Copyright (c) 2001 - 2003 Hiroyuki Kondo, Hirokazu Takata, + * Hitoshi Yamamoto, Takeo Takahashi + * + * 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 +#include +#include +#include + +#include +#include "../drivers/m32r_cfc.h" + +extern void pcc_ioread_byte(int, unsigned long, void *, size_t, size_t, int); +extern void pcc_ioread_word(int, unsigned long, void *, size_t, size_t, int); +extern void pcc_iowrite_byte(int, unsigned long, void *, size_t, size_t, int); +extern void pcc_iowrite_word(int, unsigned long, void *, size_t, size_t, int); +#define CFC_IOSTART CFC_IOPORT_BASE +#define CFC_IOEND (CFC_IOSTART + (M32R_PCC_MAPSIZE * M32R_MAX_PCC) - 1) + +#if defined(CONFIG_SERIAL_8250) || defined(CONFIG_SERIAL_8250_MODULE) +#define UART0_REGSTART 0x04c20000 +#define UART1_REGSTART 0x04c20100 +#define UART_IOMAP_SIZE 8 +#define UART0_IOSTART 0x3f8 +#define UART0_IOEND (UART0_IOSTART + UART_IOMAP_SIZE - 1) +#define UART1_IOSTART 0x2f8 +#define UART1_IOEND (UART1_IOSTART + UART_IOMAP_SIZE - 1) +#endif /* CONFIG_SERIAL_8250 || CONFIG_SERIAL_8250_MODULE */ + +#define PORT2ADDR(port) _port2addr(port) + +static __inline__ void *_port2addr(unsigned long port) +{ +#if defined(CONFIG_SERIAL_8250) || defined(CONFIG_SERIAL_8250_MODULE) + if (port >= UART0_IOSTART && port <= UART0_IOEND) + port = ((port - UART0_IOSTART) << 1) + UART0_REGSTART; + else if (port >= UART1_IOSTART && port <= UART1_IOEND) + port = ((port - UART1_IOSTART) << 1) + UART1_REGSTART; +#endif /* CONFIG_SERIAL_8250 || CONFIG_SERIAL_8250_MODULE */ + return (void *)(port + NONCACHE_OFFSET); +} + +static __inline__ void delay(void) +{ + __asm__ __volatile__ ("push r0; \n\t pop r0;" : : :"memory"); +} + +unsigned char _inb(unsigned long port) +{ + if (port >= CFC_IOSTART && port <= CFC_IOEND) { + unsigned char b; + pcc_ioread_byte(0, port, &b, sizeof(b), 1, 0); + return b; + } else + return *(volatile unsigned char *)PORT2ADDR(port); +} + +unsigned short _inw(unsigned long port) +{ + if (port >= CFC_IOSTART && port <= CFC_IOEND) { + unsigned short w; + pcc_ioread_word(0, port, &w, sizeof(w), 1, 0); + return w; + } else + return *(volatile unsigned short *)PORT2ADDR(port); +} + +unsigned long _inl(unsigned long port) +{ + if (port >= CFC_IOSTART && port <= CFC_IOEND) { + unsigned long l; + pcc_ioread_word(0, port, &l, sizeof(l), 1, 0); + return l; + } else + return *(volatile unsigned long *)PORT2ADDR(port); +} + +unsigned char _inb_p(unsigned long port) +{ + unsigned char b; + + if (port >= CFC_IOSTART && port <= CFC_IOEND) { + pcc_ioread_byte(0, port, &b, sizeof(b), 1, 0); + return b; + } else { + b = *(volatile unsigned char *)PORT2ADDR(port); + delay(); + return b; + } +} + +unsigned short _inw_p(unsigned long port) +{ + unsigned short w; + + if (port >= CFC_IOSTART && port <= CFC_IOEND) { + pcc_ioread_word(0, port, &w, sizeof(w), 1, 0); + return w; + } else { + w = *(volatile unsigned short *)PORT2ADDR(port); + delay(); + return w; + } +} + +unsigned long _inl_p(unsigned long port) +{ + unsigned long v; + + v = *(volatile unsigned long *)PORT2ADDR(port); + delay(); + + return v; +} + +void _outb(unsigned char b, unsigned long port) +{ + if (port >= CFC_IOSTART && port <= CFC_IOEND) + pcc_iowrite_byte(0, port, &b, sizeof(b), 1, 0); + else + *(volatile unsigned char *)PORT2ADDR(port) = b; +} + +void _outw(unsigned short w, unsigned long port) +{ + if (port >= CFC_IOSTART && port <= CFC_IOEND) + pcc_iowrite_word(0, port, &w, sizeof(w), 1, 0); + else + *(volatile unsigned short *)PORT2ADDR(port) = w; +} + +void _outl(unsigned long l, unsigned long port) +{ + if (port >= CFC_IOSTART && port <= CFC_IOEND) + pcc_iowrite_word(0, port, &l, sizeof(l), 1, 0); + else + *(volatile unsigned long *)PORT2ADDR(port) = l; +} + +void _outb_p(unsigned char b, unsigned long port) +{ + if (port >= CFC_IOSTART && port <= CFC_IOEND) + pcc_iowrite_byte(0, port, &b, sizeof(b), 1, 0); + else + *(volatile unsigned char *)PORT2ADDR(port) = b; + delay(); +} + +void _outw_p(unsigned short w, unsigned long port) +{ + if (port >= CFC_IOSTART && port <= CFC_IOEND) + pcc_iowrite_word(0, port, &w, sizeof(w), 1, 0); + else + *(volatile unsigned short *)PORT2ADDR(port) = w; + delay(); +} + +void _outl_p(unsigned long l, unsigned long port) +{ + *(volatile unsigned long *)PORT2ADDR(port) = l; + delay(); +} + +void _insb(unsigned int port, void * addr, unsigned long count) +{ + if (port >= CFC_IOSTART && port <= CFC_IOEND) + pcc_ioread_byte(0, port, addr, sizeof(unsigned char), count, 1); + else { + unsigned char *buf = addr; + unsigned char *portp = PORT2ADDR(port); + while(count--) *buf++ = *(volatile unsigned char *)portp; + } +} + +void _insw(unsigned int port, void * addr, unsigned long count) +{ + unsigned short *buf = addr; + unsigned short *portp; + + if (port >= CFC_IOSTART && port <= CFC_IOEND) + pcc_ioread_word(0, port, addr, sizeof(unsigned short), count, + 1); + else { + portp = PORT2ADDR(port); + while (count--) *buf++ = *(volatile unsigned short *)portp; + } +} + +void _insl(unsigned int port, void * addr, unsigned long count) +{ + unsigned long *buf = addr; + unsigned long *portp; + + portp = PORT2ADDR(port); + while (count--) + *buf++ = *(volatile unsigned long *)portp; +} + +void _outsb(unsigned int port, const void * addr, unsigned long count) +{ + const unsigned char *buf = addr; + unsigned char *portp; + + if (port >= CFC_IOSTART && port <= CFC_IOEND) + pcc_iowrite_byte(0, port, (void *)addr, sizeof(unsigned char), + count, 1); + else { + portp = PORT2ADDR(port); + while (count--) + *(volatile unsigned char *)portp = *buf++; + } +} + +void _outsw(unsigned int port, const void * addr, unsigned long count) +{ + const unsigned short *buf = addr; + unsigned short *portp; + + if (port >= CFC_IOSTART && port <= CFC_IOEND) + pcc_iowrite_word(0, port, (void *)addr, sizeof(unsigned short), + count, 1); + else { + portp = PORT2ADDR(port); + while (count--) + *(volatile unsigned short *)portp = *buf++; + } +} + +void _outsl(unsigned int port, const void * addr, unsigned long count) +{ + const unsigned long *buf = addr; + unsigned char *portp; + + portp = PORT2ADDR(port); + while (count--) + *(volatile unsigned long *)portp = *buf++; +} diff --git a/arch/m32r/kernel/irq.c b/arch/m32r/kernel/irq.c new file mode 100644 index 000000000..ba1357733 --- /dev/null +++ b/arch/m32r/kernel/irq.c @@ -0,0 +1,1020 @@ +/* + * linux/arch/m32r/kernel/irq.c + * + * Copyright (c) 2003, 2004 Hitoshi Yamamoto + * + * Taken from i386 2.6.4 version. + */ + +/* + * linux/arch/i386/kernel/irq.c + * + * Copyright (C) 1992, 1998 Linus Torvalds, Ingo Molnar + * + * This file contains the code used by various IRQ handling routines: + * asking for different IRQ's should be done through these routines + * instead of just grabbing them. Thus setups with different IRQ numbers + * shouldn't result in any weird surprises, and installing new handlers + * should be easier. + */ + +/* + * (mostly architecture independent, will move to kernel/irq.c in 2.5.) + * + * IRQs are in fact implemented a bit like signal handlers for the kernel. + * Naturally it's not a 1:1 relation, but there are similarities. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * Linux has a controller-independent x86 interrupt architecture. + * every controller has a 'controller-template', that is used + * by the main code to do the right thing. Each driver-visible + * interrupt source is transparently wired to the apropriate + * controller. Thus drivers need not be aware of the + * interrupt-controller. + * + * Various interrupt controllers we handle: 8259 PIC, SMP IO-APIC, + * PIIX4's internal 8259 PIC and SGI's Visual Workstation Cobalt (IO-)APIC. + * (IO-APICs assumed to be messaging to Pentium local-APICs) + * + * the code is designed to be easily extended with new/different + * interrupt controllers, without having to do assembly magic. + */ + +/* + * Controller mappings for all interrupt sources: + */ +irq_desc_t irq_desc[NR_IRQS] __cacheline_aligned = { + [0 ... NR_IRQS-1] = { + .handler = &no_irq_type, + .lock = SPIN_LOCK_UNLOCKED + } +}; + +static void register_irq_proc (unsigned int irq); + +/* + * Special irq handlers. + */ + +irqreturn_t no_action(int cpl, void *dev_id, struct pt_regs *regs) +{ return IRQ_NONE; } + +/* + * Generic no controller code + */ + +static void enable_none(unsigned int irq) { } +static unsigned int startup_none(unsigned int irq) { return 0; } +static void disable_none(unsigned int irq) { } +static void ack_none(unsigned int irq) +{ +/* + * 'what should we do if we get a hw irq event on an illegal vector'. + * each architecture has to answer this themselves, it doesn't deserve + * a generic callback i think. + */ + printk("unexpected IRQ trap at vector %02x\n", irq); +} + +/* startup is the same as "enable", shutdown is same as "disable" */ +#define shutdown_none disable_none +#define end_none enable_none + +struct hw_interrupt_type no_irq_type = { + "none", + startup_none, + shutdown_none, + enable_none, + disable_none, + ack_none, + end_none +}; + +atomic_t irq_err_count; +atomic_t irq_mis_count; + +/* + * Generic, controller-independent functions: + */ + +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 (j=0; jtypename); + 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); + } else if (i == NR_IRQS) { + seq_printf(p, "ERR: %10u\n", atomic_read(&irq_err_count)); + seq_printf(p, "MIS: %10u\n", atomic_read(&irq_mis_count)); + } + return 0; +} + +#ifdef CONFIG_SMP +inline void synchronize_irq(unsigned int irq) +{ + while (irq_desc[irq].status & IRQ_INPROGRESS) + cpu_relax(); +} +#endif + +/* + * This should really return information about whether + * we should do bottom half handling etc. Right now we + * end up _always_ checking the bottom half, which is a + * waste of time and is not what some drivers would + * prefer. + */ +int handle_IRQ_event(unsigned int irq, + struct pt_regs *regs, struct irqaction *action) +{ + int status = 1; /* Force the "do bottom halves" bit */ + int ret, retval = 0; + + if (!(action->flags & SA_INTERRUPT)) + local_irq_enable(); + + do { + ret = action->handler(irq, action->dev_id, regs); + if (ret == IRQ_HANDLED) + status |= action->flags; + action = action->next; + retval |= ret; + } while (action); + if (status & SA_SAMPLE_RANDOM) + add_interrupt_randomness(irq); + local_irq_disable(); + return retval; +} + +static void __report_bad_irq(int irq, irq_desc_t *desc, irqreturn_t action_ret) +{ + struct irqaction *action; + + if (action_ret != IRQ_HANDLED && action_ret != IRQ_NONE) { + printk(KERN_ERR "irq event %d: bogus return value %x\n", + irq, action_ret); + } else { + printk(KERN_ERR "irq %d: nobody cared!\n", irq); + } + dump_stack(); + printk(KERN_ERR "handlers:\n"); + action = desc->action; + do { + printk(KERN_ERR "[<%p>]", action->handler); + print_symbol(" (%s)", + (unsigned long)action->handler); + printk("\n"); + action = action->next; + } while (action); +} + +static void report_bad_irq(int irq, irq_desc_t *desc, irqreturn_t action_ret) +{ + static int count = 100; + + if (count) { + count--; + __report_bad_irq(irq, desc, action_ret); + } +} + +static int noirqdebug; + +static int __init noirqdebug_setup(char *str) +{ + noirqdebug = 1; + printk("IRQ lockup detection disabled\n"); + return 1; +} + +__setup("noirqdebug", noirqdebug_setup); + +/* + * If 99,900 of the previous 100,000 interrupts have not been handled then + * assume that the IRQ is stuck in some manner. Drop a diagnostic and try to + * turn the IRQ off. + * + * (The other 100-of-100,000 interrupts may have been a correctly-functioning + * device sharing an IRQ with the failing one) + * + * Called under desc->lock + */ +static void note_interrupt(int irq, irq_desc_t *desc, irqreturn_t action_ret) +{ + if (action_ret != IRQ_HANDLED) { + desc->irqs_unhandled++; + if (action_ret != IRQ_NONE) + report_bad_irq(irq, desc, action_ret); + } + + desc->irq_count++; + if (desc->irq_count < 100000) + return; + + desc->irq_count = 0; + if (desc->irqs_unhandled > 99900) { + /* + * The interrupt is stuck + */ + __report_bad_irq(irq, desc, action_ret); + /* + * Now kill the IRQ + */ + printk(KERN_EMERG "Disabling IRQ #%d\n", irq); + desc->status |= IRQ_DISABLED; + desc->handler->disable(irq); + } + desc->irqs_unhandled = 0; +} + +/* + * Generic enable/disable code: this just calls + * down into the PIC-specific version for the actual + * hardware disable after having gotten the irq + * controller lock. + */ + +/** + * disable_irq_nosync - disable an irq without waiting + * @irq: Interrupt to disable + * + * Disable the selected interrupt line. Disables and Enables are + * nested. + * Unlike disable_irq(), this function does not ensure existing + * instances of the IRQ handler have completed before returning. + * + * This function may be called from IRQ context. + */ + +inline void disable_irq_nosync(unsigned int irq) +{ + irq_desc_t *desc = irq_desc + irq; + unsigned long flags; + + spin_lock_irqsave(&desc->lock, flags); + if (!desc->depth++) { + desc->status |= IRQ_DISABLED; + desc->handler->disable(irq); + } + spin_unlock_irqrestore(&desc->lock, flags); +} + +/** + * disable_irq - disable an irq and wait for completion + * @irq: Interrupt to disable + * + * Disable the selected interrupt line. Enables and Disables are + * nested. + * This function waits for any pending IRQ handlers for this interrupt + * to complete before returning. If you use this function while + * holding a resource the IRQ handler may need you will deadlock. + * + * This function may be called - with care - from IRQ context. + */ + +void disable_irq(unsigned int irq) +{ + irq_desc_t *desc = irq_desc + irq; + disable_irq_nosync(irq); + if (desc->action) + synchronize_irq(irq); +} + +/** + * enable_irq - enable handling of an irq + * @irq: Interrupt to enable + * + * Undoes the effect of one call to disable_irq(). If this + * matches the last disable, processing of interrupts on this + * IRQ line is re-enabled. + * + * This function may be called from IRQ context. + */ + +void enable_irq(unsigned int irq) +{ + irq_desc_t *desc = irq_desc + irq; + unsigned long flags; + + spin_lock_irqsave(&desc->lock, flags); + switch (desc->depth) { + case 1: { + unsigned int status = desc->status & ~IRQ_DISABLED; + desc->status = status; + if ((status & (IRQ_PENDING | IRQ_REPLAY)) == IRQ_PENDING) { + desc->status = status | IRQ_REPLAY; + hw_resend_irq(desc->handler,irq); + } + desc->handler->enable(irq); + /* fall-through */ + } + default: + desc->depth--; + break; + case 0: + printk("enable_irq(%u) unbalanced from %p\n", irq, + __builtin_return_address(0)); + } + spin_unlock_irqrestore(&desc->lock, flags); +} + +/* + * do_IRQ handles all normal device IRQ's (the special + * SMP cross-CPU interrupts have their own specific + * handlers). + */ +asmlinkage unsigned int do_IRQ(int irq, struct pt_regs *regs) +{ + /* + * We ack quickly, we don't want the irq controller + * thinking we're snobs just because some other CPU has + * disabled global interrupts (we have already done the + * INT_ACK cycles, it's too late to try to pretend to the + * controller that we aren't taking the interrupt). + * + * 0 return value means that this irq is already being + * handled by some other CPU. (or is disabled) + */ + irq_desc_t *desc = irq_desc + irq; + struct irqaction * action; + unsigned int status; + + irq_enter(); + +#ifdef CONFIG_DEBUG_STACKOVERFLOW + /* FIXME M32R */ +#endif + kstat_this_cpu.irqs[irq]++; + spin_lock(&desc->lock); + desc->handler->ack(irq); + /* + REPLAY is when Linux resends an IRQ that was dropped earlier + WAITING is used by probe to mark irqs that are being tested + */ + status = desc->status & ~(IRQ_REPLAY | IRQ_WAITING); + status |= IRQ_PENDING; /* we _want_ to handle it */ + + /* + * If the IRQ is disabled for whatever reason, we cannot + * use the action we have. + */ + action = NULL; + if (likely(!(status & (IRQ_DISABLED | IRQ_INPROGRESS)))) { + action = desc->action; + status &= ~IRQ_PENDING; /* we commit to handling */ + status |= IRQ_INPROGRESS; /* we are handling it */ + } + desc->status = status; + + /* + * If there is no IRQ handler or it was disabled, exit early. + Since we set PENDING, if another processor is handling + a different instance of this same irq, the other processor + will take care of it. + */ + if (unlikely(!action)) + goto out; + + /* + * Edge triggered interrupts need to remember + * pending events. + * This applies to any hw interrupts that allow a second + * instance of the same irq to arrive while we are in do_IRQ + * or in the handler. But the code here only handles the _second_ + * instance of the irq, not the third or fourth. So it is mostly + * useful for irq hardware that does not mask cleanly in an + * SMP environment. + */ + for (;;) { + irqreturn_t action_ret; + + spin_unlock(&desc->lock); + action_ret = handle_IRQ_event(irq, regs, action); + spin_lock(&desc->lock); + if (!noirqdebug) + note_interrupt(irq, desc, action_ret); + if (likely(!(desc->status & IRQ_PENDING))) + break; + desc->status &= ~IRQ_PENDING; + } + desc->status &= ~IRQ_INPROGRESS; + +out: + /* + * The ->end() handler has to deal with interrupts which got + * disabled while the handler was running. + */ + desc->handler->end(irq); + spin_unlock(&desc->lock); + + irq_exit(); + +#if defined(CONFIG_SMP) + if (irq == M32R_IRQ_MFT2) + smp_send_timer(); +#endif /* CONFIG_SMP */ + + return 1; +} + +int can_request_irq(unsigned int irq, unsigned long irqflags) +{ + struct irqaction *action; + + if (irq >= NR_IRQS) + return 0; + action = irq_desc[irq].action; + if (action) { + if (irqflags & action->flags & SA_SHIRQ) + action = NULL; + } + return !action; +} + +/** + * request_irq - allocate an interrupt line + * @irq: Interrupt line to allocate + * @handler: Function to be called when the IRQ occurs + * @irqflags: Interrupt type flags + * @devname: An ascii name for the claiming device + * @dev_id: A cookie passed back to the handler function + * + * This call allocates interrupt resources and enables the + * interrupt line and IRQ handling. From the point this + * call is made your handler function may be invoked. Since + * your handler function must clear any interrupt the board + * raises, you must take care both to initialise your hardware + * and to set up the interrupt handler in the right order. + * + * Dev_id must be globally unique. Normally the address of the + * device data structure is used as the cookie. Since the handler + * receives this value it makes sense to use it. + * + * If your interrupt is shared you must pass a non NULL dev_id + * as this is required when freeing the interrupt. + * + * Flags: + * + * SA_SHIRQ Interrupt is shared + * + * SA_INTERRUPT Disable local interrupts while processing + * + * SA_SAMPLE_RANDOM The interrupt can be used for entropy + * + */ + +int request_irq(unsigned int irq, + irqreturn_t (*handler)(int, void *, struct pt_regs *), + unsigned long irqflags, + const char * devname, + void *dev_id) +{ + int retval; + struct irqaction * action; + +#if 1 + /* + * Sanity-check: shared interrupts should REALLY pass in + * a real dev-ID, otherwise we'll have trouble later trying + * to figure out which interrupt is which (messes up the + * interrupt freeing logic etc). + */ + if (irqflags & SA_SHIRQ) { + if (!dev_id) + printk("Bad boy: %s (at 0x%x) called us without a dev_id!\n", devname, (&irq)[-1]); + } +#endif + + if (irq >= NR_IRQS) + return -EINVAL; + if (!handler) + return -EINVAL; + + action = (struct irqaction *) + kmalloc(sizeof(struct irqaction), GFP_ATOMIC); + if (!action) + return -ENOMEM; + + action->handler = handler; + action->flags = irqflags; + cpus_clear(action->mask); + action->name = devname; + action->next = NULL; + action->dev_id = dev_id; + + retval = setup_irq(irq, action); + if (retval) + kfree(action); + return retval; +} + +EXPORT_SYMBOL(request_irq); + +/** + * free_irq - free an interrupt + * @irq: Interrupt line to free + * @dev_id: Device identity to free + * + * Remove an interrupt handler. The handler is removed and if the + * interrupt line is no longer in use by any driver it is disabled. + * On a shared IRQ the caller must ensure the interrupt is disabled + * on the card it drives before calling this function. The function + * does not return until any executing interrupts for this IRQ + * have completed. + * + * This function must not be called from interrupt context. + */ + +void free_irq(unsigned int irq, void *dev_id) +{ + irq_desc_t *desc; + struct irqaction **p; + unsigned long flags; + + if (irq >= NR_IRQS) + return; + + desc = irq_desc + irq; + spin_lock_irqsave(&desc->lock,flags); + p = &desc->action; + for (;;) { + struct irqaction * action = *p; + if (action) { + struct irqaction **pp = p; + p = &action->next; + if (action->dev_id != dev_id) + continue; + + /* Found it - now remove it from the list of entries */ + *pp = action->next; + if (!desc->action) { + desc->status |= IRQ_DISABLED; + desc->handler->shutdown(irq); + } + spin_unlock_irqrestore(&desc->lock,flags); + + /* Wait to make sure it's not being used on another CPU */ + synchronize_irq(irq); + kfree(action); + return; + } + printk("Trying to free free IRQ%d\n",irq); + spin_unlock_irqrestore(&desc->lock,flags); + return; + } +} + +EXPORT_SYMBOL(free_irq); + +/* + * IRQ autodetection code.. + * + * This depends on the fact that any interrupt that + * comes in on to an unassigned handler will get stuck + * with "IRQ_WAITING" cleared and the interrupt + * disabled. + */ + +static DECLARE_MUTEX(probe_sem); + +/** + * probe_irq_on - begin an interrupt autodetect + * + * Commence probing for an interrupt. The interrupts are scanned + * and a mask of potential interrupt lines is returned. + * + */ + +unsigned long probe_irq_on(void) +{ + unsigned int i; + irq_desc_t *desc; + unsigned long val; + unsigned long delay; + + down(&probe_sem); + /* + * something may have generated an irq long ago and we want to + * flush such a longstanding irq before considering it as spurious. + */ + for (i = NR_IRQS-1; i > 0; i--) { + desc = irq_desc + i; + + spin_lock_irq(&desc->lock); + if (!irq_desc[i].action) + irq_desc[i].handler->startup(i); + spin_unlock_irq(&desc->lock); + } + + /* Wait for longstanding interrupts to trigger. */ + for (delay = jiffies + HZ/50; time_after(delay, jiffies); ) + /* about 20ms delay */ barrier(); + + /* + * enable any unassigned irqs + * (we must startup again here because if a longstanding irq + * happened in the previous stage, it may have masked itself) + */ + for (i = NR_IRQS-1; i > 0; i--) { + desc = irq_desc + i; + + spin_lock_irq(&desc->lock); + if (!desc->action) { + desc->status |= IRQ_AUTODETECT | IRQ_WAITING; + if (desc->handler->startup(i)) + desc->status |= IRQ_PENDING; + } + spin_unlock_irq(&desc->lock); + } + + /* + * Wait for spurious interrupts to trigger + */ + for (delay = jiffies + HZ/10; time_after(delay, jiffies); ) + /* about 100ms delay */ barrier(); + + /* + * Now filter out any obviously spurious interrupts + */ + val = 0; + for (i = 0; i < NR_IRQS; i++) { + irq_desc_t *desc = irq_desc + i; + unsigned int status; + + spin_lock_irq(&desc->lock); + status = desc->status; + + if (status & IRQ_AUTODETECT) { + /* It triggered already - consider it spurious. */ + if (!(status & IRQ_WAITING)) { + desc->status = status & ~IRQ_AUTODETECT; + desc->handler->shutdown(i); + } else + if (i < 32) + val |= 1 << i; + } + spin_unlock_irq(&desc->lock); + } + + return val; +} + +EXPORT_SYMBOL(probe_irq_on); + +/* + * Return a mask of triggered interrupts (this + * can handle only legacy ISA interrupts). + */ + +/** + * probe_irq_mask - scan a bitmap of interrupt lines + * @val: mask of interrupts to consider + * + * Scan the ISA bus interrupt lines and return a bitmap of + * active interrupts. The interrupt probe logic state is then + * returned to its previous value. + * + * Note: we need to scan all the irq's even though we will + * only return ISA irq numbers - just so that we reset them + * all to a known state. + */ +unsigned int probe_irq_mask(unsigned long val) +{ + int i; + unsigned int mask; + + mask = 0; + for (i = 0; i < NR_IRQS; i++) { + irq_desc_t *desc = irq_desc + i; + unsigned int status; + + spin_lock_irq(&desc->lock); + status = desc->status; + + if (status & IRQ_AUTODETECT) { + if (i < 16 && !(status & IRQ_WAITING)) + mask |= 1 << i; + + desc->status = status & ~IRQ_AUTODETECT; + desc->handler->shutdown(i); + } + spin_unlock_irq(&desc->lock); + } + up(&probe_sem); + + return mask & val; +} + +/* + * Return the one interrupt that triggered (this can + * handle any interrupt source). + */ + +/** + * probe_irq_off - end an interrupt autodetect + * @val: mask of potential interrupts (unused) + * + * Scans the unused interrupt lines and returns the line which + * appears to have triggered the interrupt. If no interrupt was + * found then zero is returned. If more than one interrupt is + * found then minus the first candidate is returned to indicate + * their is doubt. + * + * The interrupt probe logic state is returned to its previous + * value. + * + * BUGS: When used in a module (which arguably shouldnt happen) + * nothing prevents two IRQ probe callers from overlapping. The + * results of this are non-optimal. + */ + +int probe_irq_off(unsigned long val) +{ + int i, irq_found, nr_irqs; + + nr_irqs = 0; + irq_found = 0; + for (i = 0; i < NR_IRQS; i++) { + irq_desc_t *desc = irq_desc + i; + unsigned int status; + + spin_lock_irq(&desc->lock); + status = desc->status; + + if (status & IRQ_AUTODETECT) { + if (!(status & IRQ_WAITING)) { + if (!nr_irqs) + irq_found = i; + nr_irqs++; + } + desc->status = status & ~IRQ_AUTODETECT; + desc->handler->shutdown(i); + } + spin_unlock_irq(&desc->lock); + } + up(&probe_sem); + + if (nr_irqs > 1) + irq_found = -irq_found; + return irq_found; +} + +EXPORT_SYMBOL(probe_irq_off); + +/* this was setup_x86_irq but it seems pretty generic */ +int setup_irq(unsigned int irq, struct irqaction * new) +{ + int shared = 0; + unsigned long flags; + struct irqaction *old, **p; + irq_desc_t *desc = irq_desc + irq; + + if (desc->handler == &no_irq_type) + return -ENOSYS; + /* + * Some drivers like serial.c use request_irq() heavily, + * so we have to be careful not to interfere with a + * running system. + */ + if (new->flags & SA_SAMPLE_RANDOM) { + /* + * This function might sleep, we want to call it first, + * outside of the atomic block. + * Yes, this might clear the entropy pool if the wrong + * driver is attempted to be loaded, without actually + * installing a new handler, but is this really a problem, + * only the sysadmin is able to do this. + */ + rand_initialize_irq(irq); + } + + /* + * The following block of code has to be executed atomically + */ + spin_lock_irqsave(&desc->lock,flags); + p = &desc->action; + if ((old = *p) != NULL) { + /* Can't share interrupts unless both agree to */ + if (!(old->flags & new->flags & SA_SHIRQ)) { + spin_unlock_irqrestore(&desc->lock,flags); + return -EBUSY; + } + + /* add new interrupt at end of irq queue */ + do { + p = &old->next; + old = *p; + } while (old); + shared = 1; + } + + *p = new; + + if (!shared) { + desc->depth = 0; + desc->status &= ~(IRQ_DISABLED | IRQ_AUTODETECT | IRQ_WAITING | IRQ_INPROGRESS); + desc->handler->startup(irq); + } + spin_unlock_irqrestore(&desc->lock,flags); + + register_irq_proc(irq); + return 0; +} + +static struct proc_dir_entry * root_irq_dir; +static struct proc_dir_entry * irq_dir [NR_IRQS]; + +#ifdef CONFIG_SMP + +static struct proc_dir_entry *smp_affinity_entry[NR_IRQS]; + +cpumask_t irq_affinity[NR_IRQS] = { [0 ... NR_IRQS-1] = CPU_MASK_ALL }; + +static int irq_affinity_read_proc(char *page, char **start, off_t off, + int count, int *eof, void *data) +{ + int len = cpumask_scnprintf(page, count, irq_affinity[(long)data]); + if (count - len < 2) + return -EINVAL; + len += sprintf(page + len, "\n"); + return len; +} + +static int irq_affinity_write_proc(struct file *file, const char __user *buffer, + unsigned long count, void *data) +{ + int irq = (long)data, full_count = count, err; + cpumask_t new_value, tmp; + + if (!irq_desc[irq].handler->set_affinity) + return -EIO; + + err = cpumask_parse(buffer, count, new_value); + if (err) + return err; + + /* + * 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. + */ + cpus_and(tmp, new_value, cpu_online_map); + if (cpus_empty(tmp)) + return -EINVAL; + + irq_affinity[irq] = new_value; + irq_desc[irq].handler->set_affinity(irq, + cpumask_of_cpu(first_cpu(new_value))); + + return full_count; +} + +#endif + +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); + if (count - len < 2) + return -EINVAL; + len += sprintf(page + len, "\n"); + return len; +} + +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; + unsigned long full_count = count, err; + cpumask_t new_value; + + err = cpumask_parse(buffer, count, new_value); + if (err) + return err; + + *mask = new_value; + return full_count; +} + +#define MAX_NAMELEN 10 + +static void register_irq_proc (unsigned int irq) +{ + char name [MAX_NAMELEN]; + + if (!root_irq_dir || (irq_desc[irq].handler == &no_irq_type) || + irq_dir[irq]) + return; + + memset(name, 0, MAX_NAMELEN); + sprintf(name, "%d", irq); + + /* create /proc/irq/1234 */ + irq_dir[irq] = proc_mkdir(name, root_irq_dir); + +#ifdef CONFIG_SMP + { + struct proc_dir_entry *entry; + + /* create /proc/irq/1234/smp_affinity */ + entry = create_proc_entry("smp_affinity", 0600, irq_dir[irq]); + + if (entry) { + entry->nlink = 1; + entry->data = (void *)(long)irq; + entry->read_proc = irq_affinity_read_proc; + entry->write_proc = irq_affinity_write_proc; + } + + smp_affinity_entry[irq] = entry; + } +#endif +} + +unsigned long prof_cpu_mask = -1; + +void init_irq_proc (void) +{ + struct proc_dir_entry *entry; + int i; + + /* create /proc/irq */ + root_irq_dir = proc_mkdir("irq", NULL); + + /* create /proc/irq/prof_cpu_mask */ + entry = create_proc_entry("prof_cpu_mask", 0600, root_irq_dir); + + if (!entry) + return; + + entry->nlink = 1; + entry->data = (void *)&prof_cpu_mask; + entry->read_proc = prof_cpu_mask_read_proc; + entry->write_proc = prof_cpu_mask_write_proc; + + /* + * Create entries for all existing IRQs. + */ + for (i = 0; i < NR_IRQS; i++) + register_irq_proc(i); +} + diff --git a/arch/m32r/kernel/m32r_ksyms.c b/arch/m32r/kernel/m32r_ksyms.c new file mode 100644 index 000000000..f5aa0766f --- /dev/null +++ b/arch/m32r/kernel/m32r_ksyms.c @@ -0,0 +1,141 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +extern void dump_thread(struct pt_regs *, struct user *); + +#if defined(CONFIG_BLK_DEV_IDE) || defined(CONFIG_BLK_DEV_HD) || defined(CONFIG_BLK_DEV_IDE_MODULE) || defined(CONFIG_BLK_DEV_HD_MODULE) +extern struct drive_info_struct drive_info; +EXPORT_SYMBOL(drive_info); +#endif + +/* platform dependent support */ +EXPORT_SYMBOL(boot_cpu_data); +EXPORT_SYMBOL(dump_thread); +EXPORT_SYMBOL(dump_fpu); +EXPORT_SYMBOL(__ioremap); +EXPORT_SYMBOL(iounmap); +EXPORT_SYMBOL(enable_irq); +EXPORT_SYMBOL(disable_irq); +EXPORT_SYMBOL(disable_irq_nosync); +EXPORT_SYMBOL(kernel_thread); +EXPORT_SYMBOL(__down); +EXPORT_SYMBOL(__down_interruptible); +EXPORT_SYMBOL(__up); +EXPORT_SYMBOL(__down_trylock); + +/* Networking helper routines. */ +/* Delay loops */ +EXPORT_SYMBOL(__udelay); +EXPORT_SYMBOL(__delay); +EXPORT_SYMBOL(__const_udelay); + +EXPORT_SYMBOL(__get_user_1); +EXPORT_SYMBOL(__get_user_2); +EXPORT_SYMBOL(__get_user_4); + +EXPORT_SYMBOL(strpbrk); +EXPORT_SYMBOL(strstr); + +EXPORT_SYMBOL(strncpy_from_user); +EXPORT_SYMBOL(__strncpy_from_user); +EXPORT_SYMBOL(clear_user); +EXPORT_SYMBOL(__clear_user); +EXPORT_SYMBOL(__generic_copy_from_user); +EXPORT_SYMBOL(__generic_copy_to_user); +EXPORT_SYMBOL(strnlen_user); + +#ifdef CONFIG_SMP +#ifdef CONFIG_CHIP_M32700_TS1 +extern void *dcache_dummy; +EXPORT_SYMBOL(dcache_dummy); +#endif +EXPORT_SYMBOL(cpu_data); +EXPORT_SYMBOL(cpu_online_map); +EXPORT_SYMBOL(cpu_callout_map); + +/* Global SMP stuff */ +EXPORT_SYMBOL(synchronize_irq); +EXPORT_SYMBOL(smp_call_function); + +/* TLB flushing */ +EXPORT_SYMBOL(smp_flush_tlb_page); +EXPORT_SYMBOL_GPL(smp_flush_tlb_all); +#endif + +/* compiler generated symbol */ +extern void __ashldi3(void); +extern void __ashrdi3(void); +extern void __lshldi3(void); +extern void __lshrdi3(void); +extern void __muldi3(void); +EXPORT_SYMBOL(__ashldi3); +EXPORT_SYMBOL(__ashrdi3); +EXPORT_SYMBOL(__lshldi3); +EXPORT_SYMBOL(__lshrdi3); +EXPORT_SYMBOL(__muldi3); + +/* memory and string operations */ +EXPORT_SYMBOL(memchr); +EXPORT_SYMBOL(memcpy); +/* EXPORT_SYMBOL(memcpy_fromio); // not implement yet */ +/* EXPORT_SYMBOL(memcpy_toio); // not implement yet */ +EXPORT_SYMBOL(memset); +/* EXPORT_SYMBOL(memset_io); // not implement yet */ +EXPORT_SYMBOL(memmove); +EXPORT_SYMBOL(memcmp); +EXPORT_SYMBOL(memscan); +EXPORT_SYMBOL(copy_page); +EXPORT_SYMBOL(clear_page); + +EXPORT_SYMBOL(strcat); +EXPORT_SYMBOL(strchr); +EXPORT_SYMBOL(strcmp); +EXPORT_SYMBOL(strcpy); +EXPORT_SYMBOL(strlen); +EXPORT_SYMBOL(strncat); +EXPORT_SYMBOL(strncmp); +EXPORT_SYMBOL(strnlen); +EXPORT_SYMBOL(strncpy); + +EXPORT_SYMBOL(_inb); +EXPORT_SYMBOL(_inw); +EXPORT_SYMBOL(_inl); +EXPORT_SYMBOL(_outb); +EXPORT_SYMBOL(_outw); +EXPORT_SYMBOL(_outl); +EXPORT_SYMBOL(_inb_p); +EXPORT_SYMBOL(_inw_p); +EXPORT_SYMBOL(_inl_p); +EXPORT_SYMBOL(_outb_p); +EXPORT_SYMBOL(_outw_p); +EXPORT_SYMBOL(_outl_p); +EXPORT_SYMBOL(_insb); +EXPORT_SYMBOL(_insw); +EXPORT_SYMBOL(_insl); +EXPORT_SYMBOL(_outsb); +EXPORT_SYMBOL(_outsw); +EXPORT_SYMBOL(_outsl); +EXPORT_SYMBOL(_readb); +EXPORT_SYMBOL(_readw); +EXPORT_SYMBOL(_readl); +EXPORT_SYMBOL(_writeb); +EXPORT_SYMBOL(_writew); +EXPORT_SYMBOL(_writel); + diff --git a/arch/m32r/kernel/module.c b/arch/m32r/kernel/module.c new file mode 100644 index 000000000..2d5c38448 --- /dev/null +++ b/arch/m32r/kernel/module.c @@ -0,0 +1,253 @@ +/* Kernel module help for M32R. + + This program is free software; you can redistribute it and/or modify + it under the terms of the 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 +#include +#include +#include +#include +#include + +#if 0 +#define DEBUGP printk +#else +#define DEBUGP(fmt...) +#endif + +void *module_alloc(unsigned long size) +{ + if (size == 0) + return NULL; + return vmalloc_exec(size); +} + + +/* Free memory returned from module_alloc */ +void module_free(struct module *mod, void *module_region) +{ + vfree(module_region); + /* FIXME: If module_region == mod->init_region, trim exception + table entries. */ +} + +/* We don't need anything special. */ +int module_frob_arch_sections(Elf_Ehdr *hdr, + Elf_Shdr *sechdrs, + char *secstrings, + struct module *mod) +{ + return 0; +} + +#define COPY_UNALIGNED_WORD(sw, tw, align) \ +{ \ + void *__s = &(sw), *__t = &(tw); \ + unsigned short *__s2 = __s, *__t2 =__t; \ + unsigned char *__s1 = __s, *__t1 =__t; \ + switch ((align)) \ + { \ + case 0: \ + *(unsigned long *) __t = *(unsigned long *) __s; \ + break; \ + case 2: \ + *__t2++ = *__s2++; \ + *__t2 = *__s2; \ + break; \ + default: \ + *__t1++ = *__s1++; \ + *__t1++ = *__s1++; \ + *__t1++ = *__s1++; \ + *__t1 = *__s1; \ + break; \ + } \ +} + +#define COPY_UNALIGNED_HWORD(sw, tw, align) \ + { \ + void *__s = &(sw), *__t = &(tw); \ + unsigned short *__s2 = __s, *__t2 =__t; \ + unsigned char *__s1 = __s, *__t1 =__t; \ + switch ((align)) \ + { \ + case 0: \ + *__t2 = *__s2; \ + break; \ + default: \ + *__t1++ = *__s1++; \ + *__t1 = *__s1; \ + break; \ + } \ + } + +int apply_relocate_add(Elf32_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; + Elf32_Addr relocation; + uint32_t *location; + uint32_t value; + unsigned short *hlocation; + unsigned short hvalue; + int svalue; + int align; + + DEBUGP("Applying relocate section %u to %u\n", relsec, + sechdrs[relsec].sh_info); + for (i = 0; i < sechdrs[relsec].sh_size / sizeof(*rel); i++) { + /* This is where to make the change */ + location = (void *)sechdrs[sechdrs[relsec].sh_info].sh_addr + + rel[i].r_offset; + /* 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); + relocation = sym->st_value + rel[i].r_addend; + align = (int)location & 3; + + switch (ELF32_R_TYPE(rel[i].r_info)) { + case R_M32R_32_RELA: + COPY_UNALIGNED_WORD (*location, value, align); + value += relocation; + COPY_UNALIGNED_WORD (value, *location, align); + break; + case R_M32R_HI16_ULO_RELA: + COPY_UNALIGNED_WORD (*location, value, align); + relocation = (relocation >>16) & 0xffff; + /* RELA must has 0 at relocation field. */ + value += relocation; + COPY_UNALIGNED_WORD (value, *location, align); + break; + case R_M32R_HI16_SLO_RELA: + COPY_UNALIGNED_WORD (*location, value, align); + if (relocation & 0x8000) relocation += 0x10000; + relocation = (relocation >>16) & 0xffff; + /* RELA must has 0 at relocation field. */ + value += relocation; + COPY_UNALIGNED_WORD (value, *location, align); + break; + case R_M32R_16_RELA: + hlocation = (unsigned short *)location; + relocation = relocation & 0xffff; + /* RELA must has 0 at relocation field. */ + hvalue = relocation; + COPY_UNALIGNED_WORD (hvalue, *hlocation, align); + break; + case R_M32R_SDA16_RELA: + case R_M32R_LO16_RELA: + COPY_UNALIGNED_WORD (*location, value, align); + relocation = relocation & 0xffff; + /* RELA must has 0 at relocation field. */ + value += relocation; + COPY_UNALIGNED_WORD (value, *location, align); + break; + case R_M32R_24_RELA: + COPY_UNALIGNED_WORD (*location, value, align); + relocation = relocation & 0xffffff; + /* RELA must has 0 at relocation field. */ + value += relocation; + COPY_UNALIGNED_WORD (value, *location, align); + break; + case R_M32R_18_PCREL_RELA: + relocation = (relocation - (Elf32_Addr) location); + if (relocation < -0x20000 || 0x1fffc < relocation) + { + printk(KERN_ERR "module %s: relocation overflow: %u\n", + me->name, relocation); + return -ENOEXEC; + } + COPY_UNALIGNED_WORD (*location, value, align); + if (value & 0xffff) + { + /* RELA must has 0 at relocation field. */ + printk(KERN_ERR "module %s: illegal relocation field: %u\n", + me->name, value); + return -ENOEXEC; + } + relocation = (relocation >> 2) & 0xffff; + value += relocation; + COPY_UNALIGNED_WORD (value, *location, align); + break; + case R_M32R_10_PCREL_RELA: + hlocation = (unsigned short *)location; + relocation = (relocation - (Elf32_Addr) location); + COPY_UNALIGNED_HWORD (*hlocation, hvalue, align); + svalue = (int)hvalue; + svalue = (signed char)svalue << 2; + relocation += svalue; + relocation = (relocation >> 2) & 0xff; + hvalue = hvalue & 0xff00; + hvalue += relocation; + COPY_UNALIGNED_HWORD (hvalue, *hlocation, align); + break; + case R_M32R_26_PCREL_RELA: + relocation = (relocation - (Elf32_Addr) location); + if (relocation < -0x2000000 || 0x1fffffc < relocation) + { + printk(KERN_ERR "module %s: relocation overflow: %u\n", + me->name, relocation); + return -ENOEXEC; + } + COPY_UNALIGNED_WORD (*location, value, align); + if (value & 0xffffff) + { + /* RELA must has 0 at relocation field. */ + printk(KERN_ERR "module %s: illegal relocation field: %u\n", + me->name, value); + return -ENOEXEC; + } + relocation = (relocation >> 2) & 0xffffff; + value += relocation; + COPY_UNALIGNED_WORD (value, *location, align); + break; + default: + printk(KERN_ERR "module %s: Unknown relocation: %u\n", + me->name, ELF32_R_TYPE(rel[i].r_info)); + return -ENOEXEC; + } + } + return 0; +} + +int apply_relocate(Elf32_Shdr *sechdrs, + const char *strtab, + unsigned int symindex, + unsigned int relsec, + struct module *me) +{ +#if 0 + printk(KERN_ERR "module %s: REL RELOCATION unsupported\n", + me->name); + return -ENOEXEC; +#endif + return 0; + +} + +int module_finalize(const Elf_Ehdr *hdr, + const Elf_Shdr *sechdrs, + struct module *me) +{ + return 0; +} + +void module_arch_cleanup(struct module *mod) +{ +} diff --git a/arch/m32r/kernel/process.c b/arch/m32r/kernel/process.c new file mode 100644 index 000000000..9e7de27a8 --- /dev/null +++ b/arch/m32r/kernel/process.c @@ -0,0 +1,356 @@ +/* + * linux/arch/m32r/kernel/process.c + * orig : sh + * + * Copyright (c) 2001, 2002 Hiroyuki Kondo, Hirokazu Takata, + * Hitoshi Yamamoto + * Taken from sh version. + * Copyright (C) 1995 Linus Torvalds + * SuperH version: Copyright (C) 1999, 2000 Niibe Yutaka & Kaz Kojima + */ + +#undef DEBUG_PROCESS +#ifdef DEBUG_PROCESS +#define DPRINTK(fmt, args...) printk("%s:%d:%s: " fmt, __FILE__, __LINE__, \ + __FUNCTION__, ##args) +#else +#define DPRINTK(fmt, args...) +#endif + +/* + * This file handles the architecture-dependent parts of process handling.. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include + +static int hlt_counter=0; + +/* + * Return saved PC of a blocked thread. + */ +unsigned long thread_saved_pc(struct task_struct *tsk) +{ + return tsk->thread.lr; +} + +/* + * Powermanagement idle function, if any.. + */ +void (*pm_idle)(void) = NULL; + +void disable_hlt(void) +{ + hlt_counter++; +} + +EXPORT_SYMBOL(disable_hlt); + +void enable_hlt(void) +{ + hlt_counter--; +} + +EXPORT_SYMBOL(enable_hlt); + +/* + * We use this is we don't have any better + * idle routine.. + */ +void default_idle(void) +{ + /* M32R_FIXME: Please use "cpu_sleep" mode. */ + cpu_relax(); +} + +/* + * On SMP it's slightly faster (but much more power-consuming!) + * to poll the ->work.need_resched flag instead of waiting for the + * cross-CPU IPI to arrive. Use this option with caution. + */ +static void poll_idle (void) +{ + /* M32R_FIXME */ + cpu_relax(); +} + +/* + * The idle thread. There's no useful work to be + * done, so just try to conserve power and have a + * low exit latency (ie sit in a loop waiting for + * somebody to say that they'd like to reschedule) + */ +void cpu_idle (void) +{ + /* endless idle loop with no priority at all */ + while (1) { + while (!need_resched()) { + void (*idle)(void) = pm_idle; + + if (!idle) + idle = default_idle; + + idle(); + } + schedule(); + } +} + +void machine_restart(char *__unused) +{ + printk("Please push reset button!\n"); + while (1) + cpu_relax(); +} + +EXPORT_SYMBOL(machine_restart); + +void machine_halt(void) +{ + printk("Please push reset button!\n"); + while (1) + cpu_relax(); +} + +EXPORT_SYMBOL(machine_halt); + +void machine_power_off(void) +{ + /* M32R_FIXME */ +} + +EXPORT_SYMBOL(machine_power_off); + +static int __init idle_setup (char *str) +{ + if (!strncmp(str, "poll", 4)) { + printk("using poll in idle threads.\n"); + pm_idle = poll_idle; + } else if (!strncmp(str, "sleep", 4)) { + printk("using sleep in idle threads.\n"); + pm_idle = default_idle; + } + + return 1; +} + +__setup("idle=", idle_setup); + +void show_regs(struct pt_regs * regs) +{ + printk("\n"); + printk("BPC[%08lx]:PSW[%08lx]:LR [%08lx]:FP [%08lx]\n", \ + regs->bpc, regs->psw, regs->lr, regs->fp); + printk("BBPC[%08lx]:BBPSW[%08lx]:SPU[%08lx]:SPI[%08lx]\n", \ + regs->bbpc, regs->bbpsw, regs->spu, regs->spi); + printk("R0 [%08lx]:R1 [%08lx]:R2 [%08lx]:R3 [%08lx]\n", \ + regs->r0, regs->r1, regs->r2, regs->r3); + printk("R4 [%08lx]:R5 [%08lx]:R6 [%08lx]:R7 [%08lx]\n", \ + regs->r4, regs->r5, regs->r6, regs->r7); + printk("R8 [%08lx]:R9 [%08lx]:R10[%08lx]:R11[%08lx]\n", \ + regs->r8, regs->r9, regs->r10, regs->r11); + printk("R12[%08lx]\n", \ + regs->r12); + +#if defined(CONFIG_ISA_M32R2) && defined(CONFIG_ISA_DSP_LEVEL2) + printk("ACC0H[%08lx]:ACC0L[%08lx]\n", \ + regs->acc0h, regs->acc0l); + printk("ACC1H[%08lx]:ACC1L[%08lx]\n", \ + regs->acc1h, regs->acc1l); +#elif defined(CONFIG_ISA_M32R2) || defined(CONFIG_ISA_M32R) + printk("ACCH[%08lx]:ACCL[%08lx]\n", \ + regs->acch, regs->accl); +#else +#error unknown isa configuration +#endif +} + +/* + * Create a kernel thread + */ + +/* + * 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 free'd until both the parent and the child have exited. + */ +static void kernel_thread_helper(void *nouse, int (*fn)(void *), void *arg) +{ + fn(arg); + do_exit(-1); +} + +int kernel_thread(int (*fn)(void *), void *arg, unsigned long flags) +{ + struct pt_regs regs; + + memset(®s, 0, sizeof (regs)); + regs.r1 = (unsigned long)fn; + regs.r2 = (unsigned long)arg; + + regs.bpc = (unsigned long)kernel_thread_helper; + + regs.psw = M32R_PSW_BIE; + + /* Ok, create the new process. */ + return do_fork(flags | CLONE_VM | CLONE_UNTRACED, 0, ®s, 0, NULL, + NULL); +} + +/* + * Free current thread data structures etc.. + */ +void exit_thread(void) +{ + /* Nothing to do. */ + DPRINTK("pid = %d\n", current->pid); +} + +void flush_thread(void) +{ + DPRINTK("pid = %d\n", current->pid); + memset(¤t->thread.debug_trap, 0, sizeof(struct debug_trap)); +} + +void release_thread(struct task_struct *dead_task) +{ + /* do nothing */ + DPRINTK("pid = %d\n", dead_task->pid); +} + +/* Fill in the fpu structure for a core dump.. */ +int dump_fpu(struct pt_regs *regs, elf_fpregset_t *fpu) +{ + return 0; /* Task didn't use the fpu at all. */ +} + +int copy_thread(int nr, unsigned long clone_flags, unsigned long spu, + unsigned long unused, struct task_struct *tsk, struct pt_regs *regs) +{ + struct pt_regs *childregs; + unsigned long sp = (unsigned long)tsk->thread_info + THREAD_SIZE; + extern void ret_from_fork(void); + + tsk->set_child_tid = tsk->clear_child_tid = NULL; + + /* Copy registers */ + sp -= sizeof (struct pt_regs); + childregs = (struct pt_regs *)sp; + *childregs = *regs; + + childregs->spu = spu; + childregs->r0 = 0; /* Child gets zero as return value */ + regs->r0 = tsk->pid; + tsk->thread.sp = (unsigned long)childregs; + tsk->thread.lr = (unsigned long)ret_from_fork; + + return 0; +} + +/* + * fill in the user structure for a core dump.. + */ +void dump_thread(struct pt_regs * regs, struct user * dump) +{ + /* M32R_FIXME */ +} + +/* + * Capture the user space registers if the task is not running (in user space) + */ +int dump_task_regs(struct task_struct *tsk, elf_gregset_t *regs) +{ + /* M32R_FIXME */ + return 1; +} + +asmlinkage int sys_fork(unsigned long r0, unsigned long r1, unsigned long r2, + unsigned long r3, unsigned long r4, unsigned long r5, unsigned long r6, + struct pt_regs regs) +{ +#ifdef CONFIG_MMU + return do_fork(SIGCHLD, regs.spu, ®s, 0, NULL, NULL); +#else + return -EINVAL; +#endif /* CONFIG_MMU */ +} + +asmlinkage int sys_clone(unsigned long clone_flags, unsigned long newsp, + unsigned long r2, unsigned long r3, unsigned long r4, unsigned long r5, + unsigned long r6, struct pt_regs regs) +{ + if (!newsp) + newsp = regs.spu; + + return do_fork(clone_flags, newsp, ®s, 0, NULL, NULL); +} + +/* + * This is trivial, and on the face of it looks like it + * could equally well be done in user mode. + * + * Not so, for quite unobvious reasons - register pressure. + * In user mode vfork() cannot have a stack frame, and if + * done by calling the "clone()" system call directly, you + * do not have enough call-clobbered registers to hold all + * the information you need. + */ +asmlinkage int sys_vfork(unsigned long r0, unsigned long r1, unsigned long r2, + unsigned long r3, unsigned long r4, unsigned long r5, unsigned long r6, + struct pt_regs regs) +{ + return do_fork(CLONE_VFORK | CLONE_VM | SIGCHLD, regs.spu, ®s, 0, + NULL, NULL); +} + +/* + * sys_execve() executes a new program. + */ +asmlinkage int sys_execve(char __user *ufilename, char __user * __user *uargv, char __user * __user *uenvp, + unsigned long r3, unsigned long r4, unsigned long r5, unsigned long r6, + struct pt_regs regs) +{ + int error; + char *filename; + + filename = getname(ufilename); + error = PTR_ERR(filename); + if (IS_ERR(filename)) + goto out; + + error = do_execve(filename, uargv, uenvp, ®s); + if (error == 0) + current->ptrace &= ~PT_DTRACE; + putname(filename); +out: + return error; +} + +/* + * These bracket the sleeping functions.. + */ +#define first_sched ((unsigned long) scheduling_functions_start_here) +#define last_sched ((unsigned long) scheduling_functions_end_here) + +unsigned long get_wchan(struct task_struct *p) +{ + /* M32R_FIXME */ + return (0); +} + diff --git a/arch/m32r/kernel/ptrace.c b/arch/m32r/kernel/ptrace.c new file mode 100644 index 000000000..ab4137a33 --- /dev/null +++ b/arch/m32r/kernel/ptrace.c @@ -0,0 +1,858 @@ +/* + * linux/arch/m32r/kernel/ptrace.c + * + * Copyright (C) 2002 Hirokazu Takata, Takeo Takahashi + * Copyright (C) 2004 Hirokazu Takata + * + * Original x86 implementation: + * By Ross Biro 1/23/92 + * edited by Linus Torvalds + * + * Some code taken from sh version: + * Copyright (C) 1999, 2000 Kaz Kojima & Niibe Yutaka + * Some code taken from arm version: + * Copyright (C) 2000 Russell King + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +/* + * Get the address of the live pt_regs for the specified task. + * These are saved onto the top kernel stack when the process + * is not running. + * + * Note: if a user thread is execve'd from kernel space, the + * kernel stack will not be empty on entry to the kernel, so + * ptracing these tasks will fail. + */ +static inline struct pt_regs * +get_user_regs(struct task_struct *task) +{ + return (struct pt_regs *) + ((unsigned long)task->thread_info + THREAD_SIZE + - sizeof(struct pt_regs)); +} + +/* + * This routine will get a word off of the process kernel stack. + */ +static inline unsigned long int +get_stack_long(struct task_struct *task, int offset) +{ + unsigned long *stack; + + stack = (unsigned long *)get_user_regs(task); + + return stack[offset]; +} + +/* + * This routine will put a word on the process kernel stack. + */ +static inline int +put_stack_long(struct task_struct *task, int offset, unsigned long data) +{ + unsigned long *stack; + + stack = (unsigned long *)get_user_regs(task); + stack[offset] = data; + + return 0; +} + +static int reg_offset[] = { + PT_R0, PT_R1, PT_R2, PT_R3, PT_R4, PT_R5, PT_R6, PT_R7, + PT_R8, PT_R9, PT_R10, PT_R11, PT_R12, PT_FP, PT_LR, PT_SPU, +}; + +/* + * Read the word at offset "off" into the "struct user". We + * actually access the pt_regs stored on the kernel stack. + */ +static int ptrace_read_user(struct task_struct *tsk, unsigned long off, + unsigned long __user *data) +{ + unsigned long tmp; +#ifndef NO_FPU + struct user * dummy = NULL; +#endif + + if ((off & 3) || (off < 0) || (off > sizeof(struct user) - 3)) + return -EIO; + + off >>= 2; + switch (off) { + case PT_EVB: + __asm__ __volatile__ ( + "mvfc %0, cr5 \n\t" + : "=r" (tmp) + ); + break; + case PT_CBR: { + unsigned long psw; + psw = get_stack_long(tsk, PT_PSW); + tmp = ((psw >> 8) & 1); + } + break; + case PT_PSW: { + unsigned long psw, bbpsw; + psw = get_stack_long(tsk, PT_PSW); + bbpsw = get_stack_long(tsk, PT_BBPSW); + tmp = ((psw >> 8) & 0xff) | ((bbpsw & 0xff) << 8); + } + break; + case PT_PC: + tmp = get_stack_long(tsk, PT_BPC); + break; + case PT_BPC: + off = PT_BBPC; + /* fall through */ + default: + if (off < (sizeof(struct pt_regs) >> 2)) + tmp = get_stack_long(tsk, off); +#ifndef NO_FPU + else if (off >= (long)(&dummy->fpu >> 2) && + off < (long)(&dummy->u_fpvalid >> 2)) { + if (!tsk->used_math) { + if (off == (long)(&dummy->fpu.fpscr >> 2)) + tmp = FPSCR_INIT; + else + tmp = 0; + } else + tmp = ((long *)(&tsk->thread.fpu >> 2)) + [off - (long)&dummy->fpu]; + } else if (off == (long)(&dummy->u_fpvalid >> 2)) + tmp = tsk->used_math; +#endif /* not NO_FPU */ + else + tmp = 0; + } + + return put_user(tmp, data); +} + +static int ptrace_write_user(struct task_struct *tsk, unsigned long off, + unsigned long data) +{ + int ret = -EIO; +#ifndef NO_FPU + struct user * dummy = NULL; +#endif + + if ((off & 3) || off < 0 || + off > sizeof(struct user) - 3) + return -EIO; + + off >>= 2; + switch (off) { + case PT_EVB: + case PT_BPC: + case PT_SPI: + /* We don't allow to modify evb. */ + ret = 0; + break; + case PT_PSW: + case PT_CBR: { + /* We allow to modify only cbr in psw */ + unsigned long psw; + psw = get_stack_long(tsk, PT_PSW); + psw = (psw & ~0x100) | ((data & 1) << 8); + ret = put_stack_long(tsk, PT_PSW, psw); + } + break; + case PT_PC: + off = PT_BPC; + data &= ~1; + /* fall through */ + default: + if (off < (sizeof(struct pt_regs) >> 2)) + ret = put_stack_long(tsk, off, data); +#ifndef NO_FPU + else if (off >= (long)(&dummy->fpu >> 2) && + off < (long)(&dummy->u_fpvalid >> 2)) { + tsk->used_math = 1; + ((long *)&tsk->thread.fpu) + [off - (long)&dummy->fpu] = data; + ret = 0; + } else if (off == (long)(&dummy->u_fpvalid >> 2)) { + tsk->used_math = data ? 1 : 0; + ret = 0; + } +#endif /* not NO_FPU */ + break; + } + + return ret; +} + +/* + * Get all user integer registers. + */ +static int ptrace_getregs(struct task_struct *tsk, void __user *uregs) +{ + struct pt_regs *regs = get_user_regs(tsk); + + return copy_to_user(uregs, regs, sizeof(struct pt_regs)) ? -EFAULT : 0; +} + +/* + * Set all user integer registers. + */ +static int ptrace_setregs(struct task_struct *tsk, void __user *uregs) +{ + struct pt_regs newregs; + int ret; + + ret = -EFAULT; + if (copy_from_user(&newregs, uregs, sizeof(struct pt_regs)) == 0) { + struct pt_regs *regs = get_user_regs(tsk); + *regs = newregs; + ret = 0; + } + + return ret; +} + + +static inline int +check_condition_bit(struct task_struct *child) +{ + return (int)((get_stack_long(child, PT_PSW) >> 8) & 1); +} + +static int +check_condition_src(unsigned long op, unsigned long regno1, + unsigned long regno2, struct task_struct *child) +{ + unsigned long reg1, reg2; + + reg2 = get_stack_long(child, reg_offset[regno2]); + + switch (op) { + case 0x0: /* BEQ */ + reg1 = get_stack_long(child, reg_offset[regno1]); + return reg1 == reg2; + case 0x1: /* BNE */ + reg1 = get_stack_long(child, reg_offset[regno1]); + return reg1 != reg2; + case 0x8: /* BEQZ */ + return reg2 == 0; + case 0x9: /* BNEZ */ + return reg2 != 0; + case 0xa: /* BLTZ */ + return (int)reg2 < 0; + case 0xb: /* BGEZ */ + return (int)reg2 >= 0; + case 0xc: /* BLEZ */ + return (int)reg2 <= 0; + case 0xd: /* BGTZ */ + return (int)reg2 > 0; + default: + /* never reached */ + return 0; + } +} + +static void +compute_next_pc_for_16bit_insn(unsigned long insn, unsigned long pc, + unsigned long *next_pc, + struct task_struct *child) +{ + unsigned long op, op2, op3; + unsigned long disp; + unsigned long regno; + int parallel = 0; + + if (insn & 0x00008000) + parallel = 1; + if (pc & 3) + insn &= 0x7fff; /* right slot */ + else + insn >>= 16; /* left slot */ + + op = (insn >> 12) & 0xf; + op2 = (insn >> 8) & 0xf; + op3 = (insn >> 4) & 0xf; + + if (op == 0x7) { + switch (op2) { + case 0xd: /* BNC */ + case 0x9: /* BNCL */ + if (!check_condition_bit(child)) { + disp = (long)(insn << 24) >> 22; + *next_pc = (pc & ~0x3) + disp; + return; + } + break; + case 0x8: /* BCL */ + case 0xc: /* BC */ + if (check_condition_bit(child)) { + disp = (long)(insn << 24) >> 22; + *next_pc = (pc & ~0x3) + disp; + return; + } + break; + case 0xe: /* BL */ + case 0xf: /* BRA */ + disp = (long)(insn << 24) >> 22; + *next_pc = (pc & ~0x3) + disp; + return; + break; + } + } else if (op == 0x1) { + switch (op2) { + case 0x0: + if (op3 == 0xf) { /* TRAP */ +#if 1 + /* pass through */ +#else + /* kernel space is not allowed as next_pc */ + unsigned long evb; + unsigned long trapno; + trapno = insn & 0xf; + __asm__ __volatile__ ( + "mvfc %0, cr5\n" + :"=r"(evb) + : + ); + *next_pc = evb + (trapno << 2); + return; +#endif + } else if (op3 == 0xd) { /* RTE */ + *next_pc = get_stack_long(child, PT_BPC); + return; + } + break; + case 0xc: /* JC */ + if (op3 == 0xc && check_condition_bit(child)) { + regno = insn & 0xf; + *next_pc = get_stack_long(child, + reg_offset[regno]); + return; + } + break; + case 0xd: /* JNC */ + if (op3 == 0xc && !check_condition_bit(child)) { + regno = insn & 0xf; + *next_pc = get_stack_long(child, + reg_offset[regno]); + return; + } + break; + case 0xe: /* JL */ + case 0xf: /* JMP */ + if (op3 == 0xc) { /* JMP */ + regno = insn & 0xf; + *next_pc = get_stack_long(child, + reg_offset[regno]); + return; + } + break; + } + } + if (parallel) + *next_pc = pc + 4; + else + *next_pc = pc + 2; +} + +static void +compute_next_pc_for_32bit_insn(unsigned long insn, unsigned long pc, + unsigned long *next_pc, + struct task_struct *child) +{ + unsigned long op; + unsigned long op2; + unsigned long disp; + unsigned long regno1, regno2; + + op = (insn >> 28) & 0xf; + if (op == 0xf) { /* branch 24-bit relative */ + op2 = (insn >> 24) & 0xf; + switch (op2) { + case 0xd: /* BNC */ + case 0x9: /* BNCL */ + if (!check_condition_bit(child)) { + disp = (long)(insn << 8) >> 6; + *next_pc = (pc & ~0x3) + disp; + return; + } + break; + case 0x8: /* BCL */ + case 0xc: /* BC */ + if (check_condition_bit(child)) { + disp = (long)(insn << 8) >> 6; + *next_pc = (pc & ~0x3) + disp; + return; + } + break; + case 0xe: /* BL */ + case 0xf: /* BRA */ + disp = (long)(insn << 8) >> 6; + *next_pc = (pc & ~0x3) + disp; + return; + } + } else if (op == 0xb) { /* branch 16-bit relative */ + op2 = (insn >> 20) & 0xf; + switch (op2) { + case 0x0: /* BEQ */ + case 0x1: /* BNE */ + case 0x8: /* BEQZ */ + case 0x9: /* BNEZ */ + case 0xa: /* BLTZ */ + case 0xb: /* BGEZ */ + case 0xc: /* BLEZ */ + case 0xd: /* BGTZ */ + regno1 = ((insn >> 24) & 0xf); + regno2 = ((insn >> 16) & 0xf); + if (check_condition_src(op2, regno1, regno2, child)) { + disp = (long)(insn << 16) >> 14; + *next_pc = (pc & ~0x3) + disp; + return; + } + break; + } + } + *next_pc = pc + 4; +} + +static inline void +compute_next_pc(unsigned long insn, unsigned long pc, + unsigned long *next_pc, struct task_struct *child) +{ + if (insn & 0x80000000) + compute_next_pc_for_32bit_insn(insn, pc, next_pc, child); + else + compute_next_pc_for_16bit_insn(insn, pc, next_pc, child); +} + +static int +register_debug_trap(struct task_struct *child, unsigned long next_pc, + unsigned long next_insn, unsigned long *code) +{ + struct debug_trap *p = &child->thread.debug_trap; + unsigned long addr = next_pc & ~3; + + if (p->nr_trap != 0) { + printk("kernel BUG at %s %d: p->nr_trap = %d\n", + __FILE__, __LINE__, p->nr_trap); + return -1; + } + p->addr = addr; + p->insn = next_insn; + p->nr_trap++; + if (next_pc & 3) { + *code = (next_insn & 0xffff0000) | 0x10f1; + /* xxx --> TRAP1 */ + } else { + if ((next_insn & 0x80000000) || (next_insn & 0x8000)) { + *code = 0x10f17000; + /* TRAP1 --> NOP */ + } else { + *code = (next_insn & 0xffff) | 0x10f10000; + /* TRAP1 --> xxx */ + } + } + return 0; +} + +int withdraw_debug_trap_for_signal(struct task_struct *child) +{ + struct debug_trap *p = &child->thread.debug_trap; + int nr_trap = p->nr_trap; + + if (nr_trap) { + access_process_vm(child, p->addr, &p->insn, sizeof(p->insn), 1); + p->nr_trap = 0; + p->addr = 0; + p->insn = 0; + } + return nr_trap; +} + +static int +unregister_debug_trap(struct task_struct *child, unsigned long addr, + unsigned long *code) +{ + struct debug_trap *p = &child->thread.debug_trap; + + if (p->nr_trap != 1 || p->addr != addr) { + /* The trap may be requested from debugger. + * ptrace should do nothing in this case. + */ + return 0; + } + *code = p->insn; + p->insn = 0; + p->addr = 0; + p->nr_trap--; + return 1; +} + +static void +unregister_all_debug_traps(struct task_struct *child) +{ + struct debug_trap *p = &child->thread.debug_trap; + + if (p->nr_trap) { + access_process_vm(child, p->addr, &p->insn, sizeof(p->insn), 1); + p->addr = 0; + p->insn = 0; + p->nr_trap = 0; + } +} + +static inline void +invalidate_cache(void) +{ +#if defined(CONFIG_CHIP_M32700) || defined(CONFIG_CHIP_OPSP) + + _flush_cache_copyback_all(); + +#else /* ! CONFIG_CHIP_M32700 */ + + /* Invalidate cache */ + __asm__ __volatile__ ( + "ldi r0, #-1 \n\t" + "ldi r1, #0 \n\t" + "stb r1, @r0 ; cache off \n\t" + "; \n\t" + "ldi r0, #-2 \n\t" + "ldi r1, #1 \n\t" + "stb r1, @r0 ; cache invalidate \n\t" + ".fillinsn \n" + "0: \n\t" + "ldb r1, @r0 ; invalidate check \n\t" + "bnez r1, 0b \n\t" + "; \n\t" + "ldi r0, #-1 \n\t" + "ldi r1, #1 \n\t" + "stb r1, @r0 ; cache on \n\t" + : : : "r0", "r1", "memory" + ); + /* FIXME: copying-back d-cache and invalidating i-cache are needed. + */ +#endif /* CONFIG_CHIP_M32700 */ +} + +/* Embed a debug trap (TRAP1) code */ +static int +embed_debug_trap(struct task_struct *child, unsigned long next_pc) +{ + unsigned long next_insn, code; + unsigned long addr = next_pc & ~3; + + if (access_process_vm(child, addr, &next_insn, sizeof(next_insn), 0) + != sizeof(next_insn)) { + return -1; /* error */ + } + + /* Set a trap code. */ + if (register_debug_trap(child, next_pc, next_insn, &code)) { + return -1; /* error */ + } + if (access_process_vm(child, addr, &code, sizeof(code), 1) + != sizeof(code)) { + return -1; /* error */ + } + return 0; /* success */ +} + +void +embed_debug_trap_for_signal(struct task_struct *child) +{ + unsigned long next_pc; + unsigned long pc, insn; + int ret; + + pc = get_stack_long(child, PT_BPC); + ret = access_process_vm(child, pc&~3, &insn, sizeof(insn), 0); + if (ret != sizeof(insn)) { + printk("kernel BUG at %s %d: access_process_vm returns %d\n", + __FILE__, __LINE__, ret); + return; + } + compute_next_pc(insn, pc, &next_pc, child); + if (next_pc & 0x80000000) { + printk("kernel BUG at %s %d: next_pc = 0x%08x\n", + __FILE__, __LINE__, (int)next_pc); + return; + } + if (embed_debug_trap(child, next_pc)) { + printk("kernel BUG at %s %d: embed_debug_trap error\n", + __FILE__, __LINE__); + return; + } + invalidate_cache(); +} + +void +withdraw_debug_trap(struct pt_regs *regs) +{ + unsigned long addr; + unsigned long code; + + addr = (regs->bpc - 2) & ~3; + regs->bpc -= 2; + if (unregister_debug_trap(current, addr, &code)) { + access_process_vm(current, addr, &code, sizeof(code), 1); + invalidate_cache(); + } +} + +static void +init_debug_traps(struct task_struct *child) +{ + struct debug_trap *p = &child->thread.debug_trap; + p->nr_trap = 0; + p->addr = 0; + p->insn = 0; +} + + +/* + * Called by kernel/ptrace.c when detaching.. + * + * Make sure single step bits etc are not set. + */ +void ptrace_disable(struct task_struct *child) +{ + /* nothing to do.. */ +} + +static int +do_ptrace(long request, struct task_struct *child, long addr, long data) +{ + unsigned long tmp; + int ret; + + switch (request) { + /* + * read word at location "addr" in the child process. + */ + case PTRACE_PEEKTEXT: + case PTRACE_PEEKDATA: + ret = access_process_vm(child, addr, &tmp, sizeof(tmp), 0); + if (ret == sizeof(tmp)) + ret = put_user(tmp,(unsigned long __user *) data); + else + ret = -EIO; + break; + + /* + * read the word at location addr in the USER area. + */ + case PTRACE_PEEKUSR: + ret = ptrace_read_user(child, addr, + (unsigned long __user *)data); + break; + + /* + * write the word at location addr. + */ + case PTRACE_POKETEXT: + case PTRACE_POKEDATA: + ret = access_process_vm(child, addr, &data, sizeof(data), 1); + if (ret == sizeof(data)) { + ret = 0; + if (request == PTRACE_POKETEXT) { + invalidate_cache(); + } + } else { + ret = -EIO; + } + break; + + /* + * write the word at location addr in the USER area. + */ + case PTRACE_POKEUSR: + ret = ptrace_write_user(child, addr, data); + break; + + /* + * continue/restart and stop at next (return from) syscall + */ + case PTRACE_SYSCALL: + case PTRACE_CONT: + ret = -EIO; + if ((unsigned long) data > _NSIG) + break; + if (request == PTRACE_SYSCALL) + set_tsk_thread_flag(child, TIF_SYSCALL_TRACE); + else + clear_tsk_thread_flag(child, TIF_SYSCALL_TRACE); + child->exit_code = data; + wake_up_process(child); + ret = 0; + break; + + /* + * make the child exit. Best I can do is send it a sigkill. + * perhaps it should be put in the status that it wants to + * exit. + */ + case PTRACE_KILL: { + ret = 0; + unregister_all_debug_traps(child); + invalidate_cache(); + if (child->state == TASK_ZOMBIE) /* already dead */ + break; + child->exit_code = SIGKILL; + wake_up_process(child); + break; + } + + /* + * execute single instruction. + */ + case PTRACE_SINGLESTEP: { + unsigned long next_pc; + unsigned long pc, insn; + + ret = -EIO; + if ((unsigned long) data > _NSIG) + break; + clear_tsk_thread_flag(child, TIF_SYSCALL_TRACE); + if ((child->ptrace & PT_DTRACE) == 0) { + /* Spurious delayed TF traps may occur */ + child->ptrace |= PT_DTRACE; + } + + /* Compute next pc. */ + pc = get_stack_long(child, PT_BPC); + + if (access_process_vm(child, pc&~3, &insn, sizeof(insn), 0) + != sizeof(insn)) + break; + + compute_next_pc(insn, pc, &next_pc, child); + if (next_pc & 0x80000000) + break; + + if (embed_debug_trap(child, next_pc)) + break; + + invalidate_cache(); + child->exit_code = data; + + /* give it a chance to run. */ + wake_up_process(child); + ret = 0; + break; + } + + /* + * detach a process that was attached. + */ + case PTRACE_DETACH: + ret = 0; + ret = ptrace_detach(child, data); + break; + + case PTRACE_GETREGS: + ret = ptrace_getregs(child, (void __user *)data); + break; + + case PTRACE_SETREGS: + ret = ptrace_setregs(child, (void __user *)data); + break; + + default: + ret = ptrace_request(child, request, addr, data); + break; + } + + return ret; +} + +asmlinkage int sys_ptrace(long request, long pid, long addr, long data) +{ + struct task_struct *child; + int ret; + + lock_kernel(); + ret = -EPERM; + if (request == PTRACE_TRACEME) { + /* are we already being traced? */ + if (current->ptrace & PT_PTRACED) + goto out; + /* set the ptrace bit in the process flags. */ + current->ptrace |= PT_PTRACED; + ret = 0; + goto out; + } + ret = -ESRCH; + read_lock(&tasklist_lock); + child = find_task_by_pid(pid); + if (child) + get_task_struct(child); + read_unlock(&tasklist_lock); + if (!child) + goto out; + + ret = -EPERM; + if (pid == 1) /* you may not mess with init */ + goto out; + + if (request == PTRACE_ATTACH) { + ret = ptrace_attach(child); + if (ret == 0) + init_debug_traps(child); + goto out_tsk; + } + + ret = ptrace_check_attach(child, request == PTRACE_KILL); + if (ret == 0) + ret = do_ptrace(request, child, addr, data); + +out_tsk: + put_task_struct(child); +out: + unlock_kernel(); + + return ret; +} + +/* notification of system call entry/exit + * - triggered by current->work.syscall_trace + */ +void do_syscall_trace(void) +{ + if (!test_thread_flag(TIF_SYSCALL_TRACE)) + return; + if (!(current->ptrace & PT_PTRACED)) + return; + /* the 0x80 provides a way for the tracing parent to distinguish + between a syscall stop and SIGTRAP delivery */ + ptrace_notify(SIGTRAP | ((current->ptrace & PT_TRACESYSGOOD) + ? 0x80 : 0)); + + /* + * this isn't the same as continuing with a signal, but it will do + * for normal use. strace only continues with a signal if the + * stopping signal is not SIGTRAP. -brl + */ + if (current->exit_code) { + send_sig(current->exit_code, current, 1); + current->exit_code = 0; + } +} + diff --git a/arch/m32r/kernel/semaphore.c b/arch/m32r/kernel/semaphore.c new file mode 100644 index 000000000..358f217c5 --- /dev/null +++ b/arch/m32r/kernel/semaphore.c @@ -0,0 +1,186 @@ +/* + * linux/arch/m32r/semaphore.c + * orig : i386 2.6.4 + * + * M32R semaphore implementation. + * + * Copyright (c) 2002 - 2004 Hitoshi Yamamoto + */ + +/* + * i386 semaphore implementation. + * + * (C) Copyright 1999 Linus Torvalds + * + * Portions Copyright 1999 Red Hat, 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. + * + * rw semaphores implemented November 1999 by Benjamin LaHaise + */ +#include +#include +#include +#include +#include + +/* + * Semaphores are implemented using a two-way counter: + * The "count" variable is decremented for each process + * that tries to acquire the semaphore, while the "sleeping" + * variable is a count of such acquires. + * + * Notably, the inline "up()" and "down()" functions can + * efficiently test if they need to do any extra work (up + * needs to do something only if count was negative before + * the increment operation. + * + * "sleeping" and the contention routine ordering is protected + * by the spinlock in the semaphore's waitqueue head. + * + * Note that these functions are only called when there is + * contention on the lock, and as such all this is the + * "non-critical" part of the whole semaphore business. The + * critical part is the inline stuff in + * where we want to avoid any extra jumps and calls. + */ + +/* + * Logic: + * - only on a boundary condition do we need to care. When we go + * from a negative count to a non-negative, we wake people up. + * - when we go from a non-negative count to a negative do we + * (a) synchronize with the "sleeper" count and (b) make sure + * that we're on the wakeup list before we synchronize so that + * we cannot lose wakeup events. + */ + +asmlinkage void __up(struct semaphore *sem) +{ + wake_up(&sem->wait); +} + +asmlinkage void __sched __down(struct semaphore * sem) +{ + struct task_struct *tsk = current; + DECLARE_WAITQUEUE(wait, tsk); + unsigned long flags; + + tsk->state = TASK_UNINTERRUPTIBLE; + spin_lock_irqsave(&sem->wait.lock, flags); + add_wait_queue_exclusive_locked(&sem->wait, &wait); + + sem->sleepers++; + for (;;) { + int sleepers = sem->sleepers; + + /* + * Add "everybody else" into it. They aren't + * playing, because we own the spinlock in + * the wait_queue_head. + */ + if (!atomic_add_negative(sleepers - 1, &sem->count)) { + sem->sleepers = 0; + break; + } + sem->sleepers = 1; /* us - see -1 above */ + spin_unlock_irqrestore(&sem->wait.lock, flags); + + schedule(); + + spin_lock_irqsave(&sem->wait.lock, flags); + tsk->state = TASK_UNINTERRUPTIBLE; + } + remove_wait_queue_locked(&sem->wait, &wait); + wake_up_locked(&sem->wait); + spin_unlock_irqrestore(&sem->wait.lock, flags); + tsk->state = TASK_RUNNING; +} + +asmlinkage int __sched __down_interruptible(struct semaphore * sem) +{ + int retval = 0; + struct task_struct *tsk = current; + DECLARE_WAITQUEUE(wait, tsk); + unsigned long flags; + + tsk->state = TASK_INTERRUPTIBLE; + spin_lock_irqsave(&sem->wait.lock, flags); + add_wait_queue_exclusive_locked(&sem->wait, &wait); + + sem->sleepers++; + for (;;) { + int sleepers = sem->sleepers; + + /* + * With signals pending, this turns into + * the trylock failure case - we won't be + * sleeping, and we* can't get the lock as + * it has contention. Just correct the count + * and exit. + */ + if (signal_pending(current)) { + retval = -EINTR; + sem->sleepers = 0; + atomic_add(sleepers, &sem->count); + break; + } + + /* + * Add "everybody else" into it. They aren't + * playing, because we own the spinlock in + * wait_queue_head. The "-1" is because we're + * still hoping to get the semaphore. + */ + if (!atomic_add_negative(sleepers - 1, &sem->count)) { + sem->sleepers = 0; + break; + } + sem->sleepers = 1; /* us - see -1 above */ + spin_unlock_irqrestore(&sem->wait.lock, flags); + + schedule(); + + spin_lock_irqsave(&sem->wait.lock, flags); + tsk->state = TASK_INTERRUPTIBLE; + } + remove_wait_queue_locked(&sem->wait, &wait); + wake_up_locked(&sem->wait); + spin_unlock_irqrestore(&sem->wait.lock, flags); + + tsk->state = TASK_RUNNING; + return retval; +} + +/* + * Trylock failed - make sure we correct for + * having decremented the count. + * + * We could have done the trylock with a + * single "cmpxchg" without failure cases, + * but then it wouldn't work on a 386. + */ +asmlinkage int __down_trylock(struct semaphore * sem) +{ + int sleepers; + unsigned long flags; + + spin_lock_irqsave(&sem->wait.lock, flags); + sleepers = sem->sleepers + 1; + sem->sleepers = 0; + + /* + * Add "everybody else" and us into it. They aren't + * playing, because we own the spinlock in the + * wait_queue_head. + */ + if (!atomic_add_negative(sleepers, &sem->count)) { + wake_up_locked(&sem->wait); + } + + spin_unlock_irqrestore(&sem->wait.lock, flags); + return 1; +} diff --git a/arch/m32r/kernel/setup.c b/arch/m32r/kernel/setup.c new file mode 100644 index 000000000..db9589c24 --- /dev/null +++ b/arch/m32r/kernel/setup.c @@ -0,0 +1,403 @@ +/* + * linux/arch/m32r/kernel/setup.c + * + * Setup routines for Renesas M32R + * + * Copyright (c) 2001, 2002 Hiroyuki Kondo, Hirokazu Takata, + * Hitoshi Yamamoto + */ + +/* $Id$ */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef CONFIG_MMU +extern void init_mmu(void); +#endif + +#if defined(CONFIG_BLK_DEV_IDE) || defined(CONFIG_BLK_DEV_HD) \ + || defined(CONFIG_BLK_DEV_IDE_MODULE) \ + || defined(CONFIG_BLK_DEV_HD_MODULE) +struct drive_info_struct { char dummy[32]; } drive_info; +#endif + +extern char _end[]; + +/* + * Machine setup.. + */ +struct cpuinfo_m32r boot_cpu_data; + +#ifdef CONFIG_BLK_DEV_RAM +extern int rd_doload; /* 1 = load ramdisk, 0 = don't load */ +extern int rd_prompt; /* 1 = prompt for ramdisk, 0 = don't prompt */ +extern int rd_image_start; /* starting block # of image */ +#endif + +#if defined(CONFIG_VGA_CONSOLE) +struct screen_info screen_info = { + .orig_video_lines = 25, + .orig_video_cols = 80, + .orig_video_mode = 0, + .orig_video_ega_bx = 0, + .orig_video_isVGA = 1, + .orig_video_points = 8 +}; +#endif + +extern int root_mountflags; + +static char command_line[COMMAND_LINE_SIZE]; + +static struct resource data_resource = { + .name = "Kernel data", + .start = 0, + .end = 0, + .flags = IORESOURCE_BUSY | IORESOURCE_MEM +}; + +static struct resource code_resource = { + .name = "Kernel code", + .start = 0, + .end = 0, + .flags = IORESOURCE_BUSY | IORESOURCE_MEM +}; + +unsigned long memory_start; +unsigned long memory_end; + +void __init setup_arch(char **); +int get_cpuinfo(char *); + +static __inline__ void parse_mem_cmdline(char ** cmdline_p) +{ + char c = ' '; + char *to = command_line; + char *from = COMMAND_LINE; + int len = 0; + int usermem = 0; + + /* Save unparsed command line copy for /proc/cmdline */ + memcpy(saved_command_line, COMMAND_LINE, COMMAND_LINE_SIZE); + saved_command_line[COMMAND_LINE_SIZE-1] = '\0'; + + memory_start = (unsigned long)CONFIG_MEMORY_START+PAGE_OFFSET; + memory_end = memory_start+(unsigned long)CONFIG_MEMORY_SIZE; + + for ( ; ; ) { + if (c == ' ' && !memcmp(from, "mem=", 4)) { + if (to != command_line) + to--; + + { + unsigned long mem_size; + + usermem = 1; + mem_size = memparse(from+4, &from); + memory_end = memory_start + mem_size; + } + } + c = *(from++); + if (!c) + break; + + if (COMMAND_LINE_SIZE <= ++len) + break; + + *(to++) = c; + } + *to = '\0'; + *cmdline_p = command_line; + if (usermem) + printk(KERN_INFO "user-defined physical RAM map:\n"); +} + +#ifndef CONFIG_DISCONTIGMEM +static unsigned long __init setup_memory(void) +{ + unsigned long start_pfn, max_low_pfn, bootmap_size; + + start_pfn = PFN_UP( __pa(_end) ); + max_low_pfn = PFN_DOWN( __pa(memory_end) ); + + /* + * Initialize the boot-time allocator (with low memory only): + */ + bootmap_size = init_bootmem_node(NODE_DATA(0), start_pfn, + CONFIG_MEMORY_START>>PAGE_SHIFT, max_low_pfn); + + /* + * Register fully available low RAM pages with the bootmem allocator. + */ + { + unsigned long curr_pfn; + unsigned long last_pfn; + unsigned long pages; + + /* + * We are rounding up the start address of usable memory: + */ + curr_pfn = PFN_UP(__pa(memory_start)); + + /* + * ... and at the end of the usable range downwards: + */ + last_pfn = PFN_DOWN(__pa(memory_end)); + + if (last_pfn > max_low_pfn) + last_pfn = max_low_pfn; + + pages = last_pfn - curr_pfn; + free_bootmem(PFN_PHYS(curr_pfn), PFN_PHYS(pages)); + } + + /* + * Reserve the kernel text and + * Reserve the bootmem bitmap. We do this in two steps (first step + * was init_bootmem()), because this catches the (definitely buggy) + * case of us accidentally initializing the bootmem allocator with + * an invalid RAM area. + */ + reserve_bootmem(CONFIG_MEMORY_START + PAGE_SIZE, + (PFN_PHYS(start_pfn) + bootmap_size + PAGE_SIZE - 1) + - CONFIG_MEMORY_START); + + /* + * reserve physical page 0 - it's a special BIOS page on many boxes, + * enabling clean reboots, SMP operation, laptop functions. + */ + reserve_bootmem(CONFIG_MEMORY_START, PAGE_SIZE); + + /* + * reserve memory hole + */ +#ifdef CONFIG_MEMHOLE + reserve_bootmem(CONFIG_MEMHOLE_START, CONFIG_MEMHOLE_SIZE); +#endif + +#ifdef CONFIG_BLK_DEV_INITRD + if (LOADER_TYPE && INITRD_START) { + if (INITRD_START + INITRD_SIZE <= (max_low_pfn << PAGE_SHIFT)) { + reserve_bootmem(INITRD_START, INITRD_SIZE); + initrd_start = INITRD_START ? + INITRD_START + PAGE_OFFSET : 0; + + initrd_end = initrd_start + INITRD_SIZE; + printk("initrd:start[%08lx],size[%08lx]\n", + initrd_start, INITRD_SIZE); + } else { + printk("initrd extends beyond end of memory " + "(0x%08lx > 0x%08lx)\ndisabling initrd\n", + INITRD_START + INITRD_SIZE, + max_low_pfn << PAGE_SHIFT); + + initrd_start = 0; + } + } +#endif + + return max_low_pfn; +} +#else /* CONFIG_DISCONTIGMEM */ +extern unsigned long setup_memory(void); +#endif /* CONFIG_DISCONTIGMEM */ + +#define M32R_PCC_PCATCR 0x00ef7014 /* will move to m32r.h */ + +void __init setup_arch(char **cmdline_p) +{ + ROOT_DEV = old_decode_dev(ORIG_ROOT_DEV); + + boot_cpu_data.cpu_clock = M32R_CPUCLK; + boot_cpu_data.bus_clock = M32R_BUSCLK; + boot_cpu_data.timer_divide = M32R_TIMER_DIVIDE; + +#ifdef CONFIG_BLK_DEV_RAM + rd_image_start = RAMDISK_FLAGS & RAMDISK_IMAGE_START_MASK; + rd_prompt = ((RAMDISK_FLAGS & RAMDISK_PROMPT_FLAG) != 0); + rd_doload = ((RAMDISK_FLAGS & RAMDISK_LOAD_FLAG) != 0); +#endif + + if (!MOUNT_ROOT_RDONLY) + root_mountflags &= ~MS_RDONLY; + +#ifdef CONFIG_VT +#if defined(CONFIG_VGA_CONSOLE) + conswitchp = &vga_con; +#elif defined(CONFIG_DUMMY_CONSOLE) + conswitchp = &dummy_con; +#endif +#endif + +#ifdef CONFIG_DISCONTIGMEM + numnodes = 2; +#endif /* CONFIG_DISCONTIGMEM */ + + 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; + + code_resource.start = virt_to_phys(_text); + code_resource.end = virt_to_phys(_etext)-1; + data_resource.start = virt_to_phys(_etext); + data_resource.end = virt_to_phys(_edata)-1; + + parse_mem_cmdline(cmdline_p); + + setup_memory(); + + paging_init(); +} + +#ifdef CONFIG_PROC_FS +/* + * Get CPU information for use by the procfs. + */ +static int show_cpuinfo(struct seq_file *m, void *v) +{ + struct cpuinfo_m32r *c = v; + unsigned long cpu = c - cpu_data; + +#ifdef CONFIG_SMP + if (!cpu_online(cpu)) + return 0; +#endif /* CONFIG_SMP */ + + seq_printf(m, "processor\t: %ld\n", cpu); + +#ifdef CONFIG_CHIP_VDEC2 + seq_printf(m, "cpu family\t: VDEC2\n" + "cache size\t: Unknown\n"); +#elif CONFIG_CHIP_M32700 + seq_printf(m,"cpu family\t: M32700\n" + "cache size\t: I-8KB/D-8KB\n"); +#elif CONFIG_CHIP_M32102 + seq_printf(m,"cpu family\t: M32102\n" + "cache size\t: I-8KB\n"); +#elif CONFIG_CHIP_OPSP + seq_printf(m,"cpu family\t: OPSP\n" + "cache size\t: I-8KB/D-8KB\n"); +#elif CONFIG_CHIP_MP + seq_printf(m, "cpu family\t: M32R-MP\n" + "cache size\t: I-xxKB/D-xxKB\n"); +#else + seq_printf(m, "cpu family\t: Unknown\n"); +#endif + seq_printf(m, "bogomips\t: %lu.%02lu\n", + c->loops_per_jiffy/(500000/HZ), + (c->loops_per_jiffy/(5000/HZ)) % 100); +#ifdef CONFIG_PLAT_MAPPI + seq_printf(m, "Machine\t\t: Mappi Evaluation board\n"); +#elif CONFIG_PLAT_MAPPI2 + seq_printf(m, "Machine\t\t: Mappi-II Evaluation board\n"); +#elif CONFIG_PLAT_M32700UT + seq_printf(m, "Machine\t\t: M32700UT Evaluation board\n"); +#elif CONFIG_PLAT_OPSPUT + seq_printf(m, "Machine\t\t: OPSPUT Evaluation board\n"); +#elif CONFIG_PLAT_USRV + seq_printf(m, "Machine\t\t: uServer\n"); +#elif CONFIG_PLAT_OAKS32R + seq_printf(m, "Machine\t\t: OAKS32R\n"); +#else + seq_printf(m, "Machine\t\t: Unknown\n"); +#endif + +#define PRINT_CLOCK(name, value) \ + seq_printf(m, name " clock\t: %d.%02dMHz\n", \ + ((value) / 1000000), ((value) % 1000000)/10000) + + PRINT_CLOCK("CPU", (int)c->cpu_clock); + PRINT_CLOCK("Bus", (int)c->bus_clock); + + seq_printf(m, "\n"); + + return 0; +} + +static void *c_start(struct seq_file *m, loff_t *pos) +{ + return *pos < NR_CPUS ? cpu_data + *pos : 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) +{ +} + +struct seq_operations cpuinfo_op = { + start: c_start, + next: c_next, + stop: c_stop, + show: show_cpuinfo, +}; +#endif /* CONFIG_PROC_FS */ + +unsigned long cpu_initialized __initdata = 0; + +/* + * cpu_init() initializes state that is per-CPU. Some data is already + * initialized (naturally) in the bootstrap process. + * We reload them nevertheless, this function acts as a + * 'CPU state barrier', nothing should get across. + */ +#if defined(CONFIG_CHIP_VDEC2) || defined(CONFIG_CHIP_XNUX2) \ + || defined(CONFIG_CHIP_M32700) || defined(CONFIG_CHIP_M32102) \ + || defined(CONFIG_CHIP_OPSP) +void __init cpu_init (void) +{ + int cpu_id = smp_processor_id(); + + if (test_and_set_bit(cpu_id, &cpu_initialized)) { + printk(KERN_WARNING "CPU#%d already initialized!\n", cpu_id); + for ( ; ; ) + local_irq_enable(); + } + printk(KERN_INFO "Initializing CPU#%d\n", cpu_id); + + /* Set up and load the per-CPU TSS and LDT */ + atomic_inc(&init_mm.mm_count); + current->active_mm = &init_mm; + if (current->mm) + BUG(); + + /* Force FPU initialization */ + current_thread_info()->status = 0; + current->used_math = 0; + +#ifdef CONFIG_MMU + /* Set up MMU */ + init_mmu(); +#endif + + /* Set up ICUIMASK */ + outl(0x00070000, M32R_ICU_IMASK_PORTL); /* imask=111 */ +} +#endif /* defined(CONFIG_CHIP_VDEC2) ... */ + diff --git a/arch/m32r/kernel/setup_m32700ut.c b/arch/m32r/kernel/setup_m32700ut.c new file mode 100644 index 000000000..5d0780b8b --- /dev/null +++ b/arch/m32r/kernel/setup_m32700ut.c @@ -0,0 +1,478 @@ +/* + * linux/arch/m32r/kernel/setup_m32700ut.c + * + * Setup routines for Renesas M32700UT Board + * + * Copyright (c) 2002 Hiroyuki Kondo, Hirokazu Takata, + * Hitoshi Yamamoto, Takeo Takahashi + * + * 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 +#include +#include +#include +#include + +#include +#include +#include + +/* + * M32700 Interrupt Control Unit (Level 1) + */ +#define irq2port(x) (M32R_ICU_CR1_PORTL + ((x - 1) * sizeof(unsigned long))) + +#ifndef CONFIG_SMP +typedef struct { + unsigned long icucr; /* ICU Control Register */ +} icu_data_t; +#endif /* CONFIG_SMP */ + +static icu_data_t icu_data[M32700UT_NUM_CPU_IRQ]; + +static void disable_m32700ut_irq(unsigned int irq) +{ + unsigned long port, data; + + port = irq2port(irq); + data = icu_data[irq].icucr|M32R_ICUCR_ILEVEL7; + outl(data, port); +} + +static void enable_m32700ut_irq(unsigned int irq) +{ + unsigned long port, data; + + port = irq2port(irq); + data = icu_data[irq].icucr|M32R_ICUCR_IEN|M32R_ICUCR_ILEVEL6; + outl(data, port); +} + +static void mask_and_ack_m32700ut(unsigned int irq) +{ + disable_m32700ut_irq(irq); +} + +static void end_m32700ut_irq(unsigned int irq) +{ + enable_m32700ut_irq(irq); +} + +static unsigned int startup_m32700ut_irq(unsigned int irq) +{ + enable_m32700ut_irq(irq); + return (0); +} + +static void shutdown_m32700ut_irq(unsigned int irq) +{ + unsigned long port; + + port = irq2port(irq); + outl(M32R_ICUCR_ILEVEL7, port); +} + +static struct hw_interrupt_type m32700ut_irq_type = +{ + "M32700UT-IRQ", + startup_m32700ut_irq, + shutdown_m32700ut_irq, + enable_m32700ut_irq, + disable_m32700ut_irq, + mask_and_ack_m32700ut, + end_m32700ut_irq +}; + +/* + * Interrupt Control Unit of PLD on M32700UT (Level 2) + */ +#define irq2pldirq(x) ((x) - M32700UT_PLD_IRQ_BASE) +#define pldirq2port(x) (unsigned long)((int)PLD_ICUCR1 + \ + (((x) - 1) * sizeof(unsigned short))) + +typedef struct { + unsigned short icucr; /* ICU Control Register */ +} pld_icu_data_t; + +static pld_icu_data_t pld_icu_data[M32700UT_NUM_PLD_IRQ]; + +static void disable_m32700ut_pld_irq(unsigned int irq) +{ + unsigned long port, data; + unsigned int pldirq; + + pldirq = irq2pldirq(irq); +// disable_m32700ut_irq(M32R_IRQ_INT1); + port = pldirq2port(pldirq); + data = pld_icu_data[pldirq].icucr|PLD_ICUCR_ILEVEL7; + outw(data, port); +} + +static void enable_m32700ut_pld_irq(unsigned int irq) +{ + unsigned long port, data; + unsigned int pldirq; + + pldirq = irq2pldirq(irq); +// enable_m32700ut_irq(M32R_IRQ_INT1); + port = pldirq2port(pldirq); + data = pld_icu_data[pldirq].icucr|PLD_ICUCR_IEN|PLD_ICUCR_ILEVEL6; + outw(data, port); +} + +static void mask_and_ack_m32700ut_pld(unsigned int irq) +{ + disable_m32700ut_pld_irq(irq); +// mask_and_ack_m32700ut(M32R_IRQ_INT1); +} + +static void end_m32700ut_pld_irq(unsigned int irq) +{ + enable_m32700ut_pld_irq(irq); + end_m32700ut_irq(M32R_IRQ_INT1); +} + +static unsigned int startup_m32700ut_pld_irq(unsigned int irq) +{ + enable_m32700ut_pld_irq(irq); + return (0); +} + +static void shutdown_m32700ut_pld_irq(unsigned int irq) +{ + unsigned long port; + unsigned int pldirq; + + pldirq = irq2pldirq(irq); +// shutdown_m32700ut_irq(M32R_IRQ_INT1); + port = pldirq2port(pldirq); + outw(PLD_ICUCR_ILEVEL7, port); +} + +static struct hw_interrupt_type m32700ut_pld_irq_type = +{ + "M32700UT-PLD-IRQ", + startup_m32700ut_pld_irq, + shutdown_m32700ut_pld_irq, + enable_m32700ut_pld_irq, + disable_m32700ut_pld_irq, + mask_and_ack_m32700ut_pld, + end_m32700ut_pld_irq +}; + +/* + * Interrupt Control Unit of PLD on M32700UT-LAN (Level 2) + */ +#define irq2lanpldirq(x) ((x) - M32700UT_LAN_PLD_IRQ_BASE) +#define lanpldirq2port(x) (unsigned long)((int)M32700UT_LAN_ICUCR1 + \ + (((x) - 1) * sizeof(unsigned short))) + +static pld_icu_data_t lanpld_icu_data[M32700UT_NUM_LAN_PLD_IRQ]; + +static void disable_m32700ut_lanpld_irq(unsigned int irq) +{ + unsigned long port, data; + unsigned int pldirq; + + pldirq = irq2lanpldirq(irq); + port = lanpldirq2port(pldirq); + data = lanpld_icu_data[pldirq].icucr|PLD_ICUCR_ILEVEL7; + outw(data, port); +} + +static void enable_m32700ut_lanpld_irq(unsigned int irq) +{ + unsigned long port, data; + unsigned int pldirq; + + pldirq = irq2lanpldirq(irq); + port = lanpldirq2port(pldirq); + data = lanpld_icu_data[pldirq].icucr|PLD_ICUCR_IEN|PLD_ICUCR_ILEVEL6; + outw(data, port); +} + +static void mask_and_ack_m32700ut_lanpld(unsigned int irq) +{ + disable_m32700ut_lanpld_irq(irq); +} + +static void end_m32700ut_lanpld_irq(unsigned int irq) +{ + enable_m32700ut_lanpld_irq(irq); + end_m32700ut_irq(M32R_IRQ_INT0); +} + +static unsigned int startup_m32700ut_lanpld_irq(unsigned int irq) +{ + enable_m32700ut_lanpld_irq(irq); + return (0); +} + +static void shutdown_m32700ut_lanpld_irq(unsigned int irq) +{ + unsigned long port; + unsigned int pldirq; + + pldirq = irq2lanpldirq(irq); + port = lanpldirq2port(pldirq); + outw(PLD_ICUCR_ILEVEL7, port); +} + +static struct hw_interrupt_type m32700ut_lanpld_irq_type = +{ + "M32700UT-PLD-LAN-IRQ", + startup_m32700ut_lanpld_irq, + shutdown_m32700ut_lanpld_irq, + enable_m32700ut_lanpld_irq, + disable_m32700ut_lanpld_irq, + mask_and_ack_m32700ut_lanpld, + end_m32700ut_lanpld_irq +}; + +/* + * Interrupt Control Unit of PLD on M32700UT-LCD (Level 2) + */ +#define irq2lcdpldirq(x) ((x) - M32700UT_LCD_PLD_IRQ_BASE) +#define lcdpldirq2port(x) (unsigned long)((int)M32700UT_LCD_ICUCR1 + \ + (((x) - 1) * sizeof(unsigned short))) + +static pld_icu_data_t lcdpld_icu_data[M32700UT_NUM_LCD_PLD_IRQ]; + +static void disable_m32700ut_lcdpld_irq(unsigned int irq) +{ + unsigned long port, data; + unsigned int pldirq; + + pldirq = irq2lcdpldirq(irq); + port = lcdpldirq2port(pldirq); + data = lcdpld_icu_data[pldirq].icucr|PLD_ICUCR_ILEVEL7; + outw(data, port); +} + +static void enable_m32700ut_lcdpld_irq(unsigned int irq) +{ + unsigned long port, data; + unsigned int pldirq; + + pldirq = irq2lcdpldirq(irq); + port = lcdpldirq2port(pldirq); + data = lcdpld_icu_data[pldirq].icucr|PLD_ICUCR_IEN|PLD_ICUCR_ILEVEL6; + outw(data, port); +} + +static void mask_and_ack_m32700ut_lcdpld(unsigned int irq) +{ + disable_m32700ut_lcdpld_irq(irq); +} + +static void end_m32700ut_lcdpld_irq(unsigned int irq) +{ + enable_m32700ut_lcdpld_irq(irq); + end_m32700ut_irq(M32R_IRQ_INT2); +} + +static unsigned int startup_m32700ut_lcdpld_irq(unsigned int irq) +{ + enable_m32700ut_lcdpld_irq(irq); + return (0); +} + +static void shutdown_m32700ut_lcdpld_irq(unsigned int irq) +{ + unsigned long port; + unsigned int pldirq; + + pldirq = irq2lcdpldirq(irq); + port = lcdpldirq2port(pldirq); + outw(PLD_ICUCR_ILEVEL7, port); +} + +static struct hw_interrupt_type m32700ut_lcdpld_irq_type = +{ + "M32700UT-PLD-LCD-IRQ", + startup_m32700ut_lcdpld_irq, + shutdown_m32700ut_lcdpld_irq, + enable_m32700ut_lcdpld_irq, + disable_m32700ut_lcdpld_irq, + mask_and_ack_m32700ut_lcdpld, + end_m32700ut_lcdpld_irq +}; + +void __init init_IRQ(void) +{ +#if defined(CONFIG_SMC91X) + /* INT#0: LAN controller on M32700UT-LAN (SMC91C111)*/ + irq_desc[M32700UT_LAN_IRQ_LAN].status = IRQ_DISABLED; + irq_desc[M32700UT_LAN_IRQ_LAN].handler = &m32700ut_lanpld_irq_type; + irq_desc[M32700UT_LAN_IRQ_LAN].action = 0; + irq_desc[M32700UT_LAN_IRQ_LAN].depth = 1; /* disable nested irq */ + lanpld_icu_data[irq2lanpldirq(M32700UT_LAN_IRQ_LAN)].icucr = PLD_ICUCR_IEN|PLD_ICUCR_ISMOD02; /* "H" edge sense */ + disable_m32700ut_lanpld_irq(M32700UT_LAN_IRQ_LAN); +#endif /* CONFIG_SMC91X */ + + /* MFT2 : system timer */ + irq_desc[M32R_IRQ_MFT2].status = IRQ_DISABLED; + irq_desc[M32R_IRQ_MFT2].handler = &m32700ut_irq_type; + irq_desc[M32R_IRQ_MFT2].action = 0; + irq_desc[M32R_IRQ_MFT2].depth = 1; + icu_data[M32R_IRQ_MFT2].icucr = M32R_ICUCR_IEN; + disable_m32700ut_irq(M32R_IRQ_MFT2); + + /* SIO0 : receive */ + irq_desc[M32R_IRQ_SIO0_R].status = IRQ_DISABLED; + irq_desc[M32R_IRQ_SIO0_R].handler = &m32700ut_irq_type; + irq_desc[M32R_IRQ_SIO0_R].action = 0; + irq_desc[M32R_IRQ_SIO0_R].depth = 1; + icu_data[M32R_IRQ_SIO0_R].icucr = 0; + disable_m32700ut_irq(M32R_IRQ_SIO0_R); + + /* SIO0 : send */ + irq_desc[M32R_IRQ_SIO0_S].status = IRQ_DISABLED; + irq_desc[M32R_IRQ_SIO0_S].handler = &m32700ut_irq_type; + irq_desc[M32R_IRQ_SIO0_S].action = 0; + irq_desc[M32R_IRQ_SIO0_S].depth = 1; + icu_data[M32R_IRQ_SIO0_S].icucr = 0; + disable_m32700ut_irq(M32R_IRQ_SIO0_S); + + /* SIO1 : receive */ + irq_desc[M32R_IRQ_SIO1_R].status = IRQ_DISABLED; + irq_desc[M32R_IRQ_SIO1_R].handler = &m32700ut_irq_type; + irq_desc[M32R_IRQ_SIO1_R].action = 0; + irq_desc[M32R_IRQ_SIO1_R].depth = 1; + icu_data[M32R_IRQ_SIO1_R].icucr = 0; + disable_m32700ut_irq(M32R_IRQ_SIO1_R); + + /* SIO1 : send */ + irq_desc[M32R_IRQ_SIO1_S].status = IRQ_DISABLED; + irq_desc[M32R_IRQ_SIO1_S].handler = &m32700ut_irq_type; + irq_desc[M32R_IRQ_SIO1_S].action = 0; + irq_desc[M32R_IRQ_SIO1_S].depth = 1; + icu_data[M32R_IRQ_SIO1_S].icucr = 0; + disable_m32700ut_irq(M32R_IRQ_SIO1_S); + + /* DMA1 : */ + irq_desc[M32R_IRQ_DMA1].status = IRQ_DISABLED; + irq_desc[M32R_IRQ_DMA1].handler = &m32700ut_irq_type; + irq_desc[M32R_IRQ_DMA1].action = 0; + irq_desc[M32R_IRQ_DMA1].depth = 1; + icu_data[M32R_IRQ_DMA1].icucr = 0; + disable_m32700ut_irq(M32R_IRQ_DMA1); + +#ifdef CONFIG_SERIAL_M32R_PLDSIO + /* INT#1: SIO0 Receive on PLD */ + irq_desc[PLD_IRQ_SIO0_RCV].status = IRQ_DISABLED; + irq_desc[PLD_IRQ_SIO0_RCV].handler = &m32700ut_pld_irq_type; + irq_desc[PLD_IRQ_SIO0_RCV].action = 0; + irq_desc[PLD_IRQ_SIO0_RCV].depth = 1; /* disable nested irq */ + pld_icu_data[irq2pldirq(PLD_IRQ_SIO0_RCV)].icucr = PLD_ICUCR_IEN|PLD_ICUCR_ISMOD03; + disable_m32700ut_pld_irq(PLD_IRQ_SIO0_RCV); + + /* INT#1: SIO0 Send on PLD */ + irq_desc[PLD_IRQ_SIO0_SND].status = IRQ_DISABLED; + irq_desc[PLD_IRQ_SIO0_SND].handler = &m32700ut_pld_irq_type; + irq_desc[PLD_IRQ_SIO0_SND].action = 0; + irq_desc[PLD_IRQ_SIO0_SND].depth = 1; /* disable nested irq */ + pld_icu_data[irq2pldirq(PLD_IRQ_SIO0_SND)].icucr = PLD_ICUCR_IEN|PLD_ICUCR_ISMOD03; + disable_m32700ut_pld_irq(PLD_IRQ_SIO0_SND); +#endif /* CONFIG_SERIAL_M32R_PLDSIO */ + + /* INT#1: CFC IREQ on PLD */ + irq_desc[PLD_IRQ_CFIREQ].status = IRQ_DISABLED; + irq_desc[PLD_IRQ_CFIREQ].handler = &m32700ut_pld_irq_type; + irq_desc[PLD_IRQ_CFIREQ].action = 0; + irq_desc[PLD_IRQ_CFIREQ].depth = 1; /* disable nested irq */ + pld_icu_data[irq2pldirq(PLD_IRQ_CFIREQ)].icucr = PLD_ICUCR_IEN|PLD_ICUCR_ISMOD01; /* 'L' level sense */ + disable_m32700ut_pld_irq(PLD_IRQ_CFIREQ); + + /* INT#1: CFC Insert on PLD */ + irq_desc[PLD_IRQ_CFC_INSERT].status = IRQ_DISABLED; + irq_desc[PLD_IRQ_CFC_INSERT].handler = &m32700ut_pld_irq_type; + irq_desc[PLD_IRQ_CFC_INSERT].action = 0; + irq_desc[PLD_IRQ_CFC_INSERT].depth = 1; /* disable nested irq */ + pld_icu_data[irq2pldirq(PLD_IRQ_CFC_INSERT)].icucr = PLD_ICUCR_IEN|PLD_ICUCR_ISMOD00; /* 'L' edge sense */ + disable_m32700ut_pld_irq(PLD_IRQ_CFC_INSERT); + + /* INT#1: CFC Eject on PLD */ + irq_desc[PLD_IRQ_CFC_EJECT].status = IRQ_DISABLED; + irq_desc[PLD_IRQ_CFC_EJECT].handler = &m32700ut_pld_irq_type; + irq_desc[PLD_IRQ_CFC_EJECT].action = 0; + irq_desc[PLD_IRQ_CFC_EJECT].depth = 1; /* disable nested irq */ + pld_icu_data[irq2pldirq(PLD_IRQ_CFC_EJECT)].icucr = PLD_ICUCR_IEN|PLD_ICUCR_ISMOD02; /* 'H' edge sense */ + disable_m32700ut_pld_irq(PLD_IRQ_CFC_EJECT); + + /* + * INT0# is used for LAN, DIO + * We enable it here. + */ + icu_data[M32R_IRQ_INT0].icucr = M32R_ICUCR_IEN|M32R_ICUCR_ISMOD11; + enable_m32700ut_irq(M32R_IRQ_INT0); + + /* + * INT1# is used for UART, MMC, CF Controller in FPGA. + * We enable it here. + */ + icu_data[M32R_IRQ_INT1].icucr = M32R_ICUCR_IEN|M32R_ICUCR_ISMOD11; + enable_m32700ut_irq(M32R_IRQ_INT1); + +#if defined(CONFIG_USB) + outw(USBCR_OTGS, USBCR); /* USBCR: non-OTG */ + + irq_desc[M32700UT_LCD_IRQ_USB_INT1].status = IRQ_DISABLED; + irq_desc[M32700UT_LCD_IRQ_USB_INT1].handler = &m32700ut_lcdpld_irq_type; + irq_desc[M32700UT_LCD_IRQ_USB_INT1].action = 0; + irq_desc[M32700UT_LCD_IRQ_USB_INT1].depth = 1; + lcdpld_icu_data[irq2lcdpldirq(M32700UT_LCD_IRQ_USB_INT1)].icucr = PLD_ICUCR_IEN|PLD_ICUCR_ISMOD01; /* "L" level sense */ + disable_m32700ut_lcdpld_irq(M32700UT_LCD_IRQ_USB_INT1); +#endif + /* + * INT2# is used for BAT, USB, AUDIO + * We enable it here. + */ + icu_data[M32R_IRQ_INT2].icucr = M32R_ICUCR_IEN|M32R_ICUCR_ISMOD01; + enable_m32700ut_irq(M32R_IRQ_INT2); + +//#if defined(CONFIG_M32R_AR_VGA) + /* + * INT3# is used for AR + */ + irq_desc[M32R_IRQ_INT3].status = IRQ_DISABLED; + irq_desc[M32R_IRQ_INT3].handler = &m32700ut_irq_type; + irq_desc[M32R_IRQ_INT3].action = 0; + irq_desc[M32R_IRQ_INT3].depth = 1; + icu_data[M32R_IRQ_INT3].icucr = M32R_ICUCR_IEN|M32R_ICUCR_ISMOD10; + disable_m32700ut_irq(M32R_IRQ_INT3); +//#endif /* CONFIG_M32R_ARV */ +} + +#define LAN_IOSTART 0x300 +#define LAN_IOEND 0x320 +static struct resource smc91x_resources[] = { + [0] = { + .start = (LAN_IOSTART), + .end = (LAN_IOEND), + .flags = IORESOURCE_MEM, + }, + [1] = { + .start = M32700UT_LAN_IRQ_LAN, + .end = M32700UT_LAN_IRQ_LAN, + .flags = IORESOURCE_IRQ, + } +}; + +static struct platform_device smc91x_device = { + .name = "smc91x", + .id = 0, + .num_resources = ARRAY_SIZE(smc91x_resources), + .resource = smc91x_resources, +}; + +static int __init platform_init(void) +{ + platform_device_register(&smc91x_device); + return 0; +} +arch_initcall(platform_init); diff --git a/arch/m32r/kernel/setup_mappi.c b/arch/m32r/kernel/setup_mappi.c new file mode 100644 index 000000000..523cee330 --- /dev/null +++ b/arch/m32r/kernel/setup_mappi.c @@ -0,0 +1,160 @@ +/* + * linux/arch/m32r/kernel/setup_mappi.c + * + * Setup routines for Renesas MAPPI Board + * + * Copyright (c) 2001, 2002 Hiroyuki Kondo, Hirokazu Takata, + * Hitoshi Yamamoto + */ + +#include +#include +#include +#include + +#include +#include +#include + +#define irq2port(x) (M32R_ICU_CR1_PORTL + ((x - 1) * sizeof(unsigned long))) + +#ifndef CONFIG_SMP +typedef struct { + unsigned long icucr; /* ICU Control Register */ +} icu_data_t; +#endif /* CONFIG_SMP */ + +icu_data_t icu_data[NR_IRQS]; + +static void disable_mappi_irq(unsigned int irq) +{ + unsigned long port, data; + + port = irq2port(irq); + data = icu_data[irq].icucr|M32R_ICUCR_ILEVEL7; + outl(data, port); +} + +static void enable_mappi_irq(unsigned int irq) +{ + unsigned long port, data; + + port = irq2port(irq); + data = icu_data[irq].icucr|M32R_ICUCR_IEN|M32R_ICUCR_ILEVEL6; + outl(data, port); +} + +static void mask_and_ack_mappi(unsigned int irq) +{ + disable_mappi_irq(irq); +} + +static void end_mappi_irq(unsigned int irq) +{ + enable_mappi_irq(irq); +} + +static unsigned int startup_mappi_irq(unsigned int irq) +{ + enable_mappi_irq(irq); + return (0); +} + +static void shutdown_mappi_irq(unsigned int irq) +{ + unsigned long port; + + port = irq2port(irq); + outl(M32R_ICUCR_ILEVEL7, port); +} + +static struct hw_interrupt_type mappi_irq_type = +{ + "MAPPI-IRQ", + startup_mappi_irq, + shutdown_mappi_irq, + enable_mappi_irq, + disable_mappi_irq, + mask_and_ack_mappi, + end_mappi_irq +}; + +void __init init_IRQ(void) +{ + static int once = 0; + + if (once) + return; + else + once++; + +#ifdef CONFIG_NE2000 + /* INT0 : LAN controller (RTL8019AS) */ + irq_desc[M32R_IRQ_INT0].status = IRQ_DISABLED; + irq_desc[M32R_IRQ_INT0].handler = &mappi_irq_type; + irq_desc[M32R_IRQ_INT0].action = 0; + irq_desc[M32R_IRQ_INT0].depth = 1; + icu_data[M32R_IRQ_INT0].icucr = M32R_ICUCR_IEN|M32R_ICUCR_ISMOD10; + disable_mappi_irq(M32R_IRQ_INT0); +#endif /* CONFIG_M32R_NE2000 */ + + /* MFT2 : system timer */ + irq_desc[M32R_IRQ_MFT2].status = IRQ_DISABLED; + irq_desc[M32R_IRQ_MFT2].handler = &mappi_irq_type; + irq_desc[M32R_IRQ_MFT2].action = 0; + irq_desc[M32R_IRQ_MFT2].depth = 1; + icu_data[M32R_IRQ_MFT2].icucr = M32R_ICUCR_IEN; + disable_mappi_irq(M32R_IRQ_MFT2); + +#ifdef CONFIG_SERIAL_M32R_SIO + /* SIO0_R : uart receive data */ + irq_desc[M32R_IRQ_SIO0_R].status = IRQ_DISABLED; + irq_desc[M32R_IRQ_SIO0_R].handler = &mappi_irq_type; + irq_desc[M32R_IRQ_SIO0_R].action = 0; + irq_desc[M32R_IRQ_SIO0_R].depth = 1; + icu_data[M32R_IRQ_SIO0_R].icucr = 0; + disable_mappi_irq(M32R_IRQ_SIO0_R); + + /* SIO0_S : uart send data */ + irq_desc[M32R_IRQ_SIO0_S].status = IRQ_DISABLED; + irq_desc[M32R_IRQ_SIO0_S].handler = &mappi_irq_type; + irq_desc[M32R_IRQ_SIO0_S].action = 0; + irq_desc[M32R_IRQ_SIO0_S].depth = 1; + icu_data[M32R_IRQ_SIO0_S].icucr = 0; + disable_mappi_irq(M32R_IRQ_SIO0_S); + + /* SIO1_R : uart receive data */ + irq_desc[M32R_IRQ_SIO1_R].status = IRQ_DISABLED; + irq_desc[M32R_IRQ_SIO1_R].handler = &mappi_irq_type; + irq_desc[M32R_IRQ_SIO1_R].action = 0; + irq_desc[M32R_IRQ_SIO1_R].depth = 1; + icu_data[M32R_IRQ_SIO1_R].icucr = 0; + disable_mappi_irq(M32R_IRQ_SIO1_R); + + /* SIO1_S : uart send data */ + irq_desc[M32R_IRQ_SIO1_S].status = IRQ_DISABLED; + irq_desc[M32R_IRQ_SIO1_S].handler = &mappi_irq_type; + irq_desc[M32R_IRQ_SIO1_S].action = 0; + irq_desc[M32R_IRQ_SIO1_S].depth = 1; + icu_data[M32R_IRQ_SIO1_S].icucr = 0; + disable_mappi_irq(M32R_IRQ_SIO1_S); +#endif /* CONFIG_SERIAL_M32R_SIO */ + +#if defined(CONFIG_M32RPCC) + /* INT1 : pccard0 interrupt */ + irq_desc[M32R_IRQ_INT1].status = IRQ_DISABLED; + irq_desc[M32R_IRQ_INT1].handler = &mappi_irq_type; + irq_desc[M32R_IRQ_INT1].action = 0; + irq_desc[M32R_IRQ_INT1].depth = 1; + icu_data[M32R_IRQ_INT1].icucr = M32R_ICUCR_IEN | M32R_ICUCR_ISMOD00; + disable_mappi_irq(M32R_IRQ_INT1); + + /* INT2 : pccard1 interrupt */ + irq_desc[M32R_IRQ_INT2].status = IRQ_DISABLED; + irq_desc[M32R_IRQ_INT2].handler = &mappi_irq_type; + irq_desc[M32R_IRQ_INT2].action = 0; + irq_desc[M32R_IRQ_INT2].depth = 1; + icu_data[M32R_IRQ_INT2].icucr = M32R_ICUCR_IEN | M32R_ICUCR_ISMOD00; + disable_mappi_irq(M32R_IRQ_INT2); +#endif /* CONFIG_M32RPCC */ +} diff --git a/arch/m32r/kernel/setup_mappi2.c b/arch/m32r/kernel/setup_mappi2.c new file mode 100644 index 000000000..92eb06e85 --- /dev/null +++ b/arch/m32r/kernel/setup_mappi2.c @@ -0,0 +1,212 @@ +/* + * linux/arch/m32r/kernel/setup_mappi.c + * + * Setup routines for Renesas MAPPI-II(M3A-ZA36) Board + * + * Copyright (c) 2001, 2002 Hiroyuki Kondo, Hirokazu Takata, + * Hitoshi Yamamoto, Mamoru Sakugawa + */ + +#include +#include +#include +#include +#include + +#include +#include +#include + +#define irq2port(x) (M32R_ICU_CR1_PORTL + ((x - 1) * sizeof(unsigned long))) + +#ifndef CONFIG_SMP +typedef struct { + unsigned long icucr; /* ICU Control Register */ +} icu_data_t; +#endif /* CONFIG_SMP */ + +icu_data_t icu_data[NR_IRQS]; + +static void disable_mappi2_irq(unsigned int irq) +{ + unsigned long port, data; + + if ((irq == 0) ||(irq >= NR_IRQS)) { + printk("bad irq 0x%08x\n", irq); + return; + } + port = irq2port(irq); + data = icu_data[irq].icucr|M32R_ICUCR_ILEVEL7; + outl(data, port); +} + +static void enable_mappi2_irq(unsigned int irq) +{ + unsigned long port, data; + + if ((irq == 0) ||(irq >= NR_IRQS)) { + printk("bad irq 0x%08x\n", irq); + return; + } + port = irq2port(irq); + data = icu_data[irq].icucr|M32R_ICUCR_IEN|M32R_ICUCR_ILEVEL6; + outl(data, port); +} + +static void mask_and_ack_mappi2(unsigned int irq) +{ + disable_mappi2_irq(irq); +} + +static void end_mappi2_irq(unsigned int irq) +{ + enable_mappi2_irq(irq); +} + +static unsigned int startup_mappi2_irq(unsigned int irq) +{ + enable_mappi2_irq(irq); + return (0); +} + +static void shutdown_mappi2_irq(unsigned int irq) +{ + unsigned long port; + + port = irq2port(irq); + outl(M32R_ICUCR_ILEVEL7, port); +} + +static struct hw_interrupt_type mappi2_irq_type = +{ + "MAPPI2-IRQ", + startup_mappi2_irq, + shutdown_mappi2_irq, + enable_mappi2_irq, + disable_mappi2_irq, + mask_and_ack_mappi2, + end_mappi2_irq +}; + +void __init init_IRQ(void) +{ +#if defined(CONFIG_SMC91X) + /* INT0 : LAN controller (SMC91111) */ + irq_desc[M32R_IRQ_INT0].status = IRQ_DISABLED; + irq_desc[M32R_IRQ_INT0].handler = &mappi2_irq_type; + irq_desc[M32R_IRQ_INT0].action = 0; + irq_desc[M32R_IRQ_INT0].depth = 1; + icu_data[M32R_IRQ_INT0].icucr = M32R_ICUCR_IEN|M32R_ICUCR_ISMOD10; + disable_mappi2_irq(M32R_IRQ_INT0); +#endif /* CONFIG_SMC91X */ + + /* MFT2 : system timer */ + irq_desc[M32R_IRQ_MFT2].status = IRQ_DISABLED; + irq_desc[M32R_IRQ_MFT2].handler = &mappi2_irq_type; + irq_desc[M32R_IRQ_MFT2].action = 0; + irq_desc[M32R_IRQ_MFT2].depth = 1; + icu_data[M32R_IRQ_MFT2].icucr = M32R_ICUCR_IEN; + disable_mappi2_irq(M32R_IRQ_MFT2); + +#ifdef CONFIG_SERIAL_M32R_SIO + /* SIO0_R : uart receive data */ + irq_desc[M32R_IRQ_SIO0_R].status = IRQ_DISABLED; + irq_desc[M32R_IRQ_SIO0_R].handler = &mappi2_irq_type; + irq_desc[M32R_IRQ_SIO0_R].action = 0; + irq_desc[M32R_IRQ_SIO0_R].depth = 1; + icu_data[M32R_IRQ_SIO0_R].icucr = 0; + disable_mappi2_irq(M32R_IRQ_SIO0_R); + + /* SIO0_S : uart send data */ + irq_desc[M32R_IRQ_SIO0_S].status = IRQ_DISABLED; + irq_desc[M32R_IRQ_SIO0_S].handler = &mappi2_irq_type; + irq_desc[M32R_IRQ_SIO0_S].action = 0; + irq_desc[M32R_IRQ_SIO0_S].depth = 1; + icu_data[M32R_IRQ_SIO0_S].icucr = 0; + disable_mappi2_irq(M32R_IRQ_SIO0_S); + /* SIO1_R : uart receive data */ + irq_desc[M32R_IRQ_SIO1_R].status = IRQ_DISABLED; + irq_desc[M32R_IRQ_SIO1_R].handler = &mappi2_irq_type; + irq_desc[M32R_IRQ_SIO1_R].action = 0; + irq_desc[M32R_IRQ_SIO1_R].depth = 1; + icu_data[M32R_IRQ_SIO1_R].icucr = 0; + disable_mappi2_irq(M32R_IRQ_SIO1_R); + + /* SIO1_S : uart send data */ + irq_desc[M32R_IRQ_SIO1_S].status = IRQ_DISABLED; + irq_desc[M32R_IRQ_SIO1_S].handler = &mappi2_irq_type; + irq_desc[M32R_IRQ_SIO1_S].action = 0; + irq_desc[M32R_IRQ_SIO1_S].depth = 1; + icu_data[M32R_IRQ_SIO1_S].icucr = 0; + disable_mappi2_irq(M32R_IRQ_SIO1_S); +#endif /* CONFIG_M32R_USE_DBG_CONSOLE */ + +#if defined(CONFIG_USB) + /* INT1 : USB Host controller interrupt */ + irq_desc[M32R_IRQ_INT1].status = IRQ_DISABLED; + irq_desc[M32R_IRQ_INT1].handler = &mappi2_irq_type; + irq_desc[M32R_IRQ_INT1].action = 0; + irq_desc[M32R_IRQ_INT1].depth = 1; + icu_data[M32R_IRQ_INT1].icucr = M32R_ICUCR_ISMOD01; + disable_mappi2_irq(M32R_IRQ_INT1); +#endif /* CONFIG_USB */ + +#if defined(CONFIG_M32R_CFC) + /* ICUCR40: CFC IREQ */ + irq_desc[PLD_IRQ_CFIREQ].status = IRQ_DISABLED; + irq_desc[PLD_IRQ_CFIREQ].handler = &mappi2_irq_type; + irq_desc[PLD_IRQ_CFIREQ].action = 0; + irq_desc[PLD_IRQ_CFIREQ].depth = 1; /* disable nested irq */ +// icu_data[PLD_IRQ_CFIREQ].icucr = M32R_ICUCR_IEN|M32R_ICUCR_ISMOD00; + icu_data[PLD_IRQ_CFIREQ].icucr = M32R_ICUCR_IEN|M32R_ICUCR_ISMOD01; + disable_mappi2_irq(PLD_IRQ_CFIREQ); + + /* ICUCR41: CFC Insert */ + irq_desc[PLD_IRQ_CFC_INSERT].status = IRQ_DISABLED; + irq_desc[PLD_IRQ_CFC_INSERT].handler = &mappi2_irq_type; + irq_desc[PLD_IRQ_CFC_INSERT].action = 0; + irq_desc[PLD_IRQ_CFC_INSERT].depth = 1; /* disable nested irq */ + icu_data[PLD_IRQ_CFC_INSERT].icucr = M32R_ICUCR_IEN|M32R_ICUCR_ISMOD00; +// icu_data[PLD_IRQ_CFC_INSERT].icucr = 0; + disable_mappi2_irq(PLD_IRQ_CFC_INSERT); + + /* ICUCR42: CFC Eject */ + irq_desc[PLD_IRQ_CFC_EJECT].status = IRQ_DISABLED; + irq_desc[PLD_IRQ_CFC_EJECT].handler = &mappi2_irq_type; + irq_desc[PLD_IRQ_CFC_EJECT].action = 0; + irq_desc[PLD_IRQ_CFC_EJECT].depth = 1; /* disable nested irq */ + icu_data[PLD_IRQ_CFC_EJECT].icucr = M32R_ICUCR_IEN|M32R_ICUCR_ISMOD10; +// icu_data[PLD_IRQ_CFC_EJECT].icucr = 0; + disable_mappi2_irq(PLD_IRQ_CFC_EJECT); + +#endif /* CONFIG_MAPPI2_CFC */ +} + +#define LAN_IOSTART 0x300 +#define LAN_IOEND 0x320 +static struct resource smc91x_resources[] = { + [0] = { + .start = (LAN_IOSTART), + .end = (LAN_IOEND), + .flags = IORESOURCE_MEM, + }, + [1] = { + .start = M32R_IRQ_INT0, + .end = M32R_IRQ_INT0, + .flags = IORESOURCE_IRQ, + } +}; + +static struct platform_device smc91x_device = { + .name = "smc91x", + .id = 0, + .num_resources = ARRAY_SIZE(smc91x_resources), + .resource = smc91x_resources, +}; + +static int __init platform_init(void) +{ + platform_device_register(&smc91x_device); + return 0; +} +arch_initcall(platform_init); diff --git a/arch/m32r/kernel/setup_oaks32r.c b/arch/m32r/kernel/setup_oaks32r.c new file mode 100644 index 000000000..b04834526 --- /dev/null +++ b/arch/m32r/kernel/setup_oaks32r.c @@ -0,0 +1,143 @@ +/* + * linux/arch/m32r/kernel/setup_oaks32r.c + * + * Setup routines for OAKS32R Board + * + * Copyright (c) 2002-2004 Hiroyuki Kondo, Hirokazu Takata, + * Hitoshi Yamamoto, Mamoru Sakugawa + */ + +#include +#include +#include +#include + +#include +#include +#include + +#define irq2port(x) (M32R_ICU_CR1_PORTL + ((x - 1) * sizeof(unsigned long))) + +#ifndef CONFIG_SMP +typedef struct { + unsigned long icucr; /* ICU Control Register */ +} icu_data_t; +#endif /* CONFIG_SMP */ + +icu_data_t icu_data[NR_IRQS]; + +static void disable_oaks32r_irq(unsigned int irq) +{ + unsigned long port, data; + + port = irq2port(irq); + data = icu_data[irq].icucr|M32R_ICUCR_ILEVEL7; + outl(data, port); +} + +static void enable_oaks32r_irq(unsigned int irq) +{ + unsigned long port, data; + + port = irq2port(irq); + data = icu_data[irq].icucr|M32R_ICUCR_IEN|M32R_ICUCR_ILEVEL6; + outl(data, port); +} + +static void mask_and_ack_mappi(unsigned int irq) +{ + disable_oaks32r_irq(irq); +} + +static void end_oaks32r_irq(unsigned int irq) +{ + enable_oaks32r_irq(irq); +} + +static unsigned int startup_oaks32r_irq(unsigned int irq) +{ + enable_oaks32r_irq(irq); + return (0); +} + +static void shutdown_oaks32r_irq(unsigned int irq) +{ + unsigned long port; + + port = irq2port(irq); + outl(M32R_ICUCR_ILEVEL7, port); +} + +static struct hw_interrupt_type oaks32r_irq_type = +{ + "OAKS32R-IRQ", + startup_oaks32r_irq, + shutdown_oaks32r_irq, + enable_oaks32r_irq, + disable_oaks32r_irq, + mask_and_ack_mappi, + end_oaks32r_irq +}; + +void __init init_IRQ(void) +{ + static int once = 0; + + if (once) + return; + else + once++; + +#ifdef CONFIG_NE2000 + /* INT3 : LAN controller (RTL8019AS) */ + irq_desc[M32R_IRQ_INT3].status = IRQ_DISABLED; + irq_desc[M32R_IRQ_INT3].handler = &oaks32r_irq_type; + irq_desc[M32R_IRQ_INT3].action = 0; + irq_desc[M32R_IRQ_INT3].depth = 1; + icu_data[M32R_IRQ_INT3].icucr = M32R_ICUCR_IEN|M32R_ICUCR_ISMOD10; + disable_oaks32r_irq(M32R_IRQ_INT3); +#endif /* CONFIG_M32R_NE2000 */ + + /* MFT2 : system timer */ + irq_desc[M32R_IRQ_MFT2].status = IRQ_DISABLED; + irq_desc[M32R_IRQ_MFT2].handler = &oaks32r_irq_type; + irq_desc[M32R_IRQ_MFT2].action = 0; + irq_desc[M32R_IRQ_MFT2].depth = 1; + icu_data[M32R_IRQ_MFT2].icucr = M32R_ICUCR_IEN; + disable_oaks32r_irq(M32R_IRQ_MFT2); + +#ifdef CONFIG_SERIAL_M32R_SIO + /* SIO0_R : uart receive data */ + irq_desc[M32R_IRQ_SIO0_R].status = IRQ_DISABLED; + irq_desc[M32R_IRQ_SIO0_R].handler = &oaks32r_irq_type; + irq_desc[M32R_IRQ_SIO0_R].action = 0; + irq_desc[M32R_IRQ_SIO0_R].depth = 1; + icu_data[M32R_IRQ_SIO0_R].icucr = 0; + disable_oaks32r_irq(M32R_IRQ_SIO0_R); + + /* SIO0_S : uart send data */ + irq_desc[M32R_IRQ_SIO0_S].status = IRQ_DISABLED; + irq_desc[M32R_IRQ_SIO0_S].handler = &oaks32r_irq_type; + irq_desc[M32R_IRQ_SIO0_S].action = 0; + irq_desc[M32R_IRQ_SIO0_S].depth = 1; + icu_data[M32R_IRQ_SIO0_S].icucr = 0; + disable_oaks32r_irq(M32R_IRQ_SIO0_S); + + /* SIO1_R : uart receive data */ + irq_desc[M32R_IRQ_SIO1_R].status = IRQ_DISABLED; + irq_desc[M32R_IRQ_SIO1_R].handler = &oaks32r_irq_type; + irq_desc[M32R_IRQ_SIO1_R].action = 0; + irq_desc[M32R_IRQ_SIO1_R].depth = 1; + icu_data[M32R_IRQ_SIO1_R].icucr = 0; + disable_oaks32r_irq(M32R_IRQ_SIO1_R); + + /* SIO1_S : uart send data */ + irq_desc[M32R_IRQ_SIO1_S].status = IRQ_DISABLED; + irq_desc[M32R_IRQ_SIO1_S].handler = &oaks32r_irq_type; + irq_desc[M32R_IRQ_SIO1_S].action = 0; + irq_desc[M32R_IRQ_SIO1_S].depth = 1; + icu_data[M32R_IRQ_SIO1_S].icucr = 0; + disable_oaks32r_irq(M32R_IRQ_SIO1_S); +#endif /* CONFIG_SERIAL_M32R_SIO */ + +} diff --git a/arch/m32r/kernel/setup_opsput.c b/arch/m32r/kernel/setup_opsput.c new file mode 100644 index 000000000..17a4d3e52 --- /dev/null +++ b/arch/m32r/kernel/setup_opsput.c @@ -0,0 +1,482 @@ +/* + * linux/arch/m32r/kernel/setup_opsput.c + * + * Setup routines for Renesas OPSPUT Board + * + * Copyright (c) 2002-2004 + * Hiroyuki Kondo, Hirokazu Takata, + * Hitoshi Yamamoto, Takeo Takahashi, Mamoru Sakugawa + * + * 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 +#include +#include +#include +#include + +#include +#include +#include + +/* + * OPSP Interrupt Control Unit (Level 1) + */ +#define irq2port(x) (M32R_ICU_CR1_PORTL + ((x - 1) * sizeof(unsigned long))) + +#ifndef CONFIG_SMP +typedef struct { + unsigned long icucr; /* ICU Control Register */ +} icu_data_t; +#endif /* CONFIG_SMP */ + +static icu_data_t icu_data[OPSPUT_NUM_CPU_IRQ]; + +static void disable_opsput_irq(unsigned int irq) +{ + unsigned long port, data; + + port = irq2port(irq); + data = icu_data[irq].icucr|M32R_ICUCR_ILEVEL7; + outl(data, port); +} + +static void enable_opsput_irq(unsigned int irq) +{ + unsigned long port, data; + + port = irq2port(irq); + data = icu_data[irq].icucr|M32R_ICUCR_IEN|M32R_ICUCR_ILEVEL6; + outl(data, port); +} + +static void mask_and_ack_opsput(unsigned int irq) +{ + disable_opsput_irq(irq); +} + +static void end_opsput_irq(unsigned int irq) +{ + enable_opsput_irq(irq); +} + +static unsigned int startup_opsput_irq(unsigned int irq) +{ + enable_opsput_irq(irq); + return (0); +} + +static void shutdown_opsput_irq(unsigned int irq) +{ + unsigned long port; + + port = irq2port(irq); + outl(M32R_ICUCR_ILEVEL7, port); +} + +static struct hw_interrupt_type opsput_irq_type = +{ + "OPSPUT-IRQ", + startup_opsput_irq, + shutdown_opsput_irq, + enable_opsput_irq, + disable_opsput_irq, + mask_and_ack_opsput, + end_opsput_irq +}; + +/* + * Interrupt Control Unit of PLD on OPSPUT (Level 2) + */ +#define irq2pldirq(x) ((x) - OPSPUT_PLD_IRQ_BASE) +#define pldirq2port(x) (unsigned long)((int)PLD_ICUCR1 + \ + (((x) - 1) * sizeof(unsigned short))) + +typedef struct { + unsigned short icucr; /* ICU Control Register */ +} pld_icu_data_t; + +static pld_icu_data_t pld_icu_data[OPSPUT_NUM_PLD_IRQ]; + +static void disable_opsput_pld_irq(unsigned int irq) +{ + unsigned long port, data; + unsigned int pldirq; + + pldirq = irq2pldirq(irq); +// disable_opsput_irq(M32R_IRQ_INT1); + port = pldirq2port(pldirq); + data = pld_icu_data[pldirq].icucr|PLD_ICUCR_ILEVEL7; + outw(data, port); +} + +static void enable_opsput_pld_irq(unsigned int irq) +{ + unsigned long port, data; + unsigned int pldirq; + + pldirq = irq2pldirq(irq); +// enable_opsput_irq(M32R_IRQ_INT1); + port = pldirq2port(pldirq); + data = pld_icu_data[pldirq].icucr|PLD_ICUCR_IEN|PLD_ICUCR_ILEVEL6; + outw(data, port); +} + +static void mask_and_ack_opsput_pld(unsigned int irq) +{ + disable_opsput_pld_irq(irq); +// mask_and_ack_opsput(M32R_IRQ_INT1); +} + +static void end_opsput_pld_irq(unsigned int irq) +{ + enable_opsput_pld_irq(irq); + end_opsput_irq(M32R_IRQ_INT1); +} + +static unsigned int startup_opsput_pld_irq(unsigned int irq) +{ + enable_opsput_pld_irq(irq); + return (0); +} + +static void shutdown_opsput_pld_irq(unsigned int irq) +{ + unsigned long port; + unsigned int pldirq; + + pldirq = irq2pldirq(irq); +// shutdown_opsput_irq(M32R_IRQ_INT1); + port = pldirq2port(pldirq); + outw(PLD_ICUCR_ILEVEL7, port); +} + +static struct hw_interrupt_type opsput_pld_irq_type = +{ + "OPSPUT-PLD-IRQ", + startup_opsput_pld_irq, + shutdown_opsput_pld_irq, + enable_opsput_pld_irq, + disable_opsput_pld_irq, + mask_and_ack_opsput_pld, + end_opsput_pld_irq +}; + +/* + * Interrupt Control Unit of PLD on OPSPUT-LAN (Level 2) + */ +#define irq2lanpldirq(x) ((x) - OPSPUT_LAN_PLD_IRQ_BASE) +#define lanpldirq2port(x) (unsigned long)((int)OPSPUT_LAN_ICUCR1 + \ + (((x) - 1) * sizeof(unsigned short))) + +static pld_icu_data_t lanpld_icu_data[OPSPUT_NUM_LAN_PLD_IRQ]; + +static void disable_opsput_lanpld_irq(unsigned int irq) +{ + unsigned long port, data; + unsigned int pldirq; + + pldirq = irq2lanpldirq(irq); + port = lanpldirq2port(pldirq); + data = lanpld_icu_data[pldirq].icucr|PLD_ICUCR_ILEVEL7; + outw(data, port); +} + +static void enable_opsput_lanpld_irq(unsigned int irq) +{ + unsigned long port, data; + unsigned int pldirq; + + pldirq = irq2lanpldirq(irq); + port = lanpldirq2port(pldirq); + data = lanpld_icu_data[pldirq].icucr|PLD_ICUCR_IEN|PLD_ICUCR_ILEVEL6; + outw(data, port); +} + +static void mask_and_ack_opsput_lanpld(unsigned int irq) +{ + disable_opsput_lanpld_irq(irq); +} + +static void end_opsput_lanpld_irq(unsigned int irq) +{ + enable_opsput_lanpld_irq(irq); + end_opsput_irq(M32R_IRQ_INT0); +} + +static unsigned int startup_opsput_lanpld_irq(unsigned int irq) +{ + enable_opsput_lanpld_irq(irq); + return (0); +} + +static void shutdown_opsput_lanpld_irq(unsigned int irq) +{ + unsigned long port; + unsigned int pldirq; + + pldirq = irq2lanpldirq(irq); + port = lanpldirq2port(pldirq); + outw(PLD_ICUCR_ILEVEL7, port); +} + +static struct hw_interrupt_type opsput_lanpld_irq_type = +{ + "OPSPUT-PLD-LAN-IRQ", + startup_opsput_lanpld_irq, + shutdown_opsput_lanpld_irq, + enable_opsput_lanpld_irq, + disable_opsput_lanpld_irq, + mask_and_ack_opsput_lanpld, + end_opsput_lanpld_irq +}; + +/* + * Interrupt Control Unit of PLD on OPSPUT-LCD (Level 2) + */ +#define irq2lcdpldirq(x) ((x) - OPSPUT_LCD_PLD_IRQ_BASE) +#define lcdpldirq2port(x) (unsigned long)((int)OPSPUT_LCD_ICUCR1 + \ + (((x) - 1) * sizeof(unsigned short))) + +static pld_icu_data_t lcdpld_icu_data[OPSPUT_NUM_LCD_PLD_IRQ]; + +static void disable_opsput_lcdpld_irq(unsigned int irq) +{ + unsigned long port, data; + unsigned int pldirq; + + pldirq = irq2lcdpldirq(irq); + port = lcdpldirq2port(pldirq); + data = lcdpld_icu_data[pldirq].icucr|PLD_ICUCR_ILEVEL7; + outw(data, port); +} + +static void enable_opsput_lcdpld_irq(unsigned int irq) +{ + unsigned long port, data; + unsigned int pldirq; + + pldirq = irq2lcdpldirq(irq); + port = lcdpldirq2port(pldirq); + data = lcdpld_icu_data[pldirq].icucr|PLD_ICUCR_IEN|PLD_ICUCR_ILEVEL6; + outw(data, port); +} + +static void mask_and_ack_opsput_lcdpld(unsigned int irq) +{ + disable_opsput_lcdpld_irq(irq); +} + +static void end_opsput_lcdpld_irq(unsigned int irq) +{ + enable_opsput_lcdpld_irq(irq); + end_opsput_irq(M32R_IRQ_INT2); +} + +static unsigned int startup_opsput_lcdpld_irq(unsigned int irq) +{ + enable_opsput_lcdpld_irq(irq); + return (0); +} + +static void shutdown_opsput_lcdpld_irq(unsigned int irq) +{ + unsigned long port; + unsigned int pldirq; + + pldirq = irq2lcdpldirq(irq); + port = lcdpldirq2port(pldirq); + outw(PLD_ICUCR_ILEVEL7, port); +} + +static struct hw_interrupt_type opsput_lcdpld_irq_type = +{ + "OPSPUT-PLD-LCD-IRQ", + startup_opsput_lcdpld_irq, + shutdown_opsput_lcdpld_irq, + enable_opsput_lcdpld_irq, + disable_opsput_lcdpld_irq, + mask_and_ack_opsput_lcdpld, + end_opsput_lcdpld_irq +}; + +void __init init_IRQ(void) +{ +#if defined(CONFIG_SMC91X) + /* INT#0: LAN controller on OPSPUT-LAN (SMC91C111)*/ + irq_desc[OPSPUT_LAN_IRQ_LAN].status = IRQ_DISABLED; + irq_desc[OPSPUT_LAN_IRQ_LAN].handler = &opsput_lanpld_irq_type; + irq_desc[OPSPUT_LAN_IRQ_LAN].action = 0; + irq_desc[OPSPUT_LAN_IRQ_LAN].depth = 1; /* disable nested irq */ + lanpld_icu_data[irq2lanpldirq(OPSPUT_LAN_IRQ_LAN)].icucr = PLD_ICUCR_IEN|PLD_ICUCR_ISMOD02; /* "H" edge sense */ + disable_opsput_lanpld_irq(OPSPUT_LAN_IRQ_LAN); +#endif /* CONFIG_SMC91X */ + + /* MFT2 : system timer */ + irq_desc[M32R_IRQ_MFT2].status = IRQ_DISABLED; + irq_desc[M32R_IRQ_MFT2].handler = &opsput_irq_type; + irq_desc[M32R_IRQ_MFT2].action = 0; + irq_desc[M32R_IRQ_MFT2].depth = 1; + icu_data[M32R_IRQ_MFT2].icucr = M32R_ICUCR_IEN; + disable_opsput_irq(M32R_IRQ_MFT2); + + /* SIO0 : receive */ + irq_desc[M32R_IRQ_SIO0_R].status = IRQ_DISABLED; + irq_desc[M32R_IRQ_SIO0_R].handler = &opsput_irq_type; + irq_desc[M32R_IRQ_SIO0_R].action = 0; + irq_desc[M32R_IRQ_SIO0_R].depth = 1; + icu_data[M32R_IRQ_SIO0_R].icucr = 0; + disable_opsput_irq(M32R_IRQ_SIO0_R); + + /* SIO0 : send */ + irq_desc[M32R_IRQ_SIO0_S].status = IRQ_DISABLED; + irq_desc[M32R_IRQ_SIO0_S].handler = &opsput_irq_type; + irq_desc[M32R_IRQ_SIO0_S].action = 0; + irq_desc[M32R_IRQ_SIO0_S].depth = 1; + icu_data[M32R_IRQ_SIO0_S].icucr = 0; + disable_opsput_irq(M32R_IRQ_SIO0_S); + + /* SIO1 : receive */ + irq_desc[M32R_IRQ_SIO1_R].status = IRQ_DISABLED; + irq_desc[M32R_IRQ_SIO1_R].handler = &opsput_irq_type; + irq_desc[M32R_IRQ_SIO1_R].action = 0; + irq_desc[M32R_IRQ_SIO1_R].depth = 1; + icu_data[M32R_IRQ_SIO1_R].icucr = 0; + disable_opsput_irq(M32R_IRQ_SIO1_R); + + /* SIO1 : send */ + irq_desc[M32R_IRQ_SIO1_S].status = IRQ_DISABLED; + irq_desc[M32R_IRQ_SIO1_S].handler = &opsput_irq_type; + irq_desc[M32R_IRQ_SIO1_S].action = 0; + irq_desc[M32R_IRQ_SIO1_S].depth = 1; + icu_data[M32R_IRQ_SIO1_S].icucr = 0; + disable_opsput_irq(M32R_IRQ_SIO1_S); + + /* DMA1 : */ + irq_desc[M32R_IRQ_DMA1].status = IRQ_DISABLED; + irq_desc[M32R_IRQ_DMA1].handler = &opsput_irq_type; + irq_desc[M32R_IRQ_DMA1].action = 0; + irq_desc[M32R_IRQ_DMA1].depth = 1; + icu_data[M32R_IRQ_DMA1].icucr = 0; + disable_opsput_irq(M32R_IRQ_DMA1); + +#ifdef CONFIG_SERIAL_M32R_PLDSIO + /* INT#1: SIO0 Receive on PLD */ + irq_desc[PLD_IRQ_SIO0_RCV].status = IRQ_DISABLED; + irq_desc[PLD_IRQ_SIO0_RCV].handler = &opsput_pld_irq_type; + irq_desc[PLD_IRQ_SIO0_RCV].action = 0; + irq_desc[PLD_IRQ_SIO0_RCV].depth = 1; /* disable nested irq */ + pld_icu_data[irq2pldirq(PLD_IRQ_SIO0_RCV)].icucr = PLD_ICUCR_IEN|PLD_ICUCR_ISMOD03; + disable_opsput_pld_irq(PLD_IRQ_SIO0_RCV); + + /* INT#1: SIO0 Send on PLD */ + irq_desc[PLD_IRQ_SIO0_SND].status = IRQ_DISABLED; + irq_desc[PLD_IRQ_SIO0_SND].handler = &opsput_pld_irq_type; + irq_desc[PLD_IRQ_SIO0_SND].action = 0; + irq_desc[PLD_IRQ_SIO0_SND].depth = 1; /* disable nested irq */ + pld_icu_data[irq2pldirq(PLD_IRQ_SIO0_SND)].icucr = PLD_ICUCR_IEN|PLD_ICUCR_ISMOD03; + disable_opsput_pld_irq(PLD_IRQ_SIO0_SND); +#endif /* CONFIG_SERIAL_M32R_PLDSIO */ + +#if defined(CONFIG_M32R_CFC) + /* INT#1: CFC IREQ on PLD */ + irq_desc[PLD_IRQ_CFIREQ].status = IRQ_DISABLED; + irq_desc[PLD_IRQ_CFIREQ].handler = &opsput_pld_irq_type; + irq_desc[PLD_IRQ_CFIREQ].action = 0; + irq_desc[PLD_IRQ_CFIREQ].depth = 1; /* disable nested irq */ + pld_icu_data[irq2pldirq(PLD_IRQ_CFIREQ)].icucr = PLD_ICUCR_IEN|PLD_ICUCR_ISMOD01; /* 'L' level sense */ + disable_opsput_pld_irq(PLD_IRQ_CFIREQ); + + /* INT#1: CFC Insert on PLD */ + irq_desc[PLD_IRQ_CFC_INSERT].status = IRQ_DISABLED; + irq_desc[PLD_IRQ_CFC_INSERT].handler = &opsput_pld_irq_type; + irq_desc[PLD_IRQ_CFC_INSERT].action = 0; + irq_desc[PLD_IRQ_CFC_INSERT].depth = 1; /* disable nested irq */ + pld_icu_data[irq2pldirq(PLD_IRQ_CFC_INSERT)].icucr = PLD_ICUCR_IEN|PLD_ICUCR_ISMOD00; /* 'L' edge sense */ + disable_opsput_pld_irq(PLD_IRQ_CFC_INSERT); + + /* INT#1: CFC Eject on PLD */ + irq_desc[PLD_IRQ_CFC_EJECT].status = IRQ_DISABLED; + irq_desc[PLD_IRQ_CFC_EJECT].handler = &opsput_pld_irq_type; + irq_desc[PLD_IRQ_CFC_EJECT].action = 0; + irq_desc[PLD_IRQ_CFC_EJECT].depth = 1; /* disable nested irq */ + pld_icu_data[irq2pldirq(PLD_IRQ_CFC_EJECT)].icucr = PLD_ICUCR_IEN|PLD_ICUCR_ISMOD02; /* 'H' edge sense */ + disable_opsput_pld_irq(PLD_IRQ_CFC_EJECT); +#endif /* CONFIG_M32R_CFC */ + + + /* + * INT0# is used for LAN, DIO + * We enable it here. + */ + icu_data[M32R_IRQ_INT0].icucr = M32R_ICUCR_IEN|M32R_ICUCR_ISMOD11; + enable_opsput_irq(M32R_IRQ_INT0); + + /* + * INT1# is used for UART, MMC, CF Controller in FPGA. + * We enable it here. + */ + icu_data[M32R_IRQ_INT1].icucr = M32R_ICUCR_IEN|M32R_ICUCR_ISMOD11; + enable_opsput_irq(M32R_IRQ_INT1); + +#if defined(CONFIG_USB) + outw(USBCR_OTGS, USBCR); /* USBCR: non-OTG */ + + irq_desc[OPSPUT_LCD_IRQ_USB_INT1].status = IRQ_DISABLED; + irq_desc[OPSPUT_LCD_IRQ_USB_INT1].handler = &opsput_lcdpld_irq_type; + irq_desc[OPSPUT_LCD_IRQ_USB_INT1].action = 0; + irq_desc[OPSPUT_LCD_IRQ_USB_INT1].depth = 1; + lcdpld_icu_data[irq2lcdpldirq(OPSPUT_LCD_IRQ_USB_INT1)].icucr = PLD_ICUCR_IEN|PLD_ICUCR_ISMOD01; /* "L" level sense */ + disable_opsput_lcdpld_irq(OPSPUT_LCD_IRQ_USB_INT1); +#endif + /* + * INT2# is used for BAT, USB, AUDIO + * We enable it here. + */ + icu_data[M32R_IRQ_INT2].icucr = M32R_ICUCR_IEN|M32R_ICUCR_ISMOD01; + enable_opsput_irq(M32R_IRQ_INT2); + +//#if defined(CONFIG_M32R_AR_VGA) + /* + * INT3# is used for AR + */ + irq_desc[M32R_IRQ_INT3].status = IRQ_DISABLED; + irq_desc[M32R_IRQ_INT3].handler = &opsput_irq_type; + irq_desc[M32R_IRQ_INT3].action = 0; + irq_desc[M32R_IRQ_INT3].depth = 1; + icu_data[M32R_IRQ_INT3].icucr = M32R_ICUCR_IEN|M32R_ICUCR_ISMOD10; + disable_opsput_irq(M32R_IRQ_INT3); +//#endif /* CONFIG_M32R_ARV */ +} + +#define LAN_IOSTART 0x300 +#define LAN_IOEND 0x320 +static struct resource smc91x_resources[] = { + [0] = { + .start = (LAN_IOSTART), + .end = (LAN_IOEND), + .flags = IORESOURCE_MEM, + }, + [1] = { + .start = OPSPUT_LAN_IRQ_LAN, + .end = OPSPUT_LAN_IRQ_LAN, + .flags = IORESOURCE_IRQ, + } +}; + +static struct platform_device smc91x_device = { + .name = "smc91x", + .id = 0, + .num_resources = ARRAY_SIZE(smc91x_resources), + .resource = smc91x_resources, +}; + +static int __init platform_init(void) +{ + platform_device_register(&smc91x_device); + return 0; +} +arch_initcall(platform_init); diff --git a/arch/m32r/kernel/setup_usrv.c b/arch/m32r/kernel/setup_usrv.c new file mode 100644 index 000000000..fe417be5e --- /dev/null +++ b/arch/m32r/kernel/setup_usrv.c @@ -0,0 +1,256 @@ +/* + * linux/arch/m32r/kernel/setup_usrv.c + * + * Setup routines for MITSUBISHI uServer + * + * Copyright (c) 2001, 2002, 2003 Hiroyuki Kondo, Hirokazu Takata, + * Hitoshi Yamamoto + */ + +#include +#include +#include +#include + +#include +#include +#include + +#define irq2port(x) (M32R_ICU_CR1_PORTL + ((x - 1) * sizeof(unsigned long))) + +#if !defined(CONFIG_SMP) +typedef struct { + unsigned long icucr; /* ICU Control Register */ +} icu_data_t; +#endif /* CONFIG_SMP */ + +icu_data_t icu_data[M32700UT_NUM_CPU_IRQ]; + +static void disable_mappi_irq(unsigned int irq) +{ + unsigned long port, data; + + port = irq2port(irq); + data = icu_data[irq].icucr|M32R_ICUCR_ILEVEL7; + outl(data, port); +} + +static void enable_mappi_irq(unsigned int irq) +{ + unsigned long port, data; + + port = irq2port(irq); + data = icu_data[irq].icucr|M32R_ICUCR_IEN|M32R_ICUCR_ILEVEL6; + outl(data, port); +} + +static void mask_and_ack_mappi(unsigned int irq) +{ + disable_mappi_irq(irq); +} + +static void end_mappi_irq(unsigned int irq) +{ + enable_mappi_irq(irq); +} + +static unsigned int startup_mappi_irq(unsigned int irq) +{ + enable_mappi_irq(irq); + return 0; +} + +static void shutdown_mappi_irq(unsigned int irq) +{ + unsigned long port; + + port = irq2port(irq); + outl(M32R_ICUCR_ILEVEL7, port); +} + +static struct hw_interrupt_type mappi_irq_type = +{ + "M32700-IRQ", + startup_mappi_irq, + shutdown_mappi_irq, + enable_mappi_irq, + disable_mappi_irq, + mask_and_ack_mappi, + end_mappi_irq +}; + +/* + * Interrupt Control Unit of PLD on M32700UT (Level 2) + */ +#define irq2pldirq(x) ((x) - M32700UT_PLD_IRQ_BASE) +#define pldirq2port(x) (unsigned long)((int)PLD_ICUCR1 + \ + (((x) - 1) * sizeof(unsigned short))) + +typedef struct { + unsigned short icucr; /* ICU Control Register */ +} pld_icu_data_t; + +static pld_icu_data_t pld_icu_data[M32700UT_NUM_PLD_IRQ]; + +static void disable_m32700ut_pld_irq(unsigned int irq) +{ + unsigned long port, data; + unsigned int pldirq; + + pldirq = irq2pldirq(irq); + port = pldirq2port(pldirq); + data = pld_icu_data[pldirq].icucr|PLD_ICUCR_ILEVEL7; + outw(data, port); +} + +static void enable_m32700ut_pld_irq(unsigned int irq) +{ + unsigned long port, data; + unsigned int pldirq; + + pldirq = irq2pldirq(irq); + port = pldirq2port(pldirq); + data = pld_icu_data[pldirq].icucr|PLD_ICUCR_IEN|PLD_ICUCR_ILEVEL6; + outw(data, port); +} + +static void mask_and_ack_m32700ut_pld(unsigned int irq) +{ + disable_m32700ut_pld_irq(irq); +} + +static void end_m32700ut_pld_irq(unsigned int irq) +{ + enable_m32700ut_pld_irq(irq); + end_mappi_irq(M32R_IRQ_INT1); +} + +static unsigned int startup_m32700ut_pld_irq(unsigned int irq) +{ + enable_m32700ut_pld_irq(irq); + return 0; +} + +static void shutdown_m32700ut_pld_irq(unsigned int irq) +{ + unsigned long port; + unsigned int pldirq; + + pldirq = irq2pldirq(irq); + port = pldirq2port(pldirq); + outw(PLD_ICUCR_ILEVEL7, port); +} + +static struct hw_interrupt_type m32700ut_pld_irq_type = +{ + "USRV-PLD-IRQ", + startup_m32700ut_pld_irq, + shutdown_m32700ut_pld_irq, + enable_m32700ut_pld_irq, + disable_m32700ut_pld_irq, + mask_and_ack_m32700ut_pld, + end_m32700ut_pld_irq +}; + +void __init init_IRQ(void) +{ + static int once = 0; + int i; + + if (once) + return; + else + once++; + + /* MFT2 : system timer */ + irq_desc[M32R_IRQ_MFT2].status = IRQ_DISABLED; + irq_desc[M32R_IRQ_MFT2].handler = &mappi_irq_type; + irq_desc[M32R_IRQ_MFT2].action = 0; + irq_desc[M32R_IRQ_MFT2].depth = 1; + icu_data[M32R_IRQ_MFT2].icucr = M32R_ICUCR_IEN; + disable_mappi_irq(M32R_IRQ_MFT2); + +#if defined(CONFIG_SERIAL_M32R_SIO) + /* SIO0_R : uart receive data */ + irq_desc[M32R_IRQ_SIO0_R].status = IRQ_DISABLED; + irq_desc[M32R_IRQ_SIO0_R].handler = &mappi_irq_type; + irq_desc[M32R_IRQ_SIO0_R].action = 0; + irq_desc[M32R_IRQ_SIO0_R].depth = 1; + icu_data[M32R_IRQ_SIO0_R].icucr = 0; + disable_mappi_irq(M32R_IRQ_SIO0_R); + + /* SIO0_S : uart send data */ + irq_desc[M32R_IRQ_SIO0_S].status = IRQ_DISABLED; + irq_desc[M32R_IRQ_SIO0_S].handler = &mappi_irq_type; + irq_desc[M32R_IRQ_SIO0_S].action = 0; + irq_desc[M32R_IRQ_SIO0_S].depth = 1; + icu_data[M32R_IRQ_SIO0_S].icucr = 0; + disable_mappi_irq(M32R_IRQ_SIO0_S); + + /* SIO1_R : uart receive data */ + irq_desc[M32R_IRQ_SIO1_R].status = IRQ_DISABLED; + irq_desc[M32R_IRQ_SIO1_R].handler = &mappi_irq_type; + irq_desc[M32R_IRQ_SIO1_R].action = 0; + irq_desc[M32R_IRQ_SIO1_R].depth = 1; + icu_data[M32R_IRQ_SIO1_R].icucr = 0; + disable_mappi_irq(M32R_IRQ_SIO1_R); + + /* SIO1_S : uart send data */ + irq_desc[M32R_IRQ_SIO1_S].status = IRQ_DISABLED; + irq_desc[M32R_IRQ_SIO1_S].handler = &mappi_irq_type; + irq_desc[M32R_IRQ_SIO1_S].action = 0; + irq_desc[M32R_IRQ_SIO1_S].depth = 1; + icu_data[M32R_IRQ_SIO1_S].icucr = 0; + disable_mappi_irq(M32R_IRQ_SIO1_S); +#endif /* CONFIG_SERIAL_M32R_SIO */ + + /* INT#67-#71: CFC#0 IREQ on PLD */ + for (i = 0 ; i < CONFIG_CFC_NUM ; i++ ) { + irq_desc[PLD_IRQ_CF0 + i].status = IRQ_DISABLED; + irq_desc[PLD_IRQ_CF0 + i].handler = &m32700ut_pld_irq_type; + irq_desc[PLD_IRQ_CF0 + i].action = 0; + irq_desc[PLD_IRQ_CF0 + i].depth = 1; /* disable nested irq */ + pld_icu_data[irq2pldirq(PLD_IRQ_CF0 + i)].icucr + = PLD_ICUCR_ISMOD01; /* 'L' level sense */ + disable_m32700ut_pld_irq(PLD_IRQ_CF0 + i); + } + +#if defined(CONFIG_SERIAL_8250) || defined(CONFIG_SERIAL_8250_MODULE) + /* INT#76: 16552D#0 IREQ on PLD */ + irq_desc[PLD_IRQ_UART0].status = IRQ_DISABLED; + irq_desc[PLD_IRQ_UART0].handler = &m32700ut_pld_irq_type; + irq_desc[PLD_IRQ_UART0].action = 0; + irq_desc[PLD_IRQ_UART0].depth = 1; /* disable nested irq */ + pld_icu_data[irq2pldirq(PLD_IRQ_UART0)].icucr + = PLD_ICUCR_ISMOD03; /* 'H' level sense */ + disable_m32700ut_pld_irq(PLD_IRQ_UART0); + + /* INT#77: 16552D#1 IREQ on PLD */ + irq_desc[PLD_IRQ_UART1].status = IRQ_DISABLED; + irq_desc[PLD_IRQ_UART1].handler = &m32700ut_pld_irq_type; + irq_desc[PLD_IRQ_UART1].action = 0; + irq_desc[PLD_IRQ_UART1].depth = 1; /* disable nested irq */ + pld_icu_data[irq2pldirq(PLD_IRQ_UART1)].icucr + = PLD_ICUCR_ISMOD03; /* 'H' level sense */ + disable_m32700ut_pld_irq(PLD_IRQ_UART1); +#endif /* CONFIG_SERIAL_8250 || CONFIG_SERIAL_8250_MODULE */ + +#if defined(CONFIG_IDC_AK4524) || defined(CONFIG_IDC_AK4524_MODULE) + /* INT#80: AK4524 IREQ on PLD */ + irq_desc[PLD_IRQ_SNDINT].status = IRQ_DISABLED; + irq_desc[PLD_IRQ_SNDINT].handler = &m32700ut_pld_irq_type; + irq_desc[PLD_IRQ_SNDINT].action = 0; + irq_desc[PLD_IRQ_SNDINT].depth = 1; /* disable nested irq */ + pld_icu_data[irq2pldirq(PLD_IRQ_SNDINT)].icucr + = PLD_ICUCR_ISMOD01; /* 'L' level sense */ + disable_m32700ut_pld_irq(PLD_IRQ_SNDINT); +#endif /* CONFIG_IDC_AK4524 || CONFIG_IDC_AK4524_MODULE */ + + /* + * INT1# is used for UART, MMC, CF Controller in FPGA. + * We enable it here. + */ + icu_data[M32R_IRQ_INT1].icucr = M32R_ICUCR_ISMOD11; + enable_mappi_irq(M32R_IRQ_INT1); +} + diff --git a/arch/m32r/kernel/signal.c b/arch/m32r/kernel/signal.c new file mode 100644 index 000000000..cbfa6748e --- /dev/null +++ b/arch/m32r/kernel/signal.c @@ -0,0 +1,621 @@ +/* + * linux/arch/m32r/kernel/signal.c + * + * Copyright (c) 2003 Hitoshi Yamamoto + * + * Taken from i386 version. + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define DEBUG_SIG 0 + +#define _BLOCKABLE (~(sigmask(SIGKILL) | sigmask(SIGSTOP))) + +int do_signal(struct pt_regs *, sigset_t *); + +/* + * Atomically swap in the new signal mask, and wait for a signal. + */ +asmlinkage int +sys_sigsuspend(old_sigset_t mask, unsigned long r1, + unsigned long r2, unsigned long r3, unsigned long r4, + unsigned long r5, unsigned long r6, struct pt_regs regs) +{ + sigset_t saveset; + + mask &= _BLOCKABLE; + spin_lock_irq(¤t->sighand->siglock); + saveset = current->blocked; + siginitset(¤t->blocked, mask); + recalc_sigpending(); + spin_unlock_irq(¤t->sighand->siglock); + + regs.r0 = -EINTR; + while (1) { + current->state = TASK_INTERRUPTIBLE; + schedule(); + if (do_signal(®s, &saveset)) + return regs.r0; + } +} + +asmlinkage int +sys_rt_sigsuspend(sigset_t *unewset, size_t sigsetsize, + unsigned long r2, unsigned long r3, unsigned long r4, + unsigned long r5, unsigned long r6, struct pt_regs regs) +{ + sigset_t saveset, newset; + + /* XXX: Don't preclude handling different sized sigset_t's. */ + if (sigsetsize != sizeof(sigset_t)) + return -EINVAL; + + if (copy_from_user(&newset, unewset, sizeof(newset))) + return -EFAULT; + sigdelsetmask(&newset, ~_BLOCKABLE); + + spin_lock_irq(¤t->sighand->siglock); + saveset = current->blocked; + current->blocked = newset; + recalc_sigpending(); + spin_unlock_irq(¤t->sighand->siglock); + + regs.r0 = -EINTR; + while (1) { + current->state = TASK_INTERRUPTIBLE; + schedule(); + if (do_signal(®s, &saveset)) + return regs.r0; + } +} + +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 (verify_area(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 (verify_area(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(const stack_t __user *uss, stack_t __user *uoss, + unsigned long r2, unsigned long r3, unsigned long r4, + unsigned long r5, unsigned long r6, struct pt_regs regs) +{ + return do_sigaltstack(uss, uoss, regs.spu); +} + + +/* + * Do a signal return; undo the signal stack. + */ + +struct sigframe +{ +// char *pretcode; + int sig; + struct sigcontext sc; +// struct _fpstate fpstate; + unsigned long extramask[_NSIG_WORDS-1]; + char retcode[4]; +}; + +struct rt_sigframe +{ +// char *pretcode; + int sig; + struct siginfo *pinfo; + void *puc; + struct siginfo info; + struct ucontext uc; +// struct _fpstate fpstate; + char retcode[8]; +}; + +static int +restore_sigcontext(struct pt_regs *regs, struct sigcontext __user *sc, + int *r0_p) +{ + unsigned int err = 0; + + /* Always make any pending restarted system calls return -EINTR */ + current_thread_info()->restart_block.fn = do_no_restart_syscall; + +#define COPY(x) err |= __get_user(regs->x, &sc->sc_##x) + COPY(r4); + COPY(r5); + COPY(r6); + COPY(pt_regs); + /* COPY(r0); Skip r0 */ + COPY(r1); + COPY(r2); + COPY(r3); + COPY(r7); + COPY(r8); + COPY(r9); + COPY(r10); + COPY(r11); + COPY(r12); +#if defined(CONFIG_ISA_M32R2) && defined(CONFIG_ISA_DSP_LEVEL2) + COPY(acc0h); + COPY(acc0l); + COPY(acc1h); + COPY(acc1l); +#elif defined(CONFIG_ISA_M32R2) || defined(CONFIG_ISA_M32R) + COPY(acch); + COPY(accl); +#else +#error unknown isa configuration +#endif + COPY(psw); + COPY(bpc); + COPY(bbpsw); + COPY(bbpc); + COPY(spu); + COPY(fp); + COPY(lr); + COPY(spi); +#undef COPY + + regs->syscall_nr = -1; /* disable syscall checks */ + err |= __get_user(*r0_p, &sc->sc_r0); + + return err; +} + +asmlinkage int +sys_sigreturn(unsigned long r0, unsigned long r1, + unsigned long r2, unsigned long r3, unsigned long r4, + unsigned long r5, unsigned long r6, struct pt_regs regs) +{ + struct sigframe __user *frame = (struct sigframe __user *)regs.spu; + sigset_t set; + int result; + + if (verify_area(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(¤t->sighand->siglock); + current->blocked = set; + recalc_sigpending(); + spin_unlock_irq(¤t->sighand->siglock); + + if (restore_sigcontext(®s, &frame->sc, &result)) + goto badframe; + return result; + +badframe: + force_sig(SIGSEGV, current); + return 0; +} + +asmlinkage int +sys_rt_sigreturn(unsigned long r0, unsigned long r1, + unsigned long r2, unsigned long r3, unsigned long r4, + unsigned long r5, unsigned long r6, struct pt_regs regs) +{ + struct rt_sigframe __user *frame = (struct rt_sigframe __user *)regs.spu; + sigset_t set; + stack_t st; + int result; + + if (verify_area(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(¤t->sighand->siglock); + current->blocked = set; + recalc_sigpending(); + spin_unlock_irq(¤t->sighand->siglock); + + if (restore_sigcontext(®s, &frame->uc.uc_mcontext, &result)) + goto badframe; + + if (__copy_from_user(&st, &frame->uc.uc_stack, sizeof(st))) + goto badframe; + /* It is more difficult to avoid calling this function than to + call it and ignore errors. */ + do_sigaltstack(&st, NULL, regs.spu); + + return result; + +badframe: + force_sig(SIGSEGV, current); + return 0; +} + +/* + * Set up a signal frame. + */ + +static int +setup_sigcontext(struct sigcontext __user *sc, struct pt_regs *regs, + unsigned long mask) +{ + int err = 0; + +#define COPY(x) err |= __put_user(regs->x, &sc->sc_##x) + COPY(r4); + COPY(r5); + COPY(r6); + COPY(pt_regs); + COPY(r0); + COPY(r1); + COPY(r2); + COPY(r3); + COPY(r7); + COPY(r8); + COPY(r9); + COPY(r10); + COPY(r11); + COPY(r12); +#if defined(CONFIG_ISA_M32R2) && defined(CONFIG_ISA_DSP_LEVEL2) + COPY(acc0h); + COPY(acc0l); + COPY(acc1h); + COPY(acc1l); +#elif defined(CONFIG_ISA_M32R2) || defined(CONFIG_ISA_M32R) + COPY(acch); + COPY(accl); +#else +#error unknown isa configuration +#endif + COPY(psw); + COPY(bpc); + COPY(bbpsw); + COPY(bbpc); + COPY(spu); + COPY(fp); + COPY(lr); + COPY(spi); +#undef COPY + + err |= __put_user(mask, &sc->oldmask); + + return err; +} + +/* + * Determine which stack to use.. + */ +static inline void __user * +get_sigframe(struct k_sigaction *ka, unsigned long sp, size_t frame_size) +{ + /* 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 *)((sp - frame_size) & -8ul); +} + +static void setup_frame(int sig, struct k_sigaction *ka, + sigset_t *set, struct pt_regs *regs) +{ + struct sigframe __user *frame; + int err = 0; + int signal; + + frame = get_sigframe(ka, regs->spu, sizeof(*frame)); + + if (!access_ok(VERIFY_WRITE, frame, sizeof(*frame))) + goto give_sigsegv; + + signal = current_thread_info()->exec_domain + && current_thread_info()->exec_domain->signal_invmap + && sig < 32 + ? current_thread_info()->exec_domain->signal_invmap[sig] + : sig; + + err |= __put_user(signal, &frame->sig); + if (err) + goto give_sigsegv; + + err |= setup_sigcontext(&frame->sc, regs, set->sig[0]); + if (err) + goto give_sigsegv; + + if (_NSIG_WORDS > 1) { + err |= __copy_to_user(frame->extramask, &set->sig[1], + sizeof(frame->extramask)); + if (err) + goto give_sigsegv; + } + + if (ka->sa.sa_flags & SA_RESTORER) + regs->lr = (unsigned long)ka->sa.sa_restorer; + else { + /* This is : ldi r7, #__NR_sigreturn ; trap #2 */ + unsigned long code = 0x670010f2 | (__NR_sigreturn << 16); + + regs->lr = (unsigned long)frame->retcode; + err |= __put_user(code, (long __user *)(frame->retcode+0)); + if (err) + goto give_sigsegv; + flush_cache_sigtramp((unsigned long)frame->retcode); + } + + /* Set up registers for signal handler */ + regs->spu = (unsigned long)frame; + regs->r0 = signal; /* Arg for signal handler */ + regs->r1 = (unsigned long)&frame->sc; + regs->bpc = (unsigned long)ka->sa.sa_handler; + + set_fs(USER_DS); + +#if DEBUG_SIG + printk("SIG deliver (%s:%d): sp=%p pc=%p\n", + current->comm, current->pid, frame, regs->pc); +#endif + + return; + +give_sigsegv: + force_sigsegv(sig, current); +} + +static void 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; + int signal; + + frame = get_sigframe(ka, regs->spu, sizeof(*frame)); + + if (!access_ok(VERIFY_WRITE, frame, sizeof(*frame))) + goto give_sigsegv; + + signal = current_thread_info()->exec_domain + && current_thread_info()->exec_domain->signal_invmap + && sig < 32 + ? current_thread_info()->exec_domain->signal_invmap[sig] + : sig; + + err |= __put_user(signal, &frame->sig); + if (err) + goto give_sigsegv; + + err |= __put_user(&frame->info, &frame->pinfo); + err |= __put_user(&frame->uc, &frame->puc); + err |= copy_siginfo_to_user(&frame->info, info); + if (err) + goto give_sigsegv; + + /* Create the ucontext. */ + 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->spu), + &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, regs, set->sig[0]); + err |= __copy_to_user(&frame->uc.uc_sigmask, set, sizeof(*set)); + if (err) + goto give_sigsegv; + + /* Set up to return from userspace. */ + if (ka->sa.sa_flags & SA_RESTORER) + regs->lr = (unsigned long)ka->sa.sa_restorer; + else { + /* This is : ldi r7, #__NR_rt_sigreturn ; trap #2 */ + unsigned long code1 = 0x97f00000 | (__NR_rt_sigreturn); + unsigned long code2 = 0x10f2f000; + + regs->lr = (unsigned long)frame->retcode; + err |= __put_user(code1, (long __user *)(frame->retcode+0)); + err |= __put_user(code2, (long __user *)(frame->retcode+4)); + if (err) + goto give_sigsegv; + flush_cache_sigtramp((unsigned long)frame->retcode); + } + + /* Set up registers for signal handler */ + regs->spu = (unsigned long)frame; + regs->r0 = signal; /* Arg for signal handler */ + regs->r1 = (unsigned long)&frame->info; + regs->r2 = (unsigned long)&frame->uc; + regs->bpc = (unsigned long)ka->sa.sa_handler; + + set_fs(USER_DS); + +#if DEBUG_SIG + printk("SIG deliver (%s:%d): sp=%p pc=%p\n", + current->comm, current->pid, frame, regs->pc); +#endif + + return; + +give_sigsegv: + force_sigsegv(sig, current); +} + +/* + * OK, we're invoking a handler + */ + +static void +handle_signal(unsigned long sig, struct k_sigaction *ka, siginfo_t *info, + sigset_t *oldset, struct pt_regs *regs) +{ + unsigned short inst; + + /* Are we from a system call? */ + if (regs->syscall_nr >= 0) { + /* If so, check system call restarting.. */ + switch (regs->r0) { + case -ERESTART_RESTARTBLOCK: + case -ERESTARTNOHAND: + regs->r0 = -EINTR; + break; + + case -ERESTARTSYS: + if (!(ka->sa.sa_flags & SA_RESTART)) { + regs->r0 = -EINTR; + break; + } + /* fallthrough */ + case -ERESTARTNOINTR: + regs->r0 = regs->orig_r0; + inst = *(unsigned short *)(regs->bpc - 2); + if ((inst & 0xfff0) == 0x10f0) /* trap ? */ + regs->bpc -= 2; + else + regs->bpc -= 4; + } + } + + /* Set up the stack frame */ + if (ka->sa.sa_flags & SA_SIGINFO) + setup_rt_frame(sig, ka, info, oldset, regs); + else + setup_frame(sig, ka, oldset, regs); + + if (!(ka->sa.sa_flags & SA_NODEFER)) { + spin_lock_irq(¤t->sighand->siglock); + sigorsets(¤t->blocked,¤t->blocked,&ka->sa.sa_mask); + sigaddset(¤t->blocked,sig); + recalc_sigpending(); + spin_unlock_irq(¤t->sighand->siglock); + } +} + +/* + * 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. + */ +int do_signal(struct pt_regs *regs, sigset_t *oldset) +{ + siginfo_t info; + int signr; + struct k_sigaction ka; + unsigned short inst; + + /* + * 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. + */ + if (!user_mode(regs)) + return 1; + + if (current->flags & PF_FREEZE) { + refrigerator(0); + goto no_signal; + } + + if (!oldset) + oldset = ¤t->blocked; + + signr = get_signal_to_deliver(&info, &ka, regs, NULL); + if (signr > 0) { + /* Reenable any watchpoints before delivering the + * signal to user space. The processor register will + * have been cleared if the watchpoint triggered + * inside the kernel. + */ + + /* Whee! Actually deliver the signal. */ + handle_signal(signr, &ka, &info, oldset, regs); + return 1; + } + + no_signal: + /* Did we come from a system call? */ + if (regs->syscall_nr >= 0) { + /* Restart the system call - no handlers present */ + if (regs->r0 == -ERESTARTNOHAND || + regs->r0 == -ERESTARTSYS || + regs->r0 == -ERESTARTNOINTR) { + regs->r0 = regs->orig_r0; + inst = *(unsigned short *)(regs->bpc - 2); + if ((inst & 0xfff0) == 0x10f0) /* trap ? */ + regs->bpc -= 2; + else + regs->bpc -= 4; + } + if (regs->r0 == -ERESTART_RESTARTBLOCK){ + regs->r0 = regs->orig_r0; + regs->r7 = __NR_restart_syscall; + inst = *(unsigned short *)(regs->bpc - 2); + if ((inst & 0xfff0) == 0x10f0) /* trap ? */ + regs->bpc -= 2; + else + regs->bpc -= 4; + } + } + return 0; +} + +/* + * notification of userspace execution resumption + * - triggered by current->work.notify_resume + */ +void do_notify_resume(struct pt_regs *regs, sigset_t *oldset, + __u32 thread_info_flags) +{ + /* Pending single-step? */ + if (thread_info_flags & _TIF_SINGLESTEP) + clear_thread_flag(TIF_SINGLESTEP); + + /* deal with pending signal delivery */ + if (thread_info_flags & _TIF_SIGPENDING) + do_signal(regs,oldset); + + clear_thread_flag(TIF_IRET); +} diff --git a/arch/m32r/kernel/smp.c b/arch/m32r/kernel/smp.c new file mode 100644 index 000000000..2dc542eb1 --- /dev/null +++ b/arch/m32r/kernel/smp.c @@ -0,0 +1,965 @@ +/* + * linux/arch/m32r/kernel/smp.c + * + * M32R SMP support routines. + * + * Copyright (c) 2001, 2002 Hitoshi Yamamoto + * + * Taken from i386 version. + * (c) 1995 Alan Cox, Building #3 + * (c) 1998-99, 2000 Ingo Molnar + * + * This code is released under the GNU General Public License version 2 or + * later. + */ + +#undef DEBUG_SMP + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +/*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*/ +/* Data structures and variables */ +/*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*/ + +/* + * Structure and data for smp_call_function(). This is designed to minimise + * static memory requirements. It also looks cleaner. + */ +static spinlock_t call_lock = SPIN_LOCK_UNLOCKED; + +struct call_data_struct { + void (*func) (void *info); + void *info; + atomic_t started; + atomic_t finished; + int wait; +} __attribute__ ((__aligned__(SMP_CACHE_BYTES))); + +static struct call_data_struct *call_data; + +/* + * For flush_cache_all() + */ +static spinlock_t flushcache_lock = SPIN_LOCK_UNLOCKED; +static volatile unsigned long flushcache_cpumask = 0; + +/* + * For flush_tlb_others() + */ +static volatile cpumask_t flush_cpumask; +static struct mm_struct *flush_mm; +static struct vm_area_struct *flush_vma; +static volatile unsigned long flush_va; +static spinlock_t tlbstate_lock = SPIN_LOCK_UNLOCKED; +#define FLUSH_ALL 0xffffffff + +DECLARE_PER_CPU(int, prof_multiplier); +DECLARE_PER_CPU(int, prof_old_multiplier); +DECLARE_PER_CPU(int, prof_counter); + +extern spinlock_t ipi_lock[]; + +/*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*/ +/* Function Prototypes */ +/*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*/ + +void smp_send_reschedule(int); +void smp_reschedule_interrupt(void); + +void smp_flush_cache_all(void); +void smp_flush_cache_all_interrupt(void); + +void smp_flush_tlb_all(void); +static void flush_tlb_all_ipi(void *); + +void smp_flush_tlb_mm(struct mm_struct *); +void smp_flush_tlb_range(struct vm_area_struct *, unsigned long, \ + unsigned long); +void smp_flush_tlb_page(struct vm_area_struct *, unsigned long); +static void flush_tlb_others(cpumask_t, struct mm_struct *, + struct vm_area_struct *, unsigned long); +void smp_invalidate_interrupt(void); + +void smp_send_stop(void); +static void stop_this_cpu(void *); + +int smp_call_function(void (*) (void *), void *, int, int); +void smp_call_function_interrupt(void); + +void smp_send_timer(void); +void smp_ipi_timer_interrupt(struct pt_regs *); +void smp_local_timer_interrupt(struct pt_regs *); + +void send_IPI_allbutself(int, int); +static void send_IPI_mask(cpumask_t, int, int); +unsigned long send_IPI_mask_phys(cpumask_t, int, int); + +/*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*/ +/* Rescheduling request Routines */ +/*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*/ + +/*==========================================================================* + * Name: smp_send_reschedule + * + * Description: This routine requests other CPU to execute rescheduling. + * 1.Send 'RESCHEDULE_IPI' to other CPU. + * Request other CPU to execute 'smp_reschedule_interrupt()'. + * + * Born on Date: 2002.02.05 + * + * Arguments: cpu_id - Target CPU ID + * + * Returns: void (cannot fail) + * + * Modification log: + * Date Who Description + * ---------- --- -------------------------------------------------------- + * + *==========================================================================*/ +void smp_send_reschedule(int cpu_id) +{ + WARN_ON(cpu_is_offline(cpu_id)); + send_IPI_mask(cpumask_of_cpu(cpu_id), RESCHEDULE_IPI, 1); +} + +/*==========================================================================* + * Name: smp_reschedule_interrupt + * + * Description: This routine executes on CPU which received + * 'RESCHEDULE_IPI'. + * Rescheduling is processed at the exit of interrupt + * operation. + * + * Born on Date: 2002.02.05 + * + * Arguments: NONE + * + * Returns: void (cannot fail) + * + * Modification log: + * Date Who Description + * ---------- --- -------------------------------------------------------- + * + *==========================================================================*/ +void smp_reschedule_interrupt(void) +{ + /* nothing to do */ +} + +/*==========================================================================* + * Name: smp_flush_cache_all + * + * Description: This routine sends a 'INVALIDATE_CACHE_IPI' to all other + * CPUs in the system. + * + * Born on Date: 2003-05-28 + * + * Arguments: NONE + * + * Returns: void (cannot fail) + * + * Modification log: + * Date Who Description + * ---------- --- -------------------------------------------------------- + * + *==========================================================================*/ +void smp_flush_cache_all(void) +{ + cpumask_t cpumask; + unsigned long *mask; + + preempt_disable(); + cpumask = cpu_online_map; + cpu_clear(smp_processor_id(), cpumask); + spin_lock(&flushcache_lock); + mask=cpus_addr(cpumask); + atomic_set_mask(*mask, (atomic_t *)&flushcache_cpumask); + send_IPI_mask(cpumask, INVALIDATE_CACHE_IPI, 0); + _flush_cache_copyback_all(); + while (flushcache_cpumask) + mb(); + spin_unlock(&flushcache_lock); + preempt_enable(); +} + +void smp_flush_cache_all_interrupt(void) +{ + _flush_cache_copyback_all(); + clear_bit(smp_processor_id(), &flushcache_cpumask); +} + +/*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*/ +/* TLB flush request Routins */ +/*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*/ + +/*==========================================================================* + * Name: smp_flush_tlb_all + * + * Description: This routine flushes all processes TLBs. + * 1.Request other CPU to execute 'flush_tlb_all_ipi()'. + * 2.Execute 'do_flush_tlb_all_local()'. + * + * Born on Date: 2002.02.05 + * + * Arguments: NONE + * + * Returns: void (cannot fail) + * + * Modification log: + * Date Who Description + * ---------- --- -------------------------------------------------------- + * + *==========================================================================*/ +void smp_flush_tlb_all(void) +{ + unsigned long flags; + + preempt_disable(); + local_irq_save(flags); + __flush_tlb_all(); + local_irq_restore(flags); + smp_call_function(flush_tlb_all_ipi, 0, 1, 1); + preempt_enable(); +} + +/*==========================================================================* + * Name: flush_tlb_all_ipi + * + * Description: This routine flushes all local TLBs. + * 1.Execute 'do_flush_tlb_all_local()'. + * + * Born on Date: 2002.02.05 + * + * Arguments: *info - not used + * + * Returns: void (cannot fail) + * + * Modification log: + * Date Who Description + * ---------- --- -------------------------------------------------------- + * + *==========================================================================*/ +static void flush_tlb_all_ipi(void *info) +{ + __flush_tlb_all(); +} + +/*==========================================================================* + * Name: smp_flush_tlb_mm + * + * Description: This routine flushes the specified mm context TLB's. + * + * Born on Date: 2002.02.05 + * + * Arguments: *mm - a pointer to the mm struct for flush TLB + * + * Returns: void (cannot fail) + * + * Modification log: + * Date Who Description + * ---------- --- -------------------------------------------------------- + * + *==========================================================================*/ +void smp_flush_tlb_mm(struct mm_struct *mm) +{ + int cpu_id = smp_processor_id(); + cpumask_t cpu_mask; + unsigned long *mmc = &mm->context[cpu_id]; + unsigned long flags; + + preempt_disable(); + cpu_mask = mm->cpu_vm_mask; + cpu_clear(cpu_id, cpu_mask); + + if (*mmc != NO_CONTEXT) { + local_irq_save(flags); + *mmc = NO_CONTEXT; + if (mm == current->mm) + activate_context(mm); + else + cpu_clear(cpu_id, mm->cpu_vm_mask); + local_irq_restore(flags); + } + if (!cpus_empty(cpu_mask)) + flush_tlb_others(cpu_mask, mm, NULL, FLUSH_ALL); + + preempt_enable(); +} + +/*==========================================================================* + * Name: smp_flush_tlb_range + * + * Description: This routine flushes a range of pages. + * + * Born on Date: 2002.02.05 + * + * Arguments: *mm - a pointer to the mm struct for flush TLB + * start - not used + * end - not used + * + * Returns: void (cannot fail) + * + * Modification log: + * Date Who Description + * ---------- --- -------------------------------------------------------- + * + *==========================================================================*/ +void smp_flush_tlb_range(struct vm_area_struct *vma, unsigned long start, + unsigned long end) +{ + smp_flush_tlb_mm(vma->vm_mm); +} + +/*==========================================================================* + * Name: smp_flush_tlb_page + * + * Description: This routine flushes one page. + * + * Born on Date: 2002.02.05 + * + * Arguments: *vma - a pointer to the vma struct include va + * va - virtual address for flush TLB + * + * Returns: void (cannot fail) + * + * Modification log: + * Date Who Description + * ---------- --- -------------------------------------------------------- + * + *==========================================================================*/ +void smp_flush_tlb_page(struct vm_area_struct *vma, unsigned long va) +{ + struct mm_struct *mm = vma->vm_mm; + int cpu_id = smp_processor_id(); + cpumask_t cpu_mask; + unsigned long *mmc = &mm->context[cpu_id]; + unsigned long flags; + + preempt_disable(); + cpu_mask = mm->cpu_vm_mask; + cpu_clear(cpu_id, cpu_mask); + +#ifdef DEBUG_SMP + if (!mm) + BUG(); +#endif + + if (*mmc != NO_CONTEXT) { + local_irq_save(flags); + va &= PAGE_MASK; + va |= (*mmc & MMU_CONTEXT_ASID_MASK); + __flush_tlb_page(va); + local_irq_restore(flags); + } + if (!cpus_empty(cpu_mask)) + flush_tlb_others(cpu_mask, mm, vma, va); + + preempt_enable(); +} + +/*==========================================================================* + * Name: flush_tlb_others + * + * Description: This routine requests other CPU to execute flush TLB. + * 1.Setup parmeters. + * 2.Send 'INVALIDATE_TLB_IPI' to other CPU. + * Request other CPU to execute 'smp_invalidate_interrupt()'. + * 3.Wait for other CPUs operation finished. + * + * Born on Date: 2002.02.05 + * + * Arguments: cpumask - bitmap of target CPUs + * *mm - a pointer to the mm struct for flush TLB + * *vma - a pointer to the vma struct include va + * va - virtual address for flush TLB + * + * Returns: void (cannot fail) + * + * Modification log: + * Date Who Description + * ---------- --- -------------------------------------------------------- + * + *==========================================================================*/ +static void flush_tlb_others(cpumask_t cpumask, struct mm_struct *mm, + struct vm_area_struct *vma, unsigned long va) +{ + unsigned long *mask; +#ifdef DEBUG_SMP + unsigned long flags; + __save_flags(flags); + if (!(flags & 0x0040)) /* Interrupt Disable NONONO */ + BUG(); +#endif /* DEBUG_SMP */ + + /* + * A couple of (to be removed) sanity checks: + * + * - we do not send IPIs to not-yet booted CPUs. + * - current CPU must not be in mask + * - mask must exist :) + */ + BUG_ON(cpus_empty(cpumask)); + + BUG_ON(cpu_isset(smp_processor_id(), cpumask)); + BUG_ON(!mm); + + /* If a CPU which we ran on has gone down, OK. */ + cpus_and(cpumask, cpumask, cpu_online_map); + if (cpus_empty(cpumask)) + return; + + /* + * i'm not happy about this global shared spinlock in the + * MM hot path, but we'll see how contended it is. + * Temporarily this turns IRQs off, so that lockups are + * detected by the NMI watchdog. + */ + spin_lock(&tlbstate_lock); + + flush_mm = mm; + flush_vma = vma; + flush_va = va; + mask=cpus_addr(cpumask); + atomic_set_mask(*mask, (atomic_t *)&flush_cpumask); + + /* + * We have to send the IPI only to + * CPUs affected. + */ + send_IPI_mask(cpumask, INVALIDATE_TLB_IPI, 0); + + while (!cpus_empty(flush_cpumask)) { + /* nothing. lockup detection does not belong here */ + mb(); + } + + flush_mm = NULL; + flush_vma = NULL; + flush_va = 0; + spin_unlock(&tlbstate_lock); +} + +/*==========================================================================* + * Name: smp_invalidate_interrupt + * + * Description: This routine executes on CPU which received + * 'INVALIDATE_TLB_IPI'. + * 1.Flush local TLB. + * 2.Report flush TLB process was finished. + * + * Born on Date: 2002.02.05 + * + * Arguments: NONE + * + * Returns: void (cannot fail) + * + * Modification log: + * Date Who Description + * ---------- --- -------------------------------------------------------- + * + *==========================================================================*/ +void smp_invalidate_interrupt(void) +{ + int cpu_id = smp_processor_id(); + unsigned long *mmc = &flush_mm->context[cpu_id]; + + if (!cpu_isset(cpu_id, flush_cpumask)) + return; + + if (flush_va == FLUSH_ALL) { + *mmc = NO_CONTEXT; + if (flush_mm == current->active_mm) + activate_context(flush_mm); + else + cpu_clear(cpu_id, flush_mm->cpu_vm_mask); + } else { + unsigned long va = flush_va; + + if (*mmc != NO_CONTEXT) { + va &= PAGE_MASK; + va |= (*mmc & MMU_CONTEXT_ASID_MASK); + __flush_tlb_page(va); + } + } + cpu_clear(cpu_id, flush_cpumask); +} + +/*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*/ +/* Stop CPU request Routins */ +/*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*/ + +/*==========================================================================* + * Name: smp_send_stop + * + * Description: This routine requests stop all CPUs. + * 1.Request other CPU to execute 'stop_this_cpu()'. + * + * Born on Date: 2002.02.05 + * + * Arguments: NONE + * + * Returns: void (cannot fail) + * + * Modification log: + * Date Who Description + * ---------- --- -------------------------------------------------------- + * + *==========================================================================*/ +void smp_send_stop(void) +{ + smp_call_function(stop_this_cpu, NULL, 1, 0); +} + +/*==========================================================================* + * Name: stop_this_cpu + * + * Description: This routine halt CPU. + * + * Born on Date: 2002.02.05 + * + * Arguments: NONE + * + * Returns: void (cannot fail) + * + * Modification log: + * Date Who Description + * ---------- --- -------------------------------------------------------- + * + *==========================================================================*/ +static void stop_this_cpu(void *dummy) +{ + int cpu_id = smp_processor_id(); + + /* + * Remove this CPU: + */ + cpu_clear(cpu_id, cpu_online_map); + + /* + * PSW IE = 1; + * IMASK = 0; + * goto SLEEP + */ + local_irq_disable(); + outl(0, M32R_ICU_IMASK_PORTL); + inl(M32R_ICU_IMASK_PORTL); /* dummy read */ + local_irq_enable(); + + for ( ; ; ); +} + +/*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*/ +/* Call function Routins */ +/*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*/ + +/*==========================================================================* + * Name: smp_call_function + * + * Description: This routine sends a 'CALL_FUNCTION_IPI' to all other CPUs + * in the system. + * + * Born on Date: 2002.02.05 + * + * Arguments: *func - The function to run. This must be fast and + * non-blocking. + * *info - An arbitrary pointer to pass to the function. + * nonatomic - currently unused. + * wait - If true, wait (atomically) until function has + * completed on other CPUs. + * + * Returns: 0 on success, else a negative status code. Does not return + * until remote CPUs are nearly ready to execute <> or + * are or have executed. + * + * Cautions: You must not call this function with disabled interrupts or + * from a hardware interrupt handler, you may call it from a + * bottom half handler. + * + * Modification log: + * Date Who Description + * ---------- --- -------------------------------------------------------- + * + *==========================================================================*/ +int smp_call_function(void (*func) (void *info), void *info, int nonatomic, + int wait) +{ + struct call_data_struct data; + int cpus; + +#ifdef DEBUG_SMP + unsigned long flags; + __save_flags(flags); + if (!(flags & 0x0040)) /* Interrupt Disable NONONO */ + BUG(); +#endif /* DEBUG_SMP */ + + /* Holding any lock stops cpus from going down. */ + spin_lock(&call_lock); + cpus = num_online_cpus() - 1; + + if (!cpus) { + spin_unlock(&call_lock); + return 0; + } + + /* Can deadlock when called with interrupts disabled */ + WARN_ON(irqs_disabled()); + + data.func = func; + data.info = info; + atomic_set(&data.started, 0); + data.wait = wait; + if (wait) + atomic_set(&data.finished, 0); + + call_data = &data; + mb(); + + /* Send a message to all other CPUs and wait for them to respond */ + send_IPI_allbutself(CALL_FUNCTION_IPI, 0); + + /* Wait for response */ + while (atomic_read(&data.started) != cpus) + barrier(); + + if (wait) + while (atomic_read(&data.finished) != cpus) + barrier(); + spin_unlock(&call_lock); + + return 0; +} + +/*==========================================================================* + * Name: smp_call_function_interrupt + * + * Description: This routine executes on CPU which received + * 'CALL_FUNCTION_IPI'. + * + * Born on Date: 2002.02.05 + * + * Arguments: NONE + * + * Returns: void (cannot fail) + * + * Modification log: + * Date Who Description + * ---------- --- -------------------------------------------------------- + * + *==========================================================================*/ +void smp_call_function_interrupt(void) +{ + void (*func) (void *info) = call_data->func; + void *info = call_data->info; + int wait = call_data->wait; + + /* + * Notify initiating CPU that I've grabbed the data and am + * about to execute the function + */ + mb(); + atomic_inc(&call_data->started); + /* + * At this point the info structure may be out of scope unless wait==1 + */ + irq_enter(); + (*func)(info); + irq_exit(); + + if (wait) { + mb(); + atomic_inc(&call_data->finished); + } +} + +/*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*/ +/* Timer Routins */ +/*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*/ + +/*==========================================================================* + * Name: smp_send_timer + * + * Description: This routine sends a 'LOCAL_TIMER_IPI' to all other CPUs + * in the system. + * + * Born on Date: 2002.02.05 + * + * Arguments: NONE + * + * Returns: void (cannot fail) + * + * Modification log: + * Date Who Description + * ---------- --- -------------------------------------------------------- + * + *==========================================================================*/ +void smp_send_timer(void) +{ + send_IPI_allbutself(LOCAL_TIMER_IPI, 1); +} + +/*==========================================================================* + * Name: smp_send_timer + * + * Description: This routine executes on CPU which received + * 'LOCAL_TIMER_IPI'. + * + * Born on Date: 2002.02.05 + * + * Arguments: *regs - a pointer to the saved regster info + * + * Returns: void (cannot fail) + * + * Modification log: + * Date Who Description + * ---------- --- -------------------------------------------------------- + * + *==========================================================================*/ +void smp_ipi_timer_interrupt(struct pt_regs *regs) +{ + irq_enter(); + smp_local_timer_interrupt(regs); + irq_exit(); +} + +/*==========================================================================* + * Name: smp_local_timer_interrupt + * + * Description: Local timer interrupt handler. It does both profiling and + * process statistics/rescheduling. + * We do profiling in every local tick, statistics/rescheduling + * happen only every 'profiling multiplier' ticks. The default + * multiplier is 1 and it can be changed by writing the new + * multiplier value into /proc/profile. + * + * Born on Date: 2002.02.05 + * + * Arguments: *regs - a pointer to the saved regster info + * + * Returns: void (cannot fail) + * + * Original: arch/i386/kernel/apic.c + * + * Modification log: + * Date Who Description + * ---------- --- -------------------------------------------------------- + * 2003-06-24 hy use per_cpu structure. + *==========================================================================*/ +void smp_local_timer_interrupt(struct pt_regs *regs) +{ + int user = user_mode(regs); + int cpu_id = smp_processor_id(); + + /* + * The profiling function is SMP safe. (nothing can mess + * around with "current", and the profiling counters are + * updated with atomic operations). This is especially + * useful with a profiling multiplier != 1 + */ + + profile_tick(CPU_PROFILING, regs); + + if (--per_cpu(prof_counter, cpu_id) <= 0) { + /* + * The multiplier may have changed since the last time we got + * to this point as a result of the user writing to + * /proc/profile. In this case we need to adjust the APIC + * timer accordingly. + * + * Interrupts are already masked off at this point. + */ + per_cpu(prof_counter, cpu_id) + = per_cpu(prof_multiplier, cpu_id); + if (per_cpu(prof_counter, cpu_id) + != per_cpu(prof_old_multiplier, cpu_id)) + { + per_cpu(prof_old_multiplier, cpu_id) + = per_cpu(prof_counter, cpu_id); + } + + update_process_times(user); + } +} + +/*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*/ +/* Send IPI Routins */ +/*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*/ + +/*==========================================================================* + * Name: send_IPI_allbutself + * + * Description: This routine sends a IPI to all other CPUs in the system. + * + * Born on Date: 2002.02.05 + * + * Arguments: ipi_num - Number of IPI + * try - 0 : Send IPI certainly. + * !0 : The following IPI is not sended when Target CPU + * has not received the before IPI. + * + * Returns: void (cannot fail) + * + * Modification log: + * Date Who Description + * ---------- --- -------------------------------------------------------- + * + *==========================================================================*/ +void send_IPI_allbutself(int ipi_num, int try) +{ + cpumask_t cpumask; + + cpumask = cpu_online_map; + cpu_clear(smp_processor_id(), cpumask); + + send_IPI_mask(cpumask, ipi_num, try); +} + +/*==========================================================================* + * Name: send_IPI_mask + * + * Description: This routine sends a IPI to CPUs in the system. + * + * Born on Date: 2002.02.05 + * + * Arguments: cpu_mask - Bitmap of target CPUs logical ID + * ipi_num - Number of IPI + * try - 0 : Send IPI certainly. + * !0 : The following IPI is not sended when Target CPU + * has not received the before IPI. + * + * Returns: void (cannot fail) + * + * Modification log: + * Date Who Description + * ---------- --- -------------------------------------------------------- + * + *==========================================================================*/ +static void send_IPI_mask(cpumask_t cpumask, int ipi_num, int try) +{ + cpumask_t physid_mask, tmp; + int cpu_id, phys_id; + int num_cpus = num_online_cpus(); + + if (num_cpus <= 1) /* NO MP */ + return; + + cpus_and(tmp, cpumask, cpu_online_map); + BUG_ON(!cpus_equal(cpumask, tmp)); + + physid_mask = CPU_MASK_NONE; + for_each_cpu_mask(cpu_id, cpumask){ + if ((phys_id = cpu_to_physid(cpu_id)) != -1) + cpu_set(phys_id, physid_mask); + } + + send_IPI_mask_phys(physid_mask, ipi_num, try); +} + +/*==========================================================================* + * Name: send_IPI_mask_phys + * + * Description: This routine sends a IPI to other CPUs in the system. + * + * Born on Date: 2002.02.05 + * + * Arguments: cpu_mask - Bitmap of target CPUs physical ID + * ipi_num - Number of IPI + * try - 0 : Send IPI certainly. + * !0 : The following IPI is not sended when Target CPU + * has not received the before IPI. + * + * Returns: IPICRi regster value. + * + * Modification log: + * Date Who Description + * ---------- --- -------------------------------------------------------- + * + *==========================================================================*/ +unsigned long send_IPI_mask_phys(cpumask_t physid_mask, int ipi_num, + int try) +{ + spinlock_t *ipilock; + unsigned long flags = 0; + volatile unsigned long *ipicr_addr; + unsigned long ipicr_val; + unsigned long my_physid_mask; + unsigned long mask = cpus_addr(physid_mask)[0]; + + + if (mask & ~physids_coerce(phys_cpu_present_map)) + BUG(); + if (ipi_num >= NR_IPIS) + BUG(); + + mask <<= IPI_SHIFT; + ipilock = &ipi_lock[ipi_num]; + ipicr_addr = (volatile unsigned long *)(M32R_ICU_IPICR_ADDR + + (ipi_num << 2)); + my_physid_mask = ~(1 << smp_processor_id()); + + /* + * lock ipi_lock[i] + * check IPICRi == 0 + * write IPICRi (send IPIi) + * unlock ipi_lock[i] + */ + __asm__ __volatile__ ( + ";; LOCK ipi_lock[i] \n\t" + ".fillinsn \n" + "1: \n\t" + "mvfc %1, psw \n\t" + "clrpsw #0x40 -> nop \n\t" + DCACHE_CLEAR("r4", "r5", "%2") + "lock r4, @%2 \n\t" + "addi r4, #-1 \n\t" + "unlock r4, @%2 \n\t" + "mvtc %1, psw \n\t" + "bnez r4, 2f \n\t" + LOCK_SECTION_START(".balign 4 \n\t") + ".fillinsn \n" + "2: \n\t" + "ld r4, @%2 \n\t" + "blez r4, 2b \n\t" + "bra 1b \n\t" + LOCK_SECTION_END + ";; CHECK IPICRi == 0 \n\t" + ".fillinsn \n" + "3: \n\t" + "ld %0, @%3 \n\t" + "and %0, %6 \n\t" + "beqz %0, 4f \n\t" + "bnez %5, 5f \n\t" + "bra 3b \n\t" + ";; WRITE IPICRi (send IPIi) \n\t" + ".fillinsn \n" + "4: \n\t" + "st %4, @%3 \n\t" + ";; UNLOCK ipi_lock[i] \n\t" + ".fillinsn \n" + "5: \n\t" + "ldi r4, #1 \n\t" + "st r4, @%2 \n\t" + : "=&r"(ipicr_val) + : "r"(flags), "r"(&ipilock->lock), "r"(ipicr_addr), + "r"(mask), "r"(try), "r"(my_physid_mask) + : "memory", "r4" +#ifdef CONFIG_CHIP_M32700_TS1 + , "r5" +#endif /* CONFIG_CHIP_M32700_TS1 */ + ); + + return ipicr_val; +} diff --git a/arch/m32r/kernel/smpboot.c b/arch/m32r/kernel/smpboot.c new file mode 100644 index 000000000..6ac4f705e --- /dev/null +++ b/arch/m32r/kernel/smpboot.c @@ -0,0 +1,635 @@ +/* + * linux/arch/m32r/kernel/smpboot.c + * orig : i386 2.4.10 + * + * M32R SMP booting functions + * + * Copyright (c) 2001, 2002, 2003 Hitoshi Yamamoto + * + * Taken from i386 version. + * (c) 1995 Alan Cox, Building #3 + * (c) 1998, 1999, 2000 Ingo Molnar + * + * Much of the core SMP work is based on previous work by Thomas Radke, to + * whom a great many thanks are extended. + * + * Thanks to Intel for making available several different Pentium, + * Pentium Pro and Pentium-II/Xeon MP machines. + * Original development of Linux SMP code supported by Caldera. + * + * This code is released under the GNU General Public License version 2 or + * later. + * + * Fixes + * Felix Koop : NR_CPUS used properly + * Jose Renau : Handle single CPU case. + * Alan Cox : By repeated request + * 8) - Total BogoMIP report. + * Greg Wright : Fix for kernel stacks panic. + * Erich Boleyn : MP v1.4 and additional changes. + * Matthias Sattler : Changes for 2.1 kernel map. + * Michel Lespinasse : Changes for 2.1 kernel map. + * Michael Chastain : Change trampoline.S to gnu as. + * Alan Cox : Dumb bug: 'B' step PPro's are fine + * Ingo Molnar : Added APIC timers, based on code + * from Jose Renau + * Ingo Molnar : various cleanups and rewrites + * Tigran Aivazian : fixed "0.00 in /proc/uptime on SMP" bug. + * Maciej W. Rozycki : Bits for genuine 82489DX APICs + * Martin J. Bligh : Added support for multi-quad systems + */ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#define DEBUG_SMP +#ifdef DEBUG_SMP +#define Dprintk(x...) printk(x) +#else +#define Dprintk(x...) +#endif + +extern int cpu_idle(void); +extern cpumask_t cpu_initialized; + +/*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*/ +/* Data structures and variables */ +/*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*/ + +/* Processor that is doing the boot up */ +static unsigned int bsp_phys_id = -1; + +/* Bitmask of physically existing CPUs */ +physid_mask_t phys_cpu_present_map; + +/* Bitmask of currently online CPUs */ +cpumask_t cpu_online_map; + +cpumask_t cpu_bootout_map; +cpumask_t cpu_bootin_map; +cpumask_t cpu_callout_map; +static cpumask_t cpu_callin_map; + +/* Per CPU bogomips and other parameters */ +struct cpuinfo_m32r cpu_data[NR_CPUS] __cacheline_aligned; + +/* Set when the idlers are all forked */ +int smp_threads_ready; + +static int cpucount; +static cpumask_t smp_commenced_mask; + +extern struct { + void * spi; + unsigned short ss; +} stack_start; + +/* which physical physical ID maps to which logical CPU number */ +static volatile int physid_2_cpu[NR_CPUS]; + +/* which logical CPU number maps to which physical ID */ +volatile int cpu_2_physid[NR_CPUS]; + +DEFINE_PER_CPU(int, prof_multiplier) = 1; +DEFINE_PER_CPU(int, prof_old_multiplier) = 1; +DEFINE_PER_CPU(int, prof_counter) = 1; + +spinlock_t ipi_lock[NR_IPIS]; + +static unsigned int calibration_result; + +unsigned long cache_decay_ticks = HZ / 100; + +/*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*/ +/* Function Prototypes */ +/*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*/ + +void smp_prepare_boot_cpu(void); +void smp_prepare_cpus(unsigned int); +static void smp_tune_scheduling(void); +static void init_ipi_lock(void); +static void do_boot_cpu(int); +int __cpu_up(unsigned int); +void smp_cpus_done(unsigned int); + +int start_secondary(void *); +static void smp_callin(void); +static void smp_online(void); + +static void show_mp_info(int); +static void smp_store_cpu_info(int); +static void show_cpu_info(int); +int setup_profiling_timer(unsigned int); +static void init_cpu_to_physid(void); +static void map_cpu_to_physid(int, int); +static void unmap_cpu_to_physid(int, int); + +/*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*/ +/* Boot up APs Routins : BSP */ +/*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*/ +void __devinit smp_prepare_boot_cpu(void) +{ + bsp_phys_id = hard_smp_processor_id(); + physid_set(bsp_phys_id, phys_cpu_present_map); + cpu_set(0, cpu_online_map); /* BSP's cpu_id == 0 */ + cpu_set(0, cpu_callout_map); + cpu_set(0, cpu_callin_map); + + /* + * Initialize the logical to physical CPU number mapping + */ + init_cpu_to_physid(); + map_cpu_to_physid(0, bsp_phys_id); + current_thread_info()->cpu = 0; +} + +/*==========================================================================* + * Name: smp_prepare_cpus (old smp_boot_cpus) + * + * Description: This routine boot up APs. + * + * Born on Date: 2002.02.05 + * + * Arguments: NONE + * + * Returns: void (cannot fail) + * + * Modification log: + * Date Who Description + * ---------- --- -------------------------------------------------------- + * 2003-06-24 hy modify for linux-2.5.69 + * + *==========================================================================*/ +void __init smp_prepare_cpus(unsigned int max_cpus) +{ + int phys_id; + unsigned long nr_cpu; + + nr_cpu = inl(M32R_FPGA_NUM_OF_CPUS_PORTL); + if (nr_cpu > NR_CPUS) { + printk(KERN_INFO "NUM_OF_CPUS reg. value [%ld] > NR_CPU [%d]", + nr_cpu, NR_CPUS); + goto smp_done; + } + for (phys_id = 0 ; phys_id < nr_cpu ; phys_id++) + physid_set(phys_id, phys_cpu_present_map); + + show_mp_info(nr_cpu); + + init_ipi_lock(); + + /* + * Setup boot CPU information + */ + smp_store_cpu_info(0); /* Final full version of the data */ + smp_tune_scheduling(); + + /* + * If SMP should be disabled, then really disable it! + */ + if (!max_cpus) { + printk(KERN_INFO "SMP mode deactivated by commandline.\n"); + goto smp_done; + } + + /* + * Now scan the CPU present map and fire up the other CPUs. + */ + Dprintk("CPU present map : %lx\n", physids_coerce(phys_cpu_present_map)); + + for (phys_id = 0 ; phys_id < NR_CPUS ; phys_id++) { + /* + * Don't even attempt to start the boot CPU! + */ + if (phys_id == bsp_phys_id) + continue; + + if (!physid_isset(phys_id, phys_cpu_present_map)) + continue; + + if ((max_cpus >= 0) && (max_cpus <= cpucount + 1)) + continue; + + do_boot_cpu(phys_id); + + /* + * Make sure we unmap all failed CPUs + */ + if (physid_to_cpu(phys_id) == -1) { + physid_clear(phys_id, phys_cpu_present_map); + printk("phys CPU#%d not responding - " \ + "cannot use it.\n", phys_id); + } + } + +smp_done: + Dprintk("Boot done.\n"); +} + +static void __init smp_tune_scheduling(void) +{ + /* Nothing to do. */ +} + +/* + * init_ipi_lock : Initialize IPI locks. + */ +static void __init init_ipi_lock(void) +{ + int ipi; + + for (ipi = 0 ; ipi < NR_IPIS ; ipi++) + ipi_lock[ipi] = SPIN_LOCK_UNLOCKED; +} + +/*==========================================================================* + * Name: do_boot_cpu + * + * Description: This routine boot up one AP. + * + * Born on Date: 2002.02.05 + * + * Arguments: phys_id - Target CPU physical ID + * + * Returns: void (cannot fail) + * + * Modification log: + * Date Who Description + * ---------- --- -------------------------------------------------------- + * 2003-06-24 hy modify for linux-2.5.69 + * + *==========================================================================*/ +static void __init do_boot_cpu(int phys_id) +{ + struct task_struct *idle; + unsigned long send_status, boot_status; + int timeout, cpu_id; + + cpu_id = ++cpucount; + + /* + * We can't use kernel_thread since we must avoid to + * reschedule the child. + */ + idle = fork_idle(cpu_id); + if (IS_ERR(idle)) + panic("failed fork for CPU#%d.", cpu_id); + + idle->thread.lr = (unsigned long)start_secondary; + + map_cpu_to_physid(cpu_id, phys_id); + + /* So we see what's up */ + printk("Booting processor %d/%d\n", phys_id, cpu_id); + stack_start.spi = (void *)idle->thread.sp; + idle->thread_info->cpu = cpu_id; + + /* + * Send Startup IPI + * 1.IPI received by CPU#(phys_id). + * 2.CPU#(phys_id) enter startup_AP (arch/m32r/kernel/head.S) + * 3.CPU#(phys_id) enter start_secondary() + */ + send_status = 0; + boot_status = 0; + + cpu_set(phys_id, cpu_bootout_map); + + /* Send Startup IPI */ + send_IPI_mask_phys(cpumask_of_cpu(phys_id), CPU_BOOT_IPI, 0); + + Dprintk("Waiting for send to finish...\n"); + timeout = 0; + + /* Wait 100[ms] */ + do { + Dprintk("+"); + udelay(1000); + send_status = !cpu_isset(phys_id, cpu_bootin_map); + } while (send_status && (timeout++ < 100)); + + Dprintk("After Startup.\n"); + + if (!send_status) { + /* + * allow APs to start initializing. + */ + Dprintk("Before Callout %d.\n", cpu_id); + cpu_set(cpu_id, cpu_callout_map); + Dprintk("After Callout %d.\n", cpu_id); + + /* + * Wait 5s total for a response + */ + for (timeout = 0; timeout < 5000; timeout++) { + if (cpu_isset(cpu_id, cpu_callin_map)) + break; /* It has booted */ + udelay(1000); + } + + if (cpu_isset(cpu_id, cpu_callin_map)) { + /* number CPUs logically, starting from 1 (BSP is 0) */ + Dprintk("OK.\n"); + } else { + boot_status = 1; + printk("Not responding.\n"); + } + } else + printk("IPI never delivered???\n"); + + if (send_status || boot_status) { + unmap_cpu_to_physid(cpu_id, phys_id); + cpu_clear(cpu_id, cpu_callout_map); + cpu_clear(cpu_id, cpu_callin_map); + cpu_clear(cpu_id, cpu_initialized); + cpucount--; + } +} + +int __devinit __cpu_up(unsigned int cpu_id) +{ + int timeout; + + cpu_set(cpu_id, smp_commenced_mask); + + /* + * Wait 5s total for a response + */ + for (timeout = 0; timeout < 5000; timeout++) { + if (cpu_isset(cpu_id, cpu_online_map)) + break; + udelay(1000); + } + if (!cpu_isset(cpu_id, cpu_online_map)) + BUG(); + + return 0; +} + +void __init smp_cpus_done(unsigned int max_cpus) +{ + int cpu_id, timeout; + unsigned long bogosum = 0; + + for (timeout = 0; timeout < 5000; timeout++) { + if (cpus_equal(cpu_callin_map, cpu_online_map)) + break; + udelay(1000); + } + if (!cpus_equal(cpu_callin_map, cpu_online_map)) + BUG(); + + for (cpu_id = 0 ; cpu_id < num_online_cpus() ; cpu_id++) + show_cpu_info(cpu_id); + + /* + * Allow the user to impress friends. + */ + Dprintk("Before bogomips.\n"); + if (cpucount) { + for_each_cpu_mask(cpu_id, cpu_online_map) + bogosum += cpu_data[cpu_id].loops_per_jiffy; + + printk(KERN_INFO "Total of %d processors activated " \ + "(%lu.%02lu BogoMIPS).\n", cpucount + 1, + bogosum / (500000 / HZ), + (bogosum / (5000 / HZ)) % 100); + Dprintk("Before bogocount - setting activated=1.\n"); + } +} + +/*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*/ +/* Activate a secondary processor Routins */ +/*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*/ + +/*==========================================================================* + * Name: start_secondary + * + * Description: This routine activate a secondary processor. + * + * Born on Date: 2002.02.05 + * + * Arguments: *unused - currently unused. + * + * Returns: void (cannot fail) + * + * Modification log: + * Date Who Description + * ---------- --- -------------------------------------------------------- + * 2003-06-24 hy modify for linux-2.5.69 + * + *==========================================================================*/ +int __init start_secondary(void *unused) +{ + cpu_init(); + smp_callin(); + while (!cpu_isset(smp_processor_id(), smp_commenced_mask)) + rep_nop(); + + smp_online(); + + /* + * low-memory mappings have been cleared, flush them from + * the local TLBs too. + */ + local_flush_tlb_all(); + + return cpu_idle(); +} + +/*==========================================================================* + * Name: smp_callin + * + * Description: This routine activate a secondary processor. + * + * Born on Date: 2002.02.05 + * + * Arguments: NONE + * + * Returns: void (cannot fail) + * + * Modification log: + * Date Who Description + * ---------- --- -------------------------------------------------------- + * 2003-06-24 hy modify for linux-2.5.69 + * + *==========================================================================*/ +static void __init smp_callin(void) +{ + int phys_id = hard_smp_processor_id(); + int cpu_id = smp_processor_id(); + unsigned long timeout; + + if (cpu_isset(cpu_id, cpu_callin_map)) { + printk("huh, phys CPU#%d, CPU#%d already present??\n", + phys_id, cpu_id); + BUG(); + } + Dprintk("CPU#%d (phys ID: %d) waiting for CALLOUT\n", cpu_id, phys_id); + + /* Waiting 2s total for startup (udelay is not yet working) */ + timeout = jiffies + (2 * HZ); + while (time_before(jiffies, timeout)) { + /* Has the boot CPU finished it's STARTUP sequence ? */ + if (cpu_isset(cpu_id, cpu_callout_map)) + break; + rep_nop(); + } + + if (!time_before(jiffies, timeout)) { + printk("BUG: CPU#%d started up but did not get a callout!\n", + cpu_id); + BUG(); + } + + /* Allow the master to continue. */ + cpu_set(cpu_id, cpu_callin_map); +} + +static void __init smp_online(void) +{ + int cpu_id = smp_processor_id(); + + local_irq_enable(); + + /* Get our bogomips. */ + calibrate_delay(); + + /* Save our processor parameters */ + smp_store_cpu_info(cpu_id); + + cpu_set(cpu_id, cpu_online_map); +} + +/*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*/ +/* Boot up CPUs common Routins */ +/*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*/ +static void __init show_mp_info(int nr_cpu) +{ + int i; + char cpu_model0[17], cpu_model1[17], cpu_ver[9]; + + strncpy(cpu_model0, (char *)M32R_FPGA_CPU_NAME_ADDR, 16); + strncpy(cpu_model1, (char *)M32R_FPGA_MODEL_ID_ADDR, 16); + strncpy(cpu_ver, (char *)M32R_FPGA_VERSION_ADDR, 8); + + cpu_model0[16] = '\0'; + for (i = 15 ; i >= 0 ; i--) { + if (cpu_model0[i] != ' ') + break; + cpu_model0[i] = '\0'; + } + cpu_model1[16] = '\0'; + for (i = 15 ; i >= 0 ; i--) { + if (cpu_model1[i] != ' ') + break; + cpu_model1[i] = '\0'; + } + cpu_ver[8] = '\0'; + for (i = 7 ; i >= 0 ; i--) { + if (cpu_ver[i] != ' ') + break; + cpu_ver[i] = '\0'; + } + + printk(KERN_INFO "M32R-mp information\n"); + printk(KERN_INFO " On-chip CPUs : %d\n", nr_cpu); + printk(KERN_INFO " CPU model : %s/%s(%s)\n", cpu_model0, + cpu_model1, cpu_ver); +} + +/* + * The bootstrap kernel entry code has set these up. Save them for + * a given CPU + */ +static void __init smp_store_cpu_info(int cpu_id) +{ + struct cpuinfo_m32r *ci = cpu_data + cpu_id; + + *ci = boot_cpu_data; + ci->loops_per_jiffy = loops_per_jiffy; +} + +static void __init show_cpu_info(int cpu_id) +{ + struct cpuinfo_m32r *ci = &cpu_data[cpu_id]; + + printk("CPU#%d : ", cpu_id); + +#define PRINT_CLOCK(name, value) \ + printk(name " clock %d.%02dMHz", \ + ((value) / 1000000), ((value) % 1000000) / 10000) + + PRINT_CLOCK("CPU", (int)ci->cpu_clock); + PRINT_CLOCK(", Bus", (int)ci->bus_clock); + printk(", loops_per_jiffy[%ld]\n", ci->loops_per_jiffy); +} + +/* + * the frequency of the profiling timer can be changed + * by writing a multiplier value into /proc/profile. + */ +int setup_profiling_timer(unsigned int multiplier) +{ + int i; + + /* + * Sanity check. [at least 500 APIC cycles should be + * between APIC interrupts as a rule of thumb, to avoid + * irqs flooding us] + */ + if ( (!multiplier) || (calibration_result / multiplier < 500)) + return -EINVAL; + + /* + * Set the new multiplier for each CPU. CPUs don't start using the + * new values until the next timer interrupt in which they do process + * accounting. At that time they also adjust their APIC timers + * accordingly. + */ + for (i = 0; i < NR_CPUS; ++i) + per_cpu(prof_multiplier, i) = multiplier; + + return 0; +} + +/* Initialize all maps between cpu number and apicids */ +static void __init init_cpu_to_physid(void) +{ + int i; + + for (i = 0 ; i < NR_CPUS ; i++) { + cpu_2_physid[i] = -1; + physid_2_cpu[i] = -1; + } +} + +/* + * set up a mapping between cpu and apicid. Uses logical apicids for multiquad, + * else physical apic ids + */ +static void __init map_cpu_to_physid(int cpu_id, int phys_id) +{ + physid_2_cpu[phys_id] = cpu_id; + cpu_2_physid[cpu_id] = phys_id; +} + +/* + * undo a mapping between cpu and apicid. Uses logical apicids for multiquad, + * else physical apic ids + */ +static void __init unmap_cpu_to_physid(int cpu_id, int phys_id) +{ + physid_2_cpu[phys_id] = -1; + cpu_2_physid[cpu_id] = -1; +} + diff --git a/arch/m32r/kernel/sys_m32r.c b/arch/m32r/kernel/sys_m32r.c new file mode 100644 index 000000000..f34fa19ac --- /dev/null +++ b/arch/m32r/kernel/sys_m32r.c @@ -0,0 +1,217 @@ +/* + * linux/arch/m32r/kernel/sys_m32r.c + * + * This file contains various random system calls that + * have a non-standard calling sequence on the Linux/M32R platform. + * + * Taken from i386 version. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +/* + * sys_tas() - test-and-set + * linuxthreads testing version + */ +#ifndef CONFIG_SMP +asmlinkage int sys_tas(int *addr) +{ + int oldval; + unsigned long flags; + + if (!access_ok(VERIFY_WRITE, addr, sizeof (int))) + return -EFAULT; + local_irq_save(flags); + oldval = *addr; + *addr = 1; + local_irq_restore(flags); + return oldval; +} +#else /* CONFIG_SMP */ +#include + +static spinlock_t tas_lock = SPIN_LOCK_UNLOCKED; + +asmlinkage int sys_tas(int *addr) +{ + int oldval; + + if (!access_ok(VERIFY_WRITE, addr, sizeof (int))) + return -EFAULT; + + spin_lock(&tas_lock); + oldval = *addr; + *addr = 1; + spin_unlock(&tas_lock); + + return oldval; +} +#endif /* CONFIG_SMP */ + +/* + * sys_pipe() is the normal C calling standard for creating + * a pipe. It's not the way Unix traditionally does this, though. + */ +asmlinkage int +sys_pipe(unsigned long r0, unsigned long r1, unsigned long r2, + unsigned long r3, unsigned long r4, unsigned long r5, + unsigned long r6, struct pt_regs regs) +{ + int fd[2]; + int error; + + error = do_pipe(fd); + if (!error) { + if (copy_to_user((void *)r0, (void *)fd, 2*sizeof(int))) + error = -EFAULT; + } + return error; +} + +asmlinkage long sys_mmap2(unsigned long addr, unsigned long len, + unsigned long prot, unsigned long flags, + unsigned long fd, unsigned long pgoff) +{ + int error = -EBADF; + struct file *file = NULL; + + flags &= ~(MAP_EXECUTABLE | MAP_DENYWRITE); + if (!(flags & MAP_ANONYMOUS)) { + file = fget(fd); + if (!file) + goto out; + } + + down_write(¤t->mm->mmap_sem); + error = do_mmap_pgoff(file, addr, len, prot, flags, pgoff); + up_write(¤t->mm->mmap_sem); + + if (file) + fput(file); +out: + return error; +} + +/* + * sys_ipc() is the de-multiplexer for the SysV IPC calls.. + * + * This is really horribly ugly. + */ +asmlinkage int sys_ipc(uint call, int first, int second, + int third, void __user *ptr, long fifth) +{ + int version, ret; + + version = call >> 16; /* hack for backward compatibility */ + call &= 0xffff; + + switch (call) { + case SEMOP: + return sys_semtimedop(first, (struct sembuf __user *)ptr, + second, NULL); + case SEMTIMEDOP: + return sys_semtimedop(first, (struct sembuf __user *)ptr, + second, (const struct timespec __user *)fifth); + case SEMGET: + return sys_semget (first, second, third); + case SEMCTL: { + union semun fourth; + if (!ptr) + return -EINVAL; + if (get_user(fourth.__pad, (void __user * __user *) ptr)) + return -EFAULT; + return sys_semctl (first, second, third, fourth); + } + + case MSGSND: + return sys_msgsnd (first, (struct msgbuf __user *) ptr, + second, third); + case MSGRCV: + switch (version) { + case 0: { + struct ipc_kludge tmp; + if (!ptr) + return -EINVAL; + + if (copy_from_user(&tmp, + (struct ipc_kludge __user *) ptr, + sizeof (tmp))) + return -EFAULT; + return sys_msgrcv (first, tmp.msgp, second, + tmp.msgtyp, third); + } + default: + return sys_msgrcv (first, + (struct msgbuf __user *) ptr, + second, fifth, third); + } + case MSGGET: + return sys_msgget ((key_t) first, second); + case MSGCTL: + return sys_msgctl (first, second, + (struct msqid_ds __user *) ptr); + case SHMAT: { + ulong raddr; + + if ((ret = verify_area(VERIFY_WRITE, (ulong __user *) third, + sizeof(ulong)))) + return ret; + ret = do_shmat (first, (char __user *) ptr, second, &raddr); + if (ret) + return ret; + return put_user (raddr, (ulong __user *) third); + } + case SHMDT: + return sys_shmdt ((char __user *)ptr); + case SHMGET: + return sys_shmget (first, second, third); + case SHMCTL: + return sys_shmctl (first, second, + (struct shmid_ds __user *) ptr); + default: + return -ENOSYS; + } +} + +asmlinkage int sys_uname(struct old_utsname * name) +{ + int err; + if (!name) + return -EFAULT; + down_read(&uts_sem); + err=copy_to_user(name, &system_utsname, sizeof (*name)); + up_read(&uts_sem); + return err?-EFAULT:0; +} + +asmlinkage int sys_cacheflush(void *addr, int bytes, int cache) +{ + /* This should flush more selectivly ... */ + _flush_cache_all(); + return 0; +} + +asmlinkage int sys_cachectl(char *addr, int nbytes, int op) +{ + /* Not implemented yet. */ + return -ENOSYS; +} + diff --git a/arch/m32r/kernel/time.c b/arch/m32r/kernel/time.c new file mode 100644 index 000000000..25a31668f --- /dev/null +++ b/arch/m32r/kernel/time.c @@ -0,0 +1,317 @@ +/* + * linux/arch/m32r/kernel/time.c + * + * Copyright (c) 2001, 2002 Hiroyuki Kondo, Hirokazu Takata, + * Hitoshi Yamamoto + * Taken from i386 version. + * Copyright (C) 1991, 1992, 1995 Linus Torvalds + * Copyright (C) 1996, 1997, 1998 Ralf Baechle + * + * This file contains the time handling details for PC-style clocks as + * found in some MIPS systems. + * + * Some code taken from sh version. + * Copyright (C) 1999 Tetsuya Okada & Niibe Yutaka + * Copyright (C) 2000 Philipp Rumpf + */ + +#undef DEBUG_TIMER + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include + +#ifdef CONFIG_SMP +extern void send_IPI_allbutself(int, int); +extern void smp_local_timer_interrupt(struct pt_regs *); +#endif + +u64 jiffies_64 = INITIAL_JIFFIES; + +EXPORT_SYMBOL(jiffies_64); + +extern unsigned long wall_jiffies; +#define TICK_SIZE (tick_nsec / 1000) + +/* + * Change this if you have some constant time drift + */ + +/* This is for machines which generate the exact clock. */ +#define USECS_PER_JIFFY (1000000/HZ) + +static unsigned long latch; + +static unsigned long do_gettimeoffset(void) +{ + unsigned long elapsed_time = 0; /* [us] */ + +#if defined(CONFIG_CHIP_M32102) || defined(CONFIG_CHIP_XNUX2) \ + || defined(CONFIG_CHIP_VDEC2) || defined(CONFIG_CHIP_M32700) \ + || defined(CONFIG_CHIP_OPSP) +#ifndef CONFIG_SMP + + unsigned long count; + + /* timer count may underflow right here */ + count = inl(M32R_MFT2CUT_PORTL); + + if (inl(M32R_ICU_CR18_PORTL) & 0x00000100) /* underflow check */ + count = 0; + + count = (latch - count) * TICK_SIZE; + elapsed_time = (count + latch / 2) / latch; + /* NOTE: LATCH is equal to the "interval" value (= reload count). */ + +#else /* CONFIG_SMP */ + unsigned long count; + static unsigned long p_jiffies = -1; + static unsigned long p_count = 0; + + /* timer count may underflow right here */ + count = inl(M32R_MFT2CUT_PORTL); + + if (jiffies == p_jiffies && count > p_count) + count = 0; + + p_jiffies = jiffies; + p_count = count; + + count = (latch - count) * TICK_SIZE; + elapsed_time = (count + latch / 2) / latch; + /* NOTE: LATCH is equal to the "interval" value (= reload count). */ +#endif /* CONFIG_SMP */ +#elif defined(CONFIG_CHIP_M32310) +#warning do_gettimeoffse not implemented +#else +#error no chip configuration +#endif + + return elapsed_time; +} + +/* + * This version of gettimeofday has near microsecond resolution. + */ +void do_gettimeofday(struct timeval *tv) +{ + unsigned long seq; + unsigned long usec, sec; + unsigned long max_ntp_tick = tick_usec - tickadj; + + do { + unsigned long lost; + + seq = read_seqbegin(&xtime_lock); + + usec = do_gettimeoffset(); + lost = jiffies - wall_jiffies; + + /* + * If time_adjust is negative then NTP is slowing the clock + * so make sure not to go into next possible interval. + * Better to lose some accuracy than have time go backwards.. + */ + if (unlikely(time_adjust < 0)) { + usec = min(usec, max_ntp_tick); + if (lost) + usec += lost * max_ntp_tick; + } else if (unlikely(lost)) + usec += lost * tick_usec; + + sec = xtime.tv_sec; + usec += (xtime.tv_nsec / 1000); + } while (read_seqretry(&xtime_lock, seq)); + + while (usec >= 1000000) { + usec -= 1000000; + sec++; + } + + tv->tv_sec = sec; + tv->tv_usec = usec; +} + +EXPORT_SYMBOL(do_gettimeofday); + +int do_settimeofday(struct timespec *tv) +{ + time_t wtm_sec, sec = tv->tv_sec; + long wtm_nsec, nsec = tv->tv_nsec; + + if ((unsigned long)tv->tv_nsec >= NSEC_PER_SEC) + return -EINVAL; + + write_seqlock_irq(&xtime_lock); + /* + * This is revolting. We need to set "xtime" correctly. However, the + * value in this location is the value at the most recent update of + * wall time. Discover what correction gettimeofday() would have + * made, and then undo it! + */ + nsec -= do_gettimeoffset() * NSEC_PER_USEC; + nsec -= (jiffies - wall_jiffies) * TICK_NSEC; + + wtm_sec = wall_to_monotonic.tv_sec + (xtime.tv_sec - sec); + wtm_nsec = wall_to_monotonic.tv_nsec + (xtime.tv_nsec - nsec); + + set_normalized_timespec(&xtime, sec, nsec); + set_normalized_timespec(&wall_to_monotonic, wtm_sec, wtm_nsec); + + time_adjust = 0; /* stop active adjtime() */ + time_status |= STA_UNSYNC; + time_maxerror = NTP_PHASE_LIMIT; + time_esterror = NTP_PHASE_LIMIT; + write_sequnlock_irq(&xtime_lock); + clock_was_set(); + + return 0; +} + +EXPORT_SYMBOL(do_settimeofday); + +/* + * In order to set the CMOS clock precisely, set_rtc_mmss has to be + * called 500 ms after the second nowtime has started, because when + * nowtime is written into the registers of the CMOS clock, it will + * jump to the next second precisely 500 ms later. Check the Motorola + * MC146818A or Dallas DS12887 data sheet for details. + * + * BUG: This routine does not handle hour overflow properly; it just + * sets the minutes. Usually you won't notice until after reboot! + */ +static __inline__ int set_rtc_mmss(unsigned long nowtime) +{ + return 0; +} + +/* last time the cmos clock got updated */ +static long last_rtc_update = 0; + +/* + * timer_interrupt() needs to keep up the real-time clock, + * as well as call the "do_timer()" routine every clocktick + */ +static __inline__ void do_timer_interrupt(int irq, void *dev_id, + struct pt_regs * regs) +{ + do_timer(regs); + + /* + * If we have an externally synchronized Linux clock, then update + * CMOS clock accordingly every ~11 minutes. Set_rtc_mmss() has to be + * called as close as possible to 500 ms before the new second starts. + */ + if ((time_status & STA_UNSYNC) == 0 + && xtime.tv_sec > last_rtc_update + 660 + && (xtime.tv_nsec / 1000) >= 500000 - ((unsigned)TICK_SIZE) / 2 + && (xtime.tv_nsec / 1000) <= 500000 + ((unsigned)TICK_SIZE) / 2) + { + if (set_rtc_mmss(xtime.tv_sec) == 0) + last_rtc_update = xtime.tv_sec; + else /* do it again in 60 s */ + last_rtc_update = xtime.tv_sec - 600; + } + /* As we return to user mode fire off the other CPU schedulers.. + this is basically because we don't yet share IRQ's around. + This message is rigged to be safe on the 386 - basically it's + a hack, so don't look closely for now.. */ + +#ifdef CONFIG_SMP + smp_local_timer_interrupt(regs); +#endif +} + +irqreturn_t timer_interrupt(int irq, void *dev_id, struct pt_regs *regs) +{ + write_seqlock(&xtime_lock); + do_timer_interrupt(irq, NULL, regs); + write_sequnlock(&xtime_lock); + +#ifndef CONFIG_SMP + profile_tick(CPU_PROFILING, regs); +#endif + + return IRQ_HANDLED; +} + +struct irqaction irq0 = { timer_interrupt, SA_INTERRUPT, CPU_MASK_NONE, + "MFT2", NULL, NULL }; + +void __init time_init(void) +{ + unsigned int epoch, year, mon, day, hour, min, sec; + + sec = min = hour = day = mon = year = 0; + epoch = 0; + + year = 23; + mon = 4; + day = 17; + + /* Attempt to guess the epoch. This is the same heuristic as in rtc.c + so no stupid things will happen to timekeeping. Who knows, maybe + Ultrix also uses 1952 as epoch ... */ + if (year > 10 && year < 44) + epoch = 1980; + else if (year < 96) + epoch = 1952; + year += epoch; + + xtime.tv_sec = mktime(year, mon, day, hour, min, sec); + xtime.tv_nsec = (INITIAL_JIFFIES % HZ) * (NSEC_PER_SEC / HZ); + wall_to_monotonic.tv_sec = -xtime.tv_sec; + wall_to_monotonic.tv_nsec = -xtime.tv_nsec; + +#if defined(CONFIG_CHIP_M32102) || defined(CONFIG_CHIP_XNUX2) \ + || defined(CONFIG_CHIP_VDEC2) || defined(CONFIG_CHIP_M32700) \ + || defined(CONFIG_CHIP_OPSP) + + /* M32102 MFT setup */ + setup_irq(M32R_IRQ_MFT2, &irq0); + { + unsigned long bus_clock; + unsigned short divide; + + bus_clock = boot_cpu_data.bus_clock; + divide = boot_cpu_data.timer_divide; + latch = (bus_clock/divide + HZ / 2) / HZ; + + printk("Timer start : latch = %ld\n", latch); + + outl((M32R_MFTMOD_CC_MASK | M32R_MFTMOD_TCCR \ + |M32R_MFTMOD_CSSEL011), M32R_MFT2MOD_PORTL); + outl(latch, M32R_MFT2RLD_PORTL); + outl(latch, M32R_MFT2CUT_PORTL); + outl(0, M32R_MFT2CMPRLD_PORTL); + outl((M32R_MFTCR_MFT2MSK|M32R_MFTCR_MFT2EN), M32R_MFTCR_PORTL); + } + +#elif defined(CONFIG_CHIP_M32310) +#warning time_init not implemented +#else +#error no chip configuration +#endif +} + +/* + * Scheduler clock - returns current time in nanosec units. + */ +unsigned long long sched_clock(void) +{ + return (unsigned long long)jiffies * (1000000000 / HZ); +} + diff --git a/arch/m32r/kernel/traps.c b/arch/m32r/kernel/traps.c new file mode 100644 index 000000000..87fdc4ddc --- /dev/null +++ b/arch/m32r/kernel/traps.c @@ -0,0 +1,330 @@ +/* + * linux/arch/m32r/kernel/traps.c + * + * Copyright (C) 2001, 2002 Hirokazu Takata, Hiroyuki Kondo, + * Hitoshi Yamamoto + */ + +/* $Id$ */ + +/* + * 'traps.c' handles hardware traps and faults after we have saved some + * state in 'entry.S'. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include + +#include + +asmlinkage void alignment_check(void); +asmlinkage void ei_handler(void); +asmlinkage void rie_handler(void); +asmlinkage void debug_trap(void); +asmlinkage void cache_flushing_handler(void); + +#ifdef CONFIG_SMP +extern void smp_reschedule_interrupt(void); +extern void smp_invalidate_interrupt(void); +extern void smp_call_function_interrupt(void); +extern void smp_ipi_timer_interrupt(void); +extern void smp_flush_cache_all_interrupt(void); + +/* + * for Boot AP function + */ +asm ( + " .section .eit_vector4,\"ax\" \n" + " .global _AP_RE \n" + " .global startup_AP \n" + "_AP_RE: \n" + " .fill 32, 4, 0 \n" + "_AP_EI: bra startup_AP \n" + " .previous \n" +); +#endif /* CONFIG_SMP */ + +extern unsigned long eit_vector[]; +#define BRA_INSN(func, entry) \ + ((unsigned long)func - (unsigned long)eit_vector - entry*4)/4 \ + + 0xff000000UL + +void set_eit_vector_entries(void) +{ + extern void default_eit_handler(void); + extern void system_call(void); + extern void pie_handler(void); + extern void ace_handler(void); + extern void tme_handler(void); + extern void _flush_cache_copyback_all(void); + + eit_vector[0] = 0xd0c00001; /* seth r0, 0x01 */ + eit_vector[1] = BRA_INSN(default_eit_handler, 1); + eit_vector[4] = 0xd0c00010; /* seth r0, 0x10 */ + eit_vector[5] = BRA_INSN(default_eit_handler, 5); + eit_vector[8] = BRA_INSN(rie_handler, 8); + eit_vector[12] = BRA_INSN(alignment_check, 12); + eit_vector[16] = 0xff000000UL; + eit_vector[17] = BRA_INSN(debug_trap, 17); + eit_vector[18] = BRA_INSN(system_call, 18); + eit_vector[19] = 0xff000000UL; + eit_vector[20] = 0xff000000UL; + eit_vector[21] = 0xff000000UL; + eit_vector[22] = 0xff000000UL; + eit_vector[23] = 0xff000000UL; + eit_vector[24] = 0xff000000UL; + eit_vector[25] = 0xff000000UL; + eit_vector[26] = 0xff000000UL; + eit_vector[27] = 0xff000000UL; + eit_vector[28] = BRA_INSN(cache_flushing_handler, 28); + eit_vector[29] = 0xff000000UL; + eit_vector[30] = 0xff000000UL; + eit_vector[31] = 0xff000000UL; + eit_vector[32] = BRA_INSN(ei_handler, 32); + eit_vector[64] = BRA_INSN(pie_handler, 64); + eit_vector[68] = BRA_INSN(ace_handler, 68); + eit_vector[72] = BRA_INSN(tme_handler, 72); +#ifdef CONFIG_SMP + eit_vector[184] = (unsigned long)smp_reschedule_interrupt; + eit_vector[185] = (unsigned long)smp_invalidate_interrupt; + eit_vector[186] = (unsigned long)smp_call_function_interrupt; + eit_vector[187] = (unsigned long)smp_ipi_timer_interrupt; + eit_vector[188] = (unsigned long)smp_flush_cache_all_interrupt; + eit_vector[189] = 0; + eit_vector[190] = 0; + eit_vector[191] = 0; +#endif + _flush_cache_copyback_all(); +} + +void __init trap_init(void) +{ + set_eit_vector_entries(); + + /* + * Should be a barrier for any external CPU state. + */ + cpu_init(); +} + +int kstack_depth_to_print = 24; + +void show_trace(struct task_struct *task, unsigned long *stack) +{ + unsigned long addr; + + if (!stack) + stack = (unsigned long*)&stack; + + printk("Call Trace: "); + while (!kstack_end(stack)) { + addr = *stack++; + if (__kernel_text_address(addr)) { + printk("[<%08lx>] ", addr); + print_symbol("%s\n", addr); + } + } + printk("\n"); +} + +void show_stack(struct task_struct *task, unsigned long *sp) +{ + unsigned long *stack; + int i; + + /* + * debugging aid: "show_stack(NULL);" prints the + * back trace for this cpu. + */ + + if(sp==NULL) { + if (task) + sp = (unsigned long *)task->thread.sp; + else + sp=(unsigned long*)&sp; + } + + stack = sp; + for(i=0; i < kstack_depth_to_print; i++) { + if (kstack_end(stack)) + break; + if (i && ((i % 4) == 0)) + printk("\n "); + printk("%08lx ", *stack++); + } + printk("\n"); + show_trace(task, sp); +} + +void dump_stack(void) +{ + unsigned long stack; + + show_trace(current, &stack); +} + +EXPORT_SYMBOL(dump_stack); + +static void show_registers(struct pt_regs *regs) +{ + int i = 0; + int in_kernel = 1; + unsigned long sp; + + printk("CPU: %d\n", smp_processor_id()); + show_regs(regs); + + sp = (unsigned long) (1+regs); + if (user_mode(regs)) { + in_kernel = 0; + sp = regs->spu; + printk("SPU: %08lx\n", sp); + } else { + printk("SPI: %08lx\n", sp); + } + printk("Process %s (pid: %d, process nr: %d, stackpage=%08lx)", + current->comm, current->pid, 0xffff & i, 4096+(unsigned long)current); + + /* + * When in-kernel, we also print out the stack and code at the + * time of the fault.. + */ + if (in_kernel) { + printk("\nStack: "); + show_stack(current, (unsigned long*) sp); + + printk("\nCode: "); + if (regs->bpc < PAGE_OFFSET) + goto bad; + + for(i=0;i<20;i++) { + unsigned char c; + if (__get_user(c, &((unsigned char*)regs->bpc)[i])) { +bad: + printk(" Bad PC value."); + break; + } + printk("%02x ", c); + } + } + printk("\n"); +} + +spinlock_t die_lock = SPIN_LOCK_UNLOCKED; + +void die(const char * str, struct pt_regs * regs, long err) +{ + console_verbose(); + spin_lock_irq(&die_lock); + bust_spinlocks(1); + printk("%s: %04lx\n", str, err & 0xffff); + show_registers(regs); + bust_spinlocks(0); + spin_unlock_irq(&die_lock); + do_exit(SIGSEGV); +} + +static __inline__ void die_if_kernel(const char * str, + struct pt_regs * regs, long err) +{ + if (!user_mode(regs)) + die(str, regs, err); +} + +static __inline__ void do_trap(int trapnr, int signr, const char * str, + struct pt_regs * regs, long error_code, siginfo_t *info) +{ + if (user_mode(regs)) { + /* trap_signal */ + struct task_struct *tsk = current; + tsk->thread.error_code = error_code; + tsk->thread.trap_no = trapnr; + if (info) + force_sig_info(signr, info, tsk); + else + force_sig(signr, tsk); + return; + } else { + /* kernel_trap */ + if (!fixup_exception(regs)) + die(str, regs, error_code); + return; + } +} + +#define DO_ERROR(trapnr, signr, str, name) \ +asmlinkage void do_##name(struct pt_regs * regs, long error_code) \ +{ \ + do_trap(trapnr, signr, 0, regs, error_code, NULL); \ +} + +#define DO_ERROR_INFO(trapnr, signr, str, name, sicode, siaddr) \ +asmlinkage void do_##name(struct pt_regs * regs, long error_code) \ +{ \ + siginfo_t info; \ + info.si_signo = signr; \ + info.si_errno = 0; \ + info.si_code = sicode; \ + info.si_addr = (void __user *)siaddr; \ + do_trap(trapnr, signr, str, regs, error_code, &info); \ +} + +DO_ERROR( 1, SIGTRAP, "debug trap", debug_trap) +DO_ERROR_INFO(0x20, SIGILL, "reserved instruction ", rie_handler, ILL_ILLOPC, regs->bpc) +DO_ERROR_INFO(0x100, SIGILL, "privilege instruction", pie_handler, ILL_PRVOPC, regs->bpc) + +extern int handle_unaligned_access(unsigned long, struct pt_regs *); + +/* This code taken from arch/sh/kernel/traps.c */ +asmlinkage void do_alignment_check(struct pt_regs *regs, long error_code) +{ + mm_segment_t oldfs; + unsigned long insn; + int tmp; + + oldfs = get_fs(); + + if (user_mode(regs)) { + local_irq_enable(); + current->thread.error_code = error_code; + current->thread.trap_no = 0x17; + + set_fs(USER_DS); + if (copy_from_user(&insn, (void *)regs->bpc, 4)) { + set_fs(oldfs); + goto uspace_segv; + } + tmp = handle_unaligned_access(insn, regs); + set_fs(oldfs); + + if (!tmp) + return; + + uspace_segv: + printk(KERN_NOTICE "Killing process \"%s\" due to unaligned " + "access\n", current->comm); + force_sig(SIGSEGV, current); + } else { + set_fs(KERNEL_DS); + if (copy_from_user(&insn, (void *)regs->bpc, 4)) { + set_fs(oldfs); + die("insn faulting in do_address_error", regs, 0); + } + handle_unaligned_access(insn, regs); + set_fs(oldfs); + } +} + diff --git a/arch/m32r/kernel/vmlinux.lds.S b/arch/m32r/kernel/vmlinux.lds.S new file mode 100644 index 000000000..729a2645a --- /dev/null +++ b/arch/m32r/kernel/vmlinux.lds.S @@ -0,0 +1,143 @@ +/* ld script to make M32R Linux kernel + */ + +#include +#include +#include +#include + +OUTPUT_ARCH(m32r) +ENTRY(startup_32) +#if defined(__LITTLE_ENDIAN__) + jiffies = jiffies_64; +#else + jiffies = jiffies_64 + 4; +#endif +SECTIONS +{ + . = CONFIG_MEMORY_START + __PAGE_OFFSET; + eit_vector = .; + + . = . + 0x1000; + .empty_zero_page : { *(.empty_zero_page) } = 0 + + /* read-only */ + _text = .; /* Text and read-only data */ + .boot : { *(.boot) } = 0 + .text : { + *(.text) + SCHED_TEXT + LOCK_TEXT + *(.fixup) + *(.gnu.warning) + } = 0x9090 +#ifdef CONFIG_SMP + . = ALIGN(65536); + .eit_vector4 : { *(.eit_vector4) } +#endif + _etext = .; /* End of text section */ + + . = ALIGN(16); /* Exception table */ + __start___ex_table = .; + __ex_table : { *(__ex_table) } + __stop___ex_table = .; + + RODATA + + /* writeable */ + .data : { /* Data */ + *(.spu) + *(.spi) + *(.data) + CONSTRUCTORS + } + + . = ALIGN(4096); + __nosave_begin = .; + .data_nosave : { *(.data.nosave) } + . = ALIGN(4096); + __nosave_end = .; + + . = ALIGN(4096); + .data.page_aligned : { *(.data.idt) } + + . = ALIGN(32); + .data.cacheline_aligned : { *(.data.cacheline_aligned) } + + _edata = .; /* End of data section */ + + . = ALIGN(8192); /* init_task */ + .data.init_task : { *(.data.init_task) } + + /* will be freed after init */ + . = ALIGN(4096); /* Init code and data */ + __init_begin = .; + .init.text : { + _sinittext = .; + *(.init.text) + _einittext = .; + } + .init.data : { *(.init.data) } + . = ALIGN(16); + __setup_start = .; + .init.setup : { *(.init.setup) } + __setup_end = .; + __initcall_start = .; + .initcall.init : { + *(.initcall1.init) + *(.initcall2.init) + *(.initcall3.init) + *(.initcall4.init) + *(.initcall5.init) + *(.initcall6.init) + *(.initcall7.init) + } + __initcall_end = .; + __con_initcall_start = .; + .con_initcall.init : { *(.con_initcall.init) } + __con_initcall_end = .; + SECURITY_INIT + . = ALIGN(4); + __alt_instructions = .; + .altinstructions : { *(.altinstructions) } + __alt_instructions_end = .; + .altinstr_replacement : { *(.altinstr_replacement) } + /* .exit.text is discard at runtime, not link time, to deal with references + from .altinstructions and .eh_frame */ + .exit.text : { *(.exit.text) } + .exit.data : { *(.exit.data) } + . = ALIGN(4096); + __initramfs_start = .; + .init.ramfs : { *(.init.ramfs) } + __initramfs_end = .; + . = ALIGN(32); + __per_cpu_start = .; + .data.percpu : { *(.data.percpu) } + __per_cpu_end = .; + . = ALIGN(4096); + __init_end = .; + /* freed after init ends here */ + + __bss_start = .; /* BSS */ + .bss : { *(.bss) } + . = ALIGN(4); + __bss_stop = .; + + _end = . ; + + /* Sections to be discarded */ + /DISCARD/ : { + *(.exit.text) + *(.exit.data) + *(.exitcall.exit) + } + + /* Stabs debugging sections. */ + .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) } +} diff --git a/arch/m32r/lib/Makefile b/arch/m32r/lib/Makefile new file mode 100644 index 000000000..e632d10c7 --- /dev/null +++ b/arch/m32r/lib/Makefile @@ -0,0 +1,7 @@ +# +# Makefile for M32R-specific library files.. +# + +lib-y := checksum.o ashxdi3.o memset.o memcpy.o getuser.o \ + putuser.o delay.o strlen.o usercopy.o csum_partial_copy.o + diff --git a/arch/m32r/lib/ashxdi3.S b/arch/m32r/lib/ashxdi3.S new file mode 100644 index 000000000..78effca9d --- /dev/null +++ b/arch/m32r/lib/ashxdi3.S @@ -0,0 +1,297 @@ +/* + * linux/arch/m32r/lib/ashxdi3.S + * + * Copyright (C) 2001,2002 Hiroyuki Kondo, and Hirokazu Takata + * + */ +/* $Id$ */ + +#include + +; +; input (r0,r1) src +; input r2 shift val +; r3 scratch +; output (r0,r1) +; + +#ifdef CONFIG_ISA_DUAL_ISSUE + +#ifndef __LITTLE_ENDIAN__ + + .text + .align 4 + .globl __ashrdi3 +__ashrdi3: + cmpz r2 || ldi r3, #32 + jc r14 || cmpu r2, r3 + bc 1f + ; case 32 =< shift + mv r1, r0 || srai r0, #31 + addi r2, #-32 + sra r1, r2 + jmp r14 + .fillinsn +1: ; case shift <32 + mv r3, r0 || srl r1, r2 + sra r0, r2 || neg r2, r2 + sll r3, r2 + or r1, r3 || jmp r14 + + .align 4 + .globl __ashldi3 + .globl __lshldi3 +__ashldi3: +__lshldi3: + cmpz r2 || ldi r3, #32 + jc r14 || cmpu r2, r3 + bc 1f + ; case 32 =< shift + mv r0, r1 || addi r2, #-32 + sll r0, r2 || ldi r1, #0 + jmp r14 + .fillinsn +1: ; case shift <32 + mv r3, r1 || sll r0, r2 + sll r1, r2 || neg r2, r2 + srl r3, r2 + or r0, r3 || jmp r14 + + .align 4 + .globl __lshrdi3 +__lshrdi3: + cmpz r2 || ldi r3, #32 + jc r14 || cmpu r2, r3 + bc 1f + ; case 32 =< shift + mv r1, r0 || addi r2, #-32 + ldi r0, #0 || srl r1, r2 + jmp r14 + .fillinsn +1: ; case shift <32 + mv r3, r0 || srl r1, r2 + srl r0, r2 || neg r2, r2 + sll r3, r2 + or r1, r3 || jmp r14 + +#else /* LITTLE_ENDIAN */ + + .text + .align 4 + .globl __ashrdi3 +__ashrdi3: + cmpz r2 || ldi r3, #32 + jc r14 || cmpu r2, r3 + bc 1f + ; case 32 =< shift + mv r0, r1 || srai r1, #31 + addi r2, #-32 + sra r0, r2 + jmp r14 + .fillinsn +1: ; case shift <32 + mv r3, r1 || srl r0, r2 + sra r1, r2 || neg r2, r2 + sll r3, r2 + or r0, r3 || jmp r14 + + .align 4 + .globl __ashldi3 + .globl __lshldi3 +__ashldi3: +__lshldi3: + cmpz r2 || ldi r3, #32 + jc r14 || cmpu r2, r3 + bc 1f + ; case 32 =< shift + mv r1, r0 || addi r2, #-32 + sll r1, r2 || ldi r0, #0 + jmp r14 + .fillinsn +1: ; case shift <32 + mv r3, r0 || sll r1, r2 + sll r0, r2 || neg r2, r2 + srl r3, r2 + or r1, r3 || jmp r14 + + .align 4 + .globl __lshrdi3 +__lshrdi3: + cmpz r2 || ldi r3, #32 + jc r14 || cmpu r2, r3 + bc 1f + ; case 32 =< shift + mv r0, r1 || addi r2, #-32 + ldi r1, #0 || srl r0, r2 + jmp r14 + .fillinsn +1: ; case shift <32 + mv r3, r1 || srl r0, r2 + srl r1, r2 || neg r2, r2 + sll r3, r2 + or r0, r3 || jmp r14 + +#endif + +#else /* not CONFIG_ISA_DUAL_ISSUE */ + +#ifndef __LITTLE_ENDIAN__ + + .text + .align 4 + .globl __ashrdi3 +__ashrdi3: + beqz r2, 2f + cmpui r2, #32 + bc 1f + ; case 32 =< shift + mv r1, r0 + srai r0, #31 + addi r2, #-32 + sra r1, r2 + jmp r14 + .fillinsn +1: ; case shift <32 + mv r3, r0 + srl r1, r2 + sra r0, r2 + neg r2, r2 + sll r3, r2 + or r1, r3 + .fillinsn +2: + jmp r14 + + .align 4 + .globl __ashldi3 + .globl __lshldi3 +__ashldi3: +__lshldi3: + beqz r2, 2f + cmpui r2, #32 + bc 1f + ; case 32 =< shift + mv r0, r1 + addi r2, #-32 + sll r0, r2 + ldi r1, #0 + jmp r14 + .fillinsn +1: ; case shift <32 + mv r3, r1 + sll r0, r2 + sll r1, r2 + neg r2, r2 + srl r3, r2 + or r0, r3 + .fillinsn +2: + jmp r14 + + .align 4 + .globl __lshrdi3 +__lshrdi3: + beqz r2, 2f + cmpui r2, #32 + bc 1f + ; case 32 =< shift + mv r1, r0 + ldi r0, #0 + addi r2, #-32 + srl r1, r2 + jmp r14 + .fillinsn +1: ; case shift <32 + mv r3, r0 + srl r1, r2 + srl r0, r2 + neg r2, r2 + sll r3, r2 + or r1, r3 + .fillinsn +2: + jmp r14 + +#else + + .text + .align 4 + .globl __ashrdi3 +__ashrdi3: + beqz r2, 2f + cmpui r2, #32 + bc 1f + ; case 32 =< shift + mv r0, r1 + srai r1, #31 + addi r2, #-32 + sra r0, r2 + jmp r14 + .fillinsn +1: ; case shift <32 + mv r3, r1 + srl r0, r2 + sra r1, r2 + neg r2, r2 + sll r3, r2 + or r0, r3 + .fillinsn +2: + jmp r14 + + .align 4 + .globl __ashldi3 + .globl __lshldi3 +__ashldi3: +__lshldi3: + beqz r2, 2f + cmpui r2, #32 + bc 1f + ; case 32 =< shift + mv r1, r0 + addi r2, #-32 + sll r1, r2 + ldi r0, #0 + jmp r14 + .fillinsn +1: ; case shift <32 + mv r3, r0 + sll r1, r2 + sll r0, r2 + neg r2, r2 + srl r3, r2 + or r1, r3 + .fillinsn +2: + jmp r14 + + .align 4 + .globl __lshrdi3 +__lshrdi3: + beqz r2, 2f + cmpui r2, #32 + bc 1f + ; case 32 =< shift + mv r0, r1 + ldi r1, #0 + addi r2, #-32 + srl r0, r2 + jmp r14 + .fillinsn +1: ; case shift <32 + mv r3, r1 + srl r0, r2 + srl r1, r2 + neg r2, r2 + sll r3, r2 + or r0, r3 + .fillinsn +2: + jmp r14 + +#endif + +#endif /* not CONFIG_ISA_DUAL_ISSUE */ + + .end + diff --git a/arch/m32r/lib/checksum.S b/arch/m32r/lib/checksum.S new file mode 100644 index 000000000..f6fc1bdb8 --- /dev/null +++ b/arch/m32r/lib/checksum.S @@ -0,0 +1,322 @@ +/* + * INET An implementation of the TCP/IP protocol suite for the LINUX + * operating system. INET is implemented using the BSD Socket + * interface as the means of communication with the user level. + * + * IP/TCP/UDP checksumming routines + * + * Authors: Jorge Cwik, + * Arnt Gulbrandsen, + * Tom May, + * Pentium Pro/II routines: + * Alexander Kjeldaas + * Finn Arne Gangstad + * Lots of code moved from tcp.c and ip.c; see those files + * for more names. + * + * Changes: Ingo Molnar, converted csum_partial_copy() to 2.1 exception + * handling. + * Andi Kleen, add zeroing on error + * converted to pure assembler + * Hirokazu Takata,Hiroyuki Kondo rewrite for the m32r architecture. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ +/* $Id$ */ + + +#include +#include +#include +#include + +/* + * computes a partial checksum, e.g. for TCP/UDP fragments + */ + +/* +unsigned int csum_partial(const unsigned char * buff, int len, unsigned int sum) + */ + + +#ifdef CONFIG_ISA_DUAL_ISSUE + + /* + * Experiments with Ethernet and SLIP connections show that buff + * is aligned on either a 2-byte or 4-byte boundary. We get at + * least a twofold speedup on 486 and Pentium if it is 4-byte aligned. + * Fortunately, it is easy to convert 2-byte alignment to 4-byte + * alignment for the unrolled loop. + */ + + .text +ENTRY(csum_partial) + ; Function args + ; r0: unsigned char *buff + ; r1: int len + ; r2: unsigned int sum + + push r2 || ldi r2, #0 + and3 r7, r0, #1 ; Check alignment. + beqz r7, 1f ; Jump if alignment is ok. + ; 1-byte mis aligned + ldub r4, @r0 || addi r0, #1 + ; clear c-bit || Alignment uses up bytes. + cmp r0, r0 || addi r1, #-1 + ldi r3, #0 || addx r2, r4 + addx r2, r3 + .fillinsn +1: + and3 r4, r0, #2 ; Check alignment. + beqz r4, 2f ; Jump if alignment is ok. + ; clear c-bit || Alignment uses up two bytes. + cmp r0, r0 || addi r1, #-2 + bgtz r1, 1f ; Jump if we had at least two bytes. + bra 4f || addi r1, #2 + .fillinsn ; len(r1) was < 2. Deal with it. +1: + ; 2-byte aligned + lduh r4, @r0 || ldi r3, #0 + addx r2, r4 || addi r0, #2 + addx r2, r3 + .fillinsn +2: + ; 4-byte aligned + cmp r0, r0 ; clear c-bit + srl3 r6, r1, #5 + beqz r6, 2f + .fillinsn + +1: ld r3, @r0+ + ld r4, @r0+ ; +4 + ld r5, @r0+ ; +8 + ld r3, @r0+ || addx r2, r3 ; +12 + ld r4, @r0+ || addx r2, r4 ; +16 + ld r5, @r0+ || addx r2, r5 ; +20 + ld r3, @r0+ || addx r2, r3 ; +24 + ld r4, @r0+ || addx r2, r4 ; +28 + addx r2, r5 || addi r6, #-1 + addx r2, r3 + addx r2, r4 + bnez r6, 1b + + addx r2, r6 ; r6=0 + cmp r0, r0 ; This clears c-bit + .fillinsn +2: and3 r6, r1, #0x1c ; withdraw len + beqz r6, 4f + srli r6, #2 + .fillinsn + +3: ld r4, @r0+ || addi r6, #-1 + addx r2, r4 + bnez r6, 3b + + addx r2, r6 ; r6=0 + cmp r0, r0 ; This clears c-bit + .fillinsn +4: and3 r1, r1, #3 + beqz r1, 7f ; if len == 0 goto end + and3 r6, r1, #2 + beqz r6, 5f ; if len < 2 goto 5f(1byte) + lduh r4, @r0 || addi r0, #2 + addi r1, #-2 || slli r4, #16 + addx r2, r4 + beqz r1, 6f + .fillinsn +5: ldub r4, @r0 || ldi r1, #0 +#ifndef __LITTLE_ENDIAN__ + slli r4, #8 +#endif + addx r2, r4 + .fillinsn +6: addx r2, r1 + .fillinsn +7: + and3 r0, r2, #0xffff + srli r2, #16 + add r0, r2 + srl3 r2, r0, #16 + beqz r2, 1f + addi r0, #1 + and3 r0, r0, #0xffff + .fillinsn +1: + beqz r7, 1f ; swap the upper byte for the lower + and3 r2, r0, #0xff + srl3 r0, r0, #8 + slli r2, #8 + or r0, r2 + .fillinsn +1: + pop r2 || cmp r0, r0 + addx r0, r2 || ldi r2, #0 + addx r0, r2 + jmp r14 + +#else /* not CONFIG_ISA_DUAL_ISSUE */ + + /* + * Experiments with Ethernet and SLIP connections show that buff + * is aligned on either a 2-byte or 4-byte boundary. We get at + * least a twofold speedup on 486 and Pentium if it is 4-byte aligned. + * Fortunately, it is easy to convert 2-byte alignment to 4-byte + * alignment for the unrolled loop. + */ + + .text +ENTRY(csum_partial) + ; Function args + ; r0: unsigned char *buff + ; r1: int len + ; r2: unsigned int sum + + push r2 + ldi r2, #0 + and3 r7, r0, #1 ; Check alignment. + beqz r7, 1f ; Jump if alignment is ok. + ; 1-byte mis aligned + ldub r4, @r0 + addi r0, #1 + addi r1, #-1 ; Alignment uses up bytes. + cmp r0, r0 ; clear c-bit + ldi r3, #0 + addx r2, r4 + addx r2, r3 + .fillinsn +1: + and3 r4, r0, #2 ; Check alignment. + beqz r4, 2f ; Jump if alignment is ok. + addi r1, #-2 ; Alignment uses up two bytes. + cmp r0, r0 ; clear c-bit + bgtz r1, 1f ; Jump if we had at least two bytes. + addi r1, #2 ; len(r1) was < 2. Deal with it. + bra 4f + .fillinsn +1: + ; 2-byte aligned + lduh r4, @r0 + addi r0, #2 + ldi r3, #0 + addx r2, r4 + addx r2, r3 + .fillinsn +2: + ; 4-byte aligned + cmp r0, r0 ; clear c-bit + srl3 r6, r1, #5 + beqz r6, 2f + .fillinsn + +1: ld r3, @r0+ + ld r4, @r0+ ; +4 + ld r5, @r0+ ; +8 + addx r2, r3 + addx r2, r4 + addx r2, r5 + ld r3, @r0+ ; +12 + ld r4, @r0+ ; +16 + ld r5, @r0+ ; +20 + addx r2, r3 + addx r2, r4 + addx r2, r5 + ld r3, @r0+ ; +24 + ld r4, @r0+ ; +28 + addi r6, #-1 + addx r2, r3 + addx r2, r4 + bnez r6, 1b + addx r2, r6 ; r6=0 + cmp r0, r0 ; This clears c-bit + .fillinsn + +2: and3 r6, r1, #0x1c ; withdraw len + beqz r6, 4f + srli r6, #2 + .fillinsn + +3: ld r4, @r0+ + addi r6, #-1 + addx r2, r4 + bnez r6, 3b + addx r2, r6 ; r6=0 + cmp r0, r0 ; This clears c-bit + .fillinsn + +4: and3 r1, r1, #3 + beqz r1, 7f ; if len == 0 goto end + and3 r6, r1, #2 + beqz r6, 5f ; if len < 2 goto 5f(1byte) + + lduh r4, @r0 + addi r0, #2 + addi r1, #-2 + slli r4, #16 + addx r2, r4 + beqz r1, 6f + .fillinsn +5: ldub r4, @r0 +#ifndef __LITTLE_ENDIAN__ + slli r4, #8 +#endif + addx r2, r4 + .fillinsn +6: ldi r5, #0 + addx r2, r5 + .fillinsn +7: + and3 r0, r2, #0xffff + srli r2, #16 + add r0, r2 + srl3 r2, r0, #16 + beqz r2, 1f + addi r0, #1 + and3 r0, r0, #0xffff + .fillinsn +1: + beqz r7, 1f + mv r2, r0 + srl3 r0, r2, #8 + and3 r2, r2, #0xff + slli r2, #8 + or r0, r2 + .fillinsn +1: + pop r2 + cmp r0, r0 + addx r0, r2 + ldi r2, #0 + addx r0, r2 + jmp r14 + +#endif /* not CONFIG_ISA_DUAL_ISSUE */ + +/* +unsigned int csum_partial_copy_generic (const char *src, char *dst, + int len, int sum, int *src_err_ptr, int *dst_err_ptr) + */ + +/* + * Copy from ds while checksumming, otherwise like csum_partial + * + * The macros SRC and DST specify the type of access for the instruction. + * thus we can call a custom exception handler for all access types. + * + * FIXME: could someone double-check whether I haven't mixed up some SRC and + * DST definitions? It's damn hard to trigger all cases. I hope I got + * them all but there's no guarantee. + */ + +ENTRY(csum_partial_copy_generic) + nop + nop + nop + nop + jmp r14 + nop + nop + nop + diff --git a/arch/m32r/lib/csum_partial_copy.c b/arch/m32r/lib/csum_partial_copy.c new file mode 100644 index 000000000..22600fc08 --- /dev/null +++ b/arch/m32r/lib/csum_partial_copy.c @@ -0,0 +1,58 @@ +/* + * INET An implementation of the TCP/IP protocol suite for the LINUX + * operating system. INET is implemented using the BSD Socket + * interface as the means of communication with the user level. + * + * MIPS specific IP/TCP/UDP checksumming routines + * + * Authors: Ralf Baechle, + * Lots of code moved from tcp.c and ip.c; see those files + * for more names. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the 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 +#include + +#include +#include +#include +#include + +/* + * Copy while checksumming, otherwise like csum_partial + */ +unsigned int csum_partial_copy_nocheck (const char *src, char *dst, + int len, unsigned int sum) +{ + sum = csum_partial(src, len, sum); + memcpy(dst, src, len); + + return sum; +} +EXPORT_SYMBOL(csum_partial_copy_nocheck); + +/* + * Copy from userspace and compute checksum. If we catch an exception + * then zero the rest of the buffer. + */ +unsigned int csum_partial_copy_from_user (const char __user *src, char *dst, + int len, unsigned int sum, + int *err_ptr) +{ + int missing; + + missing = copy_from_user(dst, src, len); + if (missing) { + memset(dst + len - missing, 0, missing); + *err_ptr = -EFAULT; + } + + return csum_partial(dst, len-missing, sum); +} +EXPORT_SYMBOL(csum_partial_copy_from_user); diff --git a/arch/m32r/lib/delay.c b/arch/m32r/lib/delay.c new file mode 100644 index 000000000..7afe66e9d --- /dev/null +++ b/arch/m32r/lib/delay.c @@ -0,0 +1,126 @@ +/* + * linux/arch/m32r/lib/delay.c + * + * Copyright (c) 2002 Hitoshi Yamamoto, Hirokazu Takata + * Copyright (c) 2004 Hirokazu Takata + */ + +/* $Id$ */ + +#include +#include +#ifdef CONFIG_SMP +#include +#include +#include +#endif /* CONFIG_SMP */ +#include + +void __delay(unsigned long loops) +{ +#ifdef CONFIG_ISA_DUAL_ISSUE + __asm__ __volatile__ ( + "beqz %0, 2f \n\t" + "addi %0, #-1 \n\t" + + " .fillinsn \n\t" + "1: \n\t" + "cmpz %0 || addi %0, #-1 \n\t" + "bc 2f || cmpz %0 \n\t" + "bc 2f || addi %0, #-1 \n\t" + "cmpz %0 || addi %0, #-1 \n\t" + "bc 2f || cmpz %0 \n\t" + "bnc 1b || addi %0, #-1 \n\t" + " .fillinsn \n\t" + "2: \n\t" + : "+r" (loops) + : "r" (0) + : "cbit" + ); +#else + __asm__ __volatile__ ( + "beqz %0, 2f \n\t" + " .fillinsn \n\t" + "1: \n\t" + "addi %0, #-1 \n\t" + "blez %0, 2f \n\t" + "addi %0, #-1 \n\t" + "blez %0, 2f \n\t" + "addi %0, #-1 \n\t" + "blez %0, 2f \n\t" + "addi %0, #-1 \n\t" + "bgtz %0, 1b \n\t" + " .fillinsn \n\t" + "2:i \n\t" + : "+r" (loops) + : "r" (0) + ); +#endif +} + +void __const_udelay(unsigned long xloops) +{ +#if defined(CONFIG_ISA_M32R2) && defined(CONFIG_ISA_DSP_LEVEL2) + /* + * loops [1] = (xloops >> 32) [sec] * loops_per_jiffy [1/jiffy] + * * HZ [jiffy/sec] + * = (xloops >> 32) [sec] * (loops_per_jiffy * HZ) [1/sec] + * = (((xloops * loops_per_jiffy) >> 32) * HZ) [1] + * + * NOTE: + * - '[]' depicts variable's dimension in the above equation. + * - "rac" instruction rounds the accumulator in word size. + */ + __asm__ __volatile__ ( + "srli %0, #1 \n\t" + "mulwhi %0, %1 ; a0 \n\t" + "mulwu1 %0, %1 ; a1 \n\t" + "sadd ; a0 += (a1 >> 16) \n\t" + "rac a0, a0, #1 \n\t" + "mvfacmi %0, a0 \n\t" + : "+r" (xloops) + : "r" (current_cpu_data.loops_per_jiffy) + : "a0", "a1" + ); +#elif defined(CONFIG_ISA_M32R2) || defined(CONFIG_ISA_M32R) + /* + * u64 ull; + * ull = (u64)xloops * (u64)current_cpu_data.loops_per_jiffy; + * xloops = (ull >> 32); + */ + __asm__ __volatile__ ( + "and3 r4, %0, #0xffff \n\t" + "and3 r5, %1, #0xffff \n\t" + "mul r4, r5 \n\t" + "srl3 r6, %0, #16 \n\t" + "srli r4, #16 \n\t" + "mul r5, r6 \n\t" + "add r4, r5 \n\t" + "and3 r5, %0, #0xffff \n\t" + "srl3 r6, %1, #16 \n\t" + "mul r5, r6 \n\t" + "add r4, r5 \n\t" + "srl3 r5, %0, #16 \n\t" + "srli r4, #16 \n\t" + "mul r5, r6 \n\t" + "add r4, r5 \n\t" + "mv %0, r4 \n\t" + : "+r" (xloops) + : "r" (current_cpu_data.loops_per_jiffy) + : "r4", "r5", "r6" + ); +#else +#error unknown isa configuration +#endif + __delay(xloops * HZ); +} + +void __udelay(unsigned long usecs) +{ + __const_udelay(usecs * 0x000010c7); /* 2**32 / 1000000 (rounded up) */ +} + +void __ndelay(unsigned long nsecs) +{ + __const_udelay(nsecs * 0x00005); /* 2**32 / 1000000000 (rounded up) */ +} diff --git a/arch/m32r/lib/getuser.S b/arch/m32r/lib/getuser.S new file mode 100644 index 000000000..58a0db055 --- /dev/null +++ b/arch/m32r/lib/getuser.S @@ -0,0 +1,88 @@ +/* + * __get_user functions. + * + * (C) Copyright 2001 Hirokazu Takata + * + * These functions have a non-standard call interface + * to make them more efficient, especially as they + * return an error value in addition to the "real" + * return value. + */ + +#include + +/* + * __get_user_X + * + * Inputs: r0 contains the address + * + * Outputs: r0 is error code (0 or -EFAULT) + * r1 contains zero-extended value + * + * These functions should not modify any other registers, + * as they get called from within inline assembly. + */ + +#ifdef CONFIG_ISA_DUAL_ISSUE + + .text + .balign 4 + .globl __get_user_1 +__get_user_1: +1: ldub r1, @r0 || ldi r0, #0 + jmp r14 + + .balign 4 + .globl __get_user_2 +__get_user_2: +2: lduh r1, @r0 || ldi r0, #0 + jmp r14 + + .balign 4 + .globl __get_user_4 +__get_user_4: +3: ld r1, @r0 || ldi r0, #0 + jmp r14 + +bad_get_user: + ldi r1, #0 || ldi r0, #-14 + jmp r14 + +#else /* not CONFIG_ISA_DUAL_ISSUE */ + + .text + .balign 4 + .globl __get_user_1 +__get_user_1: +1: ldub r1, @r0 + ldi r0, #0 + jmp r14 + + .balign 4 + .globl __get_user_2 +__get_user_2: +2: lduh r1, @r0 + ldi r0, #0 + jmp r14 + + .balign 4 + .globl __get_user_4 +__get_user_4: +3: ld r1, @r0 + ldi r0, #0 + jmp r14 + +bad_get_user: + ldi r1, #0 + ldi r0, #-14 + jmp r14 + +#endif /* not CONFIG_ISA_DUAL_ISSUE */ + +.section __ex_table,"a" + .long 1b,bad_get_user + .long 2b,bad_get_user + .long 3b,bad_get_user +.previous + + .end diff --git a/arch/m32r/lib/memcpy.S b/arch/m32r/lib/memcpy.S new file mode 100644 index 000000000..800898a2d --- /dev/null +++ b/arch/m32r/lib/memcpy.S @@ -0,0 +1,95 @@ +/* + * linux/arch/m32r/lib/memcpy.S + * + * Copyright (C) 2001 Hiroyuki Kondo, and Hirokazu Takata + * Copyright (C) 2004 Hirokazu Takata + * + * void *memcopy(void *dst, const void *src, int n); + * + * dst: r0 + * src: r1 + * n : r2 + */ +/* $Id$ */ + + + .text +#include +#include +#include + +#ifdef CONFIG_ISA_DUAL_ISSUE + + .text +ENTRY(memcpy) +memcopy: + mv r4, r0 || mv r7, r0 + or r7, r1 || cmpz r2 + jc r14 || cmpeq r0, r1 ; return if r2=0 + jc r14 ; return if r0=r1 + + and3 r7, r7, #3 + bnez r7, byte_copy + srl3 r3, r2, #2 + and3 r2, r2, #3 + beqz r3, byte_copy + addi r4, #-4 +word_copy: + ld r7, @r1+ || addi r3, #-1 + st r7, @+r4 || cmpz r2 + bnez r3, word_copy + addi r4, #4 || jc r14 ; return if r2=0 +#if defined(CONFIG_ISA_M32R2) +byte_copy: + ldb r7, @r1 || addi r1, #1 + addi r2, #-1 || stb r7, @r4+ + bnez r2, byte_copy +#elif defined(CONFIG_ISA_M32R) +byte_copy: + ldb r7, @r1 || addi r1, #1 + addi r2, #-1 || stb r7, @r4 + addi r4, #1 + bnez r2, byte_copy +#else +#error unknown isa configuration +#endif +end_memcopy: + jmp r14 + +#else /* not CONFIG_ISA_DUAL_ISSUE */ + + .text +ENTRY(memcpy) +memcopy: + mv r4, r0 + mv r7, r0 + or r7, r1 + beq r0, r1, end_memcopy + beqz r2, end_memcopy + + and3 r7, r7, #3 + bnez r7, byte_copy + srl3 r3, r2, #2 + and3 r2, r2, #3 + beqz r3, byte_copy + addi r4, #-4 +word_copy: + ld r7, @r1+ + addi r3, #-1 + st r7, @+r4 + bnez r3, word_copy + beqz r2, end_memcopy + addi r4, #4 +byte_copy: + ldb r7, @r1 + addi r1, #1 + addi r2, #-1 + stb r7, @r4 + addi r4, #1 + bnez r2, byte_copy +end_memcopy: + jmp r14 + +#endif /* not CONFIG_ISA_DUAL_ISSUE */ + + .end diff --git a/arch/m32r/lib/memset.S b/arch/m32r/lib/memset.S new file mode 100644 index 000000000..7fe94b6c6 --- /dev/null +++ b/arch/m32r/lib/memset.S @@ -0,0 +1,178 @@ +/* + * linux/arch/m32r/lib/memset.S + * + * Copyright (C) 2001,2002 Hiroyuki Kondo, and Hirokazu Takata + * Copyright (C) 2004 Hirokazu Takata + * + * void *memset(void *dst, int val, int len); + * + * dst: r0 + * val: r1 + * len: r2 + * ret: r0 + * + */ +/* $Id$ */ + +#include + + .text + .global memset + +#ifdef CONFIG_ISA_DUAL_ISSUE + + .align 4 +memset: + mv r4, r0 || cmpz r2 + jc r14 + cmpui r2, #16 + bnc qword_align_check + cmpui r2, #4 + bc byte_set +word_align_check: /* len >= 4 */ + and3 r3, r4, #3 + beqz r3, word_set + addi r3, #-4 + neg r3, r3 /* r3 = -(r3 - 4) */ +align_word: + stb r1, @r4 || addi r4, #1 + addi r2, #-1 || addi r3, #-1 + bnez r3, align_word + cmpui r2, #4 + bc byte_set +word_set: + and3 r1, r1, #0x00ff /* r1: abababab <-- ??????ab */ + sll3 r3, r1, #8 + or r1, r3 || addi r4, #-4 + sll3 r3, r1, #16 + or r1, r3 || addi r2, #-4 +word_set_loop: + st r1, @+r4 || addi r2, #-4 + bgtz r2, word_set_loop + bnez r2, byte_set_wrap + st r1, @+r4 + jmp r14 + +qword_align_check: /* len >= 16 */ + and3 r3, r4, #15 + bnez r3, word_align_check +qword_set: + and3 r1, r1, #0x00ff /* r1: abababab <-- ??????ab */ + sll3 r3, r1, #8 + or r1, r3 || addi r4, #-4 + sll3 r3, r1, #16 + or r1, r3 || ldi r5, #16 +qword_set_loop: + ld r3, @(4,r4) /* cache line allocate */ + st r1, @+r4 || addi r2, #-16 + st r1, @+r4 || cmpu r2, r5 + st r1, @+r4 + st r1, @+r4 + bnc qword_set_loop || cmpz r2 + jc r14 +word_set_wrap: + cmpui r2, #4 + bc byte_set + addi r2, #-4 + bra word_set_loop + +byte_set_wrap: + addi r2, #4 + addi r4, #4 || cmpz r2 + jc r14 +#if defined(CONFIG_ISA_M32R2) +byte_set: + addi r2, #-1 || stb r1, @r4+ + bnez r2, byte_set +#elif defined(CONFIG_ISA_M32R) +byte_set: + addi r2, #-1 || stb r1, @r4 + addi r4, #1 + bnez r2, byte_set +#else +#error unknown isa configuration +#endif +end_memset: + jmp r14 + +#else /* not CONFIG_ISA_DUAL_ISSUE */ + + .align 4 +memset: + mv r4, r0 + beqz r2, end_memset + cmpui r2, #16 + bnc qword_align_check + cmpui r2, #4 + bc byte_set +word_align_check: /* len >= 4 */ + and3 r3, r4, #3 + beqz r3, word_set + addi r3, #-4 + neg r3, r3 /* r3 = -(r3 - 4) */ +align_word: + stb r1, @r4 + addi r4, #1 + addi r2, #-1 + addi r3, #-1 + bnez r3, align_word + cmpui r2, #4 + bc byte_set +word_set: + and3 r1, r1, #0x00ff /* r1: abababab <-- ??????ab */ + sll3 r3, r1, #8 + or r1, r3 + sll3 r3, r1, #16 + or r1, r3 + addi r2, #-4 + addi r4, #-4 +word_set_loop: + st r1, @+r4 + addi r2, #-4 + bgtz r2, word_set_loop + bnez r2, byte_set_wrap + st r1, @+r4 + jmp r14 + +qword_align_check: /* len >= 16 */ + and3 r3, r4, #15 + bnez r3, word_align_check +qword_set: + and3 r1, r1, #0x00ff /* r1: abababab <-- ??????ab */ + sll3 r3, r1, #8 + or r1, r3 + sll3 r3, r1, #16 + or r1, r3 + addi r4, #-4 +qword_set_loop: + ld r3, @(4,r4) /* cache line allocate */ + addi r2, #-16 + st r1, @+r4 + st r1, @+r4 + cmpui r2, #16 + st r1, @+r4 + st r1, @+r4 + bnc qword_set_loop + bnez r2, word_set_wrap + jmp r14 +word_set_wrap: + cmpui r2, #4 + bc byte_set + addi r2, #-4 + bra word_set_loop + +byte_set_wrap: + addi r2, #4 + addi r4, #4 + beqz r2, end_memset +byte_set: + addi r2, #-1 + stb r1, @r4 + addi r4, #1 + bnez r2, byte_set +end_memset: + jmp r14 + +#endif /* not CONFIG_ISA_DUAL_ISSUE */ + + .end diff --git a/arch/m32r/lib/putuser.S b/arch/m32r/lib/putuser.S new file mode 100644 index 000000000..218154cc3 --- /dev/null +++ b/arch/m32r/lib/putuser.S @@ -0,0 +1,84 @@ +/* + * __put_user functions. + * + * (C) Copyright 1998 Linus Torvalds + * (C) Copyright 2001 Hirokazu Takata + * + * These functions have a non-standard call interface + * to make them more efficient. + */ + +#include + +/* + * __put_user_X + * + * Inputs: r0 contains the address + * r1 contains the value + * + * Outputs: r0 is error code (0 or -EFAULT) + * r1 is corrupted (will contain "current_task"). + * + * These functions should not modify any other registers, + * as they get called from within inline assembly. + */ + +#ifdef CONFIG_ISA_DUAL_ISSUE + + .text + .balign 4 + .globl __put_user_1 +__put_user_1: +1: stb r1, @r0 || ldi r0, #0 + jmp r14 + + .balign 4 + .globl __put_user_2 +__put_user_2: +2: sth r1, @r0 || ldi r0, #0 + jmp r14 + + .balign 4 + .globl __put_user_4 +__put_user_4: +3: st r1, @r0 || ldi r0, #0 + jmp r14 + +bad_put_user: + ldi r0, #-14 || jmp r14 + +#else /* not CONFIG_ISA_DUAL_ISSUE */ + + .text + .balign 4 + .globl __put_user_1 +__put_user_1: +1: stb r1, @r0 + ldi r0, #0 + jmp r14 + + .balign 4 + .globl __put_user_2 +__put_user_2: +2: sth r1, @r0 + ldi r0, #0 + jmp r14 + + .balign 4 + .globl __put_user_4 +__put_user_4: +3: st r1, @r0 + ldi r0, #0 + jmp r14 + +bad_put_user: + ldi r0, #-14 + jmp r14 + +#endif /* not CONFIG_ISA_DUAL_ISSUE */ + +.section __ex_table,"a" + .long 1b,bad_put_user + .long 2b,bad_put_user + .long 3b,bad_put_user +.previous diff --git a/arch/m32r/lib/strlen.S b/arch/m32r/lib/strlen.S new file mode 100644 index 000000000..8d23cfbd6 --- /dev/null +++ b/arch/m32r/lib/strlen.S @@ -0,0 +1,120 @@ +/* + * linux/arch/m32r/strlen.S -- strlen code. + * + * Copyright (C) 2001 Hirokazu Takata + * + * size_t strlen(const char *s); + * + */ +/* $Id$ */ + + +#include +#include +#include + +#ifdef CONFIG_ISA_DUAL_ISSUE + + .text +ENTRY(strlen) + mv r6, r0 || ldi r2, #0 + and3 r0, r0, #3 + bnez r0, strlen_byte +; +strlen_word: + ld r0, @r6+ +; + seth r5, #high(0x01010101) + or3 r5, r5, #low(0x01010101) + sll3 r7, r5, #7 +strlen_word_loop: + ld r1, @r6+ || not r4, r0 + sub r0, r5 || and r4, r7 + and r4, r0 + bnez r4, strlen_last_bytes + ld r0, @r6+ || not r4, r1 + sub r1, r5 || and r4, r7 + and r4, r1 || addi r2, #4 + bnez r4, strlen_last_bytes + addi r2, #4 || bra.s strlen_word_loop + + ; NOTE: If a null char. exists, return 0. + ; if ((x - 0x01010101) & ~x & 0x80808080) + ; return 0; +; +strlen_byte: + ldb r1, @r6 || addi r6, #1 + beqz r1, strlen_exit + addi r2, #1 || bra.s strlen_byte +; +strlen_last_bytes: + ldi r0, #4 || addi r6, #-8 +; +strlen_byte_loop: + ldb r1, @r6 || addi r6, #1 + addi r0, #-1 || cmpz r1 + bc.s strlen_exit || cmpz r0 + addi r2, #1 || bnc.s strlen_byte_loop +; +strlen_exit: + mv r0, r2 || jmp r14 + +#else /* not CONFIG_ISA_DUAL_ISSUE */ + + .text +ENTRY(strlen) + mv r6, r0 + ldi r2, #0 + and3 r0, r0, #3 + bnez r0, strlen_byte +; +strlen_word: + ld r0, @r6+ +; + seth r5, #high(0x01010101) + or3 r5, r5, #low(0x01010101) + sll3 r7, r5, #7 +strlen_word_loop: + ld r1, @r6+ + not r4, r0 ; NOTE: If a null char. exists, return 0. + sub r0, r5 ; if ((x - 0x01010101) & ~x & 0x80808080) + and r4, r7 ; return 0; + and r4, r0 + bnez r4, strlen_last_bytes + addi r2, #4 +; + ld r0, @r6+ + not r4, r1 ; NOTE: If a null char. exists, return 0. + sub r1, r5 ; if ((x - 0x01010101) & ~x & 0x80808080) + and r4, r7 ; return 0; + and r4, r1 + bnez r4, strlen_last_bytes + addi r2, #4 + bra strlen_word_loop +; +strlen_byte: + ldb r1, @r6 + addi r6, #1 + beqz r1, strlen_exit + addi r2, #1 + bra strlen_byte +; +strlen_last_bytes: + ldi r0, #4 + addi r6, #-8 +; +strlen_byte_loop: + ldb r1, @r6 + addi r6, #1 + addi r0, #-1 + beqz r1, strlen_exit + addi r2, #1 + bnez r0, strlen_byte_loop +; +strlen_exit: + mv r0, r2 + jmp r14 + +#endif /* not CONFIG_ISA_DUAL_ISSUE */ + + .end diff --git a/arch/m32r/lib/usercopy.c b/arch/m32r/lib/usercopy.c new file mode 100644 index 000000000..6c6855f1a --- /dev/null +++ b/arch/m32r/lib/usercopy.c @@ -0,0 +1,391 @@ +/* + * User address space access functions. + * The non inlined parts of asm-m32r/uaccess.h are here. + * + * Copyright 1997 Andi Kleen + * Copyright 1997 Linus Torvalds + * Copyright 2001, 2002, 2004 Hirokazu Takata + */ +#include +#include +#include +#include +#include + +unsigned long +__generic_copy_to_user(void *to, const void *from, unsigned long n) +{ + prefetch(from); + if (access_ok(VERIFY_WRITE, to, n)) + __copy_user(to,from,n); + return n; +} + +unsigned long +__generic_copy_from_user(void *to, const void *from, unsigned long n) +{ + prefetchw(to); + if (access_ok(VERIFY_READ, from, n)) + __copy_user_zeroing(to,from,n); + else + memset(to, 0, n); + return n; +} + + +/* + * Copy a null terminated string from userspace. + */ + +#ifdef CONFIG_ISA_DUAL_ISSUE + +#define __do_strncpy_from_user(dst,src,count,res) \ +do { \ + int __d0, __d1, __d2; \ + __asm__ __volatile__( \ + " beqz %1, 2f\n" \ + " .fillinsn\n" \ + "0: ldb r14, @%3 || addi %3, #1\n" \ + " stb r14, @%4 || addi %4, #1\n" \ + " beqz r14, 1f\n" \ + " addi %1, #-1\n" \ + " bnez %1, 0b\n" \ + " .fillinsn\n" \ + "1: sub %0, %1\n" \ + " .fillinsn\n" \ + "2:\n" \ + ".section .fixup,\"ax\"\n" \ + " .balign 4\n" \ + "3: seth r14, #high(2b)\n" \ + " or3 r14, r14, #low(2b)\n" \ + " jmp r14 || ldi %0, #%5\n" \ + ".previous\n" \ + ".section __ex_table,\"a\"\n" \ + " .balign 4\n" \ + " .long 0b,3b\n" \ + ".previous" \ + : "=r"(res), "=r"(count), "=&r" (__d0), "=&r" (__d1), \ + "=&r" (__d2) \ + : "i"(-EFAULT), "0"(count), "1"(count), "3"(src), \ + "4"(dst) \ + : "r14", "cbit", "memory"); \ +} while (0) + +#else /* not CONFIG_ISA_DUAL_ISSUE */ + +#define __do_strncpy_from_user(dst,src,count,res) \ +do { \ + int __d0, __d1, __d2; \ + __asm__ __volatile__( \ + " beqz %1, 2f\n" \ + " .fillinsn\n" \ + "0: ldb r14, @%3\n" \ + " stb r14, @%4\n" \ + " addi %3, #1\n" \ + " addi %4, #1\n" \ + " beqz r14, 1f\n" \ + " addi %1, #-1\n" \ + " bnez %1, 0b\n" \ + " .fillinsn\n" \ + "1: sub %0, %1\n" \ + " .fillinsn\n" \ + "2:\n" \ + ".section .fixup,\"ax\"\n" \ + " .balign 4\n" \ + "3: ldi %0, #%5\n" \ + " seth r14, #high(2b)\n" \ + " or3 r14, r14, #low(2b)\n" \ + " jmp r14\n" \ + ".previous\n" \ + ".section __ex_table,\"a\"\n" \ + " .balign 4\n" \ + " .long 0b,3b\n" \ + ".previous" \ + : "=r"(res), "=r"(count), "=&r" (__d0), "=&r" (__d1), \ + "=&r" (__d2) \ + : "i"(-EFAULT), "0"(count), "1"(count), "3"(src), \ + "4"(dst) \ + : "r14", "cbit", "memory"); \ +} while (0) + +#endif /* CONFIG_ISA_DUAL_ISSUE */ + +long +__strncpy_from_user(char *dst, const char *src, long count) +{ + long res; + __do_strncpy_from_user(dst, src, count, res); + return res; +} + +long +strncpy_from_user(char *dst, const char *src, long count) +{ + long res = -EFAULT; + if (access_ok(VERIFY_READ, src, 1)) + __do_strncpy_from_user(dst, src, count, res); + return res; +} + + +/* + * Zero Userspace + */ + +#ifdef CONFIG_ISA_DUAL_ISSUE + +#define __do_clear_user(addr,size) \ +do { \ + int __dst, __c; \ + __asm__ __volatile__( \ + " beqz %1, 9f\n" \ + " and3 r14, %0, #3\n" \ + " bnez r14, 2f\n" \ + " and3 r14, %1, #3\n" \ + " bnez r14, 2f\n" \ + " and3 %1, %1, #3\n" \ + " beqz %2, 2f\n" \ + " addi %0, #-4\n" \ + " .fillinsn\n" \ + "0: ; word clear \n" \ + " st %6, @+%0 || addi %2, #-1\n" \ + " bnez %2, 0b\n" \ + " beqz %1, 9f\n" \ + " .fillinsn\n" \ + "2: ; byte clear \n" \ + " stb %6, @%0 || addi %1, #-1\n" \ + " addi %0, #1\n" \ + " bnez %1, 2b\n" \ + " .fillinsn\n" \ + "9:\n" \ + ".section .fixup,\"ax\"\n" \ + " .balign 4\n" \ + "4: slli %2, #2\n" \ + " seth r14, #high(9b)\n" \ + " or3 r14, r14, #low(9b)\n" \ + " jmp r14 || add %1, %2\n" \ + ".previous\n" \ + ".section __ex_table,\"a\"\n" \ + " .balign 4\n" \ + " .long 0b,4b\n" \ + " .long 2b,9b\n" \ + ".previous\n" \ + : "=&r"(__dst), "=&r"(size), "=&r"(__c) \ + : "0"(addr), "1"(size), "2"(size / 4), "r"(0) \ + : "r14", "cbit", "memory"); \ +} while (0) + +#else /* not CONFIG_ISA_DUAL_ISSUE */ + +#define __do_clear_user(addr,size) \ +do { \ + int __dst, __c; \ + __asm__ __volatile__( \ + " beqz %1, 9f\n" \ + " and3 r14, %0, #3\n" \ + " bnez r14, 2f\n" \ + " and3 r14, %1, #3\n" \ + " bnez r14, 2f\n" \ + " and3 %1, %1, #3\n" \ + " beqz %2, 2f\n" \ + " addi %0, #-4\n" \ + " .fillinsn\n" \ + "0: st %6, @+%0 ; word clear \n" \ + " addi %2, #-1\n" \ + " bnez %2, 0b\n" \ + " beqz %1, 9f\n" \ + " .fillinsn\n" \ + "2: stb %6, @%0 ; byte clear \n" \ + " addi %1, #-1\n" \ + " addi %0, #1\n" \ + " bnez %1, 2b\n" \ + " .fillinsn\n" \ + "9:\n" \ + ".section .fixup,\"ax\"\n" \ + " .balign 4\n" \ + "4: slli %2, #2\n" \ + " add %1, %2\n" \ + " seth r14, #high(9b)\n" \ + " or3 r14, r14, #low(9b)\n" \ + " jmp r14\n" \ + ".previous\n" \ + ".section __ex_table,\"a\"\n" \ + " .balign 4\n" \ + " .long 0b,4b\n" \ + " .long 2b,9b\n" \ + ".previous\n" \ + : "=&r"(__dst), "=&r"(size), "=&r"(__c) \ + : "0"(addr), "1"(size), "2"(size / 4), "r"(0) \ + : "r14", "cbit", "memory"); \ +} while (0) + +#endif /* not CONFIG_ISA_DUAL_ISSUE */ + +unsigned long +clear_user(void *to, unsigned long n) +{ + if (access_ok(VERIFY_WRITE, to, n)) + __do_clear_user(to, n); + return n; +} + +unsigned long +__clear_user(void *to, unsigned long n) +{ + __do_clear_user(to, n); + return n; +} + +/* + * Return the size of a string (including the ending 0) + * + * Return 0 on exception, a value greater than N if too long + */ + +#ifdef CONFIG_ISA_DUAL_ISSUE + +long strnlen_user(const char *s, long n) +{ + unsigned long mask = -__addr_ok(s); + unsigned long res; + + __asm__ __volatile__( + " and %0, %5 || mv r1, %1\n" + " beqz %0, strnlen_exit\n" + " and3 r0, %1, #3\n" + " bnez r0, strnlen_byte_loop\n" + " cmpui %0, #4\n" + " bc strnlen_byte_loop\n" + "strnlen_word_loop:\n" + "0: ld r0, @%1+\n" + " pcmpbz r0\n" + " bc strnlen_last_bytes_fixup\n" + " addi %0, #-4\n" + " beqz %0, strnlen_exit\n" + " bgtz %0, strnlen_word_loop\n" + "strnlen_last_bytes:\n" + " mv %0, %4\n" + "strnlen_last_bytes_fixup:\n" + " addi %1, #-4\n" + "strnlen_byte_loop:\n" + "1: ldb r0, @%1 || addi %0, #-1\n" + " beqz r0, strnlen_exit\n" + " addi %1, #1\n" + " bnez %0, strnlen_byte_loop\n" + "strnlen_exit:\n" + " sub %1, r1\n" + " add3 %0, %1, #1\n" + " .fillinsn\n" + "9:\n" + ".section .fixup,\"ax\"\n" + " .balign 4\n" + "4: addi %1, #-4\n" + " .fillinsn\n" + "5: seth r1, #high(9b)\n" + " or3 r1, r1, #low(9b)\n" + " jmp r1 || ldi %0, #0\n" + ".previous\n" + ".section __ex_table,\"a\"\n" + " .balign 4\n" + " .long 0b,4b\n" + " .long 1b,5b\n" + ".previous" + : "=&r" (res), "=r" (s) + : "0" (n), "1" (s), "r" (n & 3), "r" (mask), "r"(0x01010101) + : "r0", "r1", "cbit"); + + /* NOTE: strnlen_user() algorism: + * { + * char *p; + * for (p = s; n-- && *p != '\0'; ++p) + * ; + * return p - s + 1; + * } + */ + + /* NOTE: If a null char. exists, return 0. + * if ((x - 0x01010101) & ~x & 0x80808080)\n" + * return 0;\n" + */ + + return res & mask; +} + +#else /* not CONFIG_ISA_DUAL_ISSUE */ + +long strnlen_user(const char *s, long n) +{ + unsigned long mask = -__addr_ok(s); + unsigned long res; + + __asm__ __volatile__( + " and %0, %5\n" + " mv r1, %1\n" + " beqz %0, strnlen_exit\n" + " and3 r0, %1, #3\n" + " bnez r0, strnlen_byte_loop\n" + " cmpui %0, #4\n" + " bc strnlen_byte_loop\n" + " sll3 r3, %6, #7\n" + "strnlen_word_loop:\n" + "0: ld r0, @%1+\n" + " not r2, r0\n" + " sub r0, %6\n" + " and r2, r3\n" + " and r2, r0\n" + " bnez r2, strnlen_last_bytes_fixup\n" + " addi %0, #-4\n" + " beqz %0, strnlen_exit\n" + " bgtz %0, strnlen_word_loop\n" + "strnlen_last_bytes:\n" + " mv %0, %4\n" + "strnlen_last_bytes_fixup:\n" + " addi %1, #-4\n" + "strnlen_byte_loop:\n" + "1: ldb r0, @%1\n" + " addi %0, #-1\n" + " beqz r0, strnlen_exit\n" + " addi %1, #1\n" + " bnez %0, strnlen_byte_loop\n" + "strnlen_exit:\n" + " sub %1, r1\n" + " add3 %0, %1, #1\n" + " .fillinsn\n" + "9:\n" + ".section .fixup,\"ax\"\n" + " .balign 4\n" + "4: addi %1, #-4\n" + " .fillinsn\n" + "5: ldi %0, #0\n" + " seth r1, #high(9b)\n" + " or3 r1, r1, #low(9b)\n" + " jmp r1\n" + ".previous\n" + ".section __ex_table,\"a\"\n" + " .balign 4\n" + " .long 0b,4b\n" + " .long 1b,5b\n" + ".previous" + : "=&r" (res), "=r" (s) + : "0" (n), "1" (s), "r" (n & 3), "r" (mask), "r"(0x01010101) + : "r0", "r1", "r2", "r3", "cbit"); + + /* NOTE: strnlen_user() algorism: + * { + * char *p; + * for (p = s; n-- && *p != '\0'; ++p) + * ; + * return p - s + 1; + * } + */ + + /* NOTE: If a null char. exists, return 0. + * if ((x - 0x01010101) & ~x & 0x80808080)\n" + * return 0;\n" + */ + + return res & mask; +} + +#endif /* CONFIG_ISA_DUAL_ISSUE */ + diff --git a/arch/m32r/m32700ut/defconfig.m32700ut.smp b/arch/m32r/m32700ut/defconfig.m32700ut.smp new file mode 100644 index 000000000..df7f9d01b --- /dev/null +++ b/arch/m32r/m32700ut/defconfig.m32700ut.smp @@ -0,0 +1,662 @@ +# +# Automatically generated make config: don't edit +# +CONFIG_M32R=y +CONFIG_UID16=y +CONFIG_GENERIC_ISA_DMA=y + +# +# Code maturity level options +# +CONFIG_EXPERIMENTAL=y +CONFIG_CLEAN_COMPILE=y + +# +# General setup +# +CONFIG_SWAP=y +CONFIG_SYSVIPC=y +# CONFIG_POSIX_MQUEUE is not set +CONFIG_BSD_PROCESS_ACCT=y +# CONFIG_BSD_PROCESS_ACCT_V3 is not set +CONFIG_SYSCTL=y +# CONFIG_AUDIT is not set +CONFIG_LOG_BUF_SHIFT=15 +CONFIG_HOTPLUG=y +CONFIG_IKCONFIG=y +# CONFIG_IKCONFIG_PROC is not set +CONFIG_EMBEDDED=y +# CONFIG_KALLSYMS is not set +# CONFIG_FUTEX is not set +# CONFIG_EPOLL is not set +CONFIG_IOSCHED_NOOP=y +# CONFIG_IOSCHED_AS is not set +CONFIG_IOSCHED_DEADLINE=y +CONFIG_IOSCHED_CFQ=y +# CONFIG_CC_OPTIMIZE_FOR_SIZE is not set + +# +# Loadable module support +# +CONFIG_MODULES=y +CONFIG_MODULE_UNLOAD=y +# CONFIG_MODULE_FORCE_UNLOAD is not set +CONFIG_OBSOLETE_MODPARM=y +# CONFIG_MODVERSIONS is not set +CONFIG_KMOD=y +CONFIG_STOP_MACHINE=y + +# +# Processor type and features +# +# CONFIG_PLAT_MAPPI is not set +# CONFIG_PLAT_USRV is not set +CONFIG_PLAT_M32700UT=y +# CONFIG_PLAT_OPSPUT is not set +# CONFIG_PLAT_OAKS32R is not set +# CONFIG_PLAT_MAPPI2 is not set +CONFIG_CHIP_M32700=y +# CONFIG_CHIP_M32102 is not set +# CONFIG_CHIP_VDEC2 is not set +# CONFIG_CHIP_OPSP is not set +CONFIG_MMU=y +CONFIG_TLB_ENTRIES=32 +CONFIG_ISA_M32R2=y +CONFIG_ISA_DSP_LEVEL2=y +CONFIG_ISA_DUAL_ISSUE=y +CONFIG_BUS_CLOCK=50000000 +CONFIG_TIMER_DIVIDE=128 +# CONFIG_CPU_LITTLE_ENDIAN is not set +CONFIG_MEMORY_START=0x08000000 +CONFIG_MEMORY_SIZE=0x01000000 +CONFIG_NOHIGHMEM=y +# CONFIG_DISCONTIGMEM is not set +CONFIG_RWSEM_GENERIC_SPINLOCK=y +# CONFIG_RWSEM_XCHGADD_ALGORITHM is not set +CONFIG_PREEMPT=y +# CONFIG_HAVE_DEC_LOCK is not set +CONFIG_SMP=y +CONFIG_CHIP_M32700_TS1=y +CONFIG_NR_CPUS=2 +# CONFIG_NUMA is not set + +# +# M32R drivers +# +# CONFIG_M32RPCC is not set +CONFIG_M32R_CFC=y +CONFIG_M32700UT_CFC=y +CONFIG_CFC_NUM=1 +# CONFIG_MTD_M32R is not set +CONFIG_M32R_SMC91111=y +CONFIG_M32700UT_DS1302=y + +# +# Power management options (ACPI, APM) +# +# CONFIG_PM is not set + +# +# Bus options (PCI, PCMCIA, EISA, MCA, ISA) +# +# CONFIG_PCI is not set +# CONFIG_ISA is not set + +# +# PCMCIA/CardBus support +# +CONFIG_PCMCIA=y +# CONFIG_PCMCIA_DEBUG is not set +# CONFIG_TCIC is not set + +# +# PCI Hotplug Support +# + +# +# Executable file formats +# +CONFIG_BINFMT_ELF=y +# CONFIG_BINFMT_MISC is not set + +# +# Device Drivers +# + +# +# Generic Driver Options +# +CONFIG_STANDALONE=y +CONFIG_PREVENT_FIRMWARE_BUILD=y +# CONFIG_FW_LOADER is not set + +# +# Memory Technology Devices (MTD) +# +# CONFIG_MTD is not set + +# +# Parallel port support +# +# CONFIG_PARPORT is not set + +# +# Plug and Play support +# + +# +# Block devices +# +# CONFIG_BLK_DEV_FD is not set +CONFIG_BLK_DEV_LOOP=y +# CONFIG_BLK_DEV_CRYPTOLOOP is not set +CONFIG_BLK_DEV_NBD=y +CONFIG_BLK_DEV_RAM=y +CONFIG_BLK_DEV_RAM_SIZE=4096 +# CONFIG_BLK_DEV_INITRD is not set + +# +# ATA/ATAPI/MFM/RLL support +# +CONFIG_IDE=y +CONFIG_BLK_DEV_IDE=y + +# +# Please see Documentation/ide.txt for help/info on IDE drives +# +# CONFIG_BLK_DEV_IDE_SATA is not set +CONFIG_BLK_DEV_IDEDISK=y +# CONFIG_IDEDISK_MULTI_MODE is not set +CONFIG_BLK_DEV_IDECS=y +CONFIG_BLK_DEV_IDECD=m +# CONFIG_BLK_DEV_IDETAPE is not set +# CONFIG_BLK_DEV_IDEFLOPPY is not set +# CONFIG_BLK_DEV_IDESCSI is not set +# CONFIG_IDE_TASK_IOCTL is not set +# CONFIG_IDE_TASKFILE_IO is not set + +# +# IDE chipset support/bugfixes +# +CONFIG_IDE_GENERIC=y +# CONFIG_IDE_ARM is not set +# CONFIG_BLK_DEV_IDEDMA is not set +# CONFIG_IDEDMA_AUTO is not set +# CONFIG_BLK_DEV_HD is not set + +# +# SCSI device support +# +CONFIG_SCSI=m +CONFIG_SCSI_PROC_FS=y + +# +# SCSI support type (disk, tape, CD-ROM) +# +CONFIG_BLK_DEV_SD=m +# CONFIG_CHR_DEV_ST is not set +# CONFIG_CHR_DEV_OSST is not set +CONFIG_BLK_DEV_SR=m +# CONFIG_BLK_DEV_SR_VENDOR is not set +CONFIG_CHR_DEV_SG=m + +# +# Some SCSI devices (e.g. CD jukebox) support multiple LUNs +# +CONFIG_SCSI_MULTI_LUN=y +# CONFIG_SCSI_CONSTANTS is not set +# CONFIG_SCSI_LOGGING is not set + +# +# SCSI Transport Attributes +# +# CONFIG_SCSI_SPI_ATTRS is not set +# CONFIG_SCSI_FC_ATTRS is not set + +# +# SCSI low-level drivers +# +# CONFIG_SCSI_AIC7XXX_OLD is not set +# CONFIG_SCSI_SATA is not set +# CONFIG_SCSI_EATA_PIO is not set +# CONFIG_SCSI_DEBUG is not set + +# +# PCMCIA SCSI adapter support +# +# CONFIG_PCMCIA_AHA152X is not set +# CONFIG_PCMCIA_FDOMAIN is not set +# CONFIG_PCMCIA_NINJA_SCSI is not set +# CONFIG_PCMCIA_QLOGIC is not set +# CONFIG_PCMCIA_SYM53C500 is not set + +# +# Multi-device support (RAID and LVM) +# +# CONFIG_MD is not set + +# +# Fusion MPT device support +# + +# +# IEEE 1394 (FireWire) support +# + +# +# I2O device support +# + +# +# Networking support +# +CONFIG_NET=y + +# +# Networking options +# +CONFIG_PACKET=y +# CONFIG_PACKET_MMAP is not set +# CONFIG_NETLINK_DEV 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_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_IPV6 is not set +# CONFIG_NETFILTER is not set + +# +# SCTP Configuration (EXPERIMENTAL) +# +# CONFIG_IP_SCTP 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_NET_DIVERT is not set +# CONFIG_ECONET is not set +# CONFIG_WAN_ROUTER is not set +# CONFIG_NET_HW_FLOWCONTROL is not set + +# +# QoS and/or fair queueing +# +# CONFIG_NET_SCHED is not set +# CONFIG_NET_CLS_ROUTE is not set + +# +# Network testing +# +# CONFIG_NET_PKTGEN is not set +# CONFIG_NETPOLL is not set +# CONFIG_NET_POLL_CONTROLLER is not set +# CONFIG_HAMRADIO is not set +# CONFIG_IRDA is not set +# CONFIG_BT is not set +# CONFIG_NETDEVICES is not set + +# +# ISDN subsystem +# +# CONFIG_ISDN is not set + +# +# Telephony Support +# +# CONFIG_PHONE is not set + +# +# Input device support +# +CONFIG_INPUT=y + +# +# Userland interfaces +# +# CONFIG_INPUT_MOUSEDEV is not set +# CONFIG_INPUT_JOYDEV is not set +# CONFIG_INPUT_TSDEV is not set +# CONFIG_INPUT_EVDEV is not set +# CONFIG_INPUT_EVBUG is not set + +# +# Input I/O drivers +# +# CONFIG_GAMEPORT is not set +CONFIG_SOUND_GAMEPORT=y +CONFIG_SERIO=y +# CONFIG_SERIO_I8042 is not set +CONFIG_SERIO_SERPORT=y +# CONFIG_SERIO_CT82C710 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_TOUCHSCREEN is not set +# CONFIG_INPUT_MISC is not set + +# +# Character devices +# +CONFIG_VT=y +CONFIG_VT_CONSOLE=y +CONFIG_HW_CONSOLE=y +# CONFIG_SERIAL_NONSTANDARD is not set + +# +# Serial drivers +# +# CONFIG_SERIAL_8250 is not set + +# +# Non-8250 serial port support +# +CONFIG_SERIAL_CORE=y +CONFIG_SERIAL_CORE_CONSOLE=y +# CONFIG_SERIAL_M32R_SIO is not set +CONFIG_SERIAL_M32R_PLDSIO=y +CONFIG_SERIAL_M32R_PLDSIO_CONSOLE=y +CONFIG_UNIX98_PTYS=y +CONFIG_LEGACY_PTYS=y +CONFIG_LEGACY_PTY_COUNT=256 +# CONFIG_QIC02_TAPE is not set + +# +# IPMI +# +# CONFIG_IPMI_HANDLER is not set + +# +# Watchdog Cards +# +# CONFIG_WATCHDOG is not set +# CONFIG_RTC is not set +# CONFIG_GEN_RTC is not set +# CONFIG_DTLK is not set +# CONFIG_R3964 is not set + +# +# Ftape, the floppy tape device driver +# +# CONFIG_AGP is not set +# CONFIG_DRM is not set + +# +# PCMCIA character devices +# +# CONFIG_SYNCLINK_CS is not set +# CONFIG_RAW_DRIVER is not set + +# +# I2C support +# +# CONFIG_I2C is not set + +# +# Dallas's 1-wire bus +# +# CONFIG_W1 is not set + +# +# Misc devices +# + +# +# Multimedia devices +# +CONFIG_VIDEO_DEV=y + +# +# Video For Linux +# + +# +# Video Adapters +# +# CONFIG_VIDEO_CPIA is not set +CONFIG_M32R_AR=y +CONFIG_M32R_AR_VGA=y + +# +# Radio Adapters +# +# CONFIG_RADIO_MAESTRO is not set + +# +# Digital Video Broadcasting Devices +# +# CONFIG_DVB is not set + +# +# Graphics support +# +CONFIG_FB=y +CONFIG_FB_EPSON_S1D13806=y +# CONFIG_FB_VIRTUAL is not set + +# +# Console display driver support +# +# CONFIG_VGA_CONSOLE is not set +# CONFIG_MDA_CONSOLE is not set +CONFIG_DUMMY_CONSOLE=y +CONFIG_FRAMEBUFFER_CONSOLE=y +# CONFIG_FONTS is not set +CONFIG_FONT_8x8=y +CONFIG_FONT_8x16=y + +# +# Logo configuration +# +CONFIG_LOGO=y +CONFIG_LOGO_LINUX_MONO=y +CONFIG_LOGO_LINUX_VGA16=y +CONFIG_LOGO_LINUX_CLUT224=y +CONFIG_LOGO_M32R_CLUT224=y + +# +# Sound +# +# CONFIG_SOUND is not set + +# +# USB support +# + +# +# USB Gadget Support +# +# CONFIG_USB_GADGET is not set + +# +# File systems +# +CONFIG_EXT2_FS=y +# CONFIG_EXT2_FS_XATTR 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_JBD=m +CONFIG_JBD_DEBUG=y +CONFIG_FS_MBCACHE=y +CONFIG_REISERFS_FS=m +# CONFIG_REISERFS_CHECK is not set +# CONFIG_REISERFS_PROC_INFO is not set +# CONFIG_REISERFS_FS_XATTR is not set +# CONFIG_JFS_FS is not set +# CONFIG_XFS_FS is not set +# CONFIG_MINIX_FS is not set +# CONFIG_ROMFS_FS is not set +# CONFIG_QUOTA is not set +# CONFIG_AUTOFS_FS is not set +# CONFIG_AUTOFS4_FS is not set + +# +# CD-ROM/DVD Filesystems +# +CONFIG_ISO9660_FS=m +CONFIG_JOLIET=y +# CONFIG_ZISOFS is not set +CONFIG_UDF_FS=m +CONFIG_UDF_NLS=y + +# +# DOS/FAT/NT Filesystems +# +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 + +# +# Pseudo filesystems +# +CONFIG_PROC_FS=y +CONFIG_PROC_KCORE=y +CONFIG_SYSFS=y +CONFIG_DEVFS_FS=y +CONFIG_DEVFS_MOUNT=y +# CONFIG_DEVFS_DEBUG is not set +# CONFIG_DEVPTS_FS_XATTR is not set +CONFIG_TMPFS=y +# CONFIG_HUGETLB_PAGE is not set +CONFIG_RAMFS=y + +# +# 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_HPFS_FS is not set +# CONFIG_QNX4FS_FS is not set +# CONFIG_SYSV_FS is not set +# CONFIG_UFS_FS is not set + +# +# Network File Systems +# +CONFIG_NFS_FS=y +CONFIG_NFS_V3=y +# CONFIG_NFS_V4 is not set +# CONFIG_NFS_DIRECTIO is not set +# CONFIG_NFSD is not set +CONFIG_ROOT_NFS=y +CONFIG_LOCKD=y +CONFIG_LOCKD_V4=y +# CONFIG_EXPORTFS is not set +CONFIG_SUNRPC=y +# CONFIG_RPCSEC_GSS_KRB5 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 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 + +# +# Profiling support +# +# CONFIG_PROFILING is not set + +# +# Kernel hacking +# +# CONFIG_DEBUG_KERNEL is not set +# CONFIG_DEBUG_SPINLOCK_SLEEP is not set +# CONFIG_FRAME_POINTER is not set + +# +# Security options +# +# CONFIG_SECURITY is not set + +# +# Cryptographic options +# +# CONFIG_CRYPTO is not set + +# +# Library routines +# +# CONFIG_CRC_CCITT is not set +CONFIG_CRC32=y +# CONFIG_LIBCRC32C is not set diff --git a/arch/m32r/m32700ut/defconfig.m32700ut.up b/arch/m32r/m32700ut/defconfig.m32700ut.up new file mode 100644 index 000000000..5a9da73fb --- /dev/null +++ b/arch/m32r/m32700ut/defconfig.m32700ut.up @@ -0,0 +1,659 @@ +# +# Automatically generated make config: don't edit +# +CONFIG_M32R=y +CONFIG_UID16=y +CONFIG_GENERIC_ISA_DMA=y + +# +# Code maturity level options +# +CONFIG_EXPERIMENTAL=y +CONFIG_CLEAN_COMPILE=y +CONFIG_BROKEN_ON_SMP=y + +# +# General setup +# +CONFIG_SWAP=y +CONFIG_SYSVIPC=y +# CONFIG_POSIX_MQUEUE is not set +CONFIG_BSD_PROCESS_ACCT=y +# CONFIG_BSD_PROCESS_ACCT_V3 is not set +CONFIG_SYSCTL=y +# CONFIG_AUDIT is not set +CONFIG_LOG_BUF_SHIFT=14 +CONFIG_HOTPLUG=y +CONFIG_IKCONFIG=y +# CONFIG_IKCONFIG_PROC is not set +CONFIG_EMBEDDED=y +# CONFIG_KALLSYMS is not set +# CONFIG_FUTEX is not set +# CONFIG_EPOLL is not set +CONFIG_IOSCHED_NOOP=y +# CONFIG_IOSCHED_AS is not set +CONFIG_IOSCHED_DEADLINE=y +CONFIG_IOSCHED_CFQ=y +# CONFIG_CC_OPTIMIZE_FOR_SIZE is not set + +# +# Loadable module support +# +CONFIG_MODULES=y +CONFIG_MODULE_UNLOAD=y +# CONFIG_MODULE_FORCE_UNLOAD is not set +CONFIG_OBSOLETE_MODPARM=y +# CONFIG_MODVERSIONS is not set +CONFIG_KMOD=y + +# +# Processor type and features +# +# CONFIG_PLAT_MAPPI is not set +# CONFIG_PLAT_USRV is not set +CONFIG_PLAT_M32700UT=y +# CONFIG_PLAT_OPSPUT is not set +# CONFIG_PLAT_OAKS32R is not set +# CONFIG_PLAT_MAPPI2 is not set +CONFIG_CHIP_M32700=y +# CONFIG_CHIP_M32102 is not set +# CONFIG_CHIP_VDEC2 is not set +# CONFIG_CHIP_OPSP is not set +CONFIG_MMU=y +CONFIG_TLB_ENTRIES=32 +CONFIG_ISA_M32R2=y +CONFIG_ISA_DSP_LEVEL2=y +CONFIG_ISA_DUAL_ISSUE=y +CONFIG_BUS_CLOCK=50000000 +CONFIG_TIMER_DIVIDE=128 +# CONFIG_CPU_LITTLE_ENDIAN is not set +CONFIG_MEMORY_START=0x08000000 +CONFIG_MEMORY_SIZE=0x01000000 +CONFIG_NOHIGHMEM=y +# CONFIG_DISCONTIGMEM is not set +CONFIG_RWSEM_GENERIC_SPINLOCK=y +# CONFIG_RWSEM_XCHGADD_ALGORITHM is not set +CONFIG_PREEMPT=y +# CONFIG_HAVE_DEC_LOCK is not set +# CONFIG_SMP is not set + +# +# M32R drivers +# +# CONFIG_M32RPCC is not set +CONFIG_M32R_CFC=y +CONFIG_M32700UT_CFC=y +CONFIG_CFC_NUM=1 +# CONFIG_MTD_M32R is not set +CONFIG_M32R_SMC91111=y +CONFIG_M32700UT_DS1302=y + +# +# Power management options (ACPI, APM) +# +# CONFIG_PM is not set + +# +# Bus options (PCI, PCMCIA, EISA, MCA, ISA) +# +# CONFIG_PCI is not set +# CONFIG_ISA is not set + +# +# PCMCIA/CardBus support +# +CONFIG_PCMCIA=y +# CONFIG_PCMCIA_DEBUG is not set +# CONFIG_TCIC is not set + +# +# PCI Hotplug Support +# + +# +# Executable file formats +# +CONFIG_BINFMT_ELF=y +# CONFIG_BINFMT_MISC is not set + +# +# Device Drivers +# + +# +# Generic Driver Options +# +CONFIG_STANDALONE=y +CONFIG_PREVENT_FIRMWARE_BUILD=y +# CONFIG_FW_LOADER is not set + +# +# Memory Technology Devices (MTD) +# +# CONFIG_MTD is not set + +# +# Parallel port support +# +# CONFIG_PARPORT is not set + +# +# Plug and Play support +# + +# +# Block devices +# +# CONFIG_BLK_DEV_FD is not set +CONFIG_BLK_DEV_LOOP=y +# CONFIG_BLK_DEV_CRYPTOLOOP is not set +CONFIG_BLK_DEV_NBD=y +CONFIG_BLK_DEV_RAM=y +CONFIG_BLK_DEV_RAM_SIZE=4096 +# CONFIG_BLK_DEV_INITRD is not set + +# +# ATA/ATAPI/MFM/RLL support +# +CONFIG_IDE=y +CONFIG_BLK_DEV_IDE=y + +# +# Please see Documentation/ide.txt for help/info on IDE drives +# +# CONFIG_BLK_DEV_IDE_SATA is not set +CONFIG_BLK_DEV_IDEDISK=y +# CONFIG_IDEDISK_MULTI_MODE is not set +CONFIG_BLK_DEV_IDECS=y +CONFIG_BLK_DEV_IDECD=m +# CONFIG_BLK_DEV_IDETAPE is not set +# CONFIG_BLK_DEV_IDEFLOPPY is not set +# CONFIG_BLK_DEV_IDESCSI is not set +# CONFIG_IDE_TASK_IOCTL is not set +# CONFIG_IDE_TASKFILE_IO is not set + +# +# IDE chipset support/bugfixes +# +CONFIG_IDE_GENERIC=y +# CONFIG_IDE_ARM is not set +# CONFIG_BLK_DEV_IDEDMA is not set +# CONFIG_IDEDMA_AUTO is not set +# CONFIG_BLK_DEV_HD is not set + +# +# SCSI device support +# +CONFIG_SCSI=m +CONFIG_SCSI_PROC_FS=y + +# +# SCSI support type (disk, tape, CD-ROM) +# +CONFIG_BLK_DEV_SD=m +# CONFIG_CHR_DEV_ST is not set +# CONFIG_CHR_DEV_OSST is not set +CONFIG_BLK_DEV_SR=m +# CONFIG_BLK_DEV_SR_VENDOR is not set +CONFIG_CHR_DEV_SG=m + +# +# Some SCSI devices (e.g. CD jukebox) support multiple LUNs +# +CONFIG_SCSI_MULTI_LUN=y +# CONFIG_SCSI_CONSTANTS is not set +# CONFIG_SCSI_LOGGING is not set + +# +# SCSI Transport Attributes +# +# CONFIG_SCSI_SPI_ATTRS is not set +# CONFIG_SCSI_FC_ATTRS is not set + +# +# SCSI low-level drivers +# +# CONFIG_SCSI_AIC7XXX_OLD is not set +# CONFIG_SCSI_SATA is not set +# CONFIG_SCSI_EATA_PIO is not set +# CONFIG_SCSI_DEBUG is not set + +# +# PCMCIA SCSI adapter support +# +# CONFIG_PCMCIA_AHA152X is not set +# CONFIG_PCMCIA_FDOMAIN is not set +# CONFIG_PCMCIA_NINJA_SCSI is not set +# CONFIG_PCMCIA_QLOGIC is not set +# CONFIG_PCMCIA_SYM53C500 is not set + +# +# Multi-device support (RAID and LVM) +# +# CONFIG_MD is not set + +# +# Fusion MPT device support +# + +# +# IEEE 1394 (FireWire) support +# + +# +# I2O device support +# + +# +# Networking support +# +CONFIG_NET=y + +# +# Networking options +# +CONFIG_PACKET=y +# CONFIG_PACKET_MMAP is not set +# CONFIG_NETLINK_DEV 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_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_IPV6 is not set +# CONFIG_NETFILTER is not set + +# +# SCTP Configuration (EXPERIMENTAL) +# +# CONFIG_IP_SCTP 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_NET_DIVERT is not set +# CONFIG_ECONET is not set +# CONFIG_WAN_ROUTER is not set +# CONFIG_NET_HW_FLOWCONTROL is not set + +# +# QoS and/or fair queueing +# +# CONFIG_NET_SCHED is not set +# CONFIG_NET_CLS_ROUTE is not set + +# +# Network testing +# +# CONFIG_NET_PKTGEN is not set +# CONFIG_NETPOLL is not set +# CONFIG_NET_POLL_CONTROLLER is not set +# CONFIG_HAMRADIO is not set +# CONFIG_IRDA is not set +# CONFIG_BT is not set +# CONFIG_NETDEVICES is not set + +# +# ISDN subsystem +# +# CONFIG_ISDN is not set + +# +# Telephony Support +# +# CONFIG_PHONE is not set + +# +# Input device support +# +CONFIG_INPUT=y + +# +# Userland interfaces +# +# CONFIG_INPUT_MOUSEDEV is not set +# CONFIG_INPUT_JOYDEV is not set +# CONFIG_INPUT_TSDEV is not set +# CONFIG_INPUT_EVDEV is not set +# CONFIG_INPUT_EVBUG is not set + +# +# Input I/O drivers +# +# CONFIG_GAMEPORT is not set +CONFIG_SOUND_GAMEPORT=y +CONFIG_SERIO=y +# CONFIG_SERIO_I8042 is not set +CONFIG_SERIO_SERPORT=y +# CONFIG_SERIO_CT82C710 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_TOUCHSCREEN is not set +# CONFIG_INPUT_MISC is not set + +# +# Character devices +# +CONFIG_VT=y +CONFIG_VT_CONSOLE=y +CONFIG_HW_CONSOLE=y +# CONFIG_SERIAL_NONSTANDARD is not set + +# +# Serial drivers +# +# CONFIG_SERIAL_8250 is not set + +# +# Non-8250 serial port support +# +CONFIG_SERIAL_CORE=y +CONFIG_SERIAL_CORE_CONSOLE=y +# CONFIG_SERIAL_M32R_SIO is not set +CONFIG_SERIAL_M32R_PLDSIO=y +CONFIG_SERIAL_M32R_PLDSIO_CONSOLE=y +CONFIG_UNIX98_PTYS=y +CONFIG_LEGACY_PTYS=y +CONFIG_LEGACY_PTY_COUNT=256 +# CONFIG_QIC02_TAPE is not set + +# +# IPMI +# +# CONFIG_IPMI_HANDLER is not set + +# +# Watchdog Cards +# +# CONFIG_WATCHDOG is not set +# CONFIG_RTC is not set +# CONFIG_GEN_RTC is not set +# CONFIG_DTLK is not set +# CONFIG_R3964 is not set + +# +# Ftape, the floppy tape device driver +# +# CONFIG_AGP is not set +# CONFIG_DRM is not set + +# +# PCMCIA character devices +# +# CONFIG_SYNCLINK_CS is not set +# CONFIG_RAW_DRIVER is not set + +# +# I2C support +# +# CONFIG_I2C is not set + +# +# Dallas's 1-wire bus +# +# CONFIG_W1 is not set + +# +# Misc devices +# + +# +# Multimedia devices +# +CONFIG_VIDEO_DEV=y + +# +# Video For Linux +# + +# +# Video Adapters +# +# CONFIG_VIDEO_CPIA is not set +CONFIG_M32R_AR=y +CONFIG_M32R_AR_VGA=y + +# +# Radio Adapters +# +# CONFIG_RADIO_MAESTRO is not set + +# +# Digital Video Broadcasting Devices +# +# CONFIG_DVB is not set + +# +# Graphics support +# +CONFIG_FB=y +CONFIG_FB_EPSON_S1D13806=y +# CONFIG_FB_VIRTUAL is not set + +# +# Console display driver support +# +# CONFIG_VGA_CONSOLE is not set +# CONFIG_MDA_CONSOLE is not set +CONFIG_DUMMY_CONSOLE=y +CONFIG_FRAMEBUFFER_CONSOLE=y +# CONFIG_FONTS is not set +CONFIG_FONT_8x8=y +CONFIG_FONT_8x16=y + +# +# Logo configuration +# +CONFIG_LOGO=y +CONFIG_LOGO_LINUX_MONO=y +CONFIG_LOGO_LINUX_VGA16=y +CONFIG_LOGO_LINUX_CLUT224=y +CONFIG_LOGO_M32R_CLUT224=y + +# +# Sound +# +# CONFIG_SOUND is not set + +# +# USB support +# + +# +# USB Gadget Support +# +# CONFIG_USB_GADGET is not set + +# +# File systems +# +CONFIG_EXT2_FS=y +# CONFIG_EXT2_FS_XATTR 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_JBD=m +CONFIG_JBD_DEBUG=y +CONFIG_FS_MBCACHE=y +CONFIG_REISERFS_FS=m +# CONFIG_REISERFS_CHECK is not set +# CONFIG_REISERFS_PROC_INFO is not set +# CONFIG_REISERFS_FS_XATTR is not set +# CONFIG_JFS_FS is not set +# CONFIG_XFS_FS is not set +# CONFIG_MINIX_FS is not set +# CONFIG_ROMFS_FS is not set +# CONFIG_QUOTA is not set +# CONFIG_AUTOFS_FS is not set +# CONFIG_AUTOFS4_FS is not set + +# +# CD-ROM/DVD Filesystems +# +CONFIG_ISO9660_FS=m +CONFIG_JOLIET=y +# CONFIG_ZISOFS is not set +CONFIG_UDF_FS=m +CONFIG_UDF_NLS=y + +# +# DOS/FAT/NT Filesystems +# +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 + +# +# Pseudo filesystems +# +CONFIG_PROC_FS=y +CONFIG_PROC_KCORE=y +CONFIG_SYSFS=y +CONFIG_DEVFS_FS=y +CONFIG_DEVFS_MOUNT=y +# CONFIG_DEVFS_DEBUG is not set +# CONFIG_DEVPTS_FS_XATTR is not set +CONFIG_TMPFS=y +# CONFIG_HUGETLB_PAGE is not set +CONFIG_RAMFS=y + +# +# 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_HPFS_FS is not set +# CONFIG_QNX4FS_FS is not set +# CONFIG_SYSV_FS is not set +# CONFIG_UFS_FS is not set + +# +# Network File Systems +# +CONFIG_NFS_FS=y +CONFIG_NFS_V3=y +# CONFIG_NFS_V4 is not set +# CONFIG_NFS_DIRECTIO is not set +# CONFIG_NFSD is not set +CONFIG_ROOT_NFS=y +CONFIG_LOCKD=y +CONFIG_LOCKD_V4=y +# CONFIG_EXPORTFS is not set +CONFIG_SUNRPC=y +# CONFIG_RPCSEC_GSS_KRB5 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 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 + +# +# Profiling support +# +# CONFIG_PROFILING is not set + +# +# Kernel hacking +# +# CONFIG_DEBUG_KERNEL is not set +# CONFIG_DEBUG_SPINLOCK_SLEEP is not set +# CONFIG_FRAME_POINTER is not set + +# +# Security options +# +# CONFIG_SECURITY is not set + +# +# Cryptographic options +# +# CONFIG_CRYPTO is not set + +# +# Library routines +# +# CONFIG_CRC_CCITT is not set +CONFIG_CRC32=y +# CONFIG_LIBCRC32C is not set diff --git a/arch/m32r/m32700ut/dot.gdbinit_200MHz_16MB b/arch/m32r/m32700ut/dot.gdbinit_200MHz_16MB new file mode 100644 index 000000000..fa79cd9fb --- /dev/null +++ b/arch/m32r/m32700ut/dot.gdbinit_200MHz_16MB @@ -0,0 +1,249 @@ +# .gdbinit file +# $Id: dot.gdbinit_200MHz_16MB,v 1.1 2004/08/17 02:58:11 takata Exp $ +#----- +# NOTE: this file is generated by a script, "gen_gdbinit.pl". +# (Please type "gen_gdbinit.pl --help" and check the help message). +# $ Id: gen_gdbinit.pl,v 1.12 2004/07/26 09:56:10 takata Exp $ +#----- +# target platform: m32700ut + +# setting +set width 0d70 +set radix 0d16 + +debug_chaos + +# clk xin:cpu:bif:bus=25:200:50:50 +define clock_init + set *(unsigned long *)0x00ef4008 = 0x00000000 + set *(unsigned long *)0x00ef4004 = 0 + shell sleep 0.1 + # NOTE: Please change the master clock source from PLL-clock to Xin-clock + # and switch off PLL, before resetting the clock gear ratio. + + set *(unsigned long *)0x00ef4024 = 2 + set *(unsigned long *)0x00ef4020 = 2 + set *(unsigned long *)0x00ef4010 = 0 + set *(unsigned long *)0x00ef4014 = 0 + set *(unsigned long *)0x00ef4004 = 3 + shell sleep 0.1 + set *(unsigned long *)0x00ef4008 = 0x00000200 +end + +# Initialize SDRAM controller +define sdram_init + # SDIR0 + set *(unsigned long *)0x00ef6008 = 0x00000182 + # SDIR1 + set *(unsigned long *)0x00ef600c = 0x00000001 + # Initialize wait + shell sleep 0.1 + # Ch0-MOD + set *(unsigned long *)0x00ef602c = 0x00000020 + # Ch0-TR + set *(unsigned long *)0x00ef6028 = 0x00041302 + # Ch0-ADR (size:16MB) + set *(unsigned long *)0x00ef6020 = 0x08000002 + # AutoRef On + set *(unsigned long *)0x00ef6004 = 0x00010517 + # Access enable + set *(unsigned long *)0x00ef6024 = 0x00000001 +end +document sdram_init + SDRAM controller initialization + 0x08000000 - 0x08ffffff (16MB) +end + +# Initialize BSEL3 for UT-CFC +define cfc_init + set $sfrbase = 0xa0ef0000 +# too fast +# set *(unsigned long *)($sfrbase + 0x5300) = 0x0b0b8000 +# set *(unsigned long *)($sfrbase + 0x5304) = 0x00102204 +# set *(unsigned long *)($sfrbase + 0x5300) = 0x1f1f8000 +# set *(unsigned long *)($sfrbase + 0x5300) = 0x1f1f1fdf +# set *(unsigned long *)($sfrbase + 0x5304) = 0x0013220f +# set *(unsigned long *)($sfrbase + 0x5304) = 0x0013330f +end +document cfc_init + CF controller initialization +end + +# MMU enable +define mmu_enable + set $evb=0x88000000 + set *(unsigned long *)0xffff0024=1 +end + +# MMU disable +define mmu_disable + set $evb=0 + set *(unsigned long *)0xffff0024=0 +end + +# Show TLB entries +define show_tlb_entries + set $i = 0 + set $addr = $arg0 + set $nr_entries = $arg1 + use_mon_code + while ($i < $nr_entries) + set $tlb_tag = *(unsigned long*)$addr + set $tlb_data = *(unsigned long*)($addr + 4) + printf " [%2d] 0x%08lx : 0x%08lx - 0x%08lx\n", $i, $addr, $tlb_tag, $tlb_data + set $i = $i + 1 + set $addr = $addr + 8 + end + use_debug_dma +end +define itlb + set $itlb=0xfe000000 + show_tlb_entries $itlb 0d32 +end +define dtlb + set $dtlb=0xfe000800 + show_tlb_entries $dtlb 0d32 +end + +# Initialize TLB entries +define init_tlb_entries + set $i = 0 + set $addr = $arg0 + set $nr_entries = $arg1 + use_mon_code + while ($i < $nr_entries) + set *(unsigned long *)($addr + 0x4) = 0 + set $i = $i + 1 + set $addr = $addr + 8 + end + use_debug_dma +end +define tlb_init + set $itlb=0xfe000000 + init_tlb_entries $itlb 0d32 + set $dtlb=0xfe000800 + init_tlb_entries $dtlb 0d32 +end + +# Show current task structure +define show_current + set $current = $spi & 0xffffe000 + printf "$current=0x%08lX\n",$current + print *(struct task_struct *)$current +end + +# Show user assigned task structure +define show_task + set = $arg0 & 0xffffe000 + printf "$task=0x%08lX\n",$task + print *(struct task_struct *)$task +end +document show_task + Show user assigned task structure + arg0 : task structure address +end + +# Show M32R registers +define show_regs + printf " R0[0x%08lX] R1[0x%08lX] R2[0x%08lX] R3[0x%08lX]\n",$r0,$r1,$r2,$r3 + printf " R4[0x%08lX] R5[0x%08lX] R6[0x%08lX] R7[0x%08lX]\n",$r4,$r5,$r6,$r7 + printf " R8[0x%08lX] R9[0x%08lX] R10[0x%08lX] R11[0x%08lX]\n",$r8,$r9,$r10,$r11 + printf "R12[0x%08lX] FP[0x%08lX] LR[0x%08lX] SP[0x%08lX]\n",$r12,$fp,$lr,$sp + printf "PSW[0x%08lX] CBR[0x%08lX] SPI[0x%08lX] SPU[0x%08lX]\n",$psw,$cbr,$spi,$spu + printf "BPC[0x%08lX] PC[0x%08lX] ACCL[0x%08lX] ACCH[0x%08lX]\n",$bpc,$pc,$accl,$acch + printf "EVB[0x%08lX]\n",$evb +end + +# Setup all +define setup + use_mon_code + set *(unsigned int)0xfffffffc=0x60 + shell sleep 0.1 + clock_init + shell sleep 0.1 + # SDRAM: 16MB + set *(unsigned long *)0x00ef6020 = 0x08000002 + cfc_init + # USB + set *(unsigned short *)0xb0301000 = 0x100 + + set $evb=0x08000000 +end + +# Load modules +define load_modules + use_debug_dma + load +end + +# Set kernel parameters +define set_kernel_parameters + set $param = (void*)0x08002000 + # INITRD_START + set *(unsigned long *)($param + 0x0010) = 0x082a0000 + # INITRD_SIZE + set *(unsigned long *)($param + 0x0014) = 0x00000000 + # M32R_CPUCLK + set *(unsigned long *)($param + 0x0018) = 0d200000000 + # M32R_BUSCLK + set *(unsigned long *)($param + 0x001c) = 0d50000000 + + # M32R_TIMER_DIVIDE + set *(unsigned long *)($param + 0x0020) = 0d128 + + set {char[0x200]}($param + 0x100) = "console=ttyD0,115200n8x console=tty1 video=s1d13xxxfb:mode:240x320-16 root=/dev/nfsroot nfsroot=192.168.0.1:/project/m32r-linux/export/rootfs,rsize=1024,wsize=1024 nfsaddrs=192.168.0.101:192.168.0.1:192.168.0.1:255.255.255.0:mappi001 mem=16M \0" +end + +# Boot +define boot + set_kernel_parameters + set $fp = 0 + set $pc = 0x08001000 + set *(unsigned char *)0xffffffff = 0x03 + si + c +end + +# Set breakpoints +define set_breakpoints + b *0x08000030 +end + +# Restart +define restart + sdireset + sdireset + set $pc = 0 + b *0x04001000 + b *0x08001000 + b *0x08002000 + si + c + tlb_init + del + setup + load_modules + boot +end + +define si + stepi + x/i $pc + show_reg +end + +sdireset +sdireset +file vmlinux +target m32rsdi +set $pc = 0 +b *0x04001000 +b *0x08001000 +b *0x08002000 +c +tlb_init +del +setup +load_modules +boot + diff --git a/arch/m32r/m32700ut/dot.gdbinit_300MHz_32MB b/arch/m32r/m32700ut/dot.gdbinit_300MHz_32MB new file mode 100644 index 000000000..4df06e1d9 --- /dev/null +++ b/arch/m32r/m32700ut/dot.gdbinit_300MHz_32MB @@ -0,0 +1,249 @@ +# .gdbinit file +# $Id: dot.gdbinit_300MHz_32MB,v 1.1 2004/08/17 02:58:11 takata Exp $ +#----- +# NOTE: this file is generated by a script, "gen_gdbinit.pl". +# (Please type "gen_gdbinit.pl --help" and check the help message). +# $ Id: gen_gdbinit.pl,v 1.12 2004/07/26 09:56:10 takata Exp $ +#----- +# target platform: m32700ut + +# setting +set width 0d70 +set radix 0d16 + +debug_chaos + +# clk xin:cpu:bif:bus=25:300:75:75 +define clock_init + set *(unsigned long *)0x00ef4008 = 0x00000000 + set *(unsigned long *)0x00ef4004 = 0 + shell sleep 0.1 + # NOTE: Please change the master clock source from PLL-clock to Xin-clock + # and switch off PLL, before resetting the clock gear ratio. + + set *(unsigned long *)0x00ef4024 = 2 + set *(unsigned long *)0x00ef4020 = 2 + set *(unsigned long *)0x00ef4010 = 0 + set *(unsigned long *)0x00ef4014 = 0 + set *(unsigned long *)0x00ef4004 = 5 + shell sleep 0.1 + set *(unsigned long *)0x00ef4008 = 0x00000200 +end + +# Initialize SDRAM controller +define sdram_init + # SDIR0 + set *(unsigned long *)0x00ef6008 = 0x00000182 + # SDIR1 + set *(unsigned long *)0x00ef600c = 0x00000001 + # Initialize wait + shell sleep 0.1 + # Ch0-MOD + set *(unsigned long *)0x00ef602c = 0x00000020 + # Ch0-TR + set *(unsigned long *)0x00ef6028 = 0x00051502 + # Ch0-ADR (size:16MB) + set *(unsigned long *)0x00ef6020 = 0x08000002 + # AutoRef On + set *(unsigned long *)0x00ef6004 = 0x00010e24 + # Access enable + set *(unsigned long *)0x00ef6024 = 0x00000001 +end +document sdram_init + SDRAM controller initialization + 0x08000000 - 0x08ffffff (16MB) +end + +# Initialize BSEL3 for UT-CFC +define cfc_init + set $sfrbase = 0xa0ef0000 +# too fast +# set *(unsigned long *)($sfrbase + 0x5300) = 0x0b0b8000 +# set *(unsigned long *)($sfrbase + 0x5304) = 0x00102204 +# set *(unsigned long *)($sfrbase + 0x5300) = 0x1f1f8000 +# set *(unsigned long *)($sfrbase + 0x5300) = 0x1f1f1fdf +# set *(unsigned long *)($sfrbase + 0x5304) = 0x0013220f +# set *(unsigned long *)($sfrbase + 0x5304) = 0x0013330f +end +document cfc_init + CF controller initialization +end + +# MMU enable +define mmu_enable + set $evb=0x88000000 + set *(unsigned long *)0xffff0024=1 +end + +# MMU disable +define mmu_disable + set $evb=0 + set *(unsigned long *)0xffff0024=0 +end + +# Show TLB entries +define show_tlb_entries + set $i = 0 + set $addr = $arg0 + set $nr_entries = $arg1 + use_mon_code + while ($i < $nr_entries) + set $tlb_tag = *(unsigned long*)$addr + set $tlb_data = *(unsigned long*)($addr + 4) + printf " [%2d] 0x%08lx : 0x%08lx - 0x%08lx\n", $i, $addr, $tlb_tag, $tlb_data + set $i = $i + 1 + set $addr = $addr + 8 + end + use_debug_dma +end +define itlb + set $itlb=0xfe000000 + show_tlb_entries $itlb 0d32 +end +define dtlb + set $dtlb=0xfe000800 + show_tlb_entries $dtlb 0d32 +end + +# Initialize TLB entries +define init_tlb_entries + set $i = 0 + set $addr = $arg0 + set $nr_entries = $arg1 + use_mon_code + while ($i < $nr_entries) + set *(unsigned long *)($addr + 0x4) = 0 + set $i = $i + 1 + set $addr = $addr + 8 + end + use_debug_dma +end +define tlb_init + set $itlb=0xfe000000 + init_tlb_entries $itlb 0d32 + set $dtlb=0xfe000800 + init_tlb_entries $dtlb 0d32 +end + +# Show current task structure +define show_current + set $current = $spi & 0xffffe000 + printf "$current=0x%08lX\n",$current + print *(struct task_struct *)$current +end + +# Show user assigned task structure +define show_task + set = $arg0 & 0xffffe000 + printf "$task=0x%08lX\n",$task + print *(struct task_struct *)$task +end +document show_task + Show user assigned task structure + arg0 : task structure address +end + +# Show M32R registers +define show_regs + printf " R0[0x%08lX] R1[0x%08lX] R2[0x%08lX] R3[0x%08lX]\n",$r0,$r1,$r2,$r3 + printf " R4[0x%08lX] R5[0x%08lX] R6[0x%08lX] R7[0x%08lX]\n",$r4,$r5,$r6,$r7 + printf " R8[0x%08lX] R9[0x%08lX] R10[0x%08lX] R11[0x%08lX]\n",$r8,$r9,$r10,$r11 + printf "R12[0x%08lX] FP[0x%08lX] LR[0x%08lX] SP[0x%08lX]\n",$r12,$fp,$lr,$sp + printf "PSW[0x%08lX] CBR[0x%08lX] SPI[0x%08lX] SPU[0x%08lX]\n",$psw,$cbr,$spi,$spu + printf "BPC[0x%08lX] PC[0x%08lX] ACCL[0x%08lX] ACCH[0x%08lX]\n",$bpc,$pc,$accl,$acch + printf "EVB[0x%08lX]\n",$evb +end + +# Setup all +define setup + use_mon_code + set *(unsigned int)0xfffffffc=0x60 + shell sleep 0.1 + clock_init + shell sleep 0.1 + # SDRAM: 16MB + set *(unsigned long *)0x00ef6020 = 0x08000002 + cfc_init + # USB + set *(unsigned short *)0xb0301000 = 0x100 + + set $evb=0x08000000 +end + +# Load modules +define load_modules + use_debug_dma + load +end + +# Set kernel parameters +define set_kernel_parameters + set $param = (void*)0x08002000 + # INITRD_START + set *(unsigned long *)($param + 0x0010) = 0x082a0000 + # INITRD_SIZE + set *(unsigned long *)($param + 0x0014) = 0x00000000 + # M32R_CPUCLK + set *(unsigned long *)($param + 0x0018) = 0d300000000 + # M32R_BUSCLK + set *(unsigned long *)($param + 0x001c) = 0d75000000 + + # M32R_TIMER_DIVIDE + set *(unsigned long *)($param + 0x0020) = 0d128 + + set {char[0x200]}($param + 0x100) = "console=ttyD0,115200n8x console=tty1 video=s1d13xxxfb:mode:240x320-16 root=/dev/nfsroot nfsroot=192.168.0.1:/project/m32r-linux/export/rootfs,rsize=1024,wsize=1024 nfsaddrs=192.168.0.101:192.168.0.1:192.168.0.1:255.255.255.0:mappi001 mem=16M \0" +end + +# Boot +define boot + set_kernel_parameters + set $fp = 0 + set $pc = 0x08001000 + set *(unsigned char *)0xffffffff = 0x03 + si + c +end + +# Set breakpoints +define set_breakpoints + b *0x08000030 +end + +# Restart +define restart + sdireset + sdireset + set $pc = 0 + b *0x04001000 + b *0x08001000 + b *0x08002000 + si + c + tlb_init + del + setup + load_modules + boot +end + +define si + stepi + x/i $pc + show_reg +end + +sdireset +sdireset +file vmlinux +target m32rsdi +set $pc = 0 +b *0x04001000 +b *0x08001000 +b *0x08002000 +c +tlb_init +del +setup +load_modules +boot + diff --git a/arch/m32r/mappi/defconfig.nommu b/arch/m32r/mappi/defconfig.nommu new file mode 100644 index 000000000..cae54bf18 --- /dev/null +++ b/arch/m32r/mappi/defconfig.nommu @@ -0,0 +1,529 @@ +# +# Automatically generated make config: don't edit +# +CONFIG_M32R=y +CONFIG_UID16=y +CONFIG_GENERIC_ISA_DMA=y + +# +# Code maturity level options +# +CONFIG_EXPERIMENTAL=y +CONFIG_CLEAN_COMPILE=y +CONFIG_BROKEN_ON_SMP=y + +# +# General setup +# +# CONFIG_POSIX_MQUEUE is not set +CONFIG_BSD_PROCESS_ACCT=y +# CONFIG_BSD_PROCESS_ACCT_V3 is not set +CONFIG_SYSCTL=y +# CONFIG_AUDIT is not set +CONFIG_LOG_BUF_SHIFT=14 +CONFIG_HOTPLUG=y +CONFIG_IKCONFIG=y +# CONFIG_IKCONFIG_PROC is not set +CONFIG_EMBEDDED=y +# CONFIG_KALLSYMS is not set +# CONFIG_FUTEX is not set +# CONFIG_EPOLL is not set +CONFIG_IOSCHED_NOOP=y +# CONFIG_IOSCHED_AS is not set +CONFIG_IOSCHED_DEADLINE=y +CONFIG_IOSCHED_CFQ=y +# CONFIG_CC_OPTIMIZE_FOR_SIZE is not set + +# +# Loadable module support +# +CONFIG_MODULES=y +CONFIG_MODULE_UNLOAD=y +# CONFIG_MODULE_FORCE_UNLOAD is not set +CONFIG_OBSOLETE_MODPARM=y +# CONFIG_MODVERSIONS is not set +CONFIG_KMOD=y + +# +# Processor type and features +# +CONFIG_PLAT_MAPPI=y +# CONFIG_PLAT_USRV is not set +# CONFIG_PLAT_M32700UT is not set +# CONFIG_PLAT_OPSPUT is not set +# CONFIG_PLAT_OAKS32R is not set +# CONFIG_PLAT_MAPPI2 is not set +CONFIG_CHIP_M32700=y +# CONFIG_CHIP_M32102 is not set +# CONFIG_CHIP_VDEC2 is not set +# CONFIG_CHIP_OPSP is not set +# CONFIG_MMU is not set +CONFIG_TLB_ENTRIES=32 +CONFIG_ISA_M32R2=y +CONFIG_ISA_DSP_LEVEL2=y +CONFIG_ISA_DUAL_ISSUE=y +CONFIG_BUS_CLOCK=50000000 +CONFIG_TIMER_DIVIDE=128 +# CONFIG_CPU_LITTLE_ENDIAN is not set +CONFIG_MEMORY_START=0x00000000 +CONFIG_MEMORY_SIZE=0x00E00000 +CONFIG_NOHIGHMEM=y +# CONFIG_DISCONTIGMEM is not set +CONFIG_RWSEM_GENERIC_SPINLOCK=y +# CONFIG_RWSEM_XCHGADD_ALGORITHM is not set +CONFIG_PREEMPT=y +# CONFIG_HAVE_DEC_LOCK is not set +# CONFIG_SMP is not set + +# +# M32R drivers +# +# CONFIG_M32RPCC is not set +CONFIG_M32R_NE2000=y + +# +# Power management options (ACPI, APM) +# +# CONFIG_PM is not set + +# +# Bus options (PCI, PCMCIA, EISA, MCA, ISA) +# +# CONFIG_PCI is not set +# CONFIG_ISA is not set + +# +# PCMCIA/CardBus support +# +# CONFIG_PCMCIA is not set + +# +# PCI Hotplug Support +# + +# +# Executable file formats +# +CONFIG_BINFMT_FLAT=y +# CONFIG_BINFMT_ZFLAT is not set +# CONFIG_BINFMT_SHARED_FLAT is not set +# CONFIG_BINFMT_MISC is not set + +# +# Device Drivers +# + +# +# Generic Driver Options +# +CONFIG_STANDALONE=y +CONFIG_PREVENT_FIRMWARE_BUILD=y +# CONFIG_FW_LOADER is not set + +# +# Memory Technology Devices (MTD) +# +# CONFIG_MTD is not set + +# +# Parallel port support +# +# CONFIG_PARPORT is not set + +# +# Plug and Play support +# + +# +# Block devices +# +# CONFIG_BLK_DEV_FD is not set +CONFIG_BLK_DEV_LOOP=y +# CONFIG_BLK_DEV_CRYPTOLOOP is not set +CONFIG_BLK_DEV_NBD=y +CONFIG_BLK_DEV_RAM=y +CONFIG_BLK_DEV_RAM_SIZE=4096 +# CONFIG_BLK_DEV_INITRD is not set + +# +# ATA/ATAPI/MFM/RLL support +# +# CONFIG_IDE is not set + +# +# SCSI device support +# +# CONFIG_SCSI is not set + +# +# Multi-device support (RAID and LVM) +# +# CONFIG_MD is not set + +# +# Fusion MPT device support +# + +# +# IEEE 1394 (FireWire) support +# + +# +# I2O device support +# + +# +# Networking support +# +CONFIG_NET=y + +# +# Networking options +# +CONFIG_PACKET=y +# CONFIG_PACKET_MMAP is not set +# CONFIG_NETLINK_DEV 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_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_IPV6 is not set +# CONFIG_NETFILTER is not set + +# +# SCTP Configuration (EXPERIMENTAL) +# +# CONFIG_IP_SCTP 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_NET_DIVERT is not set +# CONFIG_ECONET is not set +# CONFIG_WAN_ROUTER is not set +# CONFIG_NET_HW_FLOWCONTROL is not set + +# +# QoS and/or fair queueing +# +# CONFIG_NET_SCHED is not set +# CONFIG_NET_CLS_ROUTE is not set + +# +# Network testing +# +# CONFIG_NET_PKTGEN is not set +# CONFIG_NETPOLL is not set +# CONFIG_NET_POLL_CONTROLLER is not set +# CONFIG_HAMRADIO is not set +# CONFIG_IRDA is not set +# CONFIG_BT is not set +# CONFIG_NETDEVICES is not set + +# +# ISDN subsystem +# +# CONFIG_ISDN is not set + +# +# Telephony Support +# +# CONFIG_PHONE is not set + +# +# Input device support +# +CONFIG_INPUT=y + +# +# Userland interfaces +# +# CONFIG_INPUT_MOUSEDEV is not set +# CONFIG_INPUT_JOYDEV is not set +# CONFIG_INPUT_TSDEV is not set +# CONFIG_INPUT_EVDEV is not set +# CONFIG_INPUT_EVBUG is not set + +# +# Input I/O drivers +# +# CONFIG_GAMEPORT is not set +CONFIG_SOUND_GAMEPORT=y +CONFIG_SERIO=y +# CONFIG_SERIO_I8042 is not set +CONFIG_SERIO_SERPORT=y +# CONFIG_SERIO_CT82C710 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_TOUCHSCREEN is not set +# CONFIG_INPUT_MISC is not set + +# +# Character devices +# +# CONFIG_VT is not set +# CONFIG_SERIAL_NONSTANDARD is not set + +# +# Serial drivers +# +# CONFIG_SERIAL_8250 is not set + +# +# Non-8250 serial port support +# +CONFIG_SERIAL_CORE=y +CONFIG_SERIAL_CORE_CONSOLE=y +CONFIG_SERIAL_M32R_SIO=y +CONFIG_SERIAL_M32R_SIO_CONSOLE=y +# CONFIG_SERIAL_M32R_PLDSIO is not set +CONFIG_UNIX98_PTYS=y +CONFIG_LEGACY_PTYS=y +CONFIG_LEGACY_PTY_COUNT=256 +# CONFIG_QIC02_TAPE is not set + +# +# IPMI +# +# CONFIG_IPMI_HANDLER is not set + +# +# Watchdog Cards +# +# CONFIG_WATCHDOG is not set +# CONFIG_RTC is not set +# CONFIG_GEN_RTC is not set +# CONFIG_DTLK is not set +# CONFIG_R3964 is not set + +# +# Ftape, the floppy tape device driver +# +# CONFIG_AGP is not set +# CONFIG_DRM is not set +# CONFIG_RAW_DRIVER is not set + +# +# I2C support +# +# CONFIG_I2C is not set + +# +# Dallas's 1-wire bus +# +# CONFIG_W1 is not set + +# +# Misc devices +# + +# +# Multimedia devices +# +# CONFIG_VIDEO_DEV is not set + +# +# Digital Video Broadcasting Devices +# +# CONFIG_DVB is not set + +# +# Graphics support +# +# CONFIG_FB is not set + +# +# Sound +# +# CONFIG_SOUND is not set + +# +# USB support +# + +# +# USB Gadget Support +# +# CONFIG_USB_GADGET is not set + +# +# File systems +# +CONFIG_EXT2_FS=y +# CONFIG_EXT2_FS_XATTR is not set +# CONFIG_EXT3_FS is not set +# CONFIG_JBD is not set +# CONFIG_REISERFS_FS is not set +# CONFIG_JFS_FS is not set +# CONFIG_XFS_FS is not set +# CONFIG_MINIX_FS is not set +# CONFIG_ROMFS_FS is not set +# CONFIG_QUOTA is not set +# CONFIG_AUTOFS_FS is not set +# CONFIG_AUTOFS4_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_KCORE=y +CONFIG_SYSFS=y +CONFIG_DEVFS_FS=y +CONFIG_DEVFS_MOUNT=y +# CONFIG_DEVFS_DEBUG is not set +CONFIG_DEVPTS_FS_XATTR=y +CONFIG_DEVPTS_FS_SECURITY=y +# CONFIG_TMPFS is not set +# CONFIG_HUGETLB_PAGE is not set +CONFIG_RAMFS=y + +# +# 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_HPFS_FS is not set +# CONFIG_QNX4FS_FS is not set +# CONFIG_SYSV_FS is not set +# CONFIG_UFS_FS is not set + +# +# Network File Systems +# +CONFIG_NFS_FS=y +CONFIG_NFS_V3=y +# CONFIG_NFS_V4 is not set +# CONFIG_NFS_DIRECTIO is not set +# CONFIG_NFSD is not set +CONFIG_ROOT_NFS=y +CONFIG_LOCKD=y +CONFIG_LOCKD_V4=y +# CONFIG_EXPORTFS is not set +CONFIG_SUNRPC=y +# CONFIG_RPCSEC_GSS_KRB5 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 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 + +# +# Profiling support +# +# CONFIG_PROFILING is not set + +# +# Kernel hacking +# +# CONFIG_DEBUG_KERNEL is not set +# CONFIG_DEBUG_SPINLOCK_SLEEP is not set +# CONFIG_FRAME_POINTER is not set + +# +# Security options +# +# CONFIG_SECURITY is not set + +# +# Cryptographic options +# +# CONFIG_CRYPTO is not set + +# +# Library routines +# +# CONFIG_CRC_CCITT is not set +CONFIG_CRC32=y +# CONFIG_LIBCRC32C is not set diff --git a/arch/m32r/mappi/defconfig.smp b/arch/m32r/mappi/defconfig.smp new file mode 100644 index 000000000..b0fe57100 --- /dev/null +++ b/arch/m32r/mappi/defconfig.smp @@ -0,0 +1,646 @@ +# +# Automatically generated make config: don't edit +# +CONFIG_M32R=y +CONFIG_UID16=y +CONFIG_GENERIC_ISA_DMA=y + +# +# Code maturity level options +# +CONFIG_EXPERIMENTAL=y +# CONFIG_CLEAN_COMPILE is not set +CONFIG_BROKEN=y +CONFIG_BROKEN_ON_SMP=y + +# +# General setup +# +CONFIG_SWAP=y +CONFIG_SYSVIPC=y +# CONFIG_POSIX_MQUEUE is not set +# CONFIG_BSD_PROCESS_ACCT is not set +CONFIG_SYSCTL=y +# CONFIG_AUDIT is not set +CONFIG_LOG_BUF_SHIFT=15 +CONFIG_HOTPLUG=y +CONFIG_IKCONFIG=y +CONFIG_IKCONFIG_PROC=y +CONFIG_EMBEDDED=y +# CONFIG_KALLSYMS is not set +# CONFIG_FUTEX is not set +# CONFIG_EPOLL is not set +CONFIG_IOSCHED_NOOP=y +# CONFIG_IOSCHED_AS is not set +CONFIG_IOSCHED_DEADLINE=y +CONFIG_IOSCHED_CFQ=y +# CONFIG_CC_OPTIMIZE_FOR_SIZE is not set + +# +# Loadable module support +# +CONFIG_MODULES=y +CONFIG_MODULE_UNLOAD=y +# CONFIG_MODULE_FORCE_UNLOAD is not set +CONFIG_OBSOLETE_MODPARM=y +# CONFIG_MODVERSIONS is not set +CONFIG_KMOD=y +CONFIG_STOP_MACHINE=y + +# +# Processor type and features +# +CONFIG_PLAT_MAPPI=y +# CONFIG_PLAT_USRV is not set +# CONFIG_PLAT_M32700UT is not set +# CONFIG_PLAT_OPSPUT is not set +# CONFIG_PLAT_OAKS32R is not set +# CONFIG_PLAT_MAPPI2 is not set +CONFIG_CHIP_M32700=y +# CONFIG_CHIP_M32102 is not set +# CONFIG_CHIP_VDEC2 is not set +# CONFIG_CHIP_OPSP is not set +CONFIG_MMU=y +CONFIG_TLB_ENTRIES=32 +CONFIG_ISA_M32R2=y +CONFIG_ISA_DSP_LEVEL2=y +CONFIG_ISA_DUAL_ISSUE=y +CONFIG_BUS_CLOCK=10000000 +CONFIG_TIMER_DIVIDE=128 +# CONFIG_CPU_LITTLE_ENDIAN is not set +CONFIG_MEMORY_START=0x08000000 +CONFIG_MEMORY_SIZE=0x04000000 +CONFIG_NOHIGHMEM=y +CONFIG_DISCONTIGMEM=y +CONFIG_IRAM_START=0x00f00000 +CONFIG_IRAM_SIZE=0x00080000 +CONFIG_RWSEM_GENERIC_SPINLOCK=y +# CONFIG_RWSEM_XCHGADD_ALGORITHM is not set +CONFIG_PREEMPT=y +# CONFIG_HAVE_DEC_LOCK is not set +CONFIG_SMP=y +CONFIG_CHIP_M32700_TS1=y +CONFIG_NR_CPUS=2 +# CONFIG_NUMA is not set + +# +# M32R drivers +# +CONFIG_M32RPCC=y +CONFIG_M32R_NE2000=y + +# +# Power management options (ACPI, APM) +# +# CONFIG_PM is not set + +# +# Bus options (PCI, PCMCIA, EISA, MCA, ISA) +# +# CONFIG_PCI is not set +# CONFIG_ISA is not set + +# +# PCMCIA/CardBus support +# +CONFIG_PCMCIA=y +# CONFIG_PCMCIA_DEBUG is not set +# CONFIG_TCIC is not set + +# +# PCI Hotplug Support +# + +# +# Executable file formats +# +CONFIG_BINFMT_ELF=y +# CONFIG_BINFMT_MISC is not set + +# +# Device Drivers +# + +# +# Generic Driver Options +# +# CONFIG_STANDALONE is not set +CONFIG_PREVENT_FIRMWARE_BUILD=y +# CONFIG_FW_LOADER is not set + +# +# Memory Technology Devices (MTD) +# +CONFIG_MTD=y +# CONFIG_MTD_DEBUG is not set +CONFIG_MTD_PARTITIONS=y +# CONFIG_MTD_CONCAT is not set +CONFIG_MTD_REDBOOT_PARTS=y +# CONFIG_MTD_REDBOOT_PARTS_UNALLOCATED is not set +# CONFIG_MTD_REDBOOT_PARTS_READONLY is not set +# CONFIG_MTD_CMDLINE_PARTS is not set + +# +# User Modules And Translation Layers +# +CONFIG_MTD_CHAR=y +CONFIG_MTD_BLOCK=y +# CONFIG_FTL is not set +# CONFIG_NFTL is not set +# CONFIG_INFTL 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 +# CONFIG_MTD_OBSOLETE_CHIPS is not set + +# +# Mapping drivers for chip access +# +# CONFIG_MTD_COMPLEX_MAPPINGS 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_BLKMTD 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 + +# +# NAND Flash Device Drivers +# +# CONFIG_MTD_NAND is not set + +# +# Parallel port support +# +# CONFIG_PARPORT is not set + +# +# Plug and Play support +# + +# +# Block devices +# +# CONFIG_BLK_DEV_FD is not set +CONFIG_BLK_DEV_LOOP=y +# CONFIG_BLK_DEV_CRYPTOLOOP is not set +CONFIG_BLK_DEV_NBD=m +CONFIG_BLK_DEV_RAM=y +CONFIG_BLK_DEV_RAM_SIZE=4096 +CONFIG_BLK_DEV_INITRD=y + +# +# ATA/ATAPI/MFM/RLL support +# +CONFIG_IDE=m +CONFIG_BLK_DEV_IDE=m + +# +# Please see Documentation/ide.txt for help/info on IDE drives +# +# CONFIG_BLK_DEV_IDE_SATA is not set +CONFIG_BLK_DEV_IDEDISK=m +# CONFIG_IDEDISK_MULTI_MODE is not set +CONFIG_BLK_DEV_IDECS=m +CONFIG_BLK_DEV_IDECD=m +# CONFIG_BLK_DEV_IDETAPE is not set +# CONFIG_BLK_DEV_IDEFLOPPY is not set +# CONFIG_IDE_TASK_IOCTL is not set +# CONFIG_IDE_TASKFILE_IO is not set + +# +# IDE chipset support/bugfixes +# +CONFIG_IDE_GENERIC=m +# CONFIG_IDE_ARM is not set +# CONFIG_BLK_DEV_IDEDMA is not set +# CONFIG_IDEDMA_AUTO is not set +# CONFIG_BLK_DEV_HD is not set + +# +# SCSI device support +# +# CONFIG_SCSI is not set + +# +# Multi-device support (RAID and LVM) +# +# CONFIG_MD is not set + +# +# Fusion MPT device support +# + +# +# IEEE 1394 (FireWire) support +# +# CONFIG_IEEE1394 is not set + +# +# I2O device support +# + +# +# Networking support +# +CONFIG_NET=y + +# +# Networking options +# +# CONFIG_PACKET is not set +# CONFIG_NETLINK_DEV 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_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_IPV6 is not set +# CONFIG_NETFILTER is not set + +# +# SCTP Configuration (EXPERIMENTAL) +# +# CONFIG_IP_SCTP 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_NET_DIVERT is not set +# CONFIG_ECONET is not set +# CONFIG_WAN_ROUTER is not set +# CONFIG_NET_HW_FLOWCONTROL is not set + +# +# QoS and/or fair queueing +# +# CONFIG_NET_SCHED is not set +# CONFIG_NET_CLS_ROUTE is not set + +# +# Network testing +# +# CONFIG_NET_PKTGEN is not set +# CONFIG_NETPOLL is not set +# CONFIG_NET_POLL_CONTROLLER is not set +# CONFIG_HAMRADIO is not set +# CONFIG_IRDA is not set +# CONFIG_BT is not set +# CONFIG_NETDEVICES is not set + +# +# ISDN subsystem +# +# CONFIG_ISDN is not set + +# +# Telephony Support +# +# CONFIG_PHONE is not set + +# +# Input device support +# +CONFIG_INPUT=y + +# +# 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_TSDEV is not set +# CONFIG_INPUT_EVDEV is not set +# CONFIG_INPUT_EVBUG is not set + +# +# Input I/O drivers +# +# CONFIG_GAMEPORT is not set +CONFIG_SOUND_GAMEPORT=y +CONFIG_SERIO=y +# CONFIG_SERIO_I8042 is not set +# CONFIG_SERIO_SERPORT is not set +# CONFIG_SERIO_CT82C710 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_TOUCHSCREEN is not set +# CONFIG_INPUT_MISC is not set + +# +# Character devices +# +# CONFIG_VT is not set +# CONFIG_SERIAL_NONSTANDARD is not set + +# +# Serial drivers +# +# CONFIG_SERIAL_8250 is not set + +# +# Non-8250 serial port support +# +CONFIG_SERIAL_CORE=y +CONFIG_SERIAL_CORE_CONSOLE=y +CONFIG_SERIAL_M32R_SIO=y +CONFIG_SERIAL_M32R_SIO_CONSOLE=y +# CONFIG_SERIAL_M32R_PLDSIO is not set +CONFIG_UNIX98_PTYS=y +CONFIG_LEGACY_PTYS=y +CONFIG_LEGACY_PTY_COUNT=256 +# CONFIG_QIC02_TAPE is not set + +# +# IPMI +# +# CONFIG_IPMI_HANDLER is not set + +# +# Watchdog Cards +# +# CONFIG_WATCHDOG is not set +# CONFIG_RTC is not set +# CONFIG_GEN_RTC is not set +# CONFIG_DTLK is not set +# CONFIG_R3964 is not set + +# +# Ftape, the floppy tape device driver +# +# CONFIG_AGP is not set +# CONFIG_DRM is not set + +# +# PCMCIA character devices +# +# CONFIG_SYNCLINK_CS is not set +# CONFIG_RAW_DRIVER is not set + +# +# I2C support +# +# CONFIG_I2C is not set + +# +# Dallas's 1-wire bus +# +# CONFIG_W1 is not set + +# +# Misc devices +# + +# +# Multimedia devices +# +# CONFIG_VIDEO_DEV is not set + +# +# Digital Video Broadcasting Devices +# +# CONFIG_DVB is not set + +# +# Graphics support +# +# CONFIG_FB is not set + +# +# Sound +# +# CONFIG_SOUND is not set + +# +# USB support +# + +# +# USB Gadget Support +# +# CONFIG_USB_GADGET is not set + +# +# File systems +# +CONFIG_EXT2_FS=y +# CONFIG_EXT2_FS_XATTR is not set +# CONFIG_EXT3_FS is not set +# CONFIG_JBD is not set +# CONFIG_REISERFS_FS is not set +# CONFIG_JFS_FS is not set +# CONFIG_XFS_FS is not set +# CONFIG_MINIX_FS is not set +CONFIG_ROMFS_FS=y +# CONFIG_QUOTA is not set +# CONFIG_AUTOFS_FS is not set +# CONFIG_AUTOFS4_FS is not set + +# +# CD-ROM/DVD Filesystems +# +CONFIG_ISO9660_FS=y +# CONFIG_JOLIET is not set +# CONFIG_ZISOFS is not set +# CONFIG_UDF_FS is not set + +# +# DOS/FAT/NT Filesystems +# +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 + +# +# Pseudo filesystems +# +CONFIG_PROC_FS=y +CONFIG_PROC_KCORE=y +CONFIG_SYSFS=y +CONFIG_DEVFS_FS=y +CONFIG_DEVFS_MOUNT=y +# CONFIG_DEVFS_DEBUG is not set +# CONFIG_DEVPTS_FS_XATTR is not set +CONFIG_TMPFS=y +# CONFIG_HUGETLBFS is not set +# CONFIG_HUGETLB_PAGE is not set +CONFIG_RAMFS=y + +# +# 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_JFFS_FS=y +CONFIG_JFFS_FS_VERBOSE=0 +CONFIG_JFFS_PROC_FS=y +CONFIG_JFFS2_FS=y +CONFIG_JFFS2_FS_DEBUG=0 +# CONFIG_JFFS2_FS_NAND is not set +# CONFIG_JFFS2_COMPRESSION_OPTIONS is not set +CONFIG_JFFS2_ZLIB=y +CONFIG_JFFS2_RTIME=y +# CONFIG_JFFS2_RUBIN is not set +# CONFIG_CRAMFS is not set +# CONFIG_VXFS_FS is not set +# CONFIG_HPFS_FS is not set +# CONFIG_QNX4FS_FS is not set +# CONFIG_SYSV_FS is not set +# CONFIG_UFS_FS is not set + +# +# Network File Systems +# +CONFIG_NFS_FS=y +CONFIG_NFS_V3=y +# CONFIG_NFS_V4 is not set +# CONFIG_NFS_DIRECTIO is not set +# CONFIG_NFSD is not set +CONFIG_ROOT_NFS=y +CONFIG_LOCKD=y +CONFIG_LOCKD_V4=y +# CONFIG_EXPORTFS is not set +CONFIG_SUNRPC=y +# CONFIG_RPCSEC_GSS_KRB5 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 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 + +# +# Profiling support +# +# CONFIG_PROFILING is not set + +# +# Kernel hacking +# +# CONFIG_DEBUG_KERNEL is not set +# CONFIG_DEBUG_SPINLOCK_SLEEP is not set +# CONFIG_FRAME_POINTER is not set + +# +# Security options +# +# CONFIG_SECURITY is not set + +# +# Cryptographic options +# +# CONFIG_CRYPTO is not set + +# +# Library routines +# +# CONFIG_CRC_CCITT is not set +CONFIG_CRC32=y +# CONFIG_LIBCRC32C is not set +CONFIG_ZLIB_INFLATE=y +CONFIG_ZLIB_DEFLATE=y diff --git a/arch/m32r/mappi/defconfig.up b/arch/m32r/mappi/defconfig.up new file mode 100644 index 000000000..b4ab5ff29 --- /dev/null +++ b/arch/m32r/mappi/defconfig.up @@ -0,0 +1,642 @@ +# +# Automatically generated make config: don't edit +# +CONFIG_M32R=y +CONFIG_UID16=y +CONFIG_GENERIC_ISA_DMA=y + +# +# Code maturity level options +# +CONFIG_EXPERIMENTAL=y +# CONFIG_CLEAN_COMPILE is not set +CONFIG_BROKEN=y +CONFIG_BROKEN_ON_SMP=y + +# +# General setup +# +CONFIG_SWAP=y +CONFIG_SYSVIPC=y +# CONFIG_POSIX_MQUEUE is not set +# CONFIG_BSD_PROCESS_ACCT is not set +CONFIG_SYSCTL=y +# CONFIG_AUDIT is not set +CONFIG_LOG_BUF_SHIFT=14 +CONFIG_HOTPLUG=y +CONFIG_IKCONFIG=y +CONFIG_IKCONFIG_PROC=y +CONFIG_EMBEDDED=y +# CONFIG_KALLSYMS is not set +# CONFIG_FUTEX is not set +# CONFIG_EPOLL is not set +CONFIG_IOSCHED_NOOP=y +# CONFIG_IOSCHED_AS is not set +CONFIG_IOSCHED_DEADLINE=y +CONFIG_IOSCHED_CFQ=y +# CONFIG_CC_OPTIMIZE_FOR_SIZE is not set + +# +# Loadable module support +# +CONFIG_MODULES=y +CONFIG_MODULE_UNLOAD=y +# CONFIG_MODULE_FORCE_UNLOAD is not set +CONFIG_OBSOLETE_MODPARM=y +# CONFIG_MODVERSIONS is not set +CONFIG_KMOD=y + +# +# Processor type and features +# +CONFIG_PLAT_MAPPI=y +# CONFIG_PLAT_USRV is not set +# CONFIG_PLAT_M32700UT is not set +# CONFIG_PLAT_OPSPUT is not set +# CONFIG_PLAT_OAKS32R is not set +# CONFIG_PLAT_MAPPI2 is not set +CONFIG_CHIP_M32700=y +# CONFIG_CHIP_M32102 is not set +# CONFIG_CHIP_VDEC2 is not set +# CONFIG_CHIP_OPSP is not set +CONFIG_MMU=y +CONFIG_TLB_ENTRIES=32 +CONFIG_ISA_M32R2=y +CONFIG_ISA_DSP_LEVEL2=y +CONFIG_ISA_DUAL_ISSUE=y +CONFIG_BUS_CLOCK=10000000 +CONFIG_TIMER_DIVIDE=128 +# CONFIG_CPU_LITTLE_ENDIAN is not set +CONFIG_MEMORY_START=0x08000000 +CONFIG_MEMORY_SIZE=0x04000000 +CONFIG_NOHIGHMEM=y +CONFIG_DISCONTIGMEM=y +CONFIG_IRAM_START=0x00f00000 +CONFIG_IRAM_SIZE=0x00080000 +CONFIG_RWSEM_GENERIC_SPINLOCK=y +# CONFIG_RWSEM_XCHGADD_ALGORITHM is not set +CONFIG_PREEMPT=y +# CONFIG_HAVE_DEC_LOCK is not set +# CONFIG_SMP is not set + +# +# M32R drivers +# +CONFIG_M32RPCC=y +CONFIG_M32R_NE2000=y + +# +# Power management options (ACPI, APM) +# +# CONFIG_PM is not set + +# +# Bus options (PCI, PCMCIA, EISA, MCA, ISA) +# +# CONFIG_PCI is not set +# CONFIG_ISA is not set + +# +# PCMCIA/CardBus support +# +CONFIG_PCMCIA=y +# CONFIG_PCMCIA_DEBUG is not set +# CONFIG_TCIC is not set + +# +# PCI Hotplug Support +# + +# +# Executable file formats +# +CONFIG_BINFMT_ELF=y +# CONFIG_BINFMT_MISC is not set + +# +# Device Drivers +# + +# +# Generic Driver Options +# +# CONFIG_STANDALONE is not set +CONFIG_PREVENT_FIRMWARE_BUILD=y +# CONFIG_FW_LOADER is not set + +# +# Memory Technology Devices (MTD) +# +CONFIG_MTD=y +# CONFIG_MTD_DEBUG is not set +CONFIG_MTD_PARTITIONS=y +# CONFIG_MTD_CONCAT is not set +CONFIG_MTD_REDBOOT_PARTS=y +# CONFIG_MTD_REDBOOT_PARTS_UNALLOCATED is not set +# CONFIG_MTD_REDBOOT_PARTS_READONLY is not set +# CONFIG_MTD_CMDLINE_PARTS is not set + +# +# User Modules And Translation Layers +# +CONFIG_MTD_CHAR=y +CONFIG_MTD_BLOCK=y +# CONFIG_FTL is not set +# CONFIG_NFTL is not set +# CONFIG_INFTL 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 +# CONFIG_MTD_OBSOLETE_CHIPS is not set + +# +# Mapping drivers for chip access +# +# CONFIG_MTD_COMPLEX_MAPPINGS 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_BLKMTD 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 + +# +# NAND Flash Device Drivers +# +# CONFIG_MTD_NAND is not set + +# +# Parallel port support +# +# CONFIG_PARPORT is not set + +# +# Plug and Play support +# + +# +# Block devices +# +# CONFIG_BLK_DEV_FD is not set +CONFIG_BLK_DEV_LOOP=y +# CONFIG_BLK_DEV_CRYPTOLOOP is not set +CONFIG_BLK_DEV_NBD=m +CONFIG_BLK_DEV_RAM=y +CONFIG_BLK_DEV_RAM_SIZE=4096 +CONFIG_BLK_DEV_INITRD=y + +# +# ATA/ATAPI/MFM/RLL support +# +CONFIG_IDE=m +CONFIG_BLK_DEV_IDE=m + +# +# Please see Documentation/ide.txt for help/info on IDE drives +# +# CONFIG_BLK_DEV_IDE_SATA is not set +CONFIG_BLK_DEV_IDEDISK=m +# CONFIG_IDEDISK_MULTI_MODE is not set +CONFIG_BLK_DEV_IDECS=m +CONFIG_BLK_DEV_IDECD=m +# CONFIG_BLK_DEV_IDETAPE is not set +# CONFIG_BLK_DEV_IDEFLOPPY is not set +# CONFIG_IDE_TASK_IOCTL is not set +# CONFIG_IDE_TASKFILE_IO is not set + +# +# IDE chipset support/bugfixes +# +CONFIG_IDE_GENERIC=m +# CONFIG_IDE_ARM is not set +# CONFIG_BLK_DEV_IDEDMA is not set +# CONFIG_IDEDMA_AUTO is not set +# CONFIG_BLK_DEV_HD is not set + +# +# SCSI device support +# +# CONFIG_SCSI is not set + +# +# Multi-device support (RAID and LVM) +# +# CONFIG_MD is not set + +# +# Fusion MPT device support +# + +# +# IEEE 1394 (FireWire) support +# +# CONFIG_IEEE1394 is not set + +# +# I2O device support +# + +# +# Networking support +# +CONFIG_NET=y + +# +# Networking options +# +# CONFIG_PACKET is not set +# CONFIG_NETLINK_DEV 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_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_IPV6 is not set +# CONFIG_NETFILTER is not set + +# +# SCTP Configuration (EXPERIMENTAL) +# +# CONFIG_IP_SCTP 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_NET_DIVERT is not set +# CONFIG_ECONET is not set +# CONFIG_WAN_ROUTER is not set +# CONFIG_NET_HW_FLOWCONTROL is not set + +# +# QoS and/or fair queueing +# +# CONFIG_NET_SCHED is not set +# CONFIG_NET_CLS_ROUTE is not set + +# +# Network testing +# +# CONFIG_NET_PKTGEN is not set +# CONFIG_NETPOLL is not set +# CONFIG_NET_POLL_CONTROLLER is not set +# CONFIG_HAMRADIO is not set +# CONFIG_IRDA is not set +# CONFIG_BT is not set +# CONFIG_NETDEVICES is not set + +# +# ISDN subsystem +# +# CONFIG_ISDN is not set + +# +# Telephony Support +# +# CONFIG_PHONE is not set + +# +# Input device support +# +CONFIG_INPUT=y + +# +# 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_TSDEV is not set +# CONFIG_INPUT_EVDEV is not set +# CONFIG_INPUT_EVBUG is not set + +# +# Input I/O drivers +# +# CONFIG_GAMEPORT is not set +CONFIG_SOUND_GAMEPORT=y +CONFIG_SERIO=y +# CONFIG_SERIO_I8042 is not set +# CONFIG_SERIO_SERPORT is not set +# CONFIG_SERIO_CT82C710 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_TOUCHSCREEN is not set +# CONFIG_INPUT_MISC is not set + +# +# Character devices +# +# CONFIG_VT is not set +# CONFIG_SERIAL_NONSTANDARD is not set + +# +# Serial drivers +# +# CONFIG_SERIAL_8250 is not set + +# +# Non-8250 serial port support +# +CONFIG_SERIAL_CORE=y +CONFIG_SERIAL_CORE_CONSOLE=y +CONFIG_SERIAL_M32R_SIO=y +CONFIG_SERIAL_M32R_SIO_CONSOLE=y +# CONFIG_SERIAL_M32R_PLDSIO is not set +CONFIG_UNIX98_PTYS=y +CONFIG_LEGACY_PTYS=y +CONFIG_LEGACY_PTY_COUNT=256 +# CONFIG_QIC02_TAPE is not set + +# +# IPMI +# +# CONFIG_IPMI_HANDLER is not set + +# +# Watchdog Cards +# +# CONFIG_WATCHDOG is not set +# CONFIG_RTC is not set +# CONFIG_GEN_RTC is not set +# CONFIG_DTLK is not set +# CONFIG_R3964 is not set + +# +# Ftape, the floppy tape device driver +# +# CONFIG_AGP is not set +# CONFIG_DRM is not set + +# +# PCMCIA character devices +# +# CONFIG_SYNCLINK_CS is not set +# CONFIG_RAW_DRIVER is not set + +# +# I2C support +# +# CONFIG_I2C is not set + +# +# Dallas's 1-wire bus +# +# CONFIG_W1 is not set + +# +# Misc devices +# + +# +# Multimedia devices +# +# CONFIG_VIDEO_DEV is not set + +# +# Digital Video Broadcasting Devices +# +# CONFIG_DVB is not set + +# +# Graphics support +# +# CONFIG_FB is not set + +# +# Sound +# +# CONFIG_SOUND is not set + +# +# USB support +# + +# +# USB Gadget Support +# +# CONFIG_USB_GADGET is not set + +# +# File systems +# +CONFIG_EXT2_FS=y +# CONFIG_EXT2_FS_XATTR is not set +# CONFIG_EXT3_FS is not set +# CONFIG_JBD is not set +# CONFIG_REISERFS_FS is not set +# CONFIG_JFS_FS is not set +# CONFIG_XFS_FS is not set +# CONFIG_MINIX_FS is not set +CONFIG_ROMFS_FS=y +# CONFIG_QUOTA is not set +# CONFIG_AUTOFS_FS is not set +# CONFIG_AUTOFS4_FS is not set + +# +# CD-ROM/DVD Filesystems +# +CONFIG_ISO9660_FS=y +# CONFIG_JOLIET is not set +# CONFIG_ZISOFS is not set +# CONFIG_UDF_FS is not set + +# +# DOS/FAT/NT Filesystems +# +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 + +# +# Pseudo filesystems +# +CONFIG_PROC_FS=y +CONFIG_PROC_KCORE=y +CONFIG_SYSFS=y +CONFIG_DEVFS_FS=y +CONFIG_DEVFS_MOUNT=y +# CONFIG_DEVFS_DEBUG is not set +# CONFIG_DEVPTS_FS_XATTR is not set +CONFIG_TMPFS=y +# CONFIG_HUGETLBFS is not set +# CONFIG_HUGETLB_PAGE is not set +CONFIG_RAMFS=y + +# +# 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_JFFS_FS=y +CONFIG_JFFS_FS_VERBOSE=0 +CONFIG_JFFS_PROC_FS=y +CONFIG_JFFS2_FS=y +CONFIG_JFFS2_FS_DEBUG=0 +# CONFIG_JFFS2_FS_NAND is not set +# CONFIG_JFFS2_COMPRESSION_OPTIONS is not set +CONFIG_JFFS2_ZLIB=y +CONFIG_JFFS2_RTIME=y +# CONFIG_JFFS2_RUBIN is not set +# CONFIG_CRAMFS is not set +# CONFIG_VXFS_FS is not set +# CONFIG_HPFS_FS is not set +# CONFIG_QNX4FS_FS is not set +# CONFIG_SYSV_FS is not set +# CONFIG_UFS_FS is not set + +# +# Network File Systems +# +CONFIG_NFS_FS=y +CONFIG_NFS_V3=y +# CONFIG_NFS_V4 is not set +# CONFIG_NFS_DIRECTIO is not set +# CONFIG_NFSD is not set +CONFIG_ROOT_NFS=y +CONFIG_LOCKD=y +CONFIG_LOCKD_V4=y +# CONFIG_EXPORTFS is not set +CONFIG_SUNRPC=y +# CONFIG_RPCSEC_GSS_KRB5 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 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 + +# +# Profiling support +# +# CONFIG_PROFILING is not set + +# +# Kernel hacking +# +# CONFIG_DEBUG_KERNEL is not set +# CONFIG_DEBUG_SPINLOCK_SLEEP is not set +# CONFIG_FRAME_POINTER is not set + +# +# Security options +# +# CONFIG_SECURITY is not set + +# +# Cryptographic options +# +# CONFIG_CRYPTO is not set + +# +# Library routines +# +# CONFIG_CRC_CCITT is not set +CONFIG_CRC32=y +# CONFIG_LIBCRC32C is not set +CONFIG_ZLIB_INFLATE=y +CONFIG_ZLIB_DEFLATE=y diff --git a/arch/m32r/mappi/dot.gdbinit b/arch/m32r/mappi/dot.gdbinit new file mode 100644 index 000000000..ea566b691 --- /dev/null +++ b/arch/m32r/mappi/dot.gdbinit @@ -0,0 +1,242 @@ +# .gdbinit file +# $Id$ +#----- +# NOTE: this file is generated by a script, "gen_gdbinit.pl". +# (Please type "gen_gdbinit.pl --help" and check the help message). +# $ Id: gen_gdbinit.pl,v 1.8 2004/02/27 07:08:32 takata Exp $ +#----- +# target platform: mappi + +# setting +set width 0d70 +set radix 0d16 +debug_chaos + +# clk xin:cpu:bif:bus=30:360:180:90 +define clock_init + set *(unsigned long *)0x00ef4024 = 2 + set *(unsigned long *)0x00ef4020 = 1 + set *(unsigned long *)0x00ef4010 = 0 + set *(unsigned long *)0x00ef4014 = 0 + set *(unsigned long *)0x00ef4004 = 5 + shell sleep 0.1 + set *(unsigned long *)0x00ef4008 = 0x00000200 +end + +# Initialize programmable ports +define port_init + set $sfrbase = 0x00ef0000 + set *(unsigned short *)0x00ef1060 = 0x5555 + set *(unsigned short *)0x00ef1062 = 0x5555 + set *(unsigned short *)0x00ef1064 = 0x5555 + set *(unsigned short *)0x00ef1066 = 0x5555 + set *(unsigned short *)0x00ef1068 = 0x5555 + set *(unsigned short *)0x00ef106a = 0x0000 + set *(unsigned short *)0x00ef106e = 0x5555 + set *(unsigned short *)0x00ef1070 = 0x5555 + # LED ON + set *(unsigned char *)($sfrbase + 0x1015) = 0xff + set *(unsigned char *)($sfrbase + 0x1085) = 0xff + shell sleep 0.1 + # LED OFF + set *(unsigned char *)($sfrbase + 0x1085) = 0x00 +end +document port_init + P5=LED(output), P6.b4=LAN_RESET(output) +end + +# Initialize SDRAM controller +define sdram_init + # SDIR0 + set *(unsigned long *)0x00ef6008 = 0x00000182 + # SDIR1 + set *(unsigned long *)0x00ef600c = 0x00000001 + # Initialize wait + shell sleep 0.1 + # Ch0-MOD + set *(unsigned long *)0x00ef602c = 0x00000020 + # Ch0-TR + set *(unsigned long *)0x00ef6028 = 0x00051502 + # Ch0-ADR (size:64MB) + set *(unsigned long *)0x00ef6020 = 0x08000004 + # AutoRef On + set *(unsigned long *)0x00ef6004 = 0x00010e2b + # Access enable + set *(unsigned long *)0x00ef6024 = 0x00000001 +end +document sdram_init + SDRAM controller initialization + 0x08000000 - 0x0bffffff (64MB) +end + +# Initialize LAN controller +define lanc_init + set $sfrbase = 0x00ef0000 + # Set BSEL3 (BSEL3 for the Chaos's bselc) + set *(unsigned long *)($sfrbase + 0x5300) = 0x0a0a8040 + set *(unsigned long *)($sfrbase + 0x5304) = 0x01120203 + set *(unsigned long *)($sfrbase + 0x5308) = 0x00000001 + # Reset (P5=LED,P6.b4=LAN_RESET) + set *(unsigned short *)($sfrbase + 0x106c) = 0x0000 + set *(unsigned char *)($sfrbase + 0x1016) = 0xff + set *(unsigned char *)($sfrbase + 0x1086) = 0xff + shell sleep 0.1 + # swivel: 0=normal, 4=reverse +# set *(unsigned char *)($sfrbase + 0x1086) = 0x00 + set *(unsigned char *)($sfrbase + 0x1086) = 0x04 + set *(unsigned long *)(0x0c000330) = 0xffffffff + # Set mac address + set $lanc = (void*)0x0c000300 + set *(unsigned long *)($lanc + 0x0000) = 0x00610010 + set *(unsigned long *)($lanc + 0x0004) = 0x00200030 + set *(unsigned long *)($lanc + 0x0008) = 0x00400050 + set *(unsigned long *)($lanc + 0x000c) = 0x00600007 +end +document lanc_init + LAN controller initialization + ex.) MAC address: 10 20 30 40 50 60 +end + +# LCD & CRT dual-head setting (8bpp) +define dispc_init + set $sfrbase = 0x00ef0000 + # BSEL4 Dispc + set *(unsigned long *)($sfrbase + 0x5400) = 0x0e0e8000 + set *(unsigned long *)($sfrbase + 0x5404) = 0x0012220a +end + +# MMU enable +define mmu_enable + set $evb=0x88000000 + set *(unsigned long *)0xffff0024=1 +end + +# MMU disable +define mmu_disable + set $evb=0 + set *(unsigned long *)0xffff0024=0 +end + +# Show TLB entries +define show_tlb_entries + set $i = 0 + set $addr = $arg0 + set $nr_entries = $arg1 + use_mon_code + while ($i < $nr_entries) + set $tlb_tag = *(unsigned long*)$addr + set $tlb_data = *(unsigned long*)($addr + 4) + printf " [%2d] 0x%08lx : 0x%08lx - 0x%08lx\n", $i, $addr, $tlb_tag, $tlb_data + set $i = $i + 1 + set $addr = $addr + 8 + end + use_debug_dma +end +define itlb + set $itlb=0xfe000000 + show_tlb_entries $itlb 0d32 +end +define dtlb + set $dtlb=0xfe000800 + show_tlb_entries $dtlb 0d32 +end + +# Show current task structure +define show_current + set $current = $spi & 0xffffe000 + printf "$current=0x%08lX\n",$current + print *(struct task_struct *)$current +end + +# Show user assigned task structure +define show_task + set = $arg0 & 0xffffe000 + printf "$task=0x%08lX\n",$task + print *(struct task_struct *)$task +end +document show_task + Show user assigned task structure + arg0 : task structure address +end + +# Show M32R registers +define show_regs + printf " R0[0x%08lX] R1[0x%08lX] R2[0x%08lX] R3[0x%08lX]\n",$r0,$r1,$r2,$r3 + printf " R4[0x%08lX] R5[0x%08lX] R6[0x%08lX] R7[0x%08lX]\n",$r4,$r5,$r6,$r7 + printf " R8[0x%08lX] R9[0x%08lX] R10[0x%08lX] R11[0x%08lX]\n",$r8,$r9,$r10,$r11 + printf "R12[0x%08lX] FP[0x%08lX] LR[0x%08lX] SP[0x%08lX]\n",$r12,$fp,$lr,$sp + printf "PSW[0x%08lX] CBR[0x%08lX] SPI[0x%08lX] SPU[0x%08lX]\n",$psw,$cbr,$spi,$spu + printf "BPC[0x%08lX] PC[0x%08lX] ACCL[0x%08lX] ACCH[0x%08lX]\n",$bpc,$pc,$accl,$acch + printf "EVB[0x%08lX]\n",$evb +end + +# Setup all +define setup + use_mon_code + set *(unsigned int)0xfffffffc=0x60 + shell sleep 0.1 + clock_init + shell sleep 0.1 + port_init + sdram_init + lanc_init + dispc_init + set $evb=0x08000000 +end + +# Load modules +define load_modules + use_debug_dma + load +end + +# Set kernel parameters +define set_kernel_parameters + set $param = (void*)0x08002000 + # INITRD_START + set *(unsigned long *)($param + 0x0010) = 0x082a0000 + # INITRD_SIZE + set *(unsigned long *)($param + 0x0014) = 0x00000000 + # M32R_CPUCLK + set *(unsigned long *)($param + 0x0018) = 0d360000000 + # M32R_BUSCLK + set *(unsigned long *)($param + 0x001c) = 0d90000000 + + # M32R_TIMER_DIVIDE + set *(unsigned long *)($param + 0x0020) = 0d128 + + set {char[0x200]}($param + 0x100) = "console=ttyD0,115200n8x root=/dev/nfsroot nfsroot=192.168.0.1:/project/m32r-linux/export/root.x nfsaddrs=192.168.0.101:192.168.0.1:192.168.0.1:255.255.255.0:mappi001 \0" +end + +# Boot +define boot + set_kernel_parameters + set $fp = 0 + set $pc=0x08001000 + si + c +end + +# Set breakpoints +define set_breakpoints + b *0x08000030 +end + +# Restart +define restart + sdireset + sdireset + setup + load_modules + boot +end + +sdireset +sdireset +file vmlinux +target m32rsdi +setup +#load_module +#set_breakpoints +#boot + diff --git a/arch/m32r/mappi/dot.gdbinit.nommu b/arch/m32r/mappi/dot.gdbinit.nommu new file mode 100644 index 000000000..1ca03f872 --- /dev/null +++ b/arch/m32r/mappi/dot.gdbinit.nommu @@ -0,0 +1,245 @@ +# .gdbinit file +# $Id$ +#----- +# NOTE: this file is generated by a script, "gen_gdbinit.pl". +# (Please type "gen_gdbinit.pl --help" and check the help message). +# $ Id: gen_gdbinit.pl,v 1.5 2004/01/23 08:23:25 takata Exp $ +#----- +# target platform: mappi + +# setting +set width 0d70 +set radix 0d16 +debug_chaos + +# clk xin:cpu:bif:bus=25:200:50:50 +define clock_init + set *(unsigned long *)0x00ef4024 = 2 + set *(unsigned long *)0x00ef4020 = 2 + set *(unsigned long *)0x00ef4010 = 0 + set *(unsigned long *)0x00ef4014 = 0 + set *(unsigned long *)0x00ef4004 = 3 + shell sleep 0.1 + set *(unsigned long *)0x00ef4008 = 0x00000200 +end + +# Initialize programmable ports +define port_init + set $sfrbase = 0x00ef0000 + set *(unsigned short *)0x00ef1060 = 0x5555 + set *(unsigned short *)0x00ef1062 = 0x5555 + set *(unsigned short *)0x00ef1064 = 0x5555 + set *(unsigned short *)0x00ef1066 = 0x5555 + set *(unsigned short *)0x00ef1068 = 0x5555 + set *(unsigned short *)0x00ef106a = 0x0000 + set *(unsigned short *)0x00ef106e = 0x5555 + set *(unsigned short *)0x00ef1070 = 0x5555 + # LED ON + set *(unsigned char *)($sfrbase + 0x1015) = 0xff + set *(unsigned char *)($sfrbase + 0x1085) = 0xff + shell sleep 0.1 + # LED OFF + set *(unsigned char *)($sfrbase + 0x1085) = 0x00 +end +document port_init + P5=LED(output), P6.b4=LAN_RESET(output) +end + +# Initialize SDRAM controller +define sdram_init + # SDIR0 + set *(unsigned long *)0x00ef6008 = 0x00000182 + # SDIR1 + set *(unsigned long *)0x00ef600c = 0x00000001 + # Initialize wait + shell sleep 0.1 + # Ch0-MOD + set *(unsigned long *)0x00ef602c = 0x00000020 + # Ch0-TR + set *(unsigned long *)0x00ef6028 = 0x00051502 + # Ch0-ADR (size:64MB) + set *(unsigned long *)0x00ef6020 = 0x00000004 + # AutoRef On + set *(unsigned long *)0x00ef6004 = 0x00010f05 + # Access enable + set *(unsigned long *)0x00ef6024 = 0x00000001 +end +document sdram_init + SDRAM controller initialization + 0x08000000 - 0x0bffffff (64MB) +end + +# Initialize LAN controller +define lanc_init + set $sfrbase = 0x00ef0000 + # Set BSEL3 (BSEL3 for the Chaos's bselc) + set *(unsigned long *)($sfrbase + 0x5300) = 0x07078040 + set *(unsigned long *)($sfrbase + 0x5304) = 0x01110102 + set *(unsigned long *)($sfrbase + 0x5308) = 0x00000001 + # Reset (P5=LED,P6.b4=LAN_RESET) + set *(unsigned short *)($sfrbase + 0x106c) = 0x0000 + set *(unsigned char *)($sfrbase + 0x1016) = 0xff + set *(unsigned char *)($sfrbase + 0x1086) = 0xff + shell sleep 0.1 + # swivel: 0=normal, 4=reverse +# set *(unsigned char *)($sfrbase + 0x1086) = 0x00 + set *(unsigned char *)($sfrbase + 0x1086) = 0x04 + set *(unsigned long *)(0x0c000330) = 0xffffffff + # Set mac address + set $lanc = (void*)0x0c000300 + set *(unsigned long *)($lanc + 0x0000) = 0x00610010 + set *(unsigned long *)($lanc + 0x0004) = 0x00200030 + set *(unsigned long *)($lanc + 0x0008) = 0x00400050 + set *(unsigned long *)($lanc + 0x000c) = 0x00600007 +end +document lanc_init + LAN controller initialization + ex.) MAC address: 10 20 30 40 50 60 +end + +# LCD & CRT dual-head setting (8bpp) +define dispc_init + set $sfrbase = 0x00ef0000 + # BSEL4 Dispc + set *(unsigned long *)($sfrbase + 0x5400) = 0x06078000 + set *(unsigned long *)($sfrbase + 0x5404) = 0x00101101 +end + +# MMU enable +define mmu_enable + set $evb=0x88000000 + set *(unsigned long *)0xffff0024=1 +end + +# MMU disable +define mmu_disable + set $evb=0 + set *(unsigned long *)0xffff0024=0 +end + +# Show TLB entries +define show_tlb_entries + set $i = 0 + set $addr = $arg0 + set $nr_entries = $arg1 + use_mon_code + while ($i < $nr_entries) + set $tlb_tag = *(unsigned long*)$addr + set $tlb_data = *(unsigned long*)($addr + 4) + printf " [%2d] 0x%08lx : 0x%08lx - 0x%08lx\n", $i, $addr, $tlb_tag, $tlb_data + set $i = $i + 1 + set $addr = $addr + 8 + end + use_debug_dma +end +define itlb + set $itlb=0xfe000000 + show_tlb_entries $itlb 0d32 +end +define dtlb + set $dtlb=0xfe000800 + show_tlb_entries $dtlb 0d32 +end + +# Show current task structure +define show_current + set $current = $spi & 0xffffe000 + printf "$current=0x%08lX\n",$current + print *(struct task_struct *)$current +end + +# Show user assigned task structure +define show_task + set = $arg0 & 0xffffe000 + printf "$task=0x%08lX\n",$task + print *(struct task_struct *)$task +end +document show_task + Show user assigned task structure + arg0 : task structure address +end + +# Show M32R registers +define show_regs + printf " R0[0x%08lX] R1[0x%08lX] R2[0x%08lX] R3[0x%08lX]\n",$r0,$r1,$r2,$r3 + printf " R4[0x%08lX] R5[0x%08lX] R6[0x%08lX] R7[0x%08lX]\n",$r4,$r5,$r6,$r7 + printf " R8[0x%08lX] R9[0x%08lX] R10[0x%08lX] R11[0x%08lX]\n",$r8,$r9,$r10,$r11 + printf "R12[0x%08lX] FP[0x%08lX] LR[0x%08lX] SP[0x%08lX]\n",$r12,$fp,$lr,$sp + printf "PSW[0x%08lX] CBR[0x%08lX] SPI[0x%08lX] SPU[0x%08lX]\n",$psw,$cbr,$spi,$spu + printf "BPC[0x%08lX] PC[0x%08lX] ACCL[0x%08lX] ACCH[0x%08lX]\n",$bpc,$pc,$accl,$acch + printf "EVB[0x%08lX]\n",$evb +end + +# Setup all +define setup + use_mon_code + set *(unsigned int)0xfffffffc=0x60 + shell sleep 0.1 + clock_init + shell sleep 0.1 + port_init + sdram_init + lanc_init + dispc_init + set $evb=0x00000000 +end + +# Load modules +define load_modules + use_debug_dma + load +end + +# Set kernel parameters +define set_kernel_parameters + set $param = (void*)0x00002000 + # INITRD_START + #set *(unsigned long *)($param + 0x0010) = 0x082a0000 + # INITRD_SIZE + #set *(unsigned long *)($param + 0x0014) = 0x00000000 + # M32R_CPUCLK + set *(unsigned long *)($param + 0x0018) = 0d200000000 + # M32R_BUSCLK + set *(unsigned long *)($param + 0x001c) = 0d50000000 + + # M32R_TIMER_DIVIDE + set *(unsigned long *)($param + 0x0020) = 0d128 + + set {char[0x200]}($param + 0x100) = "console=ttyD0,115200n8x root=/dev/nfsroot nfsroot=192.168.0.1:/project/m32r-linux/export/root.bbox-httpd nfsaddrs=192.168.0.101:192.168.0.1:192.168.0.1:255.255.255.0:mappi001 \0" +end + +# Boot +define boot + set_kernel_parameters + set $fp = 0 + set $pc=0x00001000 + set *(long *)0xfffffff4=0x8080 +# b load_flat_binary +# set *(unsigned char *)0x08001003=0x63 +# set *(unsigned char *)0x08001003=0x02 + si +# c +end + +# Set breakpoints +define set_breakpoints + b *0x08000030 +end + +# Restart +define restart + sdireset + sdireset + setup + load_modules + boot +end + +sdireset +sdireset +file vmlinux +target m32rsdi +setup +load_modules +boot + diff --git a/arch/m32r/mappi/dot.gdbinit.smp b/arch/m32r/mappi/dot.gdbinit.smp new file mode 100644 index 000000000..db0274fef --- /dev/null +++ b/arch/m32r/mappi/dot.gdbinit.smp @@ -0,0 +1,344 @@ +# .gdbinit file +# $Id$ + +# setting +set width 0d70 +set radix 0d16 +debug_chaos + +# clk xin:cpu:bif:bus=1:4:2:1 +define clock_init_on + set *(unsigned long *)0x00ef4024 = 2 + set *(unsigned long *)0x00ef4020 = 1 + set *(unsigned long *)0x00ef4010 = 0 + set *(unsigned long *)0x00ef4014 = 0 + set *(unsigned long *)0x00ef4004 = 0x1 + shell sleep 0.1 + set *(unsigned long *)0x00ef4008 = 0x0200 +# set *(unsigned long *)0x00ef4008 = 0x0201 +end + +# clk xin:cpu:bif:bus=1:4:1:1 +define clock_init_on_1411 + set *(unsigned long *)0x00ef4024 = 2 + set *(unsigned long *)0x00ef4020 = 2 + set *(unsigned long *)0x00ef4010 = 0 + set *(unsigned long *)0x00ef4014 = 0 + set *(unsigned long *)0x00ef4004 = 0x1 + shell sleep 0.1 + set *(unsigned long *)0x00ef4008 = 0x0200 +end + +# clk xin:cpu:bif:bus=1:4:2:1 +define clock_init_on_1421 + set *(unsigned long *)0x00ef4024 = 2 + set *(unsigned long *)0x00ef4020 = 1 + set *(unsigned long *)0x00ef4010 = 0 + set *(unsigned long *)0x00ef4014 = 0 + set *(unsigned long *)0x00ef4004 = 0x1 + shell sleep 0.1 + set *(unsigned long *)0x00ef4008 = 0x0200 +end + +# clk xin:cpu:bif:bus=1:8:2:1 +define clock_init_on_1821 + set *(unsigned long *)0x00ef4024 = 3 + set *(unsigned long *)0x00ef4020 = 2 + set *(unsigned long *)0x00ef4010 = 0 + set *(unsigned long *)0x00ef4014 = 0 + set *(unsigned long *)0x00ef4004 = 0x3 + shell sleep 0.1 + set *(unsigned long *)0x00ef4008 = 0x0200 +end + +# clk xin:cpu:bif:bus=1:8:4:1 +define clock_init_on_1841 + set *(unsigned long *)0x00ef4024 = 3 + set *(unsigned long *)0x00ef4020 = 1 + set *(unsigned long *)0x00ef4010 = 0 + set *(unsigned long *)0x00ef4014 = 0 + set *(unsigned long *)0x00ef4004 = 0x3 + shell sleep 0.1 + set *(unsigned long *)0x00ef4008 = 0x0200 +end + +# clk xin:cpu:bif:bus=1:16:8:1 +define clock_init_on_11681 + set *(unsigned long *)0x00ef4024 = 4 + set *(unsigned long *)0x00ef4020 = 2 + set *(unsigned long *)0x00ef4010 = 0 + set *(unsigned long *)0x00ef4014 = 0 + set *(unsigned long *)0x00ef4004 = 0x7 + shell sleep 0.1 + set *(unsigned long *)0x00ef4008 = 0x0200 +end + +# clk xin:cpu:bif:bus=1:1:1:1 +define clock_init_off + # CPU + set *(unsigned long *)0x00ef4010 = 0 + set *(unsigned long *)0x00ef4014 = 0 + # BIF + set *(unsigned long *)0x00ef4020 = 0 + # BUS + set *(unsigned long *)0x00ef4024 = 0 + # PLL + set *(unsigned long *)0x00ef4008 = 0x0000 +end + +# Initialize programmable ports +define port_init + set $sfrbase = 0x00ef0000 + set *(unsigned short *)0x00ef1060 = 0x5555 + set *(unsigned short *)0x00ef1062 = 0x5555 + set *(unsigned short *)0x00ef1064 = 0x5555 + set *(unsigned short *)0x00ef1066 = 0x5555 + set *(unsigned short *)0x00ef1068 = 0x5555 + set *(unsigned short *)0x00ef106a = 0x0000 + set *(unsigned short *)0x00ef106e = 0x5555 + set *(unsigned short *)0x00ef1070 = 0x5555 + # LED ON + set *(unsigned char *)($sfrbase + 0x1015) = 0xff + set *(unsigned char *)($sfrbase + 0x1085) = 0xff + shell sleep 0.1 + # LED OFF + set *(unsigned char *)($sfrbase + 0x1085) = 0x00 +end +document port_init + P5=LED(output), P6.b4=LAN_RESET(output) +end + +# Initialize SDRAM controller for Mappi +define sdram_init + # SDIR0 + set *(unsigned long *)0x00ef6008 = 0x00000182 + # SDIR1 + set *(unsigned long *)0x00ef600c = 0x00000001 + # Initialize wait + shell sleep 0.1 + # Ch0-MOD + set *(unsigned long *)0x00ef602c = 0x00000020 + # Ch0-TR + set *(unsigned long *)0x00ef6028 = 0x00010002 + # Ch0-ADR + set *(unsigned long *)0x00ef6020 = 0x08000004 + # AutoRef On + set *(unsigned long *)0x00ef6004 = 0x00010107 + # Access enable + set *(unsigned long *)0x00ef6024 = 0x00000001 +end +document sdram_init + Mappi SDRAM controller initialization + 0x08000000 - 0x0bffffff (64MB) +end + +# Initialize LAN controller for Mappi +define lanc_init + set $sfrbase = 0x00ef0000 + # Set BSEL3 (BSEL3 for the Chaos's bselc) +# set *(unsigned long *)($sfrbase + 0x5300) = 0x01018040 +# set *(unsigned long *)($sfrbase + 0x5304) = 0x01011101 + set *(unsigned long *)($sfrbase + 0x5300) = 0x04048000 + set *(unsigned long *)($sfrbase + 0x5304) = 0x01011103 + set *(unsigned long *)($sfrbase + 0x5308) = 0x00000001 + # Reset (P5=LED,P6.b4=LAN_RESET) + set *(unsigned short *)($sfrbase + 0x106c) = 0x0000 + set *(unsigned char *)($sfrbase + 0x1016) = 0xff + set *(unsigned char *)($sfrbase + 0x1086) = 0xff + shell sleep 0.1 +# set *(unsigned char *)($sfrbase + 0x1086) = 0x00 + set *(unsigned char *)($sfrbase + 0x1086) = 0x04 + set *(unsigned long *)(0x0c000330) = 0xffffffff + # Set mac address + set $lanc = (void*)0x0c000300 + set *(unsigned long *)($lanc + 0x0000) = 0x00610010 + set *(unsigned long *)($lanc + 0x0004) = 0x00200030 + set *(unsigned long *)($lanc + 0x0008) = 0x00400050 + set *(unsigned long *)($lanc + 0x000c) = 0x00600007 +end +document lanc_init + Mappi LAN controller initialization + ex.) MAC address: 10 20 30 40 50 60 +end + +# LCD & CRT dual-head setting (8bpp) +define dispc_init + set $sfrbase = 0x00ef0000 + # BSEL4 Dispc + # 20MHz +# set *(unsigned long *)($sfrbase + 0x5400) = 0x02028282 +# set *(unsigned long *)($sfrbase + 0x5404) = 0x00122202 + # 40MHz + set *(unsigned long *)($sfrbase + 0x5400) = 0x04048000 + set *(unsigned long *)($sfrbase + 0x5404) = 0x00101103 +end + +# MMU enable +define mmu_enable + set $evb=0x88000000 + set *(unsigned long *)0xffff0024=1 +end + +# MMU disable +define mmu_disable + set $evb=0 + set *(unsigned long *)0xffff0024=0 +end + +# Show TLB entries +define show_tlb_entries + set $i = 0 + set $addr = $arg0 + use_mon_code + while ($i < 0d32 ) + set $tlb_tag = *(unsigned long*)$addr + set $tlb_data = *(unsigned long*)($addr + 4) + printf " [%2d] 0x%08lx : 0x%08lx - 0x%08lx\n", $i, $addr, $tlb_tag, $tlb_data + set $i = $i + 1 + set $addr = $addr + 8 + end + use_debug_dma +end +define itlb + set $itlb=0xfe000000 + show_tlb_entries $itlb +end +define dtlb + set $dtlb=0xfe000800 + show_tlb_entries $dtlb +end + + +# Show current task structure +define show_current + set $current = $spi & 0xffffe000 + printf "$current=0x%08lX\n",$current + print *(struct task_struct *)$current +end + +# Show user assigned task structure +define show_task + set $task = $arg0 & 0xffffe000 + printf "$task=0x%08lX\n",$task + print *(struct task_struct *)$task +end +document show_task + Show user assigned task structure + arg0 : task structure address +end + +# Show M32R registers +define show_regs + printf " R0[0x%08lX] R1[0x%08lX] R2[0x%08lX] R3[0x%08lX]\n",$r0,$r1,$r2,$r3 + printf " R4[0x%08lX] R5[0x%08lX] R6[0x%08lX] R7[0x%08lX]\n",$r4,$r5,$r6,$r7 + printf " R8[0x%08lX] R9[0x%08lX] R10[0x%08lX] R11[0x%08lX]\n",$r8,$r9,$r10,$r11 + printf "R12[0x%08lX] FP[0x%08lX] LR[0x%08lX] SP[0x%08lX]\n",$r12,$fp,$lr,$fp + printf "PSW[0x%08lX] CBR[0x%08lX] SPI[0x%08lX] SPU[0x%08lX]\n",$psw,$cbr,$spi,$spu + printf "BPC[0x%08lX] PC[0x%08lX] ACCL[0x%08lX] ACCH[0x%08lX]\n",$bpc,$pc,$accl,$acch + printf "EVB[0x%08lX]\n",$evb +end + + +# Setup all +define setup + use_mon_code + set *(unsigned int)0xfffffffc=0x60 + shell sleep 0.1 +# clock_init_on_1411 + clock_init_on_1421 +# clock_init_on_1821 +# clock_init_on_1841 +# clock_init_on_11681 +# clock_init_off + shell sleep 0.1 + port_init + sdram_init + lanc_init + dispc_init + set $evb=0x08000000 +end + +# Load modules +define load_modules + use_debug_dma + load +# load ramdisk_082a0000.mot +# load romfs_082a0000.mot +# use_mon_code +end + +# Set kernel parameters +define set_kernel_parameters + set $param = (void*)0x08002000 + # INITRD_START +# set *(unsigned long *)($param + 0x0010) = 0x082a0000 + # INITRD_SIZE + set *(unsigned long *)($param + 0x0014) = 0x00000000 + # M32R_CPUCLK + set *(unsigned long *)($param + 0x0018) = 0d160000000 +# set *(unsigned long *)($param + 0x0018) = 0d80000000 +# set *(unsigned long *)($param + 0x0018) = 0d40000000 + # M32R_BUSCLK + set *(unsigned long *)($param + 0x001c) = 0d40000000 + + # M32R_TIMER_DIVIDE + set *(unsigned long *)($param + 0x0020) = 0d128 + + set {char[0x200]}($param + 0x100) = "console=tty1 console=ttyD0,115200n8x root=/dev/nfsroot nfsroot=192.168.0.1:/project/m32r-linux/export/root.x nfsaddrs=192.168.0.101:192.168.0.1:192.168.0.1:255.255.255.0:mappi001 \0" +# set {char[0x200]}($param + 0x100) = "console=tty1 root=/dev/nfsroot nfsroot=192.168.0.1:/project/m32r-linux/export/root.x nfsaddrs=192.168.0.101:192.168.0.1:192.168.0.1:255.255.255.0:mappi001 \0" +end + +# Boot +define boot + set_kernel_parameters + set $pc=0x08001000 + set *(unsigned char *)0x08001003=0x03 + si + c +end + +# Set breakpoints +define set_breakpoints + b *0x08000030 +end + +## Boot MP +define boot_mp + set_kernel_parameters + set *(unsigned long *)0x00f00000 = boot - 0x80000000 + set *(unsigned long *)0x00eff2f8 = 0x2 + x 0x00eff2f8 + + set $pc=0x08001000 + si + c +end +document boot_mp + Boot BSP +end + +## Boot UP +define boot_up + set_kernel_parameters + set $pc=0x08001000 + si + c +end +document boot_up + Boot BSP +end + +# Restart +define restart + sdireset + sdireset + setup + load_modules + boot_mp +end + +sdireset +sdireset +file vmlinux +target m32rsdi +setup diff --git a/arch/m32r/mm/Makefile b/arch/m32r/mm/Makefile new file mode 100644 index 000000000..c51c1c3b4 --- /dev/null +++ b/arch/m32r/mm/Makefile @@ -0,0 +1,12 @@ +# +# Makefile for the Linux M32R-specific parts of the memory manager. +# + +ifdef CONFIG_MMU +obj-y := init.o fault.o mmu.o extable.o ioremap.o cache.o page.o +else +obj-y := init.o fault-nommu.o mmu.o extable.o ioremap-nommu.o cache.o page.o +endif + +obj-$(CONFIG_DISCONTIGMEM) += discontig.o + diff --git a/arch/m32r/mm/cache.c b/arch/m32r/mm/cache.c new file mode 100644 index 000000000..e2dd92d69 --- /dev/null +++ b/arch/m32r/mm/cache.c @@ -0,0 +1,68 @@ +/* + * linux/arch/m32r/mm/cache.c + * + * Copyright (C) 2002 Hirokazu Takata + */ + +/* $Id$ */ + +#include +#include + +#undef MCCR + +#if defined(CONFIG_CHIP_XNUX2) || defined(CONFIG_CHIP_M32700) || defined(CONFIG_CHIP_VDEC2) || defined(CONFIG_CHIP_OPSP) +/* Cache Control Register */ +#define MCCR ((volatile unsigned long*)0xfffffffc) +#define MCCR_CC (1UL << 7) /* Cache mode modify bit */ +#define MCCR_IIV (1UL << 6) /* I-cache invalidate */ +#define MCCR_DIV (1UL << 5) /* D-cache invalidate */ +#define MCCR_DCB (1UL << 4) /* D-cache copy back */ +#define MCCR_ICM (1UL << 1) /* I-cache mode [0:off,1:on] */ +#define MCCR_DCM (1UL << 0) /* D-cache mode [0:off,1:on] */ +#define MCCR_ICACHE_INV (MCCR_CC|MCCR_IIV) +#define MCCR_DCACHE_CB (MCCR_CC|MCCR_DCB) +#define MCCR_DCACHE_CBINV (MCCR_CC|MCCR_DIV|MCCR_DCB) +#define CHECK_MCCR(mccr) (mccr = *MCCR) +#elif defined(CONFIG_CHIP_M32102) +#define MCCR ((volatile unsigned long*)0xfffffffc) +#define MCCR_IIV (1UL << 8) /* I-cache invalidate */ +#define MCCR_ICACHE_INV MCCR_IIV +#endif /* CONFIG_CHIP_XNUX2 || CONFIG_CHIP_M32700 */ + +#ifndef MCCR +#error Unknown cache type. +#endif + + +/* Copy back and invalidate D-cache and invalidate I-cache all */ +void _flush_cache_all(void) +{ +#if defined(CONFIG_CHIP_M32102) + *MCCR = MCCR_ICACHE_INV; +#else + unsigned long mccr; + + /* Copyback and invalidate D-cache */ + /* Invalidate I-cache */ + *MCCR = MCCR_ICACHE_INV | MCCR_DCACHE_CBINV; + while ((mccr = *MCCR) & MCCR_IIV); /* loop while invalidating... */ +#endif +} + +/* Copy back D-cache and invalidate I-cache all */ +void _flush_cache_copyback_all(void) +{ +#if defined(CONFIG_CHIP_M32102) + *MCCR = MCCR_ICACHE_INV; +#else + unsigned long mccr; + + /* Copyback D-cache */ + /* Invalidate I-cache */ + *MCCR = MCCR_ICACHE_INV | MCCR_DCACHE_CB; + while ((mccr = *MCCR) & MCCR_IIV); /* loop while invalidating... */ + +#endif +} + diff --git a/arch/m32r/mm/discontig.c b/arch/m32r/mm/discontig.c new file mode 100644 index 000000000..3e49ff64b --- /dev/null +++ b/arch/m32r/mm/discontig.c @@ -0,0 +1,170 @@ +/* + * linux/arch/m32r/mm/discontig.c + * + * Discontig memory support + * + * Copyright (c) 2003 Hitoshi Yamamoto + */ + +#include +#include +#include +#include +#include + +#include + +extern char _end[]; + +struct pglist_data *node_data[MAX_NUMNODES]; +static bootmem_data_t node_bdata[MAX_NUMNODES] __initdata; + +pg_data_t m32r_node_data[MAX_NUMNODES]; + +/* Memory profile */ +typedef struct { + unsigned long start_pfn; + unsigned long pages; + unsigned long holes; + unsigned long free_pfn; +} mem_prof_t; +static mem_prof_t mem_prof[MAX_NUMNODES]; + +static void __init mem_prof_init(void) +{ + unsigned long start_pfn, holes, free_pfn; + const unsigned long zone_alignment = 1UL << (MAX_ORDER - 1); + unsigned long ul; + mem_prof_t *mp; + + /* Node#0 SDRAM */ + mp = &mem_prof[0]; + mp->start_pfn = PFN_UP(CONFIG_MEMORY_START); + mp->pages = PFN_DOWN(CONFIG_MEMORY_SIZE); + mp->holes = 0; + mp->free_pfn = PFN_UP(__pa(_end)); + + /* Node#1 internal SRAM */ + mp = &mem_prof[1]; + start_pfn = free_pfn = PFN_UP(CONFIG_IRAM_START); + holes = 0; + if (start_pfn & (zone_alignment - 1)) { + ul = zone_alignment; + while (start_pfn >= ul) + ul += zone_alignment; + + start_pfn = ul - zone_alignment; + holes = free_pfn - start_pfn; + } + + mp->start_pfn = start_pfn; + mp->pages = PFN_DOWN(CONFIG_IRAM_SIZE) + holes; + mp->holes = holes; + mp->free_pfn = PFN_UP(CONFIG_IRAM_START); +} + +unsigned long __init setup_memory(void) +{ + unsigned long bootmap_size; + unsigned long min_pfn; + int nid; + mem_prof_t *mp; + + max_low_pfn = 0; + min_low_pfn = -1; + + mem_prof_init(); + + for (nid = 0 ; nid < numnodes ; nid++) { + mp = &mem_prof[nid]; + NODE_DATA(nid)=(pg_data_t *)&m32r_node_data[nid]; + NODE_DATA(nid)->bdata = &node_bdata[nid]; + min_pfn = mp->start_pfn; + max_pfn = mp->start_pfn + mp->pages; + bootmap_size = init_bootmem_node(NODE_DATA(nid), mp->free_pfn, + mp->start_pfn, max_pfn); + + free_bootmem_node(NODE_DATA(nid), PFN_PHYS(mp->start_pfn), + PFN_PHYS(mp->pages)); + + reserve_bootmem_node(NODE_DATA(nid), PFN_PHYS(mp->start_pfn), + PFN_PHYS(mp->free_pfn - mp->start_pfn) + bootmap_size); + + if (max_low_pfn < max_pfn) + max_low_pfn = max_pfn; + + if (min_low_pfn > min_pfn) + min_low_pfn = min_pfn; + } + +#ifdef CONFIG_BLK_DEV_INITRD + if (LOADER_TYPE && INITRD_START) { + if (INITRD_START + INITRD_SIZE <= PFN_PHYS(max_low_pfn)) { + reserve_bootmem_node(NODE_DATA(0), INITRD_START, + INITRD_SIZE); + initrd_start = INITRD_START ? + INITRD_START + PAGE_OFFSET : 0; + + initrd_end = initrd_start + INITRD_SIZE; + printk("initrd:start[%08lx],size[%08lx]\n", + initrd_start, INITRD_SIZE); + } else { + printk("initrd extends beyond end of memory " + "(0x%08lx > 0x%08lx)\ndisabling initrd\n", + INITRD_START + INITRD_SIZE, + PFN_PHYS(max_low_pfn)); + + initrd_start = 0; + } + } +#endif /* CONFIG_BLK_DEV_INITRD */ + + return max_low_pfn; +} + +#define START_PFN(nid) \ + (NODE_DATA(nid)->bdata->node_boot_start >> PAGE_SHIFT) +#define MAX_LOW_PFN(nid) (NODE_DATA(nid)->bdata->node_low_pfn) + +unsigned long __init zone_sizes_init(void) +{ + unsigned long zones_size[MAX_NR_ZONES], zholes_size[MAX_NR_ZONES]; + unsigned long low, start_pfn; + unsigned long holes = 0; + int nid, i; + mem_prof_t *mp; + + pgdat_list = NULL; + for (nid = numnodes - 1 ; nid >= 0 ; nid--) { + NODE_DATA(nid)->pgdat_next = pgdat_list; + pgdat_list = NODE_DATA(nid); + } + + for (nid = 0 ; nid < numnodes ; nid++) { + mp = &mem_prof[nid]; + for (i = 0 ; i < MAX_NR_ZONES ; i++) { + zones_size[i] = 0; + zholes_size[i] = 0; + } + start_pfn = START_PFN(nid); + low = MAX_LOW_PFN(nid); + zones_size[ZONE_DMA] = low - start_pfn; + zholes_size[ZONE_DMA] = mp->holes; + holes += zholes_size[ZONE_DMA]; + + free_area_init_node(nid, NODE_DATA(nid), zones_size, + start_pfn, zholes_size); + } + + /* + * For test + * Use all area of internal RAM. + * see __alloc_pages() + */ + NODE_DATA(1)->node_zones->pages_min = 0; + NODE_DATA(1)->node_zones->pages_low = 0; + NODE_DATA(1)->node_zones->pages_high = 0; + + return holes; +} + diff --git a/arch/m32r/mm/extable.c b/arch/m32r/mm/extable.c new file mode 100644 index 000000000..9a97363b6 --- /dev/null +++ b/arch/m32r/mm/extable.c @@ -0,0 +1,22 @@ +/* + * linux/arch/i386/mm/extable.c + */ + +#include +#include +#include +#include + +int fixup_exception(struct pt_regs *regs) +{ + const struct exception_table_entry *fixup; + + fixup = search_exception_tables(regs->bpc); + if (fixup) { + regs->bpc = fixup->fixup; + return 1; + } + + return 0; +} + diff --git a/arch/m32r/mm/fault-nommu.c b/arch/m32r/mm/fault-nommu.c new file mode 100644 index 000000000..da267d843 --- /dev/null +++ b/arch/m32r/mm/fault-nommu.c @@ -0,0 +1,164 @@ +/* + * linux/arch/m32r/mm/fault.c + * + * Copyright (c) 2001, 2002 Hitoshi Yamamoto, and H. Kondo + * + * Some code taken from i386 version. + * Copyright (C) 1995 Linus Torvalds + */ + +/* $Id: fault-nommu.c,v 1.1 2004/03/30 06:40:59 sakugawa Exp $ */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +extern void die(const char *, struct pt_regs *, long); + +#ifndef CONFIG_SMP +asmlinkage unsigned int tlb_entry_i_dat; +asmlinkage unsigned int tlb_entry_d_dat; +#define tlb_entry_i tlb_entry_i_dat +#define tlb_entry_d tlb_entry_d_dat +#else +unsigned int tlb_entry_i_dat[NR_CPUS]; +unsigned int tlb_entry_d_dat[NR_CPUS]; +#define tlb_entry_i tlb_entry_i_dat[smp_processor_id()] +#define tlb_entry_d tlb_entry_d_dat[smp_processor_id()] +#endif + +/* + * Unlock any spinlocks which will prevent us from getting the + * message out + */ +void bust_spinlocks(int yes) +{ + int loglevel_save = console_loglevel; + + if (yes) { + oops_in_progress = 1; + return; + } +#ifdef CONFIG_VT + unblank_screen(); +#endif + oops_in_progress = 0; + /* + * OK, the message is on the console. Now we call printk() + * without oops_in_progress set so that printk will give klogd + * a poke. Hold onto your hats... + */ + console_loglevel = 15; /* NMI oopser may have shut the console up */ + printk(" "); + console_loglevel = loglevel_save; +} + +void do_BUG(const char *file, int line) +{ + bust_spinlocks(1); + printk("kernel BUG at %s:%d!\n", file, line); +} + +/*======================================================================* + * do_page_fault() + *======================================================================* + * This routine handles page faults. It determines the address, + * and the problem, and then passes it off to one of the appropriate + * routines. + * + * ARGUMENT: + * regs : M32R SP reg. + * error_code : See below + * address : M32R MMU MDEVA reg. (Operand ACE) + * : M32R BPC reg. (Instruction ACE) + * + * error_code : + * bit 0 == 0 means no page found, 1 means protection fault + * bit 1 == 0 means read, 1 means write + * bit 2 == 0 means kernel, 1 means user-mode + *======================================================================*/ +asmlinkage void do_page_fault(struct pt_regs *regs, unsigned long error_code, + unsigned long address) +{ + +/* + * Oops. The kernel tried to access some bad page. We'll have to + * terminate things with extreme prejudice. + */ + + bust_spinlocks(1); + + if (address < PAGE_SIZE) + printk(KERN_ALERT "Unable to handle kernel NULL pointer dereference"); + else + printk(KERN_ALERT "Unable to handle kernel paging request"); + printk(" at virtual address %08lx\n",address); + printk(" printing bpc:\n"); + printk(KERN_ALERT "bpc = %08lx\n", regs->bpc); + + die("Oops", regs, error_code); + bust_spinlocks(0); + do_exit(SIGKILL); +} + +/*======================================================================* + * update_mmu_cache() + *======================================================================*/ +void update_mmu_cache(struct vm_area_struct *vma, unsigned long addr, + pte_t pte) +{ + BUG(); +} + +/*======================================================================* + * flush_tlb_page() : flushes one page + *======================================================================*/ +void local_flush_tlb_page(struct vm_area_struct *vma, unsigned long page) +{ + BUG(); +} + +/*======================================================================* + * flush_tlb_range() : flushes a range of pages + *======================================================================*/ +void local_flush_tlb_range(struct vm_area_struct *vma, unsigned long start, + unsigned long end) +{ + BUG(); +} + +/*======================================================================* + * flush_tlb_mm() : flushes the specified mm context TLB's + *======================================================================*/ +void local_flush_tlb_mm(struct mm_struct *mm) +{ + BUG(); +} + +/*======================================================================* + * flush_tlb_all() : flushes all processes TLBs + *======================================================================*/ +void local_flush_tlb_all(void) +{ + BUG(); +} + diff --git a/arch/m32r/mm/fault.c b/arch/m32r/mm/fault.c new file mode 100644 index 000000000..393ff03ae --- /dev/null +++ b/arch/m32r/mm/fault.c @@ -0,0 +1,572 @@ +/* + * linux/arch/m32r/mm/fault.c + * + * Copyright (c) 2001, 2002 Hitoshi Yamamoto, and H. Kondo + * + * Some code taken from i386 version. + * Copyright (C) 1995 Linus Torvalds + */ + +/* $Id$ */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include /* For unblank_screen() */ +#include +#include + +#include +#include +#include +#include +#include +#include + +extern void die(const char *, struct pt_regs *, long); + +#ifndef CONFIG_SMP +asmlinkage unsigned int tlb_entry_i_dat; +asmlinkage unsigned int tlb_entry_d_dat; +#define tlb_entry_i tlb_entry_i_dat +#define tlb_entry_d tlb_entry_d_dat +#else +unsigned int tlb_entry_i_dat[NR_CPUS]; +unsigned int tlb_entry_d_dat[NR_CPUS]; +#define tlb_entry_i tlb_entry_i_dat[smp_processor_id()] +#define tlb_entry_d tlb_entry_d_dat[smp_processor_id()] +#endif + +extern void init_tlb(void); + +/* + * Unlock any spinlocks which will prevent us from getting the + * message out + */ +void bust_spinlocks(int yes) +{ + int loglevel_save = console_loglevel; + + if (yes) { + oops_in_progress = 1; + return; + } +#ifdef CONFIG_VT + unblank_screen(); +#endif + oops_in_progress = 0; + /* + * OK, the message is on the console. Now we call printk() + * without oops_in_progress set so that printk will give klogd + * a poke. Hold onto your hats... + */ + console_loglevel = 15; /* NMI oopser may have shut the console up */ + printk(" "); + console_loglevel = loglevel_save; +} + +/*======================================================================* + * do_page_fault() + *======================================================================* + * This routine handles page faults. It determines the address, + * and the problem, and then passes it off to one of the appropriate + * routines. + * + * ARGUMENT: + * regs : M32R SP reg. + * error_code : See below + * address : M32R MMU MDEVA reg. (Operand ACE) + * : M32R BPC reg. (Instruction ACE) + * + * error_code : + * bit 0 == 0 means no page found, 1 means protection fault + * bit 1 == 0 means read, 1 means write + * bit 2 == 0 means kernel, 1 means user-mode + * bit 3 == 0 means data, 1 means instruction + *======================================================================*/ +asmlinkage void do_page_fault(struct pt_regs *regs, unsigned long error_code, + unsigned long address) +{ + struct task_struct *tsk; + struct mm_struct *mm; + struct vm_area_struct * vma; + unsigned long page, addr; + int write; + siginfo_t info; + + /* + * If BPSW IE bit enable --> set PSW IE bit + */ + if (regs->psw & M32R_PSW_BIE) + local_irq_enable(); + + tsk = current; + + info.si_code = SEGV_MAPERR; + + /* + * We fault-in kernel-space virtual memory on-demand. The + * 'reference' page table is init_mm.pgd. + * + * NOTE! We MUST NOT take any locks for this case. We may + * be in an interrupt or a critical region, and should + * only copy the information from the master page table, + * nothing more. + * + * This verifies that the fault happens in kernel space + * (error_code & 4) == 0, and that the fault was not a + * protection error (error_code & 1) == 0. + */ + if (address >= TASK_SIZE && !(error_code & 4)) + goto vmalloc_fault; + + mm = tsk->mm; + + /* + * If we're in an interrupt or have no user context or are running in an + * atomic region then we must not take the fault.. + */ + if (in_atomic() || !mm) + goto bad_area_nosemaphore; + + /* When running in the kernel we expect faults to occur only to + * addresses in user space. All other faults represent errors in the + * kernel and should generate an OOPS. Unfortunatly, in the case of an + * erroneous fault occuring in a code path which already holds mmap_sem + * we will deadlock attempting to validate the fault against the + * address space. Luckily the kernel only validly references user + * space from well defined areas of code, which are listed in the + * exceptions table. + * + * As the vast majority of faults will be valid we will only perform + * the source reference check when there is a possibilty of a deadlock. + * Attempt to lock the address space, if we cannot we then validate the + * source. If this is invalid we can skip the address space check, + * thus avoiding the deadlock. + */ + if (!down_read_trylock(&mm->mmap_sem)) { + if ((error_code & 4) == 0 && + !search_exception_tables(regs->psw)) + goto bad_area_nosemaphore; + down_read(&mm->mmap_sem); + } + + vma = find_vma(mm, address); + if (!vma) + goto bad_area; + if (vma->vm_start <= address) + goto good_area; + if (!(vma->vm_flags & VM_GROWSDOWN)) + goto bad_area; +#if 0 + if (error_code & 4) { + /* + * accessing the stack below "spu" is always a bug. + * The "+ 4" is there due to the push instruction + * doing pre-decrement on the stack and that + * doesn't show up until later.. + */ + if (address + 4 < regs->spu) + goto bad_area; + } +#endif + if (expand_stack(vma, address)) + goto bad_area; +/* + * Ok, we have a good vm_area for this memory access, so + * we can handle it.. + */ +good_area: + info.si_code = SEGV_ACCERR; + write = 0; + switch (error_code & 3) { + default: /* 3: write, present */ + /* fall through */ + case 2: /* write, not present */ + if (!(vma->vm_flags & VM_WRITE)) + goto bad_area; + write++; + break; + case 1: /* read, present */ + case 0: /* read, not present */ + if (!(vma->vm_flags & (VM_READ | VM_EXEC))) + goto bad_area; + } + +survive: + /* + * If for any reason at all we couldn't handle the fault, + * make sure we exit gracefully rather than endlessly redo + * the fault. + */ + addr = (address & PAGE_MASK) | (error_code & 8); + switch (handle_mm_fault(mm, vma, addr, write)) { + case VM_FAULT_MINOR: + tsk->min_flt++; + break; + case VM_FAULT_MAJOR: + tsk->maj_flt++; + break; + case VM_FAULT_SIGBUS: + goto do_sigbus; + case VM_FAULT_OOM: + goto out_of_memory; + default: + BUG(); + } + + up_read(&mm->mmap_sem); + return; + +/* + * Something tried to access memory that isn't in our memory map.. + * Fix it, but check if it's kernel or user first.. + */ +bad_area: + up_read(&mm->mmap_sem); + +bad_area_nosemaphore: + /* User mode accesses just cause a SIGSEGV */ + if (error_code & 4) { + tsk->thread.address = address; + tsk->thread.error_code = error_code | (address >= TASK_SIZE); + tsk->thread.trap_no = 14; + info.si_signo = SIGSEGV; + info.si_errno = 0; + /* info.si_code has been set above */ + info.si_addr = (void __user *)address; + force_sig_info(SIGSEGV, &info, tsk); + return; + } + +no_context: + /* Are we prepared to handle this kernel fault? */ + if (fixup_exception(regs)) + return; + +/* + * Oops. The kernel tried to access some bad page. We'll have to + * terminate things with extreme prejudice. + */ + + bust_spinlocks(1); + + if (address < PAGE_SIZE) + printk(KERN_ALERT "Unable to handle kernel NULL pointer dereference"); + else + printk(KERN_ALERT "Unable to handle kernel paging request"); + printk(" at virtual address %08lx\n",address); + printk(KERN_ALERT " printing bpc:\n"); + printk("%08lx\n", regs->bpc); + page = *(unsigned long *)MPTB; + page = ((unsigned long *) page)[address >> PGDIR_SHIFT]; + printk(KERN_ALERT "*pde = %08lx\n", page); + if (page & _PAGE_PRESENT) { + page &= PAGE_MASK; + address &= 0x003ff000; + page = ((unsigned long *) __va(page))[address >> PAGE_SHIFT]; + printk(KERN_ALERT "*pte = %08lx\n", page); + } + die("Oops", regs, error_code); + bust_spinlocks(0); + do_exit(SIGKILL); + +/* + * We ran out of memory, or some other thing happened to us that made + * us unable to handle the page fault gracefully. + */ +out_of_memory: + up_read(&mm->mmap_sem); + if (tsk->pid == 1) { + yield(); + down_read(&mm->mmap_sem); + goto survive; + } + printk("VM: killing process %s\n", tsk->comm); + if (error_code & 4) + do_exit(SIGKILL); + goto no_context; + +do_sigbus: + up_read(&mm->mmap_sem); + + /* Kernel mode? Handle exception or die */ + if (!(error_code & 4)) + goto no_context; + + tsk->thread.address = address; + tsk->thread.error_code = error_code; + tsk->thread.trap_no = 14; + info.si_signo = SIGBUS; + info.si_errno = 0; + info.si_code = BUS_ADRERR; + info.si_addr = (void __user *)address; + force_sig_info(SIGBUS, &info, tsk); + return; + +vmalloc_fault: + { + /* + * Synchronize this task's top level page-table + * with the 'reference' page table. + * + * Do _not_ use "tsk" here. We might be inside + * an interrupt in the middle of a task switch.. + */ + int offset = pgd_index(address); + pgd_t *pgd, *pgd_k; + pmd_t *pmd, *pmd_k; + pte_t *pte_k; + + pgd = (pgd_t *)*(unsigned long *)MPTB; + pgd = offset + (pgd_t *)pgd; + pgd_k = init_mm.pgd + offset; + + if (!pgd_present(*pgd_k)) + goto no_context; + + /* + * set_pgd(pgd, *pgd_k); here would be useless on PAE + * and redundant with the set_pmd() on non-PAE. + */ + + pmd = pmd_offset(pgd, address); + pmd_k = pmd_offset(pgd_k, address); + if (!pmd_present(*pmd_k)) + goto no_context; + set_pmd(pmd, *pmd_k); + + pte_k = pte_offset_kernel(pmd_k, address); + if (!pte_present(*pte_k)) + goto no_context; + + addr = (address & PAGE_MASK) | (error_code & 8); + update_mmu_cache(NULL, addr, *pte_k); + return; + } +} + +/*======================================================================* + * update_mmu_cache() + *======================================================================*/ +#define TLB_MASK (NR_TLB_ENTRIES - 1) +#define ITLB_END (unsigned long *)(ITLB_BASE + (NR_TLB_ENTRIES * 8)) +#define DTLB_END (unsigned long *)(DTLB_BASE + (NR_TLB_ENTRIES * 8)) +void update_mmu_cache(struct vm_area_struct *vma, unsigned long vaddr, + pte_t pte) +{ + unsigned long *entry1, *entry2; + unsigned long pte_data, flags; + unsigned int *entry_dat; + int inst = vaddr & 8; + int i; + + /* Ptrace may call this routine. */ + if (vma && current->active_mm != vma->vm_mm) + return; + + local_irq_save(flags); + + vaddr = (vaddr & PAGE_MASK) | get_asid(); + +#ifdef CONFIG_CHIP_OPSP + entry1 = (unsigned long *)ITLB_BASE; + for(i = 0 ; i < NR_TLB_ENTRIES; i++) { + if(*entry1++ == vaddr) { + pte_data = pte_val(pte); + set_tlb_data(entry1, pte_data); + break; + } + entry1++; + } + entry2 = (unsigned long *)DTLB_BASE; + for(i = 0 ; i < NR_TLB_ENTRIES ; i++) { + if(*entry2++ == vaddr) { + pte_data = pte_val(pte); + set_tlb_data(entry2, pte_data); + break; + } + entry2++; + } + local_irq_restore(flags); + return; +#else + pte_data = pte_val(pte); + + /* + * Update TLB entries + * entry1: ITLB entry address + * entry2: DTLB entry address + */ + __asm__ __volatile__ ( + "seth %0, #high(%4) \n\t" + "st %2, @(%5, %0) \n\t" + "ldi %1, #1 \n\t" + "st %1, @(%6, %0) \n\t" + "add3 r4, %0, %7 \n\t" + ".fillinsn \n" + "1: \n\t" + "ld %1, @(%6, %0) \n\t" + "bnez %1, 1b \n\t" + "ld %0, @r4+ \n\t" + "ld %1, @r4 \n\t" + "st %3, @+%0 \n\t" + "st %3, @+%1 \n\t" + : "=&r" (entry1), "=&r" (entry2) + : "r" (vaddr), "r" (pte_data), "i" (MMU_REG_BASE), + "i" (MSVA_offset), "i" (MTOP_offset), "i" (MIDXI_offset) + : "r4", "memory" + ); + + if ((!inst && entry2 >= DTLB_END) || (inst && entry1 >= ITLB_END)) + goto notfound; + +found: + local_irq_restore(flags); + + return; + + /* Valid entry not found */ +notfound: + /* + * Update ITLB or DTLB entry + * entry1: TLB entry address + * entry2: TLB base address + */ + if (!inst) { + entry2 = (unsigned long *)DTLB_BASE; + entry_dat = &tlb_entry_d; + } else { + entry2 = (unsigned long *)ITLB_BASE; + entry_dat = &tlb_entry_i; + } + entry1 = entry2 + (((*entry_dat - 1) & TLB_MASK) << 1); + + for (i = 0 ; i < NR_TLB_ENTRIES ; i++) { + if (!(entry1[1] & 2)) /* Valid bit check */ + break; + + if (entry1 != entry2) + entry1 -= 2; + else + entry1 += TLB_MASK << 1; + } + + if (i >= NR_TLB_ENTRIES) { /* Empty entry not found */ + entry1 = entry2 + (*entry_dat << 1); + *entry_dat = (*entry_dat + 1) & TLB_MASK; + } + *entry1++ = vaddr; /* Set TLB tag */ + set_tlb_data(entry1, pte_data); + + goto found; +#endif +} + +/*======================================================================* + * flush_tlb_page() : flushes one page + *======================================================================*/ +void local_flush_tlb_page(struct vm_area_struct *vma, unsigned long page) +{ + if (vma->vm_mm && mm_context(vma->vm_mm) != NO_CONTEXT) { + unsigned long flags; + + local_irq_save(flags); + page &= PAGE_MASK; + page |= (mm_context(vma->vm_mm) & MMU_CONTEXT_ASID_MASK); + __flush_tlb_page(page); + local_irq_restore(flags); + } +} + +/*======================================================================* + * flush_tlb_range() : flushes a range of pages + *======================================================================*/ +void local_flush_tlb_range(struct vm_area_struct *vma, unsigned long start, + unsigned long end) +{ + struct mm_struct *mm; + + mm = vma->vm_mm; + if (mm_context(mm) != NO_CONTEXT) { + unsigned long flags; + int size; + + local_irq_save(flags); + size = (end - start + (PAGE_SIZE - 1)) >> PAGE_SHIFT; + if (size > (NR_TLB_ENTRIES / 4)) { /* Too many TLB to flush */ + mm_context(mm) = NO_CONTEXT; + if (mm == current->mm) + activate_context(mm); + } else { + unsigned long asid; + + asid = mm_context(mm) & MMU_CONTEXT_ASID_MASK; + start &= PAGE_MASK; + end += (PAGE_SIZE - 1); + end &= PAGE_MASK; + + start |= asid; + end |= asid; + while (start < end) { + __flush_tlb_page(start); + start += PAGE_SIZE; + } + } + local_irq_restore(flags); + } +} + +/*======================================================================* + * flush_tlb_mm() : flushes the specified mm context TLB's + *======================================================================*/ +void local_flush_tlb_mm(struct mm_struct *mm) +{ + /* Invalidate all TLB of this process. */ + /* Instead of invalidating each TLB, we get new MMU context. */ + if (mm_context(mm) != NO_CONTEXT) { + unsigned long flags; + + local_irq_save(flags); + mm_context(mm) = NO_CONTEXT; + if (mm == current->mm) + activate_context(mm); + local_irq_restore(flags); + } +} + +/*======================================================================* + * flush_tlb_all() : flushes all processes TLBs + *======================================================================*/ +void local_flush_tlb_all(void) +{ + unsigned long flags; + + local_irq_save(flags); + __flush_tlb_all(); + local_irq_restore(flags); +} + +/*======================================================================* + * init_mmu() + *======================================================================*/ +void __init init_mmu(void) +{ + tlb_entry_i = 0; + tlb_entry_d = 0; + mmu_context_cache = MMU_CONTEXT_FIRST_VERSION; + set_asid(mmu_context_cache & MMU_CONTEXT_ASID_MASK); + *(volatile unsigned long *)MPTB = (unsigned long)swapper_pg_dir; +} diff --git a/arch/m32r/mm/init.c b/arch/m32r/mm/init.c new file mode 100644 index 000000000..a290e3793 --- /dev/null +++ b/arch/m32r/mm/init.c @@ -0,0 +1,250 @@ +/* + * linux/arch/m32r/mm/init.c + * + * Copyright (c) 2001, 2002 Hitoshi Yamamoto + * + * Some code taken from sh version. + * Copyright (C) 1999 Niibe Yutaka + * Based on linux/arch/i386/mm/init.c: + * Copyright (C) 1995 Linus Torvalds + */ + +/* $Id$ */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* References to section boundaries */ +extern char _text, _etext, _edata; +extern char __init_begin, __init_end; + +pgd_t swapper_pg_dir[1024]; + +DEFINE_PER_CPU(struct mmu_gather, mmu_gathers); + +void show_mem(void) +{ + int total = 0, reserved = 0; + int shared = 0, cached = 0; + int highmem = 0; + struct page *page; + pg_data_t *pgdat; + unsigned long i; + + printk("Mem-info:\n"); + show_free_areas(); + printk("Free swap: %6ldkB\n",nr_swap_pages<<(PAGE_SHIFT-10)); + for_each_pgdat(pgdat) { + for (i = 0; i < pgdat->node_spanned_pages; ++i) { + page = pgdat->node_mem_map + i; + total++; + if (PageHighMem(page)) + highmem++; + if (PageReserved(page)) + reserved++; + else if (PageSwapCache(page)) + cached++; + else if (page_count(page)) + shared += page_count(page) - 1; + } + } + printk("%d pages of RAM\n", total); + printk("%d pages of HIGHMEM\n",highmem); + printk("%d reserved pages\n",reserved); + printk("%d pages shared\n",shared); + printk("%d pages swap cached\n",cached); +} + +/* + * Cache of MMU context last used. + */ +#ifndef CONFIG_SMP +unsigned long mmu_context_cache_dat; +#else +unsigned long mmu_context_cache_dat[NR_CPUS]; +#endif +static unsigned long hole_pages; + +/* + * function prototype + */ +void __init paging_init(void); +void __init mem_init(void); +void free_initmem(void); +#ifdef CONFIG_BLK_DEV_INITRD +void free_initrd_mem(unsigned long, unsigned long); +#endif + +/* It'd be good if these lines were in the standard header file. */ +#define START_PFN(nid) \ + (NODE_DATA(nid)->bdata->node_boot_start >> PAGE_SHIFT) +#define MAX_LOW_PFN(nid) (NODE_DATA(nid)->bdata->node_low_pfn) + +#ifndef CONFIG_DISCONTIGMEM +unsigned long __init zone_sizes_init(void) +{ + unsigned long zones_size[MAX_NR_ZONES] = {0, 0, 0}; + unsigned long max_dma; + unsigned long low; + unsigned long start_pfn; + +#ifdef CONFIG_MMU + start_pfn = START_PFN(0); + max_dma = virt_to_phys((char *)MAX_DMA_ADDRESS) >> PAGE_SHIFT; + low = MAX_LOW_PFN(0); + + if (low < max_dma){ + zones_size[ZONE_DMA] = low - start_pfn; + zones_size[ZONE_NORMAL] = 0; + } else { + zones_size[ZONE_DMA] = low - start_pfn; + zones_size[ZONE_NORMAL] = low - max_dma; + } +#else + zones_size[ZONE_DMA] = 0 >> PAGE_SHIFT; + zones_size[ZONE_NORMAL] = __MEMORY_SIZE >> PAGE_SHIFT; + start_pfn = __MEMORY_START >> PAGE_SHIFT; +#endif /* CONFIG_MMU */ + + free_area_init_node(0, NODE_DATA(0), zones_size, start_pfn, 0); + + mem_map = contig_page_data.node_mem_map; + + return 0; +} +#else /* CONFIG_DISCONTIGMEM */ +extern unsigned long zone_sizes_init(void); +#endif /* CONFIG_DISCONTIGMEM */ + +/*======================================================================* + * paging_init() : sets up the page tables + *======================================================================*/ +void __init paging_init(void) +{ +#ifdef CONFIG_MMU + int i; + pgd_t *pg_dir; + + /* We don't need kernel mapping as hardware support that. */ + pg_dir = swapper_pg_dir; + + for (i = 0 ; i < USER_PTRS_PER_PGD * 2 ; i++) + pgd_val(pg_dir[i]) = 0; +#endif /* CONFIG_MMU */ + hole_pages = zone_sizes_init(); +} + +int __init reservedpages_count(void) +{ + int reservedpages, nid, i; + + reservedpages = 0; + for (nid = 0 ; nid < numnodes ; nid++) + for (i = 0 ; i < MAX_LOW_PFN(nid) - START_PFN(nid) ; i++) + if (PageReserved(NODE_DATA(nid)->node_mem_map + i)) + reservedpages++; + + return reservedpages; +} + +/*======================================================================* + * mem_init() : + * orig : arch/sh/mm/init.c + *======================================================================*/ +void __init mem_init(void) +{ + int codesize, reservedpages, datasize, initsize; + int nid; +#ifndef CONFIG_MMU + extern unsigned long memory_end; +#endif + + num_physpages = 0; + for (nid = 0 ; nid < numnodes ; nid++) + num_physpages += MAX_LOW_PFN(nid) - START_PFN(nid) + 1; + + num_physpages -= hole_pages; + +#ifndef CONFIG_DISCONTIGMEM + max_mapnr = num_physpages; +#endif /* CONFIG_DISCONTIGMEM */ + +#ifdef CONFIG_MMU + high_memory = (void *)__va(PFN_PHYS(MAX_LOW_PFN(0))); +#else + high_memory = (void *)(memory_end & PAGE_MASK); +#endif /* CONFIG_MMU */ + + /* clear the zero-page */ + memset(empty_zero_page, 0, PAGE_SIZE); + + /* this will put all low memory onto the freelists */ + for (nid = 0 ; nid < numnodes ; nid++) + totalram_pages += free_all_bootmem_node(NODE_DATA(nid)); + + reservedpages = reservedpages_count() - hole_pages; + codesize = (unsigned long) &_etext - (unsigned long)&_text; + datasize = (unsigned long) &_edata - (unsigned long)&_etext; + initsize = (unsigned long) &__init_end - (unsigned long)&__init_begin; + + printk(KERN_INFO "Memory: %luk/%luk available (%dk kernel code, " + "%dk reserved, %dk data, %dk init)\n", + (unsigned long) nr_free_pages() << (PAGE_SHIFT-10), + num_physpages << (PAGE_SHIFT-10), + codesize >> 10, + reservedpages << (PAGE_SHIFT-10), + datasize >> 10, + initsize >> 10); +} + +/*======================================================================* + * free_initmem() : + * orig : arch/sh/mm/init.c + *======================================================================*/ +void free_initmem(void) +{ + unsigned long addr; + + addr = (unsigned long)(&__init_begin); + for (; addr < (unsigned long)(&__init_end); addr += PAGE_SIZE) { + ClearPageReserved(virt_to_page(addr)); + set_page_count(virt_to_page(addr), 1); + free_page(addr); + totalram_pages++; + } + printk (KERN_INFO "Freeing unused kernel memory: %dk freed\n", \ + (int)(&__init_end - &__init_begin) >> 10); +} + +#ifdef CONFIG_BLK_DEV_INITRD +/*======================================================================* + * free_initrd_mem() : + * orig : arch/sh/mm/init.c + *======================================================================*/ +void free_initrd_mem(unsigned long start, unsigned long end) +{ + unsigned long p; + for (p = start; p < end; p += PAGE_SIZE) { + ClearPageReserved(virt_to_page(p)); + set_page_count(virt_to_page(p), 1); + free_page(p); + totalram_pages++; + } + printk (KERN_INFO "Freeing initrd memory: %ldk freed\n", (end - start) >> 10); +} +#endif + diff --git a/arch/m32r/mm/ioremap-nommu.c b/arch/m32r/mm/ioremap-nommu.c new file mode 100644 index 000000000..2759f2d48 --- /dev/null +++ b/arch/m32r/mm/ioremap-nommu.c @@ -0,0 +1,52 @@ +/* + * linux/arch/m32r/mm/ioremap-nommu.c + * + * Copyright (c) 2001, 2002 Hiroyuki Kondo + * + * Taken from mips version. + * (C) Copyright 1995 1996 Linus Torvalds + * (C) Copyright 2001 Ralf Baechle + */ + +/* + * 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 +#include +#include + +#include +#include +#include +#include +#include + + +/* + * Remap an arbitrary physical address space into the kernel virtual + * address space. Needed when the kernel wants to access high addresses + * directly. + * + * NOTE! We need to allow non-page-aligned mappings too: we will obviously + * have to convert them into an offset in a page-aligned mapping, but the + * caller shouldn't need to know that small detail. + */ + +#define IS_LOW512(addr) (!((unsigned long)(addr) & ~0x1fffffffUL)) + +void __iomem * +__ioremap(unsigned long phys_addr, unsigned long size, unsigned long flags) +{ + return (void *)phys_addr; +} + +#define IS_KSEG1(addr) (((unsigned long)(addr) & ~0x1fffffffUL) == KSEG1) + +void iounmap(volatile void __iomem *addr) +{ +} + diff --git a/arch/m32r/mm/ioremap.c b/arch/m32r/mm/ioremap.c new file mode 100644 index 000000000..70c59055c --- /dev/null +++ b/arch/m32r/mm/ioremap.c @@ -0,0 +1,192 @@ +/* + * linux/arch/m32r/mm/ioremap.c + * + * Copyright (c) 2001, 2002 Hiroyuki Kondo + * + * Taken from mips version. + * (C) Copyright 1995 1996 Linus Torvalds + * (C) Copyright 2001 Ralf Baechle + */ + +/* + * 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 +#include +#include + +#include +#include +#include +#include +#include + +static inline void +remap_area_pte(pte_t * pte, unsigned long address, unsigned long size, + unsigned long phys_addr, unsigned long flags) +{ + unsigned long end; + unsigned long pfn; + pgprot_t pgprot = __pgprot(_PAGE_GLOBAL | _PAGE_PRESENT | _PAGE_READ + | _PAGE_WRITE | flags); + + address &= ~PMD_MASK; + end = address + size; + if (end > PMD_SIZE) + end = PMD_SIZE; + if (address >= end) + BUG(); + pfn = phys_addr >> PAGE_SHIFT; + do { + if (!pte_none(*pte)) { + printk("remap_area_pte: page already exists\n"); + BUG(); + } + set_pte(pte, pfn_pte(pfn, pgprot)); + address += PAGE_SIZE; + pfn++; + pte++; + } while (address && (address < end)); +} + +static inline int +remap_area_pmd(pmd_t * pmd, unsigned long address, unsigned long size, + unsigned long phys_addr, unsigned long flags) +{ + unsigned long end; + + address &= ~PGDIR_MASK; + end = address + size; + if (end > PGDIR_SIZE) + end = PGDIR_SIZE; + phys_addr -= address; + if (address >= end) + BUG(); + do { + pte_t * pte = pte_alloc_kernel(&init_mm, pmd, address); + if (!pte) + return -ENOMEM; + remap_area_pte(pte, address, end - address, address + phys_addr, flags); + address = (address + PMD_SIZE) & PMD_MASK; + pmd++; + } while (address && (address < end)); + return 0; +} + +static int +remap_area_pages(unsigned long address, unsigned long phys_addr, + unsigned long size, unsigned long flags) +{ + int error; + pgd_t * dir; + unsigned long end = address + size; + + phys_addr -= address; + dir = pgd_offset(&init_mm, address); + flush_cache_all(); + if (address >= end) + BUG(); + spin_lock(&init_mm.page_table_lock); + do { + pmd_t *pmd; + pmd = pmd_alloc(&init_mm, dir, address); + error = -ENOMEM; + if (!pmd) + break; + if (remap_area_pmd(pmd, address, end - address, + phys_addr + address, flags)) + break; + error = 0; + address = (address + PGDIR_SIZE) & PGDIR_MASK; + dir++; + } while (address && (address < end)); + spin_unlock(&init_mm.page_table_lock); + flush_tlb_all(); + return error; +} + +/* + * Generic mapping function (not visible outside): + */ + +/* + * Remap an arbitrary physical address space into the kernel virtual + * address space. Needed when the kernel wants to access high addresses + * directly. + * + * NOTE! We need to allow non-page-aligned mappings too: we will obviously + * have to convert them into an offset in a page-aligned mapping, but the + * caller shouldn't need to know that small detail. + */ + +#define IS_LOW512(addr) (!((unsigned long)(addr) & ~0x1fffffffUL)) + +void __iomem * +__ioremap(unsigned long phys_addr, unsigned long size, unsigned long flags) +{ + void __iomem * addr; + struct vm_struct * area; + unsigned long offset, last_addr; + + /* Don't allow wraparound or zero size */ + last_addr = phys_addr + size - 1; + if (!size || last_addr < phys_addr) + return NULL; + + /* + * Map objects in the low 512mb of address space using KSEG1, otherwise + * map using page tables. + */ + if (IS_LOW512(phys_addr) && IS_LOW512(phys_addr + size - 1)) + return (void *) KSEG1ADDR(phys_addr); + + /* + * Don't allow anybody to remap normal RAM that we're using.. + */ + if (phys_addr < virt_to_phys(high_memory)) { + char *t_addr, *t_end; + struct page *page; + + t_addr = __va(phys_addr); + t_end = t_addr + (size - 1); + + for(page = virt_to_page(t_addr); page <= virt_to_page(t_end); page++) + if(!PageReserved(page)) + return NULL; + } + + /* + * Mappings have to be page-aligned + */ + offset = phys_addr & ~PAGE_MASK; + phys_addr &= PAGE_MASK; + size = PAGE_ALIGN(last_addr + 1) - phys_addr; + + /* + * Ok, go for it.. + */ + area = get_vm_area(size, VM_IOREMAP); + if (!area) + return NULL; + area->phys_addr = phys_addr; + addr = (void __iomem *) area->addr; + if (remap_area_pages((unsigned long)addr, phys_addr, size, flags)) { + vunmap((void __force *) addr); + return NULL; + } + + return (void __iomem *) (offset + (char __iomem *)addr); +} + +#define IS_KSEG1(addr) (((unsigned long)(addr) & ~0x1fffffffUL) == KSEG1) + +void iounmap(volatile void __iomem *addr) +{ + if (!IS_KSEG1(addr)) + vfree((void *) (PAGE_MASK & (unsigned long) addr)); +} + diff --git a/arch/m32r/mm/mmu.S b/arch/m32r/mm/mmu.S new file mode 100644 index 000000000..0c28f11d6 --- /dev/null +++ b/arch/m32r/mm/mmu.S @@ -0,0 +1,350 @@ +/* + * linux/arch/m32r/mm/mmu.S + * + * Copyright (C) 2001 by Hiroyuki Kondo + */ + +/* $Id: mmu.S,v 1.15 2004/03/16 02:56:27 takata Exp $ */ + +#include /* CONFIG_MMU */ +#include +#include +#include + + .text +#ifdef CONFIG_MMU + +#include +#include +#include +#include + +/* + * TLB Miss Exception handler + */ + .balign 16 +ENTRY(tme_handler) + .global tlb_entry_i_dat + .global tlb_entry_d_dat + + SWITCH_TO_KERNEL_STACK + +#if defined(CONFIG_ISA_M32R2) + st r0, @-sp + st r1, @-sp + st r2, @-sp + st r3, @-sp + + seth r3, #high(MMU_REG_BASE) + ld r1, @(MESTS_offset, r3) ; r1: status (MESTS reg.) + ld r0, @(MDEVP_offset, r3) ; r0: PFN + ASID (MDEVP reg.) + st r1, @(MESTS_offset, r3) ; clear status (MESTS reg.) + and3 r1, r1, #(MESTS_IT) + bnez r1, 1f ; instruction TLB miss? + +;; data TLB miss +;; input +;; r0: PFN + ASID (MDEVP reg.) +;; r1 - r3: free +;; output +;; r0: PFN + ASID +;; r1: TLB entry base address +;; r2: &tlb_entry_{i|d}_dat +;; r3: free + +#ifndef CONFIG_SMP + seth r2, #high(tlb_entry_d_dat) + or3 r2, r2, #low(tlb_entry_d_dat) +#else /* CONFIG_SMP */ + ldi r1, #-8192 + seth r2, #high(tlb_entry_d_dat) + or3 r2, r2, #low(tlb_entry_d_dat) + and r1, sp + ld r1, @(16, r1) ; current_thread_info->cpu + slli r1, #2 + add r2, r1 +#endif /* !CONFIG_SMP */ + seth r1, #high(DTLB_BASE) + or3 r1, r1, #low(DTLB_BASE) + bra 2f + + .balign 16 + .fillinsn +1: +;; instrucntion TLB miss +;; input +;; r0: MDEVP reg. (included ASID) +;; r1 - r3: free +;; output +;; r0: PFN + ASID +;; r1: TLB entry base address +;; r2: &tlb_entry_{i|d}_dat +;; r3: free + ldi r3, #-4096 + and3 r0, r0, #(MMU_CONTEXT_ASID_MASK) + mvfc r1, bpc + and r1, r3 + or r0, r1 ; r0: PFN + ASID +#ifndef CONFIG_SMP + seth r2, #high(tlb_entry_i_dat) + or3 r2, r2, #low(tlb_entry_i_dat) +#else /* CONFIG_SMP */ + ldi r1, #-8192 + seth r2, #high(tlb_entry_i_dat) + or3 r2, r2, #low(tlb_entry_i_dat) + and r1, sp + ld r1, @(16, r1) ; current_thread_info->cpu + slli r1, #2 + add r2, r1 +#endif /* !CONFIG_SMP */ + seth r1, #high(ITLB_BASE) + or3 r1, r1, #low(ITLB_BASE) + + .fillinsn +2: +;; select TLB entry +;; input +;; r0: PFN + ASID +;; r1: TLB entry base address +;; r2: &tlb_entry_{i|d}_dat +;; r3: free +;; output +;; r0: PFN + ASID +;; r1: TLB entry address +;; r2, r3: free +#ifdef CONFIG_ISA_DUAL_ISSUE + ld r3, @r2 || srli r1, #3 +#else + ld r3, @r2 + srli r1, #3 +#endif + add r1, r3 + ; tlb_entry_{d|i}_dat++; + addi r3, #1 + and3 r3, r3, #(NR_TLB_ENTRIES - 1) +#ifdef CONFIG_ISA_DUAL_ISSUE + st r3, @r2 || slli r1, #3 +#else + st r3, @r2 + slli r1, #3 +#endif + +;; load pte +;; input +;; r0: PFN + ASID +;; r1: TLB entry address +;; r2, r3: free +;; output +;; r0: PFN + ASID +;; r1: TLB entry address +;; r2: pte_data +;; r3: free + ; pgd = *(unsigned long *)MPTB; + ld24 r2, #(-MPTB - 1) + srl3 r3, r0, #22 +#ifdef CONFIG_ISA_DUAL_ISSUE + not r2, r2 || slli r3, #2 ; r3: pgd offset +#else + not r2, r2 + slli r3, #2 +#endif + ld r2, @r2 ; r2: pgd base addr (MPTB reg.) + or r3, r2 ; r3: pmd addr + + ; pmd = pmd_offset(pgd, address); + ld r3, @r3 ; r3: pmd data + ldi r2, #-4096 + beqz r3, 3f ; pmd_none(*pmd) ? + + ; pte = pte_offset(pmd, address); + and r2, r3 ; r2: pte base addr + srl3 r3, r0, #10 + and3 r3, r3, #0xffc ; r3: pte offset + or r3, r2 + seth r2, #0x8000 + or r3, r2 ; r3: pte addr + + ; pte_data = (unsigned long)pte_val(*pte); + ld r2, @r3 ; r2: pte data + or3 r2, r2, #2 ; _PAGE_PRESENT(=2) + + .fillinsn +5: +;; set tlb +;; input +;; r0: PFN + ASID +;; r1: TLB entry address +;; r2: pte_data +;; r3: free + st r0, @r1 ; set_tlb_tag(entry++, address); + st r2, @+r1 ; set_tlb_data(entry, pte_data); + + .fillinsn +6: + ld r3, @sp+ + ld r2, @sp+ + ld r1, @sp+ + ld r0, @sp+ + rte + + .fillinsn +3: +;; error +;; input +;; r0: PFN + ASID +;; r1: TLB entry address +;; r2, r3: free +;; output +;; r0: PFN + ASID +;; r1: TLB entry address +;; r2: pte_data +;; r3: free +#ifdef CONFIG_ISA_DUAL_ISSUE + bra 5b || ldi r2, #2 +#else + ldi r2, #2 ; r2: pte_data = 0 | _PAGE_PRESENT(=2) + bra 5b +#endif + +#elif defined (CONFIG_ISA_M32R) + + st sp, @-sp + st r0, @-sp + st r1, @-sp + st r2, @-sp + st r3, @-sp + st r4, @-sp + + seth r3, #high(MMU_REG_BASE) + ld r0, @(MDEVA_offset,r3) ; r0: address (MDEVA reg.) + mvfc r2, bpc ; r2: bpc + ld r1, @(MESTS_offset,r3) ; r1: status (MESTS reg.) + st r1, @(MESTS_offset,r3) ; clear status (MESTS reg.) + and3 r1, r1, #(MESTS_IT) + beqz r1, 1f ; data TLB miss? + +;; instrucntion TLB miss + mv r0, r2 ; address = bpc; + ; entry = (unsigned long *)ITLB_BASE+tlb_entry_i*2; + seth r3, #shigh(tlb_entry_i_dat) + ld r4, @(low(tlb_entry_i_dat),r3) + sll3 r2, r4, #3 + seth r1, #high(ITLB_BASE) + or3 r1, r1, #low(ITLB_BASE) + add r2, r1 ; r2: entry + addi r4, #1 ; tlb_entry_i++; + and3 r4, r4, #(NR_TLB_ENTRIES-1) + st r4, @(low(tlb_entry_i_dat),r3) + bra 2f + .fillinsn +1: +;; data TLB miss + ; entry = (unsigned long *)DTLB_BASE+tlb_entry_d*2; + seth r3, #shigh(tlb_entry_d_dat) + ld r4, @(low(tlb_entry_d_dat),r3) + sll3 r2, r4, #3 + seth r1, #high(DTLB_BASE) + or3 r1, r1, #low(DTLB_BASE) + add r2, r1 ; r2: entry + addi r4, #1 ; tlb_entry_d++; + and3 r4, r4, #(NR_TLB_ENTRIES-1) + st r4, @(low(tlb_entry_d_dat),r3) + .fillinsn +2: +;; load pte +; r0: address, r2: entry +; r1,r3,r4: (free) + ; pgd = *(unsigned long *)MPTB; + ld24 r1, #(-MPTB-1) + not r1, r1 + ld r1, @r1 + srl3 r4, r0, #22 + sll3 r3, r4, #2 + add r3, r1 ; r3: pgd + ; pmd = pmd_offset(pgd, address); + ld r1, @r3 ; r1: pmd + beqz r1, 3f ; pmd_none(*pmd) ? +; + and3 r1, r1, #0xeff + ldi r4, #611 ; _KERNPG_TABLE(=611) + beq r1, r4, 4f ; !pmd_bad(*pmd) ? + .fillinsn +3: + ldi r1, #0 ; r1: pte_data = 0 + bra 5f + .fillinsn +4: + ; pte = pte_offset(pmd, address); + ld r4, @r3 ; r4: pte + ldi r3, #-4096 + and r4, r3 + srl3 r3, r0, #10 + and3 r3, r3, #0xffc + add r4, r3 + seth r3, #0x8000 + add r4, r3 ; r4: pte + ; pte_data = (unsigned long)pte_val(*pte); + ld r1, @r4 ; r1: pte_data + .fillinsn + +;; set tlb +; r0: address, r1: pte_data, r2: entry +; r3,r4: (free) +5: + ldi r3, #-4096 ; set_tlb_tag(entry++, address); + and r3, r0 + seth r4, #shigh(MASID) + ld r4, @(low(MASID),r4) ; r4: MASID + and3 r4, r4, #(MMU_CONTEXT_ASID_MASK) + or r3, r4 + st r3, @r2 + or3 r4, r1, #2 ; _PAGE_PRESENT(=2) + st r4, @(4,r2) ; set_tlb_data(entry, pte_data); + + ld r4, @sp+ + ld r3, @sp+ + ld r2, @sp+ + ld r1, @sp+ + ld r0, @sp+ + ld sp, @sp+ + rte + +#else +#error unknown isa configuration +#endif + +ENTRY(init_tlb) +;; Set MMU Register + seth r0, #high(MMU_REG_BASE) ; Set MMU_REG_BASE higher + or3 r0, r0, #low(MMU_REG_BASE) ; Set MMU_REG_BASE lower + ldi r1, #0 + st r1, @(MPSZ_offset,r0) ; Set MPSZ Reg(Page size 4KB:0 16KB:1 64KB:2) + ldi r1, #0 + st r1, @(MASID_offset,r0) ; Set ASID Zero + +;; Set TLB + seth r0, #high(ITLB_BASE) ; Set ITLB_BASE higher + or3 r0, r0, #low(ITLB_BASE) ; Set ITLB_BASE lower + seth r1, #high(DTLB_BASE) ; Set DTLB_BASE higher + or3 r1, r1, #low(DTLB_BASE) ; Set DTLB_BASE lower + ldi r2, #0 + ldi r3, #NR_TLB_ENTRIES + addi r0, #-4 + addi r1, #-4 +clear_tlb: + st r2, @+r0 ; VPA <- 0 + st r2, @+r0 ; PPA <- 0 + st r2, @+r1 ; VPA <- 0 + st r2, @+r1 ; PPA <- 0 + addi r3, #-1 + bnez r3, clear_tlb +;; + jmp r14 + +ENTRY(m32r_itlb_entrys) +ENTRY(m32r_otlb_entrys) + +#endif /* CONFIG_MMU */ + +.end + diff --git a/arch/m32r/mm/page.S b/arch/m32r/mm/page.S new file mode 100644 index 000000000..a2e9367db --- /dev/null +++ b/arch/m32r/mm/page.S @@ -0,0 +1,82 @@ +/* + * linux/arch/m32r/mm/page.S + * + * Clear/Copy page with CPU + * + * Copyright (C) 2004 The Free Software Initiative of Japan + * + * Written by Niibe Yutaka + * + * 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. + * + */ + .text + .global copy_page + /* + * copy_page (to, from) + * + * PAGE_SIZE = 4096-byte + * Cache line = 16-byte + * 16 * 256 + */ + .align 4 +copy_page: + ldi r2, #255 + ld r3, @r0 /* cache line allocate */ + ld r4, @r1+ + ld r5, @r1+ + ld r6, @r1+ + ld r7, @r1+ + .fillinsn +0: + st r4, @r0 + st r5, @+r0 + st r6, @+r0 + st r7, @+r0 + ld r4, @r1+ + addi r0, #4 + ld r5, @r1+ + ld r6, @r1+ + ld r7, @r1+ + ld r3, @r0 /* cache line allocate */ + addi r2, #-1 + bnez r2, 0b + + st r4, @r0 + st r5, @+r0 + st r6, @+r0 + st r7, @+r0 + jmp r14 + + .text + .global clear_page + /* + * clear_page (to) + * + * PAGE_SIZE = 4096-byte + * Cache line = 16-byte + * 16 * 256 + */ + .align 4 +clear_page: + ldi r2, #255 + ldi r4, #0 + ld r3, @r0 /* cache line allocate */ + .fillinsn +0: + st r4, @r0 + st r4, @+r0 + st r4, @+r0 + st r4, @+r0 + addi r0, #4 + ld r3, @r0 /* cache line allocate */ + addi r2, #-1 + bnez r2, 0b + + st r4, @r0 + st r4, @+r0 + st r4, @+r0 + st r4, @+r0 + jmp r14 diff --git a/arch/m32r/oaks32r/defconfig.nommu b/arch/m32r/oaks32r/defconfig.nommu new file mode 100644 index 000000000..816b53ee7 --- /dev/null +++ b/arch/m32r/oaks32r/defconfig.nommu @@ -0,0 +1,521 @@ +# +# Automatically generated make config: don't edit +# +CONFIG_M32R=y +CONFIG_UID16=y +CONFIG_GENERIC_ISA_DMA=y + +# +# Code maturity level options +# +CONFIG_EXPERIMENTAL=y +CONFIG_CLEAN_COMPILE=y +CONFIG_BROKEN_ON_SMP=y + +# +# General setup +# +# CONFIG_POSIX_MQUEUE is not set +CONFIG_BSD_PROCESS_ACCT=y +# CONFIG_BSD_PROCESS_ACCT_V3 is not set +CONFIG_SYSCTL=y +# CONFIG_AUDIT is not set +CONFIG_LOG_BUF_SHIFT=14 +CONFIG_HOTPLUG=y +# CONFIG_IKCONFIG is not set +CONFIG_EMBEDDED=y +# CONFIG_KALLSYMS is not set +# CONFIG_FUTEX is not set +# CONFIG_EPOLL is not set +CONFIG_IOSCHED_NOOP=y +# CONFIG_IOSCHED_AS is not set +CONFIG_IOSCHED_DEADLINE=y +CONFIG_IOSCHED_CFQ=y +# CONFIG_CC_OPTIMIZE_FOR_SIZE is not set + +# +# Loadable module support +# +CONFIG_MODULES=y +CONFIG_MODULE_UNLOAD=y +# CONFIG_MODULE_FORCE_UNLOAD is not set +CONFIG_OBSOLETE_MODPARM=y +# CONFIG_MODVERSIONS is not set +CONFIG_KMOD=y + +# +# Processor type and features +# +# CONFIG_PLAT_MAPPI is not set +# CONFIG_PLAT_USRV is not set +# CONFIG_PLAT_M32700UT is not set +# CONFIG_PLAT_OPSPUT is not set +CONFIG_PLAT_OAKS32R=y +# CONFIG_PLAT_MAPPI2 is not set +# CONFIG_CHIP_M32700 is not set +CONFIG_CHIP_M32102=y +# CONFIG_CHIP_VDEC2 is not set +# CONFIG_CHIP_OPSP is not set +CONFIG_ISA_M32R=y +CONFIG_BUS_CLOCK=33333333 +CONFIG_TIMER_DIVIDE=128 +# CONFIG_CPU_LITTLE_ENDIAN is not set +CONFIG_MEMORY_START=0x01000000 +CONFIG_MEMORY_SIZE=0x00800000 +CONFIG_NOHIGHMEM=y +# CONFIG_DISCONTIGMEM is not set +CONFIG_RWSEM_GENERIC_SPINLOCK=y +# CONFIG_RWSEM_XCHGADD_ALGORITHM is not set +CONFIG_PREEMPT=y +# CONFIG_HAVE_DEC_LOCK is not set +# CONFIG_SMP is not set + +# +# M32R drivers +# +CONFIG_M32R_NE2000=y + +# +# Power management options (ACPI, APM) +# +# CONFIG_PM is not set + +# +# Bus options (PCI, PCMCIA, EISA, MCA, ISA) +# +# CONFIG_PCI is not set +# CONFIG_ISA is not set + +# +# PCMCIA/CardBus support +# +# CONFIG_PCMCIA is not set + +# +# PCI Hotplug Support +# + +# +# Executable file formats +# +CONFIG_BINFMT_FLAT=y +# CONFIG_BINFMT_ZFLAT is not set +# CONFIG_BINFMT_SHARED_FLAT is not set +# CONFIG_BINFMT_MISC is not set + +# +# Device Drivers +# + +# +# Generic Driver Options +# +CONFIG_STANDALONE=y +CONFIG_PREVENT_FIRMWARE_BUILD=y +# CONFIG_FW_LOADER is not set + +# +# Memory Technology Devices (MTD) +# +# CONFIG_MTD is not set + +# +# Parallel port support +# +# CONFIG_PARPORT is not set + +# +# Plug and Play support +# + +# +# Block devices +# +# CONFIG_BLK_DEV_FD is not set +CONFIG_BLK_DEV_LOOP=y +# CONFIG_BLK_DEV_CRYPTOLOOP is not set +CONFIG_BLK_DEV_NBD=y +CONFIG_BLK_DEV_RAM=y +CONFIG_BLK_DEV_RAM_SIZE=4096 +# CONFIG_BLK_DEV_INITRD is not set + +# +# ATA/ATAPI/MFM/RLL support +# +# CONFIG_IDE is not set + +# +# SCSI device support +# +# CONFIG_SCSI is not set + +# +# Multi-device support (RAID and LVM) +# +# CONFIG_MD is not set + +# +# Fusion MPT device support +# + +# +# IEEE 1394 (FireWire) support +# + +# +# I2O device support +# + +# +# Networking support +# +CONFIG_NET=y + +# +# Networking options +# +CONFIG_PACKET=y +# CONFIG_PACKET_MMAP is not set +# CONFIG_NETLINK_DEV 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_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_IPV6 is not set +# CONFIG_NETFILTER is not set + +# +# SCTP Configuration (EXPERIMENTAL) +# +# CONFIG_IP_SCTP 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_NET_DIVERT is not set +# CONFIG_ECONET is not set +# CONFIG_WAN_ROUTER is not set +# CONFIG_NET_HW_FLOWCONTROL is not set + +# +# QoS and/or fair queueing +# +# CONFIG_NET_SCHED is not set +# CONFIG_NET_CLS_ROUTE is not set + +# +# Network testing +# +# CONFIG_NET_PKTGEN is not set +# CONFIG_NETPOLL is not set +# CONFIG_NET_POLL_CONTROLLER is not set +# CONFIG_HAMRADIO is not set +# CONFIG_IRDA is not set +# CONFIG_BT is not set +# CONFIG_NETDEVICES is not set + +# +# ISDN subsystem +# +# CONFIG_ISDN is not set + +# +# Telephony Support +# +# CONFIG_PHONE is not set + +# +# Input device support +# +CONFIG_INPUT=y + +# +# Userland interfaces +# +# CONFIG_INPUT_MOUSEDEV is not set +# CONFIG_INPUT_JOYDEV is not set +# CONFIG_INPUT_TSDEV is not set +# CONFIG_INPUT_EVDEV is not set +# CONFIG_INPUT_EVBUG is not set + +# +# Input I/O drivers +# +# CONFIG_GAMEPORT is not set +CONFIG_SOUND_GAMEPORT=y +CONFIG_SERIO=y +# CONFIG_SERIO_I8042 is not set +CONFIG_SERIO_SERPORT=y +# CONFIG_SERIO_CT82C710 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_TOUCHSCREEN is not set +# CONFIG_INPUT_MISC is not set + +# +# Character devices +# +# CONFIG_VT is not set +# CONFIG_SERIAL_NONSTANDARD is not set + +# +# Serial drivers +# +# CONFIG_SERIAL_8250 is not set + +# +# Non-8250 serial port support +# +CONFIG_SERIAL_CORE=y +CONFIG_SERIAL_CORE_CONSOLE=y +CONFIG_SERIAL_M32R_SIO=y +CONFIG_SERIAL_M32R_SIO_CONSOLE=y +# CONFIG_SERIAL_M32R_PLDSIO is not set +CONFIG_UNIX98_PTYS=y +CONFIG_LEGACY_PTYS=y +CONFIG_LEGACY_PTY_COUNT=256 +# CONFIG_QIC02_TAPE is not set + +# +# IPMI +# +# CONFIG_IPMI_HANDLER is not set + +# +# Watchdog Cards +# +# CONFIG_WATCHDOG is not set +# CONFIG_RTC is not set +# CONFIG_GEN_RTC is not set +# CONFIG_DTLK is not set +# CONFIG_R3964 is not set + +# +# Ftape, the floppy tape device driver +# +# CONFIG_AGP is not set +# CONFIG_DRM is not set +# CONFIG_RAW_DRIVER is not set + +# +# I2C support +# +# CONFIG_I2C is not set + +# +# Dallas's 1-wire bus +# +# CONFIG_W1 is not set + +# +# Misc devices +# + +# +# Multimedia devices +# +# CONFIG_VIDEO_DEV is not set + +# +# Digital Video Broadcasting Devices +# +# CONFIG_DVB is not set + +# +# Graphics support +# +# CONFIG_FB is not set + +# +# Sound +# +# CONFIG_SOUND is not set + +# +# USB support +# + +# +# USB Gadget Support +# +# CONFIG_USB_GADGET is not set + +# +# File systems +# +CONFIG_EXT2_FS=y +# CONFIG_EXT2_FS_XATTR is not set +# CONFIG_EXT3_FS is not set +# CONFIG_JBD is not set +# CONFIG_REISERFS_FS is not set +# CONFIG_JFS_FS is not set +# CONFIG_XFS_FS is not set +# CONFIG_MINIX_FS is not set +# CONFIG_ROMFS_FS is not set +# CONFIG_QUOTA is not set +# CONFIG_AUTOFS_FS is not set +# CONFIG_AUTOFS4_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_KCORE=y +CONFIG_SYSFS=y +# CONFIG_DEVFS_FS is not set +CONFIG_DEVPTS_FS_XATTR=y +CONFIG_DEVPTS_FS_SECURITY=y +# CONFIG_TMPFS is not set +# CONFIG_HUGETLB_PAGE is not set +CONFIG_RAMFS=y + +# +# 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_HPFS_FS is not set +# CONFIG_QNX4FS_FS is not set +# CONFIG_SYSV_FS is not set +# CONFIG_UFS_FS is not set + +# +# Network File Systems +# +CONFIG_NFS_FS=y +CONFIG_NFS_V3=y +# CONFIG_NFS_V4 is not set +# CONFIG_NFS_DIRECTIO is not set +# CONFIG_NFSD is not set +CONFIG_ROOT_NFS=y +CONFIG_LOCKD=y +CONFIG_LOCKD_V4=y +# CONFIG_EXPORTFS is not set +CONFIG_SUNRPC=y +# CONFIG_RPCSEC_GSS_KRB5 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 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 + +# +# Profiling support +# +# CONFIG_PROFILING is not set + +# +# Kernel hacking +# +# CONFIG_DEBUG_KERNEL is not set +# CONFIG_DEBUG_SPINLOCK_SLEEP is not set +# CONFIG_FRAME_POINTER is not set + +# +# Security options +# +# CONFIG_SECURITY is not set + +# +# Cryptographic options +# +# CONFIG_CRYPTO is not set + +# +# Library routines +# +# CONFIG_CRC_CCITT is not set +CONFIG_CRC32=y +# CONFIG_LIBCRC32C is not set diff --git a/arch/m32r/oaks32r/dot.gdbinit.nommu b/arch/m32r/oaks32r/dot.gdbinit.nommu new file mode 100644 index 000000000..48420f7e8 --- /dev/null +++ b/arch/m32r/oaks32r/dot.gdbinit.nommu @@ -0,0 +1,155 @@ +# .gdbinit file +# $Id: dot.gdbinit.oaks32r,v 1.2 2004/04/15 02:33:14 takata Exp $ +#----- +# NOTE: this file is generated by a script, "gen_gdbinit.pl". +# (Please type "gen_gdbinit.pl --help" and check the help message). +# $ Id: gen_gdbinit.pl,v 1.10 2004/04/15 02:10:45 takata Exp $ +#----- +# target platform: oaks32r + +# setting +set width 0d70 +set radix 0d16 + +# clk xin:cpu:bus=16:66:33 +define clock_init + set *(unsigned long *)0x00ef4008 = 1 + shell sleep 0.1 + set *(unsigned long *)0x00ef4000 = 0x00020100 +end + +# Initialize programmable ports +define port_init + set *(unsigned long *)0x00ef1000 = 0x1 + set *(unsigned long *)0x00ef1060 = 0x01400001 + set *(unsigned long *)0x00ef1064 = 0x00015555 + set *(unsigned long *)0x00ef1068 = 0x55555050 + set *(unsigned long *)0x00ef106c = 0x05150040 +end + +# Initialize SDRAM controller +define sdram_init + set *(unsigned long *)0x00ef6008 = 0x00000182 + set *(unsigned long *)0x00ef600c = 0x00000001 + shell sleep 0.1 + set *(unsigned long *)0x00ef602c = 0x00000010 + set *(unsigned long *)0x00ef6028 = 0x00000300 + set *(unsigned long *)0x00ef6048 = 0x00000001 + set *(unsigned long *)0x00ef6020 = 0x01000041 + set *(unsigned long *)0x00ef6004 = 0x00010117 + set *(unsigned long *)0x00ef6010 = 0x00000001 + set *(unsigned long *)0x00ef6024 = 0x00000001 +end +document sdram_init + SDRAM controller initialization + 0x01000000 - 0x017fffff (8MB) +end + +# Initialize LAN controller +define lanc_init + set *(unsigned long *)0x00ef5008 = 0x03031303 + #RST DRV (P64) + set *(unsigned char *)0x00ef1046 = 0x08 + set *(unsigned char *)0x00ef1026 = 0xff + set *(unsigned char *)0x00ef1026 = 0x00 + set *(unsigned short *)0x02000630 = 0xffff +end + +# Show current task structure +define show_current + set $current = $spi & 0xffffe000 + printf "$current=0x%08lX\n",$current + print *(struct task_struct *)$current +end + +# Show user assigned task structure +define show_task + set = $arg0 & 0xffffe000 + printf "$task=0x%08lX\n",$task + print *(struct task_struct *)$task +end +document show_task + Show user assigned task structure + arg0 : task structure address +end + +# Show M32R registers +define show_regs + printf " R0[0x%08lX] R1[0x%08lX] R2[0x%08lX] R3[0x%08lX]\n",$r0,$r1,$r2,$r3 + printf " R4[0x%08lX] R5[0x%08lX] R6[0x%08lX] R7[0x%08lX]\n",$r4,$r5,$r6,$r7 + printf " R8[0x%08lX] R9[0x%08lX] R10[0x%08lX] R11[0x%08lX]\n",$r8,$r9,$r10,$r11 + printf "R12[0x%08lX] FP[0x%08lX] LR[0x%08lX] SP[0x%08lX]\n",$r12,$fp,$lr,$sp + printf "PSW[0x%08lX] CBR[0x%08lX] SPI[0x%08lX] SPU[0x%08lX]\n",$psw,$cbr,$spi,$spu + printf "BPC[0x%08lX] PC[0x%08lX] ACCL[0x%08lX] ACCH[0x%08lX]\n",$bpc,$pc,$accl,$acch +end + +# Setup all +define setup + use_mon_code + set *(unsigned int)0xfffffffc=0x60 + shell sleep 0.1 + clock_init + shell sleep 0.1 + port_init + sdram_init + lanc_init +end + +# Load modules +define load_modules + use_debug_dma + load +end + +# Set kernel parameters +define set_kernel_parameters + set $param = (void*)0x01002000 + # INITRD_START + set *(unsigned long *)($param + 0x0010) = 0x00000000 + # INITRD_SIZE + set *(unsigned long *)($param + 0x0014) = 0x00000000 + # M32R_CPUCLK + set *(unsigned long *)($param + 0x0018) = 0d66666667 + # M32R_BUSCLK + set *(unsigned long *)($param + 0x001c) = 0d33333333 + + # M32R_TIMER_DIVIDE + set *(unsigned long *)($param + 0x0020) = 0d128 + +# set {char[0x200]}($param + 0x100) = "console=ttyD0,115200n8x root=/dev/nfsroot nfsroot=192.168.0.1:/project/m32r-linux/export/rootfs nfsaddrs=192.168.0.101:192.168.0.1:192.168.0.1:255.255.255.0:mappi001 \0" + set {char[0x200]}($param + 0x100) = "console=ttyD0,115200n8x root=/dev/nfsroot nfsroot=192.168.0.1:/project/m32r-linux/export/root.busybox.flat nfsaddrs=192.168.0.101:192.168.0.1:192.168.0.1:255.255.255.0:mappi001 \0" +end + +# Boot +define boot + set_kernel_parameters + set $fp = 0 + set $pc = 0x01001000 + si + c +end + +# Set breakpoints +define set_breakpoints + b *0x00000020 + b *0x00000030 +end + +# Restart +define restart + sdireset + sdireset + setup + load_modules + boot +end + +sdireset +sdireset +file vmlinux +target m32rsdi +setup +#load_modules +#set_breakpoints +#boot + diff --git a/arch/m32r/oprofile/Kconfig b/arch/m32r/oprofile/Kconfig new file mode 100644 index 000000000..19d37730b --- /dev/null +++ b/arch/m32r/oprofile/Kconfig @@ -0,0 +1,23 @@ + +menu "Profiling support" + depends on EXPERIMENTAL + +config PROFILING + bool "Profiling support (EXPERIMENTAL)" + help + Say Y here to enable the extended profiling support mechanisms used + by profilers such as OProfile. + + +config OPROFILE + tristate "OProfile system profiling (EXPERIMENTAL)" + depends on PROFILING + help + OProfile is a profiling system capable of profiling the + whole system, include the kernel, kernel modules, libraries, + and applications. + + If unsure, say N. + +endmenu + diff --git a/arch/m32r/oprofile/Makefile b/arch/m32r/oprofile/Makefile new file mode 100644 index 000000000..06e7c81ea --- /dev/null +++ b/arch/m32r/oprofile/Makefile @@ -0,0 +1,9 @@ +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/m32r/oprofile/init.c b/arch/m32r/oprofile/init.c new file mode 100644 index 000000000..f5843c8cc --- /dev/null +++ b/arch/m32r/oprofile/init.c @@ -0,0 +1,25 @@ +/** + * @file init.c + * + * @remark Copyright 2002 OProfile authors + * @remark Read the file COPYING + * + * @author John Levon + */ + +#include +#include +#include +#include + +extern void timer_init(struct oprofile_operations ** ops); + +int __init oprofile_arch_init(struct oprofile_operations ** ops) +{ + return -ENODEV; +} + + +void oprofile_arch_exit(void) +{ +} diff --git a/arch/m32r/opsput/defconfig.opsput b/arch/m32r/opsput/defconfig.opsput new file mode 100644 index 000000000..07eae9601 --- /dev/null +++ b/arch/m32r/opsput/defconfig.opsput @@ -0,0 +1,598 @@ +# +# Automatically generated make config: don't edit +# +CONFIG_M32R=y +CONFIG_UID16=y +CONFIG_GENERIC_ISA_DMA=y + +# +# Code maturity level options +# +CONFIG_EXPERIMENTAL=y +CONFIG_CLEAN_COMPILE=y +CONFIG_BROKEN_ON_SMP=y + +# +# General setup +# +CONFIG_SWAP=y +CONFIG_SYSVIPC=y +# CONFIG_POSIX_MQUEUE is not set +CONFIG_BSD_PROCESS_ACCT=y +# CONFIG_BSD_PROCESS_ACCT_V3 is not set +CONFIG_SYSCTL=y +# CONFIG_AUDIT is not set +CONFIG_LOG_BUF_SHIFT=14 +CONFIG_HOTPLUG=y +CONFIG_IKCONFIG=y +# CONFIG_IKCONFIG_PROC is not set +CONFIG_EMBEDDED=y +# CONFIG_KALLSYMS is not set +# CONFIG_FUTEX is not set +# CONFIG_EPOLL is not set +CONFIG_IOSCHED_NOOP=y +# CONFIG_IOSCHED_AS is not set +CONFIG_IOSCHED_DEADLINE=y +CONFIG_IOSCHED_CFQ=y +# CONFIG_CC_OPTIMIZE_FOR_SIZE is not set + +# +# Loadable module support +# +CONFIG_MODULES=y +CONFIG_MODULE_UNLOAD=y +# CONFIG_MODULE_FORCE_UNLOAD is not set +CONFIG_OBSOLETE_MODPARM=y +# CONFIG_MODVERSIONS is not set +CONFIG_KMOD=y + +# +# Processor type and features +# +# CONFIG_PLAT_MAPPI is not set +# CONFIG_PLAT_USRV is not set +# CONFIG_PLAT_M32700UT is not set +CONFIG_PLAT_OPSPUT=y +# CONFIG_PLAT_OAKS32R is not set +# CONFIG_PLAT_MAPPI2 is not set +# CONFIG_CHIP_M32700 is not set +# CONFIG_CHIP_M32102 is not set +# CONFIG_CHIP_VDEC2 is not set +CONFIG_CHIP_OPSP=y +CONFIG_MMU=y +CONFIG_TLB_ENTRIES=32 +CONFIG_ISA_M32R2=y +CONFIG_ISA_DSP_LEVEL2=y +CONFIG_ISA_DUAL_ISSUE=y +CONFIG_BUS_CLOCK=50000000 +CONFIG_TIMER_DIVIDE=128 +# CONFIG_CPU_LITTLE_ENDIAN is not set +CONFIG_MEMORY_START=0x08000000 +CONFIG_MEMORY_SIZE=0x01000000 +CONFIG_NOHIGHMEM=y +# CONFIG_DISCONTIGMEM is not set +CONFIG_RWSEM_GENERIC_SPINLOCK=y +# CONFIG_RWSEM_XCHGADD_ALGORITHM is not set +# CONFIG_PREEMPT is not set +# CONFIG_SMP is not set + +# +# M32R drivers +# +# CONFIG_M32R_CFC is not set +CONFIG_M32R_SMC91111=y +CONFIG_M32700UT_DS1302=y + +# +# Power management options (ACPI, APM) +# +# CONFIG_PM is not set + +# +# Bus options (PCI, PCMCIA, EISA, MCA, ISA) +# +# CONFIG_PCI is not set +# CONFIG_ISA is not set + +# +# PCMCIA/CardBus support +# +CONFIG_PCMCIA=y +# CONFIG_PCMCIA_DEBUG is not set +# CONFIG_TCIC is not set + +# +# PCI Hotplug Support +# + +# +# Executable file formats +# +CONFIG_BINFMT_ELF=y +# CONFIG_BINFMT_MISC is not set + +# +# Device Drivers +# + +# +# Generic Driver Options +# +CONFIG_STANDALONE=y +CONFIG_PREVENT_FIRMWARE_BUILD=y +# CONFIG_FW_LOADER is not set +# CONFIG_DEBUG_DRIVER is not set + +# +# Memory Technology Devices (MTD) +# +# CONFIG_MTD is not set + +# +# Parallel port support +# +# CONFIG_PARPORT is not set + +# +# Plug and Play support +# + +# +# Block devices +# +# CONFIG_BLK_DEV_FD 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_RAM=y +CONFIG_BLK_DEV_RAM_SIZE=4096 +# CONFIG_BLK_DEV_INITRD is not set + +# +# ATA/ATAPI/MFM/RLL support +# +# CONFIG_IDE is not set + +# +# SCSI device support +# +CONFIG_SCSI=m +CONFIG_SCSI_PROC_FS=y + +# +# SCSI support type (disk, tape, CD-ROM) +# +CONFIG_BLK_DEV_SD=m +# CONFIG_CHR_DEV_ST is not set +# CONFIG_CHR_DEV_OSST is not set +CONFIG_BLK_DEV_SR=m +# CONFIG_BLK_DEV_SR_VENDOR is not set +CONFIG_CHR_DEV_SG=m + +# +# Some SCSI devices (e.g. CD jukebox) support multiple LUNs +# +CONFIG_SCSI_MULTI_LUN=y +# CONFIG_SCSI_CONSTANTS is not set +# CONFIG_SCSI_LOGGING is not set + +# +# SCSI Transport Attributes +# +# CONFIG_SCSI_SPI_ATTRS is not set +# CONFIG_SCSI_FC_ATTRS is not set + +# +# SCSI low-level drivers +# +# CONFIG_SCSI_AIC7XXX_OLD is not set +# CONFIG_SCSI_SATA is not set +# CONFIG_SCSI_EATA_PIO is not set +# CONFIG_SCSI_DEBUG is not set + +# +# PCMCIA SCSI adapter support +# +# CONFIG_PCMCIA_AHA152X is not set +# CONFIG_PCMCIA_FDOMAIN is not set +# CONFIG_PCMCIA_NINJA_SCSI is not set +# CONFIG_PCMCIA_QLOGIC is not set +# CONFIG_PCMCIA_SYM53C500 is not set + +# +# Multi-device support (RAID and LVM) +# +# CONFIG_MD is not set + +# +# Fusion MPT device support +# + +# +# IEEE 1394 (FireWire) support +# + +# +# I2O device support +# + +# +# Networking support +# +CONFIG_NET=y + +# +# Networking options +# +CONFIG_PACKET=y +# CONFIG_PACKET_MMAP is not set +# CONFIG_NETLINK_DEV 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_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_IPV6 is not set +# CONFIG_NETFILTER is not set + +# +# SCTP Configuration (EXPERIMENTAL) +# +# CONFIG_IP_SCTP 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_NET_DIVERT is not set +# CONFIG_ECONET is not set +# CONFIG_WAN_ROUTER is not set +# CONFIG_NET_HW_FLOWCONTROL is not set + +# +# QoS and/or fair queueing +# +# CONFIG_NET_SCHED is not set +# CONFIG_NET_CLS_ROUTE is not set + +# +# Network testing +# +# CONFIG_NET_PKTGEN is not set +# CONFIG_NETPOLL is not set +# CONFIG_NET_POLL_CONTROLLER is not set +# CONFIG_HAMRADIO is not set +# CONFIG_IRDA is not set +# CONFIG_BT is not set +# CONFIG_NETDEVICES is not set + +# +# ISDN subsystem +# +# CONFIG_ISDN is not set + +# +# Telephony Support +# +# CONFIG_PHONE is not set + +# +# Input device support +# +CONFIG_INPUT=y + +# +# Userland interfaces +# +# CONFIG_INPUT_MOUSEDEV is not set +# CONFIG_INPUT_JOYDEV is not set +# CONFIG_INPUT_TSDEV is not set +# CONFIG_INPUT_EVDEV is not set +# CONFIG_INPUT_EVBUG is not set + +# +# Input I/O drivers +# +# CONFIG_GAMEPORT is not set +CONFIG_SOUND_GAMEPORT=y +CONFIG_SERIO=y +# CONFIG_SERIO_I8042 is not set +CONFIG_SERIO_SERPORT=y +# CONFIG_SERIO_CT82C710 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_TOUCHSCREEN is not set +# CONFIG_INPUT_MISC is not set + +# +# Character devices +# +# CONFIG_VT is not set +# CONFIG_SERIAL_NONSTANDARD is not set + +# +# Serial drivers +# +# CONFIG_SERIAL_8250 is not set + +# +# Non-8250 serial port support +# +CONFIG_SERIAL_CORE=y +CONFIG_SERIAL_CORE_CONSOLE=y +# CONFIG_SERIAL_M32R_SIO is not set +CONFIG_SERIAL_M32R_PLDSIO=y +CONFIG_SERIAL_M32R_PLDSIO_CONSOLE=y +CONFIG_UNIX98_PTYS=y +CONFIG_LEGACY_PTYS=y +CONFIG_LEGACY_PTY_COUNT=256 +# CONFIG_QIC02_TAPE is not set + +# +# IPMI +# +# CONFIG_IPMI_HANDLER is not set + +# +# Watchdog Cards +# +# CONFIG_WATCHDOG is not set +# CONFIG_RTC is not set +# CONFIG_GEN_RTC is not set +# CONFIG_DTLK is not set +# CONFIG_R3964 is not set + +# +# Ftape, the floppy tape device driver +# +# CONFIG_AGP is not set +# CONFIG_DRM is not set + +# +# PCMCIA character devices +# +# CONFIG_SYNCLINK_CS is not set +# CONFIG_RAW_DRIVER is not set + +# +# I2C support +# +# CONFIG_I2C is not set + +# +# Dallas's 1-wire bus +# +# CONFIG_W1 is not set + +# +# Misc devices +# + +# +# Multimedia devices +# +# CONFIG_VIDEO_DEV is not set + +# +# Digital Video Broadcasting Devices +# +# CONFIG_DVB is not set + +# +# Graphics support +# +# CONFIG_FB is not set + +# +# Sound +# +# CONFIG_SOUND is not set + +# +# USB support +# + +# +# USB Gadget Support +# +# CONFIG_USB_GADGET is not set + +# +# File systems +# +CONFIG_EXT2_FS=y +# CONFIG_EXT2_FS_XATTR 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_JBD=m +CONFIG_JBD_DEBUG=y +CONFIG_FS_MBCACHE=y +CONFIG_REISERFS_FS=m +# CONFIG_REISERFS_CHECK is not set +# CONFIG_REISERFS_PROC_INFO is not set +# CONFIG_REISERFS_FS_XATTR is not set +# CONFIG_JFS_FS is not set +# CONFIG_XFS_FS is not set +# CONFIG_MINIX_FS is not set +# CONFIG_ROMFS_FS is not set +# CONFIG_QUOTA is not set +# CONFIG_AUTOFS_FS is not set +# CONFIG_AUTOFS4_FS is not set + +# +# CD-ROM/DVD Filesystems +# +CONFIG_ISO9660_FS=m +CONFIG_JOLIET=y +# CONFIG_ZISOFS is not set +CONFIG_UDF_FS=m +CONFIG_UDF_NLS=y + +# +# DOS/FAT/NT Filesystems +# +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 + +# +# Pseudo filesystems +# +CONFIG_PROC_FS=y +CONFIG_PROC_KCORE=y +CONFIG_SYSFS=y +CONFIG_DEVFS_FS=y +CONFIG_DEVFS_MOUNT=y +# CONFIG_DEVFS_DEBUG is not set +# CONFIG_DEVPTS_FS_XATTR is not set +CONFIG_TMPFS=y +# CONFIG_HUGETLB_PAGE is not set +CONFIG_RAMFS=y + +# +# 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_HPFS_FS is not set +# CONFIG_QNX4FS_FS is not set +# CONFIG_SYSV_FS is not set +# CONFIG_UFS_FS is not set + +# +# Network File Systems +# +CONFIG_NFS_FS=y +CONFIG_NFS_V3=y +# CONFIG_NFS_V4 is not set +# CONFIG_NFS_DIRECTIO is not set +# CONFIG_NFSD is not set +CONFIG_ROOT_NFS=y +CONFIG_LOCKD=y +CONFIG_LOCKD_V4=y +# CONFIG_EXPORTFS is not set +CONFIG_SUNRPC=y +# CONFIG_RPCSEC_GSS_KRB5 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 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 + +# +# Profiling support +# +# CONFIG_PROFILING is not set + +# +# Kernel hacking +# +CONFIG_DEBUG_KERNEL=y +# CONFIG_DEBUG_STACKOVERFLOW is not set +# CONFIG_DEBUG_SLAB is not set +# CONFIG_DEBUG_IOVIRT is not set +# CONFIG_MAGIC_SYSRQ is not set +# CONFIG_DEBUG_SPINLOCK is not set +# CONFIG_DEBUG_PAGEALLOC is not set +CONFIG_DEBUG_INFO=y +# CONFIG_DEBUG_SPINLOCK_SLEEP is not set +# CONFIG_FRAME_POINTER is not set + +# +# Security options +# +# CONFIG_SECURITY is not set + +# +# Cryptographic options +# +# CONFIG_CRYPTO is not set + +# +# Library routines +# +# CONFIG_CRC_CCITT is not set +CONFIG_CRC32=y +# CONFIG_LIBCRC32C is not set diff --git a/arch/m32r/opsput/dot.gdbinit b/arch/m32r/opsput/dot.gdbinit new file mode 100644 index 000000000..9883f001d --- /dev/null +++ b/arch/m32r/opsput/dot.gdbinit @@ -0,0 +1,180 @@ +# .gdbinit file +# $Id: dot.gdbinit,v 1.1 2004/07/27 06:54:20 sakugawa Exp $ + +# setting +set width 0d70 +set radix 0d16 +set height 0 +debug_chaos + +define tlb_init + set $tlbbase = 0xfe000000 + set *(unsigned long *)($tlbbase + 0x04) = 0x0 + set *(unsigned long *)($tlbbase + 0x0c) = 0x0 + set *(unsigned long *)($tlbbase + 0x14) = 0x0 + set *(unsigned long *)($tlbbase + 0x1c) = 0x0 + set *(unsigned long *)($tlbbase + 0x24) = 0x0 + set *(unsigned long *)($tlbbase + 0x2c) = 0x0 + set *(unsigned long *)($tlbbase + 0x34) = 0x0 + set *(unsigned long *)($tlbbase + 0x3c) = 0x0 + set *(unsigned long *)($tlbbase + 0x44) = 0x0 + set *(unsigned long *)($tlbbase + 0x4c) = 0x0 + set *(unsigned long *)($tlbbase + 0x54) = 0x0 + set *(unsigned long *)($tlbbase + 0x5c) = 0x0 + set *(unsigned long *)($tlbbase + 0x64) = 0x0 + set *(unsigned long *)($tlbbase + 0x6c) = 0x0 + set *(unsigned long *)($tlbbase + 0x74) = 0x0 + set *(unsigned long *)($tlbbase + 0x7c) = 0x0 + set *(unsigned long *)($tlbbase + 0x84) = 0x0 + set *(unsigned long *)($tlbbase + 0x8c) = 0x0 + set *(unsigned long *)($tlbbase + 0x94) = 0x0 + set *(unsigned long *)($tlbbase + 0x9c) = 0x0 + set *(unsigned long *)($tlbbase + 0xa4) = 0x0 + set *(unsigned long *)($tlbbase + 0xac) = 0x0 + set *(unsigned long *)($tlbbase + 0xb4) = 0x0 + set *(unsigned long *)($tlbbase + 0xbc) = 0x0 + set *(unsigned long *)($tlbbase + 0xc4) = 0x0 + set *(unsigned long *)($tlbbase + 0xcc) = 0x0 + set *(unsigned long *)($tlbbase + 0xd4) = 0x0 + set *(unsigned long *)($tlbbase + 0xdc) = 0x0 + set *(unsigned long *)($tlbbase + 0xe4) = 0x0 + set *(unsigned long *)($tlbbase + 0xec) = 0x0 + set *(unsigned long *)($tlbbase + 0xf4) = 0x0 + set *(unsigned long *)($tlbbase + 0xfc) = 0x0 + set $tlbbase = 0xfe000800 + set *(unsigned long *)($tlbbase + 0x04) = 0x0 + set *(unsigned long *)($tlbbase + 0x0c) = 0x0 + set *(unsigned long *)($tlbbase + 0x14) = 0x0 + set *(unsigned long *)($tlbbase + 0x1c) = 0x0 + set *(unsigned long *)($tlbbase + 0x24) = 0x0 + set *(unsigned long *)($tlbbase + 0x2c) = 0x0 + set *(unsigned long *)($tlbbase + 0x34) = 0x0 + set *(unsigned long *)($tlbbase + 0x3c) = 0x0 + set *(unsigned long *)($tlbbase + 0x44) = 0x0 + set *(unsigned long *)($tlbbase + 0x4c) = 0x0 + set *(unsigned long *)($tlbbase + 0x54) = 0x0 + set *(unsigned long *)($tlbbase + 0x5c) = 0x0 + set *(unsigned long *)($tlbbase + 0x64) = 0x0 + set *(unsigned long *)($tlbbase + 0x6c) = 0x0 + set *(unsigned long *)($tlbbase + 0x74) = 0x0 + set *(unsigned long *)($tlbbase + 0x7c) = 0x0 + set *(unsigned long *)($tlbbase + 0x84) = 0x0 + set *(unsigned long *)($tlbbase + 0x8c) = 0x0 + set *(unsigned long *)($tlbbase + 0x94) = 0x0 + set *(unsigned long *)($tlbbase + 0x9c) = 0x0 + set *(unsigned long *)($tlbbase + 0xa4) = 0x0 + set *(unsigned long *)($tlbbase + 0xac) = 0x0 + set *(unsigned long *)($tlbbase + 0xb4) = 0x0 + set *(unsigned long *)($tlbbase + 0xbc) = 0x0 + set *(unsigned long *)($tlbbase + 0xc4) = 0x0 + set *(unsigned long *)($tlbbase + 0xcc) = 0x0 + set *(unsigned long *)($tlbbase + 0xd4) = 0x0 + set *(unsigned long *)($tlbbase + 0xdc) = 0x0 + set *(unsigned long *)($tlbbase + 0xe4) = 0x0 + set *(unsigned long *)($tlbbase + 0xec) = 0x0 + set *(unsigned long *)($tlbbase + 0xf4) = 0x0 + set *(unsigned long *)($tlbbase + 0xfc) = 0x0 +end + +define load_modules + use_debug_dma + load +end + +# Set kernel parameters +define set_kernel_parameters + set $param = (void*)0x88002000 + # INITRD_START +# set *(unsigned long *)($param + 0x0010) = 0x08300000 + # INITRD_SIZE +# set *(unsigned long *)($param + 0x0014) = 0x00400000 + # M32R_CPUCLK + set *(unsigned long *)($param + 0x0018) = 0d200000000 + # M32R_BUSCLK + set *(unsigned long *)($param + 0x001c) = 0d50000000 +# set *(unsigned long *)($param + 0x001c) = 0d25000000 + + # M32R_TIMER_DIVIDE + set *(unsigned long *)($param + 0x0020) = 0d128 + + set {char[0x200]}($param + 0x100) = "console=ttyD0,115200n8x\ + root=/dev/nfsroot \ + nfsroot=192.168.0.1:/project/m32r-linux/export/root.2.6 \ + nfsaddrs=192.168.0.101:192.168.0.1:192.168.0.1:255.255.255.0:mappi001 \ + mem=16m \0" +end + +define boot + set_kernel_parameters + set $pc=0x88001000 + set $fp=0 + set $evb=0x88000000 + # I/D-Cache ON + +# IPI +# set *(long *)0x00eff2f8 = 0x2 + set $fp=0 +# set *(unsigned long *)0xa0ef4000 = 0x100 + si +end + +# Show TLB entries +define show_tlb_entries + set $i = 0 + set $addr = $arg0 + use_mon_code + while ($i < 0d32 ) + set $tlb_tag = *(unsigned long*)$addr + set $tlb_data = *(unsigned long*)($addr + 4) + printf " [%2d] 0x%08lx : 0x%08lx - 0x%08lx\n", $i, $addr, $tlb_tag, $tlb_data + set $i = $i + 1 + set $addr = $addr + 8 + end +# use_debug_dma +end +define itlb + set $itlb=0xfe000000 + show_tlb_entries $itlb +end +define dtlb + set $dtlb=0xfe000800 + show_tlb_entries $dtlb +end + +define show_regs + printf " R0[%08lx] R1[%08lx] R2[%08lx] R3[%08lx]\n",$r0,$r1,$r2,$r3 + printf " R4[%08lx] R5[%08lx] R6[%08lx] R7[%08lx]\n",$r4,$r5,$r6,$r7 + printf " R8[%08lx] R9[%08lx] R10[%08lx] R11[%08lx]\n",$r8,$r9,$r10,$r11 + printf "R12[%08lx] FP[%08lx] LR[%08lx] SP[%08lx]\n",$r12,$fp,$lr,$sp + printf "PSW[%08lx] CBR[%08lx] SPI[%08lx] SPU[%08lx]\n",$psw,$cbr,$spi,$spu + printf "BPC[%08lx] PC[%08lx] ACCL[%08lx] ACCH[%08lx]\n",$bpc,$pc,$accl,$acch + printf "EVB[%08lx]\n",$evb +end + +define setup + debug_chaos + set *(unsigned long *)0xa0ef6004 = 0x0001053f + set *(unsigned long *)0xa0ef6028 = 0x00031102 +# set *(unsigned long *)0xa0ef400c = 0x2 +end + +sdireset +sdireset +file vmlinux +target m32rsdi +set $pc=0x0 +b *0x30000 +c +setup +tlb_init +load_modules +#set *(long *)0xa0ef4000=0x101 +#set *(long *)0xa0ef400c=0x002 + +boot +#b tme_handler +b *0x88000020 + + + + diff --git a/arch/m68k/Kconfig.debug b/arch/m68k/Kconfig.debug new file mode 100644 index 000000000..f53b6d530 --- /dev/null +++ b/arch/m68k/Kconfig.debug @@ -0,0 +1,5 @@ +menu "Kernel hacking" + +source "lib/Kconfig.debug" + +endmenu diff --git a/arch/m68k/kernel/Makefile b/arch/m68k/kernel/Makefile index 081a6747c..458925c47 100644 --- a/arch/m68k/kernel/Makefile +++ b/arch/m68k/kernel/Makefile @@ -7,7 +7,7 @@ ifndef CONFIG_SUN3 else extra-y := sun3-head.o endif -extra-y += vmlinux.lds.s +extra-y += vmlinux.lds obj-y := entry.o process.o traps.o ints.o signal.o ptrace.o \ sys_m68k.o time.o semaphore.o setup.o m68k_ksyms.o diff --git a/arch/m68k/kernel/m68k_ksyms.c b/arch/m68k/kernel/m68k_ksyms.c index b069b0c84..fe837e31a 100644 --- a/arch/m68k/kernel/m68k_ksyms.c +++ b/arch/m68k/kernel/m68k_ksyms.c @@ -16,7 +16,6 @@ #include #include #include -#include asmlinkage long long __ashldi3 (long long, int); asmlinkage long long __ashrdi3 (long long, int); @@ -72,18 +71,18 @@ EXPORT_SYMBOL(vme_brdtype); explicitly (the C compiler generates them). Fortunately, their interface isn't gonna change any time soon now, so it's OK to leave it out of version control. */ -EXPORT_SYMBOL_NOVERS(__ashldi3); -EXPORT_SYMBOL_NOVERS(__ashrdi3); -EXPORT_SYMBOL_NOVERS(__lshrdi3); -EXPORT_SYMBOL_NOVERS(memcpy); -EXPORT_SYMBOL_NOVERS(memset); -EXPORT_SYMBOL_NOVERS(memcmp); -EXPORT_SYMBOL_NOVERS(memscan); -EXPORT_SYMBOL_NOVERS(__muldi3); +EXPORT_SYMBOL(__ashldi3); +EXPORT_SYMBOL(__ashrdi3); +EXPORT_SYMBOL(__lshrdi3); +EXPORT_SYMBOL(memcpy); +EXPORT_SYMBOL(memset); +EXPORT_SYMBOL(memcmp); +EXPORT_SYMBOL(memscan); +EXPORT_SYMBOL(__muldi3); -EXPORT_SYMBOL_NOVERS(__down_failed); -EXPORT_SYMBOL_NOVERS(__down_failed_interruptible); -EXPORT_SYMBOL_NOVERS(__down_failed_trylock); -EXPORT_SYMBOL_NOVERS(__up_wakeup); +EXPORT_SYMBOL(__down_failed); +EXPORT_SYMBOL(__down_failed_interruptible); +EXPORT_SYMBOL(__down_failed_trylock); +EXPORT_SYMBOL(__up_wakeup); EXPORT_SYMBOL(get_wchan); diff --git a/arch/m68k/kernel/time.c b/arch/m68k/kernel/time.c index d85616f14..11d122980 100644 --- a/arch/m68k/kernel/time.c +++ b/arch/m68k/kernel/time.c @@ -38,24 +38,6 @@ static inline int set_rtc_mmss(unsigned long nowtime) return -1; } -static inline void do_profile (unsigned long pc) -{ - if (prof_buffer && current->pid) { - extern int _stext; - pc -= (unsigned long) &_stext; - pc >>= prof_shift; - if (pc < prof_len) - ++prof_buffer[pc]; - else - /* - * Don't ignore out-of-bounds PC values silently, - * put them into the last histogram slot, so if - * present, they will show up as a sharp peak. - */ - ++prof_buffer[prof_len-1]; - } -} - /* * timer_interrupt() needs to keep up the real-time clock, * as well as call the "do_timer()" routine every clocktick @@ -63,9 +45,7 @@ static inline void do_profile (unsigned long pc) static irqreturn_t timer_interrupt(int irq, void *dummy, struct pt_regs * regs) { do_timer(regs); - - if (!user_mode(regs)) - do_profile(regs->pc); + profile_tick(CPU_PROFILING, regs); #ifdef CONFIG_HEARTBEAT /* use power LED as a heartbeat instead -- much more useful diff --git a/arch/m68knommu/Kconfig.debug b/arch/m68knommu/Kconfig.debug new file mode 100644 index 000000000..763c9aa0b --- /dev/null +++ b/arch/m68knommu/Kconfig.debug @@ -0,0 +1,42 @@ +menu "Kernel hacking" + +source "lib/Kconfig.debug" + +config FULLDEBUG + bool "Full Symbolic/Source Debugging support" + help + Enable debuging symbols on kernel build. + +config HIGHPROFILE + bool "Use fast second timer for profiling" + depends on COLDFIRE + help + Use a fast secondary clock to produce profiling information. + +config BOOTPARAM + bool 'Compiled-in Kernel Boot Parameter' + +config BOOTPARAM_STRING + string 'Kernel Boot Parameter' + default 'console=ttyS0,19200' + depends on BOOTPARAM + +config DUMPTOFLASH + bool "Panic/Dump to FLASH" + depends on COLDFIRE + help + Dump any panic of trap output into a flash memory segment + for later analysis. + +config NO_KERNEL_MSG + bool "Suppress Kernel BUG Messages" + help + Do not output any debug BUG messages within the kernel. + +config BDM_DISABLE + bool "Disable BDM signals" + depends on (EXPERIMENTAL && COLDFIRE) + help + Disable the ColdFire CPU's BDM signals. + +endmenu diff --git a/arch/m68knommu/kernel/Makefile b/arch/m68knommu/kernel/Makefile index 1a6a3600e..1c6cd1ab5 100644 --- a/arch/m68knommu/kernel/Makefile +++ b/arch/m68knommu/kernel/Makefile @@ -2,7 +2,7 @@ # Makefile for arch/m68knommu/kernel. # -extra-y := vmlinux.lds.s +extra-y := vmlinux.lds obj-y += dma.o entry.o init_task.o m68k_ksyms.o process.o ptrace.o semaphore.o \ setup.o signal.o syscalltable.o sys_m68k.o time.o traps.o diff --git a/arch/m68knommu/kernel/asm-offsets.c b/arch/m68knommu/kernel/asm-offsets.c index c803fe64e..02c56f538 100644 --- a/arch/m68knommu/kernel/asm-offsets.c +++ b/arch/m68knommu/kernel/asm-offsets.c @@ -12,9 +12,9 @@ #include #include #include +#include #include #include -#include #define DEFINE(sym, val) \ asm volatile("\n->" #sym " %0 " #val : : "i" (val)) diff --git a/arch/m68knommu/kernel/signal.c b/arch/m68knommu/kernel/signal.c index 3fb0d78f4..0d5891567 100644 --- a/arch/m68knommu/kernel/signal.c +++ b/arch/m68knommu/kernel/signal.c @@ -616,9 +616,7 @@ adjust_stack: return; give_sigsegv: - if (sig == SIGSEGV) - ka->sa.sa_handler = SIG_DFL; - force_sig(SIGSEGV, current); + force_sigsegv(sig, current); goto adjust_stack; } @@ -685,9 +683,7 @@ adjust_stack: return; give_sigsegv: - if (sig == SIGSEGV) - ka->sa.sa_handler = SIG_DFL; - force_sig(SIGSEGV, current); + force_sigsegv(sig, current); goto adjust_stack; } diff --git a/arch/m68knommu/kernel/time.c b/arch/m68knommu/kernel/time.c index 74308a533..e328f2810 100644 --- a/arch/m68knommu/kernel/time.c +++ b/arch/m68knommu/kernel/time.c @@ -41,24 +41,6 @@ static inline int set_rtc_mmss(unsigned long nowtime) return -1; } -static inline void do_profile (unsigned long pc) -{ - if (prof_buffer && current->pid) { - extern int _stext; - pc -= (unsigned long) &_stext; - pc >>= prof_shift; - if (pc < prof_len) - ++prof_buffer[pc]; - else - /* - * Don't ignore out-of-bounds PC values silently, - * put them into the last histogram slot, so if - * present, they will show up as a sharp peak. - */ - ++prof_buffer[prof_len-1]; - } -} - /* * timer_interrupt() needs to keep up the real-time clock, * as well as call the "do_timer()" routine every clocktick @@ -75,9 +57,8 @@ static irqreturn_t timer_interrupt(int irq, void *dummy, struct pt_regs * regs) write_seqlock(&xtime_lock); do_timer(regs); - - if (!user_mode(regs)) - do_profile(regs->pc); + if (current->pid) + profile_tick(CPU_PROFILING, regs); /* * If we have an externally synchronized Linux clock, then update diff --git a/arch/m68knommu/kernel/vmlinux.lds.S b/arch/m68knommu/kernel/vmlinux.lds.S index 4d8f716d7..87f3bc407 100644 --- a/arch/m68knommu/kernel/vmlinux.lds.S +++ b/arch/m68knommu/kernel/vmlinux.lds.S @@ -284,9 +284,6 @@ SECTIONS { __setup_start = .; *(.init.setup) __setup_end = .; - __start___param = .; - *(__param) - __stop___param = .; __initcall_start = .; *(.initcall1.init) *(.initcall2.init) diff --git a/arch/m68knommu/platform/5307/timers.c b/arch/m68knommu/platform/5307/timers.c index 99c7d9935..ef49596aa 100644 --- a/arch/m68knommu/platform/5307/timers.c +++ b/arch/m68knommu/platform/5307/timers.c @@ -110,17 +110,8 @@ void coldfire_profile_tick(int irq, void *dummy, struct pt_regs *regs) { /* Reset ColdFire timer2 */ mcf_proftp->ter = MCFTIMER_TER_CAP | MCFTIMER_TER_REF; - - if (!user_mode(regs)) { - if (prof_buffer && current->pid) { - extern int _stext; - unsigned long ip = instruction_pointer(regs); - ip -= (unsigned long) &_stext; - ip >>= prof_shift; - if (ip < prof_len) - prof_buffer[ip]++; - } - } + if (current->pid) + profile_tick(CPU_PROFILING, regs); } /***************************************************************************/ diff --git a/arch/mips/Kconfig.debug b/arch/mips/Kconfig.debug new file mode 100644 index 000000000..d3c5cc3b9 --- /dev/null +++ b/arch/mips/Kconfig.debug @@ -0,0 +1,76 @@ +menu "Kernel hacking" + +source "lib/Kconfig.debug" + +config CROSSCOMPILE + bool "Are you using a crosscompiler" + help + Say Y here if you are compiling the kernel on a different + architecture than the one it is intended to run on. + +config CMDLINE + string "Default kernel command string" + default "" + help + On some platforms, there is currently no way for the boot loader to + pass arguments to the kernel. For these platforms, you can supply + some command-line options at build time by entering them here. In + other cases you can specify kernel args so that you don't have + to set them up in board prom initialization routines. + +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 KGDB + bool "Remote GDB kernel debugging" + depends on DEBUG_KERNEL + select DEBUG_INFO + help + If you say Y here, it will be possible to remotely debug the MIPS + kernel using gdb. This enlarges your kernel image disk size by + several megabytes and requires a machine with more than 16 MB, + better 32 MB RAM to avoid excessive linking time. This is only + useful for kernel hackers. If unsure, say N. + +config GDB_CONSOLE + bool "Console output to GDB" + depends on KGDB + help + If you are using GDB for remote debugging over a serial port and + would like kernel messages to be formatted into GDB $O packets so + that GDB prints them as program output, say 'Y'. + +config SB1XXX_CORELIS + bool "Corelis Debugger" + depends on SIBYTE_SB1xxx_SOC + select DEBUG_INFO + help + Select compile flags that produce code that can be processed by the + Corelis mksym utility and UDB Emulator. + +config RUNTIME_DEBUG + bool "Enable run-time debugging" + depends on DEBUG_KERNEL + help + If you say Y here, some debugging macros will do run-time checking. + If you say N here, those macros will mostly turn to no-ops. See + include/asm-mips/debug.h for debuging macros. + If unsure, say N. + +config MIPS_UNCACHED + bool "Run uncached" + depends on DEBUG_KERNEL && !SMP && !SGI_IP27 + help + If you say Y here there kernel will disable all CPU caches. This will + reduce the system's performance dramatically but can help finding + otherwise hard to track bugs. It can also useful if you're doing + hardware debugging with a logic analyzer and need to see all traffic + on the bus. + +endmenu diff --git a/arch/mips/au1000/common/time.c b/arch/mips/au1000/common/time.c index f58f454cd..a72de9727 100644 --- a/arch/mips/au1000/common/time.c +++ b/arch/mips/au1000/common/time.c @@ -38,11 +38,11 @@ #include #include #include +#include #include #include #include -#include #include #include diff --git a/arch/mips/kernel/vmlinux.lds.S b/arch/mips/kernel/vmlinux.lds.S index 098cfaa23..e70684ee0 100644 --- a/arch/mips/kernel/vmlinux.lds.S +++ b/arch/mips/kernel/vmlinux.lds.S @@ -29,6 +29,7 @@ SECTIONS .text : { *(.text) SCHED_TEXT + LOCK_TEXT *(.fixup) *(.gnu.warning) } =0 @@ -96,9 +97,6 @@ SECTIONS __setup_start = .; .init.setup : { *(.init.setup) } __setup_end = .; - __start___param = .; - __param : { *(__param) } - __stop___param = .; .early_initcall.init : { __earlyinitcall_start = .; diff --git a/arch/mips/mips-boards/generic/time.c b/arch/mips/mips-boards/generic/time.c index 9348cd3e6..fe7fc1730 100644 --- a/arch/mips/mips-boards/generic/time.c +++ b/arch/mips/mips-boards/generic/time.c @@ -31,7 +31,6 @@ #include #include -#include #include #include #include diff --git a/arch/mips/mm/fault.c b/arch/mips/mm/fault.c index 820c3af08..ec8077c74 100644 --- a/arch/mips/mm/fault.c +++ b/arch/mips/mm/fault.c @@ -21,7 +21,6 @@ #include #include -#include #include #include #include diff --git a/arch/mips/pci/fixup-atlas.c b/arch/mips/pci/fixup-atlas.c index 4cbef3431..ae1930e67 100644 --- a/arch/mips/pci/fixup-atlas.c +++ b/arch/mips/pci/fixup-atlas.c @@ -60,13 +60,7 @@ static void atlas_saa9730_base_fixup (struct pci_dev *pdev) printk ("saa9730_base = %x\n", saa9730_base); } -#endif - +DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_PHILIPS, PCI_DEVICE_ID_PHILIPS_SAA9730, + atlas_saa9730_base_fixup); -struct pci_fixup pcibios_fixups[] __initdata = { -#ifdef CONFIG_KGDB - {PCI_FIXUP_HEADER, PCI_VENDOR_ID_PHILIPS, PCI_DEVICE_ID_PHILIPS_SAA9730, - atlas_saa9730_base_fixup}, #endif - { 0 } -}; diff --git a/arch/mips/pci/fixup-cobalt.c b/arch/mips/pci/fixup-cobalt.c index f72022a08..84c7e98de 100644 --- a/arch/mips/pci/fixup-cobalt.c +++ b/arch/mips/pci/fixup-cobalt.c @@ -41,6 +41,9 @@ static void qube_raq_via_bmIDE_fixup(struct pci_dev *dev) pci_write_config_byte(dev, PCI_CACHE_LINE_SIZE, 7); } +DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_82C586_1, + qube_raq_via_bmIDE_fixup); + static void qube_raq_galileo_fixup(struct pci_dev *dev) { unsigned short galileo_id; @@ -73,13 +76,8 @@ static void qube_raq_galileo_fixup(struct pci_dev *dev) } } -struct pci_fixup pcibios_fixups[] __initdata = { - {PCI_FIXUP_HEADER, PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_82C586_1, - qube_raq_via_bmIDE_fixup}, - {PCI_FIXUP_HEADER, PCI_VENDOR_ID_GALILEO, PCI_ANY_ID, - qube_raq_galileo_fixup}, - 0 -}; +DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_GALILEO, PCI_ANY_ID, + qube_raq_galileo_fixup); static char irq_tab_cobalt[] __initdata = { [COBALT_PCICONF_CPU] = 0, diff --git a/arch/mips/pci/fixup-ddb5074.c b/arch/mips/pci/fixup-ddb5074.c index 025285f9a..785bc03f8 100644 --- a/arch/mips/pci/fixup-ddb5074.c +++ b/arch/mips/pci/fixup-ddb5074.c @@ -17,8 +17,5 @@ static void ddb5074_fixup(struct pci_dev *dev) pci_write_config_byte(dev, 0x7e, t8); } -struct pci_fixup pcibios_fixups[] __initdata = { - { PCI_FIXUP_FINAL, PCI_VENDOR_ID_AL, PCI_DEVICE_ID_AL_M7101, - ddb5074_fixup }, - {0} -}; +DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_AL, PCI_DEVICE_ID_AL_M7101, + ddb5074_fixup); diff --git a/arch/mips/pci/fixup-ddb5477.c b/arch/mips/pci/fixup-ddb5477.c index 985441343..25bf49307 100644 --- a/arch/mips/pci/fixup-ddb5477.c +++ b/arch/mips/pci/fixup-ddb5477.c @@ -41,6 +41,11 @@ static void ddb5477_fixup(struct pci_dev *dev) pci_write_config_byte(dev, 0x41, old | 0xd0); } +DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_AL, PCI_DEVICE_ID_AL_M1533, + ddb5477_fixup); +DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_AL, PCI_DEVICE_ID_AL_M1535, + ddb5477_fixup); + /* * Fixup baseboard AMD chip so that tx does not underflow. * bcr_18 |= 0x0800 @@ -69,12 +74,5 @@ static void ddb5477_amd_lance_fixup(struct pci_dev *dev) outw(temp, ioaddr + PCNET32_WIO_BDP); } -struct pci_fixup pcibios_fixups[] __initdata = { - { PCI_FIXUP_FINAL, PCI_VENDOR_ID_AL, PCI_DEVICE_ID_AL_M1533, - ddb5477_fixup }, - { PCI_FIXUP_FINAL, PCI_VENDOR_ID_AL, PCI_DEVICE_ID_AL_M1535, - ddb5477_fixup }, - { PCI_FIXUP_FINAL, PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_LANCE, - ddb5477_amd_lance_fixup }, - {0} -}; +DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_LANCE, + ddb5477_amd_lance_fixup); diff --git a/arch/mips/pci/fixup-ip32.c b/arch/mips/pci/fixup-ip32.c index 90c67c35f..f64529b7b 100644 --- a/arch/mips/pci/fixup-ip32.c +++ b/arch/mips/pci/fixup-ip32.c @@ -44,7 +44,3 @@ int __init pcibios_map_irq(struct pci_dev *dev, u8 slot, u8 pin) { return irq_tab_mace[slot][pin]; } - -struct pci_fixup pcibios_fixups[] = { - {0} -}; diff --git a/arch/mips/pci/fixup-jaguar.c b/arch/mips/pci/fixup-jaguar.c index fa78b9b1f..29b6a4615 100644 --- a/arch/mips/pci/fixup-jaguar.c +++ b/arch/mips/pci/fixup-jaguar.c @@ -36,7 +36,3 @@ int __init pcibios_map_irq(struct pci_dev *dev, u8 slot, u8 pin) return 0; panic("Whooops in pcibios_map_irq"); } - -struct pci_fixup pcibios_fixups[] = { - {0} -}; diff --git a/arch/mips/pci/fixup-lasat.c b/arch/mips/pci/fixup-lasat.c index 49d33ff56..e2ab64ab8 100644 --- a/arch/mips/pci/fixup-lasat.c +++ b/arch/mips/pci/fixup-lasat.c @@ -4,7 +4,3 @@ void __init pcibios_fixup_irqs(void) { } - -struct pci_fixup pcibios_fixups[] __initdata = { - { 0 } -}; diff --git a/arch/mips/pci/fixup-malta.c b/arch/mips/pci/fixup-malta.c index 3345e1952..bcde09eea 100644 --- a/arch/mips/pci/fixup-malta.c +++ b/arch/mips/pci/fixup-malta.c @@ -79,6 +79,8 @@ static void __init malta_piix_func0_fixup(struct pci_dev *pdev) } } +DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82371AB_0, + malta_piix_func0_fixup); static void __init malta_piix_func1_fixup(struct pci_dev *pdev) { @@ -96,10 +98,5 @@ static void __init malta_piix_func1_fixup(struct pci_dev *pdev) } } -struct pci_fixup pcibios_fixups[] __initdata = { - {PCI_FIXUP_HEADER, PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82371AB_0, - malta_piix_func0_fixup}, - {PCI_FIXUP_HEADER, PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82371AB, - malta_piix_func1_fixup}, - { 0 } -}; +DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82371AB, + malta_piix_func1_fixup); diff --git a/arch/mips/pci/fixup-mpc30x.c b/arch/mips/pci/fixup-mpc30x.c index 1320c42af..8e5db12a4 100644 --- a/arch/mips/pci/fixup-mpc30x.c +++ b/arch/mips/pci/fixup-mpc30x.c @@ -42,7 +42,3 @@ int __init pcibios_map_irq(struct pci_dev *dev, u8 slot, u8 pin) return irq_tab_mpc30x[slot]; } - -struct pci_fixup pcibios_fixups[] __initdata = { - { .pass = 0, }, -}; diff --git a/arch/mips/pci/fixup-ocelot-c.c b/arch/mips/pci/fixup-ocelot-c.c index 0cc86eceb..9edc05950 100644 --- a/arch/mips/pci/fixup-ocelot-c.c +++ b/arch/mips/pci/fixup-ocelot-c.c @@ -33,7 +33,3 @@ int __init pcibios_map_irq(struct pci_dev *dev, u8 slot, u8 pin) return 0; panic("Whooops in pcibios_map_irq"); } - -struct pci_fixup pcibios_fixups[] = { - {0} -}; diff --git a/arch/mips/pci/fixup-ocelot-g.c b/arch/mips/pci/fixup-ocelot-g.c index 9a2cc8505..a79fc4a08 100644 --- a/arch/mips/pci/fixup-ocelot-g.c +++ b/arch/mips/pci/fixup-ocelot-g.c @@ -29,7 +29,3 @@ int __init pcibios_map_irq(struct pci_dev *dev, u8 slot, u8 pin) return -1; } - -struct pci_fixup pcibios_fixups[] = { - {0} -}; diff --git a/arch/mips/pci/fixup-sni.c b/arch/mips/pci/fixup-sni.c index 99067c76b..563fb2c89 100644 --- a/arch/mips/pci/fixup-sni.c +++ b/arch/mips/pci/fixup-sni.c @@ -82,7 +82,3 @@ int __init pcibios_map_irq(struct pci_dev *dev, u8 slot, u8 pin) return irq_tab_rm200[slot][pin]; } - -struct pci_fixup pcibios_fixups[] = { - {0} -}; diff --git a/arch/mips/pci/fixup-tb0219.c b/arch/mips/pci/fixup-tb0219.c index ca4d99fbe..4d62bce5b 100644 --- a/arch/mips/pci/fixup-tb0219.c +++ b/arch/mips/pci/fixup-tb0219.c @@ -58,7 +58,3 @@ int __init pcibios_map_irq(struct pci_dev *dev, u8 slot, u8 pin) return irq; } - -struct pci_fixup pcibios_fixups[] __initdata = { - { .pass = 0, }, -}; diff --git a/arch/mips/pci/pci-ip27.c b/arch/mips/pci/pci-ip27.c index 3c4183d45..b996766a2 100644 --- a/arch/mips/pci/pci-ip27.c +++ b/arch/mips/pci/pci-ip27.c @@ -329,6 +329,9 @@ static void __init pci_fixup_ioc3(struct pci_dev *d) pci_disable_swapping(d); } +DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_SGI, PCI_DEVICE_ID_SGI_IOC3, + pci_fixup_ioc3); + static void __init pci_fixup_isp1020(struct pci_dev *d) { struct bridge_controller *bc = BRIDGE_CONTROLLER(d->bus); @@ -353,6 +356,9 @@ static void __init pci_fixup_isp1020(struct pci_dev *d) pci_enable_swapping(d); } +DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_QLOGIC, PCI_DEVICE_ID_QLOGIC_ISP1020, + pci_fixup_isp1020); + static void __init pci_fixup_isp2x00(struct pci_dev *d) { struct bridge_controller *bc = BRIDGE_CONTROLLER(d->bus); @@ -427,14 +433,7 @@ static void __init pci_fixup_isp2x00(struct pci_dev *d) /*d->resource[1].flags |= 1; */ } -struct pci_fixup pcibios_fixups[] = { - {PCI_FIXUP_HEADER, PCI_VENDOR_ID_SGI, PCI_DEVICE_ID_SGI_IOC3, - pci_fixup_ioc3}, - {PCI_FIXUP_HEADER, PCI_VENDOR_ID_QLOGIC, PCI_DEVICE_ID_QLOGIC_ISP1020, - pci_fixup_isp1020}, - {PCI_FIXUP_HEADER, PCI_VENDOR_ID_QLOGIC, PCI_DEVICE_ID_QLOGIC_ISP2100, - pci_fixup_isp2x00}, - {PCI_FIXUP_HEADER, PCI_VENDOR_ID_QLOGIC, PCI_DEVICE_ID_QLOGIC_ISP2200, - pci_fixup_isp2x00}, - {0} -}; +DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_QLOGIC, PCI_DEVICE_ID_QLOGIC_ISP2100, + pci_fixup_isp2x00); +DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_QLOGIC, PCI_DEVICE_ID_QLOGIC_ISP2200, + pci_fixup_isp2x00); diff --git a/arch/mips/pci/pci-sb1250.c b/arch/mips/pci/pci-sb1250.c index 18e06ecd4..8c7c385e4 100644 --- a/arch/mips/pci/pci-sb1250.c +++ b/arch/mips/pci/pci-sb1250.c @@ -279,7 +279,3 @@ static int __init sb1250_pcibios_init(void) return 0; } arch_initcall(sb1250_pcibios_init); - -struct pci_fixup pcibios_fixups[] = { - {0} -}; diff --git a/arch/mips/pmc-sierra/yosemite/ht.c b/arch/mips/pmc-sierra/yosemite/ht.c index b9a12e90a..95bbfd013 100644 --- a/arch/mips/pmc-sierra/yosemite/ht.c +++ b/arch/mips/pmc-sierra/yosemite/ht.c @@ -414,11 +414,6 @@ struct pci_ops titan_pci_ops = { titan_ht_config_write_dword }; - -struct pci_fixup pcibios_fixups[] = { - {0} -}; - void __init pcibios_fixup_bus(struct pci_bus *c) { titan_ht_pcibios_fixup_bus(c); diff --git a/arch/mips/sgi-ip27/ip27-memory.c b/arch/mips/sgi-ip27/ip27-memory.c index 6c6df7b5b..e3cdb9582 100644 --- a/arch/mips/sgi-ip27/ip27-memory.c +++ b/arch/mips/sgi-ip27/ip27-memory.c @@ -225,7 +225,7 @@ void __init paging_init(void) pfn_t end_pfn = node_getmaxclick(node) + 1; zones_size[ZONE_DMA] = end_pfn - start_pfn; - free_area_init_node(node, NODE_DATA(node), NULL, + free_area_init_node(node, NODE_DATA(node), zones_size, start_pfn, NULL); if (end_pfn > max_low_pfn) diff --git a/arch/parisc/Kconfig.debug b/arch/parisc/Kconfig.debug new file mode 100644 index 000000000..8caaed187 --- /dev/null +++ b/arch/parisc/Kconfig.debug @@ -0,0 +1,14 @@ +menu "Kernel hacking" + +source "lib/Kconfig.debug" + +config DEBUG_RWLOCK + bool "Read-write spinlock debugging" + depends on DEBUG_KERNEL && SMP + help + If you say Y here then read-write lock processing will count how many + times it has tried to get the lock and issue an error message after + too many attempts. If you suspect a rwlock problem or a kernel + hacker asks for this option then say Y. Otherwise say N. + +endmenu diff --git a/arch/parisc/kernel/pci.c b/arch/parisc/kernel/pci.c index 937ab23ca..3cb08a4a5 100644 --- a/arch/parisc/kernel/pci.c +++ b/arch/parisc/kernel/pci.c @@ -146,15 +146,6 @@ char *pcibios_setup(char *str) return str; } -/* Used in drivers/pci/quirks.c */ -struct pci_fixup pcibios_fixups[] = { -#ifdef CONFIG_SUPERIO - { PCI_FIXUP_HEADER, PCI_VENDOR_ID_NS, PCI_DEVICE_ID_NS_87415, superio_fixup_pci }, -#endif - { 0 } -}; - - /* * Called by pci_set_master() - a driver interface. * diff --git a/arch/parisc/kernel/time.c b/arch/parisc/kernel/time.c index 949998ffb..8f4ad0cf2 100644 --- a/arch/parisc/kernel/time.c +++ b/arch/parisc/kernel/time.c @@ -47,44 +47,6 @@ static long halftick; extern void smp_do_timer(struct pt_regs *regs); #endif -static inline void -parisc_do_profile(struct pt_regs *regs) -{ - unsigned long pc = regs->iaoq[0]; -#if 0 - extern unsigned long prof_cpu_mask; -#endif - extern char _stext; - - profile_hook(regs); - - if (user_mode(regs)) - return; - - if (!prof_buffer) - return; - -#if 0 - /* FIXME: when we have irq affinity to cpu, we need to - * only look at the cpus specified in this mask - */ - - if (!((1 << smp_processor_id()) & prof_cpu_mask)) - return; -#endif - - pc -= (unsigned long) &_stext; - pc >>= prof_shift; - /* - * Don't ignore out-of-bounds PC values silently, - * put them into the last histogram slot, so if - * present, they will show up as a sharp peak. - */ - if (pc > prof_len - 1) - pc = prof_len - 1; - atomic_inc((atomic_t *)&prof_buffer[pc]); -} - irqreturn_t timer_interrupt(int irq, void *dev_id, struct pt_regs *regs) { long now; @@ -92,7 +54,7 @@ irqreturn_t timer_interrupt(int irq, void *dev_id, struct pt_regs *regs) int nticks; int cpu = smp_processor_id(); - parisc_do_profile(regs); + profile_tick(CPU_PROFILING, regs); now = mfctl(16); /* initialize next_tick to time at last clocktick */ diff --git a/arch/parisc/lib/debuglocks.c b/arch/parisc/lib/debuglocks.c index 7e79f1bad..5166a9378 100644 --- a/arch/parisc/lib/debuglocks.c +++ b/arch/parisc/lib/debuglocks.c @@ -25,8 +25,8 @@ #include #include #include +#include /* in_interrupt() */ #include -#include /* in_interrupt() */ #undef INIT_STUCK #define INIT_STUCK 1L << 30 diff --git a/arch/ppc/Kconfig.debug b/arch/ppc/Kconfig.debug new file mode 100644 index 000000000..0df281455 --- /dev/null +++ b/arch/ppc/Kconfig.debug @@ -0,0 +1,84 @@ +menu "Kernel hacking" + +source "lib/Kconfig.debug" + +config KGDB + bool "Include kgdb kernel debugger" + depends on DEBUG_KERNEL && (BROKEN || PPC_GEN550 || 4xx) + select DEBUG_INFO + help + Include in-kernel hooks for kgdb, the Linux kernel source level + debugger. See for more information. + Unless you are intending to debug the kernel, say N here. + +choice + prompt "Serial Port" + depends on KGDB + default KGDB_TTYS1 + +config KGDB_TTYS0 + bool "ttyS0" + +config KGDB_TTYS1 + bool "ttyS1" + +config KGDB_TTYS2 + bool "ttyS2" + +config KGDB_TTYS3 + bool "ttyS3" + +endchoice + +config KGDB_CONSOLE + bool "Enable serial console thru kgdb port" + depends on KGDB && 8xx || CPM2 + help + If you enable this, all serial console messages will be sent + over the gdb stub. + If unsure, say N. + +config XMON + bool "Include xmon kernel debugger" + depends on DEBUG_KERNEL + help + Include in-kernel hooks for the xmon kernel monitor/debugger. + Unless you are intending to debug the kernel, say N here. + +config BDI_SWITCH + bool "Include BDI-2000 user context switcher" + depends on DEBUG_KERNEL + help + Include in-kernel support for the Abatron BDI2000 debugger. + Unless you are intending to debug the kernel with one of these + machines, say N here. + +config SCHEDSTATS + bool "Collect scheduler statistics" + depends on DEBUG_KERNEL && PROC_FS + help + If you say Y here, additional code will be inserted into the + scheduler and related routines to collect statistics about + scheduler behavior and provide them in /proc/schedstat. These + stats may be useful for both tuning and debugging the scheduler + If you aren't debugging the scheduler or trying to tune a specific + application, you can say N to avoid the very slight overhead + this adds. + +config BOOTX_TEXT + bool "Support for early boot text console (BootX or OpenFirmware only)" + depends PPC_OF + help + Say Y here to see progress messages from the boot firmware in text + mode. Requires either BootX or Open Firmware. + +config SERIAL_TEXT_DEBUG + bool "Support for early boot texts over serial port" + depends on 4xx || GT64260 || LOPEC || PPLUS || PRPMC800 || PPC_GEN550 || PPC_MPC52xx + +config PPC_OCP + bool + depends on IBM_OCP || FSL_OCP + default y + +endmenu diff --git a/arch/ppc/amiga/config.c b/arch/ppc/amiga/config.c index cc93c9eb3..af881d745 100644 --- a/arch/ppc/amiga/config.c +++ b/arch/ppc/amiga/config.c @@ -423,9 +423,6 @@ void __init config_amiga(void) mach_floppy_setup = amiga_floppy_setup; #endif mach_reset = amiga_reset; -#ifdef CONFIG_DUMMY_CONSOLE - conswitchp = &dummy_con; -#endif #ifdef CONFIG_HEARTBEAT mach_heartbeat = amiga_heartbeat; #endif diff --git a/arch/ppc/boot/common/ns16550.c b/arch/ppc/boot/common/ns16550.c index 8eb07a63f..9017c547a 100644 --- a/arch/ppc/boot/common/ns16550.c +++ b/arch/ppc/boot/common/ns16550.c @@ -5,14 +5,14 @@ #include #include #include -#include #include #include +#include "nonstdio.h" +#include "serial.h" + #define SERIAL_BAUD 9600 -extern void outb(int port, unsigned char val); -extern unsigned char inb(int port); extern unsigned long ISA_io; static struct serial_state rs_table[RS_TABLE_SIZE] = { diff --git a/arch/ppc/boot/common/serial_stub.c b/arch/ppc/boot/common/serial_stub.c index 2dc176ed0..03dfaa01f 100644 --- a/arch/ppc/boot/common/serial_stub.c +++ b/arch/ppc/boot/common/serial_stub.c @@ -11,11 +11,6 @@ * is" without any warranty of any kind, whether express or implied. */ -void __attribute__ ((weak)) -serial_fixups(void) -{ -} - unsigned long __attribute__ ((weak)) serial_init(int chan, void *ignored) { diff --git a/arch/ppc/boot/common/util.S b/arch/ppc/boot/common/util.S index 93771e298..7c4fb8f57 100644 --- a/arch/ppc/boot/common/util.S +++ b/arch/ppc/boot/common/util.S @@ -41,7 +41,7 @@ disable_6xx_mmu: /* Test for a 601 */ mfpvr r10 srwi r10,r10,16 - cmpi 0,r10,1 /* 601 ? */ + cmpwi 0,r10,1 /* 601 ? */ beq .clearbats_601 /* Clear BATs */ @@ -117,9 +117,9 @@ _setup_L2CR: /* Wait for the invalidation to complete */ mfspr r8,PVR srwi r8,r8,16 - cmpli cr0,r8,0x8000 /* 7450 */ - cmpli cr1,r8,0x8001 /* 7455 */ - cmpli cr2,r8,0x8002 /* 7457 */ + cmplwi cr0,r8,0x8000 /* 7450 */ + cmplwi cr1,r8,0x8001 /* 7455 */ + cmplwi cr2,r8,0x8002 /* 7457 */ cror 4*cr0+eq,4*cr0+eq,4*cr1+eq /* Now test if any are true. */ cror 4*cr0+eq,4*cr0+eq,4*cr2+eq bne 2f @@ -190,7 +190,7 @@ timebase_period_ns: udelay: mfspr r4,PVR srwi r4,r4,16 - cmpi 0,r4,1 /* 601 ? */ + cmpwi 0,r4,1 /* 601 ? */ bne .udelay_not_601 00: li r0,86 /* Instructions / microsecond? */ mtctr r0 @@ -213,16 +213,16 @@ udelay: 1: mftbu r5 mftb r6 mftbu r7 - cmp 0,r5,r7 + cmpw 0,r5,r7 bne 1b /* Get [synced] base time */ addc r9,r6,r4 /* Compute end time */ addze r8,r5 2: mftbu r5 - cmp 0,r5,r8 + cmpw 0,r5,r8 blt 2b bgt 3f mftb r6 - cmp 0,r6,r9 + cmpw 0,r6,r9 blt 2b 3: blr diff --git a/arch/ppc/boot/include/nonstdio.h b/arch/ppc/boot/include/nonstdio.h index 7a0f027f9..f2b5526fa 100644 --- a/arch/ppc/boot/include/nonstdio.h +++ b/arch/ppc/boot/include/nonstdio.h @@ -30,3 +30,5 @@ extern void puthex(unsigned long val); extern void puts(const char *); extern void udelay(long delay); extern unsigned char inb(int port); +extern void board_isa_init(void); +extern void ISA_init(unsigned long base); diff --git a/arch/ppc/boot/include/serial.h b/arch/ppc/boot/include/serial.h new file mode 100644 index 000000000..d710eabb4 --- /dev/null +++ b/arch/ppc/boot/include/serial.h @@ -0,0 +1,46 @@ +/* + * A really private header file for the (dumb) serial driver in arch/ppc/boot + * + * Shamelessly taken from include/linux/serialP.h: + * + * Copyright (C) 1997 by Theodore Ts'o. + * + * Redistribution of this file is permitted under the terms of the GNU + * Public License (GPL) + */ + +#ifndef _PPC_BOOT_SERIALP_H +#define _PPC_BOOT_SERIALP_H + +/* + * This is our internal structure for each serial port's state. + * + * Many fields are paralleled by the structure used by the serial_struct + * structure. + * + * Given that this is how SERIAL_PORT_DFNS are done, and that we need + * to use a few of their fields, we need to have our own copy of it. + */ +struct serial_state { + int magic; + int baud_base; + unsigned long port; + int irq; + int flags; + int hub6; + int type; + int line; + int revision; /* Chip revision (950) */ + int xmit_fifo_size; + int custom_divisor; + int count; + u8 *iomem_base; + u16 iomem_reg_shift; + unsigned short close_delay; + unsigned short closing_wait; /* time to wait before closing */ + unsigned long icount; + int io_type; + void *info; + void *dev; +}; +#endif /* _PPC_BOOT_SERIAL_H */ diff --git a/arch/ppc/boot/lib/Makefile b/arch/ppc/boot/lib/Makefile index 97ef7d19e..9d088a566 100644 --- a/arch/ppc/boot/lib/Makefile +++ b/arch/ppc/boot/lib/Makefile @@ -4,5 +4,7 @@ CFLAGS_kbd.o += -Idrivers/char -lib-y := zlib.o div64.o +lib-y := $(addprefix ../../../../lib/zlib_inflate/, \ + infblock.o infcodes.o inffast.o inflate.o inftrees.o infutil.o) +lib-y += div64.o lib-$(CONFIG_VGA_CONSOLE) += vreset.o kbd.o diff --git a/arch/ppc/boot/openfirmware/coffmain.c b/arch/ppc/boot/openfirmware/coffmain.c index 94c23c0c9..04ba9d57e 100644 --- a/arch/ppc/boot/openfirmware/coffmain.c +++ b/arch/ppc/boot/openfirmware/coffmain.c @@ -12,7 +12,6 @@ #include "nonstdio.h" #include "of1275.h" -#include "zlib.h" /* Passed from the linker */ extern char __image_begin, __image_end; diff --git a/arch/ppc/boot/openfirmware/common.c b/arch/ppc/boot/openfirmware/common.c index 71195067c..9e6952781 100644 --- a/arch/ppc/boot/openfirmware/common.c +++ b/arch/ppc/boot/openfirmware/common.c @@ -7,10 +7,10 @@ * 2 of the License, or (at your option) any later version. */ -#include "zlib.h" #include "nonstdio.h" #include "of1275.h" #include +#include #include #include @@ -30,12 +30,11 @@ struct memchunk { static struct memchunk *freechunks; -static void *zalloc(void *x, unsigned items, unsigned size) +static void *zalloc(unsigned size) { void *p; struct memchunk **mpp, *mp; - size *= items; size = (size + 7) & -8; heap_use += size; if (heap_use > heap_max) @@ -57,74 +56,57 @@ static void *zalloc(void *x, unsigned items, unsigned size) return p; } -static void zfree(void *x, void *addr, unsigned nb) -{ - struct memchunk *mp = addr; - - nb = (nb + 7) & -8; - heap_use -= nb; - if (avail_ram == addr + nb) { - avail_ram = addr; - return; - } - mp->size = nb; - mp->next = freechunks; - freechunks = mp; -} - #define HEAD_CRC 2 #define EXTRA_FIELD 4 #define ORIG_NAME 8 #define COMMENT 0x10 #define RESERVED 0xe0 -#define DEFLATED 8 - void gunzip(void *dst, int dstlen, unsigned char *src, int *lenp) { - z_stream s; - int r, i, flags; - - /* skip header */ - i = 10; - flags = src[3]; - if (src[2] != DEFLATED || (flags & RESERVED) != 0) { - printf("bad gzipped data\n\r"); - exit(); - } - if ((flags & EXTRA_FIELD) != 0) - i = 12 + src[10] + (src[11] << 8); - if ((flags & ORIG_NAME) != 0) - while (src[i++] != 0) - ; - if ((flags & COMMENT) != 0) - while (src[i++] != 0) - ; - if ((flags & HEAD_CRC) != 0) - i += 2; - if (i >= *lenp) { - printf("gunzip: ran out of data in header\n\r"); - exit(); - } + z_stream s; + int r, i, flags; + + /* skip header */ + i = 10; + flags = src[3]; + if (src[2] != Z_DEFLATED || (flags & RESERVED) != 0) { + printf("bad gzipped data\n\r"); + exit(); + } + if ((flags & EXTRA_FIELD) != 0) + i = 12 + src[10] + (src[11] << 8); + if ((flags & ORIG_NAME) != 0) + while (src[i++] != 0) + ; + if ((flags & COMMENT) != 0) + while (src[i++] != 0) + ; + if ((flags & HEAD_CRC) != 0) + i += 2; + if (i >= *lenp) { + printf("gunzip: ran out of data in header\n\r"); + exit(); + } - s.zalloc = zalloc; - s.zfree = zfree; - r = inflateInit2(&s, -MAX_WBITS); - if (r != Z_OK) { - printf("inflateInit2 returned %d\n\r", r); - exit(); - } - s.next_in = src + i; - s.avail_in = *lenp - i; - s.next_out = dst; - s.avail_out = dstlen; - r = inflate(&s, Z_FINISH); - if (r != Z_OK && r != Z_STREAM_END) { - printf("inflate returned %d msg: %s\n\r", r, s.msg); - exit(); - } - *lenp = s.next_out - (unsigned char *) dst; - inflateEnd(&s); + /* Initialize ourself. */ + s.workspace = zalloc(zlib_inflate_workspacesize()); + r = zlib_inflateInit2(&s, -MAX_WBITS); + if (r != Z_OK) { + printf("zlib_inflateInit2 returned %d\n\r", r); + exit(); + } + s.next_in = src + i; + s.avail_in = *lenp - i; + s.next_out = dst; + s.avail_out = dstlen; + r = zlib_inflate(&s, Z_FINISH); + if (r != Z_OK && r != Z_STREAM_END) { + printf("inflate returned %d msg: %s\n\r", r, s.msg); + exit(); + } + *lenp = s.next_out - (unsigned char *) dst; + zlib_inflateEnd(&s); } /* Make a bi_rec in OF. We need to be passed a name for BI_BOOTLOADER_ID, diff --git a/arch/ppc/boot/openfirmware/misc.S b/arch/ppc/boot/openfirmware/misc.S index 844c0bca2..ab9e897ca 100644 --- a/arch/ppc/boot/openfirmware/misc.S +++ b/arch/ppc/boot/openfirmware/misc.S @@ -16,7 +16,7 @@ setup_bats: mfpvr 5 rlwinm 5,5,16,16,31 /* r3 = 1 for 601, 4 for 604 */ - cmpi 0,5,1 + cmpwi 0,5,1 li 0,0 bne 4f mtibatl 3,0 /* invalidate BAT first */ diff --git a/arch/ppc/boot/simple/chrpmap.c b/arch/ppc/boot/simple/chrpmap.c new file mode 100644 index 000000000..14d9e05d9 --- /dev/null +++ b/arch/ppc/boot/simple/chrpmap.c @@ -0,0 +1,12 @@ +/* + * 2004 (C) IBM. 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 + +void board_isa_init(void) +{ + ISA_init(0xFE000000); +} diff --git a/arch/ppc/boot/simple/misc-embedded.c b/arch/ppc/boot/simple/misc-embedded.c index 58e2a92e8..8615d5f22 100644 --- a/arch/ppc/boot/simple/misc-embedded.c +++ b/arch/ppc/boot/simple/misc-embedded.c @@ -22,7 +22,6 @@ #endif #include "nonstdio.h" -#include "zlib.h" /* The linker tells us where the image is. */ extern char __image_begin, __image_end; @@ -72,6 +71,14 @@ extern void flush_instruction_cache(void); extern void gunzip(void *, int, unsigned char *, int *); extern void embed_config(bd_t **bp); +/* Weak function for boards which don't need to build the + * board info struct because they are using PPCBoot/U-Boot. + */ +void __attribute__ ((weak)) +embed_config(bd_t **bdp) +{ +} + unsigned long load_kernel(unsigned long load_addr, int num_words, unsigned long cksum, bd_t *bp) { diff --git a/arch/ppc/boot/simple/misc-prep.c b/arch/ppc/boot/simple/misc-prep.c index da29deca4..75380ac41 100644 --- a/arch/ppc/boot/simple/misc-prep.c +++ b/arch/ppc/boot/simple/misc-prep.c @@ -88,6 +88,7 @@ load_kernel(unsigned long load_addr, int num_words, unsigned long cksum, ofinit(OFW_interface); } + board_isa_init(); #if defined(CONFIG_VGA_CONSOLE) vga_init((unsigned char *)0xC0000000); #endif /* CONFIG_VGA_CONSOLE */ diff --git a/arch/ppc/boot/simple/misc-spruce.c b/arch/ppc/boot/simple/misc-spruce.c index fa181ffb7..d012c3927 100644 --- a/arch/ppc/boot/simple/misc-spruce.c +++ b/arch/ppc/boot/simple/misc-spruce.c @@ -26,7 +26,6 @@ extern unsigned long decompress_kernel(unsigned long load_addr, int num_words, /* Define some important locations of the Spruce. */ #define SPRUCE_PCI_CONFIG_ADDR 0xfec00000 #define SPRUCE_PCI_CONFIG_DATA 0xfec00004 -#define SPRUCE_ISA_IO_BASE 0xf8000000 /* PCI configuration space access routines. */ unsigned int *pci_config_address = (unsigned int *)SPRUCE_PCI_CONFIG_ADDR; @@ -86,8 +85,6 @@ void cpc700_pcibios_write_config_dword(unsigned char bus, unsigned char dev_fn, out_le32((unsigned *)pci_config_data, val); } -unsigned long isa_io_base = SPRUCE_ISA_IO_BASE; - #define PCNET32_WIO_RDP 0x10 #define PCNET32_WIO_RAP 0x12 #define PCNET32_WIO_RESET 0x14 diff --git a/arch/ppc/boot/simple/pibs.c b/arch/ppc/boot/simple/pibs.c new file mode 100644 index 000000000..9ce8847d2 --- /dev/null +++ b/arch/ppc/boot/simple/pibs.c @@ -0,0 +1,101 @@ +/* + * 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 +#include +#include +#include +#include +#include + +extern unsigned long decompress_kernel(unsigned long load_addr, int num_words, + unsigned long cksum); + +/* We need to make sure that this is before the images to ensure + * that it's in a mapped location. - Tom */ +bd_t hold_resid_buf __attribute__ ((__section__ (".data.boot"))); +bd_t *hold_residual = &hold_resid_buf; + +/* String functions lifted from lib/vsprintf.c and lib/ctype.c */ +unsigned char _ctype[] = { +_C,_C,_C,_C,_C,_C,_C,_C, /* 0-7 */ +_C,_C|_S,_C|_S,_C|_S,_C|_S,_C|_S,_C,_C, /* 8-15 */ +_C,_C,_C,_C,_C,_C,_C,_C, /* 16-23 */ +_C,_C,_C,_C,_C,_C,_C,_C, /* 24-31 */ +_S|_SP,_P,_P,_P,_P,_P,_P,_P, /* 32-39 */ +_P,_P,_P,_P,_P,_P,_P,_P, /* 40-47 */ +_D,_D,_D,_D,_D,_D,_D,_D, /* 48-55 */ +_D,_D,_P,_P,_P,_P,_P,_P, /* 56-63 */ +_P,_U|_X,_U|_X,_U|_X,_U|_X,_U|_X,_U|_X,_U, /* 64-71 */ +_U,_U,_U,_U,_U,_U,_U,_U, /* 72-79 */ +_U,_U,_U,_U,_U,_U,_U,_U, /* 80-87 */ +_U,_U,_U,_P,_P,_P,_P,_P, /* 88-95 */ +_P,_L|_X,_L|_X,_L|_X,_L|_X,_L|_X,_L|_X,_L, /* 96-103 */ +_L,_L,_L,_L,_L,_L,_L,_L, /* 104-111 */ +_L,_L,_L,_L,_L,_L,_L,_L, /* 112-119 */ +_L,_L,_L,_P,_P,_P,_P,_C, /* 120-127 */ +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 128-143 */ +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 144-159 */ +_S|_SP,_P,_P,_P,_P,_P,_P,_P,_P,_P,_P,_P,_P,_P,_P,_P, /* 160-175 */ +_P,_P,_P,_P,_P,_P,_P,_P,_P,_P,_P,_P,_P,_P,_P,_P, /* 176-191 */ +_U,_U,_U,_U,_U,_U,_U,_U,_U,_U,_U,_U,_U,_U,_U,_U, /* 192-207 */ +_U,_U,_U,_U,_U,_U,_U,_P,_U,_U,_U,_U,_U,_U,_U,_L, /* 208-223 */ +_L,_L,_L,_L,_L,_L,_L,_L,_L,_L,_L,_L,_L,_L,_L,_L, /* 224-239 */ +_L,_L,_L,_L,_L,_L,_L,_P,_L,_L,_L,_L,_L,_L,_L,_L}; /* 240-255 */ + +/** + * simple_strtoull - convert a string to an unsigned long long + * @cp: The start of the string + * @endp: A pointer to the end of the parsed string will be placed here + * @base: The number base to use + */ +unsigned long long simple_strtoull(const char *cp,char **endp,unsigned int base) +{ + unsigned long long result = 0,value; + + if (!base) { + base = 10; + if (*cp == '0') { + base = 8; + cp++; + if ((toupper(*cp) == 'X') && isxdigit(cp[1])) { + cp++; + base = 16; + } + } + } else if (base == 16) { + if (cp[0] == '0' && toupper(cp[1]) == 'X') + cp += 2; + } + while (isxdigit(*cp) && (value = isdigit(*cp) ? *cp-'0' : (islower(*cp) + ? toupper(*cp) : *cp)-'A'+10) < base) { + result = result*base + value; + cp++; + } + if (endp) + *endp = (char *)cp; + return result; +} + +void * +load_kernel(unsigned long load_addr, int num_words, unsigned long cksum, + void *ign1, void *ign2) +{ + unsigned long long mac64; + + decompress_kernel(load_addr, num_words, cksum); + + mac64 = simple_strtoull((char *)OCOTEA_PIBS_MAC_BASE, 0, 16); + memcpy(hold_residual->bi_enetaddr, (char *)&mac64+2, 6); + mac64 = simple_strtoull((char *)(OCOTEA_PIBS_MAC_BASE+OCOTEA_PIBS_MAC_OFFSET), 0, 16); + memcpy(hold_residual->bi_enet1addr, (char *)&mac64+2, 6); + mac64 = simple_strtoull((char *)(OCOTEA_PIBS_MAC_BASE+OCOTEA_PIBS_MAC_OFFSET*2), 0, 16); + memcpy(hold_residual->bi_enet2addr, (char *)&mac64+2, 6); + mac64 = simple_strtoull((char *)(OCOTEA_PIBS_MAC_BASE+OCOTEA_PIBS_MAC_OFFSET*3), 0, 16); + memcpy(hold_residual->bi_enet3addr, (char *)&mac64+2, 6); + return (void *)hold_residual; +} diff --git a/arch/ppc/boot/simple/prepmap.c b/arch/ppc/boot/simple/prepmap.c new file mode 100644 index 000000000..c871a4db6 --- /dev/null +++ b/arch/ppc/boot/simple/prepmap.c @@ -0,0 +1,12 @@ +/* + * 2004 (C) IBM. 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 + +void board_isa_init(void) +{ + ISA_init(0x80000000); +} diff --git a/arch/ppc/boot/simple/relocate.S b/arch/ppc/boot/simple/relocate.S index e57d9673e..555a216cc 100644 --- a/arch/ppc/boot/simple/relocate.S +++ b/arch/ppc/boot/simple/relocate.S @@ -50,7 +50,7 @@ relocate: * Check if we need to relocate ourselves to the link addr or were * we loaded there to begin with. */ - cmp cr0,r3,r4 + cmpw cr0,r3,r4 beq start_ldr /* If 0, we don't need to relocate */ /* Move this code somewhere safe. This is max(load + size, end) @@ -122,7 +122,7 @@ do_relocate: GETSYM(r4, start) mr r3,r8 /* Get the load addr */ - cmp cr0,r4,r3 /* If we need to copy from the end, do so */ + cmpw cr0,r4,r3 /* If we need to copy from the end, do so */ bgt do_relocate_from_end do_relocate_from_start: @@ -165,7 +165,7 @@ start_ldr: subi r4,r4,4 li r0,0 50: stwu r0,4(r3) - cmp cr0,r3,r4 + cmpw cr0,r3,r4 bne 50b 90: mr r9,r1 /* Save old stack pointer (in case it matters) */ lis r1,.stack@h diff --git a/arch/ppc/boot/utils/mkbugboot.c b/arch/ppc/boot/utils/mkbugboot.c index 8158d8c6f..f6629a28e 100644 --- a/arch/ppc/boot/utils/mkbugboot.c +++ b/arch/ppc/boot/utils/mkbugboot.c @@ -1,5 +1,5 @@ /* - * arch/ppc/pp3boot/mkbugboot.c + * arch/ppc/boot/utils/mkbugboot.c * * Makes a Motorola PPCBUG ROM bootable image which can be flashed * into one of the FLASH banks on a Motorola PowerPlus board. @@ -21,6 +21,11 @@ #include #include #include +#ifdef __sun__ +#include +#else +#include +#endif #ifdef __i386__ #define cpu_to_be32(x) le32_to_cpu(x) @@ -49,11 +54,6 @@ unsigned short le16_to_cpu(unsigned short x) /* size of read buffer */ #define SIZE 0x1000 -/* typedef long int32_t; */ -typedef unsigned long uint32_t; -typedef unsigned short uint16_t; -typedef unsigned char uint8_t; - /* PPCBUG ROM boot header */ typedef struct bug_boot_header { uint8_t magic_word[4]; /* "BOOT" */ diff --git a/arch/ppc/boot/utils/mktree.c b/arch/ppc/boot/utils/mktree.c index 7ef98c077..2be22e28f 100644 --- a/arch/ppc/boot/utils/mktree.c +++ b/arch/ppc/boot/utils/mktree.c @@ -15,7 +15,11 @@ #include #include #include +#ifdef __sun__ +#include +#else #include +#endif /* This gets tacked on the front of the image. There are also a few * bytes allocated after the _start label used by the boot rom (see diff --git a/arch/ppc/configs/mvme5100_defconfig b/arch/ppc/configs/mvme5100_defconfig index 6e1a38d72..46776b9c1 100644 --- a/arch/ppc/configs/mvme5100_defconfig +++ b/arch/ppc/configs/mvme5100_defconfig @@ -1,26 +1,48 @@ # # Automatically generated make config: don't edit +# Linux kernel version: 2.6.9-rc2 +# Wed Sep 22 09:53:26 2004 # CONFIG_MMU=y CONFIG_RWSEM_XCHGADD_ALGORITHM=y CONFIG_HAVE_DEC_LOCK=y +CONFIG_PPC=y +CONFIG_PPC32=y +CONFIG_GENERIC_NVRAM=y +CONFIG_GENERIC_IOMAP=y # # Code maturity level options # CONFIG_EXPERIMENTAL=y +CONFIG_CLEAN_COMPILE=y +CONFIG_BROKEN_ON_SMP=y # # General setup # +CONFIG_LOCALVERSION="" CONFIG_SWAP=y CONFIG_SYSVIPC=y +# CONFIG_POSIX_MQUEUE is not set # CONFIG_BSD_PROCESS_ACCT is not set CONFIG_SYSCTL=y +# CONFIG_AUDIT is not set CONFIG_LOG_BUF_SHIFT=14 -# CONFIG_EMBEDDED is not set +# CONFIG_HOTPLUG is not set +# CONFIG_IKCONFIG is not set +CONFIG_EMBEDDED=y +CONFIG_KALLSYMS=y +# CONFIG_KALLSYMS_EXTRA_PASS is not set CONFIG_FUTEX=y CONFIG_EPOLL=y +CONFIG_IOSCHED_NOOP=y +CONFIG_IOSCHED_AS=y +CONFIG_IOSCHED_DEADLINE=y +CONFIG_IOSCHED_CFQ=y +# CONFIG_CC_OPTIMIZE_FOR_SIZE is not set +CONFIG_SHMEM=y +# CONFIG_TINY_SHMEM is not set # # Loadable module support @@ -33,24 +55,26 @@ CONFIG_OBSOLETE_MODPARM=y CONFIG_KMOD=y # -# Platform support +# Processor # -CONFIG_PPC=y -CONFIG_PPC32=y CONFIG_6xx=y # CONFIG_40x is not set +# CONFIG_44x is not set # CONFIG_POWER3 is not set +# CONFIG_POWER4 is not set # CONFIG_8xx is not set +# CONFIG_E500 is not set +CONFIG_ALTIVEC=y +# CONFIG_TAU is not set +# CONFIG_CPU_FREQ is not set +CONFIG_PPC_STD_MMU=y # -# IBM 4xx options +# Platform options # -# CONFIG_8260 is not set -CONFIG_GENERIC_ISA_DMA=y -CONFIG_PPC_STD_MMU=y # CONFIG_PPC_MULTIPLATFORM is not set # CONFIG_APUS is not set -# CONFIG_WILLOW_2 is not set +# CONFIG_WILLOW is not set # CONFIG_PCORE is not set # CONFIG_POWERPMC250 is not set # CONFIG_EV64260 is not set @@ -66,34 +90,30 @@ CONFIG_MVME5100=y # CONFIG_K2 is not set # CONFIG_PAL4 is not set # CONFIG_GEMINI is not set +# CONFIG_EST8260 is not set +# CONFIG_SBC82xx is not set +# CONFIG_SBS8260 is not set +# CONFIG_RPX8260 is not set +# CONFIG_TQM8260 is not set +# CONFIG_ADS8272 is not set +# CONFIG_LITE5200 is not set # CONFIG_MVME5100_IPMC761_PRESENT is not set # CONFIG_SMP is not set # CONFIG_PREEMPT is not set -CONFIG_ALTIVEC=y -# CONFIG_TAU is not set -# CONFIG_CPU_FREQ is not set +# CONFIG_HIGHMEM is not set +CONFIG_BINFMT_ELF=y +# CONFIG_BINFMT_MISC is not set +CONFIG_CMDLINE_BOOL=y +CONFIG_CMDLINE="ip=on" # -# General setup +# Bus options # -# CONFIG_HIGHMEM is not set +CONFIG_GENERIC_ISA_DMA=y CONFIG_PCI=y CONFIG_PCI_DOMAINS=y -CONFIG_KCORE_ELF=y -CONFIG_BINFMT_ELF=y -CONFIG_KERNEL_ELF=y -# CONFIG_BINFMT_MISC is not set # CONFIG_PCI_LEGACY_PROC is not set # CONFIG_PCI_NAMES is not set -# CONFIG_HOTPLUG is not set - -# -# Parallel port support -# -# CONFIG_PARPORT is not set -# CONFIG_PPC601_SYNC_FIX is not set -CONFIG_CMDLINE_BOOL=y -CONFIG_CMDLINE="ip=on" # # Advanced setup @@ -109,15 +129,29 @@ CONFIG_KERNEL_START=0xc0000000 CONFIG_TASK_SIZE=0x80000000 CONFIG_BOOT_LOAD=0x00800000 +# +# Device Drivers +# + +# +# Generic Driver Options +# +CONFIG_STANDALONE=y +CONFIG_PREVENT_FIRMWARE_BUILD=y + # # Memory Technology Devices (MTD) # # CONFIG_MTD is not set +# +# Parallel port support +# +# CONFIG_PARPORT is not set + # # Plug and Play support # -# CONFIG_PNP is not set # # Block devices @@ -129,46 +163,46 @@ CONFIG_BOOT_LOAD=0x00800000 # CONFIG_BLK_DEV_UMEM is not set # CONFIG_BLK_DEV_LOOP is not set # CONFIG_BLK_DEV_NBD is not set +# CONFIG_BLK_DEV_SX8 is not set CONFIG_BLK_DEV_RAM=y CONFIG_BLK_DEV_RAM_SIZE=4096 CONFIG_BLK_DEV_INITRD=y +# CONFIG_LBD is not set # -# Multi-device support (RAID and LVM) -# -# CONFIG_MD is not set - -# -# ATA/IDE/MFM/RLL support +# ATA/ATAPI/MFM/RLL support # CONFIG_IDE=y - -# -# IDE, ATA and ATAPI Block devices -# CONFIG_BLK_DEV_IDE=y # # Please see Documentation/ide.txt for help/info on IDE drives # -# CONFIG_BLK_DEV_HD is not set +# CONFIG_BLK_DEV_IDE_SATA is not set CONFIG_BLK_DEV_IDEDISK=y # CONFIG_IDEDISK_MULTI_MODE is not set -# CONFIG_IDEDISK_STROKE is not set # CONFIG_BLK_DEV_IDECD is not set +# CONFIG_BLK_DEV_IDETAPE is not set # CONFIG_BLK_DEV_IDEFLOPPY is not set # CONFIG_BLK_DEV_IDESCSI is not set # CONFIG_IDE_TASK_IOCTL is not set +# CONFIG_IDE_TASKFILE_IO is not set # # IDE chipset support/bugfixes # +CONFIG_IDE_GENERIC=y # CONFIG_BLK_DEV_IDEPCI is not set +# CONFIG_IDE_ARM is not set +# CONFIG_BLK_DEV_IDEDMA is not set +# CONFIG_IDEDMA_AUTO is not set +# CONFIG_BLK_DEV_HD is not set # -# SCSI support +# SCSI device support # CONFIG_SCSI=y +CONFIG_SCSI_PROC_FS=y # # SCSI support type (disk, tape, CD-ROM) @@ -184,64 +218,70 @@ CONFIG_BLK_DEV_SR=y # Some SCSI devices (e.g. CD jukebox) support multiple LUNs # # CONFIG_SCSI_MULTI_LUN is not set -# CONFIG_SCSI_REPORT_LUNS is not set # CONFIG_SCSI_CONSTANTS is not set # CONFIG_SCSI_LOGGING is not set +# +# SCSI Transport Attributes +# +CONFIG_SCSI_SPI_ATTRS=y +# CONFIG_SCSI_FC_ATTRS is not set + # # SCSI low-level drivers # # CONFIG_BLK_DEV_3W_XXXX_RAID is not set +# CONFIG_SCSI_3W_9XXX is not set # CONFIG_SCSI_ACARD is not set # CONFIG_SCSI_AACRAID is not set # CONFIG_SCSI_AIC7XXX is not set # CONFIG_SCSI_AIC7XXX_OLD is not set # CONFIG_SCSI_AIC79XX is not set # CONFIG_SCSI_DPT_I2O is not set -# CONFIG_SCSI_ADVANSYS is not set -# CONFIG_SCSI_IN2000 is not set -# CONFIG_SCSI_AM53C974 is not set -# CONFIG_SCSI_MEGARAID is not set +# CONFIG_MEGARAID_NEWGEN is not set +# CONFIG_MEGARAID_LEGACY is not set +# CONFIG_SCSI_SATA is not set # CONFIG_SCSI_BUSLOGIC is not set -# CONFIG_SCSI_CPQFCTS is not set # CONFIG_SCSI_DMX3191D is not set # CONFIG_SCSI_EATA is not set # CONFIG_SCSI_EATA_PIO is not set # CONFIG_SCSI_FUTURE_DOMAIN is not set # CONFIG_SCSI_GDTH is not set -# CONFIG_SCSI_GENERIC_NCR5380 is not set -# CONFIG_SCSI_GENERIC_NCR5380_MMIO is not set -# CONFIG_SCSI_INITIO is not set +# CONFIG_SCSI_IPS is not set # CONFIG_SCSI_INIA100 is not set -# CONFIG_SCSI_NCR53C7xx is not set -# CONFIG_SCSI_SYM53C8XX_2 is not set -# CONFIG_SCSI_NCR53C8XX is not set -CONFIG_SCSI_SYM53C8XX=y -CONFIG_SCSI_NCR53C8XX_DEFAULT_TAGS=8 -CONFIG_SCSI_NCR53C8XX_MAX_TAGS=32 -CONFIG_SCSI_NCR53C8XX_SYNC=20 -# CONFIG_SCSI_NCR53C8XX_PROFILE is not set -# CONFIG_SCSI_NCR53C8XX_IOMAPPED is not set -# CONFIG_SCSI_NCR53C8XX_PQS_PDS is not set -# CONFIG_SCSI_NCR53C8XX_SYMBIOS_COMPAT is not set -# CONFIG_SCSI_PCI2000 is not set -# CONFIG_SCSI_PCI2220I is not set +CONFIG_SCSI_SYM53C8XX_2=y +CONFIG_SCSI_SYM53C8XX_DMA_ADDRESSING_MODE=0 +CONFIG_SCSI_SYM53C8XX_DEFAULT_TAGS=8 +CONFIG_SCSI_SYM53C8XX_MAX_TAGS=32 +# CONFIG_SCSI_SYM53C8XX_IOMAPPED is not set +# CONFIG_SCSI_IPR is not set # CONFIG_SCSI_QLOGIC_ISP is not set # CONFIG_SCSI_QLOGIC_FC is not set # CONFIG_SCSI_QLOGIC_1280 is not set +CONFIG_SCSI_QLA2XXX=y +# CONFIG_SCSI_QLA21XX is not set +# CONFIG_SCSI_QLA22XX is not set +# CONFIG_SCSI_QLA2300 is not set +# CONFIG_SCSI_QLA2322 is not set +# CONFIG_SCSI_QLA6312 is not set +# CONFIG_SCSI_QLA6322 is not set # CONFIG_SCSI_DC395x is not set # CONFIG_SCSI_DC390T is not set -# CONFIG_SCSI_U14_34F is not set # CONFIG_SCSI_NSP32 is not set # CONFIG_SCSI_DEBUG is not set +# +# Multi-device support (RAID and LVM) +# +# CONFIG_MD is not set + # # Fusion MPT device support # # CONFIG_FUSION is not set # -# IEEE 1394 (FireWire) support (EXPERIMENTAL) +# IEEE 1394 (FireWire) support # # CONFIG_IEEE1394 is not set @@ -250,6 +290,10 @@ CONFIG_SCSI_NCR53C8XX_SYNC=20 # # CONFIG_I2O is not set +# +# Macintosh device drivers +# + # # Networking support # @@ -261,86 +305,90 @@ CONFIG_NET=y CONFIG_PACKET=y # CONFIG_PACKET_MMAP is not set # CONFIG_NETLINK_DEV is not set -CONFIG_NETFILTER=y -# CONFIG_NETFILTER_DEBUG is not set 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_PNP=y -CONFIG_IP_PNP_DHCP=y -# CONFIG_IP_PNP_BOOTP is not set +# CONFIG_IP_PNP_DHCP is not set +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_IP_MROUTE is not set # CONFIG_ARPD is not set -# CONFIG_INET_ECN 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_TUNNEL=m + +# +# IP: Virtual Server Configuration +# +# CONFIG_IP_VS is not set +# CONFIG_IPV6 is not set +CONFIG_NETFILTER=y +# CONFIG_NETFILTER_DEBUG is not set # # IP: Netfilter Configuration # CONFIG_IP_NF_CONNTRACK=m +# CONFIG_IP_NF_CT_ACCT is not set +# CONFIG_IP_NF_CT_PROTO_SCTP is not set CONFIG_IP_NF_FTP=m CONFIG_IP_NF_IRC=m -# CONFIG_IP_NF_TFTP is not set -# CONFIG_IP_NF_AMANDA is not set +CONFIG_IP_NF_TFTP=m +CONFIG_IP_NF_AMANDA=m # CONFIG_IP_NF_QUEUE is not set CONFIG_IP_NF_IPTABLES=m -CONFIG_IP_NF_MATCH_LIMIT=m -CONFIG_IP_NF_MATCH_MAC=m -CONFIG_IP_NF_MATCH_PKTTYPE=m -CONFIG_IP_NF_MATCH_MARK=m -CONFIG_IP_NF_MATCH_MULTIPORT=m -CONFIG_IP_NF_MATCH_TOS=m -CONFIG_IP_NF_MATCH_ECN=m -CONFIG_IP_NF_MATCH_DSCP=m -CONFIG_IP_NF_MATCH_AH_ESP=m -CONFIG_IP_NF_MATCH_LENGTH=m -CONFIG_IP_NF_MATCH_TTL=m -CONFIG_IP_NF_MATCH_TCPMSS=m +# CONFIG_IP_NF_MATCH_LIMIT is not set +# CONFIG_IP_NF_MATCH_IPRANGE is not set +# CONFIG_IP_NF_MATCH_MAC is not set +# CONFIG_IP_NF_MATCH_PKTTYPE is not set +# CONFIG_IP_NF_MATCH_MARK is not set +# CONFIG_IP_NF_MATCH_MULTIPORT is not set +# CONFIG_IP_NF_MATCH_TOS is not set +# CONFIG_IP_NF_MATCH_RECENT is not set +# CONFIG_IP_NF_MATCH_ECN is not set +# CONFIG_IP_NF_MATCH_DSCP is not set +# CONFIG_IP_NF_MATCH_AH_ESP is not set +# CONFIG_IP_NF_MATCH_LENGTH is not set +# CONFIG_IP_NF_MATCH_TTL is not set +# CONFIG_IP_NF_MATCH_TCPMSS is not set CONFIG_IP_NF_MATCH_HELPER=m -CONFIG_IP_NF_MATCH_STATE=m -CONFIG_IP_NF_MATCH_CONNTRACK=m -CONFIG_IP_NF_MATCH_UNCLEAN=m -CONFIG_IP_NF_MATCH_OWNER=m -CONFIG_IP_NF_FILTER=m -CONFIG_IP_NF_TARGET_REJECT=m -CONFIG_IP_NF_TARGET_MIRROR=m -CONFIG_IP_NF_NAT=m -CONFIG_IP_NF_NAT_NEEDED=y -CONFIG_IP_NF_TARGET_MASQUERADE=m -CONFIG_IP_NF_TARGET_REDIRECT=m -# CONFIG_IP_NF_NAT_LOCAL is not set -# CONFIG_IP_NF_NAT_SNMP_BASIC is not set -CONFIG_IP_NF_NAT_IRC=m -CONFIG_IP_NF_NAT_FTP=m -# CONFIG_IP_NF_MANGLE is not set +# CONFIG_IP_NF_MATCH_STATE is not set +# CONFIG_IP_NF_MATCH_CONNTRACK is not set +# CONFIG_IP_NF_MATCH_OWNER is not set +# CONFIG_IP_NF_MATCH_ADDRTYPE is not set +# CONFIG_IP_NF_MATCH_REALM is not set +# CONFIG_IP_NF_MATCH_SCTP is not set +# CONFIG_IP_NF_FILTER is not set # CONFIG_IP_NF_TARGET_LOG is not set -CONFIG_IP_NF_TARGET_ULOG=m -CONFIG_IP_NF_TARGET_TCPMSS=m -CONFIG_IP_NF_ARPTABLES=m -CONFIG_IP_NF_ARPFILTER=m -CONFIG_IP_NF_COMPAT_IPCHAINS=m +# CONFIG_IP_NF_TARGET_ULOG is not set +# CONFIG_IP_NF_TARGET_TCPMSS is not set +# CONFIG_IP_NF_NAT is not set +# CONFIG_IP_NF_MANGLE is not set +# CONFIG_IP_NF_RAW is not set +# CONFIG_IP_NF_ARPTABLES is not set +# CONFIG_IP_NF_COMPAT_IPCHAINS is not set # CONFIG_IP_NF_COMPAT_IPFWADM is not set -# CONFIG_IPV6 is not set -# CONFIG_XFRM_USER is not set +CONFIG_XFRM=y +CONFIG_XFRM_USER=y # # SCTP Configuration (EXPERIMENTAL) # -CONFIG_IPV6_SCTP__=y # CONFIG_IP_SCTP is not set # CONFIG_ATM is not set +# CONFIG_BRIDGE is not set # CONFIG_VLAN_8021Q is not set -# CONFIG_LLC is not set # CONFIG_DECNET is not set -# CONFIG_BRIDGE 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_NET_DIVERT is not set @@ -352,29 +400,33 @@ CONFIG_IPV6_SCTP__=y # QoS and/or fair queueing # # CONFIG_NET_SCHED is not set +# CONFIG_NET_CLS_ROUTE is not set # # Network testing # # CONFIG_NET_PKTGEN is not set +# CONFIG_NETPOLL is not set +# CONFIG_NET_POLL_CONTROLLER is not set +# CONFIG_HAMRADIO is not set +# CONFIG_IRDA is not set +# CONFIG_BT 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 # # ARCnet devices # # CONFIG_ARCNET is not set -# CONFIG_DUMMY is not set -# CONFIG_BONDING is not set -# CONFIG_EQUALIZER is not set -# CONFIG_TUN is not set -# CONFIG_ETHERTAP is not set # # Ethernet (10 or 100Mbit) # CONFIG_NET_ETHERNET=y CONFIG_MII=y -# CONFIG_OAKNET is not set # CONFIG_HAPPYMEAL is not set # CONFIG_SUNGEM is not set # CONFIG_NET_VENDOR_3COM is not set @@ -382,17 +434,26 @@ CONFIG_MII=y # # Tulip family network device support # -# CONFIG_NET_TULIP is not set +CONFIG_NET_TULIP=y +# CONFIG_DE2104X is not set +CONFIG_TULIP=y +# CONFIG_TULIP_MWI is not set +# CONFIG_TULIP_MMIO is not set +# CONFIG_TULIP_NAPI is not set +# CONFIG_DE4X5 is not set +# CONFIG_WINBOND_840 is not set +# CONFIG_DM9102 is not set # CONFIG_HP100 is not set CONFIG_NET_PCI=y # CONFIG_PCNET32 is not set # CONFIG_AMD8111_ETH is not set # CONFIG_ADAPTEC_STARFIRE is not set # CONFIG_B44 is not set +# CONFIG_FORCEDETH is not set # CONFIG_DGRS is not set -CONFIG_EEPRO100=y -# CONFIG_EEPRO100_PIO is not set -# CONFIG_E100 is not set +# CONFIG_EEPRO100 is not set +CONFIG_E100=y +# CONFIG_E100_NAPI is not set # CONFIG_FEALNX is not set # CONFIG_NATSEMI is not set # CONFIG_NE2K_PCI is not set @@ -403,6 +464,7 @@ CONFIG_EEPRO100=y # CONFIG_SUNDANCE is not set # CONFIG_TLAN is not set # CONFIG_VIA_RHINE is not set +# CONFIG_VIA_VELOCITY is not set # # Ethernet (1000 Mbit) @@ -421,52 +483,39 @@ CONFIG_EEPRO100=y # Ethernet (10000 Mbit) # # CONFIG_IXGB is not set -# CONFIG_FDDI is not set -# CONFIG_HIPPI is not set -# CONFIG_PPP is not set -# CONFIG_SLIP is not set +# CONFIG_S2IO is not set # -# Wireless LAN (non-hamradio) +# Token Ring devices # -# CONFIG_NET_RADIO is not set +# CONFIG_TR is not set # -# Token Ring devices (depends on LLC=y) +# Wireless LAN (non-hamradio) # -# CONFIG_NET_FC is not set -# CONFIG_RCPCI is not set -# CONFIG_SHAPER is not set +# CONFIG_NET_RADIO is not set # # Wan interfaces # # CONFIG_WAN is not set - -# -# Amateur Radio support -# -# CONFIG_HAMRADIO is not set - -# -# IrDA (infrared) support -# -# CONFIG_IRDA is not set +# CONFIG_FDDI is not set +# CONFIG_HIPPI is not set +# CONFIG_PPP is not set +# CONFIG_SLIP is not set +# CONFIG_NET_FC is not set +# CONFIG_SHAPER is not set +# CONFIG_NETCONSOLE is not set # # ISDN subsystem # -# CONFIG_ISDN_BOOL is not set +# CONFIG_ISDN is not set # -# Graphics support +# Telephony Support # -# CONFIG_FB is not set - -# -# Old CD-ROM drivers (not SCSI, not IDE) -# -# CONFIG_CD_NO_IDESCSI is not set +# CONFIG_PHONE is not set # # Input device support @@ -483,18 +532,16 @@ CONFIG_EEPRO100=y # CONFIG_GAMEPORT is not set CONFIG_SOUND_GAMEPORT=y # CONFIG_SERIO is not set +# CONFIG_SERIO_I8042 is not set # # Input Device Drivers # -# -# Macintosh device drivers -# - # # Character devices # +# CONFIG_VT is not set # CONFIG_SERIAL_NONSTANDARD is not set # @@ -502,6 +549,7 @@ CONFIG_SOUND_GAMEPORT=y # CONFIG_SERIAL_8250=y CONFIG_SERIAL_8250_CONSOLE=y +CONFIG_SERIAL_8250_NR_UARTS=4 # CONFIG_SERIAL_8250_EXTENDED is not set # @@ -510,27 +558,8 @@ CONFIG_SERIAL_8250_CONSOLE=y CONFIG_SERIAL_CORE=y CONFIG_SERIAL_CORE_CONSOLE=y CONFIG_UNIX98_PTYS=y -CONFIG_UNIX98_PTY_COUNT=256 - -# -# I2C support -# -# CONFIG_I2C is not set - -# -# I2C Hardware Sensors Mainboard support -# - -# -# I2C Hardware Sensors Chip support -# -# CONFIG_I2C_SENSOR is not set - -# -# Mice -# -# CONFIG_BUSMOUSE is not set -# CONFIG_QIC02_TAPE is not set +CONFIG_LEGACY_PTYS=y +CONFIG_LEGACY_PTY_COUNT=256 # # IPMI @@ -551,11 +580,23 @@ CONFIG_GEN_RTC=y # # Ftape, the floppy tape device driver # -# CONFIG_FTAPE is not set # CONFIG_AGP is not set # CONFIG_DRM is not set # CONFIG_RAW_DRIVER is not set -# CONFIG_HANGCHECK_TIMER is not set + +# +# I2C support +# +# CONFIG_I2C is not set + +# +# Dallas's 1-wire bus +# +# CONFIG_W1 is not set + +# +# Misc devices +# # # Multimedia devices @@ -567,6 +608,26 @@ CONFIG_GEN_RTC=y # # CONFIG_DVB is not set +# +# Graphics support +# +# CONFIG_FB is not set + +# +# Sound +# +# CONFIG_SOUND is not set + +# +# USB support +# +# CONFIG_USB is not set + +# +# USB Gadget Support +# +# CONFIG_USB_GADGET is not set + # # File systems # @@ -592,17 +653,20 @@ CONFIG_EXT2_FS=y # # DOS/FAT/NT Filesystems # -# CONFIG_FAT_FS is not set +# 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_KCORE=y +CONFIG_SYSFS=y # CONFIG_DEVFS_FS is not set -CONFIG_DEVPTS_FS=y # CONFIG_DEVPTS_FS_XATTR is not set CONFIG_TMPFS=y +# CONFIG_HUGETLB_PAGE is not set CONFIG_RAMFS=y # @@ -611,6 +675,7 @@ CONFIG_RAMFS=y # 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 @@ -625,19 +690,21 @@ CONFIG_RAMFS=y # Network File Systems # CONFIG_NFS_FS=y -# CONFIG_NFS_V3 is not set +CONFIG_NFS_V3=y # CONFIG_NFS_V4 is not set +# CONFIG_NFS_DIRECTIO is not set # CONFIG_NFSD is not set CONFIG_ROOT_NFS=y CONFIG_LOCKD=y +CONFIG_LOCKD_V4=y # CONFIG_EXPORTFS is not set CONFIG_SUNRPC=y -# CONFIG_SUNRPC_GSS 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_INTERMEZZO_FS is not set # CONFIG_AFS_FS is not set # @@ -647,31 +714,26 @@ CONFIG_SUNRPC=y CONFIG_MSDOS_PARTITION=y # -# Sound +# Native Language Support # -# CONFIG_SOUND is not set +# CONFIG_NLS is not set # -# USB support -# -# CONFIG_USB is not set -# CONFIG_USB_GADGET is not set - -# -# Bluetooth support +# Library routines # -# CONFIG_BT is not set +# CONFIG_CRC_CCITT is not set +CONFIG_CRC32=y +# CONFIG_LIBCRC32C is not set # -# Library routines +# Profiling support # -# CONFIG_CRC32 is not set +# CONFIG_PROFILING is not set # # Kernel hacking # # CONFIG_DEBUG_KERNEL is not set -# CONFIG_KALLSYMS is not set # # Security options diff --git a/arch/ppc/kernel/cpu_setup_6xx.S b/arch/ppc/kernel/cpu_setup_6xx.S index ee0705c6e..e42049f3e 100644 --- a/arch/ppc/kernel/cpu_setup_6xx.S +++ b/arch/ppc/kernel/cpu_setup_6xx.S @@ -172,9 +172,9 @@ END_FTR_SECTION_IFCLR(CPU_FTR_NO_DPM) setup_750cx: mfspr r10, SPRN_HID1 rlwinm r10,r10,4,28,31 - cmpi cr0,r10,7 - cmpi cr1,r10,9 - cmpi cr2,r10,11 + cmpwi cr0,r10,7 + cmpwi cr1,r10,9 + cmpwi cr2,r10,11 cror 4*cr0+eq,4*cr0+eq,4*cr1+eq cror 4*cr0+eq,4*cr0+eq,4*cr2+eq bnelr @@ -218,10 +218,10 @@ setup_745x_specifics: /* All of the bits we have to set..... */ - ori r11,r11,HID0_SGE | HID0_FOLD | HID0_BHTE | HID0_LRSTK + ori r11,r11,HID0_SGE | HID0_FOLD | HID0_BHTE | HID0_LRSTK | HID0_BTIC BEGIN_FTR_SECTION - ori r11,r11,HID0_BTIC -END_FTR_SECTION_IFCLR(CPU_FTR_NO_BTIC) + xori r11,r11,HID0_BTIC +END_FTR_SECTION_IFSET(CPU_FTR_NO_BTIC) BEGIN_FTR_SECTION oris r11,r11,HID0_DPM@h /* enable dynamic power mgmt */ END_FTR_SECTION_IFCLR(CPU_FTR_NO_DPM) @@ -287,12 +287,12 @@ _GLOBAL(__save_cpu_setup) /* Now deal with CPU type dependent registers */ mfspr r3,PVR srwi r3,r3,16 - cmpli cr0,r3,0x8000 /* 7450 */ - cmpli cr1,r3,0x000c /* 7400 */ - cmpli cr2,r3,0x800c /* 7410 */ - cmpli cr3,r3,0x8001 /* 7455 */ - cmpli cr4,r3,0x8002 /* 7457 */ - cmpli cr5,r3,0x7000 /* 750FX */ + cmplwi cr0,r3,0x8000 /* 7450 */ + cmplwi cr1,r3,0x000c /* 7400 */ + cmplwi cr2,r3,0x800c /* 7410 */ + cmplwi cr3,r3,0x8001 /* 7455 */ + cmplwi cr4,r3,0x8002 /* 7457 */ + cmplwi cr5,r3,0x7000 /* 750FX */ /* cr1 is 7400 || 7410 */ cror 4*cr1+eq,4*cr1+eq,4*cr2+eq /* cr0 is 74xx */ @@ -323,7 +323,7 @@ _GLOBAL(__save_cpu_setup) /* If rev 2.x, backup HID2 */ mfspr r3,PVR andi. r3,r3,0xff00 - cmpi cr0,r3,0x0200 + cmpwi cr0,r3,0x0200 bne 1f mfspr r4,SPRN_HID2 stw r4,CS_HID2(r5) @@ -354,12 +354,12 @@ _GLOBAL(__restore_cpu_setup) /* Now deal with CPU type dependent registers */ mfspr r3,PVR srwi r3,r3,16 - cmpli cr0,r3,0x8000 /* 7450 */ - cmpli cr1,r3,0x000c /* 7400 */ - cmpli cr2,r3,0x800c /* 7410 */ - cmpli cr3,r3,0x8001 /* 7455 */ - cmpli cr4,r3,0x8002 /* 7457 */ - cmpli cr5,r3,0x7000 /* 750FX */ + cmplwi cr0,r3,0x8000 /* 7450 */ + cmplwi cr1,r3,0x000c /* 7400 */ + cmplwi cr2,r3,0x800c /* 7410 */ + cmplwi cr3,r3,0x8001 /* 7455 */ + cmplwi cr4,r3,0x8002 /* 7457 */ + cmplwi cr5,r3,0x7000 /* 750FX */ /* cr1 is 7400 || 7410 */ cror 4*cr1+eq,4*cr1+eq,4*cr2+eq /* cr0 is 74xx */ @@ -412,7 +412,7 @@ _GLOBAL(__restore_cpu_setup) /* If rev 2.x, restore HID2 with low voltage bit cleared */ mfspr r3,PVR andi. r3,r3,0xff00 - cmpi cr0,r3,0x0200 + cmpwi cr0,r3,0x0200 bne 4f lwz r4,CS_HID2(r5) rlwinm r4,r4,0,19,17 @@ -426,7 +426,7 @@ _GLOBAL(__restore_cpu_setup) mftbl r5 3: mftbl r6 sub r6,r6,r5 - cmpli cr0,r6,10000 + cmplwi cr0,r6,10000 ble 3b /* Setup final PLL */ mtspr SPRN_HID1,r4 diff --git a/arch/ppc/kernel/cpu_setup_power4.S b/arch/ppc/kernel/cpu_setup_power4.S index 6a6ee985c..f2ea1a990 100644 --- a/arch/ppc/kernel/cpu_setup_power4.S +++ b/arch/ppc/kernel/cpu_setup_power4.S @@ -112,7 +112,9 @@ _GLOBAL(__save_cpu_setup) /* We only deal with 970 for now */ mfspr r0,SPRN_PVR srwi r0,r0,16 - cmpwi r0,0x39 + cmpwi cr0,r0,0x39 + cmpwi cr1,r0,0x3c + cror 4*cr0+eq,4*cr0+eq,4*cr1+eq bne 1f /* Save HID0,1,4 and 5 */ @@ -144,7 +146,9 @@ _GLOBAL(__restore_cpu_setup) /* We only deal with 970 for now */ mfspr r0,SPRN_PVR srwi r0,r0,16 - cmpwi r0,0x39 + cmpwi cr0,r0,0x39 + cmpwi cr1,r0,0x3c + cror 4*cr0+eq,4*cr0+eq,4*cr1+eq bne 1f /* Clear interrupt prefix */ diff --git a/arch/ppc/kernel/dma-mapping.c b/arch/ppc/kernel/dma-mapping.c index 54f9a3e75..334b8bfdc 100644 --- a/arch/ppc/kernel/dma-mapping.c +++ b/arch/ppc/kernel/dma-mapping.c @@ -41,11 +41,11 @@ #include #include #include +#include #include #include #include -#include #include #include #include @@ -381,6 +381,7 @@ void __dma_sync(void *vaddr, size_t size, int direction) break; } } +EXPORT_SYMBOL(__dma_sync); #ifdef CONFIG_HIGHMEM /* @@ -438,3 +439,4 @@ void __dma_sync_page(struct page *page, unsigned long offset, __dma_sync((void *)start, size, direction); #endif } +EXPORT_SYMBOL(__dma_sync_page); diff --git a/arch/ppc/kernel/head_booke.h b/arch/ppc/kernel/head_booke.h new file mode 100644 index 000000000..6f54039c8 --- /dev/null +++ b/arch/ppc/kernel/head_booke.h @@ -0,0 +1,240 @@ +#ifndef __HEAD_BOOKE_H__ +#define __HEAD_BOOKE_H__ + +/* + * Macros used for common Book-e exception handling + */ + +#define SET_IVOR(vector_number, vector_label) \ + li r26,vector_label@l; \ + mtspr SPRN_IVOR##vector_number,r26; \ + sync + +#define NORMAL_EXCEPTION_PROLOG \ + mtspr SPRN_SPRG0,r10; /* save two registers to work with */\ + mtspr SPRN_SPRG1,r11; \ + mtspr SPRN_SPRG4W,r1; \ + mfcr r10; /* save CR in r10 for now */\ + mfspr r11,SPRN_SRR1; /* check whether user or kernel */\ + andi. r11,r11,MSR_PR; \ + beq 1f; \ + mfspr r1,SPRG3; /* if from user, start at top of */\ + lwz r1,THREAD_INFO-THREAD(r1); /* this thread's kernel stack */\ + addi r1,r1,THREAD_SIZE; \ +1: subi r1,r1,INT_FRAME_SIZE; /* Allocate an exception frame */\ + tophys(r11,r1); \ + stw r10,_CCR(r11); /* save various registers */\ + stw r12,GPR12(r11); \ + stw r9,GPR9(r11); \ + mfspr r10,SPRG0; \ + stw r10,GPR10(r11); \ + mfspr r12,SPRG1; \ + stw r12,GPR11(r11); \ + mflr r10; \ + stw r10,_LINK(r11); \ + mfspr r10,SPRG4R; \ + mfspr r12,SRR0; \ + stw r10,GPR1(r11); \ + mfspr r9,SRR1; \ + stw r10,0(r11); \ + rlwinm r9,r9,0,14,12; /* clear MSR_WE (necessary?) */\ + stw r0,GPR0(r11); \ + SAVE_4GPRS(3, r11); \ + SAVE_2GPRS(7, r11) + +/* + * Exception prolog for critical exceptions. This is a little different + * from the normal exception prolog above since a critical exception + * can potentially occur at any point during normal exception processing. + * Thus we cannot use the same SPRG registers as the normal prolog above. + * Instead we use a couple of words of memory at low physical addresses. + * This is OK since we don't support SMP on these processors. For Book E + * processors, we also have a reserved register (SPRG2) that is only used + * in critical exceptions so we can free up a GPR to use as the base for + * indirect access to the critical exception save area. This is necessary + * since the MMU is always on and the save area is offset from KERNELBASE. + */ +#define CRITICAL_EXCEPTION_PROLOG \ + mtspr SPRG2,r8; /* SPRG2 only used in criticals */ \ + lis r8,crit_save@ha; \ + stw r10,crit_r10@l(r8); \ + stw r11,crit_r11@l(r8); \ + mfspr r10,SPRG0; \ + stw r10,crit_sprg0@l(r8); \ + mfspr r10,SPRG1; \ + stw r10,crit_sprg1@l(r8); \ + mfspr r10,SPRG4R; \ + stw r10,crit_sprg4@l(r8); \ + mfspr r10,SPRG5R; \ + stw r10,crit_sprg5@l(r8); \ + mfspr r10,SPRG7R; \ + stw r10,crit_sprg7@l(r8); \ + mfspr r10,SPRN_PID; \ + stw r10,crit_pid@l(r8); \ + mfspr r10,SRR0; \ + stw r10,crit_srr0@l(r8); \ + mfspr r10,SRR1; \ + stw r10,crit_srr1@l(r8); \ + mfspr r8,SPRG2; /* SPRG2 only used in criticals */ \ + mfcr r10; /* save CR in r10 for now */\ + mfspr r11,SPRN_CSRR1; /* check whether user or kernel */\ + andi. r11,r11,MSR_PR; \ + lis r11,critical_stack_top@h; \ + ori r11,r11,critical_stack_top@l; \ + beq 1f; \ + /* COMING FROM USER MODE */ \ + mfspr r11,SPRG3; /* if from user, start at top of */\ + lwz r11,THREAD_INFO-THREAD(r11); /* this thread's kernel stack */\ + addi r11,r11,THREAD_SIZE; \ +1: subi r11,r11,INT_FRAME_SIZE; /* Allocate an exception frame */\ + stw r10,_CCR(r11); /* save various registers */\ + stw r12,GPR12(r11); \ + stw r9,GPR9(r11); \ + mflr r10; \ + stw r10,_LINK(r11); \ + mfspr r12,SPRN_DEAR; /* save DEAR and ESR in the frame */\ + stw r12,_DEAR(r11); /* since they may have had stuff */\ + mfspr r9,SPRN_ESR; /* in them at the point where the */\ + stw r9,_ESR(r11); /* exception was taken */\ + mfspr r12,CSRR0; \ + stw r1,GPR1(r11); \ + mfspr r9,CSRR1; \ + stw r1,0(r11); \ + tovirt(r1,r11); \ + rlwinm r9,r9,0,14,12; /* clear MSR_WE (necessary?) */\ + stw r0,GPR0(r11); \ + SAVE_4GPRS(3, r11); \ + SAVE_2GPRS(7, r11) + +/* + * Exception prolog for machine check exceptions. This is similar to + * the critical exception prolog, except that machine check exceptions + * have their own save area. For Book E processors, we also have a + * reserved register (SPRG6) that is only used in machine check exceptions + * so we can free up a GPR to use as the base for indirect access to the + * machine check exception save area. This is necessary since the MMU + * is always on and the save area is offset from KERNELBASE. + */ +#define MCHECK_EXCEPTION_PROLOG \ + mtspr SPRG6W,r8; /* SPRG6 used in machine checks */ \ + lis r8,mcheck_save@ha; \ + stw r10,mcheck_r10@l(r8); \ + stw r11,mcheck_r11@l(r8); \ + mfspr r10,SPRG0; \ + stw r10,mcheck_sprg0@l(r8); \ + mfspr r10,SPRG1; \ + stw r10,mcheck_sprg1@l(r8); \ + mfspr r10,SPRG4R; \ + stw r10,mcheck_sprg4@l(r8); \ + mfspr r10,SPRG5R; \ + stw r10,mcheck_sprg5@l(r8); \ + mfspr r10,SPRG7R; \ + stw r10,mcheck_sprg7@l(r8); \ + mfspr r10,SPRN_PID; \ + stw r10,mcheck_pid@l(r8); \ + mfspr r10,SRR0; \ + stw r10,mcheck_srr0@l(r8); \ + mfspr r10,SRR1; \ + stw r10,mcheck_srr1@l(r8); \ + mfspr r10,CSRR0; \ + stw r10,mcheck_csrr0@l(r8); \ + mfspr r10,CSRR1; \ + stw r10,mcheck_csrr1@l(r8); \ + mfspr r8,SPRG6R; /* SPRG6 used in machine checks */ \ + mfcr r10; /* save CR in r10 for now */\ + mfspr r11,SPRN_MCSRR1; /* check whether user or kernel */\ + andi. r11,r11,MSR_PR; \ + lis r11,mcheck_stack_top@h; \ + ori r11,r11,mcheck_stack_top@l; \ + beq 1f; \ + /* COMING FROM USER MODE */ \ + mfspr r11,SPRG3; /* if from user, start at top of */\ + lwz r11,THREAD_INFO-THREAD(r11); /* this thread's kernel stack */\ + addi r11,r11,THREAD_SIZE; \ +1: subi r11,r11,INT_FRAME_SIZE; /* Allocate an exception frame */\ + stw r10,_CCR(r11); /* save various registers */\ + stw r12,GPR12(r11); \ + stw r9,GPR9(r11); \ + mflr r10; \ + stw r10,_LINK(r11); \ + mfspr r12,SPRN_DEAR; /* save DEAR and ESR in the frame */\ + stw r12,_DEAR(r11); /* since they may have had stuff */\ + mfspr r9,SPRN_ESR; /* in them at the point where the */\ + stw r9,_ESR(r11); /* exception was taken */\ + mfspr r12,MCSRR0; \ + stw r1,GPR1(r11); \ + mfspr r9,MCSRR1; \ + stw r1,0(r11); \ + tovirt(r1,r11); \ + rlwinm r9,r9,0,14,12; /* clear MSR_WE (necessary?) */\ + stw r0,GPR0(r11); \ + SAVE_4GPRS(3, r11); \ + SAVE_2GPRS(7, r11) + +/* + * Exception vectors. + */ +#define START_EXCEPTION(label) \ + .align 5; \ +label: + +#define FINISH_EXCEPTION(func) \ + bl transfer_to_handler_full; \ + .long func; \ + .long ret_from_except_full + +#define EXCEPTION(n, label, hdlr, xfer) \ + START_EXCEPTION(label); \ + NORMAL_EXCEPTION_PROLOG; \ + addi r3,r1,STACK_FRAME_OVERHEAD; \ + xfer(n, hdlr) + +#define CRITICAL_EXCEPTION(n, label, hdlr) \ + START_EXCEPTION(label); \ + CRITICAL_EXCEPTION_PROLOG; \ + addi r3,r1,STACK_FRAME_OVERHEAD; \ + EXC_XFER_TEMPLATE(hdlr, n+2, (MSR_KERNEL & ~(MSR_ME|MSR_DE|MSR_CE)), \ + NOCOPY, transfer_to_handler_full, \ + ret_from_except_full) + +#define MCHECK_EXCEPTION(n, label, hdlr) \ + START_EXCEPTION(label); \ + MCHECK_EXCEPTION_PROLOG; \ + mfspr r5,SPRN_ESR; \ + stw r5,_ESR(r11); \ + addi r3,r1,STACK_FRAME_OVERHEAD; \ + EXC_XFER_TEMPLATE(hdlr, n+2, (MSR_KERNEL & ~(MSR_ME|MSR_DE|MSR_CE)), \ + NOCOPY, mcheck_transfer_to_handler, \ + ret_from_mcheck_exc) + +#define EXC_XFER_TEMPLATE(hdlr, trap, msr, copyee, tfer, ret) \ + li r10,trap; \ + stw r10,TRAP(r11); \ + lis r10,msr@h; \ + ori r10,r10,msr@l; \ + copyee(r10, r9); \ + bl tfer; \ + .long hdlr; \ + .long ret + +#define COPY_EE(d, s) rlwimi d,s,0,16,16 +#define NOCOPY(d, s) + +#define EXC_XFER_STD(n, hdlr) \ + EXC_XFER_TEMPLATE(hdlr, n, MSR_KERNEL, NOCOPY, transfer_to_handler_full, \ + ret_from_except_full) + +#define EXC_XFER_LITE(n, hdlr) \ + EXC_XFER_TEMPLATE(hdlr, n+1, MSR_KERNEL, NOCOPY, transfer_to_handler, \ + ret_from_except) + +#define EXC_XFER_EE(n, hdlr) \ + EXC_XFER_TEMPLATE(hdlr, n, MSR_KERNEL, COPY_EE, transfer_to_handler_full, \ + ret_from_except_full) + +#define EXC_XFER_EE_LITE(n, hdlr) \ + EXC_XFER_TEMPLATE(hdlr, n+1, MSR_KERNEL, COPY_EE, transfer_to_handler, \ + ret_from_except) + + +#endif /* __HEAD_BOOKE_H__ */ diff --git a/arch/ppc/kernel/head_e500.S b/arch/ppc/kernel/head_e500.S index ceb51d343..cf964198e 100644 --- a/arch/ppc/kernel/head_e500.S +++ b/arch/ppc/kernel/head_e500.S @@ -41,15 +41,7 @@ #include #include #include - -/* - * Macros - */ - -#define SET_IVOR(vector_number, vector_label) \ - li r26,vector_label@l; \ - mtspr SPRN_IVOR##vector_number,r26; \ - sync +#include "head_booke.h" /* As with the other PowerPC ports, it is expected that when code * execution begins here, the following registers contain valid, yet @@ -371,232 +363,6 @@ skpinv: addi r6,r6,1 /* Increment */ * We align on a 32 byte cache line boundary for good measure. */ -#define NORMAL_EXCEPTION_PROLOG \ - mtspr SPRN_SPRG0,r10; /* save two registers to work with */\ - mtspr SPRN_SPRG1,r11; \ - mtspr SPRN_SPRG4W,r1; \ - mfcr r10; /* save CR in r10 for now */\ - mfspr r11,SPRN_SRR1; /* check whether user or kernel */\ - andi. r11,r11,MSR_PR; \ - beq 1f; \ - mfspr r1,SPRG3; /* if from user, start at top of */\ - lwz r1,THREAD_INFO-THREAD(r1); /* this thread's kernel stack */\ - addi r1,r1,THREAD_SIZE; \ -1: subi r1,r1,INT_FRAME_SIZE; /* Allocate an exception frame */\ - tophys(r11,r1); \ - stw r10,_CCR(r11); /* save various registers */\ - stw r12,GPR12(r11); \ - stw r9,GPR9(r11); \ - mfspr r10,SPRG0; \ - stw r10,GPR10(r11); \ - mfspr r12,SPRG1; \ - stw r12,GPR11(r11); \ - mflr r10; \ - stw r10,_LINK(r11); \ - mfspr r10,SPRG4R; \ - mfspr r12,SRR0; \ - stw r10,GPR1(r11); \ - mfspr r9,SRR1; \ - stw r10,0(r11); \ - rlwinm r9,r9,0,14,12; /* clear MSR_WE (necessary?) */\ - stw r0,GPR0(r11); \ - SAVE_4GPRS(3, r11); \ - SAVE_2GPRS(7, r11) - -/* - * Exception prolog for critical exceptions. This is a little different - * from the normal exception prolog above since a critical exception - * can potentially occur at any point during normal exception processing. - * Thus we cannot use the same SPRG registers as the normal prolog above. - * Instead we use a couple of words of memory at low physical addresses. - * This is OK since we don't support SMP on these processors. For Book E - * processors, we also have a reserved register (SPRG2) that is only used - * in critical exceptions so we can free up a GPR to use as the base for - * indirect access to the critical exception save area. This is necessary - * since the MMU is always on and the save area is offset from KERNELBASE. - */ -#define CRITICAL_EXCEPTION_PROLOG \ - mtspr SPRG2,r8; /* SPRG2 only used in criticals */ \ - lis r8,crit_save@ha; \ - stw r10,crit_r10@l(r8); \ - stw r11,crit_r11@l(r8); \ - mfspr r10,SPRG0; \ - stw r10,crit_sprg0@l(r8); \ - mfspr r10,SPRG1; \ - stw r10,crit_sprg1@l(r8); \ - mfspr r10,SPRG4R; \ - stw r10,crit_sprg4@l(r8); \ - mfspr r10,SPRG5R; \ - stw r10,crit_sprg5@l(r8); \ - mfspr r10,SPRG7R; \ - stw r10,crit_sprg7@l(r8); \ - mfspr r10,SPRN_PID; \ - stw r10,crit_pid@l(r8); \ - mfspr r10,SRR0; \ - stw r10,crit_srr0@l(r8); \ - mfspr r10,SRR1; \ - stw r10,crit_srr1@l(r8); \ - mfspr r8,SPRG2; /* SPRG2 only used in criticals */ \ - mfcr r10; /* save CR in r10 for now */\ - mfspr r11,SPRN_CSRR1; /* check whether user or kernel */\ - andi. r11,r11,MSR_PR; \ - lis r11,critical_stack_top@h; \ - ori r11,r11,critical_stack_top@l; \ - beq 1f; \ - /* COMING FROM USER MODE */ \ - mfspr r11,SPRG3; /* if from user, start at top of */\ - lwz r11,THREAD_INFO-THREAD(r11); /* this thread's kernel stack */\ - addi r11,r11,THREAD_SIZE; \ -1: subi r11,r11,INT_FRAME_SIZE; /* Allocate an exception frame */\ - stw r10,_CCR(r11); /* save various registers */\ - stw r12,GPR12(r11); \ - stw r9,GPR9(r11); \ - mflr r10; \ - stw r10,_LINK(r11); \ - mfspr r12,SPRN_DEAR; /* save DEAR and ESR in the frame */\ - stw r12,_DEAR(r11); /* since they may have had stuff */\ - mfspr r9,SPRN_ESR; /* in them at the point where the */\ - stw r9,_ESR(r11); /* exception was taken */\ - mfspr r12,CSRR0; \ - stw r1,GPR1(r11); \ - mfspr r9,CSRR1; \ - stw r1,0(r11); \ - tovirt(r1,r11); \ - rlwinm r9,r9,0,14,12; /* clear MSR_WE (necessary?) */\ - stw r0,GPR0(r11); \ - SAVE_4GPRS(3, r11); \ - SAVE_2GPRS(7, r11) - -/* - * Exception prolog for machine check exceptions. This is similar to - * the critical exception prolog, except that machine check exceptions - * have their own save area. For Book E processors, we also have a - * reserved register (SPRG6) that is only used in machine check exceptions - * so we can free up a GPR to use as the base for indirect access to the - * machine check exception save area. This is necessary since the MMU - * is always on and the save area is offset from KERNELBASE. - */ -#define MCHECK_EXCEPTION_PROLOG \ - mtspr SPRG6W,r8; /* SPRG6 used in machine checks */ \ - lis r8,mcheck_save@ha; \ - stw r10,mcheck_r10@l(r8); \ - stw r11,mcheck_r11@l(r8); \ - mfspr r10,SPRG0; \ - stw r10,mcheck_sprg0@l(r8); \ - mfspr r10,SPRG1; \ - stw r10,mcheck_sprg1@l(r8); \ - mfspr r10,SPRG4R; \ - stw r10,mcheck_sprg4@l(r8); \ - mfspr r10,SPRG5R; \ - stw r10,mcheck_sprg5@l(r8); \ - mfspr r10,SPRG7R; \ - stw r10,mcheck_sprg7@l(r8); \ - mfspr r10,SPRN_PID; \ - stw r10,mcheck_pid@l(r8); \ - mfspr r10,SRR0; \ - stw r10,mcheck_srr0@l(r8); \ - mfspr r10,SRR1; \ - stw r10,mcheck_srr1@l(r8); \ - mfspr r10,CSRR0; \ - stw r10,mcheck_csrr0@l(r8); \ - mfspr r10,CSRR1; \ - stw r10,mcheck_csrr1@l(r8); \ - mfspr r8,SPRG6R; /* SPRG6 used in machine checks */ \ - mfcr r10; /* save CR in r10 for now */\ - mfspr r11,SPRN_MCSRR1; /* check whether user or kernel */\ - andi. r11,r11,MSR_PR; \ - lis r11,mcheck_stack_top@h; \ - ori r11,r11,mcheck_stack_top@l; \ - beq 1f; \ - /* COMING FROM USER MODE */ \ - mfspr r11,SPRG3; /* if from user, start at top of */\ - lwz r11,THREAD_INFO-THREAD(r11); /* this thread's kernel stack */\ - addi r11,r11,THREAD_SIZE; \ -1: subi r11,r11,INT_FRAME_SIZE; /* Allocate an exception frame */\ - stw r10,_CCR(r11); /* save various registers */\ - stw r12,GPR12(r11); \ - stw r9,GPR9(r11); \ - mflr r10; \ - stw r10,_LINK(r11); \ - mfspr r12,SPRN_DEAR; /* save DEAR and ESR in the frame */\ - stw r12,_DEAR(r11); /* since they may have had stuff */\ - mfspr r9,SPRN_ESR; /* in them at the point where the */\ - stw r9,_ESR(r11); /* exception was taken */\ - mfspr r12,MCSRR0; \ - stw r1,GPR1(r11); \ - mfspr r9,MCSRR1; \ - stw r1,0(r11); \ - tovirt(r1,r11); \ - rlwinm r9,r9,0,14,12; /* clear MSR_WE (necessary?) */\ - stw r0,GPR0(r11); \ - SAVE_4GPRS(3, r11); \ - SAVE_2GPRS(7, r11) - -/* - * Exception vectors. - */ -#define START_EXCEPTION(label) \ - .align 5; \ -label: - -#define FINISH_EXCEPTION(func) \ - bl transfer_to_handler_full; \ - .long func; \ - .long ret_from_except_full - -#define EXCEPTION(n, label, hdlr, xfer) \ - START_EXCEPTION(label); \ - NORMAL_EXCEPTION_PROLOG; \ - addi r3,r1,STACK_FRAME_OVERHEAD; \ - xfer(n, hdlr) - -#define CRITICAL_EXCEPTION(n, label, hdlr) \ - START_EXCEPTION(label); \ - CRITICAL_EXCEPTION_PROLOG; \ - addi r3,r1,STACK_FRAME_OVERHEAD; \ - EXC_XFER_TEMPLATE(hdlr, n+2, (MSR_KERNEL & ~(MSR_ME|MSR_DE|MSR_CE)), \ - NOCOPY, transfer_to_handler_full, \ - ret_from_except_full) - -#define MCHECK_EXCEPTION(n, label, hdlr) \ - START_EXCEPTION(label); \ - MCHECK_EXCEPTION_PROLOG; \ - mfspr r5,SPRN_ESR; \ - stw r5,_ESR(r11); \ - addi r3,r1,STACK_FRAME_OVERHEAD; \ - EXC_XFER_TEMPLATE(hdlr, n+2, (MSR_KERNEL & ~(MSR_ME|MSR_DE|MSR_CE)), \ - NOCOPY, mcheck_transfer_to_handler, \ - ret_from_mcheck_exc) - -#define EXC_XFER_TEMPLATE(hdlr, trap, msr, copyee, tfer, ret) \ - li r10,trap; \ - stw r10,TRAP(r11); \ - lis r10,msr@h; \ - ori r10,r10,msr@l; \ - copyee(r10, r9); \ - bl tfer; \ - .long hdlr; \ - .long ret - -#define COPY_EE(d, s) rlwimi d,s,0,16,16 -#define NOCOPY(d, s) - -#define EXC_XFER_STD(n, hdlr) \ - EXC_XFER_TEMPLATE(hdlr, n, MSR_KERNEL, NOCOPY, transfer_to_handler_full, \ - ret_from_except_full) - -#define EXC_XFER_LITE(n, hdlr) \ - EXC_XFER_TEMPLATE(hdlr, n+1, MSR_KERNEL, NOCOPY, transfer_to_handler, \ - ret_from_except) - -#define EXC_XFER_EE(n, hdlr) \ - EXC_XFER_TEMPLATE(hdlr, n, MSR_KERNEL, COPY_EE, transfer_to_handler_full, \ - ret_from_except_full) - -#define EXC_XFER_EE_LITE(n, hdlr) \ - EXC_XFER_TEMPLATE(hdlr, n+1, MSR_KERNEL, COPY_EE, transfer_to_handler, \ - ret_from_except) - interrupt_base: /* Critical Input Interrupt */ CRITICAL_EXCEPTION(0x0100, CriticalInput, UnknownException) diff --git a/arch/ppc/kernel/idle.c b/arch/ppc/kernel/idle.c index 5adb6be19..6db80f60c 100644 --- a/arch/ppc/kernel/idle.c +++ b/arch/ppc/kernel/idle.c @@ -21,6 +21,7 @@ #include #include #include +#include #include #include @@ -65,3 +66,36 @@ int cpu_idle(void) default_idle(); return 0; } + +#if defined(CONFIG_SYSCTL) && defined(CONFIG_6xx) +/* + * Register the sysctl to set/clear powersave_nap. + */ +extern unsigned long powersave_nap; + +static ctl_table powersave_nap_ctl_table[]={ + { + .ctl_name = KERN_PPC_POWERSAVE_NAP, + .procname = "powersave-nap", + .data = &powersave_nap, + .maxlen = sizeof(int), + .mode = 0644, + .proc_handler = &proc_dointvec, + }, + { 0, }, +}; +static ctl_table powersave_nap_sysctl_root[] = { + { 1, "kernel", NULL, 0, 0755, powersave_nap_ctl_table, }, + { 0,}, +}; + +static int __init +register_powersave_nap_sysctl(void) +{ + register_sysctl_table(powersave_nap_sysctl_root, 0); + + return 0; +} + +__initcall(register_powersave_nap_sysctl); +#endif diff --git a/arch/ppc/kernel/idle_6xx.S b/arch/ppc/kernel/idle_6xx.S index 608f71ccb..25d009c75 100644 --- a/arch/ppc/kernel/idle_6xx.S +++ b/arch/ppc/kernel/idle_6xx.S @@ -79,12 +79,12 @@ BEGIN_FTR_SECTION /* Now check if user or arch enabled NAP mode */ lis r4,powersave_nap@ha lwz r4,powersave_nap@l(r4) - cmpi 0,r4,0 + cmpwi 0,r4,0 beq 1f lis r3,HID0_NAP@h 1: END_FTR_SECTION_IFSET(CPU_FTR_CAN_NAP) - cmpi 0,r3,0 + cmpwi 0,r3,0 beqlr /* Clear MSR:EE */ @@ -133,7 +133,7 @@ BEGIN_FTR_SECTION /* Go to low speed mode on some 750FX */ lis r4,powersave_lowspeed@ha lwz r4,powersave_lowspeed@l(r4) - cmpi 0,r4,0 + cmpwi 0,r4,0 beq 1f mfspr r4,SPRN_HID1 oris r4,r4,0x0001 diff --git a/arch/ppc/kernel/idle_power4.S b/arch/ppc/kernel/idle_power4.S index 538a044f4..73a58ff03 100644 --- a/arch/ppc/kernel/idle_power4.S +++ b/arch/ppc/kernel/idle_power4.S @@ -56,7 +56,7 @@ END_FTR_SECTION_IFCLR(CPU_FTR_CAN_NAP) /* Now check if user or arch enabled NAP mode */ lis r4,powersave_nap@ha lwz r4,powersave_nap@l(r4) - cmpi 0,r4,0 + cmpwi 0,r4,0 beqlr /* Clear MSR:EE */ diff --git a/arch/ppc/kernel/machine_kexec.c b/arch/ppc/kernel/machine_kexec.c new file mode 100644 index 000000000..9daefb316 --- /dev/null +++ b/arch/ppc/kernel/machine_kexec.c @@ -0,0 +1,132 @@ +/* + * machine_kexec.c - handle transition of Linux booting another kernel + * Copyright (C) 2002-2003 Eric Biederman + * + * GAMECUBE/PPC32 port Copyright (C) 2004 Albert Herranz + * + * This source code is licensed under the GNU General Public License, + * Version 2. See the file COPYING for more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +typedef void (*relocate_new_kernel_t)( + unsigned long indirection_page, unsigned long reboot_code_buffer, + unsigned long start_address); + +const extern unsigned char relocate_new_kernel[]; +const extern unsigned int relocate_new_kernel_size; +extern void use_mm(struct mm_struct *mm); + +static int identity_map_pages(struct page *pages, int order) +{ + struct mm_struct *mm; + struct vm_area_struct *vma; + int error; + + mm = &init_mm; + vma = NULL; + + down_write(&mm->mmap_sem); + error = -ENOMEM; + vma = kmem_cache_alloc(vm_area_cachep, SLAB_KERNEL); + if (!vma) { + goto out; + } + + memset(vma, 0, sizeof(*vma)); + vma->vm_mm = mm; + vma->vm_start = page_to_pfn(pages) << PAGE_SHIFT; + vma->vm_end = vma->vm_start + (1 << (order + PAGE_SHIFT)); + vma->vm_ops = NULL; + vma->vm_flags = VM_SHARED \ + | VM_READ | VM_WRITE | VM_EXEC \ + | VM_MAYREAD | VM_MAYWRITE | VM_MAYEXEC \ + | VM_DONTCOPY | VM_RESERVED; + vma->vm_page_prot = protection_map[vma->vm_flags & 0xf]; + vma->vm_file = NULL; + vma->vm_private_data = NULL; + insert_vm_struct(mm, vma); + + error = remap_page_range(vma, vma->vm_start, vma->vm_start, + vma->vm_end - vma->vm_start, vma->vm_page_prot); + if (error) { + goto out; + } + + error = 0; + out: + if (error && vma) { + kmem_cache_free(vm_area_cachep, vma); + vma = NULL; + } + up_write(&mm->mmap_sem); + + return error; +} + +/* + * Do what every setup is needed on image and the + * reboot code buffer to allow us to avoid allocations + * later. + */ +int machine_kexec_prepare(struct kimage *image) +{ + unsigned int order; + order = get_order(KEXEC_CONTROL_CODE_SIZE); + return identity_map_pages(image->control_code_page, order); +} + +void machine_kexec_cleanup(struct kimage *image) +{ + unsigned int order; + order = get_order(KEXEC_CONTROL_CODE_SIZE); + do_munmap(&init_mm, + page_to_pfn(image->control_code_page) << PAGE_SHIFT, + 1 << (order + PAGE_SHIFT)); +} + +void machine_shutdown(void) +{ +} + +/* + * Do not allocate memory (or fail in any way) in machine_kexec(). + * We are past the point of no return, committed to rebooting now. + */ +void machine_kexec(struct kimage *image) +{ + unsigned long indirection_page; + unsigned long reboot_code_buffer; + relocate_new_kernel_t rnk; + + /* switch to an mm where the reboot_code_buffer is identity mapped */ + use_mm(&init_mm); + + /* Interrupts aren't acceptable while we reboot */ + local_irq_disable(); + + reboot_code_buffer = page_to_pfn(image->control_code_page) <head & PAGE_MASK; + + /* copy it out */ + memcpy((void *)reboot_code_buffer, + relocate_new_kernel, relocate_new_kernel_size); + + flush_icache_range(reboot_code_buffer, + reboot_code_buffer + KEXEC_CONTROL_CODE_SIZE); + printk(KERN_INFO "Bye!\n"); + + /* now call it */ + rnk = (relocate_new_kernel_t) reboot_code_buffer; + (*rnk)(indirection_page, reboot_code_buffer, image->start); +} + diff --git a/arch/ppc/kernel/pci.c b/arch/ppc/kernel/pci.c index 4145ff7bf..08cdddee7 100644 --- a/arch/ppc/kernel/pci.c +++ b/arch/ppc/kernel/pci.c @@ -33,6 +33,7 @@ unsigned long isa_io_base = 0; unsigned long isa_mem_base = 0; unsigned long pci_dram_offset = 0; +int pcibios_assign_bus_offset = 1; void pcibios_make_OF_bus_map(void); @@ -45,11 +46,6 @@ static void fixup_broken_pcnet32(struct pci_dev* dev); static int reparent_resources(struct resource *parent, struct resource *res); static void fixup_rev1_53c810(struct pci_dev* dev); static void fixup_cpc710_pci64(struct pci_dev* dev); -#ifdef CONFIG_PPC_PMAC -extern void pmac_pci_fixup_cardbus(struct pci_dev* dev); -extern void pmac_pci_fixup_pciata(struct pci_dev* dev); -extern void pmac_pci_fixup_k2_sata(struct pci_dev* dev); -#endif #ifdef CONFIG_PPC_OF static u8* pci_to_OF_bus_map; #endif @@ -64,20 +60,6 @@ struct pci_controller** hose_tail = &hose_head; static int pci_bus_count; -struct pci_fixup pcibios_fixups[] = { - { PCI_FIXUP_HEADER, PCI_VENDOR_ID_TRIDENT, PCI_ANY_ID, fixup_broken_pcnet32 }, - { PCI_FIXUP_HEADER, PCI_VENDOR_ID_NCR, PCI_DEVICE_ID_NCR_53C810, fixup_rev1_53c810 }, - { PCI_FIXUP_HEADER, PCI_VENDOR_ID_IBM, PCI_DEVICE_ID_IBM_CPC710_PCI64, fixup_cpc710_pci64}, - { PCI_FIXUP_HEADER, PCI_ANY_ID, PCI_ANY_ID, pcibios_fixup_resources }, -#ifdef CONFIG_PPC_PMAC - /* We should add per-machine fixup support in xxx_setup.c or xxx_pci.c */ - { PCI_FIXUP_FINAL, PCI_VENDOR_ID_TI, PCI_ANY_ID, pmac_pci_fixup_cardbus }, - { PCI_FIXUP_FINAL, PCI_ANY_ID, PCI_ANY_ID, pmac_pci_fixup_pciata }, - { PCI_FIXUP_HEADER, PCI_VENDOR_ID_SERVERWORKS, 0x0240, pmac_pci_fixup_k2_sata }, -#endif /* CONFIG_PPC_PMAC */ - { 0 } -}; - static void fixup_rev1_53c810(struct pci_dev* dev) { @@ -90,6 +72,7 @@ fixup_rev1_53c810(struct pci_dev* dev) dev->class = PCI_CLASS_STORAGE_SCSI; } } +DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_NCR, PCI_DEVICE_ID_NCR_53C810, fixup_rev1_53c810); static void fixup_broken_pcnet32(struct pci_dev* dev) @@ -100,6 +83,7 @@ fixup_broken_pcnet32(struct pci_dev* dev) pci_name_device(dev); } } +DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_TRIDENT, PCI_ANY_ID, fixup_broken_pcnet32); static void fixup_cpc710_pci64(struct pci_dev* dev) @@ -112,6 +96,7 @@ fixup_cpc710_pci64(struct pci_dev* dev) dev->resource[1].start = dev->resource[1].end = 0; dev->resource[1].flags = 0; } +DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_IBM, PCI_DEVICE_ID_IBM_CPC710_PCI64, fixup_cpc710_pci64); static void pcibios_fixup_resources(struct pci_dev *dev) @@ -158,9 +143,9 @@ pcibios_fixup_resources(struct pci_dev *dev) if (ppc_md.pcibios_fixup_resources) ppc_md.pcibios_fixup_resources(dev); } +DECLARE_PCI_FIXUP_HEADER(PCI_ANY_ID, PCI_ANY_ID, pcibios_fixup_resources); -void -pcibios_resource_to_bus(struct pci_dev *dev, struct pci_bus_region *region, +void pcibios_resource_to_bus(struct pci_dev *dev, struct pci_bus_region *region, struct resource *res) { unsigned long offset = 0; @@ -173,6 +158,7 @@ pcibios_resource_to_bus(struct pci_dev *dev, struct pci_bus_region *region, region->start = res->start - offset; region->end = res->end - offset; } +EXPORT_SYMBOL(pcibios_resource_to_bus); /* * We need to avoid collisions with `mirrored' VGA ports @@ -187,8 +173,7 @@ pcibios_resource_to_bus(struct pci_dev *dev, struct pci_bus_region *region, * but we want to try to avoid allocating at 0x2900-0x2bff * which might have be mirrored at 0x0100-0x03ff.. */ -void -pcibios_align_resource(void *data, struct resource *res, unsigned long size, +void pcibios_align_resource(void *data, struct resource *res, unsigned long size, unsigned long align) { struct pci_dev *dev = data; @@ -208,7 +193,7 @@ pcibios_align_resource(void *data, struct resource *res, unsigned long size, } } } - +EXPORT_SYMBOL(pcibios_align_resource); /* * Handle resources of PCI devices. If the world were perfect, we could @@ -427,7 +412,7 @@ probe_resource(struct pci_bus *parent, struct resource *pr, r = &dev->resource[i]; if (!r->flags || (r->flags & IORESOURCE_UNSET)) continue; - if (pci_find_parent_resource(bus->self, r) != pr) + if (pci_find_parent_resource(dev, r) != pr) continue; if (r->end >= res->start && res->end >= r->start) { *conflict = r; @@ -1279,7 +1264,7 @@ pcibios_init(void) bus = pci_scan_bus(hose->first_busno, hose->ops, hose); hose->last_busno = bus->subordinate; if (pci_assign_all_busses || next_busno <= hose->last_busno) - next_busno = hose->last_busno+1; + next_busno = hose->last_busno + pcibios_assign_bus_offset; } pci_bus_count = next_busno; @@ -1724,6 +1709,32 @@ pci_init_resource(struct resource *res, unsigned long start, unsigned long end, res->child = NULL; } +void __iomem *pci_iomap(struct pci_dev *dev, int bar, unsigned long max) +{ + unsigned long start = pci_resource_start(dev, bar); + unsigned long len = pci_resource_len(dev, bar); + unsigned long flags = pci_resource_flags(dev, bar); + + if (!len) + return NULL; + if (max && len > max) + len = max; + if (flags & IORESOURCE_IO) + return ioport_map(start, len); + if (flags & IORESOURCE_MEM) + return (void __iomem *) start; + /* What? */ + return NULL; +} + +void pci_iounmap(struct pci_dev *dev, void __iomem *addr) +{ + /* Nothing to do */ +} +EXPORT_SYMBOL(pci_iomap); +EXPORT_SYMBOL(pci_iounmap); + + /* * Null PCI config access functions, for the case when we can't * find a hose. diff --git a/arch/ppc/kernel/ppc_htab.c b/arch/ppc/kernel/ppc_htab.c index 91592c03e..d8e027fd4 100644 --- a/arch/ppc/kernel/ppc_htab.c +++ b/arch/ppc/kernel/ppc_htab.c @@ -20,6 +20,7 @@ #include #include #include +#include #include #include @@ -34,9 +35,6 @@ static int ppc_htab_show(struct seq_file *m, void *v); static ssize_t ppc_htab_write(struct file * file, const char __user * buffer, size_t count, loff_t *ppos); -int proc_dol2crvec(ctl_table *table, int write, struct file *filp, - void __user *buffer, size_t *lenp, loff_t *ppos); - extern PTE *Hash, *Hash_end; extern unsigned long Hash_size, Hash_mask; extern unsigned long _SDR1; @@ -438,3 +436,32 @@ int proc_dol2crvec(ctl_table *table, int write, struct file *filp, *ppos += *lenp; return 0; } + +#ifdef CONFIG_SYSCTL +/* + * Register our sysctl. + */ +static ctl_table htab_ctl_table[]={ + { + .ctl_name = KERN_PPC_L2CR, + .procname = "l2cr", + .mode = 0644, + .proc_handler = &proc_dol2crvec, + }, + { 0, }, +}; +static ctl_table htab_sysctl_root[] = { + { 1, "kernel", NULL, 0, 0755, htab_ctl_table, }, + { 0,}, +}; + +static int __init +register_ppc_htab_sysctl(void) +{ + register_sysctl_table(htab_sysctl_root, 0); + + return 0; +} + +__initcall(register_ppc_htab_sysctl); +#endif diff --git a/arch/ppc/kernel/relocate_kernel.S b/arch/ppc/kernel/relocate_kernel.S new file mode 100644 index 000000000..837e5965a --- /dev/null +++ b/arch/ppc/kernel/relocate_kernel.S @@ -0,0 +1,127 @@ +/* + * relocate_kernel.S - put the kernel image in place to boot + * Copyright (C) 2002-2003 Eric Biederman + * + * GAMECUBE/PPC32 port Copyright (C) 2004 Albert Herranz + * + * This source code is licensed under the GNU General Public License, + * Version 2. See the file COPYING for more details. + */ + +#include +#include +#include + +#include + +#define PAGE_SIZE 4096 /* must be same value as in */ + +/* returns r3 = relocated address of sym */ +/* modifies r0 */ +#define RELOC_SYM(sym) \ + mflr r3; \ + bl 1f; \ +1: mflr r0; \ + mtlr r3; \ + lis r3, 1b@ha; \ + ori r3, r3, 1b@l; \ + subf r0, r3, r0; \ + lis r3, sym@ha; \ + ori r3, r3, sym@l; \ + add r3, r3, r0 + + /* + * Must be relocatable PIC code callable as a C function. + */ + .globl relocate_new_kernel +relocate_new_kernel: + /* r3 = indirection_page */ + /* r4 = reboot_code_buffer */ + /* r5 = start_address */ + + li r0, 0 + + /* Set Machine Status Register to a known status */ + mr r8, r0 + ori r8, r8, MSR_RI|MSR_ME + mtmsr r8 + isync + + /* from this point address translation is turned off */ + /* and interrupts are disabled */ + + /* set a new stack at the bottom of our page... */ + /* (not really needed now) */ + addi r1, r4, KEXEC_CONTROL_CODE_SIZE - 8 /* for LR Save+Back Chain */ + stw r0, 0(r1) + + /* Do the copies */ + li r6, 0 /* checksum */ + subi r3, r3, 4 + +0: /* top, read another word for the indirection page */ + lwzu r0, 4(r3) + + /* is it a destination page? (r8) */ + rlwinm. r7, r0, 0, 31, 31 /* IND_DESTINATION (1<<0) */ + beq 1f + + rlwinm r8, r0, 0, 0, 19 /* clear kexec flags, page align */ + b 0b + +1: /* is it an indirection page? (r3) */ + rlwinm. r7, r0, 0, 30, 30 /* IND_INDIRECTION (1<<1) */ + beq 1f + + rlwinm r3, r0, 0, 0, 19 /* clear kexec flags, page align */ + subi r3, r3, 4 + b 0b + +1: /* are we done? */ + rlwinm. r7, r0, 0, 29, 29 /* IND_DONE (1<<2) */ + beq 1f + b 2f + +1: /* is it a source page? (r9) */ + rlwinm. r7, r0, 0, 28, 28 /* IND_SOURCE (1<<3) */ + beq 0b + + rlwinm r9, r0, 0, 0, 19 /* clear kexec flags, page align */ + + li r7, PAGE_SIZE / 4 + mtctr r7 + subi r9, r9, 4 + subi r8, r8, 4 +9: + lwzu r0, 4(r9) /* do the copy */ + xor r6, r6, r0 + stwu r0, 4(r8) + dcbst 0, r8 + sync + icbi 0, r8 + bdnz 9b + + addi r9, r9, 4 + addi r8, r8, 4 + b 0b + +2: + + /* To be certain of avoiding problems with self-modifying code + * execute a serializing instruction here. + */ + isync + sync + + /* jump to the entry point, usually the setup routine */ + mtlr r5 + blrl + +1: b 1b + +relocate_new_kernel_end: + + .globl relocate_new_kernel_size +relocate_new_kernel_size: + .long relocate_new_kernel_end - relocate_new_kernel + diff --git a/arch/ppc/kernel/vector.S b/arch/ppc/kernel/vector.S index d8fe6b5fb..82a21346b 100644 --- a/arch/ppc/kernel/vector.S +++ b/arch/ppc/kernel/vector.S @@ -106,7 +106,7 @@ vmaddfp: 1: lfsx fr0,r4,r7 lfsx fr1,r5,r7 lfsx fr2,r6,r7 - fmadds fr0,fr0,fr1,fr2 + fmadds fr0,fr0,fr2,fr1 stfsx fr0,r3,r7 addi r7,r7,4 bdnz 1b @@ -133,7 +133,7 @@ vnmsubfp: 1: lfsx fr0,r4,r7 lfsx fr1,r5,r7 lfsx fr2,r6,r7 - fnmsubs fr0,fr0,fr1,fr2 + fnmsubs fr0,fr0,fr2,fr1 stfsx fr0,r3,r7 addi r7,r7,4 bdnz 1b diff --git a/arch/ppc/lib/checksum.S b/arch/ppc/lib/checksum.S index 74928a7a2..7874e8a80 100644 --- a/arch/ppc/lib/checksum.S +++ b/arch/ppc/lib/checksum.S @@ -80,13 +80,13 @@ _GLOBAL(csum_partial) adde r0,r0,r5 /* be unnecessary to unroll this loop */ bdnz 2b andi. r4,r4,3 -3: cmpi 0,r4,2 +3: cmpwi 0,r4,2 blt+ 4f lhz r5,4(r3) addi r3,r3,2 subi r4,r4,2 adde r0,r0,r5 -4: cmpi 0,r4,1 +4: cmpwi 0,r4,1 bne+ 5f lbz r5,4(r3) slwi r5,r5,8 /* Upper byte of word */ @@ -143,7 +143,7 @@ _GLOBAL(csum_partial_copy_generic) adde r0,r0,r9 bdnz 82b 13: andi. r5,r5,3 -3: cmpi 0,r5,2 +3: cmpwi 0,r5,2 blt+ 4f 83: lhz r6,4(r3) addi r3,r3,2 @@ -151,7 +151,7 @@ _GLOBAL(csum_partial_copy_generic) 93: sth r6,4(r4) addi r4,r4,2 adde r0,r0,r6 -4: cmpi 0,r5,1 +4: cmpwi 0,r5,1 bne+ 5f 84: lbz r6,4(r3) 94: stb r6,4(r4) @@ -188,7 +188,7 @@ src_error_3: 97: stbu r6,1(r4) bdnz 97b src_error: - cmpi 0,r7,0 + cmpwi 0,r7,0 beq 1f li r6,-EFAULT stw r6,0(r7) @@ -196,7 +196,7 @@ src_error: blr dst_error: - cmpi 0,r8,0 + cmpwi 0,r8,0 beq 1f li r6,-EFAULT stw r6,0(r8) diff --git a/arch/ppc/lib/rheap.c b/arch/ppc/lib/rheap.c index 103765660..9991c48f9 100644 --- a/arch/ppc/lib/rheap.c +++ b/arch/ppc/lib/rheap.c @@ -216,7 +216,7 @@ static void attach_free_block(rh_info_t * info, rh_block_t * blkn) /* Grow the after block backwards */ if (before == NULL && after != NULL) { - (int8_t *) after->start -= size; + after->start = (int8_t *)after->start - size; after->size += size; return; } @@ -407,7 +407,7 @@ void *rh_detach_region(rh_info_t * info, void *start, int size) /* blk still in free list, with updated start and/or size */ if (bs == s || be == e) { if (bs == s) - (int8_t *) blk->start += size; + blk->start = (int8_t *)blk->start + size; blk->size -= size; } else { @@ -471,7 +471,7 @@ void *rh_alloc(rh_info_t * info, int size, const char *owner) newblk->owner = owner; /* blk still in free list, with updated start, size */ - (int8_t *) blk->start += size; + blk->start = (int8_t *)blk->start + size; blk->size -= size; start = newblk->start; @@ -535,7 +535,7 @@ void *rh_alloc_fixed(rh_info_t * info, void *start, int size, const char *owner) /* blk still in free list, with updated start and/or size */ if (bs == s || be == e) { if (bs == s) - (int8_t *) blk->start += size; + blk->start = (int8_t *)blk->start + size; blk->size -= size; } else { diff --git a/arch/ppc/math-emu/op-common.h b/arch/ppc/math-emu/op-common.h index aeaa1cde5..afb82b649 100644 --- a/arch/ppc/math-emu/op-common.h +++ b/arch/ppc/math-emu/op-common.h @@ -82,7 +82,6 @@ do { \ if (X##_e <= _FP_WFRACBITS_##fs) \ { \ _FP_FRAC_SRS_##wc(X, X##_e, _FP_WFRACBITS_##fs); \ - __ret |= _FP_ROUND(wc, X); \ _FP_FRAC_SLL_##wc(X, 1); \ if (_FP_FRAC_OVERP_##wc(fs, X)) \ { \ diff --git a/arch/ppc/mm/44x_mmu.c b/arch/ppc/mm/44x_mmu.c index 1d79838d9..a2ab8b053 100644 --- a/arch/ppc/mm/44x_mmu.c +++ b/arch/ppc/mm/44x_mmu.c @@ -72,7 +72,7 @@ unsigned int tlb_44x_hwater = 62; static void __init ppc44x_pin_tlb(int slot, unsigned int virt, unsigned int phys) { - unsigned long attrib; + unsigned long attrib = 0; __asm__ __volatile__("\ clrrwi %2,%2,10\n\ diff --git a/arch/ppc/mm/4xx_mmu.c b/arch/ppc/mm/4xx_mmu.c index 6066ff35c..a7f616140 100644 --- a/arch/ppc/mm/4xx_mmu.c +++ b/arch/ppc/mm/4xx_mmu.c @@ -52,6 +52,7 @@ #include #include "mmu_decl.h" +extern int __map_without_ltlbs; /* * MMU_init_hw does the chip-specific initialization of the MMU hardware. */ @@ -102,6 +103,10 @@ unsigned long __init mmu_mapin_ram(void) p = PPC_MEMSTART; s = 0; + if (__map_without_ltlbs) { + return s; + } + while (s <= (total_lowmem - LARGE_PAGE_SIZE_16M)) { pmd_t *pmdp; unsigned long val = p | _PMD_SIZE_16M | _PAGE_HWEXEC | _PAGE_HWWRITE; diff --git a/arch/ppc/platforms/85xx/mpc8540_ads.c b/arch/ppc/platforms/85xx/mpc8540_ads.c index aada7e456..686dd6d9a 100644 --- a/arch/ppc/platforms/85xx/mpc8540_ads.c +++ b/arch/ppc/platforms/85xx/mpc8540_ads.c @@ -120,10 +120,6 @@ mpc8540ads_setup_arch(void) mpc85xx_setup_hose(); #endif -#ifdef CONFIG_DUMMY_CONSOLE - conswitchp = &dummy_con; -#endif - #ifdef CONFIG_SERIAL_8250 mpc85xx_early_serial_map(); #endif diff --git a/arch/ppc/platforms/85xx/mpc85xx_cds_common.c b/arch/ppc/platforms/85xx/mpc85xx_cds_common.c index c7e53e39c..5eb6e4218 100644 --- a/arch/ppc/platforms/85xx/mpc85xx_cds_common.c +++ b/arch/ppc/platforms/85xx/mpc85xx_cds_common.c @@ -307,7 +307,7 @@ mpc85xx_exclude_device(u_char bus, u_char devfn) { if (bus == 0 && PCI_SLOT(devfn) == 0) return PCIBIOS_DEVICE_NOT_FOUND; -#if CONFIG_85xx_PCI2 +#ifdef CONFIG_85xx_PCI2 /* With the current code we know PCI2 will be bus 2, however this may * not be guarnteed */ if (bus == 2 && PCI_SLOT(devfn) == 0) @@ -358,10 +358,6 @@ mpc85xx_cds_setup_arch(void) mpc85xx_setup_hose(); #endif -#ifdef CONFIG_DUMMY_CONSOLE - conswitchp = &dummy_con; -#endif - #ifdef CONFIG_SERIAL_8250 mpc85xx_early_serial_map(); #endif diff --git a/arch/ppc/platforms/85xx/sbc8560.c b/arch/ppc/platforms/85xx/sbc8560.c index d97c00945..9a4a7a415 100644 --- a/arch/ppc/platforms/85xx/sbc8560.c +++ b/arch/ppc/platforms/85xx/sbc8560.c @@ -145,9 +145,6 @@ sbc8560_setup_arch(void) /* setup PCI host bridges */ mpc85xx_setup_hose(); #endif -#ifdef CONFIG_DUMMY_CONSOLE - conswitchp = &dummy_con; -#endif #ifdef CONFIG_SERIAL_8250 sbc8560_early_serial_map(); #endif diff --git a/arch/ppc/platforms/chrp_setup.c b/arch/ppc/platforms/chrp_setup.c index 56be78eb4..3514bb9ed 100644 --- a/arch/ppc/platforms/chrp_setup.c +++ b/arch/ppc/platforms/chrp_setup.c @@ -250,13 +250,6 @@ chrp_setup_arch(void) */ sio_init(); - /* - * Setup the console operations - */ -#ifdef CONFIG_DUMMY_CONSOLE - conswitchp = &dummy_con; -#endif - /* Get the event scan rate for the rtas so we know how * often it expects a heartbeat. -- Cort */ @@ -382,7 +375,7 @@ void __init chrp_init_IRQ(void) { struct device_node *np; int i; - unsigned long chrp_int_ack; + unsigned long chrp_int_ack = 0; unsigned char init_senses[NR_IRQS - NUM_8259_INTERRUPTS]; #if defined(CONFIG_VT) && defined(CONFIG_INPUT_ADBHID) && defined(XMON) struct device_node *kbd; diff --git a/arch/ppc/platforms/chrp_smp.c b/arch/ppc/platforms/chrp_smp.c index 71f62a055..863d1e325 100644 --- a/arch/ppc/platforms/chrp_smp.c +++ b/arch/ppc/platforms/chrp_smp.c @@ -24,7 +24,6 @@ #include #include #include -#include #include #include #include diff --git a/arch/ppc/platforms/k2.c b/arch/ppc/platforms/k2.c index 4e2e18a52..8a602039f 100644 --- a/arch/ppc/platforms/k2.c +++ b/arch/ppc/platforms/k2.c @@ -464,10 +464,6 @@ static void __init k2_setup_arch(void) ROOT_DEV = Root_HDC1; #endif -#ifdef CONFIG_DUMMY_CONSOLE - conswitchp = &dummy_con; -#endif - /* Identify the system */ printk(KERN_INFO "System Identification: SBS K2 - PowerPC 750 @ " "%d Mhz\n", k2_get_cpu_speed() / 1000000); diff --git a/arch/ppc/platforms/lite5200.c b/arch/ppc/platforms/lite5200.c index 043040dc9..db6ea440c 100644 --- a/arch/ppc/platforms/lite5200.c +++ b/arch/ppc/platforms/lite5200.c @@ -36,6 +36,8 @@ #include +extern int powersave_nap; + /* Board data given by U-Boot */ bd_t __res; EXPORT_SYMBOL(__res); /* For modules */ @@ -64,25 +66,55 @@ struct ocp_def board_ocp[] = { .vendor = OCP_VENDOR_INVALID } }; - + /* ======================================================================== */ /* Platform specific code */ /* ======================================================================== */ static int -icecube_show_cpuinfo(struct seq_file *m) +lite5200_show_cpuinfo(struct seq_file *m) { seq_printf(m, "machine\t\t: Freescale LITE5200\n"); return 0; } static void __init -icecube_setup_arch(void) +lite5200_setup_cpu(void) { + struct mpc52xx_intr *intr; + + u32 intr_ctrl; + + /* Map zones */ + intr = (struct mpc52xx_intr *) + ioremap(MPC52xx_INTR,sizeof(struct mpc52xx_intr)); + + if (!intr) { + printk("lite5200.c: Error while mapping INTR during lite5200_setup_cpu\n"); + goto unmap_regs; + } + + /* IRQ[0-3] setup : IRQ0 - Level Active Low */ + /* IRQ[1-3] - Level Active High */ + intr_ctrl = in_be32(&intr->ctrl); + intr_ctrl &= ~0x00ff0000; + intr_ctrl |= 0x00c00000; + out_be32(&intr->ctrl, intr_ctrl); + + /* Unmap reg zone */ +unmap_regs: + if (intr) iounmap(intr); +} +static void __init +lite5200_setup_arch(void) +{ /* Add board OCP definitions */ mpc52xx_add_board_devices(board_ocp); + + /* CPU & Port mux setup */ + lite5200_setup_cpu(); } void __init @@ -110,7 +142,7 @@ platform_init(unsigned long r3, unsigned long r4, unsigned long r5, initrd_end = r5 + KERNELBASE; } #endif - + /* Load the command line */ if (r6) { *(char *)(r7+KERNELBASE) = 0; @@ -120,14 +152,17 @@ platform_init(unsigned long r3, unsigned long r4, unsigned long r5, /* BAT setup */ mpc52xx_set_bat(); - + /* No ISA bus AFAIK */ isa_io_base = 0; isa_mem_base = 0; + /* Powersave */ + powersave_nap = 1; /* We allow this platform to NAP */ + /* Setup the ppc_md struct */ - ppc_md.setup_arch = icecube_setup_arch; - ppc_md.show_cpuinfo = icecube_show_cpuinfo; + ppc_md.setup_arch = lite5200_setup_arch; + ppc_md.show_cpuinfo = lite5200_show_cpuinfo; ppc_md.show_percpuinfo = NULL; ppc_md.init_IRQ = mpc52xx_init_irq; ppc_md.get_irq = mpc52xx_get_irq; @@ -138,12 +173,12 @@ platform_init(unsigned long r3, unsigned long r4, unsigned long r5, ppc_md.restart = mpc52xx_restart; ppc_md.power_off = mpc52xx_power_off; ppc_md.halt = mpc52xx_halt; - - /* No time keeper on the IceCube */ + + /* No time keeper on the LITE5200 */ ppc_md.time_init = NULL; ppc_md.get_rtc_time = NULL; ppc_md.set_rtc_time = NULL; - + ppc_md.calibrate_decr = mpc52xx_calibrate_decr; #ifdef CONFIG_SERIAL_TEXT_DEBUG ppc_md.progress = mpc52xx_progress; diff --git a/arch/ppc/platforms/lopec.c b/arch/ppc/platforms/lopec.c new file mode 100644 index 000000000..0037f5ca4 --- /dev/null +++ b/arch/ppc/platforms/lopec.c @@ -0,0 +1,410 @@ +/* + * arch/ppc/platforms/lopec.c + * + * Setup routines for the Motorola LoPEC. + * + * Author: Dan Cox + * Maintainer: Tom Rini + * + * 2001-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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * Define all of the IRQ senses and polarities. Taken from the + * LoPEC Programmer's Reference Guide. + */ +static u_char lopec_openpic_initsenses[16] __initdata = { + (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), /* IRQ 0 */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_NEGATIVE), /* IRQ 1 */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), /* IRQ 2 */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_NEGATIVE), /* IRQ 3 */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), /* IRQ 4 */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), /* IRQ 5 */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_NEGATIVE), /* IRQ 6 */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_NEGATIVE), /* IRQ 7 */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_NEGATIVE), /* IRQ 8 */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_NEGATIVE), /* IRQ 9 */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_NEGATIVE), /* IRQ 10 */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_NEGATIVE), /* IRQ 11 */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_NEGATIVE), /* IRQ 12 */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), /* IRQ 13 */ + (IRQ_SENSE_EDGE | IRQ_POLARITY_NEGATIVE), /* IRQ 14 */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_NEGATIVE) /* IRQ 15 */ +}; + +static inline int __init +lopec_map_irq(struct pci_dev *dev, unsigned char idsel, unsigned char pin) +{ + int irq; + static char pci_irq_table[][4] = { + {16, 0, 0, 0}, /* ID 11 - Winbond */ + {22, 0, 0, 0}, /* ID 12 - SCSI */ + {0, 0, 0, 0}, /* ID 13 - nothing */ + {17, 0, 0, 0}, /* ID 14 - 82559 Ethernet */ + {27, 0, 0, 0}, /* ID 15 - USB */ + {23, 0, 0, 0}, /* ID 16 - PMC slot 1 */ + {24, 0, 0, 0}, /* ID 17 - PMC slot 2 */ + {25, 0, 0, 0}, /* ID 18 - PCI slot */ + {0, 0, 0, 0}, /* ID 19 - nothing */ + {0, 0, 0, 0}, /* ID 20 - nothing */ + {0, 0, 0, 0}, /* ID 21 - nothing */ + {0, 0, 0, 0}, /* ID 22 - nothing */ + {0, 0, 0, 0}, /* ID 23 - nothing */ + {0, 0, 0, 0}, /* ID 24 - PMC slot 1b */ + {0, 0, 0, 0}, /* ID 25 - nothing */ + {0, 0, 0, 0} /* ID 26 - PMC Slot 2b */ + }; + const long min_idsel = 11, max_idsel = 26, irqs_per_slot = 4; + + irq = PCI_IRQ_TABLE_LOOKUP; + if (!irq) + return 0; + + return irq; +} + +static void __init +lopec_setup_winbond_83553(struct pci_controller *hose) +{ + int devfn; + + devfn = PCI_DEVFN(11,0); + + /* IDE interrupt routing (primary 14, secondary 15) */ + early_write_config_byte(hose, 0, devfn, 0x43, 0xef); + /* PCI interrupt routing */ + early_write_config_word(hose, 0, devfn, 0x44, 0x0000); + + /* ISA-PCI address decoder */ + early_write_config_byte(hose, 0, devfn, 0x48, 0xf0); + + /* RTC, kb, not used in PPC */ + early_write_config_byte(hose, 0, devfn, 0x4d, 0x00); + early_write_config_byte(hose, 0, devfn, 0x4e, 0x04); + devfn = PCI_DEVFN(11, 1); + early_write_config_byte(hose, 0, devfn, 0x09, 0x8f); + early_write_config_dword(hose, 0, devfn, 0x40, 0x00ff0011); +} + +static void __init +lopec_find_bridges(void) +{ + struct pci_controller *hose; + + hose = pcibios_alloc_controller(); + if (!hose) + return; + + hose->first_busno = 0; + hose->last_busno = 0xff; + + if (mpc10x_bridge_init(hose, MPC10X_MEM_MAP_B, MPC10X_MEM_MAP_B, + MPC10X_MAPB_EUMB_BASE) == 0) { + + hose->mem_resources[0].end = 0xffffffff; + lopec_setup_winbond_83553(hose); + hose->last_busno = pciauto_bus_scan(hose, hose->first_busno); + ppc_md.pci_swizzle = common_swizzle; + ppc_md.pci_map_irq = lopec_map_irq; + } +} + +static int +lopec_show_cpuinfo(struct seq_file *m) +{ + seq_printf(m, "machine\t\t: Motorola LoPEC\n"); + return 0; +} + +static u32 +lopec_irq_canonicalize(u32 irq) +{ + if (irq == 2) + return 9; + else + return irq; +} + +static void +lopec_restart(char *cmd) +{ +#define LOPEC_SYSSTAT1 0xffe00000 + /* force a hard reset, if possible */ + unsigned char reg = *((unsigned char *) LOPEC_SYSSTAT1); + reg |= 0x80; + *((unsigned char *) LOPEC_SYSSTAT1) = reg; + + local_irq_disable(); + while(1); +#undef LOPEC_SYSSTAT1 +} + +static void +lopec_halt(void) +{ + local_irq_disable(); + while(1); +} + +static void +lopec_power_off(void) +{ + lopec_halt(); +} + +#if defined(CONFIG_BLK_DEV_IDE) || defined(CONFIG_BLK_DEV_IDE_MODULE) +int lopec_ide_ports_known = 0; +static unsigned long lopec_ide_regbase[MAX_HWIFS]; +static unsigned long lopec_ide_ctl_regbase[MAX_HWIFS]; +static unsigned long lopec_idedma_regbase; + +static void +lopec_ide_probe(void) +{ + struct pci_dev *dev = pci_find_device(PCI_VENDOR_ID_WINBOND, + PCI_DEVICE_ID_WINBOND_82C105, + NULL); + lopec_ide_ports_known = 1; + + if (dev) { + lopec_ide_regbase[0] = dev->resource[0].start; + lopec_ide_regbase[1] = dev->resource[2].start; + lopec_ide_ctl_regbase[0] = dev->resource[1].start; + lopec_ide_ctl_regbase[1] = dev->resource[3].start; + lopec_idedma_regbase = dev->resource[4].start; + } +} + +static int +lopec_ide_default_irq(unsigned long base) +{ + if (lopec_ide_ports_known == 0) + lopec_ide_probe(); + + if (base == lopec_ide_regbase[0]) + return 14; + else if (base == lopec_ide_regbase[1]) + return 15; + else + return 0; +} + +static unsigned long +lopec_ide_default_io_base(int index) +{ + if (lopec_ide_ports_known == 0) + lopec_ide_probe(); + return lopec_ide_regbase[index]; +} + +static void __init +lopec_ide_init_hwif_ports(hw_regs_t *hw, unsigned long data, + unsigned long ctl, int *irq) +{ + unsigned long reg = data; + uint alt_status_base; + int i; + + for (i = IDE_DATA_OFFSET; i <= IDE_STATUS_OFFSET; i++) + hw->io_ports[i] = reg++; + + if (data == lopec_ide_regbase[0]) { + alt_status_base = lopec_ide_ctl_regbase[0] + 2; + hw->irq = 14; + } else if (data == lopec_ide_regbase[1]) { + alt_status_base = lopec_ide_ctl_regbase[1] + 2; + hw->irq = 15; + } else { + alt_status_base = 0; + hw->irq = 0; + } + + if (ctl) + hw->io_ports[IDE_CONTROL_OFFSET] = ctl; + else + hw->io_ports[IDE_CONTROL_OFFSET] = alt_status_base; + + if (irq != NULL) + *irq = hw->irq; + +} +#endif /* BLK_DEV_IDE */ + +static void __init +lopec_init_IRQ(void) +{ + int i; + + /* + * Provide the open_pic code with the correct table of interrupts. + */ + OpenPIC_InitSenses = lopec_openpic_initsenses; + OpenPIC_NumInitSenses = sizeof(lopec_openpic_initsenses); + + mpc10x_set_openpic(); + + /* We have a cascade on OpenPIC IRQ 0, Linux IRQ 16 */ + openpic_hookup_cascade(NUM_8259_INTERRUPTS, "82c59 cascade", + &i8259_irq); + + /* Map i8259 interrupts */ + for(i = 0; i < NUM_8259_INTERRUPTS; i++) + irq_desc[i].handler = &i8259_pic; + + /* + * The EPIC allows for a read in the range of 0xFEF00000 -> + * 0xFEFFFFFF to generate a PCI interrupt-acknowledge transaction. + */ + i8259_init(0xfef00000); +} + +static int __init +lopec_request_io(void) +{ + outb(0x00, 0x4d0); + outb(0xc0, 0x4d1); + + request_region(0x00, 0x20, "dma1"); + request_region(0x20, 0x20, "pic1"); + request_region(0x40, 0x20, "timer"); + request_region(0x80, 0x10, "dma page reg"); + request_region(0xa0, 0x20, "pic2"); + request_region(0xc0, 0x20, "dma2"); + + return 0; +} + +device_initcall(lopec_request_io); + +static void __init +lopec_map_io(void) +{ + io_block_mapping(0xf0000000, 0xf0000000, 0x10000000, _PAGE_IO); + io_block_mapping(0xb0000000, 0xb0000000, 0x10000000, _PAGE_IO); +} + +/* + * Set BAT 3 to map 0xf8000000 to end of physical memory space 1-to-1. + */ +static __inline__ void +lopec_set_bat(void) +{ + mb(); + mtspr(DBAT1U, 0xf8000ffe); + mtspr(DBAT1L, 0xf800002a); + mb(); +} + +TODC_ALLOC(); + +static void __init +lopec_setup_arch(void) +{ + + TODC_INIT(TODC_TYPE_MK48T37, 0, 0, + ioremap(0xffe80000, 0x8000), 8); + + loops_per_jiffy = 100000000/HZ; + + lopec_find_bridges(); + +#ifdef CONFIG_BLK_DEV_INITRD + if (initrd_start) + ROOT_DEV = Root_RAM0; + else +#elif defined(CONFIG_ROOT_NFS) + ROOT_DEV = Root_NFS; +#elif defined(CONFIG_BLK_DEV_IDEDISK) + ROOT_DEV = Root_HDA1; +#else + ROOT_DEV = Root_SDA1; +#endif + +#ifdef CONFIG_PPCBUG_NVRAM + /* Read in NVRAM data */ + init_prep_nvram(); + + /* if no bootargs, look in NVRAM */ + if ( cmd_line[0] == '\0' ) { + char *bootargs; + bootargs = prep_nvram_get_var("bootargs"); + if (bootargs != NULL) { + strcpy(cmd_line, bootargs); + /* again.. */ + strcpy(saved_command_line, cmd_line); + } + } +#endif +} + +void __init +platform_init(unsigned long r3, unsigned long r4, unsigned long r5, + unsigned long r6, unsigned long r7) +{ + parse_bootinfo(find_bootinfo()); + lopec_set_bat(); + + isa_io_base = MPC10X_MAPB_ISA_IO_BASE; + isa_mem_base = MPC10X_MAPB_ISA_MEM_BASE; + pci_dram_offset = MPC10X_MAPB_DRAM_OFFSET; + ISA_DMA_THRESHOLD = 0x00ffffff; + DMA_MODE_READ = 0x44; + DMA_MODE_WRITE = 0x48; + + ppc_md.setup_arch = lopec_setup_arch; + ppc_md.show_cpuinfo = lopec_show_cpuinfo; + ppc_md.irq_canonicalize = lopec_irq_canonicalize; + ppc_md.init_IRQ = lopec_init_IRQ; + ppc_md.get_irq = openpic_get_irq; + + ppc_md.restart = lopec_restart; + ppc_md.power_off = lopec_power_off; + ppc_md.halt = lopec_halt; + + ppc_md.setup_io_mappings = lopec_map_io; + + ppc_md.time_init = todc_time_init; + ppc_md.set_rtc_time = todc_set_rtc_time; + ppc_md.get_rtc_time = todc_get_rtc_time; + ppc_md.calibrate_decr = todc_calibrate_decr; + + ppc_md.nvram_read_val = todc_direct_read_val; + ppc_md.nvram_write_val = todc_direct_write_val; + +#if defined(CONFIG_BLK_DEV_IDE) || defined(CONFIG_BLK_DEV_IDE_MODULE) + ppc_ide_md.default_irq = lopec_ide_default_irq; + ppc_ide_md.default_io_base = lopec_ide_default_io_base; + ppc_ide_md.ide_init_hwif = lopec_ide_init_hwif_ports; +#endif +#ifdef CONFIG_SERIAL_TEXT_DEBUG + ppc_md.progress = gen550_progress; +#endif +} diff --git a/arch/ppc/platforms/lopec.h b/arch/ppc/platforms/lopec.h new file mode 100644 index 000000000..5490edb2d --- /dev/null +++ b/arch/ppc/platforms/lopec.h @@ -0,0 +1,39 @@ +/* + * include/asm-ppc/lopec_serial.h + * + * Definitions for Motorola LoPEC board. + * + * Author: Dan Cox + * danc@mvista.com (or, alternately, source@mvista.com) + * + * 2001 (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 __H_LOPEC_SERIAL +#define __H_LOPEC_SERIAL + +#define RS_TABLE_SIZE 3 + +#define BASE_BAUD (1843200 / 16) + +#ifdef CONFIG_SERIAL_DETECT_IRQ +#define STD_COM_FLAGS (ASYNC_BOOT_AUTOCONF|ASYNC_SKIP_TEST|ASYNC_AUTO_IRQ) +#else +#define STD_COM_FLAGS (ASYNC_BOOT_AUTOCONF|ASYNC_SKIP_TEST) +#endif + +#define SERIAL_PORT_DFNS \ + { 0, BASE_BAUD, 0xffe10000, 29, STD_COM_FLAGS, \ + iomem_base: (u8 *) 0xffe10000, \ + io_type: SERIAL_IO_MEM }, \ + { 0, BASE_BAUD, 0xffe11000, 20, STD_COM_FLAGS, \ + iomem_base: (u8 *) 0xffe11000, \ + io_type: SERIAL_IO_MEM }, \ + { 0, BASE_BAUD, 0xffe12000, 21, STD_COM_FLAGS, \ + iomem_base: (u8 *) 0xffe12000, \ + io_type: SERIAL_IO_MEM } + +#endif diff --git a/arch/ppc/platforms/mcpn765.c b/arch/ppc/platforms/mcpn765.c index 06ed9384f..348118b75 100644 --- a/arch/ppc/platforms/mcpn765.c +++ b/arch/ppc/platforms/mcpn765.c @@ -58,8 +58,6 @@ #include #include "mcpn765.h" -#include "mcpn765_serial.h" - static u_char mcpn765_openpic_initsenses[] __initdata = { (IRQ_SENSE_EDGE | IRQ_POLARITY_POSITIVE),/* 16: i8259 cascade */ @@ -324,10 +322,6 @@ mcpn765_setup_arch(void) ROOT_DEV = Root_SDA2; #endif -#ifdef CONFIG_DUMMY_CONSOLE - conswitchp = &dummy_con; -#endif - if ( ppc_md.progress ) ppc_md.progress("mcpn765_setup_arch: find_bridges", 0); diff --git a/arch/ppc/platforms/mcpn765.h b/arch/ppc/platforms/mcpn765.h index 872338317..4d35ecad0 100644 --- a/arch/ppc/platforms/mcpn765.h +++ b/arch/ppc/platforms/mcpn765.h @@ -6,7 +6,7 @@ * Author: Mark A. Greer * mgreer@mvista.com * - * 2001 (c) MontaVista, Software, Inc. This file is licensed under + * 2001-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. @@ -25,6 +25,7 @@ #ifndef __PPC_PLATFORMS_MCPN765_H #define __PPC_PLATFORMS_MCPN765_H +#include /* PCI Memory space mapping info */ #define MCPN765_PCI_MEM_SIZE 0x40000000U @@ -65,14 +66,57 @@ #define MCPN765_BOARD_EXT_FEATURE_REG 0xfef880f0U #define MCPN765_BOARD_LAST_RESET_REG 0xfef880f8U -/* UART base addresses are defined in */ +/* Defines for UART */ + +/* Define the UART base addresses */ +#define MCPN765_SERIAL_1 0xfef88000 +#define MCPN765_SERIAL_2 0xfef88200 +#define MCPN765_SERIAL_3 0xfef88400 +#define MCPN765_SERIAL_4 0xfef88600 + +#ifdef CONFIG_SERIAL_MANY_PORTS +#define RS_TABLE_SIZE 64 +#else +#define RS_TABLE_SIZE 4 +#endif + +/* Rate for the 1.8432 Mhz clock for the onboard serial chip */ +#define BASE_BAUD ( 1843200 / 16 ) +#define UART_CLK 1843200 + +#ifdef CONFIG_SERIAL_DETECT_IRQ +#define STD_COM_FLAGS (ASYNC_BOOT_AUTOCONF|ASYNC_SKIP_TEST|ASYNC_AUTO_IRQ) +#else +#define STD_COM_FLAGS (ASYNC_BOOT_AUTOCONF|ASYNC_SKIP_TEST) +#endif + +/* All UART IRQ's are wire-OR'd to IRQ 17 */ +#define STD_SERIAL_PORT_DFNS \ + { 0, BASE_BAUD, MCPN765_SERIAL_1, 17, STD_COM_FLAGS, /* ttyS0 */\ + iomem_base: (u8 *)MCPN765_SERIAL_1, \ + iomem_reg_shift: 4, \ + io_type: SERIAL_IO_MEM }, \ + { 0, BASE_BAUD, MCPN765_SERIAL_2, 17, STD_COM_FLAGS, /* ttyS1 */\ + iomem_base: (u8 *)MCPN765_SERIAL_2, \ + iomem_reg_shift: 4, \ + io_type: SERIAL_IO_MEM }, \ + { 0, BASE_BAUD, MCPN765_SERIAL_3, 17, STD_COM_FLAGS, /* ttyS2 */\ + iomem_base: (u8 *)MCPN765_SERIAL_3, \ + iomem_reg_shift: 4, \ + io_type: SERIAL_IO_MEM }, \ + { 0, BASE_BAUD, MCPN765_SERIAL_4, 17, STD_COM_FLAGS, /* ttyS3 */\ + iomem_base: (u8 *)MCPN765_SERIAL_4, \ + iomem_reg_shift: 4, \ + io_type: SERIAL_IO_MEM }, + +#define SERIAL_PORT_DFNS \ + STD_SERIAL_PORT_DFNS /* Define the NVRAM/RTC address strobe & data registers */ #define MCPN765_PHYS_NVRAM_AS0 0xfef880c8U #define MCPN765_PHYS_NVRAM_AS1 0xfef880d0U #define MCPN765_PHYS_NVRAM_DATA 0xfef880d8U - extern void mcpn765_find_bridges(void); #endif /* __PPC_PLATFORMS_MCPN765_H */ diff --git a/arch/ppc/platforms/mpc5200.c b/arch/ppc/platforms/mpc5200.c index 30b6936c3..dce7225ad 100644 --- a/arch/ppc/platforms/mpc5200.c +++ b/arch/ppc/platforms/mpc5200.c @@ -16,6 +16,12 @@ #include #include + +struct ocp_fs_i2c_data mpc5200_i2c_def = { + .flags = FS_I2C_CLOCK_5200, +}; + + /* Here is the core_ocp struct. * With all the devices common to all board. Even if port multiplexing is * not setup for them (if the user don't want them, just don't select the @@ -23,6 +29,24 @@ * board specific file. */ struct ocp_def core_ocp[] = { + { + .vendor = OCP_VENDOR_FREESCALE, + .function = OCP_FUNC_IIC, + .index = 0, + .paddr = MPC52xx_I2C1, + .irq = OCP_IRQ_NA, /* MPC52xx_IRQ_I2C1 - Buggy */ + .pm = OCP_CPM_NA, + .additions = &mpc5200_i2c_def, + }, + { + .vendor = OCP_VENDOR_FREESCALE, + .function = OCP_FUNC_IIC, + .index = 1, + .paddr = MPC52xx_I2C2, + .irq = OCP_IRQ_NA, /* MPC52xx_IRQ_I2C2 - Buggy */ + .pm = OCP_CPM_NA, + .additions = &mpc5200_i2c_def, + }, { /* Terminating entry */ .vendor = OCP_VENDOR_INVALID } diff --git a/arch/ppc/platforms/mvme5100.c b/arch/ppc/platforms/mvme5100.c new file mode 100644 index 000000000..b08be2458 --- /dev/null +++ b/arch/ppc/platforms/mvme5100.c @@ -0,0 +1,349 @@ +/* + * arch/ppc/platforms/mvme5100.c + * + * Board setup routines for the Motorola MVME5100. + * + * Author: Matt Porter + * + * 2001-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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +static u_char mvme5100_openpic_initsenses[16] __initdata = { + (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), /* i8259 cascade */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_NEGATIVE), /* TL16C550 UART 1,2 */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_NEGATIVE), /* Enet1 front panel or P2 */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_NEGATIVE), /* Hawk Watchdog 1,2 */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_NEGATIVE), /* DS1621 thermal alarm */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_NEGATIVE), /* Universe II LINT0# */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_NEGATIVE), /* Universe II LINT1# */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_NEGATIVE), /* Universe II LINT2# */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_NEGATIVE), /* Universe II LINT3# */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_NEGATIVE), /* PMC1 INTA#, PMC2 INTB# */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_NEGATIVE), /* PMC1 INTB#, PMC2 INTC# */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_NEGATIVE), /* PMC1 INTC#, PMC2 INTD# */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_NEGATIVE), /* PMC1 INTD#, PMC2 INTA# */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_NEGATIVE), /* Enet 2 (front panel) */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_NEGATIVE), /* Abort Switch */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_NEGATIVE), /* RTC Alarm */ +}; + +static inline int +mvme5100_map_irq(struct pci_dev *dev, unsigned char idsel, unsigned char pin) +{ + int irq; + + static char pci_irq_table[][4] = + /* + * PCI IDSEL/INTPIN->INTLINE + * A B C D + */ + { + { 0, 0, 0, 0 }, /* IDSEL 11 - Winbond */ + { 0, 0, 0, 0 }, /* IDSEL 12 - unused */ + { 21, 22, 23, 24 }, /* IDSEL 13 - Universe II */ + { 18, 0, 0, 0 }, /* IDSEL 14 - Enet 1 */ + { 0, 0, 0, 0 }, /* IDSEL 15 - unused */ + { 25, 26, 27, 28 }, /* IDSEL 16 - PMC Slot 1 */ + { 28, 25, 26, 27 }, /* IDSEL 17 - PMC Slot 2 */ + { 0, 0, 0, 0 }, /* IDSEL 18 - unused */ + { 29, 0, 0, 0 }, /* IDSEL 19 - Enet 2 */ + { 0, 0, 0, 0 }, /* IDSEL 20 - PMCSPAN */ + }; + + const long min_idsel = 11, max_idsel = 20, irqs_per_slot = 4; + irq = PCI_IRQ_TABLE_LOOKUP; + /* If lookup is zero, always return 0 */ + if (!irq) + return 0; + else +#ifdef CONFIG_MVME5100_IPMC761_PRESENT + /* If IPMC761 present, return table value */ + return irq; +#else + /* If IPMC761 not present, we don't have an i8259 so adjust */ + return (irq - NUM_8259_INTERRUPTS); +#endif +} + +static void +mvme5100_pcibios_fixup_resources(struct pci_dev *dev) +{ + int i; + + if ((dev->vendor == PCI_VENDOR_ID_MOTOROLA) && + (dev->device == PCI_DEVICE_ID_MOTOROLA_HAWK)) + for (i=0; iresource[i].start = 0; + dev->resource[i].end = 0; + } +} + +static void __init +mvme5100_setup_bridge(void) +{ + struct pci_controller* hose; + + hose = pcibios_alloc_controller(); + + if (!hose) + return; + + hose->first_busno = 0; + hose->last_busno = 0xff; + hose->pci_mem_offset = MVME5100_PCI_MEM_OFFSET; + + pci_init_resource(&hose->io_resource, MVME5100_PCI_LOWER_IO, + MVME5100_PCI_UPPER_IO, IORESOURCE_IO, + "PCI host bridge"); + + pci_init_resource(&hose->mem_resources[0], MVME5100_PCI_LOWER_MEM, + MVME5100_PCI_UPPER_MEM, IORESOURCE_MEM, + "PCI host bridge"); + + hose->io_space.start = MVME5100_PCI_LOWER_IO; + hose->io_space.end = MVME5100_PCI_UPPER_IO; + hose->mem_space.start = MVME5100_PCI_LOWER_MEM; + hose->mem_space.end = MVME5100_PCI_UPPER_MEM; + hose->io_base_virt = (void *)MVME5100_ISA_IO_BASE; + + /* Use indirect method of Hawk */ + setup_indirect_pci(hose, MVME5100_PCI_CONFIG_ADDR, + MVME5100_PCI_CONFIG_DATA); + + hose->last_busno = pciauto_bus_scan(hose, hose->first_busno); + + ppc_md.pcibios_fixup_resources = mvme5100_pcibios_fixup_resources; + ppc_md.pci_swizzle = common_swizzle; + ppc_md.pci_map_irq = mvme5100_map_irq; +} + +static void __init +mvme5100_setup_arch(void) +{ + if ( ppc_md.progress ) + ppc_md.progress("mvme5100_setup_arch: enter", 0); + + loops_per_jiffy = 50000000 / HZ; + +#ifdef CONFIG_BLK_DEV_INITRD + if (initrd_start) + ROOT_DEV = Root_RAM0; + else +#endif +#ifdef CONFIG_ROOT_NFS + ROOT_DEV = Root_NFS; +#else + ROOT_DEV = Root_SDA2; +#endif + + if ( ppc_md.progress ) + ppc_md.progress("mvme5100_setup_arch: find_bridges", 0); + + /* Setup PCI host bridge */ + mvme5100_setup_bridge(); + + /* Find and map our OpenPIC */ + hawk_mpic_init(MVME5100_PCI_MEM_OFFSET); + OpenPIC_InitSenses = mvme5100_openpic_initsenses; + OpenPIC_NumInitSenses = sizeof(mvme5100_openpic_initsenses); + + printk("MVME5100 port (C) 2001 MontaVista Software, Inc. (source@mvista.com)\n"); + + if ( ppc_md.progress ) + ppc_md.progress("mvme5100_setup_arch: exit", 0); + + return; +} + +static void __init +mvme5100_init2(void) +{ +#ifdef CONFIG_MVME5100_IPMC761_PRESENT + request_region(0x00,0x20,"dma1"); + request_region(0x20,0x20,"pic1"); + request_region(0x40,0x20,"timer"); + request_region(0x80,0x10,"dma page reg"); + request_region(0xa0,0x20,"pic2"); + request_region(0xc0,0x20,"dma2"); +#endif + return; +} + +/* + * Interrupt setup and service. + * Have MPIC on HAWK and cascaded 8259s on Winbond cascaded to MPIC. + */ +static void __init +mvme5100_init_IRQ(void) +{ +#ifdef CONFIG_MVME5100_IPMC761_PRESENT + int i; +#endif + + if ( ppc_md.progress ) + ppc_md.progress("init_irq: enter", 0); + + openpic_set_sources(0, 16, OpenPIC_Addr + 0x10000); +#ifdef CONFIG_MVME5100_IPMC761_PRESENT + openpic_init(NUM_8259_INTERRUPTS); + openpic_hookup_cascade(NUM_8259_INTERRUPTS, "82c59 cascade", + &i8259_irq); + + /* Map i8259 interrupts. */ + for (i = 0; i < NUM_8259_INTERRUPTS; i++) + irq_desc[i].handler = &i8259_pic; + + i8259_init(NULL); +#else + openpic_init(0); +#endif + + if ( ppc_md.progress ) + ppc_md.progress("init_irq: exit", 0); + + return; +} + +/* + * Set BAT 3 to map 0xf0000000 to end of physical memory space. + */ +static __inline__ void +mvme5100_set_bat(void) +{ + mb(); + mtspr(DBAT1U, 0xf0001ffe); + mtspr(DBAT1L, 0xf000002a); + mb(); +} + +static unsigned long __init +mvme5100_find_end_of_memory(void) +{ + return hawk_get_mem_size(MVME5100_HAWK_SMC_BASE); +} + +static void __init +mvme5100_map_io(void) +{ + io_block_mapping(0xfe000000, 0xfe000000, 0x02000000, _PAGE_IO); + ioremap_base = 0xfe000000; +} + +static void +mvme5100_reset_board(void) +{ + local_irq_disable(); + + /* Set exception prefix high - to the firmware */ + _nmask_and_or_msr(0, MSR_IP); + + out_8((u_char *)MVME5100_BOARD_MODRST_REG, 0x01); + + return; +} + +static void +mvme5100_restart(char *cmd) +{ + volatile ulong i = 10000000; + + mvme5100_reset_board(); + + while (i-- > 0); + panic("restart failed\n"); +} + +static void +mvme5100_halt(void) +{ + local_irq_disable(); + while (1); +} + +static void +mvme5100_power_off(void) +{ + mvme5100_halt(); +} + +static int +mvme5100_show_cpuinfo(struct seq_file *m) +{ + seq_printf(m, "vendor\t\t: Motorola\n"); + seq_printf(m, "machine\t\t: MVME5100\n"); + + return 0; +} + +TODC_ALLOC(); + +void __init +platform_init(unsigned long r3, unsigned long r4, unsigned long r5, + unsigned long r6, unsigned long r7) +{ + parse_bootinfo(find_bootinfo()); + mvme5100_set_bat(); + + isa_io_base = MVME5100_ISA_IO_BASE; + isa_mem_base = MVME5100_ISA_MEM_BASE; + pci_dram_offset = MVME5100_PCI_DRAM_OFFSET; + + ppc_md.setup_arch = mvme5100_setup_arch; + ppc_md.show_cpuinfo = mvme5100_show_cpuinfo; + ppc_md.init_IRQ = mvme5100_init_IRQ; + ppc_md.get_irq = openpic_get_irq; + ppc_md.init = mvme5100_init2; + + ppc_md.restart = mvme5100_restart; + ppc_md.power_off = mvme5100_power_off; + ppc_md.halt = mvme5100_halt; + + ppc_md.find_end_of_memory = mvme5100_find_end_of_memory; + ppc_md.setup_io_mappings = mvme5100_map_io; + + TODC_INIT(TODC_TYPE_MK48T37, MVME5100_NVRAM_AS0, MVME5100_NVRAM_AS1, + MVME5100_NVRAM_DATA, 8); + + ppc_md.time_init = todc_time_init; + ppc_md.set_rtc_time = todc_set_rtc_time; + ppc_md.get_rtc_time = todc_get_rtc_time; + ppc_md.calibrate_decr = todc_calibrate_decr; + + ppc_md.nvram_read_val = todc_m48txx_read_val; + ppc_md.nvram_write_val = todc_m48txx_write_val; +} diff --git a/arch/ppc/platforms/mvme5100.h b/arch/ppc/platforms/mvme5100.h index 5cc61d18b..edd479439 100644 --- a/arch/ppc/platforms/mvme5100.h +++ b/arch/ppc/platforms/mvme5100.h @@ -63,10 +63,29 @@ #define MVME5100_SERIAL_IRQ 1 #endif -#define MVME5100_WINBOND_DEVFN 0x58 -#define MVME5100_WINBOND_VIDDID 0x056510ad +#define RS_TABLE_SIZE 4 -extern void mvme5100_setup_bridge(void); +#define BASE_BAUD ( MVME5100_BASE_BAUD / 16 ) + +#define STD_COM_FLAGS ASYNC_BOOT_AUTOCONF + +/* All UART IRQ's are wire-OR'd to one MPIC IRQ */ +#define STD_SERIAL_PORT_DFNS \ + { 0, BASE_BAUD, MVME5100_SERIAL_1, \ + MVME5100_SERIAL_IRQ, \ + STD_COM_FLAGS, /* ttyS0 */ \ + iomem_base: (unsigned char *)MVME5100_SERIAL_1, \ + iomem_reg_shift: 4, \ + io_type: SERIAL_IO_MEM }, \ + { 0, BASE_BAUD, MVME5100_SERIAL_2, \ + MVME5100_SERIAL_IRQ, \ + STD_COM_FLAGS, /* ttyS1 */ \ + iomem_base: (unsigned char *)MVME5100_SERIAL_2, \ + iomem_reg_shift: 4, \ + io_type: SERIAL_IO_MEM }, + +#define SERIAL_PORT_DFNS \ + STD_SERIAL_PORT_DFNS #endif /* __ASM_MVME5100_H__ */ #endif /* __KERNEL__ */ diff --git a/arch/ppc/platforms/pcore.c b/arch/ppc/platforms/pcore.c index 9c428720d..da022ecd8 100644 --- a/arch/ppc/platforms/pcore.c +++ b/arch/ppc/platforms/pcore.c @@ -228,10 +228,6 @@ pcore_setup_arch(void) ROOT_DEV = Root_SDA2; #endif -#ifdef CONFIG_DUMMY_CONSOLE - conswitchp = &dummy_con; -#endif - printk(KERN_INFO "Force PowerCore "); if (board_type == PCORE_TYPE_6750) printk("6750\n"); diff --git a/arch/ppc/platforms/pmac_pci.c b/arch/ppc/platforms/pmac_pci.c index 9dcc4e2e5..4e53bea90 100644 --- a/arch/ppc/platforms/pmac_pci.c +++ b/arch/ppc/platforms/pmac_pci.c @@ -50,6 +50,7 @@ static struct pci_controller *u3_agp; #endif /* CONFIG_POWER4 */ extern u8 pci_cache_line_size; +extern int pcibios_assign_bus_offset; struct pci_dev *k2_skiplist[2]; @@ -315,6 +316,10 @@ u3_ht_read_config(struct pci_bus *bus, unsigned int devfn, int offset, unsigned int addr; int i; + struct device_node *np = pci_busdev_to_OF_node(bus, devfn); + if (np == NULL) + return PCIBIOS_DEVICE_NOT_FOUND; + /* * When a device in K2 is powered down, we die on config * cycle accesses. Fix that here. @@ -362,6 +367,9 @@ u3_ht_write_config(struct pci_bus *bus, unsigned int devfn, int offset, unsigned int addr; int i; + struct device_node *np = pci_busdev_to_OF_node(bus, devfn); + if (np == NULL) + return PCIBIOS_DEVICE_NOT_FOUND; /* * When a device in K2 is powered down, we die on config * cycle accesses. Fix that here. @@ -565,6 +573,14 @@ pmac_find_bridges(void) init_p2pbridge(); fixup_nec_usb2(); + + /* We are still having some issues with the Xserve G4, enabling + * some offset between bus number and domains for now when we + * assign all busses should help for now + */ + if (pci_assign_all_busses) + pcibios_assign_bus_offset = 0x10; + #ifdef CONFIG_POWER4 /* There is something wrong with DMA on U3/HT. I haven't figured out * the details yet, but if I set the cache line size to 128 bytes like @@ -707,7 +723,7 @@ setup_u3_ht(struct pci_controller* hose, struct reg_property *addr) * properties or figuring out the U3 address space decoding logic and * then read its configuration register (if any). */ - hose->io_base_phys = 0xf4000000 + 0x00400000; + hose->io_base_phys = 0xf4000000; hose->io_base_virt = ioremap(hose->io_base_phys, 0x00400000); isa_io_base = (unsigned long) hose->io_base_virt; hose->io_resource.name = np->full_name; @@ -1034,6 +1050,8 @@ void pmac_pci_fixup_cardbus(struct pci_dev* dev) } } +DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_TI, PCI_ANY_ID, pmac_pci_fixup_cardbus); + void pmac_pci_fixup_pciata(struct pci_dev* dev) { u8 progif = 0; @@ -1074,6 +1092,8 @@ void pmac_pci_fixup_pciata(struct pci_dev* dev) printk(KERN_ERR "Rewrite of PROGIF failed !\n"); } } +DECLARE_PCI_FIXUP_FINAL(PCI_ANY_ID, PCI_ANY_ID, pmac_pci_fixup_pciata); + /* * Disable second function on K2-SATA, it's broken @@ -1104,3 +1124,4 @@ void __pmac pmac_pci_fixup_k2_sata(struct pci_dev* dev) } } } +DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_SERVERWORKS, 0x0240, pmac_pci_fixup_k2_sata); diff --git a/arch/ppc/platforms/pmac_smp.c b/arch/ppc/platforms/pmac_smp.c index 704742257..1a300ff40 100644 --- a/arch/ppc/platforms/pmac_smp.c +++ b/arch/ppc/platforms/pmac_smp.c @@ -32,13 +32,13 @@ #include #include #include +#include #include #include #include #include #include -#include #include #include #include diff --git a/arch/ppc/platforms/pmac_time.c b/arch/ppc/platforms/pmac_time.c index cd8e3e7c4..09636546f 100644 --- a/arch/ppc/platforms/pmac_time.c +++ b/arch/ppc/platforms/pmac_time.c @@ -19,6 +19,7 @@ #include #include #include +#include #include #include @@ -26,7 +27,6 @@ #include #include #include -#include #include #include diff --git a/arch/ppc/platforms/powerpmc250.h b/arch/ppc/platforms/powerpmc250.h index b834e1786..41a6dc881 100644 --- a/arch/ppc/platforms/powerpmc250.h +++ b/arch/ppc/platforms/powerpmc250.h @@ -7,7 +7,7 @@ * * Borrowed heavily from prpmc750.h by Matt Porter * - * 2001 (c) MontaVista, Software, Inc. This file is licensed under + * 2001-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. @@ -16,8 +16,6 @@ #ifndef __ASMPPC_POWERPMC250_H #define __ASMPPC_POWERPMC250_H -#include - #define POWERPMC250_PCI_CONFIG_ADDR 0x80000cf8 #define POWERPMC250_PCI_CONFIG_DATA 0x80000cfc @@ -37,4 +35,18 @@ #define POWERPMC250_SERIAL 0xff000000 #define POWERPMC250_SERIAL_IRQ 20 +/* UART Defines. */ +#define RS_TABLE_SIZE 1 + +#define BASE_BAUD (POWERPMC250_BASE_BAUD / 16) + +#define STD_COM_FLAGS ASYNC_BOOT_AUTOCONF + +#define SERIAL_PORT_DFNS \ + { 0, BASE_BAUD, POWERPMC250_SERIAL, POWERPMC250_SERIAL_IRQ, \ + STD_COM_FLAGS, /* ttyS0 */ \ + iomem_base: (u8 *)POWERPMC250_SERIAL, \ + iomem_reg_shift: 0, \ + io_type: SERIAL_IO_MEM } + #endif /* __ASMPPC_POWERPMC250_H */ diff --git a/arch/ppc/platforms/pq2ads.c b/arch/ppc/platforms/pq2ads.c new file mode 100644 index 000000000..cecaba630 --- /dev/null +++ b/arch/ppc/platforms/pq2ads.c @@ -0,0 +1,26 @@ +/* + * arch/ppc/platforms/pq2ads.c + * + * PQ2ADS platform support + * + * Author: Kumar Gala + * Derived from: est8260_setup.c by Allen Curtis + * + * Copyright 2004 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 + +#include + +void __init +m82xx_board_init(void) +{ + /* Enable the 2nd UART port */ + *(volatile uint *)(BCSR_ADDR + 4) &= ~BCSR1_RS232_EN2; +} diff --git a/arch/ppc/platforms/pq2ads.h b/arch/ppc/platforms/pq2ads.h index 1bee6ca56..0c17a0cda 100644 --- a/arch/ppc/platforms/pq2ads.h +++ b/arch/ppc/platforms/pq2ads.h @@ -23,6 +23,10 @@ #define BOOTROM_RESTART_ADDR ((uint)0xff000104) +/* For our show_cpuinfo hooks. */ +#define CPUINFO_VENDOR "Motorola" +#define CPUINFO_MACHINE "PQ2 ADS PowerPC" + /* The ADS8260 has 16, 32-bit wide control/status registers, accessed * only on word boundaries. * Not all are used (yet), or are interesting to us (yet). diff --git a/arch/ppc/platforms/prep_pci.c b/arch/ppc/platforms/prep_pci.c index 58775fc7f..a528562cc 100644 --- a/arch/ppc/platforms/prep_pci.c +++ b/arch/ppc/platforms/prep_pci.c @@ -836,52 +836,59 @@ struct mot_info { void __init ibm_prep_init(void) { -#ifdef CONFIG_PREP_RESIDUAL - u32 addr, real_addr, len; - PPC_DEVICE *mpic; - PnP_TAG_PACKET *pkt; - - /* Use the PReP residual data to determine if an OpenPIC is - * present. If so, get the large vendor packet which will - * tell us the base address and length in memory. - * If we are successful, ioremap the memory area and set - * OpenPIC_Addr (this indicates that the OpenPIC was found). - */ - mpic = residual_find_device(-1, NULL, SystemPeripheral, - ProgrammableInterruptController, MPIC, 0); - if (!mpic) - return; + if (have_residual_data) { + u32 addr, real_addr, len, offset; + PPC_DEVICE *mpic; + PnP_TAG_PACKET *pkt; + + /* Use the PReP residual data to determine if an OpenPIC is + * present. If so, get the large vendor packet which will + * tell us the base address and length in memory. + * If we are successful, ioremap the memory area and set + * OpenPIC_Addr (this indicates that the OpenPIC was found). + */ + mpic = residual_find_device(-1, NULL, SystemPeripheral, + ProgrammableInterruptController, MPIC, 0); + if (!mpic) + return; - pkt = PnP_find_large_vendor_packet(res->DevicePnPHeap + - mpic->AllocatedOffset, 9, 0); + pkt = PnP_find_large_vendor_packet(res->DevicePnPHeap + + mpic->AllocatedOffset, 9, 0); - if (!pkt) - return; + if (!pkt) + return; #define p pkt->L4_Pack.L4_Data.L4_PPCPack - if (!((p.PPCData[0] == 2) && (p.PPCData[1] == 32))) - return; /* not a 32-bit memory address */ + if (p.PPCData[1] == 32) { + switch (p.PPCData[0]) { + case 1: offset = PREP_ISA_IO_BASE; break; + case 2: offset = PREP_ISA_MEM_BASE; break; + default: return; /* Not I/O or memory?? */ + } + } + else + return; /* Not a 32-bit address */ - real_addr = ld_le32((unsigned int *) (p.PPCData + 4)); - if (real_addr == 0xffffffff) - return; + real_addr = ld_le32((unsigned int *) (p.PPCData + 4)); + if (real_addr == 0xffffffff) + return; - /* Adjust address to be as seen by CPU */ - addr = real_addr + PREP_ISA_MEM_BASE; + /* Adjust address to be as seen by CPU */ + addr = real_addr + offset; - len = ld_le32((unsigned int *) (p.PPCData + 12)); - if (!len) - return; + len = ld_le32((unsigned int *) (p.PPCData + 12)); + if (!len) + return; #undef p - OpenPIC_Addr = ioremap(addr, len); - ppc_md.get_irq = openpic_get_irq; + OpenPIC_Addr = ioremap(addr, len); + ppc_md.get_irq = openpic_get_irq; - OpenPIC_InitSenses = prep_openpic_initsenses; - OpenPIC_NumInitSenses = sizeof(prep_openpic_initsenses); + OpenPIC_InitSenses = prep_openpic_initsenses; + OpenPIC_NumInitSenses = sizeof(prep_openpic_initsenses); - printk(KERN_INFO "MPIC at 0x%08x (0x%08x), length 0x%08x " - "mapped to 0x%p\n", addr, real_addr, len, OpenPIC_Addr); -#endif + printk(KERN_INFO "MPIC at 0x%08x (0x%08x), length 0x%08x " + "mapped to 0x%p\n", addr, real_addr, len, OpenPIC_Addr); + } } static void __init @@ -900,6 +907,17 @@ ibm43p_pci_map_non0(struct pci_dev *dev) pci_write_config_byte(dev, PCI_INTERRUPT_LINE, dev->irq); } +void __init +prep_residual_setup_pci(char *irq_edge_mask_lo, char *irq_edge_mask_hi) +{ + if (have_residual_data) { + Motherboard_map_name = res->VitalProductData.PrintableModel; + Motherboard_map = NULL; + Motherboard_routes = NULL; + residual_irq_mask(irq_edge_mask_lo, irq_edge_mask_hi); + } +} + void __init prep_sandalfoot_setup_pci(char *irq_edge_mask_lo, char *irq_edge_mask_hi) { @@ -1011,21 +1029,31 @@ prep_route_pci_interrupts(void) } } else if ( _prep_type == _PREP_IBM ) { unsigned char irq_edge_mask_lo, irq_edge_mask_hi; + unsigned short irq_edge_mask; + int i; setup_ibm_pci(&irq_edge_mask_lo, &irq_edge_mask_hi); outb(inb(0x04d0)|irq_edge_mask_lo, 0x4d0); /* primary 8259 */ outb(inb(0x04d1)|irq_edge_mask_hi, 0x4d1); /* cascaded 8259 */ + + irq_edge_mask = (irq_edge_mask_hi << 8) | irq_edge_mask_lo; + for (i = 0; i < 16; ++i, irq_edge_mask >>= 1) + if (irq_edge_mask & 1) + irq_desc[i].status |= IRQ_LEVEL; } else { printk("No known machine pci routing!\n"); return; } /* Set up mapping from slots */ - for (i = 1; i <= 4; i++) - ibc_pirq[i-1] = Motherboard_routes[i]; - /* Enable PCI interrupts */ - *ibc_pcicon |= 0x20; + if (Motherboard_routes) { + for (i = 1; i <= 4; i++) + ibc_pirq[i-1] = Motherboard_routes[i]; + + /* Enable PCI interrupts */ + *ibc_pcicon |= 0x20; + } } void __init @@ -1171,38 +1199,52 @@ void __init prep_pcibios_fixup(void) { struct pci_dev *dev = NULL; + int irq; + int have_openpic = (OpenPIC_Addr != NULL); prep_route_pci_interrupts(); printk("Setting PCI interrupts for a \"%s\"\n", Motherboard_map_name); - if (OpenPIC_Addr) { - /* PCI interrupts are controlled by the OpenPIC */ - while ((dev = pci_find_device(PCI_ANY_ID, PCI_ANY_ID, dev)) != NULL) { - if (dev->bus->number == 0) { - dev->irq = openpic_to_irq(Motherboard_map[PCI_SLOT(dev->devfn)]); - pci_write_config_byte(dev, PCI_INTERRUPT_LINE, dev->irq); - } else { - if (Motherboard_non0 != NULL) - Motherboard_non0(dev); - } - } - - /* Setup the Winbond or Via PIB */ - prep_pib_init(); - - return; - } - dev = NULL; + /* Iterate through all the PCI devices, setting the IRQ */ while ((dev = pci_find_device(PCI_ANY_ID, PCI_ANY_ID, dev)) != NULL) { /* - * Use our old hard-coded kludge to figure out what - * irq this device uses. This is necessary on things - * without residual data. -- Cort + * If we have residual data, then this is easy: query the + * residual data for the IRQ line allocated to the device. + * This works the same whether we have an OpenPic or not. + */ + if (have_residual_data) { + irq = residual_pcidev_irq(dev); + dev->irq = have_openpic ? openpic_to_irq(irq) : irq; + } + /* + * If we don't have residual data, then we need to use + * tables to determine the IRQ. The table organisation + * is different depending on whether there is an OpenPIC + * or not. The tables are only used for bus 0, so check + * this first. */ - unsigned char d = PCI_SLOT(dev->devfn); - dev->irq = Motherboard_routes[Motherboard_map[d]]; + else if (dev->bus->number == 0) { + irq = Motherboard_map[PCI_SLOT(dev->devfn)]; + dev->irq = have_openpic ? openpic_to_irq(irq) + : Motherboard_routes[irq]; + } + /* + * Finally, if we don't have residual data and the bus is + * non-zero, use the callback (if provided) + */ + else { + if (Motherboard_non0 != NULL) + Motherboard_non0(dev); + + continue; + } + + pci_write_config_byte(dev, PCI_INTERRUPT_LINE, dev->irq); } + + /* Setup the Winbond or Via PIB */ + prep_pib_init(); } static void __init @@ -1262,14 +1304,15 @@ prep_find_bridges(void) PREP_ISA_IO_BASE + 0xcfc); printk("PReP architecture\n"); -#ifdef CONFIG_PREP_RESIDUAL - { + + if (have_residual_data) { PPC_DEVICE *hostbridge; hostbridge = residual_find_device(PROCESSORDEVICE, NULL, BridgeController, PCIBridge, -1, 0); if (hostbridge && - hostbridge->DeviceId.Interface == PCIBridgeIndirect) { + ((hostbridge->DeviceId.Interface == PCIBridgeIndirect) || + (hostbridge->DeviceId.Interface == PCIBridgeRS6K))) { PnP_TAG_PACKET * pkt; pkt = PnP_find_large_vendor_packet( res->DevicePnPHeap+hostbridge->AllocatedOffset, @@ -1284,7 +1327,6 @@ prep_find_bridges(void) setup_indirect_pci(hose, 0x80000cf8, 0x80000cfc); } } -#endif /* CONFIG_PREP_RESIDUAL */ ppc_md.pcibios_fixup = prep_pcibios_fixup; ppc_md.pcibios_after_init = prep_pcibios_after_init; diff --git a/arch/ppc/platforms/prpmc750.c b/arch/ppc/platforms/prpmc750.c index e0ab8e301..a75553c9a 100644 --- a/arch/ppc/platforms/prpmc750.c +++ b/arch/ppc/platforms/prpmc750.c @@ -193,10 +193,6 @@ static void __init prpmc750_setup_arch(void) ROOT_DEV = Root_SDA2; #endif -#ifdef CONFIG_DUMMY_CONSOLE - conswitchp = &dummy_con; -#endif - OpenPIC_InitSenses = prpmc750_openpic_initsenses; OpenPIC_NumInitSenses = sizeof(prpmc750_openpic_initsenses); diff --git a/arch/ppc/platforms/prpmc750.h b/arch/ppc/platforms/prpmc750.h index 23ed94140..015b4f52c 100644 --- a/arch/ppc/platforms/prpmc750.h +++ b/arch/ppc/platforms/prpmc750.h @@ -5,20 +5,16 @@ * * Author: Matt Porter * - * Copyright 2001 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. + * 2001-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. */ #ifdef __KERNEL__ #ifndef __ASM_PRPMC750_H__ #define __ASM_PRPMC750_H__ -#include - /* * Due to limiations imposed by legacy hardware (primaryily IDE controllers), * the PrPMC750 carrier board operates using a PReP address map. @@ -81,5 +77,19 @@ #define PRPMC750_TBEN_REG 0xfef880c0 #define PRPMC750_TBEN_MASK 0x01 +/* UART Defines. */ +#define RS_TABLE_SIZE 4 + +/* Rate for the 1.8432 Mhz clock for the onboard serial chip */ +#define BASE_BAUD (PRPMC750_BASE_BAUD / 16) + +#define STD_COM_FLAGS ASYNC_BOOT_AUTOCONF + +#define SERIAL_PORT_DFNS \ + { 0, BASE_BAUD, PRPMC750_SERIAL_0, 1, STD_COM_FLAGS, \ + iomem_base: (unsigned char *)PRPMC750_SERIAL_0, \ + iomem_reg_shift: 4, \ + io_type: SERIAL_IO_MEM } /* ttyS0 */ + #endif /* __ASM_PRPMC750_H__ */ #endif /* __KERNEL__ */ diff --git a/arch/ppc/platforms/prpmc800.c b/arch/ppc/platforms/prpmc800.c index 0ffcfdfb1..0e99f8bc3 100644 --- a/arch/ppc/platforms/prpmc800.c +++ b/arch/ppc/platforms/prpmc800.c @@ -309,10 +309,6 @@ static void __init prpmc800_setup_arch(void) ROOT_DEV = Root_SDA2; #endif -#ifdef CONFIG_DUMMY_CONSOLE - conswitchp = &dummy_con; -#endif - printk(KERN_INFO "Port by MontaVista Software, Inc. " "(source@mvista.com)\n"); } diff --git a/arch/ppc/platforms/prpmc800.h b/arch/ppc/platforms/prpmc800.h index 4af2f2c9a..e53ec9b42 100644 --- a/arch/ppc/platforms/prpmc800.h +++ b/arch/ppc/platforms/prpmc800.h @@ -5,12 +5,10 @@ * * Author: Dale Farnsworth * - * Copyright 2001 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. + * 2001-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. */ /* * From Processor to PCI: @@ -63,4 +61,22 @@ #define PRPMC800_INT_IRQ 16 #define PRPMC800_INT_PRI 15 +/* UART Defines. */ +#define RS_TABLE_SIZE 4 + +/* Rate for the 1.8432 Mhz clock for the onboard serial chip */ +#define BASE_BAUD (PRPMC800_BASE_BAUD / 16) + +#define STD_COM_FLAGS ASYNC_BOOT_AUTOCONF + +/* UARTS are at IRQ 16 */ +#define STD_SERIAL_PORT_DFNS \ + { 0, BASE_BAUD, PRPMC800_SERIAL_1, 16, STD_COM_FLAGS, /* ttyS0 */\ + iomem_base: (unsigned char *)PRPMC800_SERIAL_1, \ + iomem_reg_shift: 0, \ + io_type: SERIAL_IO_MEM }, + +#define SERIAL_PORT_DFNS \ + STD_SERIAL_PORT_DFNS + #endif /* __ASMPPC_PRPMC800_H */ diff --git a/arch/ppc/platforms/residual.c b/arch/ppc/platforms/residual.c index b094e2715..0f84ca603 100644 --- a/arch/ppc/platforms/residual.c +++ b/arch/ppc/platforms/residual.c @@ -504,7 +504,7 @@ void __init print_residual_device_info(void) #define did dev->DeviceId /* make sure we have residual data first */ - if ( res->ResidualLength == 0 ) + if (!have_residual_data) return; printk("Residual: %ld devices\n", res->ActualNumDevices); @@ -639,7 +639,7 @@ void print_residual_device_info(void) #define did dev->DeviceId /* make sure we have residual data first */ - if ( res->ResidualLength == 0 ) + if (!have_residual_data) return; printk("Residual: %ld devices\n", res->ActualNumDevices); for ( i = 0; @@ -790,7 +790,7 @@ PPC_DEVICE __init *residual_find_device(unsigned long BusMask, int n) { int i; - if ( !res->ResidualLength ) return NULL; + if (!have_residual_data) return NULL; for (i=0; iActualNumDevices; i++) { #define Dev res->Devices[i].DeviceId if ( (Dev.BusId&BusMask) && @@ -813,7 +813,7 @@ PPC_DEVICE __init *residual_find_device_id(unsigned long BusMask, int n) { int i; - if ( !res->ResidualLength ) return NULL; + if (!have_residual_data) return NULL; for (i=0; iActualNumDevices; i++) { #define Dev res->Devices[i].DeviceId if ( (Dev.BusId&BusMask) && @@ -827,6 +827,129 @@ PPC_DEVICE __init *residual_find_device_id(unsigned long BusMask, return NULL; } +static int __init +residual_scan_pcibridge(PnP_TAG_PACKET * pkt, struct pci_dev *dev) +{ + int irq = -1; + +#define data pkt->L4_Pack.L4_Data.L4_PPCPack.PPCData + if (dev->bus->number == data[16]) { + int i, size; + + size = 3 + ld_le16((u_short *) (&pkt->L4_Pack.Count0)); + for (i = 20; i < size - 4; i += 12) { + unsigned char pin; + int line_irq; + + if (dev->devfn != data[i + 1]) + continue; + + pci_read_config_byte(dev, PCI_INTERRUPT_PIN, &pin); + if (pin) { + line_irq = ld_le16((unsigned short *) + (&data[i + 4 + 2 * (pin - 1)])); + irq = (line_irq == 0xffff) ? 0 + : line_irq & 0x7fff; + } else + irq = 0; + + break; + } + } +#undef data + + return irq; +} + +int __init +residual_pcidev_irq(struct pci_dev *dev) +{ + int i = 0; + int irq = -1; + PPC_DEVICE *bridge; + + while ((bridge = residual_find_device + (-1, NULL, BridgeController, PCIBridge, -1, i++))) { + + PnP_TAG_PACKET *pkt; + if (bridge->AllocatedOffset) { + pkt = PnP_find_large_vendor_packet(res->DevicePnPHeap + + bridge->AllocatedOffset, 3, 0); + if (!pkt) + continue; + + irq = residual_scan_pcibridge(pkt, dev); + if (irq != -1) + break; + } + } + + return (irq < 0) ? 0 : irq; +} + +void __init residual_irq_mask(char *irq_edge_mask_lo, char *irq_edge_mask_hi) +{ + PPC_DEVICE *dev; + int i = 0; + unsigned short irq_mask = 0x000; /* default to edge */ + + while ((dev = residual_find_device(-1, NULL, -1, -1, -1, i++))) { + PnP_TAG_PACKET *pkt; + unsigned short mask; + int size; + int offset = dev->AllocatedOffset; + + if (!offset) + continue; + + pkt = PnP_find_packet(res->DevicePnPHeap + offset, + IRQFormat, 0); + if (!pkt) + continue; + + size = tag_small_count(pkt->S1_Pack.Tag) + 1; + mask = ld_le16((unsigned short *)pkt->S4_Pack.IRQMask); + if (size > 3 && (pkt->S4_Pack.IRQInfo & 0x0c)) + irq_mask |= mask; + } + + *irq_edge_mask_lo = irq_mask & 0xff; + *irq_edge_mask_hi = irq_mask >> 8; +} + +unsigned int __init residual_isapic_addr(void) +{ + PPC_DEVICE *isapic; + PnP_TAG_PACKET *pkt; + unsigned int addr; + + isapic = residual_find_device(~0, NULL, SystemPeripheral, + ProgrammableInterruptController, + ISA_PIC, 0); + if (!isapic) + goto unknown; + + pkt = PnP_find_large_vendor_packet(res->DevicePnPHeap + + isapic->AllocatedOffset, 9, 0); + if (!pkt) + goto unknown; + +#define p pkt->L4_Pack.L4_Data.L4_PPCPack + /* Must be 32-bit system address */ + if (!((p.PPCData[0] == 3) && (p.PPCData[1] == 32))) + goto unknown; + + /* It doesn't seem to work where length != 1 (what can I say? :-/ ) */ + if (ld_le32((unsigned int *)(p.PPCData + 12)) != 1) + goto unknown; + + addr = ld_le32((unsigned int *) (p.PPCData + 4)); +#undef p + return addr; +unknown: + return 0; +} + PnP_TAG_PACKET *PnP_find_packet(unsigned char *p, unsigned packet_tag, int n) @@ -901,7 +1024,7 @@ static int proc_prep_residual_read(char * buf, char ** start, off_t off, int __init proc_prep_residual_init(void) { - if (res->ResidualLength) + if (have_residual_data) create_proc_read_entry("residual", S_IRUGO, NULL, proc_prep_residual_read, NULL); return 0; diff --git a/arch/ppc/platforms/rpx8260.h b/arch/ppc/platforms/rpx8260.h index 7d5cd8893..843494a50 100644 --- a/arch/ppc/platforms/rpx8260.h +++ b/arch/ppc/platforms/rpx8260.h @@ -70,5 +70,12 @@ extern volatile u_char *rpx6_csr_addr; #define PHY_INTERRUPT SIU_INT_IRQ7 +/* For our show_cpuinfo hooks. */ +#define CPUINFO_VENDOR "Embedded Planet" +#define CPUINFO_MACHINE "EP8260 PowerPC" + +/* Warm reset vector. */ +#define BOOTROM_RESTART_ADDR ((uint)0xfff00104) + #endif /* __ASM_PLATFORMS_RPX8260_H__ */ #endif /* __KERNEL__ */ diff --git a/arch/ppc/platforms/spruce.c b/arch/ppc/platforms/spruce.c index ad671021e..74be32456 100644 --- a/arch/ppc/platforms/spruce.c +++ b/arch/ppc/platforms/spruce.c @@ -228,11 +228,6 @@ spruce_setup_arch(void) ROOT_DEV = Root_SDA1; #endif -#ifdef CONFIG_VT - conswitchp = &dummy_con; -#endif - - /* Identify the system */ printk(KERN_INFO "System Identification: IBM Spruce\n"); printk(KERN_INFO "Port by MontaVista Software, Inc. (source@mvista.com)\n"); diff --git a/arch/ppc/syslib/cpm2_pic.c b/arch/ppc/syslib/cpm2_pic.c index 43eac4135..bd8335b84 100644 --- a/arch/ppc/syslib/cpm2_pic.c +++ b/arch/ppc/syslib/cpm2_pic.c @@ -1,12 +1,3 @@ -#include -#include -#include -#include -#include -#include -#include -#include "cpm2_pic.h" - /* The CPM2 internal interrupt controller. It is usually * the only interrupt controller. * There are two 32-bit registers (high/low) for up to 64 @@ -18,6 +9,18 @@ * We create two tables, indexed by vector number, to indicate * which register to use and which bit in the register to use. */ + +#include +#include +#include +#include +#include + +#include +#include + +#include "cpm2_pic.h" + static u_char irq_to_siureg[] = { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, @@ -123,9 +126,5 @@ cpm2_get_irq(struct pt_regs *regs) if (irq == 0) return(-1); -#if 0 - irq += ppc8260_pic.irq_offset; -#endif return irq; } - diff --git a/arch/ppc/syslib/cpm2_pic.h b/arch/ppc/syslib/cpm2_pic.h index a9da44168..d05003b09 100644 --- a/arch/ppc/syslib/cpm2_pic.h +++ b/arch/ppc/syslib/cpm2_pic.h @@ -1,13 +1,7 @@ #ifndef _PPC_KERNEL_CPM2_H #define _PPC_KERNEL_CPM2_H -#include - extern struct hw_interrupt_type cpm2_pic; - -void cpm2_pic_init(void); -void cpm2_do_IRQ(struct pt_regs *regs, - int cpu); -int cpm2_get_irq(struct pt_regs *regs); +extern int cpm2_get_irq(struct pt_regs *regs); #endif /* _PPC_KERNEL_CPM2_H */ diff --git a/arch/ppc/syslib/hawk_common.c b/arch/ppc/syslib/hawk_common.c index 9c2e1506a..a9911dc3a 100644 --- a/arch/ppc/syslib/hawk_common.c +++ b/arch/ppc/syslib/hawk_common.c @@ -285,3 +285,35 @@ hawk_get_mem_size(uint smc_base) return total; } + +int __init +hawk_mpic_init(unsigned int pci_mem_offset) +{ + unsigned short devid; + unsigned int pci_membase; + + /* Check the first PCI device to see if it is a Raven or Hawk. */ + early_read_config_word(0, 0, 0, PCI_DEVICE_ID, &devid); + + switch (devid) { + case PCI_DEVICE_ID_MOTOROLA_RAVEN: + case PCI_DEVICE_ID_MOTOROLA_HAWK: + break; + default: + OpenPIC_Addr = NULL; + return 1; + } + + /* Read the memory base register. */ + early_read_config_dword(0, 0, 0, PCI_BASE_ADDRESS_1, &pci_membase); + + if (pci_membase == 0) { + OpenPIC_Addr = NULL; + return 1; + } + + /* Map the MPIC registers to virtual memory. */ + OpenPIC_Addr = ioremap(pci_membase + pci_mem_offset, 0x22000); + + return 0; +} diff --git a/arch/ppc/syslib/ibm44x_common.c b/arch/ppc/syslib/ibm44x_common.c index a0cefdc7c..76d18d337 100644 --- a/arch/ppc/syslib/ibm44x_common.c +++ b/arch/ppc/syslib/ibm44x_common.c @@ -6,6 +6,9 @@ * Matt Porter * Copyright 2002-2003 MontaVista Software Inc. * + * Eugene Surovegin or + * Copyright (c) 2003, 2004 Zultys Technologies + * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the * Free Software Foundation; either version 2 of the License, or (at your @@ -14,8 +17,14 @@ */ #include #include +#include + +#include #include #include +#include +#include +#include phys_addr_t fixup_bigphys_addr(phys_addr_t addr, phys_addr_t size) { @@ -35,3 +44,159 @@ phys_addr_t fixup_bigphys_addr(phys_addr_t addr, phys_addr_t size) return (page_4gb | addr); }; + +void __init ibm44x_calibrate_decr(unsigned int freq) +{ + tb_ticks_per_jiffy = freq / HZ; + tb_to_us = mulhwu_scale_factor(freq, 1000000); + + /* Set the time base to zero */ + mtspr(SPRN_TBWL, 0); + mtspr(SPRN_TBWU, 0); + + /* Clear any pending timer interrupts */ + mtspr(SPRN_TSR, TSR_ENW | TSR_WIS | TSR_DIS | TSR_FIS); + + /* Enable decrementer interrupt */ + mtspr(SPRN_TCR, TCR_DIE); +} + +extern void abort(void); + +static void ibm44x_restart(char *cmd) +{ + local_irq_disable(); + abort(); +} + +static void ibm44x_power_off(void) +{ + local_irq_disable(); + for(;;); +} + +static void ibm44x_halt(void) +{ + local_irq_disable(); + for(;;); +} + +/* + * Read the 44x memory controller to get size of system memory. + */ +static unsigned long __init ibm44x_find_end_of_memory(void) +{ + u32 i, bank_config; + u32 mem_size = 0; + + for (i=0; i<4; i++) + { + switch (i) + { + case 0: + mtdcr(DCRN_SDRAM0_CFGADDR, SDRAM0_B0CR); + break; + case 1: + mtdcr(DCRN_SDRAM0_CFGADDR, SDRAM0_B1CR); + break; + case 2: + mtdcr(DCRN_SDRAM0_CFGADDR, SDRAM0_B2CR); + break; + case 3: + mtdcr(DCRN_SDRAM0_CFGADDR, SDRAM0_B3CR); + break; + } + + bank_config = mfdcr(DCRN_SDRAM0_CFGDATA); + + if (!(bank_config & SDRAM_CONFIG_BANK_ENABLE)) + continue; + switch (SDRAM_CONFIG_BANK_SIZE(bank_config)) + { + case SDRAM_CONFIG_SIZE_8M: + mem_size += PPC44x_MEM_SIZE_8M; + break; + case SDRAM_CONFIG_SIZE_16M: + mem_size += PPC44x_MEM_SIZE_16M; + break; + case SDRAM_CONFIG_SIZE_32M: + mem_size += PPC44x_MEM_SIZE_32M; + break; + case SDRAM_CONFIG_SIZE_64M: + mem_size += PPC44x_MEM_SIZE_64M; + break; + case SDRAM_CONFIG_SIZE_128M: + mem_size += PPC44x_MEM_SIZE_128M; + break; + case SDRAM_CONFIG_SIZE_256M: + mem_size += PPC44x_MEM_SIZE_256M; + break; + case SDRAM_CONFIG_SIZE_512M: + mem_size += PPC44x_MEM_SIZE_512M; + break; + } + } + return mem_size; +} + +static void __init ibm44x_init_irq(void) +{ + int i; + + ppc4xx_pic_init(); + + for (i = 0; i < NR_IRQS; i++) + irq_desc[i].handler = ppc4xx_pic; +} + +#ifdef CONFIG_SERIAL_TEXT_DEBUG +#include +#include +#include + +static struct serial_state rs_table[RS_TABLE_SIZE] = { + SERIAL_PORT_DFNS /* Defined in */ +}; + +static void ibm44x_progress(char *s, unsigned short hex) +{ + volatile char c; + volatile unsigned long com_port; + u16 shift; + + com_port = (unsigned long)rs_table[0].iomem_base; + shift = rs_table[0].iomem_reg_shift; + + while ((c = *s++) != 0) { + while ((*((volatile unsigned char *)com_port + + (UART_LSR << shift)) & UART_LSR_THRE) == 0) + ; + *(volatile unsigned char *)com_port = c; + + } + + /* Send LF/CR to pretty up output */ + while ((*((volatile unsigned char *)com_port + + (UART_LSR << shift)) & UART_LSR_THRE) == 0) + ; + *(volatile unsigned char *)com_port = '\r'; + while ((*((volatile unsigned char *)com_port + + (UART_LSR << shift)) & UART_LSR_THRE) == 0) + ; + *(volatile unsigned char *)com_port = '\n'; +} +#endif /* CONFIG_SERIAL_TEXT_DEBUG */ + +void __init ibm44x_platform_init(void) +{ + ppc_md.init_IRQ = ibm44x_init_irq; + ppc_md.find_end_of_memory = ibm44x_find_end_of_memory; + ppc_md.restart = ibm44x_restart; + ppc_md.power_off = ibm44x_power_off; + ppc_md.halt = ibm44x_halt; + +#ifdef CONFIG_SERIAL_TEXT_DEBUG + ppc_md.progress = ibm44x_progress; +#endif /* CONFIG_SERIAL_TEXT_DEBUG */ +} + diff --git a/arch/ppc/syslib/ibm44x_common.h b/arch/ppc/syslib/ibm44x_common.h index ee1053ac2..b14eb603c 100644 --- a/arch/ppc/syslib/ibm44x_common.h +++ b/arch/ppc/syslib/ibm44x_common.h @@ -4,7 +4,7 @@ * PPC44x system library * * Eugene Surovegin or - * Copyright (c) 2003 Zultys Technologies + * Copyright (c) 2003, 2004 Zultys Technologies * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the @@ -31,6 +31,12 @@ struct ibm44x_clocks { unsigned int uart1; }; +/* common 44x platform init */ +void ibm44x_platform_init(void) __init; + +/* initialize decrementer and tick-related variables */ +void ibm44x_calibrate_decr(unsigned int freq) __init; + #endif /* __ASSEMBLY__ */ #endif /* __PPC_SYSLIB_IBM44x_COMMON_H */ #endif /* __KERNEL__ */ diff --git a/arch/ppc/syslib/m8260_pci.h b/arch/ppc/syslib/m8260_pci.h index 4e2ce7f7c..d1352120a 100644 --- a/arch/ppc/syslib/m8260_pci.h +++ b/arch/ppc/syslib/m8260_pci.h @@ -66,6 +66,7 @@ #endif #ifdef CONFIG_8260_PCI9 +struct pci_controller; extern void setup_m8260_indirect_pci(struct pci_controller* hose, u32 cfg_addr, u32 cfg_data); #else diff --git a/arch/ppc/syslib/m8xx_wdt.c b/arch/ppc/syslib/m8xx_wdt.c new file mode 100644 index 000000000..7838a44bf --- /dev/null +++ b/arch/ppc/syslib/m8xx_wdt.c @@ -0,0 +1,99 @@ +/* + * m8xx_wdt.c - MPC8xx watchdog driver + * + * Author: Florian Schirmer + * + * 2002 (c) Florian Schirmer 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 +#include +#include +#include +#include +#include + +static int wdt_timeout; + +void m8xx_wdt_reset(void) +{ + volatile immap_t *imap = (volatile immap_t *)IMAP_ADDR; + + imap->im_siu_conf.sc_swsr = 0x556c; /* write magic1 */ + imap->im_siu_conf.sc_swsr = 0xaa39; /* write magic2 */ +} + +static irqreturn_t m8xx_wdt_interrupt(int irq, void *dev, struct pt_regs *regs) +{ + volatile immap_t *imap = (volatile immap_t *)IMAP_ADDR; + + m8xx_wdt_reset(); + + imap->im_sit.sit_piscr |= PISCR_PS; /* clear irq */ + + return IRQ_HANDLED; +} + +void __init m8xx_wdt_handler_install(bd_t * binfo) +{ + volatile immap_t *imap = (volatile immap_t *)IMAP_ADDR; + u32 pitc; + u32 sypcr; + u32 pitrtclk; + + sypcr = imap->im_siu_conf.sc_sypcr; + + if (!(sypcr & 0x04)) { + printk(KERN_NOTICE "m8xx_wdt: wdt disabled (SYPCR: 0x%08X)\n", + sypcr); + return; + } + + m8xx_wdt_reset(); + + printk(KERN_NOTICE + "m8xx_wdt: active wdt found (SWTC: 0x%04X, SWP: 0x%01X)\n", + (sypcr >> 16), sypcr & 0x01); + + wdt_timeout = (sypcr >> 16) & 0xFFFF; + + if (!wdt_timeout) + wdt_timeout = 0xFFFF; + + if (sypcr & 0x01) + wdt_timeout *= 2048; + + /* + * Fire trigger if half of the wdt ticked down + */ + + if (imap->im_sit.sit_rtcsc & RTCSC_38K) + pitrtclk = 9600; + else + pitrtclk = 8192; + + if ((wdt_timeout) > (UINT_MAX / pitrtclk)) + pitc = wdt_timeout / binfo->bi_intfreq * pitrtclk / 2; + else + pitc = pitrtclk * wdt_timeout / binfo->bi_intfreq / 2; + + imap->im_sit.sit_pitc = pitc << 16; + imap->im_sit.sit_piscr = + (mk_int_int_mask(PIT_INTERRUPT) << 8) | PISCR_PIE | PISCR_PTE; + + if (request_irq(PIT_INTERRUPT, m8xx_wdt_interrupt, 0, "watchdog", NULL)) + panic("m8xx_wdt: could not allocate watchdog irq!"); + + printk(KERN_NOTICE + "m8xx_wdt: keep-alive trigger installed (PITC: 0x%04X)\n", pitc); + + wdt_timeout /= binfo->bi_intfreq; +} + +int m8xx_wdt_get_timeout(void) +{ + return wdt_timeout; +} diff --git a/arch/ppc/syslib/m8xx_wdt.h b/arch/ppc/syslib/m8xx_wdt.h new file mode 100644 index 000000000..0d81a9f81 --- /dev/null +++ b/arch/ppc/syslib/m8xx_wdt.h @@ -0,0 +1,16 @@ +/* + * Author: Florian Schirmer + * + * 2002 (c) Florian Schirmer 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 _PPC_SYSLIB_M8XX_WDT_H +#define _PPC_SYSLIB_M8XX_WDT_H + +extern void m8xx_wdt_handler_install(bd_t * binfo); +extern int m8xx_wdt_get_timeout(void); +extern void m8xx_wdt_reset(void); + +#endif /* _PPC_SYSLIB_M8XX_WDT_H */ diff --git a/arch/ppc/syslib/mpc52xx_pic.c b/arch/ppc/syslib/mpc52xx_pic.c index 0f88e63b0..4de79d3b9 100644 --- a/arch/ppc/syslib/mpc52xx_pic.c +++ b/arch/ppc/syslib/mpc52xx_pic.c @@ -114,7 +114,7 @@ mpc52xx_ic_ack(unsigned int irq) /* * Only some irqs are reset here, others in interrupting hardware. */ - + switch (irq) { case MPC52xx_IRQ0: val = in_be32(&intr->ctrl); @@ -180,13 +180,14 @@ void __init mpc52xx_init_irq(void) { int i; + u32 intr_ctrl; /* Remap the necessary zones */ intr = (struct mpc52xx_intr *) ioremap(MPC52xx_INTR, sizeof(struct mpc52xx_intr)); sdma = (struct mpc52xx_sdma *) ioremap(MPC52xx_SDMA, sizeof(struct mpc52xx_sdma)); - + if ((intr==NULL) || (sdma==NULL)) panic("Can't ioremap PIC/SDMA register for init_irq !"); @@ -195,12 +196,13 @@ mpc52xx_init_irq(void) out_be32(&sdma->IntMask, 0xffffffff); /* 1 means disabled */ out_be32(&intr->per_mask, 0x7ffffc00); /* 1 means disabled */ out_be32(&intr->main_mask, 0x00010fff); /* 1 means disabled */ - out_be32(&intr->ctrl, - 0x0f000000 | /* clear IRQ 0-3 */ - 0x00c00000 | /* IRQ0: level-sensitive, active low */ + intr_ctrl = in_be32(&intr->ctrl); + intr_ctrl &= 0x00ff0000; /* Keeps IRQ[0-3] config */ + intr_ctrl |= 0x0f000000 | /* clear IRQ 0-3 */ 0x00001000 | /* MEE master external enable */ 0x00000000 | /* 0 means disable IRQ 0-3 */ - 0x00000001); /* CEb route critical normally */ + 0x00000001; /* CEb route critical normally */ + out_be32(&intr->ctrl, intr_ctrl); /* Zero a bunch of the priority settings. */ out_be32(&intr->per_pri1, 0); @@ -214,6 +216,14 @@ mpc52xx_init_irq(void) irq_desc[i].handler = &mpc52xx_ic; irq_desc[i].status = IRQ_LEVEL; } + + #define IRQn_MODE(intr_ctrl,irq) (((intr_ctrl) >> (22-(i<<1))) & 0x03) + for (i=0 ; i<4 ; i++) { + int mode; + mode = IRQn_MODE(intr_ctrl,i); + if ((mode == 0x1) || (mode == 0x2)) + irq_desc[i?MPC52xx_IRQ1+i-1:MPC52xx_IRQ0].status = 0; + } } int diff --git a/arch/ppc/syslib/mpc52xx_setup.c b/arch/ppc/syslib/mpc52xx_setup.c index 631cea34b..d73e6fea5 100644 --- a/arch/ppc/syslib/mpc52xx_setup.c +++ b/arch/ppc/syslib/mpc52xx_setup.c @@ -1,5 +1,5 @@ /* - * arch/ppc/syslib/mpc52xx_common.c + * arch/ppc/syslib/mpc52xx_setup.c * * Common code for the boards based on Freescale MPC52xx embedded CPU. * @@ -23,6 +23,7 @@ #include #include #include +#include #include extern bd_t __res; @@ -38,9 +39,9 @@ void mpc52xx_restart(char *cmd) { struct mpc52xx_gpt* gpt0 = (struct mpc52xx_gpt*) MPC52xx_GPTx(0); - + local_irq_disable(); - + /* Turn on the watchdog and wait for it to expire. It effectively does a reset */ if (gpt0 != NULL) { @@ -99,24 +100,28 @@ mpc52xx_map_io(void) #error "mpc52xx PSC for console not selected" #endif +static void +mpc52xx_psc_putc(struct mpc52xx_psc * psc, unsigned char c) +{ + while (!(in_be16(&psc->mpc52xx_psc_status) & + MPC52xx_PSC_SR_TXRDY)); + out_8(&psc->mpc52xx_psc_buffer_8, c); +} + void mpc52xx_progress(char *s, unsigned short hex) { struct mpc52xx_psc *psc = (struct mpc52xx_psc *)MPC52xx_CONSOLE; char c; - /* Don't we need to disable serial interrupts ? */ - while ((c = *s++) != 0) { - if (c == '\n') { - while (!(in_be16(&psc->mpc52xx_psc_status) & - MPC52xx_PSC_SR_TXRDY)) ; - out_8(&psc->mpc52xx_psc_buffer_8, '\r'); - } - while (!(in_be16(&psc->mpc52xx_psc_status) & - MPC52xx_PSC_SR_TXRDY)) ; - out_8(&psc->mpc52xx_psc_buffer_8, c); + if (c == '\n') + mpc52xx_psc_putc(psc, '\r'); + mpc52xx_psc_putc(psc, c); } + + mpc52xx_psc_putc(psc, '\r'); + mpc52xx_psc_putc(psc, '\n'); } #endif /* CONFIG_SERIAL_TEXT_DEBUG */ @@ -137,7 +142,7 @@ mpc52xx_find_end_of_memory(void) /* Temp BAT2 mapping active when this is called ! */ mmap_ctl = (struct mpc52xx_mmap_ctl*) MPC52xx_MMAP_CTL; - + sdram_config_0 = in_be32(&mmap_ctl->sdram0); sdram_config_1 = in_be32(&mmap_ctl->sdram1); @@ -147,10 +152,8 @@ mpc52xx_find_end_of_memory(void) if (((sdram_config_1 & 0x1f) >= 0x13) && ((sdram_config_1 & 0xfff00000) == ramsize)) ramsize += 1 << ((sdram_config_1 & 0xf) + 17); - - iounmap(mmap_ctl); } - + return ramsize; } @@ -167,7 +170,7 @@ mpc52xx_calibrate_decr(void) /* Get RTC & Clock manager modules */ struct mpc52xx_rtc *rtc; struct mpc52xx_cdm *cdm; - + rtc = (struct mpc52xx_rtc*) ioremap(MPC52xx_RTC, sizeof(struct mpc52xx_rtc)); cdm = (struct mpc52xx_cdm*) @@ -206,7 +209,7 @@ mpc52xx_calibrate_decr(void) __res.bi_intfreq = cpufreq; __res.bi_ipbfreq = ipbfreq; __res.bi_pcifreq = pcifreq; - + /* Release mapping */ iounmap((void*)rtc); iounmap((void*)cdm); diff --git a/arch/ppc/syslib/open_pic2.c b/arch/ppc/syslib/open_pic2.c index 52cb4a494..191e5af0e 100644 --- a/arch/ppc/syslib/open_pic2.c +++ b/arch/ppc/syslib/open_pic2.c @@ -20,6 +20,7 @@ #include #include #include +#include #include #include #include @@ -28,7 +29,6 @@ #include #include #include -#include #include "open_pic_defs.h" diff --git a/arch/ppc/syslib/ppc4xx_setup.c b/arch/ppc/syslib/ppc4xx_setup.c index f7458822f..735f3aec8 100644 --- a/arch/ppc/syslib/ppc4xx_setup.c +++ b/arch/ppc/syslib/ppc4xx_setup.c @@ -61,10 +61,6 @@ ppc4xx_setup_arch(void) #ifdef CONFIG_PCI ppc4xx_find_bridges(); #endif - -#if defined(CONFIG_FB) - conswitchp = &dummy_con; -#endif } /* diff --git a/arch/ppc/syslib/ppc85xx_setup.c b/arch/ppc/syslib/ppc85xx_setup.c index 6ceea97f4..d4e651534 100644 --- a/arch/ppc/syslib/ppc85xx_setup.c +++ b/arch/ppc/syslib/ppc85xx_setup.c @@ -277,7 +277,7 @@ mpc85xx_setup_hose(void) hose_a->io_space.start = MPC85XX_PCI1_LOWER_IO; hose_a->io_space.end = MPC85XX_PCI1_UPPER_IO; hose_a->io_base_phys = MPC85XX_PCI1_IO_BASE; -#if CONFIG_85xx_PCI2 +#ifdef CONFIG_85xx_PCI2 isa_io_base = (unsigned long) ioremap(MPC85XX_PCI1_IO_BASE, MPC85XX_PCI1_IO_SIZE + @@ -304,7 +304,7 @@ mpc85xx_setup_hose(void) hose_a->last_busno = pciauto_bus_scan(hose_a, hose_a->first_busno); -#if CONFIG_85xx_PCI2 +#ifdef CONFIG_85xx_PCI2 hose_b = pcibios_alloc_controller(); if (!hose_b) diff --git a/arch/ppc/syslib/todc_time.c b/arch/ppc/syslib/todc_time.c index c1eb1f429..1dc7e0bf5 100644 --- a/arch/ppc/syslib/todc_time.c +++ b/arch/ppc/syslib/todc_time.c @@ -277,9 +277,9 @@ todc_time_init(void) ulong todc_get_rtc_time(void) { - uint year, mon, day, hour, min, sec; + uint year = 0, mon = 0, day = 0, hour = 0, min = 0, sec = 0; uint limit, i; - u_char save_control, uip; + u_char save_control, uip = 0; spin_lock(&rtc_lock); save_control = todc_read_val(todc_info->control_a); @@ -361,7 +361,7 @@ int todc_set_rtc_time(unsigned long nowtime) { struct rtc_time tm; - u_char save_control, save_freq_select; + u_char save_control, save_freq_select = 0; spin_lock(&rtc_lock); to_tm(nowtime, &tm); @@ -416,7 +416,7 @@ todc_set_rtc_time(unsigned long nowtime) */ static unsigned char __init todc_read_timereg(int addr) { - unsigned char save_control, val; + unsigned char save_control = 0, val; switch (todc_info->rtc_type) { case TODC_TYPE_DS1557: diff --git a/arch/ppc64/Kconfig.debug b/arch/ppc64/Kconfig.debug new file mode 100644 index 000000000..c3cd4c747 --- /dev/null +++ b/arch/ppc64/Kconfig.debug @@ -0,0 +1,59 @@ +menu "Kernel hacking" + +source "lib/Kconfig.debug" + +config DEBUG_STACKOVERFLOW + bool "Check for stack overflows" + depends on DEBUG_KERNEL + +config DEBUG_STACK_USAGE + bool "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 DEBUGGER + bool "Enable debugger hooks" + depends on DEBUG_KERNEL + help + Include in-kernel hooks for kernel debuggers. Unless you are + intending to debug the kernel, say N here. + +config XMON + bool "Include xmon kernel debugger" + depends on DEBUGGER + help + Include in-kernel hooks for the xmon kernel monitor/debugger. + Unless you are intending to debug the kernel, say N here. + +config XMON_DEFAULT + bool "Enable xmon by default" + depends on XMON + +config PPCDBG + bool "Include PPCDBG realtime debugging" + depends on DEBUG_KERNEL + +config IRQSTACKS + bool "Use separate kernel stacks when processing interrupts" + help + If you say Y here the kernel will use separate kernel stacks + for handling hard and soft interrupts. This can help avoid + overflowing the process kernel stacks. + +config SCHEDSTATS + bool "Collect scheduler statistics" + depends on DEBUG_KERNEL && PROC_FS + help + If you say Y here, additional code will be inserted into the + scheduler and related routines to collect statistics about + scheduler behavior and provide them in /proc/schedstat. These + stats may be useful for both tuning and debugging the scheduler + If you aren't debugging the scheduler or trying to tune a specific + application, you can say N to avoid the very slight overhead + this adds. + +endmenu diff --git a/arch/ppc64/boot/Makefile b/arch/ppc64/boot/Makefile index 9d6431dd6..8f99c9dbc 100644 --- a/arch/ppc64/boot/Makefile +++ b/arch/ppc64/boot/Makefile @@ -25,11 +25,11 @@ CROSS32_COMPILE ?= BOOTCC := $(CROSS32_COMPILE)gcc HOSTCC := gcc -BOOTCFLAGS := $(HOSTCFLAGS) -Iinclude -fno-builtin +BOOTCFLAGS := $(HOSTCFLAGS) $(LINUXINCLUDE) -fno-builtin BOOTAS := $(CROSS32_COMPILE)as BOOTAFLAGS := -D__ASSEMBLY__ $(BOOTCFLAGS) -traditional BOOTLD := $(CROSS32_COMPILE)ld -BOOTLFLAGS := -Ttext 0x00400000 -e _start -T $(obj)/zImage.lds +BOOTLFLAGS := -Ttext 0x00400000 -e _start -T $(srctree)/$(src)/zImage.lds BOOTOBJCOPY := $(CROSS32_COMPILE)objcopy OBJCOPYFLAGS := contents,alloc,load,readonly,data @@ -51,36 +51,31 @@ $(patsubst %.S,%.o, $(filter %.S, $(src-boot))): %.o: %.S #----------------------------------------------------------- # ELF sections within the zImage bootloader/wrapper #----------------------------------------------------------- -required := vmlinux .config System.map +required := vmlinux.strip initrd := initrd obj-sec = $(foreach section, $(1), $(patsubst %,$(obj)/kernel-%.o, $(section))) src-sec = $(foreach section, $(1), $(patsubst %,$(obj)/kernel-%.c, $(section))) gz-sec = $(foreach section, $(1), $(patsubst %,$(obj)/kernel-%.gz, $(section))) -host-progs := piggy addnote addSystemMap addRamDisk +hostprogs-y := piggy addnote addRamDisk targets += zImage zImage.initrd imagesize.c \ $(patsubst $(obj)/%,%, $(call obj-sec, $(required) $(initrd))) \ $(patsubst $(obj)/%,%, $(call src-sec, $(required) $(initrd))) \ $(patsubst $(obj)/%,%, $(call gz-sec, $(required) $(initrd))) \ - vmlinux.sm vmlinux.initrd vmlinux.sminitrd -extra-y := sysmap.o initrd.o - -quiet_cmd_sysmap = SYSMAP $@ - cmd_sysmap = $(obj)/addSystemMap System.map $< $@ -$(obj)/vmlinux.sm: vmlinux $(obj)/addSystemMap System.map FORCE - $(call if_changed,sysmap) + vmlinux.initrd +extra-y := initrd.o quiet_cmd_ramdisk = RAMDISK $@ - cmd_ramdisk = $(obj)/addRamDisk $(obj)/ramdisk.image.gz System.map $< $@ -$(obj)/vmlinux.initrd: vmlinux $(obj)/addRamDisk $(obj)/ramdisk.image.gz System.map FORCE - $(call if_changed,ramdisk) + cmd_ramdisk = $(obj)/addRamDisk $(obj)/ramdisk.image.gz $< $@ -$(obj)/vmlinux.sminitrd: $(obj)/vmlinux.sm $(obj)/addRamDisk $(obj)/ramdisk.image.gz FORCE - $(call if_changed,ramdisk) +quiet_cmd_stripvm = STRIP $@ + cmd_stripvm = $(STRIP) -s $< -o $@ -$(obj)/sysmap.o: System.map $(obj)/piggyback FORCE - $(call if_changed,piggy) +vmlinux.strip: vmlinux FORCE + $(call if_changed,stripvm) +$(obj)/vmlinux.initrd: vmlinux.strip $(obj)/addRamDisk $(obj)/ramdisk.image.gz FORCE + $(call if_changed,ramdisk) addsection = $(BOOTOBJCOPY) $(1) \ --add-section=.kernel:$(strip $(patsubst $(obj)/kernel-%.o,%, $(1)))=$(patsubst %.o,%.gz, $(1)) \ @@ -113,9 +108,9 @@ $(obj)/zImage.initrd: obj-boot += $(call obj-sec, $(required) $(initrd)) $(obj)/zImage.initrd: $(call obj-sec, $(required) $(initrd)) $(obj-boot) $(obj)/addnote FORCE $(call if_changed,addnote) -$(obj)/imagesize.c: vmlinux +$(obj)/imagesize.c: vmlinux.strip @echo Generating $@ - ls -l vmlinux | \ + ls -l vmlinux.strip | \ awk '{printf "/* generated -- do not edit! */\n" \ "unsigned long vmlinux_filesize = %d;\n", $$5}' > $(obj)/imagesize.c $(CROSS_COMPILE)nm -n vmlinux | tail -n 1 | \ @@ -123,6 +118,6 @@ $(obj)/imagesize.c: vmlinux >> $(obj)/imagesize.c install: $(CONFIGURE) $(obj)/$(BOOTIMAGE) - sh -x $(src)/install.sh "$(KERNELRELEASE)" "$(obj)/$(BOOTIMAGE)" "$(TOPDIR)/System.map" "$(INSTALL_PATH)" + sh -x $(src)/install.sh "$(KERNELRELEASE)" "$(obj)/$(BOOTIMAGE)" "$(INSTALL_PATH)" -clean-files := $(patsubst $(obj)/%,%, $(obj-boot)) +clean-files := $(addprefix $(objtree)/, $(obj-boot) vmlinux.strip) diff --git a/arch/ppc64/boot/main.c b/arch/ppc64/boot/main.c index f5a9718ef..6cf78020a 100644 --- a/arch/ppc64/boot/main.c +++ b/arch/ppc64/boot/main.c @@ -31,13 +31,9 @@ unsigned long strlen(const char *s); void *memmove(void *dest, const void *src, unsigned long n); void *memcpy(void *dest, const void *src, unsigned long n); -static struct bi_record *make_bi_recs(unsigned long); - -#define RAM_START 0x00000000 -#define RAM_END (64<<20) - /* Value picked to match that used by yaboot */ #define PROG_START 0x01400000 +#define RAM_END (256<<20) // Fixme: use OF */ char *avail_ram; char *begin_avail, *end_avail; @@ -48,8 +44,6 @@ unsigned int heap_max; extern char _start[]; extern char _vmlinux_start[]; extern char _vmlinux_end[]; -extern char _sysmap_start[]; -extern char _sysmap_end[]; extern char _initrd_start[]; extern char _initrd_end[]; extern unsigned long vmlinux_filesize; @@ -62,7 +56,6 @@ struct addr_range { }; struct addr_range vmlinux = {0, 0, 0}; struct addr_range vmlinuz = {0, 0, 0}; -struct addr_range sysmap = {0, 0, 0}; struct addr_range initrd = {0, 0, 0}; static char scratch[128<<10]; /* 128kB of scratch space for gunzip */ @@ -70,7 +63,7 @@ static char scratch[128<<10]; /* 128kB of scratch space for gunzip */ typedef void (*kernel_entry_t)( unsigned long, unsigned long, void *, - struct bi_record *); + void *); int (*prom)(void *); @@ -80,12 +73,31 @@ void *stdin; void *stdout; void *stderr; +#define DEBUG + +static unsigned long claim_base = PROG_START; + +static unsigned long try_claim(unsigned long size) +{ + unsigned long addr = 0; + + for(; claim_base < RAM_END; claim_base += 0x100000) { +#ifdef DEBUG + printf(" trying: 0x%08lx\n\r", claim_base); +#endif + addr = (unsigned long)claim(claim_base, size, 0); + if ((void *)addr != (void *)-1) + break; + } + if (addr == 0) + return 0; + claim_base = PAGE_ALIGN(claim_base + size); + return addr; +} -void -start(unsigned long a1, unsigned long a2, void *promptr) +void start(unsigned long a1, unsigned long a2, void *promptr) { - unsigned long i, claim_addr, claim_size; - struct bi_record *bi_recs; + unsigned long i; kernel_entry_t kernel_entry; Elf64_Ehdr *elf64; Elf64_Phdr *elf64ph; @@ -102,61 +114,59 @@ start(unsigned long a1, unsigned long a2, void *promptr) printf("zImage starting: loaded at 0x%x\n\r", (unsigned)_start); -#if 0 - sysmap.size = (unsigned long)(_sysmap_end - _sysmap_start); - sysmap.memsize = sysmap.size; - if ( sysmap.size > 0 ) { - sysmap.addr = (RAM_END - sysmap.size) & ~0xFFF; - claim(sysmap.addr, RAM_END - sysmap.addr, 0); - printf("initial ramdisk moving 0x%lx <- 0x%lx (%lx bytes)\n\r", - sysmap.addr, (unsigned long)_sysmap_start, sysmap.size); - memcpy((void *)sysmap.addr, (void *)_sysmap_start, sysmap.size); + /* + * Now we try to claim some memory for the kernel itself + * our "vmlinux_memsize" is the memory footprint in RAM, _HOWEVER_, what + * our Makefile stuffs in is an image containing all sort of junk including + * an ELF header. We need to do some calculations here to find the right + * size... In practice we add 1Mb, that is enough, but we should really + * consider fixing the Makefile to put a _raw_ kernel in there ! + */ + vmlinux_memsize += 0x100000; + printf("Allocating 0x%lx bytes for kernel ...\n\r", vmlinux_memsize); + vmlinux.addr = try_claim(vmlinux_memsize); + if (vmlinux.addr == 0) { + printf("Can't allocate memory for kernel image !\n\r"); + exit(); } -#endif + vmlinuz.addr = (unsigned long)_vmlinux_start; + vmlinuz.size = (unsigned long)(_vmlinux_end - _vmlinux_start); + vmlinux.size = PAGE_ALIGN(vmlinux_filesize); + vmlinux.memsize = vmlinux_memsize; + /* + * Now we try to claim memory for the initrd (and copy it there) + */ initrd.size = (unsigned long)(_initrd_end - _initrd_start); initrd.memsize = initrd.size; if ( initrd.size > 0 ) { - initrd.addr = (RAM_END - initrd.size) & ~0xFFF; - a1 = a2 = 0; - claim(initrd.addr, RAM_END - initrd.addr, 0); + printf("Allocating 0x%lx bytes for initrd ...\n\r", initrd.size); + initrd.addr = try_claim(initrd.size); + if (initrd.addr == 0) { + printf("Can't allocate memory for initial ramdisk !\n\r"); + exit(); + } + a1 = initrd.addr; + a2 = initrd.size; printf("initial ramdisk moving 0x%lx <- 0x%lx (%lx bytes)\n\r", initrd.addr, (unsigned long)_initrd_start, initrd.size); - memcpy((void *)initrd.addr, (void *)_initrd_start, initrd.size); - } - - vmlinuz.addr = (unsigned long)_vmlinux_start; - vmlinuz.size = (unsigned long)(_vmlinux_end - _vmlinux_start); - vmlinux.addr = (unsigned long)(void *)-1; - vmlinux.size = PAGE_ALIGN(vmlinux_filesize); - vmlinux.memsize = vmlinux_memsize; - - claim_size = vmlinux.memsize /* PPPBBB: + fudge for bi_recs */; - for(claim_addr = PROG_START; - claim_addr <= PROG_START * 8; - claim_addr += 0x100000) { -#ifdef DEBUG - printf(" trying: 0x%08lx\n\r", claim_addr); -#endif - vmlinux.addr = (unsigned long)claim(claim_addr, claim_size, 0); - if ((void *)vmlinux.addr != (void *)-1) break; - } - if ((void *)vmlinux.addr == (void *)-1) { - printf("claim error, can't allocate kernel memory\n\r"); - exit(); + memmove((void *)initrd.addr, (void *)_initrd_start, initrd.size); + printf("initrd head: 0x%lx\n", *((u32 *)initrd.addr)); } - /* PPPBBB: should kernel always be gziped? */ + /* Eventually gunzip the kernel */ if (*(unsigned short *)vmlinuz.addr == 0x1f8b) { + int len; avail_ram = scratch; begin_avail = avail_high = avail_ram; end_avail = scratch + sizeof(scratch); printf("gunzipping (0x%lx <- 0x%lx:0x%0lx)...", vmlinux.addr, vmlinuz.addr, vmlinuz.addr+vmlinuz.size); + len = vmlinuz.size; gunzip((void *)vmlinux.addr, vmlinux.size, - (unsigned char *)vmlinuz.addr, (int *)&vmlinuz.size); - printf("done %lu bytes\n\r", vmlinuz.size); - printf("%u bytes of heap consumed, max in use %u\n\r", + (unsigned char *)vmlinuz.addr, &len); + printf("done 0x%lx bytes\n\r", len); + printf("0x%x bytes of heap consumed, max in use 0x%\n\r", (unsigned)(avail_high - begin_avail), heap_max); } else { memmove((void *)vmlinux.addr,(void *)vmlinuz.addr,vmlinuz.size); @@ -192,7 +202,8 @@ start(unsigned long a1, unsigned long a2, void *promptr) flush_cache((void *)vmlinux.addr, vmlinux.memsize); - bi_recs = make_bi_recs(vmlinux.addr + vmlinux.memsize); + if (a1) + printf("initrd head: 0x%lx\n\r", *((u32 *)initrd.addr)); kernel_entry = (kernel_entry_t)vmlinux.addr; #ifdef DEBUG @@ -203,65 +214,16 @@ start(unsigned long a1, unsigned long a2, void *promptr) " prom = 0x%lx,\n\r" " bi_recs = 0x%lx,\n\r", (unsigned long)kernel_entry, a1, a2, - (unsigned long)prom, (unsigned long)bi_recs); + (unsigned long)prom, NULL); #endif - kernel_entry( a1, a2, prom, bi_recs ); + kernel_entry( a1, a2, prom, NULL ); printf("Error: Linux kernel returned to zImage bootloader!\n\r"); exit(); } -static struct bi_record * -make_bi_recs(unsigned long addr) -{ - struct bi_record *bi_recs; - struct bi_record *rec; - - bi_recs = rec = bi_rec_init(addr); - - rec = bi_rec_alloc(rec, 2); - rec->tag = BI_FIRST; - /* rec->data[0] = ...; # Written below before return */ - /* rec->data[1] = ...; # Written below before return */ - - rec = bi_rec_alloc_bytes(rec, strlen("chrpboot")+1); - rec->tag = BI_BOOTLOADER_ID; - sprintf( (char *)rec->data, "chrpboot"); - - rec = bi_rec_alloc(rec, 2); - rec->tag = BI_MACHTYPE; - rec->data[0] = PLATFORM_PSERIES; - rec->data[1] = 1; - - if ( initrd.size > 0 ) { - rec = bi_rec_alloc(rec, 2); - rec->tag = BI_INITRD; - rec->data[0] = initrd.addr; - rec->data[1] = initrd.size; - } - - if ( sysmap.size > 0 ) { - rec = bi_rec_alloc(rec, 2); - rec->tag = BI_SYSMAP; - rec->data[0] = (unsigned long)sysmap.addr; - rec->data[1] = (unsigned long)sysmap.size; - } - - rec = bi_rec_alloc(rec, 1); - rec->tag = BI_LAST; - rec->data[0] = (bi_rec_field)bi_recs; - - /* Save the _end_ address of the bi_rec's in the first bi_rec - * data field for easy access by the kernel. - */ - bi_recs->data[0] = (bi_rec_field)rec; - bi_recs->data[1] = (bi_rec_field)rec + rec->size - (bi_rec_field)bi_recs; - - return bi_recs; -} - struct memchunk { unsigned int size; unsigned int pad; diff --git a/arch/ppc64/boot/zImage.lds b/arch/ppc64/boot/zImage.lds index 7070cd2f7..941ade38f 100644 --- a/arch/ppc64/boot/zImage.lds +++ b/arch/ppc64/boot/zImage.lds @@ -61,19 +61,9 @@ SECTIONS . = ALIGN(4096); _vmlinux_start = .; - .kernel:vmlinux : { *(.kernel:vmlinux) } + .kernel:vmlinux.strip : { *(.kernel:vmlinux.strip) } _vmlinux_end = .; - . = ALIGN(4096); - _dotconfig_start = .; - .kernel:.config : { *(.kernel:.config) } - _dotconfig_end = .; - - . = ALIGN(4096); - _sysmap_start = .; - .kernel:System.map : { *(.kernel:System.map) } - _sysmap_end = .; - . = ALIGN(4096); _initrd_start = .; .kernel:initrd : { *(.kernel:initrd) } diff --git a/arch/ppc64/configs/g5_defconfig b/arch/ppc64/configs/g5_defconfig index 0485eed76..9cc01b5ba 100644 --- a/arch/ppc64/configs/g5_defconfig +++ b/arch/ppc64/configs/g5_defconfig @@ -1,5 +1,7 @@ # # Automatically generated make config: don't edit +# Linux kernel version: 2.6.9-rc3 +# Thu Oct 7 15:18:38 2004 # CONFIG_64BIT=y CONFIG_MMU=y @@ -16,11 +18,11 @@ CONFIG_FORCE_MAX_ZONEORDER=13 # CONFIG_EXPERIMENTAL=y CONFIG_CLEAN_COMPILE=y -CONFIG_STANDALONE=y # # General setup # +CONFIG_LOCALVERSION="" CONFIG_SWAP=y CONFIG_SYSVIPC=y # CONFIG_POSIX_MQUEUE is not set @@ -32,6 +34,8 @@ CONFIG_HOTPLUG=y # CONFIG_IKCONFIG is not set # CONFIG_EMBEDDED is not set CONFIG_KALLSYMS=y +# CONFIG_KALLSYMS_ALL is not set +# CONFIG_KALLSYMS_EXTRA_PASS is not set CONFIG_FUTEX=y CONFIG_EPOLL=y CONFIG_IOSCHED_NOOP=y @@ -39,6 +43,8 @@ CONFIG_IOSCHED_AS=y CONFIG_IOSCHED_DEADLINE=y CONFIG_IOSCHED_CFQ=y # CONFIG_CC_OPTIMIZE_FOR_SIZE is not set +CONFIG_SHMEM=y +# CONFIG_TINY_SHMEM is not set # # Loadable module support @@ -50,18 +56,20 @@ CONFIG_OBSOLETE_MODPARM=y # CONFIG_MODVERSIONS is not set # CONFIG_KMOD is not set CONFIG_STOP_MACHINE=y +CONFIG_SYSVIPC_COMPAT=y # # Platform support # # CONFIG_PPC_ISERIES is not set -CONFIG_PPC_PSERIES=y +CONFIG_PPC_MULTIPLATFORM=y +# CONFIG_PPC_PSERIES is not set +CONFIG_PPC_PMAC=y CONFIG_PPC=y CONFIG_PPC64=y CONFIG_PPC_OF=y CONFIG_ALTIVEC=y -CONFIG_PPC_PMAC=y -CONFIG_PMAC_DART=y +CONFIG_U3_DART=y CONFIG_PPC_PMAC64=y CONFIG_BOOTX_TEXT=y CONFIG_POWER4_ONLY=y @@ -69,8 +77,8 @@ CONFIG_IOMMU_VMERGE=y CONFIG_SMP=y CONFIG_IRQ_ALL_CPUS=y CONFIG_NR_CPUS=2 -# CONFIG_HMT is not set -# CONFIG_DISCONTIGMEM is not set +# CONFIG_SCHED_SMT is not set +# CONFIG_PREEMPT is not set # CONFIG_PPC_RTAS is not set # CONFIG_LPARCFG is not set @@ -104,6 +112,8 @@ CONFIG_PROC_DEVICETREE=y # # Generic Driver Options # +CONFIG_STANDALONE=y +CONFIG_PREVENT_FIRMWARE_BUILD=y CONFIG_FW_LOADER=y # CONFIG_DEBUG_DRIVER is not set @@ -132,7 +142,8 @@ CONFIG_FW_LOADER=y CONFIG_BLK_DEV_LOOP=y # CONFIG_BLK_DEV_CRYPTOLOOP is not set CONFIG_BLK_DEV_NBD=m -# CONFIG_BLK_DEV_CARMEL is not set +# CONFIG_BLK_DEV_SX8 is not set +# CONFIG_BLK_DEV_UB is not set CONFIG_BLK_DEV_RAM=y CONFIG_BLK_DEV_RAM_SIZE=8192 CONFIG_BLK_DEV_INITRD=y @@ -146,9 +157,9 @@ CONFIG_BLK_DEV_IDE=y # # Please see Documentation/ide.txt for help/info on IDE drives # +# CONFIG_BLK_DEV_IDE_SATA is not set CONFIG_BLK_DEV_IDEDISK=y # CONFIG_IDEDISK_MULTI_MODE is not set -# CONFIG_IDEDISK_STROKE is not set CONFIG_BLK_DEV_IDECD=y CONFIG_BLK_DEV_IDETAPE=y CONFIG_BLK_DEV_IDEFLOPPY=y @@ -170,7 +181,6 @@ CONFIG_BLK_DEV_IDEDMA_PCI=y # CONFIG_BLK_DEV_IDEDMA_FORCED is not set CONFIG_IDEDMA_PCI_AUTO=y # CONFIG_IDEDMA_ONLYDISK is not set -CONFIG_BLK_DEV_ADMA=y # CONFIG_BLK_DEV_AEC62XX is not set # CONFIG_BLK_DEV_ALI15X3 is not set # CONFIG_BLK_DEV_AMD74XX is not set @@ -196,6 +206,7 @@ CONFIG_BLK_DEV_IDE_PMAC_ATA100FIRST=y CONFIG_BLK_DEV_IDEDMA_PMAC=y # CONFIG_BLK_DEV_IDE_PMAC_BLINK is not set CONFIG_BLK_DEV_IDEDMA_PMAC_AUTO=y +# CONFIG_IDE_ARM is not set CONFIG_BLK_DEV_IDEDMA=y # CONFIG_IDEDMA_IVB is not set CONFIG_IDEDMA_AUTO=y @@ -221,7 +232,6 @@ CONFIG_CHR_DEV_SG=y # Some SCSI devices (e.g. CD jukebox) support multiple LUNs # CONFIG_SCSI_MULTI_LUN=y -CONFIG_SCSI_REPORT_LUNS=y CONFIG_SCSI_CONSTANTS=y # CONFIG_SCSI_LOGGING is not set @@ -235,23 +245,25 @@ CONFIG_SCSI_SPI_ATTRS=y # SCSI low-level drivers # # CONFIG_BLK_DEV_3W_XXXX_RAID is not set +# CONFIG_SCSI_3W_9XXX is not set # CONFIG_SCSI_ACARD is not set # CONFIG_SCSI_AACRAID is not set # CONFIG_SCSI_AIC7XXX is not set # CONFIG_SCSI_AIC7XXX_OLD is not set # CONFIG_SCSI_AIC79XX is not set -# CONFIG_SCSI_ADVANSYS is not set -# CONFIG_SCSI_MEGARAID is not set +# CONFIG_MEGARAID_NEWGEN is not set +# CONFIG_MEGARAID_LEGACY is not set CONFIG_SCSI_SATA=y CONFIG_SCSI_SATA_SVW=y # CONFIG_SCSI_ATA_PIIX is not set +# CONFIG_SCSI_SATA_NV is not set # CONFIG_SCSI_SATA_PROMISE is not set +# CONFIG_SCSI_SATA_SX4 is not set # CONFIG_SCSI_SATA_SIL is not set # CONFIG_SCSI_SATA_SIS is not set # CONFIG_SCSI_SATA_VIA is not set # CONFIG_SCSI_SATA_VITESSE is not set # CONFIG_SCSI_BUSLOGIC is not set -# CONFIG_SCSI_CPQFCTS is not set # CONFIG_SCSI_DMX3191D is not set # CONFIG_SCSI_EATA is not set # CONFIG_SCSI_EATA_PIO is not set @@ -264,6 +276,7 @@ CONFIG_SCSI_SYM53C8XX_DMA_ADDRESSING_MODE=0 CONFIG_SCSI_SYM53C8XX_DEFAULT_TAGS=16 CONFIG_SCSI_SYM53C8XX_MAX_TAGS=64 # CONFIG_SCSI_SYM53C8XX_IOMAPPED is not set +# CONFIG_SCSI_IPR is not set # CONFIG_SCSI_QLOGIC_ISP is not set # CONFIG_SCSI_QLOGIC_FC is not set # CONFIG_SCSI_QLOGIC_1280 is not set @@ -277,7 +290,6 @@ CONFIG_SCSI_QLA2XXX=y # CONFIG_SCSI_DC395x is not set # CONFIG_SCSI_DC390T is not set # CONFIG_SCSI_DEBUG is not set -# CONFIG_SCSI_MESH is not set # CONFIG_SCSI_MAC53C94 is not set # @@ -288,11 +300,15 @@ CONFIG_BLK_DEV_MD=y CONFIG_MD_LINEAR=y CONFIG_MD_RAID0=y CONFIG_MD_RAID1=y +# CONFIG_MD_RAID10 is not set CONFIG_MD_RAID5=y # CONFIG_MD_RAID6 is not set # CONFIG_MD_MULTIPATH is not set CONFIG_BLK_DEV_DM=y # CONFIG_DM_CRYPT is not set +# CONFIG_DM_SNAPSHOT is not set +# CONFIG_DM_MIRROR is not set +# CONFIG_DM_ZERO is not set # # Fusion MPT device support @@ -332,6 +348,7 @@ CONFIG_IEEE1394_RAWIO=y # # I2O device support # +# CONFIG_I2O is not set # # Macintosh device drivers @@ -340,7 +357,6 @@ CONFIG_ADB=y CONFIG_ADB_PMU=y # CONFIG_PMAC_PBOOK is not set # CONFIG_PMAC_BACKLIGHT is not set -# CONFIG_MAC_SERIAL is not set # CONFIG_INPUT_ADBHID is not set CONFIG_THERM_PM72=y @@ -369,14 +385,13 @@ CONFIG_SYN_COOKIES=y CONFIG_INET_AH=m CONFIG_INET_ESP=m CONFIG_INET_IPCOMP=m +CONFIG_INET_TUNNEL=y # # IP: Virtual Server Configuration # # CONFIG_IP_VS is not set # CONFIG_IPV6 is not set -# CONFIG_DECNET is not set -# CONFIG_BRIDGE is not set CONFIG_NETFILTER=y # CONFIG_NETFILTER_DEBUG is not set @@ -384,6 +399,8 @@ CONFIG_NETFILTER=y # IP: Netfilter Configuration # CONFIG_IP_NF_CONNTRACK=y +# CONFIG_IP_NF_CT_ACCT is not set +# CONFIG_IP_NF_CT_PROTO_SCTP is not set # CONFIG_IP_NF_FTP is not set # CONFIG_IP_NF_IRC is not set # CONFIG_IP_NF_TFTP is not set @@ -408,8 +425,15 @@ CONFIG_IP_NF_MATCH_HELPER=y CONFIG_IP_NF_MATCH_STATE=y CONFIG_IP_NF_MATCH_CONNTRACK=y CONFIG_IP_NF_MATCH_OWNER=y +# CONFIG_IP_NF_MATCH_ADDRTYPE is not set +# CONFIG_IP_NF_MATCH_REALM is not set +# CONFIG_IP_NF_MATCH_SCTP is not set +# CONFIG_IP_NF_MATCH_COMMENT is not set CONFIG_IP_NF_FILTER=y CONFIG_IP_NF_TARGET_REJECT=y +CONFIG_IP_NF_TARGET_LOG=y +CONFIG_IP_NF_TARGET_ULOG=y +CONFIG_IP_NF_TARGET_TCPMSS=y CONFIG_IP_NF_NAT=y CONFIG_IP_NF_NAT_NEEDED=y CONFIG_IP_NF_TARGET_MASQUERADE=y @@ -424,9 +448,7 @@ CONFIG_IP_NF_TARGET_ECN=y CONFIG_IP_NF_TARGET_DSCP=y CONFIG_IP_NF_TARGET_MARK=y CONFIG_IP_NF_TARGET_CLASSIFY=y -CONFIG_IP_NF_TARGET_LOG=y -CONFIG_IP_NF_TARGET_ULOG=y -CONFIG_IP_NF_TARGET_TCPMSS=y +# CONFIG_IP_NF_RAW is not set CONFIG_IP_NF_ARPTABLES=y CONFIG_IP_NF_ARPFILTER=y CONFIG_IP_NF_ARP_MANGLE=y @@ -438,7 +460,9 @@ CONFIG_XFRM=y # # CONFIG_IP_SCTP 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_LLC=y # CONFIG_LLC2 is not set # CONFIG_IPX is not set @@ -454,30 +478,33 @@ CONFIG_LLC=y # QoS and/or fair queueing # # CONFIG_NET_SCHED is not set +# CONFIG_NET_CLS_ROUTE is not set # # Network testing # # CONFIG_NET_PKTGEN is not set +# CONFIG_NETPOLL is not set +# CONFIG_NET_POLL_CONTROLLER is not set +# CONFIG_HAMRADIO is not set +# CONFIG_IRDA is not set +# CONFIG_BT is not set CONFIG_NETDEVICES=y +CONFIG_DUMMY=m +CONFIG_BONDING=m +# CONFIG_EQUALIZER is not set +CONFIG_TUN=m # # ARCnet devices # # CONFIG_ARCNET is not set -CONFIG_DUMMY=m -CONFIG_BONDING=m -# CONFIG_EQUALIZER is not set -CONFIG_TUN=m # # Ethernet (10 or 100Mbit) # CONFIG_NET_ETHERNET=y CONFIG_MII=y -# CONFIG_MACE is not set -# CONFIG_BMAC is not set -# CONFIG_OAKNET is not set # CONFIG_HAPPYMEAL is not set CONFIG_SUNGEM=y # CONFIG_NET_VENDOR_3COM is not set @@ -509,57 +536,38 @@ CONFIG_TIGON3=m # # CONFIG_IXGB is not set # CONFIG_S2IO is not set -# CONFIG_FDDI is not set -# CONFIG_HIPPI is not set -# CONFIG_IBMVETH is not set -CONFIG_PPP=m -# CONFIG_PPP_MULTILINK is not set -# CONFIG_PPP_FILTER is not set -CONFIG_PPP_ASYNC=m -CONFIG_PPP_SYNC_TTY=m -CONFIG_PPP_DEFLATE=m -CONFIG_PPP_BSDCOMP=m -CONFIG_PPPOE=m -# CONFIG_SLIP is not set - -# -# Wireless LAN (non-hamradio) -# -# CONFIG_NET_RADIO is not set # # Token Ring devices # CONFIG_TR=y CONFIG_IBMOL=y -# CONFIG_IBMLS is not set # CONFIG_3C359 is not set # CONFIG_TMS380TR is not set -# CONFIG_NET_FC is not set -# CONFIG_SHAPER is not set -# CONFIG_NETCONSOLE is not set - -# -# Wan interfaces -# -# CONFIG_WAN is not set - -# -# Amateur Radio support -# -# CONFIG_HAMRADIO is not set # -# IrDA (infrared) support +# Wireless LAN (non-hamradio) # -# CONFIG_IRDA is not set +# CONFIG_NET_RADIO is not set # -# Bluetooth support +# Wan interfaces # -# CONFIG_BT is not set -# CONFIG_NETPOLL is not set -# CONFIG_NET_POLL_CONTROLLER is not set +# CONFIG_WAN is not set +# CONFIG_FDDI is not set +# CONFIG_HIPPI is not set +CONFIG_PPP=m +# CONFIG_PPP_MULTILINK is not set +# CONFIG_PPP_FILTER is not set +CONFIG_PPP_ASYNC=m +CONFIG_PPP_SYNC_TTY=m +CONFIG_PPP_DEFLATE=m +CONFIG_PPP_BSDCOMP=m +CONFIG_PPPOE=m +# CONFIG_SLIP is not set +# CONFIG_NET_FC is not set +# CONFIG_SHAPER is not set +# CONFIG_NETCONSOLE is not set # # ISDN subsystem @@ -598,6 +606,7 @@ CONFIG_SERIO=y # CONFIG_SERIO_SERPORT is not set # CONFIG_SERIO_CT82C710 is not set # CONFIG_SERIO_PCIPS2 is not set +# CONFIG_SERIO_RAW is not set # # Input Device Drivers @@ -636,8 +645,6 @@ CONFIG_HW_CONSOLE=y CONFIG_UNIX98_PTYS=y CONFIG_LEGACY_PTYS=y CONFIG_LEGACY_PTY_COUNT=256 -CONFIG_HVC_CONSOLE=y -# CONFIG_QIC02_TAPE is not set # # IPMI @@ -673,6 +680,7 @@ CONFIG_I2C_CHARDEV=y # CONFIG_I2C_ALGOBIT=y # CONFIG_I2C_ALGOPCF is not set +# CONFIG_I2C_ALGOPCA is not set # # I2C Hardware Bus support @@ -697,23 +705,29 @@ CONFIG_I2C_KEYWEST=y # CONFIG_I2C_VIA is not set # CONFIG_I2C_VIAPRO is not set # CONFIG_I2C_VOODOO3 is not set +# CONFIG_I2C_PCA_ISA is not set # # Hardware Sensors Chip support # # CONFIG_I2C_SENSOR is not set # CONFIG_SENSORS_ADM1021 is not set +# CONFIG_SENSORS_ADM1025 is not set +# CONFIG_SENSORS_ADM1031 is not set # CONFIG_SENSORS_ASB100 is not set # CONFIG_SENSORS_DS1621 is not set # CONFIG_SENSORS_FSCHER is not set # CONFIG_SENSORS_GL518SM is not set # CONFIG_SENSORS_IT87 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_LM90 is not set +# CONFIG_SENSORS_MAX1619 is not set +# CONFIG_SENSORS_SMSC47M1 is not set # CONFIG_SENSORS_VIA686A is not set # CONFIG_SENSORS_W83781D is not set # CONFIG_SENSORS_W83L785TS is not set @@ -725,11 +739,17 @@ CONFIG_I2C_KEYWEST=y # CONFIG_SENSORS_EEPROM is not set # CONFIG_SENSORS_PCF8574 is not set # CONFIG_SENSORS_PCF8591 is not set +# CONFIG_SENSORS_RTC8564 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 +# +# Dallas's 1-wire bus +# +# CONFIG_W1 is not set + # # Misc devices # @@ -748,6 +768,8 @@ CONFIG_I2C_KEYWEST=y # Graphics support # CONFIG_FB=y +CONFIG_FB_MODE_HELPERS=y +# CONFIG_FB_CIRRUS is not set # CONFIG_FB_PM2 is not set # CONFIG_FB_CYBER2000 is not set CONFIG_FB_OF=y @@ -755,10 +777,12 @@ CONFIG_FB_OF=y # CONFIG_FB_PLATINUM is not set # CONFIG_FB_VALKYRIE is not set # CONFIG_FB_CT65550 is not set +# CONFIG_FB_ASILIANT is not set # CONFIG_FB_IMSTT is not set -# CONFIG_FB_S3TRIO is not set # CONFIG_FB_VGA16 is not set CONFIG_FB_RIVA=y +# CONFIG_FB_RIVA_I2C is not set +# CONFIG_FB_RIVA_DEBUG is not set # CONFIG_FB_MATROX is not set # CONFIG_FB_RADEON_OLD is not set CONFIG_FB_RADEON=y @@ -778,10 +802,8 @@ CONFIG_FB_RADEON_I2C=y # Console display driver support # # CONFIG_VGA_CONSOLE is not set -# CONFIG_MDA_CONSOLE is not set CONFIG_DUMMY_CONSOLE=y CONFIG_FRAMEBUFFER_CONSOLE=y -CONFIG_PCI_CONSOLE=y # CONFIG_FONTS is not set CONFIG_FONT_8x8=y CONFIG_FONT_8x16=y @@ -811,6 +833,7 @@ CONFIG_USB=y CONFIG_USB_DEVICEFS=y # CONFIG_USB_BANDWIDTH is not set # CONFIG_USB_DYNAMIC_MINORS is not set +# CONFIG_USB_OTG is not set # # USB Host Controller Drivers @@ -829,6 +852,7 @@ CONFIG_USB_ACM=m CONFIG_USB_PRINTER=y CONFIG_USB_STORAGE=y # CONFIG_USB_STORAGE_DEBUG is not set +# CONFIG_USB_STORAGE_RW_DETECT is not set CONFIG_USB_STORAGE_DATAFAB=y CONFIG_USB_STORAGE_FREECOM=y CONFIG_USB_STORAGE_ISD200=y @@ -853,6 +877,7 @@ CONFIG_USB_HIDDEV=y # CONFIG_USB_KBTAB is not set # CONFIG_USB_POWERMATE is not set # CONFIG_USB_MTOUCH is not set +# CONFIG_USB_EGALAX is not set # CONFIG_USB_XPAD is not set # CONFIG_USB_ATI_REMOTE is not set @@ -959,6 +984,7 @@ CONFIG_USB_EZUSB=y # CONFIG_USB_LCD is not set # CONFIG_USB_LED is not set # CONFIG_USB_CYTHERM is not set +# CONFIG_USB_PHIDGETSERVO is not set # CONFIG_USB_TEST is not set # @@ -997,6 +1023,7 @@ CONFIG_ISO9660_FS=y # CONFIG_JOLIET is not set # CONFIG_ZISOFS is not set CONFIG_UDF_FS=m +CONFIG_UDF_NLS=y # # DOS/FAT/NT Filesystems @@ -1004,6 +1031,8 @@ CONFIG_UDF_FS=m 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 # @@ -1016,8 +1045,8 @@ CONFIG_SYSFS=y CONFIG_DEVPTS_FS_XATTR=y # CONFIG_DEVPTS_FS_SECURITY is not set CONFIG_TMPFS=y -# CONFIG_HUGETLBFS is not set -# CONFIG_HUGETLB_PAGE is not set +CONFIG_HUGETLBFS=y +CONFIG_HUGETLB_PAGE=y CONFIG_RAMFS=y # @@ -1030,7 +1059,7 @@ CONFIG_RAMFS=y # CONFIG_BEFS_FS is not set # CONFIG_BFS_FS is not set # CONFIG_EFS_FS is not set -# CONFIG_CRAMFS is not set +CONFIG_CRAMFS=y # CONFIG_VXFS_FS is not set # CONFIG_HPFS_FS is not set # CONFIG_QNX4FS_FS is not set @@ -1054,11 +1083,14 @@ CONFIG_EXPORTFS=y CONFIG_SUNRPC=y CONFIG_SUNRPC_GSS=y CONFIG_RPCSEC_GSS_KRB5=y +# CONFIG_RPCSEC_GSS_SPKM3 is not set # CONFIG_SMB_FS is not set CONFIG_CIFS=m +# CONFIG_CIFS_STATS is not set +# CONFIG_CIFS_XATTR is not set +# CONFIG_CIFS_POSIX is not set # CONFIG_NCP_FS is not set # CONFIG_CODA_FS is not set -# CONFIG_INTERMEZZO_FS is not set # CONFIG_AFS_FS is not set # @@ -1076,7 +1108,6 @@ CONFIG_MSDOS_PARTITION=y # CONFIG_SOLARIS_X86_PARTITION is not set # CONFIG_UNIXWARE_DISKLABEL is not set # CONFIG_LDM_PARTITION is not set -# CONFIG_NEC98_PARTITION is not set # CONFIG_SGI_PARTITION is not set # CONFIG_ULTRIX_PARTITION is not set # CONFIG_SUN_PARTITION is not set @@ -1110,6 +1141,7 @@ CONFIG_NLS_DEFAULT="iso8859-1" # 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 @@ -1135,13 +1167,16 @@ CONFIG_OPROFILE=y # Kernel hacking # CONFIG_DEBUG_KERNEL=y +CONFIG_MAGIC_SYSRQ=y +# CONFIG_DEBUG_SLAB is not set +# CONFIG_DEBUG_SPINLOCK_SLEEP is not set +# CONFIG_DEBUG_INFO is not set # CONFIG_DEBUG_STACKOVERFLOW is not set # CONFIG_DEBUG_STACK_USAGE is not set -# CONFIG_DEBUG_SLAB is not set -CONFIG_MAGIC_SYSRQ=y # CONFIG_DEBUGGER is not set # CONFIG_PPCDBG is not set -# CONFIG_DEBUG_INFO is not set +CONFIG_IRQSTACKS=y +# CONFIG_SCHEDSTATS is not set # # Security options @@ -1159,6 +1194,7 @@ CONFIG_CRYPTO_MD5=y CONFIG_CRYPTO_SHA1=m CONFIG_CRYPTO_SHA256=m CONFIG_CRYPTO_SHA512=m +# CONFIG_CRYPTO_WP512 is not set CONFIG_CRYPTO_DES=y CONFIG_CRYPTO_BLOWFISH=m CONFIG_CRYPTO_TWOFISH=m @@ -1166,14 +1202,19 @@ CONFIG_CRYPTO_SERPENT=m CONFIG_CRYPTO_AES=m CONFIG_CRYPTO_CAST5=m CONFIG_CRYPTO_CAST6=m +# CONFIG_CRYPTO_TEA is not set CONFIG_CRYPTO_ARC4=m +# CONFIG_CRYPTO_KHAZAD is not set CONFIG_CRYPTO_DEFLATE=m CONFIG_CRYPTO_MICHAEL_MIC=m +# CONFIG_CRYPTO_CRC32C is not set CONFIG_CRYPTO_TEST=m # # Library routines # +CONFIG_CRC_CCITT=m CONFIG_CRC32=y -CONFIG_ZLIB_INFLATE=m +# CONFIG_LIBCRC32C is not set +CONFIG_ZLIB_INFLATE=y CONFIG_ZLIB_DEFLATE=m diff --git a/arch/ppc64/configs/pSeries_defconfig b/arch/ppc64/configs/pSeries_defconfig index f5503873a..73981148d 100644 --- a/arch/ppc64/configs/pSeries_defconfig +++ b/arch/ppc64/configs/pSeries_defconfig @@ -1,5 +1,7 @@ # # Automatically generated make config: don't edit +# Linux kernel version: 2.6.9-rc2 +# Thu Sep 23 16:45:05 2004 # CONFIG_64BIT=y CONFIG_MMU=y @@ -16,27 +18,35 @@ CONFIG_FORCE_MAX_ZONEORDER=13 # CONFIG_EXPERIMENTAL=y CONFIG_CLEAN_COMPILE=y -CONFIG_STANDALONE=y # # General setup # +CONFIG_LOCALVERSION="" CONFIG_SWAP=y CONFIG_SYSVIPC=y +CONFIG_POSIX_MQUEUE=y # CONFIG_BSD_PROCESS_ACCT is not set CONFIG_SYSCTL=y +CONFIG_AUDIT=y +CONFIG_AUDITSYSCALL=y CONFIG_LOG_BUF_SHIFT=17 CONFIG_HOTPLUG=y CONFIG_IKCONFIG=y CONFIG_IKCONFIG_PROC=y # CONFIG_EMBEDDED is not set CONFIG_KALLSYMS=y +CONFIG_KALLSYMS_ALL=y +# CONFIG_KALLSYMS_EXTRA_PASS is not set CONFIG_FUTEX=y CONFIG_EPOLL=y CONFIG_IOSCHED_NOOP=y CONFIG_IOSCHED_AS=y CONFIG_IOSCHED_DEADLINE=y +CONFIG_IOSCHED_CFQ=y # CONFIG_CC_OPTIMIZE_FOR_SIZE is not set +CONFIG_SHMEM=y +# CONFIG_TINY_SHMEM is not set # # Loadable module support @@ -48,25 +58,31 @@ CONFIG_OBSOLETE_MODPARM=y # CONFIG_MODVERSIONS is not set # CONFIG_KMOD is not set CONFIG_STOP_MACHINE=y +CONFIG_SYSVIPC_COMPAT=y # # Platform support # # CONFIG_PPC_ISERIES is not set +CONFIG_PPC_MULTIPLATFORM=y CONFIG_PPC_PSERIES=y +# CONFIG_PPC_PMAC is not set CONFIG_PPC=y CONFIG_PPC64=y CONFIG_PPC_OF=y CONFIG_ALTIVEC=y -# CONFIG_PPC_PMAC is not set +CONFIG_PPC_SPLPAR=y # CONFIG_BOOTX_TEXT is not set # CONFIG_POWER4_ONLY is not set -# CONFIG_IOMMU_VMERGE is not set +CONFIG_IOMMU_VMERGE=y CONFIG_SMP=y CONFIG_IRQ_ALL_CPUS=y -CONFIG_NR_CPUS=32 +CONFIG_NR_CPUS=128 # CONFIG_HMT is not set -# CONFIG_DISCONTIGMEM is not set +CONFIG_DISCONTIGMEM=y +CONFIG_NUMA=y +CONFIG_SCHED_SMT=y +# CONFIG_PREEMPT is not set CONFIG_PPC_RTAS=y CONFIG_RTAS_FLASH=m CONFIG_SCANLOG=m @@ -81,6 +97,7 @@ CONFIG_BINFMT_ELF=y # CONFIG_BINFMT_MISC is not set CONFIG_PCI_LEGACY_PROC=y CONFIG_PCI_NAMES=y +CONFIG_HOTPLUG_CPU=y # # PCMCIA/CardBus support @@ -107,7 +124,9 @@ CONFIG_PROC_DEVICETREE=y # # Generic Driver Options # -CONFIG_FW_LOADER=m +CONFIG_STANDALONE=y +CONFIG_PREVENT_FIRMWARE_BUILD=y +CONFIG_FW_LOADER=y # CONFIG_DEBUG_DRIVER is not set # @@ -127,7 +146,7 @@ CONFIG_FW_LOADER=m # # Block devices # -CONFIG_BLK_DEV_FD=y +CONFIG_BLK_DEV_FD=m # CONFIG_BLK_CPQ_DA is not set # CONFIG_BLK_CPQ_CISS_DA is not set # CONFIG_BLK_DEV_DAC960 is not set @@ -135,7 +154,8 @@ CONFIG_BLK_DEV_FD=y CONFIG_BLK_DEV_LOOP=y # CONFIG_BLK_DEV_CRYPTOLOOP is not set CONFIG_BLK_DEV_NBD=m -# CONFIG_BLK_DEV_CARMEL is not set +# CONFIG_BLK_DEV_SX8 is not set +# CONFIG_BLK_DEV_UB is not set CONFIG_BLK_DEV_RAM=y CONFIG_BLK_DEV_RAM_SIZE=4096 CONFIG_BLK_DEV_INITRD=y @@ -149,9 +169,9 @@ CONFIG_BLK_DEV_IDE=y # # Please see Documentation/ide.txt for help/info on IDE drives # +# CONFIG_BLK_DEV_IDE_SATA is not set CONFIG_BLK_DEV_IDEDISK=y # CONFIG_IDEDISK_MULTI_MODE is not set -# CONFIG_IDEDISK_STROKE is not set CONFIG_BLK_DEV_IDECD=y # CONFIG_BLK_DEV_IDETAPE is not set # CONFIG_BLK_DEV_IDEFLOPPY is not set @@ -173,7 +193,6 @@ CONFIG_BLK_DEV_IDEDMA_PCI=y # CONFIG_BLK_DEV_IDEDMA_FORCED is not set CONFIG_IDEDMA_PCI_AUTO=y # CONFIG_IDEDMA_ONLYDISK is not set -CONFIG_BLK_DEV_ADMA=y # CONFIG_BLK_DEV_AEC62XX is not set # CONFIG_BLK_DEV_ALI15X3 is not set CONFIG_BLK_DEV_AMD74XX=y @@ -194,6 +213,7 @@ CONFIG_BLK_DEV_AMD74XX=y # CONFIG_BLK_DEV_SLC90E66 is not set # CONFIG_BLK_DEV_TRM290 is not set # CONFIG_BLK_DEV_VIA82CXXX is not set +# CONFIG_IDE_ARM is not set CONFIG_BLK_DEV_IDEDMA=y # CONFIG_IDEDMA_IVB is not set CONFIG_IDEDMA_AUTO=y @@ -219,7 +239,6 @@ CONFIG_CHR_DEV_SG=y # Some SCSI devices (e.g. CD jukebox) support multiple LUNs # CONFIG_SCSI_MULTI_LUN=y -CONFIG_SCSI_REPORT_LUNS=y CONFIG_SCSI_CONSTANTS=y # CONFIG_SCSI_LOGGING is not set @@ -233,28 +252,32 @@ CONFIG_SCSI_FC_ATTRS=y # SCSI low-level drivers # # CONFIG_BLK_DEV_3W_XXXX_RAID is not set +# CONFIG_SCSI_3W_9XXX is not set # CONFIG_SCSI_ACARD is not set # CONFIG_SCSI_AACRAID is not set # CONFIG_SCSI_AIC7XXX is not set # CONFIG_SCSI_AIC7XXX_OLD is not set # CONFIG_SCSI_AIC79XX is not set -# CONFIG_SCSI_ADVANSYS is not set -# CONFIG_SCSI_MEGARAID is not set +# CONFIG_MEGARAID_NEWGEN is not set +# CONFIG_MEGARAID_LEGACY is not set # CONFIG_SCSI_SATA is not set # CONFIG_SCSI_BUSLOGIC is not set -# CONFIG_SCSI_CPQFCTS is not set # CONFIG_SCSI_DMX3191D is not set # CONFIG_SCSI_EATA is not set # CONFIG_SCSI_EATA_PIO is not set # CONFIG_SCSI_FUTURE_DOMAIN is not set # CONFIG_SCSI_GDTH is not set # CONFIG_SCSI_IPS is not set +CONFIG_SCSI_IBMVSCSI=m # CONFIG_SCSI_INIA100 is not set CONFIG_SCSI_SYM53C8XX_2=y CONFIG_SCSI_SYM53C8XX_DMA_ADDRESSING_MODE=0 CONFIG_SCSI_SYM53C8XX_DEFAULT_TAGS=16 CONFIG_SCSI_SYM53C8XX_MAX_TAGS=64 # CONFIG_SCSI_SYM53C8XX_IOMAPPED is not set +CONFIG_SCSI_IPR=y +# CONFIG_SCSI_IPR_TRACE is not set +# CONFIG_SCSI_IPR_DUMP is not set # CONFIG_SCSI_QLOGIC_ISP is not set # CONFIG_SCSI_QLOGIC_FC is not set # CONFIG_SCSI_QLOGIC_1280 is not set @@ -277,11 +300,15 @@ CONFIG_BLK_DEV_MD=y CONFIG_MD_LINEAR=y CONFIG_MD_RAID0=y CONFIG_MD_RAID1=y +CONFIG_MD_RAID10=m CONFIG_MD_RAID5=y -CONFIG_MD_RAID6=y -# CONFIG_MD_MULTIPATH is not set +CONFIG_MD_RAID6=m +CONFIG_MD_MULTIPATH=m CONFIG_BLK_DEV_DM=y CONFIG_DM_CRYPT=m +CONFIG_DM_SNAPSHOT=m +CONFIG_DM_MIRROR=m +CONFIG_DM_ZERO=m # # Fusion MPT device support @@ -296,6 +323,7 @@ CONFIG_DM_CRYPT=m # # I2O device support # +# CONFIG_I2O is not set # # Macintosh device drivers @@ -322,19 +350,17 @@ CONFIG_NET_IPIP=y # CONFIG_NET_IPGRE is not set # CONFIG_IP_MROUTE is not set # CONFIG_ARPD is not set -CONFIG_INET_ECN=y CONFIG_SYN_COOKIES=y CONFIG_INET_AH=m CONFIG_INET_ESP=m CONFIG_INET_IPCOMP=m +CONFIG_INET_TUNNEL=y # # IP: Virtual Server Configuration # # CONFIG_IP_VS is not set # CONFIG_IPV6 is not set -# CONFIG_DECNET is not set -# CONFIG_BRIDGE is not set CONFIG_NETFILTER=y # CONFIG_NETFILTER_DEBUG is not set @@ -342,6 +368,8 @@ CONFIG_NETFILTER=y # IP: Netfilter Configuration # CONFIG_IP_NF_CONNTRACK=m +# CONFIG_IP_NF_CT_ACCT is not set +CONFIG_IP_NF_CT_PROTO_SCTP=m CONFIG_IP_NF_FTP=m CONFIG_IP_NF_IRC=m CONFIG_IP_NF_TFTP=m @@ -366,8 +394,14 @@ CONFIG_IP_NF_MATCH_HELPER=m CONFIG_IP_NF_MATCH_STATE=m CONFIG_IP_NF_MATCH_CONNTRACK=m CONFIG_IP_NF_MATCH_OWNER=m +CONFIG_IP_NF_MATCH_ADDRTYPE=m +CONFIG_IP_NF_MATCH_REALM=m +CONFIG_IP_NF_MATCH_SCTP=m CONFIG_IP_NF_FILTER=m CONFIG_IP_NF_TARGET_REJECT=m +CONFIG_IP_NF_TARGET_LOG=m +CONFIG_IP_NF_TARGET_ULOG=m +CONFIG_IP_NF_TARGET_TCPMSS=m CONFIG_IP_NF_NAT=m CONFIG_IP_NF_NAT_NEEDED=y CONFIG_IP_NF_TARGET_MASQUERADE=m @@ -386,9 +420,8 @@ CONFIG_IP_NF_TARGET_ECN=m CONFIG_IP_NF_TARGET_DSCP=m CONFIG_IP_NF_TARGET_MARK=m CONFIG_IP_NF_TARGET_CLASSIFY=m -CONFIG_IP_NF_TARGET_LOG=m -CONFIG_IP_NF_TARGET_ULOG=m -CONFIG_IP_NF_TARGET_TCPMSS=m +CONFIG_IP_NF_RAW=m +CONFIG_IP_NF_TARGET_NOTRACK=m CONFIG_IP_NF_ARPTABLES=m CONFIG_IP_NF_ARPFILTER=m CONFIG_IP_NF_ARP_MANGLE=m @@ -400,10 +433,11 @@ CONFIG_XFRM_USER=m # # SCTP Configuration (EXPERIMENTAL) # -CONFIG_IPV6_SCTP__=y # CONFIG_IP_SCTP 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_LLC=y # CONFIG_LLC2 is not set # CONFIG_IPX is not set @@ -419,28 +453,35 @@ CONFIG_LLC=y # QoS and/or fair queueing # # CONFIG_NET_SCHED is not set +CONFIG_NET_CLS_ROUTE=y # # Network testing # # CONFIG_NET_PKTGEN is not set +CONFIG_NETPOLL=y +CONFIG_NETPOLL_RX=y +CONFIG_NETPOLL_TRAP=y +CONFIG_NET_POLL_CONTROLLER=y +# CONFIG_HAMRADIO is not set +# CONFIG_IRDA is not set +# CONFIG_BT is not set CONFIG_NETDEVICES=y +CONFIG_DUMMY=m +CONFIG_BONDING=m +# CONFIG_EQUALIZER is not set +CONFIG_TUN=m # # ARCnet devices # # CONFIG_ARCNET is not set -CONFIG_DUMMY=m -CONFIG_BONDING=m -# CONFIG_EQUALIZER is not set -CONFIG_TUN=m # # Ethernet (10 or 100Mbit) # CONFIG_NET_ETHERNET=y CONFIG_MII=y -# CONFIG_OAKNET is not set # CONFIG_HAPPYMEAL is not set # CONFIG_SUNGEM is not set CONFIG_NET_VENDOR_3COM=y @@ -452,6 +493,7 @@ CONFIG_VORTEX=y # # CONFIG_NET_TULIP is not set # CONFIG_HP100 is not set +CONFIG_IBMVETH=m CONFIG_NET_PCI=y CONFIG_PCNET32=y # CONFIG_AMD8111_ETH is not set @@ -471,6 +513,7 @@ CONFIG_E100=y # CONFIG_EPIC100 is not set # CONFIG_SUNDANCE is not set # CONFIG_VIA_RHINE is not set +# CONFIG_VIA_VELOCITY is not set # # Ethernet (1000 Mbit) @@ -484,7 +527,6 @@ CONFIG_E1000=y # CONFIG_HAMACHI is not set # CONFIG_YELLOWFIN is not set # CONFIG_R8169 is not set -# CONFIG_SIS190 is not set # CONFIG_SK98LIN is not set CONFIG_TIGON3=y @@ -493,59 +535,40 @@ CONFIG_TIGON3=y # CONFIG_IXGB=m # CONFIG_IXGB_NAPI is not set -# CONFIG_FDDI is not set -# CONFIG_HIPPI is not set -CONFIG_IBMVETH=m -CONFIG_PPP=m -# CONFIG_PPP_MULTILINK is not set -# CONFIG_PPP_FILTER is not set -CONFIG_PPP_ASYNC=m -CONFIG_PPP_SYNC_TTY=m -CONFIG_PPP_DEFLATE=m -CONFIG_PPP_BSDCOMP=m -CONFIG_PPPOE=m -# CONFIG_SLIP is not set - -# -# Wireless LAN (non-hamradio) -# -# CONFIG_NET_RADIO is not set +CONFIG_S2IO=m +# CONFIG_S2IO_NAPI is not set # # Token Ring devices # CONFIG_TR=y CONFIG_IBMOL=y -# CONFIG_IBMLS is not set # CONFIG_3C359 is not set # CONFIG_TMS380TR is not set -# CONFIG_NET_FC is not set -# CONFIG_SHAPER is not set -CONFIG_NETCONSOLE=y # -# Wan interfaces -# -# CONFIG_WAN is not set - -# -# Amateur Radio support -# -# CONFIG_HAMRADIO is not set - -# -# IrDA (infrared) support +# Wireless LAN (non-hamradio) # -# CONFIG_IRDA is not set +# CONFIG_NET_RADIO is not set # -# Bluetooth support +# Wan interfaces # -# CONFIG_BT is not set -CONFIG_NETPOLL=y -CONFIG_NETPOLL_RX=y -CONFIG_NETPOLL_TRAP=y -CONFIG_NET_POLL_CONTROLLER=y +# CONFIG_WAN is not set +# CONFIG_FDDI is not set +# CONFIG_HIPPI is not set +CONFIG_PPP=m +# CONFIG_PPP_MULTILINK is not set +# CONFIG_PPP_FILTER is not set +CONFIG_PPP_ASYNC=m +CONFIG_PPP_SYNC_TTY=m +CONFIG_PPP_DEFLATE=m +CONFIG_PPP_BSDCOMP=m +CONFIG_PPPOE=m +# CONFIG_SLIP is not set +# CONFIG_NET_FC is not set +# CONFIG_SHAPER is not set +CONFIG_NETCONSOLE=y # # ISDN subsystem @@ -584,6 +607,7 @@ CONFIG_SERIO_I8042=y # CONFIG_SERIO_SERPORT is not set # CONFIG_SERIO_CT82C710 is not set # CONFIG_SERIO_PCIPS2 is not set +# CONFIG_SERIO_RAW is not set # # Input Device Drivers @@ -591,15 +615,17 @@ CONFIG_SERIO_I8042=y 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_INPUT_MOUSE=y CONFIG_MOUSE_PS2=y # CONFIG_MOUSE_SERIAL is not set +# CONFIG_MOUSE_VSXXXAA is not set # CONFIG_INPUT_JOYSTICK is not set # CONFIG_INPUT_TOUCHSCREEN is not set CONFIG_INPUT_MISC=y -CONFIG_INPUT_PCSPKR=y +# CONFIG_INPUT_PCSPKR is not set # CONFIG_INPUT_UINPUT is not set # @@ -624,16 +650,12 @@ CONFIG_SERIAL_8250_NR_UARTS=4 CONFIG_SERIAL_CORE=y CONFIG_SERIAL_CORE_CONSOLE=y # CONFIG_SERIAL_PMACZILOG is not set +CONFIG_SERIAL_ICOM=m CONFIG_UNIX98_PTYS=y CONFIG_LEGACY_PTYS=y CONFIG_LEGACY_PTY_COUNT=256 CONFIG_HVC_CONSOLE=y - -# -# Mice -# -# CONFIG_BUSMOUSE is not set -# CONFIG_QIC02_TAPE is not set +CONFIG_HVCS=m # # IPMI @@ -656,7 +678,7 @@ CONFIG_HVC_CONSOLE=y # CONFIG_AGP is not set # CONFIG_DRM is not set CONFIG_RAW_DRIVER=y -CONFIG_MAX_RAW_DEVS=256 +CONFIG_MAX_RAW_DEVS=1024 # # I2C support @@ -669,11 +691,13 @@ CONFIG_I2C=y # CONFIG_I2C_ALGOBIT=y # CONFIG_I2C_ALGOPCF is not set +# CONFIG_I2C_ALGOPCA is not set # # I2C Hardware Bus support # # 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 @@ -691,29 +715,51 @@ CONFIG_I2C_ALGOBIT=y # CONFIG_I2C_VIA is not set # CONFIG_I2C_VIAPRO is not set # CONFIG_I2C_VOODOO3 is not set +# CONFIG_I2C_PCA_ISA is not set # -# I2C Hardware Sensors Chip support +# Hardware Sensors Chip support # # CONFIG_I2C_SENSOR is not set # CONFIG_SENSORS_ADM1021 is not set +# CONFIG_SENSORS_ADM1025 is not set +# CONFIG_SENSORS_ADM1031 is not set # CONFIG_SENSORS_ASB100 is not set -# CONFIG_SENSORS_EEPROM is not set +# CONFIG_SENSORS_DS1621 is not set # CONFIG_SENSORS_FSCHER is not set # CONFIG_SENSORS_GL518SM is not set # CONFIG_SENSORS_IT87 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_LM90 is not set +# CONFIG_SENSORS_MAX1619 is not set +# CONFIG_SENSORS_SMSC47M1 is not set # CONFIG_SENSORS_VIA686A is not set # CONFIG_SENSORS_W83781D is not set # CONFIG_SENSORS_W83L785TS is not set +# CONFIG_SENSORS_W83627HF is not set + +# +# Other I2C Chip support +# +# CONFIG_SENSORS_EEPROM is not set +# CONFIG_SENSORS_PCF8574 is not set +# CONFIG_SENSORS_PCF8591 is not set +# CONFIG_SENSORS_RTC8564 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 +# +# Dallas's 1-wire bus +# +# CONFIG_W1 is not set + # # Misc devices # @@ -732,12 +778,14 @@ CONFIG_I2C_ALGOBIT=y # Graphics support # CONFIG_FB=y +CONFIG_FB_MODE_HELPERS=y +# CONFIG_FB_CIRRUS is not set # CONFIG_FB_PM2 is not set # CONFIG_FB_CYBER2000 is not set CONFIG_FB_OF=y # CONFIG_FB_CT65550 is not set +# CONFIG_FB_ASILIANT is not set # CONFIG_FB_IMSTT is not set -# CONFIG_FB_S3TRIO is not set # CONFIG_FB_VGA16 is not set # CONFIG_FB_RIVA is not set CONFIG_FB_MATROX=y @@ -765,10 +813,8 @@ CONFIG_FB_RADEON_I2C=y # Console display driver support # # CONFIG_VGA_CONSOLE is not set -# CONFIG_MDA_CONSOLE is not set CONFIG_DUMMY_CONSOLE=y CONFIG_FRAMEBUFFER_CONSOLE=y -CONFIG_PCI_CONSOLE=y # CONFIG_FONTS is not set CONFIG_FONT_8x8=y CONFIG_FONT_8x16=y @@ -798,11 +844,14 @@ CONFIG_USB=y CONFIG_USB_DEVICEFS=y # CONFIG_USB_BANDWIDTH is not set # CONFIG_USB_DYNAMIC_MINORS is not set +# CONFIG_USB_OTG is not set # # USB Host Controller Drivers # CONFIG_USB_EHCI_HCD=y +# CONFIG_USB_EHCI_SPLIT_ISO is not set +# CONFIG_USB_EHCI_ROOT_HUB_TT is not set CONFIG_USB_OHCI_HCD=y # CONFIG_USB_UHCI_HCD is not set @@ -814,6 +863,7 @@ CONFIG_USB_OHCI_HCD=y # CONFIG_USB_PRINTER is not set CONFIG_USB_STORAGE=y # CONFIG_USB_STORAGE_DEBUG is not set +# CONFIG_USB_STORAGE_RW_DETECT is not set # CONFIG_USB_STORAGE_DATAFAB is not set # CONFIG_USB_STORAGE_FREECOM is not set # CONFIG_USB_STORAGE_ISD200 is not set @@ -834,7 +884,10 @@ CONFIG_USB_HIDDEV=y # CONFIG_USB_WACOM is not set # CONFIG_USB_KBTAB is not set # CONFIG_USB_POWERMATE is not set +# CONFIG_USB_MTOUCH is not set +# CONFIG_USB_EGALAX is not set # CONFIG_USB_XPAD is not set +# CONFIG_USB_ATI_REMOTE is not set # # USB Imaging devices @@ -881,6 +934,8 @@ CONFIG_USB_HIDDEV=y # CONFIG_USB_LEGOTOWER is not set # CONFIG_USB_LCD is not set # CONFIG_USB_LED is not set +# CONFIG_USB_CYTHERM is not set +# CONFIG_USB_PHIDGETSERVO is not set # CONFIG_USB_TEST is not set # @@ -905,7 +960,10 @@ CONFIG_FS_MBCACHE=y CONFIG_REISERFS_FS=y # CONFIG_REISERFS_CHECK is not set # CONFIG_REISERFS_PROC_INFO is not set -CONFIG_JFS_FS=y +CONFIG_REISERFS_FS_XATTR=y +CONFIG_REISERFS_FS_POSIX_ACL=y +# CONFIG_REISERFS_FS_SECURITY is not set +CONFIG_JFS_FS=m CONFIG_JFS_POSIX_ACL=y # CONFIG_JFS_DEBUG is not set # CONFIG_JFS_STATISTICS is not set @@ -928,6 +986,7 @@ CONFIG_ISO9660_FS=y # CONFIG_JOLIET is not set # CONFIG_ZISOFS is not set CONFIG_UDF_FS=m +CONFIG_UDF_NLS=y # # DOS/FAT/NT Filesystems @@ -935,6 +994,8 @@ CONFIG_UDF_FS=m 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 # @@ -942,6 +1003,7 @@ CONFIG_VFAT_FS=y # CONFIG_PROC_FS=y CONFIG_PROC_KCORE=y +CONFIG_SYSFS=y # CONFIG_DEVFS_FS is not set CONFIG_DEVPTS_FS_XATTR=y # CONFIG_DEVPTS_FS_SECURITY is not set @@ -974,18 +1036,22 @@ CONFIG_NFS_FS=y CONFIG_NFS_V3=y CONFIG_NFS_V4=y # CONFIG_NFS_DIRECTIO is not set -CONFIG_NFSD=y +CONFIG_NFSD=m CONFIG_NFSD_V3=y CONFIG_NFSD_V4=y CONFIG_NFSD_TCP=y CONFIG_LOCKD=y CONFIG_LOCKD_V4=y -CONFIG_EXPORTFS=y +CONFIG_EXPORTFS=m CONFIG_SUNRPC=y -CONFIG_SUNRPC_GSS=m -CONFIG_RPCSEC_GSS_KRB5=m +CONFIG_SUNRPC_GSS=y +CONFIG_RPCSEC_GSS_KRB5=y +CONFIG_RPCSEC_GSS_SPKM3=m # CONFIG_SMB_FS is not set CONFIG_CIFS=m +# CONFIG_CIFS_STATS is not set +CONFIG_CIFS_XATTR=y +CONFIG_CIFS_POSIX=y # CONFIG_NCP_FS is not set # CONFIG_CODA_FS is not set # CONFIG_AFS_FS is not set @@ -1001,7 +1067,7 @@ CONFIG_MSDOS_PARTITION=y # CONFIG_NLS=y CONFIG_NLS_DEFAULT="iso8859-1" -# CONFIG_NLS_CODEPAGE_437 is not set +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 @@ -1024,7 +1090,8 @@ CONFIG_NLS_DEFAULT="iso8859-1" # CONFIG_NLS_ISO8859_8 is not set # CONFIG_NLS_CODEPAGE_1250 is not set # CONFIG_NLS_CODEPAGE_1251 is not set -# CONFIG_NLS_ISO8859_1 is not set +CONFIG_NLS_ASCII=y +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 @@ -1049,15 +1116,18 @@ CONFIG_OPROFILE=y # Kernel hacking # CONFIG_DEBUG_KERNEL=y +CONFIG_MAGIC_SYSRQ=y +# CONFIG_DEBUG_SLAB is not set +# CONFIG_DEBUG_SPINLOCK_SLEEP is not set +# CONFIG_DEBUG_INFO is not set CONFIG_DEBUG_STACKOVERFLOW=y CONFIG_DEBUG_STACK_USAGE=y -# CONFIG_DEBUG_SLAB is not set -CONFIG_MAGIC_SYSRQ=y CONFIG_DEBUGGER=y CONFIG_XMON=y CONFIG_XMON_DEFAULT=y # CONFIG_PPCDBG is not set -# CONFIG_DEBUG_INFO is not set +CONFIG_IRQSTACKS=y +# CONFIG_SCHEDSTATS is not set # # Security options @@ -1071,24 +1141,31 @@ CONFIG_CRYPTO=y CONFIG_CRYPTO_HMAC=y CONFIG_CRYPTO_NULL=m CONFIG_CRYPTO_MD4=m -CONFIG_CRYPTO_MD5=m +CONFIG_CRYPTO_MD5=y CONFIG_CRYPTO_SHA1=m CONFIG_CRYPTO_SHA256=m CONFIG_CRYPTO_SHA512=m -CONFIG_CRYPTO_DES=m +CONFIG_CRYPTO_WHIRLPOOL=m +CONFIG_CRYPTO_DES=y CONFIG_CRYPTO_BLOWFISH=m CONFIG_CRYPTO_TWOFISH=m CONFIG_CRYPTO_SERPENT=m CONFIG_CRYPTO_AES=m CONFIG_CRYPTO_CAST5=m CONFIG_CRYPTO_CAST6=m +CONFIG_CRYPTO_TEA=m CONFIG_CRYPTO_ARC4=m +CONFIG_CRYPTO_KHAZAD=m CONFIG_CRYPTO_DEFLATE=m +CONFIG_CRYPTO_MICHAEL_MIC=m +CONFIG_CRYPTO_CRC32C=m CONFIG_CRYPTO_TEST=m # # Library routines # +CONFIG_CRC_CCITT=m CONFIG_CRC32=y +CONFIG_LIBCRC32C=m CONFIG_ZLIB_INFLATE=y CONFIG_ZLIB_DEFLATE=m diff --git a/arch/ppc64/kernel/btext.c b/arch/ppc64/kernel/btext.c index 33e10865d..c53f079e9 100644 --- a/arch/ppc64/kernel/btext.c +++ b/arch/ppc64/kernel/btext.c @@ -10,7 +10,7 @@ #include #include -#include +#include #include #include #include @@ -36,6 +36,11 @@ static int g_loc_Y; static int g_max_loc_X; static int g_max_loc_Y; +static int dispDeviceRowBytes; +static int dispDeviceDepth; +static int dispDeviceRect[4]; +static unsigned char *dispDeviceBase, *logicalDisplayBase; + unsigned long disp_BAT[2] __initdata = {0, 0}; #define cmapsz (16*256) @@ -45,30 +50,6 @@ static unsigned char vga_font[cmapsz]; int boot_text_mapped; int force_printk_to_btext = 0; -boot_infos_t disp_bi; - -/* This function will enable the early boot text when doing OF booting. This - * way, xmon output should work too - */ -void __init btext_setup_display(int width, int height, int depth, int pitch, - unsigned long address) -{ - unsigned long offset = reloc_offset(); - boot_infos_t* bi = PTRRELOC(&disp_bi); - - RELOC(g_loc_X) = 0; - RELOC(g_loc_Y) = 0; - RELOC(g_max_loc_X) = width / 8; - RELOC(g_max_loc_Y) = height / 16; - bi->logicalDisplayBase = (unsigned char *)address; - bi->dispDeviceBase = (unsigned char *)address; - bi->dispDeviceRowBytes = pitch; - bi->dispDeviceDepth = depth; - bi->dispDeviceRect[0] = bi->dispDeviceRect[1] = 0; - bi->dispDeviceRect[2] = width; - bi->dispDeviceRect[3] = height; - RELOC(boot_text_mapped) = 1; -} /* Here's a small text engine to use during early boot * or for debugging purposes @@ -84,34 +65,84 @@ void __init btext_setup_display(int width, int height, int depth, int pitch, void map_boot_text(void) { unsigned long base, offset, size; - boot_infos_t *bi = &disp_bi; unsigned char *vbase; /* By default, we are no longer mapped */ boot_text_mapped = 0; - if (bi->dispDeviceBase == 0) + if (dispDeviceBase == 0) return; - base = ((unsigned long) bi->dispDeviceBase) & 0xFFFFF000UL; - offset = ((unsigned long) bi->dispDeviceBase) - base; - size = bi->dispDeviceRowBytes * bi->dispDeviceRect[3] + offset - + bi->dispDeviceRect[0]; + base = ((unsigned long) dispDeviceBase) & 0xFFFFF000UL; + offset = ((unsigned long) dispDeviceBase) - base; + size = dispDeviceRowBytes * dispDeviceRect[3] + offset + + dispDeviceRect[0]; vbase = __ioremap(base, size, _PAGE_NO_CACHE); if (vbase == 0) return; - bi->logicalDisplayBase = vbase + offset; + logicalDisplayBase = vbase + offset; boot_text_mapped = 1; } +int btext_initialize(struct device_node *np) +{ + unsigned int width, height, depth, pitch; + unsigned long address = 0; + u32 *prop; + + prop = (u32 *)get_property(np, "width", NULL); + if (prop == NULL) + return -EINVAL; + width = *prop; + prop = (u32 *)get_property(np, "height", NULL); + if (prop == NULL) + return -EINVAL; + height = *prop; + prop = (u32 *)get_property(np, "depth", NULL); + if (prop == NULL) + return -EINVAL; + depth = *prop; + pitch = width * ((depth + 7) / 8); + prop = (u32 *)get_property(np, "linebytes", NULL); + if (prop) + pitch = *prop; + if (pitch == 1) + pitch = 0x1000; + prop = (u32 *)get_property(np, "address", NULL); + if (prop) + address = *prop; + + /* FIXME: Add support for PCI reg properties */ + + if (address == 0) + return -EINVAL; + + g_loc_X = 0; + g_loc_Y = 0; + g_max_loc_X = width / 8; + g_max_loc_Y = height / 16; + logicalDisplayBase = (unsigned char *)address; + dispDeviceBase = (unsigned char *)address; + dispDeviceRowBytes = pitch; + dispDeviceDepth = depth; + dispDeviceRect[0] = dispDeviceRect[1] = 0; + dispDeviceRect[2] = width; + dispDeviceRect[3] = height; + + map_boot_text(); + + return 0; +} + + /* Calc the base address of a given point (x,y) */ -static unsigned char * calc_base(boot_infos_t *bi, int x, int y) +static unsigned char * calc_base(int x, int y) { unsigned char *base; - base = bi->logicalDisplayBase; + base = logicalDisplayBase; if (base == 0) - base = bi->dispDeviceBase; - base += (x + bi->dispDeviceRect[0]) * (bi->dispDeviceDepth >> 3); - base += (y + bi->dispDeviceRect[1]) * bi->dispDeviceRowBytes; + base = dispDeviceBase; + base += (x + dispDeviceRect[0]) * (dispDeviceDepth >> 3); + base += (y + dispDeviceRect[1]) * dispDeviceRowBytes; return base; } @@ -119,24 +150,22 @@ static unsigned char * calc_base(boot_infos_t *bi, int x, int y) void btext_update_display(unsigned long phys, int width, int height, int depth, int pitch) { - boot_infos_t *bi = &disp_bi; - - if (bi->dispDeviceBase == 0) + if (dispDeviceBase == 0) return; /* check it's the same frame buffer (within 256MB) */ - if ((phys ^ (unsigned long)bi->dispDeviceBase) & 0xf0000000) + if ((phys ^ (unsigned long)dispDeviceBase) & 0xf0000000) return; - bi->dispDeviceBase = (__u8 *) phys; - bi->dispDeviceRect[0] = 0; - bi->dispDeviceRect[1] = 0; - bi->dispDeviceRect[2] = width; - bi->dispDeviceRect[3] = height; - bi->dispDeviceDepth = depth; - bi->dispDeviceRowBytes = pitch; + dispDeviceBase = (__u8 *) phys; + dispDeviceRect[0] = 0; + dispDeviceRect[1] = 0; + dispDeviceRect[2] = width; + dispDeviceRect[3] = height; + dispDeviceDepth = depth; + dispDeviceRowBytes = pitch; if (boot_text_mapped) { - iounmap(bi->logicalDisplayBase); + iounmap(logicalDisplayBase); boot_text_mapped = 0; } map_boot_text(); @@ -148,108 +177,101 @@ void btext_update_display(unsigned long phys, int width, int height, void btext_clearscreen(void) { - unsigned long offset = reloc_offset(); - boot_infos_t* bi = PTRRELOC(&disp_bi); - unsigned long *base = (unsigned long *)calc_base(bi, 0, 0); - unsigned long width = ((bi->dispDeviceRect[2] - bi->dispDeviceRect[0]) * - (bi->dispDeviceDepth >> 3)) >> 3; + unsigned long *base = (unsigned long *)calc_base(0, 0); + unsigned long width = ((dispDeviceRect[2] - dispDeviceRect[0]) * + (dispDeviceDepth >> 3)) >> 3; int i,j; - for (i=0; i<(bi->dispDeviceRect[3] - bi->dispDeviceRect[1]); i++) + for (i=0; i<(dispDeviceRect[3] - dispDeviceRect[1]); i++) { unsigned long *ptr = base; for(j=width; j; --j) *(ptr++) = 0; - base += (bi->dispDeviceRowBytes >> 3); + base += (dispDeviceRowBytes >> 3); } } #ifndef NO_SCROLL static void scrollscreen(void) { - unsigned long offset = reloc_offset(); - boot_infos_t* bi = PTRRELOC(&disp_bi); - unsigned long *src = (unsigned long *)calc_base(bi,0,16); - unsigned long *dst = (unsigned long *)calc_base(bi,0,0); - unsigned long width = ((bi->dispDeviceRect[2] - bi->dispDeviceRect[0]) * - (bi->dispDeviceDepth >> 3)) >> 3; + unsigned long *src = (unsigned long *)calc_base(0,16); + unsigned long *dst = (unsigned long *)calc_base(0,0); + unsigned long width = ((dispDeviceRect[2] - dispDeviceRect[0]) * + (dispDeviceDepth >> 3)) >> 3; int i,j; - for (i=0; i<(bi->dispDeviceRect[3] - bi->dispDeviceRect[1] - 16); i++) + for (i=0; i<(dispDeviceRect[3] - dispDeviceRect[1] - 16); i++) { unsigned long *src_ptr = src; unsigned long *dst_ptr = dst; for(j=width; j; --j) *(dst_ptr++) = *(src_ptr++); - src += (bi->dispDeviceRowBytes >> 3); - dst += (bi->dispDeviceRowBytes >> 3); + src += (dispDeviceRowBytes >> 3); + dst += (dispDeviceRowBytes >> 3); } for (i=0; i<16; i++) { unsigned long *dst_ptr = dst; for(j=width; j; --j) *(dst_ptr++) = 0; - dst += (bi->dispDeviceRowBytes >> 3); + dst += (dispDeviceRowBytes >> 3); } } #endif /* ndef NO_SCROLL */ void btext_drawchar(char c) { - unsigned long offset = reloc_offset(); int cline = 0; #ifdef NO_SCROLL int x; #endif - if (!RELOC(boot_text_mapped)) + if (!boot_text_mapped) return; switch (c) { case '\b': - if (RELOC(g_loc_X) > 0) - --RELOC(g_loc_X); + if (g_loc_X > 0) + --g_loc_X; break; case '\t': - RELOC(g_loc_X) = (RELOC(g_loc_X) & -8) + 8; + g_loc_X = (g_loc_X & -8) + 8; break; case '\r': - RELOC(g_loc_X) = 0; + g_loc_X = 0; break; case '\n': - RELOC(g_loc_X) = 0; - RELOC(g_loc_Y)++; + g_loc_X = 0; + g_loc_Y++; cline = 1; break; default: - draw_byte(c, RELOC(g_loc_X)++, RELOC(g_loc_Y)); + draw_byte(c, g_loc_X++, g_loc_Y); } - if (RELOC(g_loc_X) >= RELOC(g_max_loc_X)) { - RELOC(g_loc_X) = 0; - RELOC(g_loc_Y)++; + if (g_loc_X >= g_max_loc_X) { + g_loc_X = 0; + g_loc_Y++; cline = 1; } #ifndef NO_SCROLL - while (RELOC(g_loc_Y) >= RELOC(g_max_loc_Y)) { + while (g_loc_Y >= g_max_loc_Y) { scrollscreen(); - RELOC(g_loc_Y)--; + g_loc_Y--; } #else /* wrap around from bottom to top of screen so we don't waste time scrolling each line. -- paulus. */ - if (RELOC(g_loc_Y) >= RELOC(g_max_loc_Y)) - RELOC(g_loc_Y) = 0; + if (g_loc_Y >= g_max_loc_Y) + g_loc_Y = 0; if (cline) { - for (x = 0; x < RELOC(g_max_loc_X); ++x) - draw_byte(' ', x, RELOC(g_loc_Y)); + for (x = 0; x < g_max_loc_X; ++x) + draw_byte(' ', x, g_loc_Y); } #endif } void btext_drawstring(const char *c) { - unsigned long offset = reloc_offset(); - - if (!RELOC(boot_text_mapped)) + if (!boot_text_mapped) return; while (*c) btext_drawchar(*c++); @@ -257,10 +279,9 @@ void btext_drawstring(const char *c) void btext_drawhex(unsigned long v) { - unsigned long offset = reloc_offset(); - char *hex_table = RELOC("0123456789abcdef"); + char *hex_table = "0123456789abcdef"; - if (!RELOC(boot_text_mapped)) + if (!boot_text_mapped) return; btext_drawchar(hex_table[(v >> 60) & 0x0000000FUL]); btext_drawchar(hex_table[(v >> 56) & 0x0000000FUL]); @@ -283,14 +304,11 @@ void btext_drawhex(unsigned long v) static void draw_byte(unsigned char c, long locX, long locY) { - unsigned long offset = reloc_offset(); - boot_infos_t* bi = PTRRELOC(&disp_bi); - unsigned char *base = calc_base(bi, locX << 3, locY << 4); - unsigned char *font = PTRRELOC(&vga_font[((unsigned int)c) * 16]); - int rb = bi->dispDeviceRowBytes; - -#if 0 - switch(bi->dispDeviceDepth) { + unsigned char *base = calc_base(locX << 3, locY << 4); + unsigned char *font = &vga_font[((unsigned int)c) * 16]; + int rb = dispDeviceRowBytes; + + switch(dispDeviceDepth) { case 24: case 32: draw_byte_32(font, (unsigned int *)base, rb); @@ -303,17 +321,6 @@ static void draw_byte(unsigned char c, long locX, long locY) draw_byte_8(font, (unsigned int *)base, rb); break; } -#else - if(bi->dispDeviceDepth == 24 || - bi->dispDeviceDepth == 32) { - draw_byte_32(font, (unsigned int *)base, rb); - } else if(bi->dispDeviceDepth == 15 || - bi->dispDeviceDepth == 16) { - draw_byte_16(font, (unsigned int *)base, rb); - } else if(bi->dispDeviceDepth == 8) { - draw_byte_8(font, (unsigned int *)base, rb); - } -#endif } static unsigned int expand_bits_8[16] = { @@ -369,8 +376,7 @@ static void draw_byte_16(unsigned char *font, unsigned int *base, int rb) int l, bits; int fg = 0xFFFFFFFFUL; int bg = 0x00000000UL; - unsigned long offset = reloc_offset(); - unsigned int *eb = PTRRELOC((int *)expand_bits_16); + unsigned int *eb = (int *)expand_bits_16; for (l = 0; l < 16; ++l) { @@ -388,8 +394,7 @@ static void draw_byte_8(unsigned char *font, unsigned int *base, int rb) int l, bits; int fg = 0x0F0F0F0FUL; int bg = 0x00000000UL; - unsigned long offset = reloc_offset(); - unsigned int *eb = PTRRELOC((int *)expand_bits_8); + unsigned int *eb = (int *)expand_bits_8; for (l = 0; l < 16; ++l) { diff --git a/arch/ppc64/kernel/hvconsole.c b/arch/ppc64/kernel/hvconsole.c index dedefb305..c72fb8ffe 100644 --- a/arch/ppc64/kernel/hvconsole.c +++ b/arch/ppc64/kernel/hvconsole.c @@ -1,6 +1,10 @@ /* * hvconsole.c * Copyright (C) 2004 Hollis Blanchard, IBM Corporation + * Copyright (C) 2004 IBM Corporation + * + * Additional Author(s): + * Ryan S. Arnold * * LPAR console support. * @@ -22,14 +26,22 @@ #include #include #include -#include #include +#include -int hvc_get_chars(int index, char *buf, int count) +/** + * hvc_get_chars - retrieve characters from firmware for denoted vterm adatper + * @vtermno: The vtermno or unit_address of the adapter from which to fetch the + * data. + * @buf: The character buffer into which to put the character data fetched from + * firmware. + * @count: not used? + */ +int hvc_get_chars(uint32_t vtermno, char *buf, int count) { unsigned long got; - if (plpar_hcall(H_GET_TERM_CHAR, index, 0, 0, 0, &got, + if (plpar_hcall(H_GET_TERM_CHAR, vtermno, 0, 0, 0, &got, (unsigned long *)buf, (unsigned long *)buf+1) == H_Success) { /* * Work around a HV bug where it gives us a null @@ -53,40 +65,56 @@ int hvc_get_chars(int index, char *buf, int count) EXPORT_SYMBOL(hvc_get_chars); -int hvc_put_chars(int index, const char *buf, int count) +/** + * hvc_put_chars: send characters to firmware for denoted vterm adapter + * @vtermno: The vtermno or unit_address of the adapter from which the data + * originated. + * @buf: The character buffer that contains the character data to send to + * firmware. + * @count: Send this number of characters. + */ +int hvc_put_chars(uint32_t vtermno, const char *buf, int count) { unsigned long *lbuf = (unsigned long *) buf; long ret; - ret = plpar_hcall_norets(H_PUT_TERM_CHAR, index, count, lbuf[0], + ret = plpar_hcall_norets(H_PUT_TERM_CHAR, vtermno, count, lbuf[0], lbuf[1]); if (ret == H_Success) return count; if (ret == H_Busy) return 0; - return -1; + return -EIO; } EXPORT_SYMBOL(hvc_put_chars); -/* return the number of client vterms present */ -/* XXX this requires an interface change to handle multiple discontiguous - * vterms */ -int hvc_count(int *start_termno) +/* + * We hope/assume that the first vty found corresponds to the first console + * device. + */ +int hvc_find_vtys(void) { struct device_node *vty; int num_found = 0; - /* consider only the first vty node. - * we should _always_ be able to find one. */ - vty = of_find_node_by_name(NULL, "vty"); - if (vty && device_is_compatible(vty, "hvterm1")) { - u32 *termno = (u32 *)get_property(vty, "reg", NULL); + for (vty = of_find_node_by_name(NULL, "vty"); vty != NULL; + vty = of_find_node_by_name(vty, "vty")) { + uint32_t *vtermno; + + /* We have statically defined space for only a certain number of + * console adapters. */ + if (num_found >= MAX_NR_HVC_CONSOLES) + break; - if (termno && start_termno) - *start_termno = *termno; - num_found = 1; - of_node_put(vty); + vtermno = (uint32_t *)get_property(vty, "reg", NULL); + if (!vtermno) + continue; + + if (device_is_compatible(vty, "hvterm1")) { + hvc_instantiate(*vtermno, num_found); + ++num_found; + } } return num_found; diff --git a/arch/ppc64/kernel/hvcserver.c b/arch/ppc64/kernel/hvcserver.c index fbe445ec0..bde8f42da 100644 --- a/arch/ppc64/kernel/hvcserver.c +++ b/arch/ppc64/kernel/hvcserver.c @@ -61,14 +61,21 @@ int hvcs_convert(long to_convert) } } +/** + * hvcs_free_partner_info - free pi allocated by hvcs_get_partner_info + * @head: list_head pointer for an allocated list of partner info structs to + * free. + * + * This function is used to free the partner info list that was returned by + * calling hvcs_get_partner_info(). + */ int hvcs_free_partner_info(struct list_head *head) { struct hvcs_partner_info *pi; struct list_head *element; - if (!head) { + if (!head) return -EINVAL; - } while (!list_empty(head)) { element = head->next; @@ -82,7 +89,7 @@ int hvcs_free_partner_info(struct list_head *head) EXPORT_SYMBOL(hvcs_free_partner_info); /* Helper function for hvcs_get_partner_info */ -int hvcs_next_partner(unsigned int unit_address, +int hvcs_next_partner(uint32_t unit_address, unsigned long last_p_partition_ID, unsigned long last_p_unit_address, unsigned long *pi_buff) @@ -94,25 +101,37 @@ int hvcs_next_partner(unsigned int unit_address, return hvcs_convert(retval); } -/* - * The unit_address parameter is the unit address of the vty-server vdevice - * in whose partner information the caller is interested. This function - * uses a pointer to a list_head instance in which to store the partner info. +/** + * hvcs_get_partner_info - Get all of the partner info for a vty-server adapter + * @unit_address: The unit_address of the vty-server adapter for which this + * function is fetching partner info. + * @head: An initialized list_head pointer to an empty list to use to return the + * list of partner info fetched from the hypervisor to the caller. + * @pi_buff: A page sized buffer pre-allocated prior to calling this function + * that is to be used to be used by firmware as an iterator to keep track + * of the partner info retrieval. + * * This function returns non-zero on success, or if there is no partner info. * + * The pi_buff is pre-allocated prior to calling this function because this + * function may be called with a spin_lock held and kmalloc of a page is not + * recommended as GFP_ATOMIC. + * + * The first long of this buffer is used to store a partner unit address. The + * second long is used to store a partner partition ID and starting at + * pi_buff[2] is the 79 character Converged Location Code (diff size than the + * unsigned longs, hence the casting mumbo jumbo you see later). + * * Invocation of this function should always be followed by an invocation of * hvcs_free_partner_info() using a pointer to the SAME list head instance - * that was used to store the partner_info list. + * that was passed as a parameter to this function. */ -int hvcs_get_partner_info(unsigned int unit_address, struct list_head *head, +int hvcs_get_partner_info(uint32_t unit_address, struct list_head *head, unsigned long *pi_buff) { /* - * This is a page sized buffer to be passed to hvcall per invocation. - * NOTE: the first long returned is unit_address. The second long - * returned is the partition ID and starting with pi_buff[2] are - * HVCS_CLC_LENGTH characters, which are diff size than the unsigned - * long, hence the casting mumbojumbo you see later. + * Dealt with as longs because of the hcall interface even though the + * values are uint32_t. */ unsigned long last_p_partition_ID; unsigned long last_p_unit_address; @@ -122,15 +141,12 @@ int hvcs_get_partner_info(unsigned int unit_address, struct list_head *head, memset(pi_buff, 0x00, PAGE_SIZE); /* invalid parameters */ - if (!head) + if (!head || !pi_buff) return -EINVAL; last_p_partition_ID = last_p_unit_address = ~0UL; INIT_LIST_HEAD(head); - if (!pi_buff) - return -ENOMEM; - do { retval = hvcs_next_partner(unit_address, last_p_partition_ID, last_p_unit_address, pi_buff); @@ -183,21 +199,29 @@ int hvcs_get_partner_info(unsigned int unit_address, struct list_head *head, } EXPORT_SYMBOL(hvcs_get_partner_info); -/* +/** + * hvcs_register_connection - establish a connection between this vty-server and + * a vty. + * @unit_address: The unit address of the vty-server adapter that is to be + * establish a connection. + * @p_partition_ID: The partition ID of the vty adapter that is to be connected. + * @p_unit_address: The unit address of the vty adapter to which the vty-server + * is to be connected. + * * If this function is called once and -EINVAL is returned it may * indicate that the partner info needs to be refreshed for the * target unit address at which point the caller must invoke * hvcs_get_partner_info() and then call this function again. If, * for a second time, -EINVAL is returned then it indicates that * there is probably already a partner connection registered to a - * different vty-server@ vdevice. It is also possible that a second + * different vty-server adapter. It is also possible that a second * -EINVAL may indicate that one of the parms is not valid, for - * instance if the link was removed between the vty-server@ vdevice - * and the vty@ vdevice that you are trying to open. Don't shoot the + * instance if the link was removed between the vty-server adapter + * and the vty adapter that you are trying to open. Don't shoot the * messenger. Firmware implemented it this way. */ -int hvcs_register_connection( unsigned int unit_address, - unsigned int p_partition_ID, unsigned int p_unit_address) +int hvcs_register_connection( uint32_t unit_address, + uint32_t p_partition_ID, uint32_t p_unit_address) { long retval; retval = plpar_hcall_norets(H_REGISTER_VTERM, unit_address, @@ -206,11 +230,17 @@ int hvcs_register_connection( unsigned int unit_address, } EXPORT_SYMBOL(hvcs_register_connection); -/* - * If -EBUSY is returned continue to call this function - * until 0 is returned. +/** + * hvcs_free_connection - free the connection between a vty-server and vty + * @unit_address: The unit address of the vty-server that is to have its + * connection severed. + * + * This function is used to free the partner connection between a vty-server + * adapter and a vty adapter. + * + * If -EBUSY is returned continue to call this function until 0 is returned. */ -int hvcs_free_connection(unsigned int unit_address) +int hvcs_free_connection(uint32_t unit_address) { long retval; retval = plpar_hcall_norets(H_FREE_VTERM, unit_address); diff --git a/arch/ppc64/kernel/iSeries_htab.c b/arch/ppc64/kernel/iSeries_htab.c index 2456227b4..b1177b476 100644 --- a/arch/ppc64/kernel/iSeries_htab.c +++ b/arch/ppc64/kernel/iSeries_htab.c @@ -233,4 +233,6 @@ void hpte_init_iSeries(void) ppc_md.hpte_updateboltedpp = iSeries_hpte_updateboltedpp; ppc_md.hpte_insert = iSeries_hpte_insert; ppc_md.hpte_remove = iSeries_hpte_remove; + + htab_finish_init(); } diff --git a/arch/ppc64/kernel/iSeries_pci.c b/arch/ppc64/kernel/iSeries_pci.c index 3472d188e..d3715a153 100644 --- a/arch/ppc64/kernel/iSeries_pci.c +++ b/arch/ppc64/kernel/iSeries_pci.c @@ -419,46 +419,39 @@ static int iSeries_Scan_Bridge_Slot(HvBusNumber Bus, * I/0 Memory copy MUST use mmio commands on iSeries * To do; For performance, include the hv call directly */ -void *iSeries_memset_io(void *dest, char c, size_t Count) +void iSeries_memset_io(volatile void __iomem *dest, char c, size_t Count) { u8 ByteValue = c; long NumberOfBytes = Count; - char *IoBuffer = dest; while (NumberOfBytes > 0) { - iSeries_Write_Byte(ByteValue, (void *)IoBuffer); - ++IoBuffer; + iSeries_Write_Byte(ByteValue, dest++); -- NumberOfBytes; } - return dest; } EXPORT_SYMBOL(iSeries_memset_io); -void *iSeries_memcpy_toio(void *dest, void *source, size_t count) +void iSeries_memcpy_toio(volatile void __iomem *dest, void *source, size_t count) { - char *dst = dest; char *src = source; long NumberOfBytes = count; while (NumberOfBytes > 0) { - iSeries_Write_Byte(*src++, (void *)dst++); + iSeries_Write_Byte(*src++, dest++); -- NumberOfBytes; } - return dest; } EXPORT_SYMBOL(iSeries_memcpy_toio); -void *iSeries_memcpy_fromio(void *dest, void *source, size_t count) +void iSeries_memcpy_fromio(void *dest, const volatile void __iomem *src, size_t count) { char *dst = dest; - char *src = source; long NumberOfBytes = count; while (NumberOfBytes > 0) { - *dst++ = iSeries_Read_Byte((void *)src++); + *dst++ = iSeries_Read_Byte(src++); -- NumberOfBytes; } - return dest; } EXPORT_SYMBOL(iSeries_memcpy_fromio); @@ -612,17 +605,19 @@ static int CheckReturnCode(char *TextHdr, struct iSeries_Device_Node *DevNode, * Note: Make sure the passed variable end up on the stack to avoid * the exposure of being device global. */ -static inline struct iSeries_Device_Node *xlateIoMmAddress(void *IoAddress, +static inline struct iSeries_Device_Node *xlateIoMmAddress(const volatile void __iomem *IoAddress, u64 *dsaptr, u64 *BarOffsetPtr) { + unsigned long OrigIoAddr; unsigned long BaseIoAddr; unsigned long TableIndex; struct iSeries_Device_Node *DevNode; - if (((unsigned long)IoAddress < iSeries_Base_Io_Memory) || - ((unsigned long)IoAddress >= iSeries_Max_Io_Memory)) + OrigIoAddr = (unsigned long __force)IoAddress; + if ((OrigIoAddr < iSeries_Base_Io_Memory) || + (OrigIoAddr >= iSeries_Max_Io_Memory)) return NULL; - BaseIoAddr = (unsigned long)IoAddress - iSeries_Base_Io_Memory; + BaseIoAddr = OrigIoAddr - iSeries_Base_Io_Memory; TableIndex = BaseIoAddr / iSeries_IoMmTable_Entry_Size; DevNode = iSeries_IoMmTable[TableIndex]; @@ -644,7 +639,7 @@ static inline struct iSeries_Device_Node *xlateIoMmAddress(void *IoAddress, * iSeries_Read_Word = Read Word (16 bit) * iSeries_Read_Long = Read Long (32 bit) */ -u8 iSeries_Read_Byte(void *IoAddress) +u8 iSeries_Read_Byte(const volatile void __iomem *IoAddress) { u64 BarOffset; u64 dsa; @@ -673,7 +668,7 @@ u8 iSeries_Read_Byte(void *IoAddress) } EXPORT_SYMBOL(iSeries_Read_Byte); -u16 iSeries_Read_Word(void *IoAddress) +u16 iSeries_Read_Word(const volatile void __iomem *IoAddress) { u64 BarOffset; u64 dsa; @@ -703,7 +698,7 @@ u16 iSeries_Read_Word(void *IoAddress) } EXPORT_SYMBOL(iSeries_Read_Word); -u32 iSeries_Read_Long(void *IoAddress) +u32 iSeries_Read_Long(const volatile void __iomem *IoAddress) { u64 BarOffset; u64 dsa; @@ -740,7 +735,7 @@ EXPORT_SYMBOL(iSeries_Read_Long); * iSeries_Write_Word = Write Word(16 bit) * iSeries_Write_Long = Write Long(32 bit) */ -void iSeries_Write_Byte(u8 data, void *IoAddress) +void iSeries_Write_Byte(u8 data, volatile void __iomem *IoAddress) { u64 BarOffset; u64 dsa; @@ -767,7 +762,7 @@ void iSeries_Write_Byte(u8 data, void *IoAddress) } EXPORT_SYMBOL(iSeries_Write_Byte); -void iSeries_Write_Word(u16 data, void *IoAddress) +void iSeries_Write_Word(u16 data, volatile void __iomem *IoAddress) { u64 BarOffset; u64 dsa; @@ -794,7 +789,7 @@ void iSeries_Write_Word(u16 data, void *IoAddress) } EXPORT_SYMBOL(iSeries_Write_Word); -void iSeries_Write_Long(u32 data, void *IoAddress) +void iSeries_Write_Long(u32 data, volatile void __iomem *IoAddress) { u64 BarOffset; u64 dsa; @@ -820,7 +815,3 @@ void iSeries_Write_Long(u32 data, void *IoAddress) } while (CheckReturnCode("WWL", DevNode, rc) != 0); } EXPORT_SYMBOL(iSeries_Write_Long); - -void pcibios_name_device(struct pci_dev *dev) -{ -} diff --git a/arch/ppc64/kernel/iSeries_pci_reset.c b/arch/ppc64/kernel/iSeries_pci_reset.c index e1e48c35e..8848ece79 100644 --- a/arch/ppc64/kernel/iSeries_pci_reset.c +++ b/arch/ppc64/kernel/iSeries_pci_reset.c @@ -65,7 +65,8 @@ int iSeries_Device_ToggleReset(struct pci_dev *PciDev, int AssertTime, AssertDelay = (5 * HZ) / 10; else AssertDelay = (AssertTime * HZ) / 10; - if (WaitDelay == 0) + + if (DelayTime == 0) WaitDelay = (30 * HZ) / 10; else WaitDelay = (DelayTime * HZ) / 10; diff --git a/arch/ppc64/kernel/iSeries_setup.h b/arch/ppc64/kernel/iSeries_setup.h index 240dad4ef..098041b4c 100644 --- a/arch/ppc64/kernel/iSeries_setup.h +++ b/arch/ppc64/kernel/iSeries_setup.h @@ -19,10 +19,6 @@ #ifndef __ISERIES_SETUP_H__ #define __ISERIES_SETUP_H__ -extern void iSeries_init_early(void); -extern void iSeries_init(unsigned long r3, unsigned long ird_start, - unsigned long ird_end, unsigned long cline_start, - unsigned long cline_end); extern void iSeries_setup_arch(void); extern void iSeries_setup_residual(struct seq_file *m, int cpu_id); extern void iSeries_get_cpuinfo(struct seq_file *m); diff --git a/arch/ppc64/kernel/idle_power4.S b/arch/ppc64/kernel/idle_power4.S index fb983069d..97e4a2655 100644 --- a/arch/ppc64/kernel/idle_power4.S +++ b/arch/ppc64/kernel/idle_power4.S @@ -46,7 +46,7 @@ END_FTR_SECTION_IFCLR(CPU_FTR_CAN_NAP) /* Now check if user or arch enabled NAP mode */ LOADBASE(r3,powersave_nap) lwz r4,powersave_nap@l(r3) - cmpi 0,r4,0 + cmpwi 0,r4,0 beqlr /* Clear MSR:EE */ diff --git a/arch/ppc64/kernel/iomap.c b/arch/ppc64/kernel/iomap.c new file mode 100644 index 000000000..b70a9240c --- /dev/null +++ b/arch/ppc64/kernel/iomap.c @@ -0,0 +1,119 @@ +#include +#include +#include +#include + +/* + * Here comes the ppc64 implementation of the IOMAP + * interfaces. + */ +unsigned int fastcall ioread8(void __iomem *addr) +{ + return readb(addr); +} +unsigned int fastcall ioread16(void __iomem *addr) +{ + return readw(addr); +} +unsigned int fastcall ioread32(void __iomem *addr) +{ + return readl(addr); +} +EXPORT_SYMBOL(ioread8); +EXPORT_SYMBOL(ioread16); +EXPORT_SYMBOL(ioread32); + +void fastcall iowrite8(u8 val, void __iomem *addr) +{ + writeb(val, addr); +} +void fastcall iowrite16(u16 val, void __iomem *addr) +{ + writew(val, addr); +} +void fastcall iowrite32(u32 val, void __iomem *addr) +{ + writel(val, addr); +} +EXPORT_SYMBOL(iowrite8); +EXPORT_SYMBOL(iowrite16); +EXPORT_SYMBOL(iowrite32); + +/* + * These are the "repeat read/write" functions. Note the + * non-CPU byte order. We do things in "IO byteorder" + * here. + * + * FIXME! We could make these do EEH handling if we really + * wanted. Not clear if we do. + */ +void ioread8_rep(void __iomem *addr, void *dst, unsigned long count) +{ + _insb((u8 __force *) addr, dst, count); +} +void ioread16_rep(void __iomem *addr, void *dst, unsigned long count) +{ + _insw_ns((u16 __force *) addr, dst, count); +} +void ioread32_rep(void __iomem *addr, void *dst, unsigned long count) +{ + _insl_ns((u32 __force *) addr, dst, count); +} +EXPORT_SYMBOL(ioread8_rep); +EXPORT_SYMBOL(ioread16_rep); +EXPORT_SYMBOL(ioread32_rep); + +void iowrite8_rep(void __iomem *addr, const void *src, unsigned long count) +{ + _outsb((u8 __force *) addr, src, count); +} +void iowrite16_rep(void __iomem *addr, const void *src, unsigned long count) +{ + _outsw_ns((u16 __force *) addr, src, count); +} +void iowrite32_rep(void __iomem *addr, const void *src, unsigned long count) +{ + _outsl_ns((u32 __force *) addr, src, count); +} +EXPORT_SYMBOL(iowrite8_rep); +EXPORT_SYMBOL(iowrite16_rep); +EXPORT_SYMBOL(iowrite32_rep); + +void __iomem *ioport_map(unsigned long port, unsigned int len) +{ + if (!_IO_IS_VALID(port)) + return NULL; + return (void __iomem *) (port+pci_io_base); +} + +void ioport_unmap(void __iomem *addr) +{ + /* Nothing to do */ +} +EXPORT_SYMBOL(ioport_map); +EXPORT_SYMBOL(ioport_unmap); + +void __iomem *pci_iomap(struct pci_dev *dev, int bar, unsigned long max) +{ + unsigned long start = pci_resource_start(dev, bar); + unsigned long len = pci_resource_len(dev, bar); + unsigned long flags = pci_resource_flags(dev, bar); + + if (!len) + return NULL; + if (max && len > max) + len = max; + if (flags & IORESOURCE_IO) + return ioport_map(start, len); + if (flags & IORESOURCE_MEM) + return (void __iomem *) start; + /* 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/ppc64/kernel/lmb.c b/arch/ppc64/kernel/lmb.c index c2e320635..56a26eec0 100644 --- a/arch/ppc64/kernel/lmb.c +++ b/arch/ppc64/kernel/lmb.c @@ -20,7 +20,44 @@ #include #include -struct lmb lmb __initdata; +struct lmb lmb; + +#undef DEBUG + +void lmb_dump_all(void) +{ +#ifdef DEBUG + unsigned long i; + struct lmb *_lmb = &lmb; + + udbg_printf("lmb_dump_all:\n"); + udbg_printf(" memory.cnt = 0x%lx\n", + _lmb->memory.cnt); + udbg_printf(" memory.size = 0x%lx\n", + _lmb->memory.size); + for (i=0; i < _lmb->memory.cnt ;i++) { + udbg_printf(" memory.region[0x%x].base = 0x%lx\n", + i, _lmb->memory.region[i].base); + udbg_printf(" .physbase = 0x%lx\n", + _lmb->memory.region[i].physbase); + udbg_printf(" .size = 0x%lx\n", + _lmb->memory.region[i].size); + } + + udbg_printf("\n reserved.cnt = 0x%lx\n", + _lmb->reserved.cnt); + udbg_printf(" reserved.size = 0x%lx\n", + _lmb->reserved.size); + for (i=0; i < _lmb->reserved.cnt ;i++) { + udbg_printf(" reserved.region[0x%x].base = 0x%lx\n", + i, _lmb->reserved.region[i].base); + udbg_printf(" .physbase = 0x%lx\n", + _lmb->reserved.region[i].physbase); + udbg_printf(" .size = 0x%lx\n", + _lmb->reserved.region[i].size); + } +#endif /* DEBUG */ +} static unsigned long __init lmb_addrs_overlap(unsigned long base1, unsigned long size1, @@ -71,8 +108,7 @@ lmb_coalesce_regions(struct lmb_region *rgn, unsigned long r1, unsigned long r2) void __init lmb_init(void) { - unsigned long offset = reloc_offset(); - struct lmb *_lmb = PTRRELOC(&lmb); + struct lmb *_lmb = &lmb; /* Create a dummy zero size LMB which will get coalesced away later. * This simplifies the lmb_add() code below... @@ -94,8 +130,7 @@ lmb_analyze(void) unsigned long i; unsigned long mem_size = 0; unsigned long size_mask = 0; - unsigned long offset = reloc_offset(); - struct lmb *_lmb = PTRRELOC(&lmb); + struct lmb *_lmb = &lmb; #ifdef CONFIG_MSCHUNKS unsigned long physbase = 0; #endif @@ -178,8 +213,7 @@ lmb_add_region(struct lmb_region *rgn, unsigned long base, unsigned long size) long __init lmb_add(unsigned long base, unsigned long size) { - unsigned long offset = reloc_offset(); - struct lmb *_lmb = PTRRELOC(&lmb); + struct lmb *_lmb = &lmb; struct lmb_region *_rgn = &(_lmb->memory); /* On pSeries LPAR systems, the first LMB is our RMO region. */ @@ -193,8 +227,7 @@ lmb_add(unsigned long base, unsigned long size) long __init lmb_reserve(unsigned long base, unsigned long size) { - unsigned long offset = reloc_offset(); - struct lmb *_lmb = PTRRELOC(&lmb); + struct lmb *_lmb = &lmb; struct lmb_region *_rgn = &(_lmb->reserved); return lmb_add_region(_rgn, base, size); @@ -227,8 +260,7 @@ lmb_alloc_base(unsigned long size, unsigned long align, unsigned long max_addr) { long i, j; unsigned long base = 0; - unsigned long offset = reloc_offset(); - struct lmb *_lmb = PTRRELOC(&lmb); + struct lmb *_lmb = &lmb; struct lmb_region *_mem = &(_lmb->memory); struct lmb_region *_rsv = &(_lmb->reserved); @@ -263,8 +295,7 @@ lmb_alloc_base(unsigned long size, unsigned long align, unsigned long max_addr) unsigned long __init lmb_phys_mem_size(void) { - unsigned long offset = reloc_offset(); - struct lmb *_lmb = PTRRELOC(&lmb); + struct lmb *_lmb = &lmb; #ifdef CONFIG_MSCHUNKS return _lmb->memory.size; #else @@ -282,8 +313,7 @@ lmb_phys_mem_size(void) unsigned long __init lmb_end_of_DRAM(void) { - unsigned long offset = reloc_offset(); - struct lmb *_lmb = PTRRELOC(&lmb); + struct lmb *_lmb = &lmb; struct lmb_region *_mem = &(_lmb->memory); int idx = _mem->cnt - 1; @@ -300,8 +330,7 @@ unsigned long __init lmb_abs_to_phys(unsigned long aa) { unsigned long i, pa = aa; - unsigned long offset = reloc_offset(); - struct lmb *_lmb = PTRRELOC(&lmb); + struct lmb *_lmb = &lmb; struct lmb_region *_mem = &(_lmb->memory); for (i=0; i < _mem->cnt; i++) { diff --git a/arch/ppc64/kernel/nvram.c b/arch/ppc64/kernel/nvram.c index 89d6a0ad7..49ddf5e09 100644 --- a/arch/ppc64/kernel/nvram.c +++ b/arch/ppc64/kernel/nvram.c @@ -340,7 +340,7 @@ static int nvram_create_os_partition(void) struct list_head * p; struct nvram_partition * part; struct nvram_partition * new_part = NULL; - struct nvram_partition * free_part; + struct nvram_partition * free_part = NULL; int seq_init[2] = { 0, 0 }; loff_t tmp_index; long size = 0; @@ -603,6 +603,7 @@ void __exit nvram_cleanup(void) } +#ifdef CONFIG_PPC_PSERIES /* nvram_write_error_log * @@ -727,6 +728,7 @@ int nvram_clear_error_log() return 0; } +#endif /* CONFIG_PPC_PSERIES */ module_init(nvram_init); module_exit(nvram_cleanup); diff --git a/arch/ppc64/kernel/open_pic_u3.c b/arch/ppc64/kernel/open_pic_u3.c index 4f9832a3b..42376756c 100644 --- a/arch/ppc64/kernel/open_pic_u3.c +++ b/arch/ppc64/kernel/open_pic_u3.c @@ -344,5 +344,5 @@ void __init openpic2_init(int offset) openpic2_set_priority(0); openpic2_disable_8259_pass_through(); - ppc64_boot_msg(0x25, "OpenPic2 Done"); + ppc64_boot_msg(0x25, "OpenPic U3 Done"); } diff --git a/arch/ppc64/kernel/pSeries_iommu.c b/arch/ppc64/kernel/pSeries_iommu.c index 91749173f..abbbe2823 100644 --- a/arch/ppc64/kernel/pSeries_iommu.c +++ b/arch/ppc64/kernel/pSeries_iommu.c @@ -42,6 +42,7 @@ #include #include #include +#include #include "pci.h" @@ -88,9 +89,153 @@ static void tce_free_pSeries(struct iommu_table *tbl, long index, long npages) } +static void tce_build_pSeriesLP(struct iommu_table *tbl, long tcenum, + long npages, unsigned long uaddr, + enum dma_data_direction direction) +{ + u64 rc; + union tce_entry tce; + + tce.te_word = 0; + tce.te_rpn = (virt_to_abs(uaddr)) >> PAGE_SHIFT; + tce.te_rdwr = 1; + if (direction != DMA_TO_DEVICE) + tce.te_pciwr = 1; + + while (npages--) { + rc = plpar_tce_put((u64)tbl->it_index, + (u64)tcenum << 12, + tce.te_word ); + + if (rc && printk_ratelimit()) { + printk("tce_build_pSeriesLP: plpar_tce_put failed. rc=%ld\n", rc); + printk("\tindex = 0x%lx\n", (u64)tbl->it_index); + printk("\ttcenum = 0x%lx\n", (u64)tcenum); + printk("\ttce val = 0x%lx\n", tce.te_word ); + show_stack(current, (unsigned long *)__get_SP()); + } + + tcenum++; + tce.te_rpn++; + } +} + +DEFINE_PER_CPU(void *, tce_page) = NULL; + +static void tce_buildmulti_pSeriesLP(struct iommu_table *tbl, long tcenum, + long npages, unsigned long uaddr, + enum dma_data_direction direction) +{ + u64 rc; + union tce_entry tce, *tcep; + long l, limit; + + if (npages == 1) + return tce_build_pSeriesLP(tbl, tcenum, npages, uaddr, + direction); + + tcep = __get_cpu_var(tce_page); + + /* This is safe to do since interrupts are off when we're called + * from iommu_alloc{,_sg}() + */ + if (!tcep) { + tcep = (void *)__get_free_page(GFP_ATOMIC); + /* If allocation fails, fall back to the loop implementation */ + if (!tcep) + return tce_build_pSeriesLP(tbl, tcenum, npages, + uaddr, direction); + __get_cpu_var(tce_page) = tcep; + } + + tce.te_word = 0; + tce.te_rpn = (virt_to_abs(uaddr)) >> PAGE_SHIFT; + tce.te_rdwr = 1; + if (direction != DMA_TO_DEVICE) + tce.te_pciwr = 1; + + /* We can map max one pageful of TCEs at a time */ + do { + /* + * Set up the page with TCE data, looping through and setting + * the values. + */ + limit = min_t(long, npages, PAGE_SIZE/sizeof(union tce_entry)); + + for (l = 0; l < limit; l++) { + tcep[l] = tce; + tce.te_rpn++; + } + + rc = plpar_tce_put_indirect((u64)tbl->it_index, + (u64)tcenum << 12, + (u64)virt_to_abs(tcep), + limit); + + npages -= limit; + tcenum += limit; + } while (npages > 0 && !rc); + + if (rc && printk_ratelimit()) { + printk("tce_buildmulti_pSeriesLP: plpar_tce_put failed. rc=%ld\n", rc); + printk("\tindex = 0x%lx\n", (u64)tbl->it_index); + printk("\tnpages = 0x%lx\n", (u64)npages); + printk("\ttce[0] val = 0x%lx\n", tcep[0].te_word); + show_stack(current, (unsigned long *)__get_SP()); + } +} + +static void tce_free_pSeriesLP(struct iommu_table *tbl, long tcenum, long npages) +{ + u64 rc; + union tce_entry tce; + + tce.te_word = 0; + + while (npages--) { + rc = plpar_tce_put((u64)tbl->it_index, + (u64)tcenum << 12, + tce.te_word); + + if (rc && printk_ratelimit()) { + printk("tce_free_pSeriesLP: plpar_tce_put failed. rc=%ld\n", rc); + printk("\tindex = 0x%lx\n", (u64)tbl->it_index); + printk("\ttcenum = 0x%lx\n", (u64)tcenum); + printk("\ttce val = 0x%lx\n", tce.te_word ); + show_stack(current, (unsigned long *)__get_SP()); + } + + tcenum++; + } +} + + +static void tce_freemulti_pSeriesLP(struct iommu_table *tbl, long tcenum, long npages) +{ + u64 rc; + union tce_entry tce; + + tce.te_word = 0; + + rc = plpar_tce_stuff((u64)tbl->it_index, + (u64)tcenum << 12, + tce.te_word, + npages); + + if (rc && printk_ratelimit()) { + printk("tce_freemulti_pSeriesLP: plpar_tce_stuff failed\n"); + printk("\trc = %ld\n", rc); + printk("\tindex = 0x%lx\n", (u64)tbl->it_index); + printk("\tnpages = 0x%lx\n", (u64)npages); + printk("\ttce val = 0x%lx\n", tce.te_word ); + show_stack(current, (unsigned long *)__get_SP()); + } +} + + static void iommu_buses_init(void) { - struct pci_controller* phb; + struct pci_controller *phb, *tmp; struct device_node *dn, *first_dn; int num_slots, num_slots_ilog2; int first_phb = 1; @@ -106,10 +251,10 @@ static void iommu_buses_init(void) else tcetable_ilog2 = 22; - /* XXX Should we be using pci_root_buses instead? -ojn + /* XXX Should we be using pci_root_buses instead? -ojn */ - for (phb=hose_head; phb; phb=phb->next) { + list_for_each_entry_safe(phb, tmp, &hose_list, list_node) { first_dn = ((struct device_node *)phb->arch_data)->child; /* Carve 2GB into the largest dma_window_size possible */ @@ -166,24 +311,25 @@ static void iommu_table_setparms(struct pci_controller *phb, struct device_node *dn, struct iommu_table *tbl) { - phandle node; - unsigned long i; - struct of_tce_table *oft; - - node = ((struct device_node *)(phb->arch_data))->node; + struct device_node *node; + unsigned long *basep; + unsigned int *sizep; - oft = NULL; + node = (struct device_node *)phb->arch_data; - for (i=0; of_tce_table[i].node; i++) - if(of_tce_table[i].node == node) { - oft = &of_tce_table[i]; - break; - } - - if (!oft) - panic("PCI_DMA: iommu_table_setparms: Can't find phb named '%s' in of_tce_table\n", dn->full_name); - - memset((void *)oft->base, 0, oft->size); + if (get_property(node, "linux,has-tce-table", NULL) == NULL) { + printk(KERN_ERR "PCI_DMA: iommu_table_setparms: %s has no tce table !\n", + dn->full_name); + return; + } + basep = (unsigned long *)get_property(node, "linux,tce-base", NULL); + sizep = (unsigned int *)get_property(node, "linux,tce-size", NULL); + if (basep == NULL || sizep == NULL) { + printk(KERN_ERR "PCI_DMA: iommu_table_setparms: %s has missing tce" + " entries !\n", dn->full_name); + return; + } + memset((void *)(*basep), 0, *sizep); tbl->it_busno = phb->bus->number; @@ -207,10 +353,11 @@ static void iommu_table_setparms(struct pci_controller *phb, if (phb->dma_window_base_cur > (1 << 19)) panic("PCI_DMA: Unexpected number of IOAs under this PHB.\n"); - tbl->it_base = oft->base; + tbl->it_base = *basep; tbl->it_index = 0; tbl->it_entrysize = sizeof(union tce_entry); tbl->it_blocksize = 16; + tbl->it_type = TCE_PCI; } /* @@ -246,6 +393,7 @@ static void iommu_table_setparms_lpar(struct pci_controller *phb, tbl->it_index = dma_window[0]; tbl->it_entrysize = sizeof(union tce_entry); tbl->it_blocksize = 16; + tbl->it_type = TCE_PCI; } @@ -293,8 +441,16 @@ void iommu_setup_pSeries(void) /* These are called very early. */ void tce_init_pSeries(void) { - ppc_md.tce_build = tce_build_pSeries; - ppc_md.tce_free = tce_free_pSeries; + if (!(systemcfg->platform & PLATFORM_LPAR)) { + ppc_md.tce_build = tce_build_pSeries; + ppc_md.tce_free = tce_free_pSeries; + } else if (cur_cpu_spec->firmware_features & FW_FEATURE_MULTITCE) { + ppc_md.tce_build = tce_buildmulti_pSeriesLP; + ppc_md.tce_free = tce_freemulti_pSeriesLP; + } else { + ppc_md.tce_build = tce_build_pSeriesLP; + ppc_md.tce_free = tce_free_pSeriesLP; + } pci_iommu_init(); } diff --git a/arch/ppc64/kernel/pSeries_lpar.c b/arch/ppc64/kernel/pSeries_lpar.c index a44c33268..68eeb7a81 100644 --- a/arch/ppc64/kernel/pSeries_lpar.c +++ b/arch/ppc64/kernel/pSeries_lpar.c @@ -19,6 +19,8 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ +#define DEBUG + #include #include #include @@ -34,10 +36,16 @@ #include #include #include -#include #include #include #include +#include + +#ifdef DEBUG +#define DBG(fmt...) udbg_printf(fmt) +#else +#define DBG(fmt...) +#endif /* in pSeries_hvCall.S */ EXPORT_SYMBOL(plpar_hcall); @@ -45,146 +53,9 @@ EXPORT_SYMBOL(plpar_hcall_4out); EXPORT_SYMBOL(plpar_hcall_norets); EXPORT_SYMBOL(plpar_hcall_8arg_2ret); -long poll_pending(void) -{ - unsigned long dummy; - return plpar_hcall(H_POLL_PENDING, 0, 0, 0, 0, - &dummy, &dummy, &dummy); -} - -long prod_processor(void) -{ - plpar_hcall_norets(H_PROD); - return(0); -} - -long cede_processor(void) -{ - plpar_hcall_norets(H_CEDE); - return(0); -} - -long register_vpa(unsigned long flags, unsigned long proc, unsigned long vpa) -{ - plpar_hcall_norets(H_REGISTER_VPA, flags, proc, vpa); - return(0); -} - -long plpar_pte_remove(unsigned long flags, - unsigned long ptex, - unsigned long avpn, - unsigned long *old_pteh_ret, unsigned long *old_ptel_ret) -{ - unsigned long dummy; - return plpar_hcall(H_REMOVE, flags, ptex, avpn, 0, - old_pteh_ret, old_ptel_ret, &dummy); -} - -long plpar_pte_read(unsigned long flags, - unsigned long ptex, - unsigned long *old_pteh_ret, unsigned long *old_ptel_ret) -{ - unsigned long dummy; - return plpar_hcall(H_READ, flags, ptex, 0, 0, - old_pteh_ret, old_ptel_ret, &dummy); -} - -long plpar_pte_protect(unsigned long flags, - unsigned long ptex, - unsigned long avpn) -{ - return plpar_hcall_norets(H_PROTECT, flags, ptex, avpn); -} - -long plpar_tce_get(unsigned long liobn, - unsigned long ioba, - unsigned long *tce_ret) -{ - unsigned long dummy; - return plpar_hcall(H_GET_TCE, liobn, ioba, 0, 0, - tce_ret, &dummy, &dummy); -} - -long plpar_tce_put(unsigned long liobn, - unsigned long ioba, - unsigned long tceval) -{ - return plpar_hcall_norets(H_PUT_TCE, liobn, ioba, tceval); -} +extern void fw_feature_init(void); +extern void pSeries_find_serial_port(void); -long plpar_get_term_char(unsigned long termno, - unsigned long *len_ret, - char *buf_ret) -{ - unsigned long *lbuf = (unsigned long *)buf_ret; /* ToDo: alignment? */ - return plpar_hcall(H_GET_TERM_CHAR, termno, 0, 0, 0, - len_ret, lbuf+0, lbuf+1); -} - -long plpar_put_term_char(unsigned long termno, - unsigned long len, - const char *buffer) -{ - unsigned long *lbuf = (unsigned long *)buffer; /* ToDo: alignment? */ - return plpar_hcall_norets(H_PUT_TERM_CHAR, termno, len, lbuf[0], - lbuf[1]); -} - -static void tce_build_pSeriesLP(struct iommu_table *tbl, long tcenum, - long npages, unsigned long uaddr, - enum dma_data_direction direction) -{ - u64 rc; - union tce_entry tce; - - tce.te_word = 0; - tce.te_rpn = (virt_to_abs(uaddr)) >> PAGE_SHIFT; - tce.te_rdwr = 1; - if (direction != DMA_TO_DEVICE) - tce.te_pciwr = 1; - - while (npages--) { - rc = plpar_tce_put((u64)tbl->it_index, - (u64)tcenum << 12, - tce.te_word ); - - if (rc && printk_ratelimit()) { - printk("tce_build_pSeriesLP: plpar_tce_put failed. rc=%ld\n", rc); - printk("\tindex = 0x%lx\n", (u64)tbl->it_index); - printk("\ttcenum = 0x%lx\n", (u64)tcenum); - printk("\ttce val = 0x%lx\n", tce.te_word ); - show_stack(current, (unsigned long *)__get_SP()); - } - - tcenum++; - tce.te_rpn++; - } -} - -static void tce_free_pSeriesLP(struct iommu_table *tbl, long tcenum, long npages) -{ - u64 rc; - union tce_entry tce; - - tce.te_word = 0; - - while (npages--) { - rc = plpar_tce_put((u64)tbl->it_index, - (u64)tcenum << 12, - tce.te_word ); - - if (rc && printk_ratelimit()) { - printk("tce_free_pSeriesLP: plpar_tce_put failed\n"); - printk("\trc = %ld\n", rc); - printk("\tindex = 0x%lx\n", (u64)tbl->it_index); - printk("\ttcenum = 0x%lx\n", (u64)tcenum); - printk("\ttce val = 0x%lx\n", tce.te_word ); - show_stack(current, (unsigned long *)__get_SP()); - } - - tcenum++; - } -} int vtermno; /* virtual terminal# for udbg */ @@ -249,8 +120,19 @@ static unsigned char udbg_getcLP(void) } } +/* call this from early_init() for a working debug console on + * vterm capable LPAR machines + */ +void udbg_init_debug_lpar(void) +{ + vtermno = 0; + ppc_md.udbg_putc = udbg_putcLP; + ppc_md.udbg_getc = udbg_getcLP; + ppc_md.udbg_getc_poll = udbg_getc_pollLP; +} + /* returns 0 if couldn't find or use /chosen/stdout as console */ -static int find_udbg_vterm(void) +int find_udbg_vterm(void) { struct device_node *stdout_node; u32 *termno; @@ -258,15 +140,14 @@ static int find_udbg_vterm(void) int found = 0; /* find the boot console from /chosen/stdout */ - if (!of_stdout_device) { - printk(KERN_WARNING "couldn't get path from /chosen/stdout!\n"); - return found; - } - stdout_node = of_find_node_by_path(of_stdout_device); - if (!stdout_node) { - printk(KERN_WARNING "couldn't find node from /chosen/stdout\n"); - return found; - } + if (!of_chosen) + return 0; + name = (char *)get_property(of_chosen, "linux,stdout-path", NULL); + if (name == NULL) + return 0; + stdout_node = of_find_node_by_path(name); + if (!stdout_node) + return 0; /* now we have the stdout node; figure out what type of device it is. */ name = (char *)get_property(stdout_node, "name", NULL); @@ -288,15 +169,17 @@ static int find_udbg_vterm(void) } else { /* XXX implement udbg_putcLP_vtty for hvterm-protocol1 case */ printk(KERN_WARNING "%s doesn't speak hvterm1; " - "can't print udbg messages\n", of_stdout_device); + "can't print udbg messages\n", + stdout_node->full_name); } } else if (strncmp(name, "serial", 6)) { /* XXX fix ISA serial console */ printk(KERN_WARNING "serial stdout on LPAR ('%s')! " - "can't print udbg messages\n", of_stdout_device); + "can't print udbg messages\n", + stdout_node->full_name); } else { printk(KERN_WARNING "don't know how to print to stdout '%s'\n", - of_stdout_device); + stdout_node->full_name); } out: @@ -304,35 +187,6 @@ out: return found; } -void pSeries_lpar_mm_init(void); - -/* This is called early in setup.c. - * Use it to setup page table ppc_md stuff as well as udbg. - */ -void pSeriesLP_init_early(void) -{ - pSeries_lpar_mm_init(); - - tce_init_pSeries(); - - ppc_md.tce_build = tce_build_pSeriesLP; - ppc_md.tce_free = tce_free_pSeriesLP; - - pci_iommu_init(); - -#ifdef CONFIG_SMP - smp_init_pSeries(); -#endif - - /* The keyboard is not useful in the LPAR environment. - * Leave all the ppc_md keyboard interfaces NULL. - */ - - if (0 == find_udbg_vterm()) { - printk(KERN_WARNING - "can't use stdout; can't print early debug messages.\n"); - } -} long pSeries_lpar_hpte_insert(unsigned long hpte_group, unsigned long va, unsigned long prpn, @@ -377,7 +231,7 @@ long pSeries_lpar_hpte_insert(unsigned long hpte_group, lpar_rc = plpar_hcall(H_ENTER, flags, hpte_group, lhpte.dw0.dword0, lhpte.dw1.dword1, &slot, &dummy0, &dummy1); - if (lpar_rc == H_PTEG_Full) + if (unlikely(lpar_rc == H_PTEG_Full)) return -1; /* @@ -385,7 +239,7 @@ long pSeries_lpar_hpte_insert(unsigned long hpte_group, * will fail. However we must catch the failure in hash_page * or we will loop forever, so return -2 in this case. */ - if (lpar_rc != H_Success) + if (unlikely(lpar_rc != H_Success)) return -2; /* Because of iSeries, we have to pass down the secondary @@ -415,9 +269,7 @@ static long pSeries_lpar_hpte_remove(unsigned long hpte_group) if (lpar_rc == H_Success) return i; - if (lpar_rc != H_Not_Found) - panic("Bad return code from pte remove rc = %lx\n", - lpar_rc); + BUG_ON(lpar_rc != H_Not_Found); slot_offset++; slot_offset &= 0x7; @@ -426,6 +278,18 @@ static long pSeries_lpar_hpte_remove(unsigned long hpte_group) return -1; } +static void pSeries_lpar_hptab_clear(void) +{ + unsigned long size_bytes = 1UL << naca->pftSize; + unsigned long hpte_count = size_bytes >> 4; + unsigned long dummy1, dummy2; + int i; + + /* TODO: Use bulk call */ + for (i = 0; i < hpte_count; i++) + plpar_pte_remove(0, i, 0, &dummy1, &dummy2); +} + /* * NOTE: for updatepp ops we are fortunate that the linux "newpp" bits and * the low 3 bits of flags happen to line up. So no transform is needed. @@ -447,8 +311,7 @@ static long pSeries_lpar_hpte_updatepp(unsigned long slot, unsigned long newpp, if (lpar_rc == H_Not_Found) return -1; - if (lpar_rc != H_Success) - panic("bad return code from pte protect rc = %lx\n", lpar_rc); + BUG_ON(lpar_rc != H_Success); return 0; } @@ -464,11 +327,10 @@ static unsigned long pSeries_lpar_hpte_getword0(unsigned long slot) /* Do not need RPN to logical page translation */ /* No cross CEC PFT access */ flags = 0; - + lpar_rc = plpar_pte_read(flags, slot, &dword0, &dummy_word1); - if (lpar_rc != H_Success) - panic("Error on pte read in get_hpte0 rc = %lx\n", lpar_rc); + BUG_ON(lpar_rc != H_Success); return dword0; } @@ -519,15 +381,12 @@ static void pSeries_lpar_hpte_updateboltedpp(unsigned long newpp, vpn = va >> PAGE_SHIFT; slot = pSeries_lpar_hpte_find(vpn); - if (slot == -1) - panic("updateboltedpp: Could not find page to bolt\n"); + BUG_ON(slot == -1); flags = newpp & 3; lpar_rc = plpar_pte_protect(flags, slot, 0); - if (lpar_rc != H_Success) - panic("Bad return code from pte bolted protect rc = %lx\n", - lpar_rc); + BUG_ON(lpar_rc != H_Success); } static void pSeries_lpar_hpte_invalidate(unsigned long slot, unsigned long va, @@ -546,8 +405,7 @@ static void pSeries_lpar_hpte_invalidate(unsigned long slot, unsigned long va, if (lpar_rc == H_Not_Found) return; - if (lpar_rc != H_Success) - panic("Bad return code from invalidate rc = %lx\n", lpar_rc); + BUG_ON(lpar_rc != H_Success); } /* @@ -560,18 +418,19 @@ void pSeries_lpar_flush_hash_range(unsigned long context, unsigned long number, int i; unsigned long flags; struct ppc64_tlb_batch *batch = &__get_cpu_var(ppc64_tlb_batch); + int lock_tlbie = !(cur_cpu_spec->cpu_features & CPU_FTR_LOCKLESS_TLBIE); - if (!(cur_cpu_spec->cpu_features & CPU_FTR_LOCKLESS_TLBIE)) + if (lock_tlbie) spin_lock_irqsave(&pSeries_lpar_tlbie_lock, flags); for (i = 0; i < number; i++) flush_hash_page(context, batch->addr[i], batch->pte[i], local); - if (!(cur_cpu_spec->cpu_features & CPU_FTR_LOCKLESS_TLBIE)) + if (lock_tlbie) spin_unlock_irqrestore(&pSeries_lpar_tlbie_lock, flags); } -void pSeries_lpar_mm_init(void) +void hpte_init_lpar(void) { ppc_md.hpte_invalidate = pSeries_lpar_hpte_invalidate; ppc_md.hpte_updatepp = pSeries_lpar_hpte_updatepp; @@ -579,4 +438,7 @@ void pSeries_lpar_mm_init(void) ppc_md.hpte_insert = pSeries_lpar_hpte_insert; ppc_md.hpte_remove = pSeries_lpar_hpte_remove; ppc_md.flush_hash_range = pSeries_lpar_flush_hash_range; + ppc_md.hpte_clear_all = pSeries_lpar_hptab_clear; + + htab_finish_init(); } diff --git a/arch/ppc64/kernel/pSeries_setup.c b/arch/ppc64/kernel/pSeries_setup.c new file mode 100644 index 000000000..80e028904 --- /dev/null +++ b/arch/ppc64/kernel/pSeries_setup.c @@ -0,0 +1,579 @@ +/* + * linux/arch/ppc/kernel/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 + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +/* + * bootup setup stuff.. + */ + +#undef DEBUG + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "i8259.h" +#include "open_pic.h" +#include +#include +#include + +#ifdef DEBUG +#define DBG(fmt...) udbg_printf(fmt) +#else +#define DBG(fmt...) +#endif + +extern void pSeries_init_openpic(void); + +extern void find_and_init_phbs(void); +extern void pSeries_final_fixup(void); + +extern void pSeries_get_boot_time(struct rtc_time *rtc_time); +extern void pSeries_get_rtc_time(struct rtc_time *rtc_time); +extern int pSeries_set_rtc_time(struct rtc_time *rtc_time); +extern void find_udbg_vterm(void); +extern void SystemReset_FWNMI(void), MachineCheck_FWNMI(void); /* from head.S */ +int fwnmi_active; /* TRUE if an FWNMI handler is present */ + +unsigned long virtPython0Facilities = 0; // python0 facility area (memory mapped io) (64-bit format) VIRTUAL address. + +extern unsigned long loops_per_jiffy; + +extern unsigned long ppc_proc_freq; +extern unsigned long ppc_tb_freq; + +void pSeries_get_cpuinfo(struct seq_file *m) +{ + struct device_node *root; + const char *model = ""; + + root = of_find_node_by_path("/"); + if (root) + model = get_property(root, "model", NULL); + seq_printf(m, "machine\t\t: CHRP %s\n", model); + of_node_put(root); +} + +/* Initialize firmware assisted non-maskable interrupts if + * the firmware supports this feature. + * + */ +static void __init fwnmi_init(void) +{ + int ret; + int ibm_nmi_register = rtas_token("ibm,nmi-register"); + if (ibm_nmi_register == RTAS_UNKNOWN_SERVICE) + return; + ret = rtas_call(ibm_nmi_register, 2, 1, NULL, + __pa((unsigned long)SystemReset_FWNMI), + __pa((unsigned long)MachineCheck_FWNMI)); + if (ret == 0) + fwnmi_active = 1; +} + +static void __init pSeries_setup_arch(void) +{ + struct device_node *root; + unsigned int *opprop; + + /* Fixup ppc_md depending on the type of interrupt controller */ + if (naca->interrupt_controller == IC_OPEN_PIC) { + ppc_md.init_IRQ = pSeries_init_openpic; + ppc_md.get_irq = openpic_get_irq; + } else { + ppc_md.init_IRQ = xics_init_IRQ; + ppc_md.get_irq = xics_get_irq; + } + +#ifdef CONFIG_SMP + smp_init_pSeries(); +#endif + /* openpic global configuration register (64-bit format). */ + /* openpic Interrupt Source Unit pointer (64-bit format). */ + /* python0 facility area (mmio) (64-bit format) REAL address. */ + + /* init to some ~sane value until calibrate_delay() runs */ + loops_per_jiffy = 50000000; + + if (ROOT_DEV == 0) { + printk("No ramdisk, default root is /dev/sda2\n"); + ROOT_DEV = Root_SDA2; + } + + fwnmi_init(); + + /* Find and initialize PCI host bridges */ + /* iSeries needs to be done much later. */ + eeh_init(); + find_and_init_phbs(); + + /* Find the Open PIC if present */ + root = of_find_node_by_path("/"); + opprop = (unsigned int *) get_property(root, + "platform-open-pic", NULL); + if (opprop != 0) { + int n = prom_n_addr_cells(root); + unsigned long openpic; + + for (openpic = 0; n > 0; --n) + openpic = (openpic << 32) + *opprop++; + printk(KERN_DEBUG "OpenPIC addr: %lx\n", openpic); + OpenPIC_Addr = __ioremap(openpic, 0x40000, _PAGE_NO_CACHE); + } + of_node_put(root); + +#ifdef CONFIG_DUMMY_CONSOLE + conswitchp = &dummy_con; +#endif + + pSeries_nvram_init(); +} + +static int __init pSeries_init_panel(void) +{ + /* Manually leave the kernel version on the panel. */ + ppc_md.progress("Linux ppc64\n", 0); + ppc_md.progress(UTS_RELEASE, 0); + + return 0; +} +arch_initcall(pSeries_init_panel); + + + +void __init pSeries_find_serial_port(void) +{ + struct device_node *np; + unsigned long encode_phys_size = 32; + u32 *sizeprop; + + struct isa_reg_property { + u32 space; + u32 address; + u32 size; + }; + struct pci_reg_property { + struct pci_address addr; + u32 size_hi; + u32 size_lo; + }; + + DBG(" -> pSeries_find_serial_port()\n"); + + naca->serialPortAddr = 0; + + np = of_find_node_by_path("/"); + if (!np) + return; + sizeprop = (u32 *)get_property(np, "#size-cells", NULL); + if (sizeprop != NULL) + encode_phys_size = (*sizeprop) << 5; + + for (np = NULL; (np = of_find_node_by_type(np, "serial"));) { + struct device_node *isa, *pci; + struct isa_reg_property *reg; + union pci_range *rangesp; + char *typep; + + typep = (char *)get_property(np, "ibm,aix-loc", NULL); + if ((typep == NULL) || (typep && strcmp(typep, "S1"))) + continue; + + reg = (struct isa_reg_property *)get_property(np, "reg", NULL); + + isa = of_get_parent(np); + if (!isa) { + DBG("no isa parent found\n"); + break; + } + pci = of_get_parent(isa); + if (!pci) { + DBG("no pci parent found\n"); + break; + } + + rangesp = (union pci_range *)get_property(pci, "ranges", NULL); + + if ( encode_phys_size == 32 ) + naca->serialPortAddr = rangesp->pci32.phys+reg->address; + else { + naca->serialPortAddr = + ((((unsigned long)rangesp->pci64.phys_hi) << 32) + | + (rangesp->pci64.phys_lo)) + reg->address; + } + break; + } + + DBG(" <- pSeries_find_serial_port()\n"); +} + + +/* Build up the firmware_features bitmask field + * using contents of device-tree/ibm,hypertas-functions. + * Ultimately this functionality may be moved into prom.c prom_init(). + */ +void __init fw_feature_init(void) +{ + struct device_node * dn; + char * hypertas; + unsigned int len; + + DBG(" -> fw_feature_init()\n"); + + cur_cpu_spec->firmware_features = 0; + dn = of_find_node_by_path("/rtas"); + if (dn == NULL) { + printk(KERN_ERR "WARNING ! Cannot find RTAS in device-tree !\n"); + goto no_rtas; + } + + hypertas = get_property(dn, "ibm,hypertas-functions", &len); + if (hypertas) { + while (len > 0){ + int i, hypertas_len; + /* check value against table of strings */ + for(i=0; i < FIRMWARE_MAX_FEATURES ;i++) { + if ((firmware_features_table[i].name) && + (strcmp(firmware_features_table[i].name,hypertas))==0) { + /* we have a match */ + cur_cpu_spec->firmware_features |= + (firmware_features_table[i].val); + break; + } + } + hypertas_len = strlen(hypertas); + len -= hypertas_len +1; + hypertas+= hypertas_len +1; + } + } + + of_node_put(dn); + no_rtas: + printk(KERN_INFO "firmware_features = 0x%lx\n", + cur_cpu_spec->firmware_features); + + DBG(" <- fw_feature_init()\n"); +} + + +static void __init pSeries_discover_pic(void) +{ + struct device_node *np; + char *typep; + + /* + * Setup interrupt mapping options that are needed for finish_device_tree + * to properly parse the OF interrupt tree & do the virtual irq mapping + */ + __irq_offset_value = NUM_ISA_INTERRUPTS; + naca->interrupt_controller = IC_INVALID; + for (np = NULL; (np = of_find_node_by_name(np, "interrupt-controller"));) { + typep = (char *)get_property(np, "compatible", NULL); + if (strstr(typep, "open-pic")) + naca->interrupt_controller = IC_OPEN_PIC; + else if (strstr(typep, "ppc-xicp")) + naca->interrupt_controller = IC_PPC_XIC; + else + printk("initialize_naca: failed to recognize" + " interrupt-controller\n"); + break; + } +} + +/* + * Early initialization. Relocation is on but do not reference unbolted pages + */ +static void __init pSeries_init_early(void) +{ + void *comport; + int iommu_off = 0; + + DBG(" -> pSeries_init_early()\n"); + + fw_feature_init(); + + if (systemcfg->platform & PLATFORM_LPAR) + hpte_init_lpar(); + else { + hpte_init_native(); + iommu_off = (of_chosen && + get_property(of_chosen, "linux,iommu-off", NULL)); + } + + pSeries_find_serial_port(); + + if (systemcfg->platform & PLATFORM_LPAR) + find_udbg_vterm(); + else if (naca->serialPortAddr) { + /* Map the uart for udbg. */ + comport = (void *)__ioremap(naca->serialPortAddr, 16, _PAGE_NO_CACHE); + udbg_init_uart(comport); + + ppc_md.udbg_putc = udbg_putc; + ppc_md.udbg_getc = udbg_getc; + ppc_md.udbg_getc_poll = udbg_getc_poll; + DBG("Hello World !\n"); + } + + + if (iommu_off) + pci_dma_init_direct(); + else + tce_init_pSeries(); + + pSeries_discover_pic(); + + DBG(" <- pSeries_init_early()\n"); +} + + +static void pSeries_progress(char *s, unsigned short hex) +{ + struct device_node *root; + int width, *p; + char *os; + static int display_character, set_indicator; + static int max_width; + static spinlock_t progress_lock = SPIN_LOCK_UNLOCKED; + static int pending_newline = 0; /* did last write end with unprinted newline? */ + + if (!rtas.base) + return; + + if (max_width == 0) { + if ((root = find_path_device("/rtas")) && + (p = (unsigned int *)get_property(root, + "ibm,display-line-length", + NULL))) + max_width = *p; + else + max_width = 0x10; + display_character = rtas_token("display-character"); + set_indicator = rtas_token("set-indicator"); + } + + if (display_character == RTAS_UNKNOWN_SERVICE) { + /* use hex display if available */ + if (set_indicator != RTAS_UNKNOWN_SERVICE) + rtas_call(set_indicator, 3, 1, NULL, 6, 0, hex); + return; + } + + spin_lock(&progress_lock); + + /* + * Last write ended with newline, but we didn't print it since + * it would just clear the bottom line of output. Print it now + * instead. + * + * If no newline is pending, print a CR to start output at the + * beginning of the line. + */ + if (pending_newline) { + rtas_call(display_character, 1, 1, NULL, '\r'); + rtas_call(display_character, 1, 1, NULL, '\n'); + pending_newline = 0; + } else { + rtas_call(display_character, 1, 1, NULL, '\r'); + } + + width = max_width; + os = s; + while (*os) { + if (*os == '\n' || *os == '\r') { + /* Blank to end of line. */ + while (width-- > 0) + rtas_call(display_character, 1, 1, NULL, ' '); + + /* If newline is the last character, save it + * until next call to avoid bumping up the + * display output. + */ + if (*os == '\n' && !os[1]) { + pending_newline = 1; + spin_unlock(&progress_lock); + return; + } + + /* RTAS wants CR-LF, not just LF */ + + if (*os == '\n') { + rtas_call(display_character, 1, 1, NULL, '\r'); + rtas_call(display_character, 1, 1, NULL, '\n'); + } else { + /* CR might be used to re-draw a line, so we'll + * leave it alone and not add LF. + */ + rtas_call(display_character, 1, 1, NULL, *os); + } + + width = max_width; + } else { + width--; + rtas_call(display_character, 1, 1, NULL, *os); + } + + os++; + + /* if we overwrite the screen length */ + if (width <= 0) + while ((*os != 0) && (*os != '\n') && (*os != '\r')) + os++; + } + + /* Blank to end of line. */ + while (width-- > 0) + rtas_call(display_character, 1, 1, NULL, ' '); + + spin_unlock(&progress_lock); +} + +extern void setup_default_decr(void); + +/* Some sane defaults: 125 MHz timebase, 1GHz processor */ +#define DEFAULT_TB_FREQ 125000000UL +#define DEFAULT_PROC_FREQ (DEFAULT_TB_FREQ * 8) + +static void __init pSeries_calibrate_decr(void) +{ + struct device_node *cpu; + struct div_result divres; + unsigned int *fp; + int node_found; + + /* + * The cpu node should have a timebase-frequency property + * to tell us the rate at which the decrementer counts. + */ + cpu = of_find_node_by_type(NULL, "cpu"); + + ppc_tb_freq = DEFAULT_TB_FREQ; /* hardcoded default */ + node_found = 0; + if (cpu != 0) { + fp = (unsigned int *)get_property(cpu, "timebase-frequency", + NULL); + if (fp != 0) { + node_found = 1; + ppc_tb_freq = *fp; + } + } + if (!node_found) + printk(KERN_ERR "WARNING: Estimating decrementer frequency " + "(not found)\n"); + + ppc_proc_freq = DEFAULT_PROC_FREQ; + node_found = 0; + if (cpu != 0) { + fp = (unsigned int *)get_property(cpu, "clock-frequency", + NULL); + if (fp != 0) { + node_found = 1; + ppc_proc_freq = *fp; + } + } + if (!node_found) + printk(KERN_ERR "WARNING: Estimating processor frequency " + "(not found)\n"); + + of_node_put(cpu); + + printk(KERN_INFO "time_init: decrementer frequency = %lu.%.6lu MHz\n", + ppc_tb_freq/1000000, ppc_tb_freq%1000000); + printk(KERN_INFO "time_init: processor frequency = %lu.%.6lu MHz\n", + ppc_proc_freq/1000000, ppc_proc_freq%1000000); + + tb_ticks_per_jiffy = ppc_tb_freq / HZ; + tb_ticks_per_sec = tb_ticks_per_jiffy * HZ; + tb_ticks_per_usec = ppc_tb_freq / 1000000; + tb_to_us = mulhwu_scale_factor(ppc_tb_freq, 1000000); + div128_by_32(1024*1024, 0, tb_ticks_per_sec, &divres); + tb_to_xs = divres.result_low; + + setup_default_decr(); +} + +/* + * Called very early, MMU is off, device-tree isn't unflattened + */ +extern struct machdep_calls pSeries_md; + +static int __init pSeries_probe(int platform) +{ + if (platform != PLATFORM_PSERIES && + platform != PLATFORM_PSERIES_LPAR) + return 0; + + /* if we have some ppc_md fixups for LPAR to do, do + * it here ... + */ + + return 1; +} + +struct machdep_calls __initdata pSeries_md = { + .probe = pSeries_probe, + .setup_arch = pSeries_setup_arch, + .init_early = pSeries_init_early, + .get_cpuinfo = pSeries_get_cpuinfo, + .log_error = pSeries_log_error, + .pcibios_fixup = pSeries_final_fixup, + .restart = rtas_restart, + .power_off = rtas_power_off, + .halt = rtas_halt, + .panic = rtas_os_term, + .get_boot_time = pSeries_get_boot_time, + .get_rtc_time = pSeries_get_rtc_time, + .set_rtc_time = pSeries_set_rtc_time, + .calibrate_decr = pSeries_calibrate_decr, + .progress = pSeries_progress, +}; diff --git a/arch/ppc64/kernel/pci.c b/arch/ppc64/kernel/pci.c index 30e50023c..751071af3 100644 --- a/arch/ppc64/kernel/pci.c +++ b/arch/ppc64/kernel/pci.c @@ -23,6 +23,7 @@ #include #include #include +#include #include #include @@ -41,6 +42,13 @@ unsigned long pci_probe_only = 1; unsigned long pci_assign_all_buses = 0; +/* legal IO pages under MAX_ISA_PORT. This is to ensure we don't touch + devices we don't have access to. */ +unsigned long io_page_mask; + +EXPORT_SYMBOL(io_page_mask); + + unsigned int pcibios_assign_all_busses(void) { return pci_assign_all_buses; @@ -55,16 +63,9 @@ unsigned int pcibios_assign_all_busses(void) unsigned long isa_io_base; /* NULL if no ISA bus */ unsigned long pci_io_base; -void pcibios_name_device(struct pci_dev* dev); -void pcibios_final_fixup(void); -static void fixup_broken_pcnet32(struct pci_dev* dev); -static void fixup_windbond_82c105(struct pci_dev* dev); -extern void fixup_k2_sata(struct pci_dev* dev); - void iSeries_pcibios_init(void); -struct pci_controller *hose_head; -struct pci_controller **hose_tail = &hose_head; +LIST_HEAD(hose_list); struct pci_dma_ops pci_dma_ops; EXPORT_SYMBOL(pci_dma_ops); @@ -74,20 +75,6 @@ int global_phb_number; /* Global phb counter */ /* Cached ISA bridge dev. */ struct pci_dev *ppc64_isabridge_dev = NULL; -struct pci_fixup pcibios_fixups[] = { - { PCI_FIXUP_HEADER, PCI_VENDOR_ID_TRIDENT, PCI_ANY_ID, - fixup_broken_pcnet32 }, - { PCI_FIXUP_HEADER, PCI_VENDOR_ID_WINBOND, PCI_DEVICE_ID_WINBOND_82C105, - fixup_windbond_82c105 }, - { PCI_FIXUP_HEADER, PCI_ANY_ID, PCI_ANY_ID, - pcibios_name_device }, -#ifdef CONFIG_PPC_PMAC - { PCI_FIXUP_HEADER, PCI_VENDOR_ID_SERVERWORKS, 0x0240, - fixup_k2_sata }, -#endif - { 0 } -}; - static void fixup_broken_pcnet32(struct pci_dev* dev) { if ((dev->class>>8 == PCI_CLASS_NETWORK_ETHERNET)) { @@ -96,6 +83,7 @@ static void fixup_broken_pcnet32(struct pci_dev* dev) pci_name_device(dev); } } +DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_TRIDENT, PCI_ANY_ID, fixup_broken_pcnet32); static void fixup_windbond_82c105(struct pci_dev* dev) { @@ -118,6 +106,7 @@ static void fixup_windbond_82c105(struct pci_dev* dev) dev->resource[i].flags &= ~IORESOURCE_IO; } } +DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_WINBOND, PCI_DEVICE_ID_WINBOND_82C105, fixup_windbond_82c105); void pcibios_resource_to_bus(struct pci_dev *dev, struct pci_bus_region *region, @@ -239,9 +228,9 @@ pci_alloc_pci_controller(enum phb_types controller_type) memcpy(hose->what,model,7); hose->type = controller_type; hose->global_number = global_phb_number++; - - *hose_tail = hose; - hose_tail = &hose->next; + + list_add_tail(&hose->list_node, &hose_list); + return hose; } @@ -281,18 +270,17 @@ static void __init pcibios_claim_of_setup(void) static int __init pcibios_init(void) { - struct pci_controller *hose; + struct pci_controller *hose, *tmp; struct pci_bus *bus; #ifdef CONFIG_PPC_ISERIES iSeries_pcibios_init(); #endif - //ppc64_boot_msg(0x40, "PCI Probe"); printk("PCI: Probing PCI hardware\n"); /* Scan all of the recorded PCI controllers. */ - for (hose = hose_head; hose; hose = hose->next) { + list_for_each_entry_safe(hose, tmp, &hose_list, list_node) { hose->last_busno = 0xff; bus = pci_scan_bus(hose->first_busno, hose->ops, hose->arch_data); @@ -308,7 +296,7 @@ static int __init pcibios_init(void) pci_assign_unassigned_resources() is able to work correctly with [partially] allocated PCI tree. */ pci_assign_unassigned_resources(); -#endif +#endif /* !CONFIG_PPC_ISERIES */ /* Call machine dependent final fixup */ if (ppc_md.pcibios_fixup) @@ -320,11 +308,6 @@ static int __init pcibios_init(void) printk("ISA bridge at %s\n", pci_name(ppc64_isabridge_dev)); printk("PCI: Probing PCI hardware done\n"); - //ppc64_boot_msg(0x41, "PCI Done"); - -#ifdef CONFIG_PPC_PSERIES - pci_addr_cache_build(); -#endif return 0; } @@ -524,7 +507,7 @@ int pci_mmap_page_range(struct pci_dev *dev, struct vm_area_struct *vma, return ret; } -#ifdef CONFIG_PPC_PSERIES +#ifdef CONFIG_PPC_MULTIPLATFORM static ssize_t pci_show_devspec(struct device *dev, char *buf) { struct pci_dev *pdev; @@ -537,11 +520,329 @@ static ssize_t pci_show_devspec(struct device *dev, char *buf) return sprintf(buf, "%s", np->full_name); } static DEVICE_ATTR(devspec, S_IRUGO, pci_show_devspec, NULL); -#endif /* CONFIG_PPC_PSERIES */ +#endif /* CONFIG_PPC_MULTIPLATFORM */ void pcibios_add_platform_entries(struct pci_dev *pdev) { -#ifdef CONFIG_PPC_PSERIES +#ifdef CONFIG_PPC_MULTIPLATFORM device_create_file(&pdev->dev, &dev_attr_devspec); -#endif /* CONFIG_PPC_PSERIES */ +#endif /* CONFIG_PPC_MULTIPLATFORM */ +} + +#ifdef CONFIG_PPC_MULTIPLATFORM + +#define ISA_SPACE_MASK 0x1 +#define ISA_SPACE_IO 0x1 + +static void pci_process_ISA_OF_ranges(struct device_node *isa_node, + unsigned long phb_io_base_phys, + void * phb_io_base_virt) +{ + struct isa_range *range; + unsigned long pci_addr; + unsigned int isa_addr; + unsigned int size; + int rlen = 0; + + range = (struct isa_range *) get_property(isa_node, "ranges", &rlen); + if (rlen < sizeof(struct isa_range)) { + printk(KERN_ERR "unexpected isa range size: %s\n", + __FUNCTION__); + return; + } + + /* From "ISA Binding to 1275" + * The ranges property is laid out as an array of elements, + * each of which comprises: + * cells 0 - 1: an ISA address + * cells 2 - 4: a PCI address + * (size depending on dev->n_addr_cells) + * cell 5: the size of the range + */ + if ((range->isa_addr.a_hi && ISA_SPACE_MASK) == ISA_SPACE_IO) { + isa_addr = range->isa_addr.a_lo; + pci_addr = (unsigned long) range->pci_addr.a_mid << 32 | + range->pci_addr.a_lo; + + /* Assume these are both zero */ + if ((pci_addr != 0) || (isa_addr != 0)) { + printk(KERN_ERR "unexpected isa to pci mapping: %s\n", + __FUNCTION__); + return; + } + + size = PAGE_ALIGN(range->size); + + __ioremap_explicit(phb_io_base_phys, + (unsigned long) phb_io_base_virt, + size, _PAGE_NO_CACHE); + } +} + +void __init pci_process_bridge_OF_ranges(struct pci_controller *hose, + struct device_node *dev, int primary) +{ + unsigned int *ranges; + unsigned long size; + int rlen = 0; + int memno = 0; + struct resource *res; + int np, na = prom_n_addr_cells(dev); + unsigned long pci_addr, cpu_phys_addr; + struct device_node *isa_dn; + + np = na + 5; + + /* From "PCI Binding to 1275" + * The ranges property is laid out as an array of elements, + * each of which comprises: + * cells 0 - 2: a PCI address + * cells 3 or 3+4: a CPU physical address + * (size depending on dev->n_addr_cells) + * cells 4+5 or 5+6: the size of the range + */ + rlen = 0; + hose->io_base_phys = 0; + ranges = (unsigned int *) get_property(dev, "ranges", &rlen); + while ((rlen -= np * sizeof(unsigned int)) >= 0) { + res = NULL; + pci_addr = (unsigned long)ranges[1] << 32 | ranges[2]; + + cpu_phys_addr = ranges[3]; + if (na == 2) + cpu_phys_addr = cpu_phys_addr << 32 | ranges[4]; + + size = (unsigned long)ranges[na+3] << 32 | ranges[na+4]; + + switch (ranges[0] >> 24) { + case 1: /* I/O space */ + hose->io_base_phys = cpu_phys_addr; + hose->io_base_virt = reserve_phb_iospace(size); + PPCDBG(PPCDBG_PHBINIT, + "phb%d io_base_phys 0x%lx io_base_virt 0x%lx\n", + hose->global_number, hose->io_base_phys, + (unsigned long) hose->io_base_virt); + + if (primary) { + pci_io_base = (unsigned long)hose->io_base_virt; + isa_dn = of_find_node_by_type(NULL, "isa"); + if (isa_dn) { + isa_io_base = pci_io_base; + pci_process_ISA_OF_ranges(isa_dn, + hose->io_base_phys, + hose->io_base_virt); + of_node_put(isa_dn); + /* Allow all IO */ + io_page_mask = -1; + } + } + + res = &hose->io_resource; + res->flags = IORESOURCE_IO; + res->start = pci_addr; + res->start += (unsigned long)hose->io_base_virt - + pci_io_base; + break; + case 2: /* memory space */ + memno = 0; + while (memno < 3 && hose->mem_resources[memno].flags) + ++memno; + + if (memno == 0) + hose->pci_mem_offset = cpu_phys_addr - pci_addr; + if (memno < 3) { + res = &hose->mem_resources[memno]; + res->flags = IORESOURCE_MEM; + res->start = cpu_phys_addr; + } + break; + } + if (res != NULL) { + res->name = dev->full_name; + res->end = res->start + size - 1; + res->parent = NULL; + res->sibling = NULL; + res->child = NULL; + } + ranges += np; + } +} + +/*********************************************************************** + * pci_find_hose_for_OF_device + * + * This function finds the PHB that matching device_node in the + * OpenFirmware by scanning all the pci_controllers. + * + ***********************************************************************/ +struct pci_controller* pci_find_hose_for_OF_device(struct device_node *node) +{ + while (node) { + struct pci_controller *hose, *tmp; + list_for_each_entry_safe(hose, tmp, &hose_list, list_node) + if (hose->arch_data == node) + return hose; + node=node->parent; + } + return NULL; +} + +/* + * ppc64 can have multifunction devices that do not respond to function 0. + * In this case we must scan all functions. + */ +int pcibios_scan_all_fns(struct pci_bus *bus, int devfn) +{ + struct device_node *busdn, *dn; + + if (bus->self) + busdn = pci_device_to_OF_node(bus->self); + else + busdn = bus->sysdata; /* must be a phb */ + + /* + * Check to see if there is any of the 8 functions are in the + * device tree. If they are then we need to scan all the + * functions of this slot. + */ + for (dn = busdn->child; dn; dn = dn->sibling) + if ((dn->devfn >> 3) == (devfn >> 3)) + return 1; + + return 0; +} + + +void __devinit pcibios_fixup_device_resources(struct pci_dev *dev, + struct pci_bus *bus) +{ + /* Update device resources. */ + struct pci_controller *hose = PCI_GET_PHB_PTR(bus); + int i; + + for (i = 0; i < PCI_NUM_RESOURCES; i++) { + if (dev->resource[i].flags & IORESOURCE_IO) { + unsigned long offset = (unsigned long)hose->io_base_virt - pci_io_base; + unsigned long start, end, mask; + + start = dev->resource[i].start += offset; + end = dev->resource[i].end += offset; + + /* Need to allow IO access to pages that are in the + ISA range */ + if (start < MAX_ISA_PORT) { + if (end > MAX_ISA_PORT) + end = MAX_ISA_PORT; + + start >>= PAGE_SHIFT; + end >>= PAGE_SHIFT; + + /* get the range of pages for the map */ + mask = ((1 << (end+1))-1) ^ ((1 << start)-1); + io_page_mask |= mask; + } + } + else if (dev->resource[i].flags & IORESOURCE_MEM) { + dev->resource[i].start += hose->pci_mem_offset; + dev->resource[i].end += hose->pci_mem_offset; + } + } +} +EXPORT_SYMBOL(pcibios_fixup_device_resources); + +void __devinit pcibios_fixup_bus(struct pci_bus *bus) +{ + struct pci_controller *hose = PCI_GET_PHB_PTR(bus); + struct list_head *ln; + + /* XXX or bus->parent? */ + struct pci_dev *dev = bus->self; + struct resource *res; + int i; + + if (!dev) { + /* Root bus. */ + + hose->bus = bus; + bus->resource[0] = res = &hose->io_resource; + if (!res->flags) + BUG(); /* No I/O resource for this PHB? */ + + if (request_resource(&ioport_resource, res)) + printk(KERN_ERR "Failed to request IO on " + "PCI domain %d\n", pci_domain_nr(bus)); + + + for (i = 0; i < 3; ++i) { + res = &hose->mem_resources[i]; + if (!res->flags && i == 0) + BUG(); /* No memory resource for this PHB? */ + bus->resource[i+1] = res; + if (res->flags && request_resource(&iomem_resource, res)) + printk(KERN_ERR "Failed to request MEM on " + "PCI domain %d\n", + pci_domain_nr(bus)); + } + } else if (pci_probe_only && + (dev->class >> 8) == PCI_CLASS_BRIDGE_PCI) { + /* This is a subordinate bridge */ + + pci_read_bridge_bases(bus); + pcibios_fixup_device_resources(dev, bus); + } + + /* XXX Need to check why Alpha doesnt do this - Anton */ + if (!pci_probe_only) + return; + + for (ln = bus->devices.next; ln != &bus->devices; ln = ln->next) { + struct pci_dev *dev = pci_dev_b(ln); + if ((dev->class >> 8) != PCI_CLASS_BRIDGE_PCI) + pcibios_fixup_device_resources(dev, bus); + } +} +EXPORT_SYMBOL(pcibios_fixup_bus); + +/****************************************************************** + * pci_read_irq_line + * + * Reads the Interrupt Pin to determine if interrupt is use by card. + * If the interrupt is used, then gets the interrupt line from the + * openfirmware and sets it in the pci_dev and pci_config line. + * + ******************************************************************/ +int pci_read_irq_line(struct pci_dev *pci_dev) +{ + u8 intpin; + struct device_node *node; + + pci_read_config_byte(pci_dev, PCI_INTERRUPT_PIN, &intpin); + + if (intpin == 0) { + PPCDBG(PPCDBG_BUSWALK,"\tDevice: %s No Interrupt used by device.\n", + pci_name(pci_dev)); + return 0; + } + + node = pci_device_to_OF_node(pci_dev); + if (node == NULL) { + PPCDBG(PPCDBG_BUSWALK,"\tDevice: %s Device Node not found.\n", + pci_name(pci_dev)); + return -1; + } + if (node->n_intrs == 0) { + PPCDBG(PPCDBG_BUSWALK,"\tDevice: %s No Device OF interrupts defined.\n", + pci_name(pci_dev)); + return -1; + } + pci_dev->irq = node->intrs[0].line; + + pci_write_config_byte(pci_dev, PCI_INTERRUPT_LINE, pci_dev->irq); + + PPCDBG(PPCDBG_BUSWALK,"\tDevice: %s pci_dev->irq = 0x%02X\n", + pci_name(pci_dev), pci_dev->irq); + return 0; } +EXPORT_SYMBOL(pci_read_irq_line); + +#endif /* CONFIG_PPC_MULTIPLATFORM */ diff --git a/arch/ppc64/kernel/pci_dn.c b/arch/ppc64/kernel/pci_dn.c index c771bdc64..2ef21d7da 100644 --- a/arch/ppc64/kernel/pci_dn.c +++ b/arch/ppc64/kernel/pci_dn.c @@ -129,10 +129,10 @@ void *traverse_pci_devices(struct device_node *start, traverse_func pre, */ static void *traverse_all_pci_devices(traverse_func pre) { - struct pci_controller *phb; + struct pci_controller *phb, *tmp; void *ret; - for (phb = hose_head; phb; phb = phb->next) + list_for_each_entry_safe(phb, tmp, &hose_list, list_node) if ((ret = traverse_pci_devices(phb->arch_data, pre, phb)) != NULL) return ret; diff --git a/arch/ppc64/kernel/pci_iommu.c b/arch/ppc64/kernel/pci_iommu.c index 4d99851fe..951831f99 100644 --- a/arch/ppc64/kernel/pci_iommu.c +++ b/arch/ppc64/kernel/pci_iommu.c @@ -54,9 +54,9 @@ static inline struct iommu_table *devnode_table(struct pci_dev *dev) return ISERIES_DEVNODE(dev)->iommu_table; #endif /* CONFIG_PPC_ISERIES */ -#ifdef CONFIG_PPC_PSERIES +#ifdef CONFIG_PPC_MULTIPLATFORM return PCI_GET_DN(dev)->iommu_table; -#endif /* CONFIG_PPC_PSERIES */ +#endif /* CONFIG_PPC_MULTIPLATFORM */ } diff --git a/arch/ppc64/kernel/pmac_feature.c b/arch/ppc64/kernel/pmac_feature.c index c059a7608..c45ecbdb2 100644 --- a/arch/ppc64/kernel/pmac_feature.c +++ b/arch/ppc64/kernel/pmac_feature.c @@ -44,9 +44,9 @@ #undef DEBUG_FEATURE #ifdef DEBUG_FEATURE -#define DBG(fmt,...) printk(KERN_DEBUG fmt) +#define DBG(fmt...) printk(KERN_DEBUG fmt) #else -#define DBG(fmt,...) +#define DBG(fmt...) #endif /* @@ -343,6 +343,10 @@ static struct pmac_mb_def pmac_mb_defs[] __pmacdata = { PMAC_TYPE_POWERMAC_G5, g5_features, 0, }, + { "PowerMac7,3", "PowerMac G5", + PMAC_TYPE_POWERMAC_G5, g5_features, + 0, + }, { "RackMac3,1", "XServe G5", PMAC_TYPE_POWERMAC_G5, g5_features, 0, @@ -611,7 +615,7 @@ int __init pmac_feature_late_init(void) device_initcall(pmac_feature_late_init); - +#if 0 static void dump_HT_speeds(char *name, u32 cfg, u32 frq) { int freqs[16] = { 200,300,400,500,600,800,1000,0,0,0,0,0,0,0,0,0 }; @@ -625,6 +629,7 @@ static void dump_HT_speeds(char *name, u32 cfg, u32 frq) name, freqs[freq], bits[(cfg >> 28) & 0x7], bits[(cfg >> 24) & 0x7]); } +#endif void __init pmac_check_ht_link(void) { diff --git a/arch/ppc64/kernel/pmac_low_i2c.c b/arch/ppc64/kernel/pmac_low_i2c.c index d07579f2b..52da75719 100644 --- a/arch/ppc64/kernel/pmac_low_i2c.c +++ b/arch/ppc64/kernel/pmac_low_i2c.c @@ -50,7 +50,7 @@ struct low_i2c_host struct device_node *np; /* OF device node */ struct semaphore mutex; /* Access mutex for use by i2c-keywest */ low_i2c_func_t func; /* Access function */ - int is_open : 1; /* Poor man's access control */ + unsigned is_open : 1; /* Poor man's access control */ int mode; /* Current mode */ int channel; /* Current channel */ int num_channels; /* Number of channels */ diff --git a/arch/ppc64/kernel/pmac_pci.c b/arch/ppc64/kernel/pmac_pci.c index 169195713..9e1a06955 100644 --- a/arch/ppc64/kernel/pmac_pci.c +++ b/arch/ppc64/kernel/pmac_pci.c @@ -271,7 +271,7 @@ static int __pmac u3_ht_read_config(struct pci_bus *bus, unsigned int devfn, int offset, int len, u32 *val) { struct pci_controller *hose; - struct device_node *busdn; + struct device_node *busdn, *dn; unsigned long addr; if (bus->self) @@ -284,6 +284,16 @@ static int __pmac u3_ht_read_config(struct pci_bus *bus, unsigned int devfn, if (hose == NULL) return PCIBIOS_DEVICE_NOT_FOUND; + /* We only allow config cycles to devices that are in OF device-tree + * as we are apparently having some weird things going on with some + * revs of K2 on recent G5s + */ + for (dn = busdn->child; dn; dn = dn->sibling) + if (dn->devfn == devfn) + break; + if (dn == NULL) + return PCIBIOS_DEVICE_NOT_FOUND; + addr = u3_ht_cfg_access(hose, bus->number, devfn, offset); if (!addr) return PCIBIOS_DEVICE_NOT_FOUND; @@ -419,7 +429,7 @@ static void __init setup_u3_ht(struct pci_controller* hose) * properties or figuring out the U3 address space decoding logic and * then read it's configuration register (if any). */ - hose->io_base_phys = 0xf4000000 + 0x00400000; + hose->io_base_phys = 0xf4000000; hose->io_base_virt = ioremap(hose->io_base_phys, 0x00400000); isa_io_base = pci_io_base = (unsigned long) hose->io_base_virt; hose->io_resource.name = np->full_name; @@ -664,17 +674,15 @@ void __init pmac_pcibios_fixup(void) pci_fix_bus_sysdata(); -#ifdef CONFIG_PMAC_DART - iommu_setup_pmac(); -#endif /* CONFIG_PMAC_DART */ + iommu_setup_u3(); } static void __init pmac_fixup_phb_resources(void) { - struct pci_controller *hose; + struct pci_controller *hose, *tmp; - for (hose = hose_head; hose; hose = hose->next) { + list_for_each_entry_safe(hose, tmp, &hose_list, list_node) { unsigned long offset = (unsigned long)hose->io_base_virt - pci_io_base; hose->io_resource.start += offset; hose->io_resource.end += offset; @@ -746,6 +754,9 @@ void __init pmac_pci_init(void) * the exception of U3/AGP (hook into pci_set_mwi) */ pci_cache_line_size = 16; /* 64 bytes */ + + /* Allow all IO */ + io_page_mask = -1; } /* @@ -777,3 +788,4 @@ void fixup_k2_sata(struct pci_dev* dev) } } } +DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_SERVERWORKS, 0x0240, fixup_k2_sata); diff --git a/arch/ppc64/kernel/pmac_setup.c b/arch/ppc64/kernel/pmac_setup.c index 58e8b392e..3d1de0f9b 100644 --- a/arch/ppc64/kernel/pmac_setup.c +++ b/arch/ppc64/kernel/pmac_setup.c @@ -23,6 +23,8 @@ * bootup setup stuff.. */ +#undef DEBUG + #include #include #include @@ -63,7 +65,6 @@ #include #include #include -#include #include #include #include @@ -73,6 +74,13 @@ #include "pmac.h" +#ifdef DEBUG +#define DBG(fmt...) udbg_printf(fmt) +#else +#define DBG(fmt...) +#endif + + static int current_root_goodness = -1; #define DEFAULT_ROOT_DEVICE Root_SDA1 /* sda1 - slightly silly choice */ @@ -81,10 +89,6 @@ int sccdbg; extern void udbg_init_scc(struct device_node *np); -#ifdef CONFIG_BOOTX_TEXT -void pmac_progress(char *s, unsigned short hex); -#endif - void __pmac pmac_show_cpuinfo(struct seq_file *m) { struct device_node *np; @@ -134,28 +138,20 @@ void __pmac pmac_show_cpuinfo(struct seq_file *m) void __init pmac_setup_arch(void) { - struct device_node *cpu; - int *fp; - unsigned long pvr; - - pvr = PVR_VER(mfspr(PVR)); - - /* Set loops_per_jiffy to a half-way reasonable value, - for use until calibrate_delay gets called. */ - cpu = find_type_devices("cpu"); - if (cpu != 0) { - fp = (int *) get_property(cpu, "clock-frequency", NULL); - if (fp != 0) { - if (pvr == 4 || pvr >= 8) - /* 604, G3, G4 etc. */ - loops_per_jiffy = *fp / HZ; - else - /* 601, 603, etc. */ - loops_per_jiffy = *fp / (2*HZ); - } else - loops_per_jiffy = 50000000 / HZ; + /* init to some ~sane value until calibrate_delay() runs */ + loops_per_jiffy = 50000000; + + /* Probe motherboard chipset */ + pmac_feature_init(); +#if 0 + /* Lock-enable the SCC channel used for debug */ + if (sccdbg) { + np = of_find_node_by_name(NULL, "escc"); + if (np) + pmac_call_feature(PMAC_FTR_SCC_ENABLE, np, + PMAC_SCC_ASYNC | PMAC_SCC_FLAG_XMON, 1); } - +#endif /* We can NAP */ powersave_nap = 1; @@ -183,67 +179,13 @@ void __init pmac_setup_arch(void) #endif } -extern char *bootpath; -extern char *bootdevice; -void *boot_host; -int boot_target; -int boot_part; -extern dev_t boot_dev; - #ifdef CONFIG_SCSI -void __init note_scsi_host(struct device_node *node, void *host) +void note_scsi_host(struct device_node *node, void *host) { - int l; - char *p; - - l = strlen(node->full_name); - if (bootpath != NULL && bootdevice != NULL - && strncmp(node->full_name, bootdevice, l) == 0 - && (bootdevice[l] == '/' || bootdevice[l] == 0)) { - boot_host = host; - /* - * There's a bug in OF 1.0.5. (Why am I not surprised.) - * If you pass a path like scsi/sd@1:0 to canon, it returns - * something like /bandit@F2000000/gc@10/53c94@10000/sd@0,0 - * That is, the scsi target number doesn't get preserved. - * So we pick the target number out of bootpath and use that. - */ - p = strstr(bootpath, "/sd@"); - if (p != NULL) { - p += 4; - boot_target = simple_strtoul(p, NULL, 10); - p = strchr(p, ':'); - if (p != NULL) - boot_part = simple_strtoul(p + 1, NULL, 10); - } - } + /* Obsolete */ } #endif -#if defined(CONFIG_BLK_DEV_IDE) && defined(CONFIG_BLK_DEV_IDE_PMAC) -static dev_t __init find_ide_boot(void) -{ - char *p; - int n; - dev_t __init pmac_find_ide_boot(char *bootdevice, int n); - - if (bootdevice == NULL) - return 0; - p = strrchr(bootdevice, '/'); - if (p == NULL) - return 0; - n = p - bootdevice; - - return pmac_find_ide_boot(bootdevice, n); -} -#endif /* CONFIG_BLK_DEV_IDE && CONFIG_BLK_DEV_IDE_PMAC */ - -void __init find_boot_device(void) -{ -#if defined(CONFIG_BLK_DEV_IDE) && defined(CONFIG_BLK_DEV_IDE_PMAC) - boot_dev = find_ide_boot(); -#endif -} static int initializing = 1; @@ -258,7 +200,7 @@ late_initcall(pmac_late_init); /* can't be __init - can be called whenever a disk is first accessed */ void __pmac note_bootable_part(dev_t dev, int part, int goodness) { - static int found_boot = 0; + extern dev_t boot_dev; char *p; if (!initializing) @@ -270,10 +212,6 @@ void __pmac note_bootable_part(dev_t dev, int part, int goodness) if (p != NULL && (p == saved_command_line || p[-1] == ' ')) return; - if (!found_boot) { - find_boot_device(); - found_boot = 1; - } if (!boot_dev || dev == boot_dev) { ROOT_DEV = dev + part; boot_dev = 0; @@ -313,21 +251,73 @@ static void btext_putc(unsigned char c) } #endif /* CONFIG_BOOTX_TEXT */ +static void __init init_boot_display(void) +{ + char *name; + struct device_node *np = NULL; + int rc = -ENODEV; + + printk("trying to initialize btext ...\n"); + + name = (char *)get_property(of_chosen, "linux,stdout-path", NULL); + if (name != NULL) { + np = of_find_node_by_path(name); + if (np != NULL) { + if (strcmp(np->type, "display") != 0) { + printk("boot stdout isn't a display !\n"); + of_node_put(np); + np = NULL; + } + } + } + if (np) + rc = btext_initialize(np); + if (rc == 0) + return; + + for (np = NULL; (np = of_find_node_by_type(np, "display"));) { + if (get_property(np, "linux,opened", NULL)) { + printk("trying %s ...\n", np->full_name); + rc = btext_initialize(np); + printk("result: %d\n", rc); + } + if (rc == 0) + return; + } +} + /* * Early initialization. - * Relocation is on but do not reference unbolted pages - * Also, device-tree hasn't been "finished", so don't muck with - * it too much */ void __init pmac_init_early(void) { - hpte_init_pSeries(); + DBG(" -> pmac_init_early\n"); + + /* Initialize hash table, from now on, we can take hash faults + * and call ioremap + */ + hpte_init_native(); + /* Init SCC */ + if (strstr(cmd_line, "sccdbg")) { + sccdbg = 1; + udbg_init_scc(NULL); + } + + else { #ifdef CONFIG_BOOTX_TEXT - ppc_md.udbg_putc = btext_putc; - ppc_md.udbg_getc = dummy_getc; - ppc_md.udbg_getc_poll = dummy_getc_poll; + init_boot_display(); + + ppc_md.udbg_putc = btext_putc; + ppc_md.udbg_getc = dummy_getc; + ppc_md.udbg_getc_poll = dummy_getc_poll; #endif /* CONFIG_BOOTX_TEXT */ + } + + /* Setup interrupt mapping options */ + naca->interrupt_controller = IC_OPEN_PIC; + + DBG(" <- pmac_init_early\n"); } extern void* OpenPIC_Addr; @@ -417,60 +407,19 @@ static int __init pmac_irq_cascade_init(void) core_initcall(pmac_irq_cascade_init); -void __init pmac_init(unsigned long r3, unsigned long r4, unsigned long r5, - unsigned long r6, unsigned long r7) -{ - /* Probe motherboard chipset */ - pmac_feature_init(); - - /* Init SCC */ - if (strstr(cmd_line, "sccdbg")) { - sccdbg = 1; - udbg_init_scc(NULL); - } - - /* Fill up the machine description */ - ppc_md.setup_arch = pmac_setup_arch; - ppc_md.get_cpuinfo = pmac_show_cpuinfo; - - ppc_md.init_IRQ = pmac_init_IRQ; - ppc_md.get_irq = openpic_get_irq; - - ppc_md.pcibios_fixup = pmac_pcibios_fixup; - - ppc_md.restart = pmac_restart; - ppc_md.power_off = pmac_power_off; - ppc_md.halt = pmac_halt; - - ppc_md.get_boot_time = pmac_get_boot_time; - ppc_md.set_rtc_time = pmac_set_rtc_time; - ppc_md.get_rtc_time = pmac_get_rtc_time; - ppc_md.calibrate_decr = pmac_calibrate_decr; - - ppc_md.feature_call = pmac_do_feature_call; - - -#ifdef CONFIG_BOOTX_TEXT - ppc_md.progress = pmac_progress; -#endif /* CONFIG_BOOTX_TEXT */ - - if (ppc_md.progress) ppc_md.progress("pmac_init(): exit", 0); - -} - -#ifdef CONFIG_BOOTX_TEXT -void __init pmac_progress(char *s, unsigned short hex) +static void __init pmac_progress(char *s, unsigned short hex) { if (sccdbg) { udbg_puts(s); - udbg_putc('\n'); + udbg_puts("\n"); } +#ifdef CONFIG_BOOTX_TEXT else if (boot_text_mapped) { btext_drawstring(s); - btext_drawchar('\n'); + btext_drawstring("\n"); } -} #endif /* CONFIG_BOOTX_TEXT */ +} static int __init pmac_declare_of_platform_devices(void) { @@ -489,3 +438,41 @@ static int __init pmac_declare_of_platform_devices(void) } device_initcall(pmac_declare_of_platform_devices); + +/* + * Called very early, MMU is off, device-tree isn't unflattened + */ +static int __init pmac_probe(int platform) +{ + if (platform != PLATFORM_POWERMAC) + return 0; + + /* + * On U3, the DART (iommu) must be allocated now since it + * has an impact on htab_initialize (due to the large page it + * occupies having to be broken up so the DART itself is not + * part of the cacheable linar mapping + */ + alloc_u3_dart_table(); + + return 1; +} + +struct machdep_calls __initdata pmac_md = { + .probe = pmac_probe, + .setup_arch = pmac_setup_arch, + .init_early = pmac_init_early, + .get_cpuinfo = pmac_show_cpuinfo, + .init_IRQ = pmac_init_IRQ, + .get_irq = openpic_get_irq, + .pcibios_fixup = pmac_pcibios_fixup, + .restart = pmac_restart, + .power_off = pmac_power_off, + .halt = pmac_halt, + .get_boot_time = pmac_get_boot_time, + .set_rtc_time = pmac_set_rtc_time, + .get_rtc_time = pmac_get_rtc_time, + .calibrate_decr = pmac_calibrate_decr, + .feature_call = pmac_do_feature_call, + .progress = pmac_progress, +}; diff --git a/arch/ppc64/kernel/pmac_time.c b/arch/ppc64/kernel/pmac_time.c index aea28b989..06f379855 100644 --- a/arch/ppc64/kernel/pmac_time.c +++ b/arch/ppc64/kernel/pmac_time.c @@ -18,6 +18,7 @@ #include #include #include +#include #include #include @@ -25,7 +26,6 @@ #include #include #include -#include #include #include diff --git a/arch/ppc64/kernel/prom_init.c b/arch/ppc64/kernel/prom_init.c new file mode 100644 index 000000000..f62bda79e --- /dev/null +++ b/arch/ppc64/kernel/prom_init.c @@ -0,0 +1,1714 @@ +/* + * + * + * Procedures for interfacing to Open Firmware. + * + * Paul Mackerras August 1996. + * Copyright (C) 1996 Paul Mackerras. + * + * Adapted for 64bit PowerPC by Dave Engebretsen and Peter Bergner. + * {engebret|bergner}@us.ibm.com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#undef DEBUG_PROM + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "open_pic.h" + +#ifdef CONFIG_LOGO_LINUX_CLUT224 +#include +extern const struct linux_logo logo_linux_clut224; +#endif + +/* + * Properties whose value is longer than this get excluded from our + * copy of the device tree. This value does need to be big enough to + * ensure that we don't lose things like the interrupt-map property + * on a PCI-PCI bridge. + */ +#define MAX_PROPERTY_LENGTH (1UL * 1024 * 1024) + +/* + * Eventually bump that one up + */ +#define DEVTREE_CHUNK_SIZE 0x100000 + +/* + * This is the size of the local memory reserve map that gets copied + * into the boot params passed to the kernel. That size is totally + * flexible as the kernel just reads the list until it encounters an + * entry with size 0, so it can be changed without breaking binary + * compatibility + */ +#define MEM_RESERVE_MAP_SIZE 8 + +/* + * prom_init() is called very early on, before the kernel text + * and data have been mapped to KERNELBASE. At this point the code + * is running at whatever address it has been loaded at, so + * references to extern and static variables must be relocated + * explicitly. The procedure reloc_offset() returns the address + * we're currently running at minus the address we were linked at. + * (Note that strings count as static variables.) + * + * Because OF may have mapped I/O devices into the area starting at + * KERNELBASE, particularly on CHRP machines, we can't safely call + * OF once the kernel has been mapped to KERNELBASE. Therefore all + * OF calls should be done within prom_init(), and prom_init() + * and all routines called within it must be careful to relocate + * references as necessary. + * + * Note that the bss is cleared *after* prom_init runs, so we have + * to make sure that any static or extern variables it accesses + * are put in the data segment. + */ + + +#define PROM_BUG() do { \ + prom_printf("kernel BUG at %s line 0x%x!\n", \ + RELOC(__FILE__), __LINE__); \ + __asm__ __volatile__(".long " BUG_ILLEGAL_INSTR); \ +} while (0) + +#ifdef DEBUG_PROM +#define prom_debug(x...) prom_printf(x) +#else +#define prom_debug(x...) +#endif + + +typedef u32 prom_arg_t; + +struct prom_args { + u32 service; + u32 nargs; + u32 nret; + prom_arg_t args[10]; + prom_arg_t *rets; /* Pointer to return values in args[16]. */ +}; + +struct prom_t { + unsigned long entry; + ihandle root; + ihandle chosen; + int cpu; + ihandle stdout; + ihandle disp_node; + struct prom_args args; + unsigned long version; + unsigned long root_size_cells; + unsigned long root_addr_cells; +}; + +struct pci_reg_property { + struct pci_address addr; + u32 size_hi; + u32 size_lo; +}; + +struct mem_map_entry { + u64 base; + u64 size; +}; + +typedef u32 cell_t; + +extern void __start(unsigned long r3, unsigned long r4, unsigned long r5); + +extern unsigned long reloc_offset(void); +extern void enter_prom(struct prom_args *args, unsigned long entry); +extern void copy_and_flush(unsigned long dest, unsigned long src, + unsigned long size, unsigned long offset); + +extern unsigned long klimit; + +//int global_width = 640, global_height = 480, global_depth = 8, global_pitch; +//unsigned global_address; +/* prom structure */ +static struct prom_t __initdata prom; + +#define PROM_SCRATCH_SIZE 256 + +static char __initdata of_stdout_device[256]; +static char __initdata prom_scratch[PROM_SCRATCH_SIZE]; + +static unsigned long __initdata dt_header_start; +static unsigned long __initdata dt_struct_start, dt_struct_end; +static unsigned long __initdata dt_string_start, dt_string_end; + +static unsigned long __initdata prom_initrd_start, prom_initrd_end; + +static int __initdata iommu_force_on; +static int __initdata ppc64_iommu_off; +static int __initdata of_platform; + +static char __initdata prom_cmd_line[COMMAND_LINE_SIZE]; + +static unsigned long __initdata alloc_top; +static unsigned long __initdata alloc_top_high; +static unsigned long __initdata alloc_bottom; +static unsigned long __initdata rmo_top; +static unsigned long __initdata ram_top; + +static struct mem_map_entry __initdata mem_reserve_map[MEM_RESERVE_MAP_SIZE]; +static int __initdata mem_reserve_cnt; + +static cell_t __initdata regbuf[1024]; + + +#define MAX_CPU_THREADS 2 + +/* TO GO */ +#ifdef CONFIG_HMT +struct { + unsigned int pir; + unsigned int threadid; +} hmt_thread_data[NR_CPUS]; +#endif /* CONFIG_HMT */ + +/* + * This are used in calls to call_prom. The 4th and following + * arguments to call_prom should be 32-bit values. 64 bit values + * are truncated to 32 bits (and fortunately don't get interpreted + * as two arguments). + */ +#define ADDR(x) (u32) ((unsigned long)(x) - offset) + +/* This is the one and *ONLY* place where we actually call open + * firmware from, since we need to make sure we're running in 32b + * mode when we do. We switch back to 64b mode upon return. + */ + +#define PROM_ERROR (-1) + +static int __init call_prom(const char *service, int nargs, int nret, ...) +{ + int i; + unsigned long offset = reloc_offset(); + struct prom_t *_prom = PTRRELOC(&prom); + va_list list; + + _prom->args.service = ADDR(service); + _prom->args.nargs = nargs; + _prom->args.nret = nret; + _prom->args.rets = (prom_arg_t *)&(_prom->args.args[nargs]); + + va_start(list, nret); + for (i=0; i < nargs; i++) + _prom->args.args[i] = va_arg(list, prom_arg_t); + va_end(list); + + for (i=0; i < nret ;i++) + _prom->args.rets[i] = 0; + + enter_prom(&_prom->args, _prom->entry); + + return (nret > 0) ? _prom->args.rets[0] : 0; +} + + +static unsigned int __init prom_claim(unsigned long virt, unsigned long size, + unsigned long align) +{ + return (unsigned int)call_prom("claim", 3, 1, + (prom_arg_t)virt, (prom_arg_t)size, + (prom_arg_t)align); +} + +static void __init prom_print(const char *msg) +{ + const char *p, *q; + unsigned long offset = reloc_offset(); + struct prom_t *_prom = PTRRELOC(&prom); + + if (_prom->stdout == 0) + return; + + for (p = msg; *p != 0; p = q) { + for (q = p; *q != 0 && *q != '\n'; ++q) + ; + if (q > p) + call_prom("write", 3, 1, _prom->stdout, p, q - p); + if (*q == 0) + break; + ++q; + call_prom("write", 3, 1, _prom->stdout, ADDR("\r\n"), 2); + } +} + + +static void __init prom_print_hex(unsigned long val) +{ + unsigned long offset = reloc_offset(); + int i, nibbles = sizeof(val)*2; + char buf[sizeof(val)*2+1]; + struct prom_t *_prom = PTRRELOC(&prom); + + for (i = nibbles-1; i >= 0; i--) { + buf[i] = (val & 0xf) + '0'; + if (buf[i] > '9') + buf[i] += ('a'-'0'-10); + val >>= 4; + } + buf[nibbles] = '\0'; + call_prom("write", 3, 1, _prom->stdout, buf, nibbles); +} + + +static void __init prom_printf(const char *format, ...) +{ + unsigned long offset = reloc_offset(); + const char *p, *q, *s; + va_list args; + unsigned long v; + struct prom_t *_prom = PTRRELOC(&prom); + + va_start(args, format); + for (p = PTRRELOC(format); *p != 0; p = q) { + for (q = p; *q != 0 && *q != '\n' && *q != '%'; ++q) + ; + if (q > p) + call_prom("write", 3, 1, _prom->stdout, p, q - p); + if (*q == 0) + break; + if (*q == '\n') { + ++q; + call_prom("write", 3, 1, _prom->stdout, + ADDR("\r\n"), 2); + continue; + } + ++q; + if (*q == 0) + break; + switch (*q) { + case 's': + ++q; + s = va_arg(args, const char *); + prom_print(s); + break; + case 'x': + ++q; + v = va_arg(args, unsigned long); + prom_print_hex(v); + break; + } + } +} + + +static void __init __attribute__((noreturn)) prom_panic(const char *reason) +{ + unsigned long offset = reloc_offset(); + + prom_print(PTRRELOC(reason)); + /* ToDo: should put up an SRC here */ + call_prom("exit", 0, 0); + + for (;;) /* should never get here */ + ; +} + + +static int __init prom_next_node(phandle *nodep) +{ + phandle node; + + if ((node = *nodep) != 0 + && (*nodep = call_prom("child", 1, 1, node)) != 0) + return 1; + if ((*nodep = call_prom("peer", 1, 1, node)) != 0) + return 1; + for (;;) { + if ((node = call_prom("parent", 1, 1, node)) == 0) + return 0; + if ((*nodep = call_prom("peer", 1, 1, node)) != 0) + return 1; + } +} + +static int __init prom_getprop(phandle node, const char *pname, + void *value, size_t valuelen) +{ + unsigned long offset = reloc_offset(); + + return call_prom("getprop", 4, 1, node, ADDR(pname), + (u32)(unsigned long) value, (u32) valuelen); +} + +static int __init prom_getproplen(phandle node, const char *pname) +{ + unsigned long offset = reloc_offset(); + + return call_prom("getproplen", 2, 1, node, ADDR(pname)); +} + +static int __init prom_setprop(phandle node, const char *pname, + void *value, size_t valuelen) +{ + unsigned long offset = reloc_offset(); + + return call_prom("setprop", 4, 1, node, ADDR(pname), + (u32)(unsigned long) value, (u32) valuelen); +} + + +/* + * Early parsing of the command line passed to the kernel, used for + * the options that affect the iommu + */ +static void __init early_cmdline_parse(void) +{ + unsigned long offset = reloc_offset(); + struct prom_t *_prom = PTRRELOC(&prom); + char *opt, *p; + int l = 0; + + RELOC(prom_cmd_line[0]) = 0; + p = RELOC(prom_cmd_line); + if ((long)_prom->chosen > 0) + l = prom_getprop(_prom->chosen, "bootargs", p, COMMAND_LINE_SIZE-1); +#ifdef CONFIG_CMDLINE + if (l == 0) /* dbl check */ + strlcpy(RELOC(prom_cmd_line), + RELOC(CONFIG_CMDLINE), sizeof(prom_cmd_line)); +#endif /* CONFIG_CMDLINE */ + prom_printf("command line: %s\n", RELOC(prom_cmd_line)); + + opt = strstr(RELOC(prom_cmd_line), RELOC("iommu=")); + if (opt) { + prom_printf("iommu opt is: %s\n", opt); + opt += 6; + while (*opt && *opt == ' ') + opt++; + if (!strncmp(opt, RELOC("off"), 3)) + RELOC(ppc64_iommu_off) = 1; + else if (!strncmp(opt, RELOC("force"), 5)) + RELOC(iommu_force_on) = 1; + } +} + +/* + * Memory allocation strategy... our layout is normally: + * + * at 14Mb or more we vmlinux, then a gap and initrd. In some rare cases, initrd + * might end up beeing before the kernel though. We assume this won't override + * the final kernel at 0, we have no provision to handle that in this version, + * but it should hopefully never happen. + * + * alloc_top is set to the top of RMO, eventually shrink down if the TCEs overlap + * alloc_bottom is set to the top of kernel/initrd + * + * from there, allocations are done that way : rtas is allocated topmost, and + * the device-tree is allocated from the bottom. We try to grow the device-tree + * allocation as we progress. If we can't, then we fail, we don't currently have + * a facility to restart elsewhere, but that shouldn't be necessary neither + * + * Note that calls to reserve_mem have to be done explicitely, memory allocated + * with either alloc_up or alloc_down isn't automatically reserved. + */ + + +/* + * Allocates memory in the RMO upward from the kernel/initrd + * + * When align is 0, this is a special case, it means to allocate in place + * at the current location of alloc_bottom or fail (that is basically + * extending the previous allocation). Used for the device-tree flattening + */ +static unsigned long __init alloc_up(unsigned long size, unsigned long align) +{ + unsigned long offset = reloc_offset(); + unsigned long base = _ALIGN_UP(RELOC(alloc_bottom), align); + unsigned long addr = 0; + + prom_debug("alloc_up(%x, %x)\n", size, align); + if (RELOC(ram_top) == 0) + prom_panic("alloc_up() called with mem not initialized\n"); + + if (align) + base = _ALIGN_UP(RELOC(alloc_bottom), align); + else + base = RELOC(alloc_bottom); + + for(; (base + size) <= RELOC(alloc_top); + base = _ALIGN_UP(base + 0x100000, align)) { + prom_debug(" trying: 0x%x\n\r", base); + addr = (unsigned long)prom_claim(base, size, 0); + if ((int)addr != PROM_ERROR) + break; + addr = 0; + if (align == 0) + break; + } + if (addr == 0) + return 0; + RELOC(alloc_bottom) = addr; + + prom_debug(" -> %x\n", addr); + prom_debug(" alloc_bottom : %x\n", RELOC(alloc_bottom)); + prom_debug(" alloc_top : %x\n", RELOC(alloc_top)); + prom_debug(" alloc_top_hi : %x\n", RELOC(alloc_top_high)); + prom_debug(" rmo_top : %x\n", RELOC(rmo_top)); + prom_debug(" ram_top : %x\n", RELOC(ram_top)); + + return addr; +} + +/* + * Allocates memory downard, either from top of RMO, or if highmem + * is set, from the top of RAM. Note that this one doesn't handle + * failures. In does claim memory if highmem is not set. + */ +static unsigned long __init alloc_down(unsigned long size, unsigned long align, + int highmem) +{ + unsigned long offset = reloc_offset(); + unsigned long base, addr = 0; + + prom_debug("alloc_down(%x, %x, %s)\n", size, align, + highmem ? RELOC("(high)") : RELOC("(low)")); + if (RELOC(ram_top) == 0) + prom_panic("alloc_down() called with mem not initialized\n"); + + if (highmem) { + /* Carve out storage for the TCE table. */ + addr = _ALIGN_DOWN(RELOC(alloc_top_high) - size, align); + if (addr <= RELOC(alloc_bottom)) + return 0; + else { + /* Will we bump into the RMO ? If yes, check out that we + * didn't overlap existing allocations there, if we did, + * we are dead, we must be the first in town ! + */ + if (addr < RELOC(rmo_top)) { + /* Good, we are first */ + if (RELOC(alloc_top) == RELOC(rmo_top)) + RELOC(alloc_top) = RELOC(rmo_top) = addr; + else + return 0; + } + RELOC(alloc_top_high) = addr; + } + goto bail; + } + + base = _ALIGN_DOWN(RELOC(alloc_top) - size, align); + for(; base > RELOC(alloc_bottom); base = _ALIGN_DOWN(base - 0x100000, align)) { + prom_debug(" trying: 0x%x\n\r", base); + addr = (unsigned long)prom_claim(base, size, 0); + if ((int)addr != PROM_ERROR) + break; + addr = 0; + } + if (addr == 0) + return 0; + RELOC(alloc_top) = addr; + + bail: + prom_debug(" -> %x\n", addr); + prom_debug(" alloc_bottom : %x\n", RELOC(alloc_bottom)); + prom_debug(" alloc_top : %x\n", RELOC(alloc_top)); + prom_debug(" alloc_top_hi : %x\n", RELOC(alloc_top_high)); + prom_debug(" rmo_top : %x\n", RELOC(rmo_top)); + prom_debug(" ram_top : %x\n", RELOC(ram_top)); + + return addr; +} + +/* + * Parse a "reg" cell + */ +static unsigned long __init prom_next_cell(int s, cell_t **cellp) +{ + cell_t *p = *cellp; + unsigned long r = 0; + + /* Ignore more than 2 cells */ + while (s > 2) { + p++; + s--; + } + while (s) { + r <<= 32; + r |= *(p++); + s--; + } + + *cellp = p; + return r; +} + +/* + * Very dumb function for adding to the memory reserve list, but + * we don't need anything smarter at this point + * + * XXX Eventually check for collisions. They should NEVER happen + * if problems seem to show up, it would be a good start to track + * them down. + */ +static void reserve_mem(unsigned long base, unsigned long size) +{ + unsigned long offset = reloc_offset(); + unsigned long top = base + size; + unsigned long cnt = RELOC(mem_reserve_cnt); + + if (size == 0) + return; + + /* We need to always keep one empty entry so that we + * have our terminator with "size" set to 0 since we are + * dumb and just copy this entire array to the boot params + */ + base = _ALIGN_DOWN(base, PAGE_SIZE); + top = _ALIGN_UP(top, PAGE_SIZE); + size = top - base; + + if (cnt >= (MEM_RESERVE_MAP_SIZE - 1)) + prom_panic("Memory reserve map exhausted !\n"); + RELOC(mem_reserve_map)[cnt].base = base; + RELOC(mem_reserve_map)[cnt].size = size; + RELOC(mem_reserve_cnt) = cnt + 1; +} + +/* + * Initialize memory allocation mecanism, parse "memory" nodes and + * obtain that way the top of memory and RMO to setup out local allocator + */ +static void __init prom_init_mem(void) +{ + phandle node; + char *path, type[64]; + unsigned int plen; + cell_t *p, *endp; + unsigned long offset = reloc_offset(); + struct prom_t *_prom = PTRRELOC(&prom); + + /* + * We iterate the memory nodes to find + * 1) top of RMO (first node) + * 2) top of memory + */ + prom_debug("root_addr_cells: %x\n", (long)_prom->root_addr_cells); + prom_debug("root_size_cells: %x\n", (long)_prom->root_size_cells); + + prom_debug("scanning memory:\n"); + path = RELOC(prom_scratch); + + for (node = 0; prom_next_node(&node); ) { + type[0] = 0; + prom_getprop(node, "device_type", type, sizeof(type)); + + if (strcmp(type, RELOC("memory"))) + continue; + + plen = prom_getprop(node, "reg", RELOC(regbuf), sizeof(regbuf)); + if (plen > sizeof(regbuf)) { + prom_printf("memory node too large for buffer !\n"); + plen = sizeof(regbuf); + } + p = RELOC(regbuf); + endp = p + (plen / sizeof(cell_t)); + +#ifdef DEBUG_PROM + memset(path, 0, PROM_SCRATCH_SIZE); + call_prom("package-to-path", 3, 1, node, path, PROM_SCRATCH_SIZE-1); + prom_debug(" node %s :\n", path); +#endif /* DEBUG_PROM */ + + while ((endp - p) >= (_prom->root_addr_cells + _prom->root_size_cells)) { + unsigned long base, size; + + base = prom_next_cell(_prom->root_addr_cells, &p); + size = prom_next_cell(_prom->root_size_cells, &p); + + if (size == 0) + continue; + prom_debug(" %x %x\n", base, size); + if (base == 0) + RELOC(rmo_top) = size; + if ((base + size) > RELOC(ram_top)) + RELOC(ram_top) = base + size; + } + } + + /* Setup our top/bottom alloc points, that is top of RMO or top of + * segment 0 when running non-LPAR + */ + if ( RELOC(of_platform) == PLATFORM_PSERIES_LPAR ) + RELOC(alloc_top) = RELOC(rmo_top); + else + RELOC(alloc_top) = min(0x40000000ul, RELOC(ram_top)); + RELOC(alloc_bottom) = PAGE_ALIGN(RELOC(klimit) - offset + 0x4000); + RELOC(alloc_top_high) = RELOC(ram_top); + + /* Check if we have an initrd after the kernel, if we do move our bottom + * point to after it + */ + if (RELOC(prom_initrd_start)) { + if ((RELOC(prom_initrd_start) + RELOC(prom_initrd_end)) + > RELOC(alloc_bottom)) + RELOC(alloc_bottom) = PAGE_ALIGN(RELOC(prom_initrd_end)); + } + + prom_printf("memory layout at init:\n"); + prom_printf(" alloc_bottom : %x\n", RELOC(alloc_bottom)); + prom_printf(" alloc_top : %x\n", RELOC(alloc_top)); + prom_printf(" alloc_top_hi : %x\n", RELOC(alloc_top_high)); + prom_printf(" rmo_top : %x\n", RELOC(rmo_top)); + prom_printf(" ram_top : %x\n", RELOC(ram_top)); +} + + +/* + * Allocate room for and instanciate RTAS + */ +static void __init prom_instantiate_rtas(void) +{ + unsigned long offset = reloc_offset(); + struct prom_t *_prom = PTRRELOC(&prom); + phandle prom_rtas; + u64 base, entry = 0; + u32 size; + + prom_debug("prom_instantiate_rtas: start...\n"); + + prom_rtas = call_prom("finddevice", 1, 1, ADDR("/rtas")); + if (prom_rtas != (phandle) -1) { + prom_getprop(prom_rtas, "rtas-size", &size, sizeof(size)); + if (size != 0) { + base = alloc_down(size, PAGE_SIZE, 0); + if (base == 0) { + prom_printf("RTAS allocation failed !\n"); + return; + } + prom_printf("instantiating rtas at 0x%x", base); + + prom_rtas = call_prom("open", 1, 1, ADDR("/rtas")); + prom_printf("..."); + + if (call_prom("call-method", 3, 2, + ADDR("instantiate-rtas"), + prom_rtas, base) != PROM_ERROR) { + entry = (long)_prom->args.rets[1]; + } + if (entry == 0) { + prom_printf(" failed\n"); + return; + } + prom_printf(" done\n"); + + reserve_mem(base, size); + } + + prom_setprop(_prom->chosen, "linux,rtas-base", &base, sizeof(base)); + prom_setprop(_prom->chosen, "linux,rtas-entry", &entry, sizeof(entry)); + prom_setprop(_prom->chosen, "linux,rtas-size", &size, sizeof(size)); + + prom_debug("rtas base = 0x%x\n", base); + prom_debug("rtas entry = 0x%x\n", entry); + prom_debug("rtas size = 0x%x\n", (long)size); + } + prom_debug("prom_instantiate_rtas: end...\n"); +} + + +/* + * Allocate room for and initialize TCE tables + */ +static void __init prom_initialize_tce_table(void) +{ + phandle node; + ihandle phb_node; + unsigned long offset = reloc_offset(); + char compatible[64], type[64], model[64]; + char *path = RELOC(prom_scratch); + u64 base, vbase, align; + u32 minalign, minsize; + u64 tce_entry, *tce_entryp; + u64 local_alloc_top, local_alloc_bottom; + u64 i; + + if (RELOC(ppc64_iommu_off)) + return; + + prom_debug("starting prom_initialize_tce_table\n"); + + /* Cache current top of allocs so we reserve a single block */ + local_alloc_top = RELOC(alloc_top_high); + local_alloc_bottom = local_alloc_top; + + /* Search all nodes looking for PHBs. */ + for (node = 0; prom_next_node(&node); ) { + compatible[0] = 0; + type[0] = 0; + model[0] = 0; + prom_getprop(node, "compatible", + compatible, sizeof(compatible)); + prom_getprop(node, "device_type", type, sizeof(type)); + prom_getprop(node, "model", model, sizeof(model)); + + if ((type[0] == 0) || (strstr(type, RELOC("pci")) == NULL)) + continue; + + /* Keep the old logic in tack to avoid regression. */ + if (compatible[0] != 0) { + if ((strstr(compatible, RELOC("python")) == NULL) && + (strstr(compatible, RELOC("Speedwagon")) == NULL) && + (strstr(compatible, RELOC("Winnipeg")) == NULL)) + continue; + } else if (model[0] != 0) { + if ((strstr(model, RELOC("ython")) == NULL) && + (strstr(model, RELOC("peedwagon")) == NULL) && + (strstr(model, RELOC("innipeg")) == NULL)) + continue; + } + + if (prom_getprop(node, "tce-table-minalign", &minalign, + sizeof(minalign)) == PROM_ERROR) + minalign = 0; + if (prom_getprop(node, "tce-table-minsize", &minsize, + sizeof(minsize)) == PROM_ERROR) + minsize = 4UL << 20; + + /* + * Even though we read what OF wants, we just set the table + * size to 4 MB. This is enough to map 2GB of PCI DMA space. + * By doing this, we avoid the pitfalls of trying to DMA to + * MMIO space and the DMA alias hole. + * + * On POWER4, firmware sets the TCE region by assuming + * each TCE table is 8MB. Using this memory for anything + * else will impact performance, so we always allocate 8MB. + * Anton + */ + if (__is_processor(PV_POWER4) || __is_processor(PV_POWER4p)) + minsize = 8UL << 20; + else + minsize = 4UL << 20; + + /* Align to the greater of the align or size */ + align = max(minalign, minsize); + base = alloc_down(minsize, align, 1); + if (base == 0) + prom_panic("ERROR, cannot find space for TCE table.\n"); + if (base < local_alloc_bottom) + local_alloc_bottom = base; + + vbase = (unsigned long)abs_to_virt(base); + + /* Save away the TCE table attributes for later use. */ + prom_setprop(node, "linux,tce-base", &vbase, sizeof(vbase)); + prom_setprop(node, "linux,tce-size", &minsize, sizeof(minsize)); + prom_setprop(node, "linux,has-tce-table", NULL, 0); + + /* It seems OF doesn't null-terminate the path :-( */ + memset(path, 0, sizeof(path)); + /* Call OF to setup the TCE hardware */ + if (call_prom("package-to-path", 3, 1, node, + path, PROM_SCRATCH_SIZE-1) == PROM_ERROR) { + prom_printf("package-to-path failed\n"); + } + + prom_debug("TCE table: %s\n", path); + prom_debug("\tnode = 0x%x\n", node); + prom_debug("\tbase = 0x%x\n", vbase); + prom_debug("\tsize = 0x%x\n", minsize); + + /* Initialize the table to have a one-to-one mapping + * over the allocated size. + */ + tce_entryp = (unsigned long *)base; + for (i = 0; i < (minsize >> 3) ;tce_entryp++, i++) { + tce_entry = (i << PAGE_SHIFT); + tce_entry |= 0x3; + *tce_entryp = tce_entry; + } + + prom_printf("opening PHB %s", path); + phb_node = call_prom("open", 1, 1, path); + if ( (long)phb_node <= 0) + prom_printf("... failed\n"); + else + prom_printf("... done\n"); + + call_prom("call-method", 6, 0, ADDR("set-64-bit-addressing"), + phb_node, -1, minsize, + (u32) base, (u32) (base >> 32)); + call_prom("close", 1, 0, phb_node); + } + + reserve_mem(local_alloc_bottom, local_alloc_top - local_alloc_bottom); + + /* Flag the first invalid entry */ + prom_debug("ending prom_initialize_tce_table\n"); +} + +/* + * With CHRP SMP we need to use the OF to start the other + * processors so we can't wait until smp_boot_cpus (the OF is + * trashed by then) so we have to put the processors into + * a holding pattern controlled by the kernel (not OF) before + * we destroy the OF. + * + * This uses a chunk of low memory, puts some holding pattern + * code there and sends the other processors off to there until + * smp_boot_cpus tells them to do something. The holding pattern + * checks that address until its cpu # is there, when it is that + * cpu jumps to __secondary_start(). smp_boot_cpus() takes care + * of setting those values. + * + * We also use physical address 0x4 here to tell when a cpu + * is in its holding pattern code. + * + * Fixup comment... DRENG / PPPBBB - Peter + * + * -- Cort + */ +static void __init prom_hold_cpus(void) +{ + unsigned long i; + unsigned int reg; + phandle node; + unsigned long offset = reloc_offset(); + char type[64]; + int cpuid = 0; + unsigned int interrupt_server[MAX_CPU_THREADS]; + unsigned int cpu_threads, hw_cpu_num; + int propsize; + extern void __secondary_hold(void); + extern unsigned long __secondary_hold_spinloop; + extern unsigned long __secondary_hold_acknowledge; + unsigned long *spinloop + = (void *)virt_to_abs(&__secondary_hold_spinloop); + unsigned long *acknowledge + = (void *)virt_to_abs(&__secondary_hold_acknowledge); + unsigned long secondary_hold + = virt_to_abs(*PTRRELOC((unsigned long *)__secondary_hold)); + struct prom_t *_prom = PTRRELOC(&prom); + + prom_debug("prom_hold_cpus: start...\n"); + prom_debug(" 1) spinloop = 0x%x\n", (unsigned long)spinloop); + prom_debug(" 1) *spinloop = 0x%x\n", *spinloop); + prom_debug(" 1) acknowledge = 0x%x\n", + (unsigned long)acknowledge); + prom_debug(" 1) *acknowledge = 0x%x\n", *acknowledge); + prom_debug(" 1) secondary_hold = 0x%x\n", secondary_hold); + + /* Set the common spinloop variable, so all of the secondary cpus + * will block when they are awakened from their OF spinloop. + * This must occur for both SMP and non SMP kernels, since OF will + * be trashed when we move the kernel. + */ + *spinloop = 0; + +#ifdef CONFIG_HMT + for (i=0; i < NR_CPUS; i++) { + RELOC(hmt_thread_data)[i].pir = 0xdeadbeef; + } +#endif + /* look for cpus */ + for (node = 0; prom_next_node(&node); ) { + type[0] = 0; + prom_getprop(node, "device_type", type, sizeof(type)); + if (strcmp(type, RELOC("cpu")) != 0) + continue; + + /* Skip non-configured cpus. */ + prom_getprop(node, "status", type, sizeof(type)); + if (strcmp(type, RELOC("okay")) != 0) + continue; + + reg = -1; + prom_getprop(node, "reg", ®, sizeof(reg)); + + prom_debug("\ncpuid = 0x%x\n", cpuid); + prom_debug("cpu hw idx = 0x%x\n", reg); + + /* Init the acknowledge var which will be reset by + * the secondary cpu when it awakens from its OF + * spinloop. + */ + *acknowledge = (unsigned long)-1; + + propsize = prom_getprop(node, "ibm,ppc-interrupt-server#s", + &interrupt_server, + sizeof(interrupt_server)); + if (propsize < 0) { + /* no property. old hardware has no SMT */ + cpu_threads = 1; + interrupt_server[0] = reg; /* fake it with phys id */ + } else { + /* We have a threaded processor */ + cpu_threads = propsize / sizeof(u32); + if (cpu_threads > MAX_CPU_THREADS) { + prom_printf("SMT: too many threads!\n" + "SMT: found %x, max is %x\n", + cpu_threads, MAX_CPU_THREADS); + cpu_threads = 1; /* ToDo: panic? */ + } + } + + hw_cpu_num = interrupt_server[0]; + if (hw_cpu_num != _prom->cpu) { + /* Primary Thread of non-boot cpu */ + prom_printf("%x : starting cpu hw idx %x... ", cpuid, reg); + call_prom("start-cpu", 3, 0, node, + secondary_hold, cpuid); + + for ( i = 0 ; (i < 100000000) && + (*acknowledge == ((unsigned long)-1)); i++ ) ; + + if (*acknowledge == cpuid) { + prom_printf("done\n"); + /* We have to get every CPU out of OF, + * even if we never start it. */ + if (cpuid >= NR_CPUS) + goto next; + } else { + prom_printf("failed: %x\n", *acknowledge); + } + } +#ifdef CONFIG_SMP + else + prom_printf("%x : boot cpu %x\n", cpuid, reg); +#endif +next: +#ifdef CONFIG_SMP + /* Init paca for secondary threads. They start later. */ + for (i=1; i < cpu_threads; i++) { + cpuid++; + if (cpuid >= NR_CPUS) + continue; + } +#endif /* CONFIG_SMP */ + cpuid++; + } +#ifdef CONFIG_HMT + /* Only enable HMT on processors that provide support. */ + if (__is_processor(PV_PULSAR) || + __is_processor(PV_ICESTAR) || + __is_processor(PV_SSTAR)) { + prom_printf(" starting secondary threads\n"); + + for (i = 0; i < NR_CPUS; i += 2) { + if (!cpu_online(i)) + continue; + + if (i == 0) { + unsigned long pir = mfspr(SPRN_PIR); + if (__is_processor(PV_PULSAR)) { + RELOC(hmt_thread_data)[i].pir = + pir & 0x1f; + } else { + RELOC(hmt_thread_data)[i].pir = + pir & 0x3ff; + } + } + } + } else { + prom_printf("Processor is not HMT capable\n"); + } +#endif + + if (cpuid > NR_CPUS) + prom_printf("WARNING: maximum CPUs (" __stringify(NR_CPUS) + ") exceeded: ignoring extras\n"); + + prom_debug("prom_hold_cpus: end...\n"); +} + + +static void __init prom_init_client_services(unsigned long pp) +{ + unsigned long offset = reloc_offset(); + struct prom_t *_prom = PTRRELOC(&prom); + + /* Get a handle to the prom entry point before anything else */ + _prom->entry = pp; + + /* Init default value for phys size */ + _prom->root_size_cells = 1; + _prom->root_addr_cells = 2; + + /* get a handle for the stdout device */ + _prom->chosen = call_prom("finddevice", 1, 1, ADDR("/chosen")); + if ((long)_prom->chosen <= 0) + prom_panic("cannot find chosen"); /* msg won't be printed :( */ + + /* get device tree root */ + _prom->root = call_prom("finddevice", 1, 1, ADDR("/")); + if ((long)_prom->root <= 0) + prom_panic("cannot find device tree root"); /* msg won't be printed :( */ +} + +static void __init prom_init_stdout(void) +{ + unsigned long offset = reloc_offset(); + struct prom_t *_prom = PTRRELOC(&prom); + char *path = RELOC(of_stdout_device); + char type[16]; + u32 val; + + if (prom_getprop(_prom->chosen, "stdout", &val, sizeof(val)) <= 0) + prom_panic("cannot find stdout"); + + _prom->stdout = val; + + /* Get the full OF pathname of the stdout device */ + memset(path, 0, 256); + call_prom("instance-to-path", 3, 1, _prom->stdout, path, 255); + val = call_prom("instance-to-package", 1, 1, _prom->stdout); + prom_setprop(_prom->chosen, "linux,stdout-package", &val, sizeof(val)); + prom_printf("OF stdout device is: %s\n", RELOC(of_stdout_device)); + prom_setprop(_prom->chosen, "linux,stdout-path", + RELOC(of_stdout_device), strlen(RELOC(of_stdout_device))+1); + + /* If it's a display, note it */ + memset(type, 0, sizeof(type)); + prom_getprop(val, "device_type", type, sizeof(type)); + if (strcmp(type, RELOC("display")) == 0) { + _prom->disp_node = val; + prom_setprop(val, "linux,boot-display", NULL, 0); + } +} + +static int __init prom_find_machine_type(void) +{ + unsigned long offset = reloc_offset(); + struct prom_t *_prom = PTRRELOC(&prom); + char compat[256]; + int len, i = 0; + phandle rtas; + + len = prom_getprop(_prom->root, "compatible", + compat, sizeof(compat)-1); + if (len > 0) { + compat[len] = 0; + while (i < len) { + char *p = &compat[i]; + int sl = strlen(p); + if (sl == 0) + break; + if (strstr(p, RELOC("Power Macintosh")) || + strstr(p, RELOC("MacRISC4"))) + return PLATFORM_POWERMAC; + i += sl + 1; + } + } + /* Default to pSeries. We need to know if we are running LPAR */ + rtas = call_prom("finddevice", 1, 1, ADDR("/rtas")); + if (rtas != (phandle) -1) { + unsigned long x; + x = prom_getproplen(rtas, "ibm,hypertas-functions"); + if (x != PROM_ERROR) { + prom_printf("Hypertas detected, assuming LPAR !\n"); + return PLATFORM_PSERIES_LPAR; + } + } + return PLATFORM_PSERIES; +} + +static int __init prom_set_color(ihandle ih, int i, int r, int g, int b) +{ + unsigned long offset = reloc_offset(); + + return call_prom("call-method", 6, 1, ADDR("color!"), ih, i, b, g, r); +} + +/* + * If we have a display that we don't know how to drive, + * we will want to try to execute OF's open method for it + * later. However, OF will probably fall over if we do that + * we've taken over the MMU. + * So we check whether we will need to open the display, + * and if so, open it now. + */ +static unsigned long __init prom_check_displays(void) +{ + unsigned long offset = reloc_offset(); + struct prom_t *_prom = PTRRELOC(&prom); + char type[16], *path; + phandle node; + ihandle ih; + int i; + + static unsigned char default_colors[] = { + 0x00, 0x00, 0x00, + 0x00, 0x00, 0xaa, + 0x00, 0xaa, 0x00, + 0x00, 0xaa, 0xaa, + 0xaa, 0x00, 0x00, + 0xaa, 0x00, 0xaa, + 0xaa, 0xaa, 0x00, + 0xaa, 0xaa, 0xaa, + 0x55, 0x55, 0x55, + 0x55, 0x55, 0xff, + 0x55, 0xff, 0x55, + 0x55, 0xff, 0xff, + 0xff, 0x55, 0x55, + 0xff, 0x55, 0xff, + 0xff, 0xff, 0x55, + 0xff, 0xff, 0xff + }; + const unsigned char *clut; + + prom_printf("Looking for displays\n"); + for (node = 0; prom_next_node(&node); ) { + memset(type, 0, sizeof(type)); + prom_getprop(node, "device_type", type, sizeof(type)); + if (strcmp(type, RELOC("display")) != 0) + continue; + + /* It seems OF doesn't null-terminate the path :-( */ + path = RELOC(prom_scratch); + memset(path, 0, PROM_SCRATCH_SIZE); + + /* + * leave some room at the end of the path for appending extra + * arguments + */ + if (call_prom("package-to-path", 3, 1, node, path, PROM_SCRATCH_SIZE-10) < 0) + continue; + prom_printf("found display : %s, opening ... ", path); + + ih = call_prom("open", 1, 1, path); + if (ih == (ihandle)0 || ih == (ihandle)-1) { + prom_printf("failed\n"); + continue; + } + + /* Success */ + prom_printf("done\n"); + prom_setprop(node, "linux,opened", NULL, 0); + + /* + * stdout wasn't a display node, pick the first we can find + * for btext + */ + if (_prom->disp_node == 0) + _prom->disp_node = node; + + /* Setup a useable color table when the appropriate + * method is available. Should update this to set-colors */ + clut = RELOC(default_colors); + for (i = 0; i < 32; i++, clut += 3) + if (prom_set_color(ih, i, clut[0], clut[1], + clut[2]) != 0) + break; + +#ifdef CONFIG_LOGO_LINUX_CLUT224 + clut = PTRRELOC(RELOC(logo_linux_clut224.clut)); + for (i = 0; i < RELOC(logo_linux_clut224.clutsize); i++, clut += 3) + if (prom_set_color(ih, i + 32, clut[0], clut[1], + clut[2]) != 0) + break; +#endif /* CONFIG_LOGO_LINUX_CLUT224 */ + } +} + + +/* Return (relocated) pointer to this much memory: moves initrd if reqd. */ +static void __init *make_room(unsigned long *mem_start, unsigned long *mem_end, + unsigned long needed, unsigned long align) +{ + unsigned long offset = reloc_offset(); + void *ret; + + *mem_start = _ALIGN(*mem_start, align); + while ((*mem_start + needed) > *mem_end) { + unsigned long room, chunk; + + prom_debug("Chunk exhausted, claiming more at %x...\n", + RELOC(alloc_bottom)); + room = RELOC(alloc_top) - RELOC(alloc_bottom); + if (room > DEVTREE_CHUNK_SIZE) + room = DEVTREE_CHUNK_SIZE; + if (room < PAGE_SIZE) + prom_panic("No memory for flatten_device_tree (no room)"); + chunk = alloc_up(room, 0); + if (chunk == 0) + prom_panic("No memory for flatten_device_tree (claim failed)"); + *mem_end = RELOC(alloc_top); + } + + ret = (void *)*mem_start; + *mem_start += needed; + + return ret; +} + +#define dt_push_token(token, mem_start, mem_end) \ + do { *((u32 *)make_room(mem_start, mem_end, 4, 4)) = token; } while(0) + +static unsigned long __init dt_find_string(char *str) +{ + unsigned long offset = reloc_offset(); + char *s, *os; + + s = os = (char *)RELOC(dt_string_start); + s += 4; + while (s < (char *)RELOC(dt_string_end)) { + if (strcmp(s, str) == 0) + return s - os; + s += strlen(s) + 1; + } + return 0; +} + +static void __init scan_dt_build_strings(phandle node, unsigned long *mem_start, + unsigned long *mem_end) +{ + unsigned long offset = reloc_offset(); + char *prev_name, *namep, *sstart; + unsigned long soff; + phandle child; + + sstart = (char *)RELOC(dt_string_start); + + /* get and store all property names */ + prev_name = RELOC(""); + for (;;) { + + /* 32 is max len of name including nul. */ + namep = make_room(mem_start, mem_end, 32, 1); + if (call_prom("nextprop", 3, 1, node, prev_name, namep) <= 0) { + /* No more nodes: unwind alloc */ + *mem_start = (unsigned long)namep; + break; + } + soff = dt_find_string(namep); + if (soff != 0) { + *mem_start = (unsigned long)namep; + namep = sstart + soff; + } else { + /* Trim off some if we can */ + *mem_start = (unsigned long)namep + strlen(namep) + 1; + RELOC(dt_string_end) = *mem_start; + } + prev_name = namep; + } + + /* do all our children */ + child = call_prom("child", 1, 1, node); + while (child != (phandle)0) { + scan_dt_build_strings(child, mem_start, mem_end); + child = call_prom("peer", 1, 1, child); + } +} + +static void __init scan_dt_build_struct(phandle node, unsigned long *mem_start, + unsigned long *mem_end) +{ + int l, align; + phandle child; + char *namep, *prev_name, *sstart; + unsigned long soff; + unsigned char *valp; + unsigned long offset = reloc_offset(); + char pname[32]; + char *path; + + path = RELOC(prom_scratch); + + dt_push_token(OF_DT_BEGIN_NODE, mem_start, mem_end); + + /* get the node's full name */ + namep = (char *)*mem_start; + l = call_prom("package-to-path", 3, 1, node, + namep, *mem_end - *mem_start); + if (l >= 0) { + /* Didn't fit? Get more room. */ + if (l+1 > *mem_end - *mem_start) { + namep = make_room(mem_start, mem_end, l+1, 1); + call_prom("package-to-path", 3, 1, node, namep, l); + } + namep[l] = '\0'; + *mem_start = _ALIGN(((unsigned long) namep) + strlen(namep) + 1, 4); + } + + /* get it again for debugging */ + memset(path, 0, PROM_SCRATCH_SIZE); + call_prom("package-to-path", 3, 1, node, path, PROM_SCRATCH_SIZE-1); + + /* get and store all properties */ + prev_name = RELOC(""); + sstart = (char *)RELOC(dt_string_start); + for (;;) { + if (call_prom("nextprop", 3, 1, node, prev_name, pname) <= 0) + break; + + /* find string offset */ + soff = dt_find_string(pname); + if (soff == 0) { + prom_printf("WARNING: Can't find string index for <%s>, node %s\n", + pname, path); + break; + } + prev_name = sstart + soff; + + /* get length */ + l = call_prom("getproplen", 2, 1, node, pname); + + /* sanity checks */ + if (l < 0) + continue; + if (l > MAX_PROPERTY_LENGTH) { + prom_printf("WARNING: ignoring large property "); + /* It seems OF doesn't null-terminate the path :-( */ + prom_printf("[%s] ", path); + prom_printf("%s length 0x%x\n", pname, l); + continue; + } + + /* push property head */ + dt_push_token(OF_DT_PROP, mem_start, mem_end); + dt_push_token(l, mem_start, mem_end); + dt_push_token(soff, mem_start, mem_end); + + /* push property content */ + align = (l >= 8) ? 8 : 4; + valp = make_room(mem_start, mem_end, l, align); + call_prom("getprop", 4, 1, node, pname, valp, l); + *mem_start = _ALIGN(*mem_start, 4); + } + + /* Add a "linux,phandle" property. */ + soff = dt_find_string(RELOC("linux,phandle")); + if (soff == 0) + prom_printf("WARNING: Can't find string index for " + " node %s\n", path); + else { + dt_push_token(OF_DT_PROP, mem_start, mem_end); + dt_push_token(4, mem_start, mem_end); + dt_push_token(soff, mem_start, mem_end); + valp = make_room(mem_start, mem_end, 4, 4); + *(u32 *)valp = node; + } + + /* do all our children */ + child = call_prom("child", 1, 1, node); + while (child != (phandle)0) { + scan_dt_build_struct(child, mem_start, mem_end); + child = call_prom("peer", 1, 1, child); + } + + dt_push_token(OF_DT_END_NODE, mem_start, mem_end); +} + +static void __init flatten_device_tree(void) +{ + phandle root; + unsigned long offset = reloc_offset(); + unsigned long mem_start, mem_end, room; + struct boot_param_header *hdr; + char *namep; + u64 *rsvmap; + + /* + * Check how much room we have between alloc top & bottom (+/- a + * few pages), crop to 4Mb, as this is our "chuck" size + */ + room = RELOC(alloc_top) - RELOC(alloc_bottom) - 0x4000; + if (room > DEVTREE_CHUNK_SIZE) + room = DEVTREE_CHUNK_SIZE; + prom_debug("starting device tree allocs at %x\n", RELOC(alloc_bottom)); + + /* Now try to claim that */ + mem_start = (unsigned long)alloc_up(room, PAGE_SIZE); + if (mem_start == 0) + prom_panic("Can't allocate initial device-tree chunk\n"); + mem_end = RELOC(alloc_top); + + /* Get root of tree */ + root = call_prom("peer", 1, 1, (phandle)0); + if (root == (phandle)0) + prom_panic ("couldn't get device tree root\n"); + + /* Build header and make room for mem rsv map */ + mem_start = _ALIGN(mem_start, 4); + hdr = make_room(&mem_start, &mem_end, sizeof(struct boot_param_header), 4); + RELOC(dt_header_start) = (unsigned long)hdr; + rsvmap = make_room(&mem_start, &mem_end, sizeof(mem_reserve_map), 8); + + /* Start of strings */ + mem_start = PAGE_ALIGN(mem_start); + RELOC(dt_string_start) = mem_start; + mem_start += 4; /* hole */ + + /* Add "linux,phandle" in there, we'll need it */ + namep = make_room(&mem_start, &mem_end, 16, 1); + strcpy(namep, RELOC("linux,phandle")); + mem_start = (unsigned long)namep + strlen(namep) + 1; + RELOC(dt_string_end) = mem_start; + + /* Build string array */ + prom_printf("Building dt strings...\n"); + scan_dt_build_strings(root, &mem_start, &mem_end); + + /* Build structure */ + mem_start = PAGE_ALIGN(mem_start); + RELOC(dt_struct_start) = mem_start; + prom_printf("Building dt structure...\n"); + scan_dt_build_struct(root, &mem_start, &mem_end); + dt_push_token(OF_DT_END, &mem_start, &mem_end); + RELOC(dt_struct_end) = PAGE_ALIGN(mem_start); + + /* Finish header */ + hdr->magic = OF_DT_HEADER; + hdr->totalsize = RELOC(dt_struct_end) - RELOC(dt_header_start); + hdr->off_dt_struct = RELOC(dt_struct_start) - RELOC(dt_header_start); + hdr->off_dt_strings = RELOC(dt_string_start) - RELOC(dt_header_start); + hdr->off_mem_rsvmap = ((unsigned long)rsvmap) - RELOC(dt_header_start); + hdr->version = OF_DT_VERSION; + hdr->last_comp_version = 1; + + /* Reserve the whole thing and copy the reserve map in, we + * also bump mem_reserve_cnt to cause further reservations to + * fail since it's too late. + */ + reserve_mem(RELOC(dt_header_start), hdr->totalsize); + memcpy(rsvmap, RELOC(mem_reserve_map), sizeof(mem_reserve_map)); + +#ifdef DEBUG_PROM + { + int i; + prom_printf("reserved memory map:\n"); + for (i = 0; i < RELOC(mem_reserve_cnt); i++) + prom_printf(" %x - %x\n", RELOC(mem_reserve_map)[i].base, + RELOC(mem_reserve_map)[i].size); + } +#endif + RELOC(mem_reserve_cnt) = MEM_RESERVE_MAP_SIZE; + + prom_printf("Device tree strings 0x%x -> 0x%x\n", + RELOC(dt_string_start), RELOC(dt_string_end)); + prom_printf("Device tree struct 0x%x -> 0x%x\n", + RELOC(dt_struct_start), RELOC(dt_struct_end)); + + } + +static void __init prom_find_boot_cpu(void) +{ + unsigned long offset = reloc_offset(); + struct prom_t *_prom = PTRRELOC(&prom); + u32 getprop_rval; + ihandle prom_cpu; + phandle cpu_pkg; + + if (prom_getprop(_prom->chosen, "cpu", &prom_cpu, sizeof(prom_cpu)) <= 0) + prom_panic("cannot find boot cpu"); + + cpu_pkg = call_prom("instance-to-package", 1, 1, prom_cpu); + + prom_setprop(cpu_pkg, "linux,boot-cpu", NULL, 0); + prom_getprop(cpu_pkg, "reg", &getprop_rval, sizeof(getprop_rval)); + _prom->cpu = getprop_rval; + + prom_debug("Booting CPU hw index = 0x%x\n", _prom->cpu); +} + +static void __init prom_check_initrd(unsigned long r3, unsigned long r4) +{ +#ifdef CONFIG_BLK_DEV_INITRD + unsigned long offset = reloc_offset(); + struct prom_t *_prom = PTRRELOC(&prom); + + if ( r3 && r4 && r4 != 0xdeadbeef) { + u64 val; + + RELOC(prom_initrd_start) = (r3 >= KERNELBASE) ? __pa(r3) : r3; + RELOC(prom_initrd_end) = RELOC(prom_initrd_start) + r4; + + val = (u64)RELOC(prom_initrd_start); + prom_setprop(_prom->chosen, "linux,initrd-start", &val, sizeof(val)); + val = (u64)RELOC(prom_initrd_end); + prom_setprop(_prom->chosen, "linux,initrd-end", &val, sizeof(val)); + + reserve_mem(RELOC(prom_initrd_start), + RELOC(prom_initrd_end) - RELOC(prom_initrd_start)); + + prom_debug("initrd_start=0x%x\n", RELOC(prom_initrd_start)); + prom_debug("initrd_end=0x%x\n", RELOC(prom_initrd_end)); + } +#endif /* CONFIG_BLK_DEV_INITRD */ +} + +/* + * We enter here early on, when the Open Firmware prom is still + * handling exceptions and the MMU hash table for us. + */ + +unsigned long __init prom_init(unsigned long r3, unsigned long r4, unsigned long pp, + unsigned long r6, unsigned long r7) +{ + unsigned long offset = reloc_offset(); + struct prom_t *_prom = PTRRELOC(&prom); + unsigned long phys = KERNELBASE - offset; + u32 getprop_rval; + + /* + * First zero the BSS + */ + memset(PTRRELOC(&__bss_start), 0, __bss_stop - __bss_start); + + /* + * Init interface to Open Firmware, get some node references, + * like /chosen + */ + prom_init_client_services(pp); + + /* + * Init prom stdout device + */ + prom_init_stdout(); + prom_debug("klimit=0x%x\n", RELOC(klimit)); + prom_debug("offset=0x%x\n", offset); + + /* + * Reserve kernel in reserve map + */ + reserve_mem(0, __pa(RELOC(klimit))); + + /* + * Check for an initrd + */ + prom_check_initrd(r3, r4); + + /* + * Get default machine type. At this point, we do not differenciate + * between pSeries SMP and pSeries LPAR + */ + RELOC(of_platform) = prom_find_machine_type(); + getprop_rval = RELOC(of_platform); + prom_setprop(_prom->chosen, "linux,platform", + &getprop_rval, sizeof(getprop_rval)); + + /* + * On pSeries, copy the CPU hold code + */ + if (RELOC(of_platform) & PLATFORM_PSERIES) + copy_and_flush(0, KERNELBASE - offset, 0x100, 0); + + /* + * Get memory cells format + */ + getprop_rval = 1; + prom_getprop(_prom->root, "#size-cells", + &getprop_rval, sizeof(getprop_rval)); + _prom->root_size_cells = getprop_rval; + getprop_rval = 2; + prom_getprop(_prom->root, "#address-cells", + &getprop_rval, sizeof(getprop_rval)); + _prom->root_addr_cells = getprop_rval; + + /* + * Do early parsing of command line + */ + early_cmdline_parse(); + + /* + * Initialize memory management within prom_init + */ + prom_init_mem(); + + /* + * Determine which cpu is actually running right _now_ + */ + prom_find_boot_cpu(); + + /* + * Initialize display devices + */ + prom_check_displays(); + + /* + * Initialize IOMMU (TCE tables) on pSeries. Do that before anything else + * that uses the allocator, we need to make sure we get the top of memory + * available for us here... + */ + if (RELOC(of_platform) == PLATFORM_PSERIES) + prom_initialize_tce_table(); + + /* + * On non-powermacs, try to instantiate RTAS and puts all CPUs + * in spin-loops. PowerMacs don't have a working RTAS and use + * a different way to spin CPUs + */ + if (RELOC(of_platform) != PLATFORM_POWERMAC) { + prom_instantiate_rtas(); + prom_hold_cpus(); + } + + /* + * Fill in some infos for use by the kernel later on + */ + if (RELOC(ppc64_iommu_off)) + prom_setprop(_prom->chosen, "linux,iommu-off", NULL, 0); + if (RELOC(iommu_force_on)) + prom_setprop(_prom->chosen, "linux,iommu-force-on", NULL, 0); + + /* + * Now finally create the flattened device-tree + */ + prom_printf("copying OF device tree ...\n"); + flatten_device_tree(); + + /* + * Call OF "quiesce" method to shut down pending DMA's from + * devices etc... + */ + prom_printf("Calling quiesce ...\n"); + call_prom("quiesce", 0, 0); + + /* + * And finally, call the kernel passing it the flattened device + * tree and NULL as r5, thus triggering the new entry point which + * is common to us and kexec + */ + prom_printf("returning from prom_init\n"); + prom_debug("->dt_header_start=0x%x\n", RELOC(dt_header_start)); + prom_debug("->phys=0x%x\n", phys); + + __start(RELOC(dt_header_start), phys, 0); + + return 0; +} + diff --git a/arch/ppc64/kernel/u3_iommu.c b/arch/ppc64/kernel/u3_iommu.c new file mode 100644 index 000000000..2bb0b39f6 --- /dev/null +++ b/arch/ppc64/kernel/u3_iommu.c @@ -0,0 +1,321 @@ +/* + * arch/ppc64/kernel/u3_iommu.c + * + * Copyright (C) 2004 Olof Johansson , IBM Corporation + * + * Based on pSeries_iommu.c: + * Copyright (C) 2001 Mike Corrigan & Dave Engebretsen, IBM Corporation + * Copyright (C) 2004 Olof Johansson , IBM Corporation + * + * Dynamic DMA mapping support, Apple U3 & IBM CPC925 "DART" iommu. + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "pci.h" + +extern int iommu_force_on; + +/* physical base of DART registers */ +#define DART_BASE 0xf8033000UL + +/* Offset from base to control register */ +#define DARTCNTL 0 +/* Offset from base to exception register */ +#define DARTEXCP 0x10 +/* Offset from base to TLB tag registers */ +#define DARTTAG 0x1000 + + +/* Control Register fields */ + +/* base address of table (pfn) */ +#define DARTCNTL_BASE_MASK 0xfffff +#define DARTCNTL_BASE_SHIFT 12 + +#define DARTCNTL_FLUSHTLB 0x400 +#define DARTCNTL_ENABLE 0x200 + +/* size of table in pages */ +#define DARTCNTL_SIZE_MASK 0x1ff +#define DARTCNTL_SIZE_SHIFT 0 + +/* DART table fields */ +#define DARTMAP_VALID 0x80000000 +#define DARTMAP_RPNMASK 0x00ffffff + +/* Physical base address and size of the DART table */ +unsigned long dart_tablebase; /* exported to htab_initialize */ +static unsigned long dart_tablesize; + +/* Virtual base address of the DART table */ +static u32 *dart_vbase; + +/* Mapped base address for the dart */ +static unsigned int *dart; + +/* Dummy val that entries are set to when unused */ +static unsigned int dart_emptyval; + +static struct iommu_table iommu_table_u3; +static int dart_dirty; + +#define DBG(...) + +static inline void dart_tlb_invalidate_all(void) +{ + unsigned long l = 0; + unsigned int reg; + unsigned long limit; + + DBG("dart: flush\n"); + + /* To invalidate the DART, set the DARTCNTL_FLUSHTLB bit in the + * control register and wait for it to clear. + * + * Gotcha: Sometimes, the DART won't detect that the bit gets + * set. If so, clear it and set it again. + */ + + limit = 0; + +retry: + reg = in_be32((unsigned int *)dart+DARTCNTL); + reg |= DARTCNTL_FLUSHTLB; + out_be32((unsigned int *)dart+DARTCNTL, reg); + + l = 0; + while ((in_be32((unsigned int *)dart+DARTCNTL) & DARTCNTL_FLUSHTLB) && + l < (1L<it_base) + index; + + /* On U3, all memory is contigous, so we can move this + * out of the loop. + */ + while (npages--) { + rpn = virt_to_abs(uaddr) >> PAGE_SHIFT; + + *(dp++) = DARTMAP_VALID | (rpn & DARTMAP_RPNMASK); + + rpn++; + uaddr += PAGE_SIZE; + } + + dart_dirty = 1; +} + + +static void dart_free(struct iommu_table *tbl, long index, long npages) +{ + unsigned int *dp; + + /* We don't worry about flushing the TLB cache. The only drawback of + * not doing it is that we won't catch buggy device drivers doing + * bad DMAs, but then no 32-bit architecture ever does either. + */ + + DBG("dart: free at: %lx, %lx\n", index, npages); + + dp = ((unsigned int *)tbl->it_base) + index; + + while (npages--) + *(dp++) = dart_emptyval; +} + + +static int dart_init(struct device_node *dart_node) +{ + unsigned int regword; + unsigned int i; + unsigned long tmp; + struct page *p; + + if (dart_tablebase == 0 || dart_tablesize == 0) { + printk(KERN_INFO "U3-DART: table not allocated, using direct DMA\n"); + return -ENODEV; + } + + /* Make sure nothing from the DART range remains in the CPU cache + * from a previous mapping that existed before the kernel took + * over + */ + flush_dcache_phys_range(dart_tablebase, dart_tablebase + dart_tablesize); + + /* Allocate a spare page to map all invalid DART pages. We need to do + * that to work around what looks like a problem with the HT bridge + * prefetching into invalid pages and corrupting data + */ + tmp = __get_free_pages(GFP_ATOMIC, 1); + if (tmp == 0) + panic("U3-DART: Cannot allocate spare page !"); + dart_emptyval = DARTMAP_VALID | + ((virt_to_abs(tmp) >> PAGE_SHIFT) & DARTMAP_RPNMASK); + + /* Map in DART registers. FIXME: Use device node to get base address */ + dart = ioremap(DART_BASE, 0x7000); + if (dart == NULL) + panic("U3-DART: Cannot map registers !"); + + /* Set initial control register contents: table base, + * table size and enable bit + */ + regword = DARTCNTL_ENABLE | + ((dart_tablebase >> PAGE_SHIFT) << DARTCNTL_BASE_SHIFT) | + (((dart_tablesize >> PAGE_SHIFT) & DARTCNTL_SIZE_MASK) + << DARTCNTL_SIZE_SHIFT); + p = virt_to_page(dart_tablebase); + dart_vbase = ioremap(virt_to_abs(dart_tablebase), dart_tablesize); + + /* Fill initial table */ + for (i = 0; i < dart_tablesize/4; i++) + dart_vbase[i] = dart_emptyval; + + /* Initialize DART with table base and enable it. */ + out_be32((unsigned int *)dart, regword); + + /* Invalidate DART to get rid of possible stale TLBs */ + dart_tlb_invalidate_all(); + + iommu_table_u3.it_busno = 0; + + /* Units of tce entries */ + iommu_table_u3.it_offset = 0; + + /* Set the tce table size - measured in pages */ + iommu_table_u3.it_size = dart_tablesize >> PAGE_SHIFT; + + /* Initialize the common IOMMU code */ + iommu_table_u3.it_base = (unsigned long)dart_vbase; + iommu_table_u3.it_index = 0; + iommu_table_u3.it_blocksize = 1; + iommu_table_u3.it_entrysize = sizeof(u32); + iommu_init_table(&iommu_table_u3); + + /* Reserve the last page of the DART to avoid possible prefetch + * past the DART mapped area + */ + set_bit(iommu_table_u3.it_mapsize - 1, iommu_table_u3.it_map); + + printk(KERN_INFO "U3/CPC925 DART IOMMU initialized\n"); + + return 0; +} + +void iommu_setup_u3(void) +{ + struct pci_dev *dev = NULL; + struct device_node *dn; + + /* Find the DART in the device-tree */ + dn = of_find_compatible_node(NULL, "dart", "u3-dart"); + if (dn == NULL) + return; + + /* Setup low level TCE operations for the core IOMMU code */ + ppc_md.tce_build = dart_build; + ppc_md.tce_free = dart_free; + ppc_md.tce_flush = dart_flush; + + /* Initialize the DART HW */ + if (dart_init(dn)) + return; + + /* Setup pci_dma ops */ + pci_iommu_init(); + + /* We only have one iommu table on the mac for now, which makes + * things simple. Setup all PCI devices to point to this table + */ + while ((dev = pci_find_device(PCI_ANY_ID, PCI_ANY_ID, dev)) != NULL) { + /* We must use pci_device_to_OF_node() to make sure that + * we get the real "final" pointer to the device in the + * pci_dev sysdata and not the temporary PHB one + */ + struct device_node *dn = pci_device_to_OF_node(dev); + if (dn) + dn->iommu_table = &iommu_table_u3; + } +} + +void __init alloc_u3_dart_table(void) +{ + /* Only reserve DART space if machine has more than 2GB of RAM + * or if requested with iommu=on on cmdline. + */ + if (lmb_end_of_DRAM() <= 0x80000000ull && !iommu_force_on) + return; + + /* 512 pages (2MB) is max DART tablesize. */ + dart_tablesize = 1UL << 21; + /* 16MB (1 << 24) alignment. We allocate a full 16Mb chuck since we + * will blow up an entire large page anyway in the kernel mapping + */ + dart_tablebase = (unsigned long) + abs_to_virt(lmb_alloc_base(1UL<<24, 1UL<<24, 0x80000000L)); + + printk(KERN_INFO "U3-DART allocated at: %lx\n", dart_tablebase); +} diff --git a/arch/ppc64/kernel/vector.S b/arch/ppc64/kernel/vector.S index 940cd7287..b79d33e40 100644 --- a/arch/ppc64/kernel/vector.S +++ b/arch/ppc64/kernel/vector.S @@ -89,7 +89,7 @@ _GLOBAL(vmaddfp) 1: lfsx fr0,r4,r7 lfsx fr1,r5,r7 lfsx fr2,r6,r7 - fmadds fr0,fr0,fr1,fr2 + fmadds fr0,fr0,fr2,fr1 stfsx fr0,r3,r7 addi r7,r7,4 bdnz 1b @@ -109,7 +109,7 @@ _GLOBAL(vnmsubfp) 1: lfsx fr0,r4,r7 lfsx fr1,r5,r7 lfsx fr2,r6,r7 - fnmsubs fr0,fr0,fr1,fr2 + fnmsubs fr0,fr0,fr2,fr1 stfsx fr0,r3,r7 addi r7,r7,4 bdnz 1b diff --git a/arch/ppc64/kernel/vmlinux.lds.S b/arch/ppc64/kernel/vmlinux.lds.S index 1d9b61143..4103cc13f 100644 --- a/arch/ppc64/kernel/vmlinux.lds.S +++ b/arch/ppc64/kernel/vmlinux.lds.S @@ -14,6 +14,7 @@ SECTIONS .text : { *(.text .text.*) SCHED_TEXT + LOCK_TEXT *(.fixup) . = ALIGN(4096); _etext = .; @@ -61,12 +62,6 @@ SECTIONS __setup_end = .; } - __param : { - __start___param = .; - *(__param) - __stop___param = .; - } - .initcall.init : { __initcall_start = .; *(.initcall1.init) @@ -122,10 +117,13 @@ SECTIONS .data : { *(.data .data.rel* .toc1) - *(.opd) *(.branch_lt) } + .opd : { + *(.opd) + } + .got : { __toc_start = .; *(.got) diff --git a/arch/ppc64/lib/checksum.S b/arch/ppc64/lib/checksum.S index 4ac6a63ca..ef96c6c58 100644 --- a/arch/ppc64/lib/checksum.S +++ b/arch/ppc64/lib/checksum.S @@ -92,19 +92,19 @@ _GLOBAL(csum_partial) adde r5,r5,r6 bdnz 2b andi. r4,r4,7 /* compute bytes left to sum after doublewords */ -3: cmpi 0,r4,4 /* is at least a full word left? */ +3: cmpwi 0,r4,4 /* is at least a full word left? */ blt 4f lwz r6,8(r3) /* sum this word */ addi r3,r3,4 subi r4,r4,4 adde r5,r5,r6 -4: cmpi 0,r4,2 /* is at least a halfword left? */ +4: cmpwi 0,r4,2 /* is at least a halfword left? */ blt+ 5f lhz r6,8(r3) /* sum this halfword */ addi r3,r3,2 subi r4,r4,2 adde r5,r5,r6 -5: cmpi 0,r4,1 /* is at least a byte left? */ +5: cmpwi 0,r4,1 /* is at least a byte left? */ bne+ 6f lbz r6,8(r3) /* sum this byte */ slwi r6,r6,8 /* this byte is assumed to be the upper byte of a halfword */ @@ -150,7 +150,7 @@ _GLOBAL(csum_partial_copy_generic) adde r0,r0,r6 bdnz 82b andi. r5,r5,3 -3: cmpi 0,r5,2 +3: cmpwi 0,r5,2 blt+ 4f 83: lhz r6,4(r3) addi r3,r3,2 @@ -158,7 +158,7 @@ _GLOBAL(csum_partial_copy_generic) 93: sth r6,4(r4) addi r4,r4,2 adde r0,r0,r6 -4: cmpi 0,r5,1 +4: cmpwi 0,r5,1 bne+ 5f 84: lbz r6,4(r3) 94: stb r6,4(r4) @@ -198,7 +198,7 @@ src_error_3: bdnz 97b .globl src_error src_error: - cmpi 0,r7,0 + cmpdi 0,r7,0 beq 1f li r6,-EFAULT stw r6,0(r7) @@ -207,7 +207,7 @@ src_error: .globl dst_error dst_error: - cmpi 0,r8,0 + cmpdi 0,r8,0 beq 1f li r6,-EFAULT stw r6,0(r8) diff --git a/arch/ppc64/lib/locks.c b/arch/ppc64/lib/locks.c index 58e973cc8..72d642d7e 100644 --- a/arch/ppc64/lib/locks.c +++ b/arch/ppc64/lib/locks.c @@ -20,28 +20,9 @@ #include #include -#ifndef CONFIG_SPINLINE - -/* - * On a system with shared processors (that is, where a physical - * processor is multiplexed between several virtual processors), - * there is no point spinning on a lock if the holder of the lock - * isn't currently scheduled on a physical processor. Instead - * we detect this situation and ask the hypervisor to give the - * rest of our timeslice to the lock holder. - * - * So that we can tell which virtual processor is holding a lock, - * we put 0x80000000 | smp_processor_id() in the lock when it is - * held. Conveniently, we have a word in the paca that holds this - * value. - */ - /* waiting for a spinlock... */ #if defined(CONFIG_PPC_SPLPAR) || defined(CONFIG_PPC_ISERIES) -/* We only yield to the hypervisor if we are in shared processor mode */ -#define SHARED_PROCESSOR (get_paca()->lppaca.xSharedProc) - void __spin_yield(spinlock_t *lock) { unsigned int lock_value, holder_cpu, yield_count; @@ -63,100 +44,16 @@ void __spin_yield(spinlock_t *lock) HvCall2(HvCallBaseYieldProcessor, HvCall_YieldToProc, ((u64)holder_cpu << 32) | yield_count); #else - plpar_hcall_norets(H_CONFER, holder_cpu, yield_count); + plpar_hcall_norets(H_CONFER, get_hard_smp_processor_id(holder_cpu), + yield_count); #endif } -#else /* SPLPAR || ISERIES */ -#define __spin_yield(x) barrier() -#define SHARED_PROCESSOR 0 -#endif - -/* - * This returns the old value in the lock, so we succeeded - * in getting the lock if the return value is 0. - */ -static __inline__ unsigned long __spin_trylock(spinlock_t *lock) -{ - unsigned long tmp, tmp2; - - __asm__ __volatile__( -" lwz %1,%3(13) # __spin_trylock\n\ -1: lwarx %0,0,%2\n\ - cmpwi 0,%0,0\n\ - bne- 2f\n\ - stwcx. %1,0,%2\n\ - bne- 1b\n\ - isync\n\ -2:" : "=&r" (tmp), "=&r" (tmp2) - : "r" (&lock->lock), "i" (offsetof(struct paca_struct, lock_token)) - : "cr0", "memory"); - - return tmp; -} - -int _raw_spin_trylock(spinlock_t *lock) -{ - return __spin_trylock(lock) == 0; -} - -EXPORT_SYMBOL(_raw_spin_trylock); - -void _raw_spin_lock(spinlock_t *lock) -{ - while (1) { - if (likely(__spin_trylock(lock) == 0)) - break; - do { - HMT_low(); - if (SHARED_PROCESSOR) - __spin_yield(lock); - } while (likely(lock->lock != 0)); - HMT_medium(); - } -} - -EXPORT_SYMBOL(_raw_spin_lock); - -void _raw_spin_lock_flags(spinlock_t *lock, unsigned long flags) -{ - unsigned long flags_dis; - - while (1) { - if (likely(__spin_trylock(lock) == 0)) - break; - local_save_flags(flags_dis); - local_irq_restore(flags); - do { - HMT_low(); - if (SHARED_PROCESSOR) - __spin_yield(lock); - } while (likely(lock->lock != 0)); - HMT_medium(); - local_irq_restore(flags_dis); - } -} - -EXPORT_SYMBOL(_raw_spin_lock_flags); - -void spin_unlock_wait(spinlock_t *lock) -{ - while (lock->lock) { - HMT_low(); - if (SHARED_PROCESSOR) - __spin_yield(lock); - } - HMT_medium(); -} - -EXPORT_SYMBOL(spin_unlock_wait); - /* * Waiting for a read lock or a write lock on a rwlock... * This turns out to be the same for read and write locks, since * we only know the holder if it is write-locked. */ -#if defined(CONFIG_PPC_SPLPAR) || defined(CONFIG_PPC_ISERIES) void __rw_yield(rwlock_t *rw) { int lock_value; @@ -179,121 +76,20 @@ void __rw_yield(rwlock_t *rw) HvCall2(HvCallBaseYieldProcessor, HvCall_YieldToProc, ((u64)holder_cpu << 32) | yield_count); #else - plpar_hcall_norets(H_CONFER, holder_cpu, yield_count); + plpar_hcall_norets(H_CONFER, get_hard_smp_processor_id(holder_cpu), + yield_count); #endif } - -#else /* SPLPAR || ISERIES */ -#define __rw_yield(x) barrier() #endif -/* - * This returns the old value in the lock + 1, - * so we got a read lock if the return value is > 0. - */ -static __inline__ long __read_trylock(rwlock_t *rw) -{ - long tmp; - - __asm__ __volatile__( -"1: lwarx %0,0,%1 # read_trylock\n\ - extsw %0,%0\n\ - addic. %0,%0,1\n\ - ble- 2f\n\ - stwcx. %0,0,%1\n\ - bne- 1b\n\ - isync\n\ -2:" : "=&r" (tmp) - : "r" (&rw->lock) - : "cr0", "xer", "memory"); - - return tmp; -} - -int _raw_read_trylock(rwlock_t *rw) -{ - return __read_trylock(rw) > 0; -} - -EXPORT_SYMBOL(_raw_read_trylock); - -void _raw_read_lock(rwlock_t *rw) -{ - while (1) { - if (likely(__read_trylock(rw) > 0)) - break; - do { - HMT_low(); - if (SHARED_PROCESSOR) - __rw_yield(rw); - } while (likely(rw->lock < 0)); - HMT_medium(); - } -} - -EXPORT_SYMBOL(_raw_read_lock); - -void _raw_read_unlock(rwlock_t *rw) -{ - long tmp; - - __asm__ __volatile__( - "eieio # read_unlock\n\ -1: lwarx %0,0,%1\n\ - addic %0,%0,-1\n\ - stwcx. %0,0,%1\n\ - bne- 1b" - : "=&r"(tmp) - : "r"(&rw->lock) - : "cr0", "memory"); -} - -EXPORT_SYMBOL(_raw_read_unlock); - -/* - * This returns the old value in the lock, - * so we got the write lock if the return value is 0. - */ -static __inline__ long __write_trylock(rwlock_t *rw) -{ - long tmp, tmp2; - - __asm__ __volatile__( -" lwz %1,%3(13) # write_trylock\n\ -1: lwarx %0,0,%2\n\ - cmpwi 0,%0,0\n\ - bne- 2f\n\ - stwcx. %1,0,%2\n\ - bne- 1b\n\ - isync\n\ -2:" : "=&r" (tmp), "=&r" (tmp2) - : "r" (&rw->lock), "i" (offsetof(struct paca_struct, lock_token)) - : "cr0", "memory"); - - return tmp; -} - -int _raw_write_trylock(rwlock_t *rw) -{ - return __write_trylock(rw) == 0; -} - -EXPORT_SYMBOL(_raw_write_trylock); - -void _raw_write_lock(rwlock_t *rw) +void spin_unlock_wait(spinlock_t *lock) { - while (1) { - if (likely(__write_trylock(rw) == 0)) - break; - do { - HMT_low(); - if (SHARED_PROCESSOR) - __rw_yield(rw); - } while (likely(rw->lock != 0)); - HMT_medium(); + while (lock->lock) { + HMT_low(); + if (SHARED_PROCESSOR) + __spin_yield(lock); } + HMT_medium(); } -EXPORT_SYMBOL(_raw_write_lock); - -#endif /* CONFIG_SPINLINE */ +EXPORT_SYMBOL(spin_unlock_wait); diff --git a/arch/ppc64/mm/hash_low.S b/arch/ppc64/mm/hash_low.S index 0d6b5c29b..68dd4b9ad 100644 --- a/arch/ppc64/mm/hash_low.S +++ b/arch/ppc64/mm/hash_low.S @@ -172,9 +172,9 @@ htab_insert_pte: li r9,0 _GLOBAL(htab_call_hpte_insert1) bl . /* Will be patched by htab_finish_init() */ - cmpi 0,r3,0 + cmpdi 0,r3,0 bge htab_pte_insert_ok /* Insertion successful */ - cmpi 0,r3,-2 /* Critical failure */ + cmpdi 0,r3,-2 /* Critical failure */ beq- htab_pte_insert_failure /* Now try secondary slot */ @@ -194,9 +194,9 @@ _GLOBAL(htab_call_hpte_insert1) li r9,0 _GLOBAL(htab_call_hpte_insert2) bl . /* Will be patched by htab_finish_init() */ - cmpi 0,r3,0 + cmpdi 0,r3,0 bge+ htab_pte_insert_ok /* Insertion successful */ - cmpi 0,r3,-2 /* Critical failure */ + cmpdi 0,r3,-2 /* Critical failure */ beq- htab_pte_insert_failure /* Both are full, we need to evict something */ @@ -263,7 +263,7 @@ _GLOBAL(htab_call_hpte_updatepp) /* if we failed because typically the HPTE wasn't really here * we try an insertion. */ - cmpi 0,r3,-1 + cmpdi 0,r3,-1 beq- htab_insert_pte /* Clear the BUSY bit and Write out the PTE */ @@ -278,6 +278,10 @@ htab_wrong_access: b bail htab_pte_insert_failure: - b .htab_insert_failure + /* Bail out restoring old PTE */ + ld r6,STK_PARM(r6)(r1) + std r31,0(r6) + li r3,-1 + b bail diff --git a/arch/ppc64/mm/hash_native.c b/arch/ppc64/mm/hash_native.c new file mode 100644 index 000000000..0d8c8ab19 --- /dev/null +++ b/arch/ppc64/mm/hash_native.c @@ -0,0 +1,419 @@ +/* + * native hashtable management. + * + * SMP scalability work: + * Copyright (C) 2001 Anton Blanchard , IBM + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the 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 +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#define HPTE_LOCK_BIT 3 + +static spinlock_t native_tlbie_lock = SPIN_LOCK_UNLOCKED; + +static inline void native_lock_hpte(HPTE *hptep) +{ + unsigned long *word = &hptep->dw0.dword0; + + while (1) { + if (!test_and_set_bit(HPTE_LOCK_BIT, word)) + break; + while(test_bit(HPTE_LOCK_BIT, word)) + cpu_relax(); + } +} + +static inline void native_unlock_hpte(HPTE *hptep) +{ + unsigned long *word = &hptep->dw0.dword0; + + asm volatile("lwsync":::"memory"); + clear_bit(HPTE_LOCK_BIT, word); +} + +long native_hpte_insert(unsigned long hpte_group, unsigned long va, + unsigned long prpn, int secondary, + unsigned long hpteflags, int bolted, int large) +{ + unsigned long arpn = physRpn_to_absRpn(prpn); + HPTE *hptep = htab_data.htab + hpte_group; + Hpte_dword0 dw0; + HPTE lhpte; + int i; + + for (i = 0; i < HPTES_PER_GROUP; i++) { + dw0 = hptep->dw0.dw0; + + if (!dw0.v) { + /* retry with lock held */ + native_lock_hpte(hptep); + dw0 = hptep->dw0.dw0; + if (!dw0.v) + break; + native_unlock_hpte(hptep); + } + + hptep++; + } + + if (i == HPTES_PER_GROUP) + return -1; + + lhpte.dw1.dword1 = 0; + lhpte.dw1.dw1.rpn = arpn; + lhpte.dw1.flags.flags = hpteflags; + + lhpte.dw0.dword0 = 0; + lhpte.dw0.dw0.avpn = va >> 23; + lhpte.dw0.dw0.h = secondary; + lhpte.dw0.dw0.bolted = bolted; + lhpte.dw0.dw0.v = 1; + + if (large) { + lhpte.dw0.dw0.l = 1; + lhpte.dw0.dw0.avpn &= ~0x1UL; + } + + hptep->dw1.dword1 = lhpte.dw1.dword1; + + /* Guarantee the second dword is visible before the valid bit */ + __asm__ __volatile__ ("eieio" : : : "memory"); + + /* + * Now set the first dword including the valid bit + * NOTE: this also unlocks the hpte + */ + hptep->dw0.dword0 = lhpte.dw0.dword0; + + __asm__ __volatile__ ("ptesync" : : : "memory"); + + return i | (secondary << 3); +} + +static long native_hpte_remove(unsigned long hpte_group) +{ + HPTE *hptep; + Hpte_dword0 dw0; + int i; + int slot_offset; + + /* pick a random entry to start at */ + slot_offset = mftb() & 0x7; + + for (i = 0; i < HPTES_PER_GROUP; i++) { + hptep = htab_data.htab + hpte_group + slot_offset; + dw0 = hptep->dw0.dw0; + + if (dw0.v && !dw0.bolted) { + /* retry with lock held */ + native_lock_hpte(hptep); + dw0 = hptep->dw0.dw0; + if (dw0.v && !dw0.bolted) + break; + native_unlock_hpte(hptep); + } + + slot_offset++; + slot_offset &= 0x7; + } + + if (i == HPTES_PER_GROUP) + return -1; + + /* Invalidate the hpte. NOTE: this also unlocks it */ + hptep->dw0.dword0 = 0; + + return i; +} + +static inline void set_pp_bit(unsigned long pp, HPTE *addr) +{ + unsigned long old; + unsigned long *p = &addr->dw1.dword1; + + __asm__ __volatile__( + "1: ldarx %0,0,%3\n\ + rldimi %0,%2,0,61\n\ + stdcx. %0,0,%3\n\ + bne 1b" + : "=&r" (old), "=m" (*p) + : "r" (pp), "r" (p), "m" (*p) + : "cc"); +} + +/* + * Only works on small pages. Yes its ugly to have to check each slot in + * the group but we only use this during bootup. + */ +static long native_hpte_find(unsigned long vpn) +{ + HPTE *hptep; + unsigned long hash; + unsigned long i, j; + long slot; + Hpte_dword0 dw0; + + hash = hpt_hash(vpn, 0); + + for (j = 0; j < 2; j++) { + slot = (hash & htab_data.htab_hash_mask) * HPTES_PER_GROUP; + for (i = 0; i < HPTES_PER_GROUP; i++) { + hptep = htab_data.htab + slot; + dw0 = hptep->dw0.dw0; + + if ((dw0.avpn == (vpn >> 11)) && dw0.v && + (dw0.h == j)) { + /* HPTE matches */ + if (j) + slot = -slot; + return slot; + } + ++slot; + } + hash = ~hash; + } + + return -1; +} + +static long native_hpte_updatepp(unsigned long slot, unsigned long newpp, + unsigned long va, int large, int local) +{ + HPTE *hptep = htab_data.htab + slot; + Hpte_dword0 dw0; + unsigned long avpn = va >> 23; + int ret = 0; + + if (large) + avpn &= ~0x1UL; + + native_lock_hpte(hptep); + + dw0 = hptep->dw0.dw0; + + /* Even if we miss, we need to invalidate the TLB */ + if ((dw0.avpn != avpn) || !dw0.v) { + native_unlock_hpte(hptep); + ret = -1; + } else { + set_pp_bit(newpp, hptep); + native_unlock_hpte(hptep); + } + + /* Ensure it is out of the tlb too */ + if ((cur_cpu_spec->cpu_features & CPU_FTR_TLBIEL) && !large && local) { + tlbiel(va); + } else { + int lock_tlbie = !(cur_cpu_spec->cpu_features & CPU_FTR_LOCKLESS_TLBIE); + + if (lock_tlbie) + spin_lock(&native_tlbie_lock); + tlbie(va, large); + if (lock_tlbie) + spin_unlock(&native_tlbie_lock); + } + + return ret; +} + +/* + * Update the page protection bits. Intended to be used to create + * guard pages for kernel data structures on pages which are bolted + * in the HPT. Assumes pages being operated on will not be stolen. + * Does not work on large pages. + * + * No need to lock here because we should be the only user. + */ +static void native_hpte_updateboltedpp(unsigned long newpp, unsigned long ea) +{ + unsigned long vsid, va, vpn, flags; + long slot; + HPTE *hptep; + int lock_tlbie = !(cur_cpu_spec->cpu_features & CPU_FTR_LOCKLESS_TLBIE); + + vsid = get_kernel_vsid(ea); + va = (vsid << 28) | (ea & 0x0fffffff); + vpn = va >> PAGE_SHIFT; + + slot = native_hpte_find(vpn); + if (slot == -1) + panic("could not find page to bolt\n"); + hptep = htab_data.htab + slot; + + set_pp_bit(newpp, hptep); + + /* Ensure it is out of the tlb too */ + if (lock_tlbie) + spin_lock_irqsave(&native_tlbie_lock, flags); + tlbie(va, 0); + if (lock_tlbie) + spin_unlock_irqrestore(&native_tlbie_lock, flags); +} + +static void native_hpte_invalidate(unsigned long slot, unsigned long va, + int large, int local) +{ + HPTE *hptep = htab_data.htab + slot; + Hpte_dword0 dw0; + unsigned long avpn = va >> 23; + unsigned long flags; + int lock_tlbie = !(cur_cpu_spec->cpu_features & CPU_FTR_LOCKLESS_TLBIE); + + if (large) + avpn &= ~0x1UL; + + local_irq_save(flags); + native_lock_hpte(hptep); + + dw0 = hptep->dw0.dw0; + + /* Even if we miss, we need to invalidate the TLB */ + if ((dw0.avpn != avpn) || !dw0.v) { + native_unlock_hpte(hptep); + } else { + /* Invalidate the hpte. NOTE: this also unlocks it */ + hptep->dw0.dword0 = 0; + } + + /* Invalidate the tlb */ + if ((cur_cpu_spec->cpu_features & CPU_FTR_TLBIEL) && !large && local) { + tlbiel(va); + } else { + if (lock_tlbie) + spin_lock(&native_tlbie_lock); + tlbie(va, large); + if (lock_tlbie) + spin_unlock(&native_tlbie_lock); + } + local_irq_restore(flags); +} + +static void native_flush_hash_range(unsigned long context, + unsigned long number, int local) +{ + unsigned long vsid, vpn, va, hash, secondary, slot, flags, avpn; + int i, j; + HPTE *hptep; + Hpte_dword0 dw0; + struct ppc64_tlb_batch *batch = &__get_cpu_var(ppc64_tlb_batch); + + /* XXX fix for large ptes */ + unsigned long large = 0; + + local_irq_save(flags); + + j = 0; + for (i = 0; i < number; i++) { + if ((batch->addr[i] >= USER_START) && + (batch->addr[i] <= USER_END)) + vsid = get_vsid(context, batch->addr[i]); + else + vsid = get_kernel_vsid(batch->addr[i]); + + va = (vsid << 28) | (batch->addr[i] & 0x0fffffff); + batch->vaddr[j] = va; + if (large) + vpn = va >> HPAGE_SHIFT; + else + vpn = va >> PAGE_SHIFT; + hash = hpt_hash(vpn, large); + secondary = (pte_val(batch->pte[i]) & _PAGE_SECONDARY) >> 15; + if (secondary) + hash = ~hash; + slot = (hash & htab_data.htab_hash_mask) * HPTES_PER_GROUP; + slot += (pte_val(batch->pte[i]) & _PAGE_GROUP_IX) >> 12; + + hptep = htab_data.htab + slot; + + avpn = va >> 23; + if (large) + avpn &= ~0x1UL; + + native_lock_hpte(hptep); + + dw0 = hptep->dw0.dw0; + + /* Even if we miss, we need to invalidate the TLB */ + if ((dw0.avpn != avpn) || !dw0.v) { + native_unlock_hpte(hptep); + } else { + /* Invalidate the hpte. NOTE: this also unlocks it */ + hptep->dw0.dword0 = 0; + } + + j++; + } + + if ((cur_cpu_spec->cpu_features & CPU_FTR_TLBIEL) && !large && local) { + asm volatile("ptesync":::"memory"); + + for (i = 0; i < j; i++) + __tlbiel(batch->vaddr[i]); + + asm volatile("ptesync":::"memory"); + } else { + int lock_tlbie = !(cur_cpu_spec->cpu_features & CPU_FTR_LOCKLESS_TLBIE); + + if (lock_tlbie) + spin_lock(&native_tlbie_lock); + + asm volatile("ptesync":::"memory"); + + for (i = 0; i < j; i++) + __tlbie(batch->vaddr[i], 0); + + asm volatile("eieio; tlbsync; ptesync":::"memory"); + + if (lock_tlbie) + spin_unlock(&native_tlbie_lock); + } + + local_irq_restore(flags); +} + +void hpte_init_native(void) +{ +#ifdef CONFIG_PPC_PSERIES + struct device_node *root; + const char *model; +#endif /* CONFIG_PPC_PSERIES */ + + ppc_md.hpte_invalidate = native_hpte_invalidate; + ppc_md.hpte_updatepp = native_hpte_updatepp; + ppc_md.hpte_updateboltedpp = native_hpte_updateboltedpp; + ppc_md.hpte_insert = native_hpte_insert; + ppc_md.hpte_remove = native_hpte_remove; + +#ifdef CONFIG_PPC_PSERIES + /* Disable TLB batching on nighthawk */ + root = of_find_node_by_path("/"); + if (root) { + model = get_property(root, "model", NULL); + if (!strcmp(model, "CHRP IBM,9076-N81")) { + of_node_put(root); + goto bail; + } + of_node_put(root); + } +#endif /* CONFIG_PPC_PSERIES */ + + ppc_md.flush_hash_range = native_flush_hash_range; + bail: + htab_finish_init(); +} diff --git a/arch/ppc64/mm/imalloc.c b/arch/ppc64/mm/imalloc.c index 2399f3fcb..9d92b0d9c 100644 --- a/arch/ppc64/mm/imalloc.c +++ b/arch/ppc64/mm/imalloc.c @@ -37,33 +37,51 @@ static int get_free_im_addr(unsigned long size, unsigned long *im_addr) return 0; } +/* Return whether the region described by v_addr and size is a subset + * of the region described by parent + */ +static inline int im_region_is_subset(unsigned long v_addr, unsigned long size, + struct vm_struct *parent) +{ + return (int) (v_addr >= (unsigned long) parent->addr && + v_addr < (unsigned long) parent->addr + parent->size && + size < parent->size); +} + +/* Return whether the region described by v_addr and size is a superset + * of the region described by child + */ +static int im_region_is_superset(unsigned long v_addr, unsigned long size, + struct vm_struct *child) +{ + struct vm_struct parent; + + parent.addr = (void *) v_addr; + parent.size = size; + + return im_region_is_subset((unsigned long) child->addr, child->size, + &parent); +} + /* Return whether the region described by v_addr and size overlaps - * the region described by vm. Overlapping regions meet the + * the region described by vm. Overlapping regions meet the * following conditions: * 1) The regions share some part of the address space * 2) The regions aren't identical - * 3) The first region is not a subset of the second + * 3) Neither region is a subset of the other */ -static inline int im_region_overlaps(unsigned long v_addr, unsigned long size, +static int im_region_overlaps(unsigned long v_addr, unsigned long size, struct vm_struct *vm) { + if (im_region_is_superset(v_addr, size, vm)) + return 0; + return (v_addr + size > (unsigned long) vm->addr + vm->size && v_addr < (unsigned long) vm->addr + vm->size) || (v_addr < (unsigned long) vm->addr && v_addr + size > (unsigned long) vm->addr); } -/* Return whether the region described by v_addr and size is a subset - * of the region described by vm - */ -static inline int im_region_is_subset(unsigned long v_addr, unsigned long size, - struct vm_struct *vm) -{ - return (int) (v_addr >= (unsigned long) vm->addr && - v_addr < (unsigned long) vm->addr + vm->size && - size < vm->size); -} - /* Determine imalloc status of region described by v_addr and size. * Can return one of the following: * IM_REGION_UNUSED - Entire region is unallocated in imalloc space. @@ -73,28 +91,37 @@ static inline int im_region_is_subset(unsigned long v_addr, unsigned long size, * IM_REGION_EXISTS - Exact region already allocated in imalloc space. * vm will be assigned to a ptr to the existing imlist * member. - * IM_REGION_OVERLAPS - A portion of the region is already allocated in - * imalloc space. + * IM_REGION_OVERLAPS - Region overlaps an allocated region in imalloc space. + * IM_REGION_SUPERSET - Region is a superset of a region that is already + * allocated in imalloc space. */ -static int im_region_status(unsigned long v_addr, unsigned long size, +static int im_region_status(unsigned long v_addr, unsigned long size, struct vm_struct **vm) { struct vm_struct *tmp; - for (tmp = imlist; tmp; tmp = tmp->next) - if (v_addr < (unsigned long) tmp->addr + tmp->size) + for (tmp = imlist; tmp; tmp = tmp->next) + if (v_addr < (unsigned long) tmp->addr + tmp->size) break; - + if (tmp) { if (im_region_overlaps(v_addr, size, tmp)) return IM_REGION_OVERLAP; *vm = tmp; - if (im_region_is_subset(v_addr, size, tmp)) + if (im_region_is_subset(v_addr, size, tmp)) { + /* Return with tmp pointing to superset */ return IM_REGION_SUBSET; - else if (v_addr == (unsigned long) tmp->addr && - size == tmp->size) + } + if (im_region_is_superset(v_addr, size, tmp)) { + /* Return with tmp pointing to first subset */ + return IM_REGION_SUPERSET; + } + else if (v_addr == (unsigned long) tmp->addr && + size == tmp->size) { + /* Return with tmp pointing to exact region */ return IM_REGION_EXISTS; + } } *vm = NULL; @@ -208,6 +235,10 @@ static struct vm_struct * __im_get_area(unsigned long req_addr, tmp = split_im_region(req_addr, size, tmp); break; case IM_REGION_EXISTS: + /* Return requested region */ + break; + case IM_REGION_SUPERSET: + /* Return first existing subset of requested region */ break; default: printk(KERN_ERR "%s() unexpected imalloc region status\n", diff --git a/arch/ppc64/mm/mmap.c b/arch/ppc64/mm/mmap.c index 5ebf68668..f90dd1f7a 100644 --- a/arch/ppc64/mm/mmap.c +++ b/arch/ppc64/mm/mmap.c @@ -54,13 +54,13 @@ static inline int mmap_is_legacy(void) */ if (!test_thread_flag(TIF_32BIT)) return 1; - - if (current->personality & ADDR_COMPAT_LAYOUT) + + if (current->personality & ADDR_COMPAT_LAYOUT) return 1; - + if (current->rlim[RLIMIT_STACK].rlim_cur == RLIM_INFINITY) return 1; - + return sysctl_legacy_va_layout; } @@ -81,7 +81,6 @@ void arch_pick_mmap_layout(struct mm_struct *mm) } else { mm->mmap_base = mmap_base(); mm->get_unmapped_area = arch_get_unmapped_area_topdown; - mm->get_unmapped_exec_area = arch_get_unmapped_exec_area; mm->unmap_area = arch_unmap_area_topdown; } } diff --git a/arch/ppc64/mm/slb.c b/arch/ppc64/mm/slb.c index bc6125804..7424d8d24 100644 --- a/arch/ppc64/mm/slb.c +++ b/arch/ppc64/mm/slb.c @@ -24,32 +24,55 @@ extern void slb_allocate(unsigned long ea); +static inline unsigned long mk_esid_data(unsigned long ea, unsigned long slot) +{ + return (ea & ESID_MASK) | SLB_ESID_V | slot; +} + +static inline unsigned long mk_vsid_data(unsigned long ea, unsigned long flags) +{ + return (get_kernel_vsid(ea) << SLB_VSID_SHIFT) | flags; +} + static inline void create_slbe(unsigned long ea, unsigned long vsid, unsigned long flags, unsigned long entry) { - ea = (ea & ESID_MASK) | SLB_ESID_V | entry; - vsid = (vsid << SLB_VSID_SHIFT) | flags; asm volatile("slbmte %0,%1" : - : "r" (vsid), "r" (ea) + : "r" (mk_vsid_data(ea, flags)), + "r" (mk_esid_data(ea, entry)) : "memory" ); } -static void slb_add_bolted(void) +static void slb_flush_and_rebolt(void) { -#ifndef CONFIG_PPC_ISERIES - WARN_ON(!irqs_disabled()); - /* If you change this make sure you change SLB_NUM_BOLTED - * appropriately too */ + * appropriately too. */ + unsigned long ksp_flags = SLB_VSID_KERNEL; + unsigned long ksp_esid_data; - /* Slot 1 - first VMALLOC segment - * Since modules end up there it gets hit very heavily. - */ - create_slbe(VMALLOCBASE, get_kernel_vsid(VMALLOCBASE), - SLB_VSID_KERNEL, 1); + WARN_ON(!irqs_disabled()); - asm volatile("isync":::"memory"); -#endif + if (cur_cpu_spec->cpu_features & CPU_FTR_16M_PAGE) + ksp_flags |= SLB_VSID_L; + + ksp_esid_data = mk_esid_data(get_paca()->kstack, 2); + if ((ksp_esid_data & ESID_MASK) == KERNELBASE) + ksp_esid_data &= ~SLB_ESID_V; + + /* We need to do this all in asm, so we're sure we don't touch + * the stack between the slbia and rebolting it. */ + asm volatile("isync\n" + "slbia\n" + /* Slot 1 - first VMALLOC segment */ + "slbmte %0,%1\n" + /* Slot 2 - kernel stack */ + "slbmte %2,%3\n" + "isync" + :: "r"(mk_vsid_data(VMALLOCBASE, SLB_VSID_KERNEL)), + "r"(mk_esid_data(VMALLOCBASE, 1)), + "r"(mk_vsid_data(ksp_esid_data, ksp_flags)), + "r"(ksp_esid_data) + : "memory"); } /* Flush all user entries from the segment table of the current processor. */ @@ -71,8 +94,7 @@ void switch_slb(struct task_struct *tsk, struct mm_struct *mm) } asm volatile("isync" : : : "memory"); } else { - asm volatile("isync; slbia; isync" : : : "memory"); - slb_add_bolted(); + slb_flush_and_rebolt(); } /* Workaround POWER5 < DD2.1 issue */ @@ -115,22 +137,27 @@ void switch_slb(struct task_struct *tsk, struct mm_struct *mm) void slb_initialize(void) { -#ifdef CONFIG_PPC_ISERIES - asm volatile("isync; slbia; isync":::"memory"); -#else + /* On iSeries the bolted entries have already been set up by + * the hypervisor from the lparMap data in head.S */ +#ifndef CONFIG_PPC_ISERIES unsigned long flags = SLB_VSID_KERNEL; - /* Invalidate the entire SLB (even slot 0) & all the ERATS */ - if (cur_cpu_spec->cpu_features & CPU_FTR_16M_PAGE) - flags |= SLB_VSID_L; + /* Invalidate the entire SLB (even slot 0) & all the ERATS */ + if (cur_cpu_spec->cpu_features & CPU_FTR_16M_PAGE) + flags |= SLB_VSID_L; - asm volatile("isync":::"memory"); - asm volatile("slbmte %0,%0"::"r" (0) : "memory"); + asm volatile("isync":::"memory"); + asm volatile("slbmte %0,%0"::"r" (0) : "memory"); asm volatile("isync; slbia; isync":::"memory"); - create_slbe(KERNELBASE, get_kernel_vsid(KERNELBASE), - flags, 0); - + create_slbe(KERNELBASE, get_kernel_vsid(KERNELBASE), flags, 0); + create_slbe(VMALLOCBASE, get_kernel_vsid(KERNELBASE), + SLB_VSID_KERNEL, 1); + /* We don't bolt the stack for the time being - we're in boot, + * so the stack is in the bolted segment. By the time it goes + * elsewhere, we'll call _switch() which will bolt in the new + * one. */ + asm volatile("isync":::"memory"); #endif - slb_add_bolted(); + get_paca()->stab_rr = SLB_NUM_BOLTED; } diff --git a/arch/ppc64/mm/slb_low.S b/arch/ppc64/mm/slb_low.S index 4b3dfe091..8379d678f 100644 --- a/arch/ppc64/mm/slb_low.S +++ b/arch/ppc64/mm/slb_low.S @@ -37,8 +37,21 @@ _GLOBAL(slb_allocate) * a free slot first but that took too long. Unfortunately we * dont have any LRU information to help us choose a slot. */ +#ifdef CONFIG_PPC_ISERIES + /* + * On iSeries, the "bolted" stack segment can be cast out on + * shared processor switch so we need to check for a miss on + * it and restore it to the right slot. + */ + ld r9,PACAKSAVE(r13) + clrrdi r9,r9,28 + clrrdi r11,r3,28 + li r10,SLB_NUM_BOLTED-1 /* Stack goes in last bolted slot */ + cmpld r9,r11 + beq 3f +#endif /* CONFIG_PPC_ISERIES */ + ld r10,PACASTABRR(r13) -3: addi r10,r10,1 /* use a cpu feature mask if we ever change our slb size */ cmpldi r10,SLB_NUM_ENTRIES @@ -46,55 +59,28 @@ _GLOBAL(slb_allocate) blt+ 4f li r10,SLB_NUM_BOLTED - /* - * Never cast out the segment for our kernel stack. Since we - * dont invalidate the ERAT we could have a valid translation - * for the kernel stack during the first part of exception exit - * which gets invalidated due to a tlbie from another cpu at a - * non recoverable point (after setting srr0/1) - Anton - */ -4: slbmfee r11,r10 - srdi r11,r11,27 - /* - * Use paca->ksave as the value of the kernel stack pointer, - * because this is valid at all times. - * The >> 27 (rather than >> 28) is so that the LSB is the - * valid bit - this way we check valid and ESID in one compare. - * In order to completely close the tiny race in the context - * switch (between updating r1 and updating paca->ksave), - * we check against both r1 and paca->ksave. - */ - srdi r9,r1,27 - ori r9,r9,1 /* mangle SP for later compare */ - cmpd r11,r9 - beq- 3b - ld r9,PACAKSAVE(r13) - srdi r9,r9,27 - ori r9,r9,1 - cmpd r11,r9 - beq- 3b - +4: std r10,PACASTABRR(r13) - +3: /* r3 = faulting address, r10 = entry */ srdi r9,r3,60 /* get region */ srdi r3,r3,28 /* get esid */ cmpldi cr7,r9,0xc /* cmp KERNELBASE for later use */ - /* r9 = region, r3 = esid, cr7 = <>KERNELBASE */ - - rldicr. r11,r3,32,16 - bne- 8f /* invalid ea bits set */ - addi r11,r9,-1 - cmpldi r11,0xb - blt- 8f /* invalid region */ + rldimi r10,r3,28,0 /* r10= ESID<<28 | entry */ + oris r10,r10,SLB_ESID_V@h /* r10 |= SLB_ESID_V */ - /* r9 = region, r3 = esid, r10 = entry, cr7 = <>KERNELBASE */ + /* r3 = esid, r10 = esid_data, cr7 = <>KERNELBASE */ blt cr7,0f /* user or kernel? */ - /* kernel address */ + /* kernel address: proto-VSID = ESID */ + /* WARNING - MAGIC: we don't use the VSID 0xfffffffff, but + * this code will generate the protoVSID 0xfffffffff for the + * top segment. That's ok, the scramble below will translate + * it to VSID 0, which is reserved as a bad VSID - one which + * will never have any pages in it. */ li r11,SLB_VSID_KERNEL BEGIN_FTR_SECTION bne cr7,9f @@ -102,8 +88,12 @@ BEGIN_FTR_SECTION END_FTR_SECTION_IFSET(CPU_FTR_16M_PAGE) b 9f -0: /* user address */ +0: /* user address: proto-VSID = context<<15 | ESID */ li r11,SLB_VSID_USER + + srdi. r9,r3,13 + bne- 8f /* invalid ea bits set */ + #ifdef CONFIG_HUGETLB_PAGE BEGIN_FTR_SECTION /* check against the hugepage ranges */ @@ -125,33 +115,18 @@ END_FTR_SECTION_IFSET(CPU_FTR_16M_PAGE) #endif /* CONFIG_HUGETLB_PAGE */ 6: ld r9,PACACONTEXTID(r13) + rldimi r3,r9,USER_ESID_BITS,0 -9: /* r9 = "context", r3 = esid, r11 = flags, r10 = entry */ - - rldimi r9,r3,15,0 /* r9= VSID ordinal */ - -7: rldimi r10,r3,28,0 /* r10= ESID<<28 | entry */ - oris r10,r10,SLB_ESID_V@h /* r10 |= SLB_ESID_V */ - - /* r9 = ordinal, r3 = esid, r11 = flags, r10 = esid_data */ - - li r3,VSID_RANDOMIZER@higher - sldi r3,r3,32 - oris r3,r3,VSID_RANDOMIZER@h - ori r3,r3,VSID_RANDOMIZER@l - - mulld r9,r3,r9 /* r9 = ordinal * VSID_RANDOMIZER */ - clrldi r9,r9,28 /* r9 &= VSID_MASK */ - sldi r9,r9,SLB_VSID_SHIFT /* r9 <<= SLB_VSID_SHIFT */ - or r9,r9,r11 /* r9 |= flags */ +9: /* r3 = protovsid, r11 = flags, r10 = esid_data, cr7 = <>KERNELBASE */ + ASM_VSID_SCRAMBLE(r3,r9) - /* r9 = vsid_data, r10 = esid_data, cr7 = <>KERNELBASE */ + rldimi r11,r3,SLB_VSID_SHIFT,16 /* combine VSID and flags */ /* * No need for an isync before or after this slbmte. The exception * we enter with and the rfid we exit with are context synchronizing. */ - slbmte r9,r10 + slbmte r11,r10 bgelr cr7 /* we're done for kernel addresses */ @@ -174,6 +149,6 @@ END_FTR_SECTION_IFSET(CPU_FTR_16M_PAGE) blr 8: /* invalid EA */ - li r9,0 /* 0 VSID ordinal -> BAD_VSID */ + li r3,0 /* BAD_VSID */ li r11,SLB_VSID_USER /* flags don't much matter */ - b 7b + b 9b diff --git a/arch/ppc64/mm/stab.c b/arch/ppc64/mm/stab.c new file mode 100644 index 000000000..9b4fe8ac1 --- /dev/null +++ b/arch/ppc64/mm/stab.c @@ -0,0 +1,240 @@ +/* + * PowerPC64 Segment Translation Support. + * + * Dave Engebretsen and Mike Corrigan {engebret|mikejc}@us.ibm.com + * Copyright (c) 2001 Dave Engebretsen + * + * Copyright (C) 2002 Anton Blanchard , IBM + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the 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 +#include +#include +#include +#include +#include +#include + +/* Both the segment table and SLB code uses the following cache */ +#define NR_STAB_CACHE_ENTRIES 8 +DEFINE_PER_CPU(long, stab_cache_ptr); +DEFINE_PER_CPU(long, stab_cache[NR_STAB_CACHE_ENTRIES]); + +/* + * Create a segment table entry for the given esid/vsid pair. + */ +static int make_ste(unsigned long stab, unsigned long esid, unsigned long vsid) +{ + unsigned long esid_data, vsid_data; + unsigned long entry, group, old_esid, castout_entry, i; + unsigned int global_entry; + struct stab_entry *ste, *castout_ste; + unsigned long kernel_segment = (esid << SID_SHIFT) >= KERNELBASE; + + vsid_data = vsid << STE_VSID_SHIFT; + esid_data = esid << SID_SHIFT | STE_ESID_KP | STE_ESID_V; + if (! kernel_segment) + esid_data |= STE_ESID_KS; + + /* Search the primary group first. */ + global_entry = (esid & 0x1f) << 3; + ste = (struct stab_entry *)(stab | ((esid & 0x1f) << 7)); + + /* Find an empty entry, if one exists. */ + for (group = 0; group < 2; group++) { + for (entry = 0; entry < 8; entry++, ste++) { + if (!(ste->esid_data & STE_ESID_V)) { + ste->vsid_data = vsid_data; + asm volatile("eieio":::"memory"); + ste->esid_data = esid_data; + return (global_entry | entry); + } + } + /* Now search the secondary group. */ + global_entry = ((~esid) & 0x1f) << 3; + ste = (struct stab_entry *)(stab | (((~esid) & 0x1f) << 7)); + } + + /* + * Could not find empty entry, pick one with a round robin selection. + * Search all entries in the two groups. + */ + castout_entry = get_paca()->stab_rr; + for (i = 0; i < 16; i++) { + if (castout_entry < 8) { + global_entry = (esid & 0x1f) << 3; + ste = (struct stab_entry *)(stab | ((esid & 0x1f) << 7)); + castout_ste = ste + castout_entry; + } else { + global_entry = ((~esid) & 0x1f) << 3; + ste = (struct stab_entry *)(stab | (((~esid) & 0x1f) << 7)); + castout_ste = ste + (castout_entry - 8); + } + + /* Dont cast out the first kernel segment */ + if ((castout_ste->esid_data & ESID_MASK) != KERNELBASE) + break; + + castout_entry = (castout_entry + 1) & 0xf; + } + + get_paca()->stab_rr = (castout_entry + 1) & 0xf; + + /* Modify the old entry to the new value. */ + + /* Force previous translations to complete. DRENG */ + asm volatile("isync" : : : "memory"); + + old_esid = castout_ste->esid_data >> SID_SHIFT; + castout_ste->esid_data = 0; /* Invalidate old entry */ + + asm volatile("sync" : : : "memory"); /* Order update */ + + castout_ste->vsid_data = vsid_data; + asm volatile("eieio" : : : "memory"); /* Order update */ + castout_ste->esid_data = esid_data; + + asm volatile("slbie %0" : : "r" (old_esid << SID_SHIFT)); + /* Ensure completion of slbie */ + asm volatile("sync" : : : "memory"); + + return (global_entry | (castout_entry & 0x7)); +} + +/* + * Allocate a segment table entry for the given ea and mm + */ +static int __ste_allocate(unsigned long ea, struct mm_struct *mm) +{ + unsigned long vsid; + unsigned char stab_entry; + unsigned long offset; + + /* Kernel or user address? */ + if (ea >= KERNELBASE) { + vsid = get_kernel_vsid(ea); + } else { + if ((ea >= TASK_SIZE_USER64) || (! mm)) + return 1; + + vsid = get_vsid(mm->context.id, ea); + } + + stab_entry = make_ste(get_paca()->stab_addr, GET_ESID(ea), vsid); + + if (ea < KERNELBASE) { + offset = __get_cpu_var(stab_cache_ptr); + if (offset < NR_STAB_CACHE_ENTRIES) + __get_cpu_var(stab_cache[offset++]) = stab_entry; + else + offset = NR_STAB_CACHE_ENTRIES+1; + __get_cpu_var(stab_cache_ptr) = offset; + + /* Order update */ + asm volatile("sync":::"memory"); + } + + return 0; +} + +int ste_allocate(unsigned long ea) +{ + return __ste_allocate(ea, current->mm); +} + +/* + * Do the segment table work for a context switch: flush all user + * entries from the table, then preload some probably useful entries + * for the new task + */ +void switch_stab(struct task_struct *tsk, struct mm_struct *mm) +{ + struct stab_entry *stab = (struct stab_entry *) get_paca()->stab_addr; + struct stab_entry *ste; + unsigned long offset = __get_cpu_var(stab_cache_ptr); + unsigned long pc = KSTK_EIP(tsk); + unsigned long stack = KSTK_ESP(tsk); + unsigned long unmapped_base; + + /* Force previous translations to complete. DRENG */ + asm volatile("isync" : : : "memory"); + + if (offset <= NR_STAB_CACHE_ENTRIES) { + int i; + + for (i = 0; i < offset; i++) { + ste = stab + __get_cpu_var(stab_cache[i]); + ste->esid_data = 0; /* invalidate entry */ + } + } else { + unsigned long entry; + + /* Invalidate all entries. */ + ste = stab; + + /* Never flush the first entry. */ + ste += 1; + for (entry = 1; + entry < (PAGE_SIZE / sizeof(struct stab_entry)); + entry++, ste++) { + unsigned long ea; + ea = ste->esid_data & ESID_MASK; + if (ea < KERNELBASE) { + ste->esid_data = 0; + } + } + } + + asm volatile("sync; slbia; sync":::"memory"); + + __get_cpu_var(stab_cache_ptr) = 0; + + /* Now preload some entries for the new task */ + if (test_tsk_thread_flag(tsk, TIF_32BIT)) + unmapped_base = TASK_UNMAPPED_BASE_USER32; + else + unmapped_base = TASK_UNMAPPED_BASE_USER64; + + __ste_allocate(pc, mm); + + if (GET_ESID(pc) == GET_ESID(stack)) + return; + + __ste_allocate(stack, mm); + + if ((GET_ESID(pc) == GET_ESID(unmapped_base)) + || (GET_ESID(stack) == GET_ESID(unmapped_base))) + return; + + __ste_allocate(unmapped_base, mm); + + /* Order update */ + asm volatile("sync" : : : "memory"); +} + +extern void slb_initialize(void); + +/* + * Build an entry for the base kernel segment and put it into + * the segment table or SLB. All other segment table or SLB + * entries are faulted in. + */ +void stab_initialize(unsigned long stab) +{ + unsigned long vsid = get_kernel_vsid(KERNELBASE); + + if (cur_cpu_spec->cpu_features & CPU_FTR_SLB) { + slb_initialize(); + } else { + asm volatile("isync; slbia; isync":::"memory"); + make_ste(stab, GET_ESID(KERNELBASE), vsid); + + /* Order update */ + asm volatile("sync":::"memory"); + } +} diff --git a/arch/ppc64/oprofile/op_impl.h b/arch/ppc64/oprofile/op_impl.h index aa9c8af60..c936c5ce6 100644 --- a/arch/ppc64/oprofile/op_impl.h +++ b/arch/ppc64/oprofile/op_impl.h @@ -19,6 +19,12 @@ /* freeze counters. set to 1 on a perfmon exception */ #define MMCR0_FC (1UL << (31 - 0)) +/* freeze in supervisor state */ +#define MMCR0_KERNEL_DISABLE (1UL << (31 - 1)) + +/* freeze in problem state */ +#define MMCR0_PROBLEM_DISABLE (1UL << (31 - 2)) + /* freeze counters while MSR mark = 1 */ #define MMCR0_FCM1 (1UL << (31 - 3)) @@ -28,15 +34,15 @@ /* freeze counters on enabled condition or event */ #define MMCR0_FCECE (1UL << (31 - 6)) -/* performance monitor alert has occurred, set to 0 after handling exception */ -#define MMCR0_PMAO (1UL << (31 - 24)) - /* PMC1 count enable*/ #define MMCR0_PMC1INTCONTROL (1UL << (31 - 16)) /* PMCn count enable*/ #define MMCR0_PMCNINTCONTROL (1UL << (31 - 17)) +/* performance monitor alert has occurred, set to 0 after handling exception */ +#define MMCR0_PMAO (1UL << (31 - 24)) + /* state of MSR HV when SIAR set */ #define MMCRA_SIHV (1UL << (63 - 35)) @@ -60,6 +66,9 @@ struct op_counter_config { /* System-wide configuration as set via oprofilefs. */ struct op_system_config { + unsigned long mmcr0; + unsigned long mmcr1; + unsigned long mmcra; unsigned long enable_kernel; unsigned long enable_user; }; diff --git a/arch/ppc64/oprofile/op_model_rs64.c b/arch/ppc64/oprofile/op_model_rs64.c index 21ceb0b7c..251fdef43 100644 --- a/arch/ppc64/oprofile/op_model_rs64.c +++ b/arch/ppc64/oprofile/op_model_rs64.c @@ -15,14 +15,14 @@ #include #include -#define dbg(args...) printk(args) +#define dbg(args...) #include "op_impl.h" static void ctrl_write(unsigned int i, unsigned int val) { - unsigned int tmp; - unsigned long shift, mask; + unsigned int tmp = 0; + unsigned long shift = 0, mask = 0; dbg("ctrl_write %d %x\n", i, val); diff --git a/arch/s390/Kconfig.debug b/arch/s390/Kconfig.debug new file mode 100644 index 000000000..f53b6d530 --- /dev/null +++ b/arch/s390/Kconfig.debug @@ -0,0 +1,5 @@ +menu "Kernel hacking" + +source "lib/Kconfig.debug" + +endmenu diff --git a/arch/s390/appldata/appldata_mem.c b/arch/s390/appldata/appldata_mem.c index a21e30846..462ee9a84 100644 --- a/arch/s390/appldata/appldata_mem.c +++ b/arch/s390/appldata/appldata_mem.c @@ -102,8 +102,12 @@ static inline void appldata_debug_print(struct appldata_mem_data *mem_data) */ static void appldata_get_mem_data(void *data) { - struct sysinfo val; - struct page_state ps; + /* + * don't put large structures on the stack, we are + * serialized through the appldata_ops_lock and can use static + */ + static struct sysinfo val; + static struct page_state ps; struct appldata_mem_data *mem_data; mem_data = data; diff --git a/arch/s390/kernel/asm-offsets.c b/arch/s390/kernel/asm-offsets.c index ce2b03578..7537972ac 100644 --- a/arch/s390/kernel/asm-offsets.c +++ b/arch/s390/kernel/asm-offsets.c @@ -39,5 +39,9 @@ int main(void) DEFINE(__PT_ILC, offsetof(struct pt_regs, ilc),); DEFINE(__PT_TRAP, offsetof(struct pt_regs, trap),); DEFINE(__PT_SIZE, sizeof(struct pt_regs),); + BLANK(); + 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),); return 0; } diff --git a/arch/s390/kernel/ebcdic.c b/arch/s390/kernel/ebcdic.c index 52d5a78b5..bb0f97313 100644 --- a/arch/s390/kernel/ebcdic.c +++ b/arch/s390/kernel/ebcdic.c @@ -391,10 +391,10 @@ __u8 _ebc_toupper[256] = 0xF8, 0xF9, 0xFA, 0xFB, 0xFC, 0xFD, 0xFE, 0xFF }; -EXPORT_SYMBOL_NOVERS(_ascebc_500); -EXPORT_SYMBOL_NOVERS(_ebcasc_500); -EXPORT_SYMBOL_NOVERS(_ascebc); -EXPORT_SYMBOL_NOVERS(_ebcasc); -EXPORT_SYMBOL_NOVERS(_ebc_tolower); -EXPORT_SYMBOL_NOVERS(_ebc_toupper); +EXPORT_SYMBOL(_ascebc_500); +EXPORT_SYMBOL(_ebcasc_500); +EXPORT_SYMBOL(_ascebc); +EXPORT_SYMBOL(_ebcasc); +EXPORT_SYMBOL(_ebc_tolower); +EXPORT_SYMBOL(_ebc_toupper); diff --git a/arch/s390/kernel/irq.c b/arch/s390/kernel/irq.c new file mode 100644 index 000000000..79a422247 --- /dev/null +++ b/arch/s390/kernel/irq.c @@ -0,0 +1,99 @@ +/* + * arch/s390/kernel/irq.c + * + * S390 version + * Copyright (C) 2004 IBM Deutschland Entwicklung GmbH, IBM Corporation + * Author(s): Martin Schwidefsky (schwidefsky@de.ibm.com), + * + * This file contains interrupt related functions. + */ + +#include +#include +#include +#include +#include +#include + +/* + * show_interrupts is needed by /proc/interrupts. + */ +int show_interrupts(struct seq_file *p, void *v) +{ + static const char *intrclass_names[] = { "EXT", "I/O", }; + int i = *(loff_t *) v, j; + + if (i == 0) { + seq_puts(p, " "); + for (j=0; j> (PAGE_SHIFT + THREAD_ORDER)) != 0) { + /* Need to switch to the async. stack. */ + new -= STACK_FRAME_OVERHEAD; + ((struct stack_frame *) new)->back_chain = old; + + asm volatile(" la 15,0(%0)\n" + " basr 14,%2\n" + " la 15,0(%1)\n" + : : "a" (new), "a" (old), + "a" (__do_softirq) + : "0", "1", "2", "3", "4", "5", + "cc", "memory" ); + } else + /* We are already on the async stack. */ + __do_softirq(); + } + + local_irq_restore(flags); +} + +EXPORT_SYMBOL(do_softirq); diff --git a/arch/s390/kernel/profile.c b/arch/s390/kernel/profile.c index f219748d7..7ba777eec 100644 --- a/arch/s390/kernel/profile.c +++ b/arch/s390/kernel/profile.c @@ -6,52 +6,15 @@ * */ #include +#include static struct proc_dir_entry * root_irq_dir; -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); - if (count - len < 2) - return -EINVAL; - len += sprintf(page + len, "\n"); - return len; -} - -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; - unsigned long full_count = count, err; - cpumask_t new_value; - - err = cpumask_parse(buffer, count, new_value); - if (err) - return err; - - *mask = new_value; - return full_count; -} - -cpumask_t prof_cpu_mask = CPU_MASK_ALL; - void init_irq_proc(void) { - struct proc_dir_entry *entry; - /* create /proc/irq */ root_irq_dir = proc_mkdir("irq", 0); /* create /proc/irq/prof_cpu_mask */ - entry = create_proc_entry("prof_cpu_mask", 0600, root_irq_dir); - - if (!entry) - return; - - entry->nlink = 1; - entry->data = (void *)&prof_cpu_mask; - entry->read_proc = prof_cpu_mask_read_proc; - entry->write_proc = prof_cpu_mask_write_proc; + create_prof_cpu_mask(root_irq_dir); } diff --git a/arch/s390/kernel/s390_ext.c b/arch/s390/kernel/s390_ext.c index c00e5e328..b61490bd1 100644 --- a/arch/s390/kernel/s390_ext.c +++ b/arch/s390/kernel/s390_ext.c @@ -12,6 +12,7 @@ #include #include #include +#include #include #include diff --git a/arch/s390/kernel/vmlinux.lds.S b/arch/s390/kernel/vmlinux.lds.S index b4534b286..89fdb3808 100644 --- a/arch/s390/kernel/vmlinux.lds.S +++ b/arch/s390/kernel/vmlinux.lds.S @@ -24,6 +24,7 @@ SECTIONS .text : { *(.text) SCHED_TEXT + LOCK_TEXT *(.fixup) *(.gnu.warning) } = 0x0700 @@ -78,9 +79,6 @@ SECTIONS __setup_start = .; .init.setup : { *(.init.setup) } __setup_end = .; - __start___param = .; - __param : { *(__param) } - __stop___param = .; __initcall_start = .; .initcall.init : { *(.initcall1.init) diff --git a/arch/s390/kernel/vtime.c b/arch/s390/kernel/vtime.c index d9ccdbdc2..02d2179e4 100644 --- a/arch/s390/kernel/vtime.c +++ b/arch/s390/kernel/vtime.c @@ -21,7 +21,7 @@ #include #include -#define VTIMER_MAGIC (0x4b87ad6e + 1) +#define VTIMER_MAGIC (TIMER_MAGIC + 1) static ext_int_info_t ext_int_info_timer; DEFINE_PER_CPU(struct vtimer_queue, virt_cpu_timer); diff --git a/arch/s390/lib/string.c b/arch/s390/lib/string.c index 966af7c52..9cf22aa48 100644 --- a/arch/s390/lib/string.c +++ b/arch/s390/lib/string.c @@ -46,7 +46,7 @@ size_t strlen(const char *s) { return __strend(s) - s; } -EXPORT_SYMBOL_NOVERS(strlen); +EXPORT_SYMBOL(strlen); /** * strnlen - Find the length of a length-limited string @@ -59,7 +59,7 @@ size_t strnlen(const char * s, size_t n) { return __strnend(s, n) - s; } -EXPORT_SYMBOL_NOVERS(strnlen); +EXPORT_SYMBOL(strnlen); /** * strcpy - Copy a %NUL terminated string @@ -79,7 +79,7 @@ char *strcpy(char *dest, const char *src) : "cc", "memory" ); return ret; } -EXPORT_SYMBOL_NOVERS(strcpy); +EXPORT_SYMBOL(strcpy); /** * strlcpy - Copy a %NUL terminated string into a sized buffer @@ -103,7 +103,7 @@ size_t strlcpy(char *dest, const char *src, size_t size) } return ret; } -EXPORT_SYMBOL_NOVERS(strlcpy); +EXPORT_SYMBOL(strlcpy); /** * strncpy - Copy a length-limited, %NUL-terminated string @@ -121,7 +121,7 @@ char *strncpy(char *dest, const char *src, size_t n) __builtin_memcpy(dest, src, len); return dest; } -EXPORT_SYMBOL_NOVERS(strncpy); +EXPORT_SYMBOL(strncpy); /** * strcat - Append one %NUL-terminated string to another @@ -144,7 +144,7 @@ char *strcat(char *dest, const char *src) : "d" (r0), "0" (0UL) : "cc", "memory" ); return ret; } -EXPORT_SYMBOL_NOVERS(strcat); +EXPORT_SYMBOL(strcat); /** * strlcat - Append a length-limited, %NUL-terminated string to another @@ -168,7 +168,7 @@ size_t strlcat(char *dest, const char *src, size_t n) } return res; } -EXPORT_SYMBOL_NOVERS(strlcat); +EXPORT_SYMBOL(strlcat); /** * strncat - Append a length-limited, %NUL-terminated string to another @@ -190,7 +190,7 @@ char *strncat(char *dest, const char *src, size_t n) __builtin_memcpy(p, src, len); return dest; } -EXPORT_SYMBOL_NOVERS(strncat); +EXPORT_SYMBOL(strncat); /** * strcmp - Compare two strings @@ -217,7 +217,7 @@ int strcmp(const char *cs, const char *ct) : : "cc" ); return ret; } -EXPORT_SYMBOL_NOVERS(strcmp); +EXPORT_SYMBOL(strcmp); /** * strrchr - Find the last occurrence of a character in a string @@ -235,7 +235,7 @@ char * strrchr(const char * s, int c) } while (--len > 0); return 0; } -EXPORT_SYMBOL_NOVERS(strrchr); +EXPORT_SYMBOL(strrchr); /** * strstr - Find the first substring in a %NUL terminated string @@ -269,7 +269,7 @@ char * strstr(const char * s1,const char * s2) } return 0; } -EXPORT_SYMBOL_NOVERS(strstr); +EXPORT_SYMBOL(strstr); /** * memchr - Find a character in an area of memory. @@ -293,7 +293,7 @@ void *memchr(const void *s, int c, size_t n) : "+a" (ret), "+&a" (s) : "d" (r0) : "cc" ); return (void *) ret; } -EXPORT_SYMBOL_NOVERS(memchr); +EXPORT_SYMBOL(memchr); /** * memcmp - Compare two areas of memory @@ -319,7 +319,7 @@ int memcmp(const void *cs, const void *ct, size_t n) ret = *(char *) r2 - *(char *) r4; return ret; } -EXPORT_SYMBOL_NOVERS(memcmp); +EXPORT_SYMBOL(memcmp); /** * memscan - Find a character in an area of memory. @@ -340,7 +340,7 @@ void *memscan(void *s, int c, size_t n) : "+a" (ret), "+&a" (s) : "d" (r0) : "cc" ); return (void *) ret; } -EXPORT_SYMBOL_NOVERS(memscan); +EXPORT_SYMBOL(memscan); /** * memcpy - Copy one area of memory to another @@ -354,7 +354,7 @@ void *memcpy(void *dest, const void *src, size_t n) { return __builtin_memcpy(dest, src, n); } -EXPORT_SYMBOL_NOVERS(memcpy); +EXPORT_SYMBOL(memcpy); /** * bcopy - Copy one area of memory to another @@ -369,7 +369,7 @@ void bcopy(const void *srcp, void *destp, size_t n) { __builtin_memcpy(destp, srcp, n); } -EXPORT_SYMBOL_NOVERS(bcopy); +EXPORT_SYMBOL(bcopy); /** * memset - Fill a region of memory with the given value @@ -393,4 +393,4 @@ void *memset(void *s, int c, size_t n) } while (--n > 0); return s; } -EXPORT_SYMBOL_NOVERS(memset); +EXPORT_SYMBOL(memset); diff --git a/arch/sh/Kconfig.debug b/arch/sh/Kconfig.debug new file mode 100644 index 000000000..3fab181da --- /dev/null +++ b/arch/sh/Kconfig.debug @@ -0,0 +1,124 @@ +menu "Kernel hacking" + +source "lib/Kconfig.debug" + +config SH_STANDARD_BIOS + bool "Use LinuxSH standard BIOS" + help + Say Y here if your target has the gdb-sh-stub + package from www.m17n.org (or any conforming standard LinuxSH BIOS) + in FLASH or EPROM. The kernel will use standard BIOS calls during + boot for various housekeeping tasks (including calls to read and + write characters to a system console, get a MAC address from an + on-board Ethernet interface, and shut down the hardware). Note this + does not work with machines with an existing operating system in + mask ROM and no flash (WindowsCE machines fall in this category). + If unsure, say N. + +config EARLY_SCIF_CONSOLE + bool "Use early SCIF console" + depends on CPU_SH4 + +config EARLY_PRINTK + bool "Early printk support" + depends on SH_STANDARD_BIOS || EARLY_SCIF_CONSOLE + help + Say Y here to redirect kernel printk messages to the serial port + used by the SH-IPL bootloader, starting very early in the boot + process and ending when the kernel's serial console is initialised. + This option is only useful porting the kernel to a new machine, + when the kernel may crash or hang before the serial console is + initialised. If unsure, say N. + +config KGDB + bool "Include KGDB kernel debugger" + help + Include in-kernel hooks for kgdb, the Linux kernel source level + debugger. See for more information. + Unless you are intending to debug the kernel, say N here. + +menu "KGDB configuration options" + depends on KGDB + +config MORE_COMPILE_OPTIONS + bool "Add any additional compile options" + help + If you want to add additional CFLAGS to the kernel build, enable this + option and then enter what you would like to add in the next question. + Note however that -g is already appended with the selection of KGDB. + +config COMPILE_OPTIONS + string "Additional compile arguments" + depends on MORE_COMPILE_OPTIONS + +config KGDB_NMI + bool "Enter KGDB on NMI" + default n + +config KGDB_THREAD + bool "Include KGDB thread support" + default y + +config SH_KGDB_CONSOLE + bool "Console messages through GDB" + default n + +config KGDB_SYSRQ + bool "Allow SysRq 'G' to enter KGDB" + default y + +config KGDB_KERNEL_ASSERTS + bool "Include KGDB kernel assertions" + default n + +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 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 KGDB + default KGDB_DEFBITS_8 + +config KGDB_DEFBITS_8 + bool "8" + +config KGDB_DEFBITS_7 + bool "7" + +endchoice + +endmenu + +config FRAME_POINTER + bool "Compile the kernel with frame pointers" + default y if KGDB + help + If you say Y here the resulting kernel image will be slightly larger + and slower, but it will give very useful debugging information. + If you don't debug the kernel, you can say N, but we may not be able + to solve problems without frame pointers. + +endmenu diff --git a/arch/sh/boards/mpc1211/pci.c b/arch/sh/boards/mpc1211/pci.c index c9bbd72b4..ba3a65439 100644 --- a/arch/sh/boards/mpc1211/pci.c +++ b/arch/sh/boards/mpc1211/pci.c @@ -176,14 +176,7 @@ static void __devinit quirk_ali_ide_ports(struct pci_dev *dev) dev->resource[4].end = 0xf00f; dev->resource[4].flags = IORESOURCE_IO; } - - -/* Add future fixups here... */ -struct pci_fixup pcibios_fixups[] = { - { PCI_FIXUP_HEADER, PCI_VENDOR_ID_AL, - PCI_DEVICE_ID_AL_M5229, quirk_ali_ide_ports }, - { 0 } -}; +DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_AL, PCI_DEVICE_ID_AL_M5229, quirk_ali_ide_ports); char * __devinit pcibios_setup(char *str) { diff --git a/arch/sh/boards/overdrive/galileo.c b/arch/sh/boards/overdrive/galileo.c index 54020ebbb..276fa11ee 100644 --- a/arch/sh/boards/overdrive/galileo.c +++ b/arch/sh/boards/overdrive/galileo.c @@ -455,13 +455,7 @@ static void __init pci_fixup_ide_bases(struct pci_dev *d) } } } - - -/* Add future fixups here... */ -struct pci_fixup pcibios_fixups[] = { - { PCI_FIXUP_HEADER, PCI_ANY_ID, PCI_ANY_ID, pci_fixup_ide_bases }, - { 0 } -}; +DECLARE_PCI_FIXUP_HEADER(PCI_ANY_ID, PCI_ANY_ID, pci_fixup_ide_bases); void __init pcibios_init(void) { diff --git a/arch/sh/drivers/pci/fixups-dreamcast.c b/arch/sh/drivers/pci/fixups-dreamcast.c index aa5e24a65..d07572bfc 100644 --- a/arch/sh/drivers/pci/fixups-dreamcast.c +++ b/arch/sh/drivers/pci/fixups-dreamcast.c @@ -47,11 +47,7 @@ static void __init gapspci_fixup_resources(struct pci_dev *dev) } } -struct pci_fixup pcibios_fixups[] = { - { PCI_FIXUP_HEADER, PCI_ANY_ID, - PCI_ANY_ID, gapspci_fixup_resources }, - { 0, } -}; +DECLARE_PCI_FIXUP_HEADER(PCI_ANY_ID, PCI_ANY_ID, gapspci_fixup_resources); void __init pcibios_fixup_bus(struct pci_bus *bus) { diff --git a/arch/sh/drivers/pci/pci-st40.c b/arch/sh/drivers/pci/pci-st40.c index 29e7bf619..4be02f627 100644 --- a/arch/sh/drivers/pci/pci-st40.c +++ b/arch/sh/drivers/pci/pci-st40.c @@ -160,13 +160,7 @@ static void __init pci_fixup_ide_bases(struct pci_dev *d) } } } - - -/* Add future fixups here... */ -struct pci_fixup pcibios_fixups[] = { - { PCI_FIXUP_HEADER, PCI_ANY_ID, PCI_ANY_ID, pci_fixup_ide_bases }, - { 0 } -}; +DECLARE_PCI_FIXUP_HEADER(PCI_ANY_ID, PCI_ANY_ID, pci_fixup_ide_bases); int __init st40pci_init(unsigned memStart, unsigned memSize) { diff --git a/arch/sh/kernel/signal.c b/arch/sh/kernel/signal.c index ccee2eca5..fc5ef9960 100644 --- a/arch/sh/kernel/signal.c +++ b/arch/sh/kernel/signal.c @@ -413,9 +413,7 @@ static void setup_frame(int sig, struct k_sigaction *ka, return; give_sigsegv: - if (sig == SIGSEGV) - ka->sa.sa_handler = SIG_DFL; - force_sig(SIGSEGV, current); + force_sigsegv(sig, current); } static void setup_rt_frame(int sig, struct k_sigaction *ka, siginfo_t *info, @@ -490,9 +488,7 @@ static void setup_rt_frame(int sig, struct k_sigaction *ka, siginfo_t *info, return; give_sigsegv: - if (sig == SIGSEGV) - ka->sa.sa_handler = SIG_DFL; - force_sig(SIGSEGV, current); + force_sigsegv(sig, current); } /* diff --git a/arch/sh/mm/fault-nommu.c b/arch/sh/mm/fault-nommu.c index e757b2c18..34d4e0c68 100644 --- a/arch/sh/mm/fault-nommu.c +++ b/arch/sh/mm/fault-nommu.c @@ -26,7 +26,6 @@ #include #include #include -#include #include #include diff --git a/arch/sh/mm/fault.c b/arch/sh/mm/fault.c index 43a9e2689..7abba2161 100644 --- a/arch/sh/mm/fault.c +++ b/arch/sh/mm/fault.c @@ -26,7 +26,6 @@ #include #include #include -#include #include #include #include diff --git a/arch/sh/mm/tlb-sh4.c b/arch/sh/mm/tlb-sh4.c index 4b7a0bcdc..115b1b6be 100644 --- a/arch/sh/mm/tlb-sh4.c +++ b/arch/sh/mm/tlb-sh4.c @@ -25,7 +25,6 @@ #include #include #include -#include #include #include diff --git a/arch/sh64/Kconfig b/arch/sh64/Kconfig index 0ac5d418e..551ba9a20 100644 --- a/arch/sh64/Kconfig +++ b/arch/sh64/Kconfig @@ -263,58 +263,10 @@ source "fs/Kconfig" source "arch/sh64/oprofile/Kconfig" -menu "Kernel hacking" - -config MAGIC_SYSRQ - bool "Magic SysRq key" - help - If you say Y here, you will have some control over the system even - if the system crashes for example during kernel debugging (e.g., you - will be able to flush the buffer cache to disk, reboot the system - immediately or dump some status information). This is accomplished - by pressing various keys while holding SysRq (Alt+PrintScreen). It - also works on a serial console (on PC hardware at least), if you - send a BREAK and then within 5 seconds a command keypress. The - keys are documented in Documentation/sysrq.txt. Don't say Y unless - you really know what this hack does. - -config EARLY_PRINTK - bool "Early SCIF console support" - -config DEBUG_KERNEL_WITH_GDB_STUB - bool "GDB Stub kernel debug" - -config SH64_PROC_TLB - bool "Debug: report TLB fill/purge activity through /proc/tlb" - depends on PROC_FS - -config SH64_PROC_ASIDS - bool "Debug: report ASIDs through /proc/asids" - depends on PROC_FS - -config SH64_SR_WATCH - bool "Debug: set SR.WATCH to enable hardware watchpoints and trace" - -config SH_ALPHANUMERIC - bool "Enable debug outputs to on-board alphanumeric display" - -config SH_NO_BSS_INIT - bool "Avoid zeroing BSS (to speed-up startup on suitable platforms)" - -config FRAME_POINTER - bool "Compile the kernel with frame pointers" - default y if KGDB - help - If you say Y here the resulting kernel image will be slightly larger - and slower, but it will give very useful debugging information. - If you don't debug the kernel, you can say N, but we may not be able - to solve problems without frame pointers. - -endmenu +source "arch/sh64/Kconfig.debug" source "security/Kconfig" source "crypto/Kconfig" source "lib/Kconfig" - diff --git a/arch/sh64/Kconfig.debug b/arch/sh64/Kconfig.debug new file mode 100644 index 000000000..530b3c650 --- /dev/null +++ b/arch/sh64/Kconfig.debug @@ -0,0 +1,37 @@ +menu "Kernel hacking" + +source "lib/Kconfig.debug" + +config EARLY_PRINTK + bool "Early SCIF console support" + +config DEBUG_KERNEL_WITH_GDB_STUB + bool "GDB Stub kernel debug" + +config SH64_PROC_TLB + bool "Debug: report TLB fill/purge activity through /proc/tlb" + depends on PROC_FS + +config SH64_PROC_ASIDS + bool "Debug: report ASIDs through /proc/asids" + depends on PROC_FS + +config SH64_SR_WATCH + bool "Debug: set SR.WATCH to enable hardware watchpoints and trace" + +config SH_ALPHANUMERIC + bool "Enable debug outputs to on-board alphanumeric display" + +config SH_NO_BSS_INIT + bool "Avoid zeroing BSS (to speed-up startup on suitable platforms)" + +config FRAME_POINTER + bool "Compile the kernel with frame pointers" + default y if KGDB + help + If you say Y here the resulting kernel image will be slightly larger + and slower, but it will give very useful debugging information. + If you don't debug the kernel, you can say N, but we may not be able + to solve problems without frame pointers. + +endmenu diff --git a/arch/sh64/boot/compressed/Makefile b/arch/sh64/boot/compressed/Makefile index 3f6cbf017..9cd216718 100644 --- a/arch/sh64/boot/compressed/Makefile +++ b/arch/sh64/boot/compressed/Makefile @@ -12,7 +12,7 @@ # targets := vmlinux vmlinux.bin vmlinux.bin.gz \ - head.o misc.o cache.o piggy.o vmlinux.lds.o + head.o misc.o cache.o piggy.o vmlinux.lds EXTRA_AFLAGS := -traditional @@ -25,7 +25,7 @@ OBJECTS := $(obj)/head.o $(obj)/misc.o $(obj)/cache.o ZIMAGE_OFFSET = $(shell printf "0x%8x" $$[$(CONFIG_MEMORY_START)+0x400000+0x10000]) LDFLAGS_vmlinux := -Ttext $(ZIMAGE_OFFSET) -e startup \ - -T $(obj)/../../kernel/vmlinux.lds.s \ + -T $(obj)/../../kernel/vmlinux.lds \ --no-warn-mismatch $(obj)/vmlinux: $(OBJECTS) $(obj)/piggy.o FORCE @@ -41,6 +41,6 @@ $(obj)/vmlinux.bin.gz: $(obj)/vmlinux.bin FORCE LDFLAGS_piggy.o := -r --format binary --oformat elf32-sh64-linux -T OBJCOPYFLAGS += -R .empty_zero_page -$(obj)/piggy.o: $(obj)/vmlinux.lds.s $(obj)/vmlinux.bin.gz FORCE +$(obj)/piggy.o: $(obj)/vmlinux.lds $(obj)/vmlinux.bin.gz FORCE $(call if_changed,ld) diff --git a/arch/sh64/kernel/irq.c b/arch/sh64/kernel/irq.c index bcf6385db..7d0f422e1 100644 --- a/arch/sh64/kernel/irq.c +++ b/arch/sh64/kernel/irq.c @@ -148,6 +148,7 @@ asmlinkage void do_NMI(unsigned long vector_num, struct pt_regs * regs) int handle_IRQ_event(unsigned int irq, struct pt_regs * regs, struct irqaction * action) { int status; + int ret; status = 1; /* Force the "do bottom halves" bit */ @@ -155,8 +156,9 @@ int handle_IRQ_event(unsigned int irq, struct pt_regs * regs, struct irqaction * local_irq_enable(); do { - status |= action->flags; - action->handler(irq, action->dev_id, regs); + ret = action->handler(irq, action->dev_id, regs); + if (ret == IRQ_HANDLED) + status |= action->flags; action = action->next; } while (action); if (status & SA_SAMPLE_RANDOM) diff --git a/arch/sh64/kernel/pci_sh5.c b/arch/sh64/kernel/pci_sh5.c index c2b4f00e2..842a24373 100644 --- a/arch/sh64/kernel/pci_sh5.c +++ b/arch/sh64/kernel/pci_sh5.c @@ -48,12 +48,7 @@ static void __init pci_fixup_ide_bases(struct pci_dev *d) } } } - -/* Add future fixups here... */ -struct pci_fixup pcibios_fixups[] = { - { PCI_FIXUP_HEADER, PCI_ANY_ID, PCI_ANY_ID, pci_fixup_ide_bases }, - { 0 } -}; +DECLARE_PCI_FIXUP_HEADER(PCI_ANY_ID, PCI_ANY_ID, pci_fixup_ide_bases); char * __init pcibios_setup(char *str) { diff --git a/arch/sh64/kernel/process.c b/arch/sh64/kernel/process.c index f9e82274e..13cec3579 100644 --- a/arch/sh64/kernel/process.c +++ b/arch/sh64/kernel/process.c @@ -820,7 +820,7 @@ asmlinkage int sys_clone(unsigned long clone_flags, unsigned long newsp, { if (!newsp) newsp = pregs->regs[15]; - return do_fork(clone_flags & ~CLONE_IDLETASK, newsp, pregs, 0, 0, 0); + return do_fork(clone_flags, newsp, pregs, 0, 0, 0); } /* @@ -901,7 +901,7 @@ unsigned long get_wchan(struct task_struct *p) */ pc = thread_saved_pc(p); -#if CONFIG_FRAME_POINTER +#ifdef CONFIG_FRAME_POINTER if (in_sh64_switch_to(pc)) { sh64_switch_to_fp = (long) p->thread.sp; /* r14 is saved at offset 4 in the sh64_switch_to frame */ diff --git a/arch/sh64/kernel/ptrace.c b/arch/sh64/kernel/ptrace.c index 887e89a45..f27c696b3 100644 --- a/arch/sh64/kernel/ptrace.c +++ b/arch/sh64/kernel/ptrace.c @@ -257,7 +257,7 @@ asmlinkage int sys_ptrace(long request, long pid, long addr, long data) */ case PTRACE_KILL: { ret = 0; - if (child->state == TASK_ZOMBIE) /* already dead */ + if (child->exit_state == EXIT_ZOMBIE) /* already dead */ break; child->exit_code = SIGKILL; wake_up_process(child); @@ -311,11 +311,8 @@ asmlinkage void syscall_trace(void) if (!(tsk->ptrace & PT_PTRACED)) return; - tsk->exit_code = SIGTRAP | ((current->ptrace & PT_TRACESYSGOOD) - ? 0x80 : 0); - tsk->state = TASK_STOPPED; - notify_parent(tsk, SIGCHLD); - schedule(); + ptrace_notify(SIGTRAP | ((current->ptrace & PT_TRACESYSGOOD) + ? 0x80 : 0)); /* * this isn't the same as continuing with a signal, but it will do * for normal use. strace only continues with a signal if the diff --git a/arch/sh64/kernel/sh_ksyms.c b/arch/sh64/kernel/sh_ksyms.c index 80712f1ef..36cd5de95 100644 --- a/arch/sh64/kernel/sh_ksyms.c +++ b/arch/sh64/kernel/sh_ksyms.c @@ -25,7 +25,6 @@ #include #include #include -#include #include #include @@ -59,17 +58,17 @@ EXPORT_SYMBOL(strstr); EXPORT_SYMBOL(screen_info); #endif -EXPORT_SYMBOL_NOVERS(__down); -EXPORT_SYMBOL_NOVERS(__down_trylock); -EXPORT_SYMBOL_NOVERS(__up); -EXPORT_SYMBOL_NOVERS(__put_user_asm_l); -EXPORT_SYMBOL_NOVERS(__get_user_asm_l); -EXPORT_SYMBOL_NOVERS(memcmp); -EXPORT_SYMBOL_NOVERS(memcpy); -EXPORT_SYMBOL_NOVERS(memset); -EXPORT_SYMBOL_NOVERS(memscan); -EXPORT_SYMBOL_NOVERS(strchr); -EXPORT_SYMBOL_NOVERS(strlen); +EXPORT_SYMBOL(__down); +EXPORT_SYMBOL(__down_trylock); +EXPORT_SYMBOL(__up); +EXPORT_SYMBOL(__put_user_asm_l); +EXPORT_SYMBOL(__get_user_asm_l); +EXPORT_SYMBOL(memcmp); +EXPORT_SYMBOL(memcpy); +EXPORT_SYMBOL(memset); +EXPORT_SYMBOL(memscan); +EXPORT_SYMBOL(strchr); +EXPORT_SYMBOL(strlen); EXPORT_SYMBOL(flush_dcache_page); @@ -78,7 +77,7 @@ EXPORT_SYMBOL(flush_dcache_page); extern void __sdivsi3(void); extern void __muldi3(void); extern void __udivsi3(void); -EXPORT_SYMBOL_NOVERS(__sdivsi3); -EXPORT_SYMBOL_NOVERS(__muldi3); -EXPORT_SYMBOL_NOVERS(__udivsi3); +EXPORT_SYMBOL(__sdivsi3); +EXPORT_SYMBOL(__muldi3); +EXPORT_SYMBOL(__udivsi3); diff --git a/arch/sh64/kernel/signal.c b/arch/sh64/kernel/signal.c index 376b8998b..a848fbdb1 100644 --- a/arch/sh64/kernel/signal.c +++ b/arch/sh64/kernel/signal.c @@ -520,9 +520,7 @@ static void setup_frame(int sig, struct k_sigaction *ka, return; give_sigsegv: - if (sig == SIGSEGV) - ka->sa.sa_handler = SIG_DFL; - force_sig(SIGSEGV, current); + force_sigsegv(sig, current); } static void setup_rt_frame(int sig, struct k_sigaction *ka, siginfo_t *info, @@ -628,9 +626,7 @@ static void setup_rt_frame(int sig, struct k_sigaction *ka, siginfo_t *info, return; give_sigsegv: - if (sig == SIGSEGV) - ka->sa.sa_handler = SIG_DFL; - force_sig(SIGSEGV, current); + force_sigsegv(sig, current); } /* diff --git a/arch/sh64/kernel/time.c b/arch/sh64/kernel/time.c index 128bd22af..b3738ed37 100644 --- a/arch/sh64/kernel/time.c +++ b/arch/sh64/kernel/time.c @@ -298,37 +298,6 @@ static int set_rtc_time(unsigned long nowtime) /* last time the RTC clock got updated */ static long last_rtc_update = 0; -static inline void sh64_do_profile(struct pt_regs *regs) -{ - extern int _stext; - unsigned long pc; - - profile_hook(regs); - - if (user_mode(regs)) - return; - - /* Don't profile cpu_idle.. */ - if (!prof_buffer || !current->pid) - return; - - pc = instruction_pointer(regs); - pc -= (unsigned long) &_stext; - pc >>= prof_shift; - - /* - * Don't ignore out-of-bounds PC values silently, put them into the - * last histogram slot, so if present, they will show up as a sharp - * peak. - */ - if (pc > prof_len - 1) - pc = prof_len - 1; - - /* We could just be sloppy and not lock against a re-entry on this - increment, but the profiling code won't always be linked in anyway. */ - atomic_inc((atomic_t *)&prof_buffer[pc]); -} - /* * timer_interrupt() needs to keep up the real-time clock, * as well as call the "do_timer()" routine every clocktick @@ -340,8 +309,7 @@ static inline void do_timer_interrupt(int irq, void *dev_id, struct pt_regs *reg ctc_last_interrupt = (unsigned long) current_ctc; do_timer(regs); - - sh64_do_profile(regs); + profile_tick(CPU_PROFILING, regs); #ifdef CONFIG_HEARTBEAT { diff --git a/arch/sh64/kernel/vmlinux.lds.S b/arch/sh64/kernel/vmlinux.lds.S index a3fba816b..7d9f7a633 100644 --- a/arch/sh64/kernel/vmlinux.lds.S +++ b/arch/sh64/kernel/vmlinux.lds.S @@ -59,6 +59,7 @@ SECTIONS *(.text64) *(.text..SHmedia32) SCHED_TEXT + LOCK_TEXT *(.fixup) *(.gnu.warning) #ifdef CONFIG_LITTLE_ENDIAN @@ -106,9 +107,6 @@ SECTIONS __setup_start = .; .init.setup : C_PHYS(.init.setup) { *(.init.setup) } __setup_end = .; - __start___param = .; - __param : C_PHYS(__param) { *(__param) } - __stop___param = .; __initcall_start = .; .initcall.init : C_PHYS(.initcall.init) { *(.initcall1.init) diff --git a/arch/sh64/mm/fault.c b/arch/sh64/mm/fault.c index b5ab4a88c..d1190b415 100644 --- a/arch/sh64/mm/fault.c +++ b/arch/sh64/mm/fault.c @@ -30,7 +30,6 @@ #include #include #include -#include #include #include /* required by inline asm statements */ diff --git a/arch/sh64/mm/init.c b/arch/sh64/mm/init.c index 17cdeb425..09cd9c879 100644 --- a/arch/sh64/mm/init.c +++ b/arch/sh64/mm/init.c @@ -122,8 +122,8 @@ void __init paging_init(void) * All memory is good as ZONE_NORMAL (fall-through) and ZONE_DMA. */ zones_size[ZONE_DMA] = MAX_LOW_PFN - START_PFN; - - free_area_init_node(0, NODE_DATA(0), 0, zones_size, __MEMORY_START >> PAGE_SHIFT, 0); + NODE_DATA(0)->node_mem_map = NULL; + free_area_init_node(0, NODE_DATA(0), zones_size, __MEMORY_START >> PAGE_SHIFT, 0); /* XXX: MRB-remove - this doesn't seem sane, should this be done somewhere else ?*/ mem_map = NODE_DATA(0)->node_mem_map; diff --git a/arch/sh64/mm/tlbmiss.c b/arch/sh64/mm/tlbmiss.c index a69f7751c..69f686c88 100644 --- a/arch/sh64/mm/tlbmiss.c +++ b/arch/sh64/mm/tlbmiss.c @@ -40,7 +40,6 @@ #include #include #include -#include #include #include /* required by inline asm statements */ diff --git a/arch/sparc/Kconfig.debug b/arch/sparc/Kconfig.debug new file mode 100644 index 000000000..120f6b529 --- /dev/null +++ b/arch/sparc/Kconfig.debug @@ -0,0 +1,14 @@ +menu "Kernel hacking" + +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. + +endmenu diff --git a/arch/sparc/boot/Makefile b/arch/sparc/boot/Makefile index b19cc8689..b36508431 100644 --- a/arch/sparc/boot/Makefile +++ b/arch/sparc/boot/Makefile @@ -7,28 +7,52 @@ ROOT_IMG := /usr/src/root.img ELFTOAOUT := elftoaout -host-progs := piggyback btfixupprep -targets := tftpboot.img btfix.o btfix.s image +hostprogs-y := piggyback btfixupprep +targets := tftpboot.img btfix.o btfix.S image quiet_cmd_elftoaout = ELFTOAOUT $@ cmd_elftoaout = $(ELFTOAOUT) $(obj)/image -o $@ -quiet_cmd_piggy = PIGGY $@ +quiet_cmd_piggy = PIGGY $@ cmd_piggy = $(obj)/piggyback $@ $(obj)/System.map $(ROOT_IMG) -quiet_cmd_btfix = BTFIX $@ +quiet_cmd_btfix = BTFIX $@ cmd_btfix = $(OBJDUMP) -x vmlinux | $(obj)/btfixupprep > $@ +quiet_cmd_sysmap = SYSMAP $(obj)/System.map + cmd_sysmap = $(CONFIG_SHELL) $(srctree)/scripts/mksysmap +quiet_cmd_image = LD $@ + cmd_image = $(LD) $(LDFLAGS) $(EXTRA_LDFLAGS) $(LDFLAGS_$(@F)) -o $@ + +define rule_image + $(if $($(quiet)cmd_image), \ + echo ' $($(quiet)cmd_image)' &&) \ + $(cmd_image); \ + $(if $($(quiet)cmd_sysmap), \ + echo ' $($(quiet)cmd_sysmap)' &&) \ + $(cmd_sysmap) $@ $(obj)/System.map; \ + if [ $$? -ne 0 ]; then \ + rm -f $@; \ + /bin/false; \ + fi; \ + echo 'cmd_$@ := $(cmd_image)' > $(@D)/.$(@F).cmd +endef BTOBJS := $(HEAD_Y) $(INIT_Y) BTLIBS := $(CORE_Y) $(LIBS_Y) $(DRIVERS_Y) $(NET_Y) -LDFLAGS_image := -T arch/sparc/kernel/vmlinux.lds.s $(BTOBJS) --start-group $(BTLIBS) --end-group $(kallsyms.o) +LDFLAGS_image := -T arch/sparc/kernel/vmlinux.lds $(BTOBJS) \ + --start-group $(BTLIBS) --end-group \ + $(kallsyms.o) $(obj)/btfix.o -# Actual linking +# Link the final image including btfixup'ed symbols. +# This is a replacement for the link done in the top-level Makefile. +# Note: No dependency on the prerequisite files since that would require +# make to try check if they are updated - and due to changes +# in gcc options (path for example) this would result in +# these files being recompiled for each build. $(obj)/image: $(obj)/btfix.o FORCE - $(call if_changed,ld) - $(NM) $@ | grep -v '\(compiled\)\|\(\.o$$\)\|\( [aUw] \)\|\(\.\.ng$$\)\|\(LASH[RL]DI\)' | sort > $(obj)/System.map + $(call if_changed_rule,image) $(obj)/tftpboot.img: $(obj)/piggyback $(obj)/System.map $(obj)/image FORCE $(call if_changed,elftoaout) $(call if_changed,piggy) -$(obj)/btfix.s: $(obj)/btfixupprep vmlinux FORCE +$(obj)/btfix.S: $(obj)/btfixupprep vmlinux FORCE $(call if_changed,btfix) diff --git a/arch/sparc/kernel/Makefile b/arch/sparc/kernel/Makefile index fcf149e00..3d22ba2af 100644 --- a/arch/sparc/kernel/Makefile +++ b/arch/sparc/kernel/Makefile @@ -2,7 +2,7 @@ # Makefile for the linux kernel. # -extra-y := head.o init_task.o vmlinux.lds.s +extra-y := head.o init_task.o vmlinux.lds EXTRA_AFLAGS := -ansi diff --git a/arch/sparc/kernel/entry.S b/arch/sparc/kernel/entry.S index 14a4b9593..624e2a6ed 100644 --- a/arch/sparc/kernel/entry.S +++ b/arch/sparc/kernel/entry.S @@ -858,7 +858,7 @@ vac_hwflush_patch1_on: addcc %l7, -PAGE_SIZE, %l7 vac_hwflush_patch2_on: sta %g0, [%l3 + %l7] ASI_HWFLUSHSEG .globl invalid_segment_patch1, invalid_segment_patch2 - .globl num_context_patch1, num_context_patch2 + .globl num_context_patch1 .globl vac_linesize_patch, vac_hwflush_patch1 .globl vac_hwflush_patch2 diff --git a/arch/sparc/kernel/pcic.c b/arch/sparc/kernel/pcic.c index bc52cd482..c982473d1 100644 --- a/arch/sparc/kernel/pcic.c +++ b/arch/sparc/kernel/pcic.c @@ -36,10 +36,6 @@ #include -struct pci_fixup pcibios_fixups[] = { - { 0 } -}; - unsigned int pcic_pin_to_irq(unsigned int pin, char *name); /* diff --git a/arch/sparc/kernel/sun4d_smp.c b/arch/sparc/kernel/sun4d_smp.c index 9f659c2f4..1de4c8a39 100644 --- a/arch/sparc/kernel/sun4d_smp.c +++ b/arch/sparc/kernel/sun4d_smp.c @@ -19,6 +19,7 @@ #include #include #include +#include #include #include @@ -29,7 +30,6 @@ #include #include #include -#include #include #include #include @@ -201,18 +201,9 @@ void __init smp4d_boot_cpus(void) int no; /* Cook up an idler for this guy. */ - kernel_thread(start_secondary, NULL, CLONE_IDLETASK); - + p = fork_idle(i); cpucount++; - - p = prev_task(&init_task); - - init_idle(p, i); - current_set[i] = p->thread_info; - - unhash_process(p); - for (no = 0; !cpu_find_by_instance(no, NULL, &mid) && mid != i; no++) ; @@ -418,8 +409,6 @@ void smp4d_message_pass(int target, int msg, unsigned long data, int wait) panic("Bogon SMP message pass."); } -extern void sparc_do_profile(unsigned long pc, unsigned long o7); - void smp4d_percpu_timer_interrupt(struct pt_regs *regs) { int cpu = hard_smp4d_processor_id(); @@ -437,8 +426,7 @@ void smp4d_percpu_timer_interrupt(struct pt_regs *regs) show_leds(cpu); } - if(!user_mode(regs)) - sparc_do_profile(regs->pc, regs->u_regs[UREG_RETPC]); + profile_tick(CPU_PROFILING, regs); if(!--prof_counter(cpu)) { int user = user_mode(regs); diff --git a/arch/sparc/kernel/vmlinux.lds.S b/arch/sparc/kernel/vmlinux.lds.S index 8d4bbfaf3..38938d2e6 100644 --- a/arch/sparc/kernel/vmlinux.lds.S +++ b/arch/sparc/kernel/vmlinux.lds.S @@ -13,6 +13,7 @@ SECTIONS { *(.text) SCHED_TEXT + LOCK_TEXT *(.gnu.warning) } =0 _etext = .; @@ -46,9 +47,6 @@ SECTIONS __setup_start = .; .init.setup : { *(.init.setup) } __setup_end = .; - __start___param = .; - __param : { *(__param) } - __stop___param = .; __initcall_start = .; .initcall.init : { *(.initcall1.init) diff --git a/arch/sparc/mm/srmmu.c b/arch/sparc/mm/srmmu.c index c18475eaa..d5593b5d2 100644 --- a/arch/sparc/mm/srmmu.c +++ b/arch/sparc/mm/srmmu.c @@ -1341,7 +1341,7 @@ void __init srmmu_paging_init(void) zones_size[ZONE_HIGHMEM] = npages; zholes_size[ZONE_HIGHMEM] = npages - calc_highpages(); - free_area_init_node(0, &contig_page_data, NULL, zones_size, + free_area_init_node(0, &contig_page_data, zones_size, pfn_base, zholes_size); mem_map = contig_page_data.node_mem_map; } diff --git a/arch/sparc64/Kconfig.debug b/arch/sparc64/Kconfig.debug new file mode 100644 index 000000000..cd8d39fb9 --- /dev/null +++ b/arch/sparc64/Kconfig.debug @@ -0,0 +1,54 @@ +menu "Kernel hacking" + +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 KPROBES + bool "Kprobes" + depends on DEBUG_KERNEL + help + Kprobes allows you to trap at almost any kernel address and + execute a callback function. register_kprobe() establishes + a probepoint and specifies the callback. Kprobes is useful + for kernel debugging, non-intrusive instrumentation and testing. + If in doubt, say "N". + +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_BOOTMEM + depends on DEBUG_KERNEL + bool "Debug BOOTMEM initialization" + +# We have a custom atomic_dec_and_lock() implementation but it's not +# compatible with spinlock debugging so we need to fall back on +# the generic version in that case. +config HAVE_DEC_LOCK + bool + depends on SMP && !DEBUG_SPINLOCK + default y + +config MCOUNT + bool + depends on STACK_DEBUG + default y + +config FRAME_POINTER + bool + depends on MCOUNT + default y + +endmenu diff --git a/arch/sparc64/boot/Makefile b/arch/sparc64/boot/Makefile index 5eb14ff1c..6968a6da5 100644 --- a/arch/sparc64/boot/Makefile +++ b/arch/sparc64/boot/Makefile @@ -7,7 +7,7 @@ ROOT_IMG := /usr/src/root.img ELFTOAOUT := elftoaout -host-progs := piggyback +hostprogs-y := piggyback targets := image tftpboot.img vmlinux.aout quiet_cmd_elftoaout = ELF2AOUT $@ diff --git a/arch/sparc64/kernel/Makefile b/arch/sparc64/kernel/Makefile index 2caca47d8..093281bdf 100644 --- a/arch/sparc64/kernel/Makefile +++ b/arch/sparc64/kernel/Makefile @@ -5,7 +5,7 @@ EXTRA_AFLAGS := -ansi EXTRA_CFLAGS := -Werror -extra-y := head.o init_task.o vmlinux.lds.s +extra-y := head.o init_task.o vmlinux.lds obj-y := process.o setup.o cpu.o idprom.o \ traps.o devices.o auxio.o \ @@ -22,6 +22,7 @@ obj-$(CONFIG_BINFMT_AOUT32) += binfmt_aout32.o obj-$(CONFIG_MODULES) += module.o obj-$(CONFIG_US3_FREQ) += us3_cpufreq.o obj-$(CONFIG_US2E_FREQ) += us2e_cpufreq.o +obj-$(CONFIG_KPROBES) += kprobes.o ifdef CONFIG_SUNOS_EMUL obj-y += sys_sunos32.o sunos_ioctl32.o diff --git a/arch/sparc64/kernel/binfmt_elf32.c b/arch/sparc64/kernel/binfmt_elf32.c index a28046203..fb3e3b780 100644 --- a/arch/sparc64/kernel/binfmt_elf32.c +++ b/arch/sparc64/kernel/binfmt_elf32.c @@ -27,12 +27,12 @@ typedef elf_greg_t elf_gregset_t[ELF_NGREG]; #define ELF_CORE_COPY_REGS(__elf_regs, __pt_regs) \ do { unsigned int *dest = &(__elf_regs[0]); \ struct pt_regs *src = (__pt_regs); \ - unsigned int *sp; \ + unsigned int __user *sp; \ int i; \ for(i = 0; i < 16; i++) \ dest[i] = (unsigned int) src->u_regs[i];\ /* Don't try this at home kids... */ \ - sp = (unsigned int *) (src->u_regs[14] & \ + sp = (unsigned int __user *) (src->u_regs[14] & \ 0x00000000fffffffc); \ for(i = 0; i < 16; i++) \ __get_user(dest[i+16], &sp[i]); \ diff --git a/arch/sparc64/kernel/chmc.c b/arch/sparc64/kernel/chmc.c index 2296525af..97cf912f0 100644 --- a/arch/sparc64/kernel/chmc.c +++ b/arch/sparc64/kernel/chmc.c @@ -72,7 +72,7 @@ struct mctrl_info { struct obp_mem_layout layout_prop; int layout_size; - void *regs; + void __iomem *regs; u64 timing_control1; u64 timing_control2; diff --git a/arch/sparc64/kernel/etrap.S b/arch/sparc64/kernel/etrap.S index 9468eacdc..d50b755c7 100644 --- a/arch/sparc64/kernel/etrap.S +++ b/arch/sparc64/kernel/etrap.S @@ -17,7 +17,8 @@ #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) +#define ETRAP_PSTATE2 \ + (PSTATE_RMO | PSTATE_PEF | PSTATE_PRIV | PSTATE_IE) /* * On entry, %g7 is return address - 0x4. @@ -27,91 +28,91 @@ .text .align 64 .globl etrap, etrap_irq, etraptl1 -etrap: rdpr %pil, %g2 ! Single Group +etrap: rdpr %pil, %g2 etrap_irq: - rdpr %tstate, %g1 ! Single Group - sllx %g2, 20, %g3 ! IEU0 Group - andcc %g1, TSTATE_PRIV, %g0 ! IEU1 - or %g1, %g3, %g1 ! IEU0 Group - bne,pn %xcc, 1f ! CTI - sub %sp, STACKFRAME_SZ+TRACEREG_SZ-STACK_BIAS, %g2 ! IEU1 - wrpr %g0, 7, %cleanwin ! Single Group+4bubbles - - sethi %hi(TASK_REGOFF), %g2 ! IEU0 Group - sethi %hi(TSTATE_PEF), %g3 ! IEU1 - or %g2, %lo(TASK_REGOFF), %g2 ! IEU0 Group - and %g1, %g3, %g3 ! IEU1 - brnz,pn %g3, 1f ! CTI+IEU1 Group - add %g6, %g2, %g2 ! IEU0 - wr %g0, 0, %fprs ! Single Group+4bubbles -1: rdpr %tpc, %g3 ! Single Group - - stx %g1, [%g2 + STACKFRAME_SZ + PT_V9_TSTATE] ! Store Group - rdpr %tnpc, %g1 ! Single Group - stx %g3, [%g2 + STACKFRAME_SZ + PT_V9_TPC] ! Store Group - rd %y, %g3 ! Single Group+4bubbles - stx %g1, [%g2 + STACKFRAME_SZ + PT_V9_TNPC] ! Store Group - st %g3, [%g2 + STACKFRAME_SZ + PT_V9_Y] ! Store Group - save %g2, -STACK_BIAS, %sp ! Ordering here is critical ! Single Group - mov %g6, %l6 ! IEU0 Group - - bne,pn %xcc, 3f ! CTI - mov PRIMARY_CONTEXT, %l4 ! IEU1 - rdpr %canrestore, %g3 ! Single Group+4bubbles - rdpr %wstate, %g2 ! Single Group+4bubbles - wrpr %g0, 0, %canrestore ! Single Group+4bubbles - sll %g2, 3, %g2 ! IEU0 Group - mov 1, %l5 ! IEU1 - stb %l5, [%l6 + TI_FPDEPTH] ! Store - - wrpr %g3, 0, %otherwin ! Single Group+4bubbles - wrpr %g2, 0, %wstate ! Single Group+4bubbles - stxa %g0, [%l4] ASI_DMMU ! Store Group - flush %l6 ! Single Group+9bubbles - wr %g0, ASI_AIUS, %asi ! Single Group+4bubbles -2: wrpr %g0, 0x0, %tl ! Single Group+4bubbles - mov %g4, %l4 ! IEU1 - mov %g5, %l5 ! IEU0 Group - - mov %g7, %l2 ! IEU1 - wrpr %g0, ETRAP_PSTATE1, %pstate ! Single Group+4bubbles - stx %g1, [%sp + PTREGS_OFF + PT_V9_G1] ! Store Group - stx %g2, [%sp + PTREGS_OFF + PT_V9_G2] ! Store Group - stx %g3, [%sp + PTREGS_OFF + PT_V9_G3] ! Store Group - stx %g4, [%sp + PTREGS_OFF + PT_V9_G4] ! Store Group - stx %g5, [%sp + PTREGS_OFF + PT_V9_G5] ! Store Group - stx %g6, [%sp + PTREGS_OFF + PT_V9_G6] ! Store Group - - stx %g7, [%sp + PTREGS_OFF + PT_V9_G7] ! Store Group - stx %i0, [%sp + PTREGS_OFF + PT_V9_I0] ! Store Group - stx %i1, [%sp + PTREGS_OFF + PT_V9_I1] ! Store Group - stx %i2, [%sp + PTREGS_OFF + PT_V9_I2] ! Store Group - stx %i3, [%sp + PTREGS_OFF + PT_V9_I3] ! Store Group - stx %i4, [%sp + PTREGS_OFF + PT_V9_I4] ! Store Group - stx %i5, [%sp + PTREGS_OFF + PT_V9_I5] ! Store Group - - stx %i6, [%sp + PTREGS_OFF + PT_V9_I6] ! Store Group - stx %i7, [%sp + PTREGS_OFF + PT_V9_I7] ! Store Group - wrpr %g0, ETRAP_PSTATE2, %pstate ! Single Group+4bubbles - mov %l6, %g6 ! IEU0 - jmpl %l2 + 0x4, %g0 ! CTI Group - ldx [%g6 + TI_TASK], %g4 ! Load + rdpr %tstate, %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] + st %g3, [%g2 + STACKFRAME_SZ + PT_V9_Y] + save %g2, -STACK_BIAS, %sp ! Ordering here is critical + 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 + stxa %g0, [%l4] ASI_DMMU + flush %l6 + wr %g0, ASI_AIUS, %asi +2: wrpr %g0, 0x0, %tl + mov %g4, %l4 + mov %g5, %l5 + + mov %g7, %l2 + wrpr %g0, ETRAP_PSTATE1, %pstate + stx %g1, [%sp + PTREGS_OFF + PT_V9_G1] + stx %g2, [%sp + PTREGS_OFF + PT_V9_G2] + stx %g3, [%sp + PTREGS_OFF + PT_V9_G3] + 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] + 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] + stx %i7, [%sp + PTREGS_OFF + PT_V9_I7] + wrpr %g0, ETRAP_PSTATE2, %pstate + mov %l6, %g6 + jmpl %l2 + 0x4, %g0 + ldx [%g6 + TI_TASK], %g4 nop nop nop -3: ldub [%l6 + TI_FPDEPTH], %l5 ! Load Group - add %l6, TI_FPSAVED + 1, %l4 ! IEU0 - srl %l5, 1, %l3 ! IEU0 Group - add %l5, 2, %l5 ! IEU1 - stb %l5, [%l6 + TI_FPDEPTH] ! Store - ba,pt %xcc, 2b ! CTI - stb %g0, [%l4 + %l3] ! Store Group +3: 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: + * 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 @@ -166,81 +167,81 @@ etraptl1: /* Save tstate/tpc/tnpc of TL 1-->4 and the tl register itself. wrpr %g1, %tl stx %g1, [%g2 + STACK_BIAS + 0x80] - rdpr %tstate, %g1 ! Single Group+4bubbles - sub %g2, STACKFRAME_SZ + TRACEREG_SZ - STACK_BIAS, %g2 ! IEU1 - ba,pt %xcc, 1b ! CTI Group - andcc %g1, TSTATE_PRIV, %g0 ! IEU0 + rdpr %tstate, %g1 + sub %g2, STACKFRAME_SZ + TRACEREG_SZ - STACK_BIAS, %g2 + ba,pt %xcc, 1b + andcc %g1, TSTATE_PRIV, %g0 .align 64 .globl scetrap -scetrap: rdpr %pil, %g2 ! Single Group - rdpr %tstate, %g1 ! Single Group - sllx %g2, 20, %g3 ! IEU0 Group - andcc %g1, TSTATE_PRIV, %g0 ! IEU1 - or %g1, %g3, %g1 ! IEU0 Group - bne,pn %xcc, 1f ! CTI - sub %sp, (STACKFRAME_SZ+TRACEREG_SZ-STACK_BIAS), %g2 ! IEU1 - wrpr %g0, 7, %cleanwin ! Single Group+4bubbles - - sllx %g1, 51, %g3 ! IEU0 Group - sethi %hi(TASK_REGOFF), %g2 ! IEU1 - or %g2, %lo(TASK_REGOFF), %g2 ! IEU0 Group - brlz,pn %g3, 1f ! CTI+IEU1 - add %g6, %g2, %g2 ! IEU0 Group - wr %g0, 0, %fprs ! Single Group+4bubbles -1: rdpr %tpc, %g3 ! Single Group - stx %g1, [%g2 + STACKFRAME_SZ + PT_V9_TSTATE] ! Store Group - - rdpr %tnpc, %g1 ! Single Group - stx %g3, [%g2 + STACKFRAME_SZ + PT_V9_TPC] ! Store Group - stx %g1, [%g2 + STACKFRAME_SZ + PT_V9_TNPC] ! Store Group - save %g2, -STACK_BIAS, %sp ! Ordering here is critical ! Single Group - mov %g6, %l6 ! IEU0 Group - bne,pn %xcc, 2f ! CTI - mov ASI_P, %l7 ! IEU1 - rdpr %canrestore, %g3 ! Single Group+4bubbles - - rdpr %wstate, %g2 ! Single Group+4bubbles - wrpr %g0, 0, %canrestore ! Single Group+4bubbles - sll %g2, 3, %g2 ! IEU0 Group - mov PRIMARY_CONTEXT, %l4 ! IEU1 - wrpr %g3, 0, %otherwin ! Single Group+4bubbles - wrpr %g2, 0, %wstate ! Single Group+4bubbles - stxa %g0, [%l4] ASI_DMMU ! Store - flush %l6 ! Single Group+9bubbles - - mov ASI_AIUS, %l7 ! IEU0 Group -2: mov %g4, %l4 ! IEU1 - mov %g5, %l5 ! IEU0 Group - add %g7, 0x4, %l2 ! IEU1 - wrpr %g0, ETRAP_PSTATE1, %pstate ! Single Group+4bubbles - stx %g1, [%sp + PTREGS_OFF + PT_V9_G1] ! Store Group - stx %g2, [%sp + PTREGS_OFF + PT_V9_G2] ! Store Group - sllx %l7, 24, %l7 ! IEU0 - - stx %g3, [%sp + PTREGS_OFF + PT_V9_G3] ! Store Group - rdpr %cwp, %l0 ! Single Group - stx %g4, [%sp + PTREGS_OFF + PT_V9_G4] ! Store Group - stx %g5, [%sp + PTREGS_OFF + PT_V9_G5] ! Store Group - stx %g6, [%sp + PTREGS_OFF + PT_V9_G6] ! Store Group - stx %g7, [%sp + PTREGS_OFF + PT_V9_G7] ! Store Group - or %l7, %l0, %l7 ! IEU0 - sethi %hi(TSTATE_RMO | TSTATE_PEF), %l0 ! IEU1 - - or %l7, %l0, %l7 ! IEU0 Group - wrpr %l2, %tnpc ! Single Group+4bubbles - wrpr %l7, (TSTATE_PRIV | TSTATE_IE), %tstate ! Single Group+4bubbles - stx %i0, [%sp + PTREGS_OFF + PT_V9_I0] ! Store Group - stx %i1, [%sp + PTREGS_OFF + PT_V9_I1] ! Store Group - stx %i2, [%sp + PTREGS_OFF + PT_V9_I2] ! Store Group - stx %i3, [%sp + PTREGS_OFF + PT_V9_I3] ! Store Group - stx %i4, [%sp + PTREGS_OFF + PT_V9_I4] ! Store Group - - stx %i5, [%sp + PTREGS_OFF + PT_V9_I5] ! Store Group - stx %i6, [%sp + PTREGS_OFF + PT_V9_I6] ! Store Group - mov %l6, %g6 ! IEU1 - stx %i7, [%sp + PTREGS_OFF + PT_V9_I7] ! Store Group - ldx [%g6 + TI_TASK], %g4 ! Load Group +scetrap: rdpr %pil, %g2 + rdpr %tstate, %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 + + sllx %g1, 51, %g3 + sethi %hi(TASK_REGOFF), %g2 + or %g2, %lo(TASK_REGOFF), %g2 + brlz,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] + stx %g1, [%g2 + STACKFRAME_SZ + PT_V9_TNPC] + save %g2, -STACK_BIAS, %sp ! Ordering here is critical + mov %g6, %l6 + bne,pn %xcc, 2f + mov ASI_P, %l7 + rdpr %canrestore, %g3 + + rdpr %wstate, %g2 + wrpr %g0, 0, %canrestore + sll %g2, 3, %g2 + mov PRIMARY_CONTEXT, %l4 + wrpr %g3, 0, %otherwin + wrpr %g2, 0, %wstate + stxa %g0, [%l4] ASI_DMMU + flush %l6 + + mov ASI_AIUS, %l7 +2: mov %g4, %l4 + mov %g5, %l5 + add %g7, 0x4, %l2 + wrpr %g0, ETRAP_PSTATE1, %pstate + 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] + ldx [%g6 + TI_TASK], %g4 done nop nop diff --git a/arch/sparc64/kernel/kprobes.c b/arch/sparc64/kernel/kprobes.c new file mode 100644 index 000000000..bc5cc1d17 --- /dev/null +++ b/arch/sparc64/kernel/kprobes.c @@ -0,0 +1,373 @@ +/* arch/sparc64/kernel/kprobes.c + * + * Copyright (C) 2004 David S. Miller + */ + +#include +#include +#include + +#include +#include + +/* We do not have hardware single-stepping on sparc64. + * So we implement software single-stepping with breakpoint + * traps. The top-level scheme is similar to that used + * in the x86 kprobes implementation. + * + * In the kprobe->insn[] array we store the original + * instruction at index zero and a break instruction at + * index one. + * + * When we hit a kprobe we: + * - Run the pre-handler + * - Remember "regs->tnpc" and interrupt level stored in + * "regs->tstate" so we can restore them later + * - Disable PIL interrupts + * - Set regs->tpc to point to kprobe->insn[0] + * - Set regs->tnpc to point to kprobe->insn[1] + * - Mark that we are actively in a kprobe + * + * At this point we wait for the second breakpoint at + * kprobe->insn[1] to hit. When it does we: + * - Run the post-handler + * - Set regs->tpc to "remembered" regs->tnpc stored above, + * restore the PIL interrupt level in "regs->tstate" as well + * - Make any adjustments necessary to regs->tnpc in order + * to handle relative branches correctly. See below. + * - Mark that we are no longer actively in a kprobe. + */ + +void arch_prepare_kprobe(struct kprobe *p) +{ + p->insn[0] = *p->addr; + p->insn[1] = BREAKPOINT_INSTRUCTION_2; +} + +/* kprobe_status settings */ +#define KPROBE_HIT_ACTIVE 0x00000001 +#define KPROBE_HIT_SS 0x00000002 + +static struct kprobe *current_kprobe; +static unsigned long current_kprobe_orig_tnpc; +static unsigned long current_kprobe_orig_tstate_pil; +static unsigned int kprobe_status; + +static inline void prepare_singlestep(struct kprobe *p, struct pt_regs *regs) +{ + current_kprobe_orig_tnpc = regs->tnpc; + current_kprobe_orig_tstate_pil = (regs->tstate & TSTATE_PIL); + regs->tstate |= TSTATE_PIL; + + regs->tpc = (unsigned long) &p->insn[0]; + regs->tnpc = (unsigned long) &p->insn[1]; +} + +static inline void disarm_kprobe(struct kprobe *p, struct pt_regs *regs) +{ + *p->addr = p->opcode; + flushi(p->addr); + + regs->tpc = (unsigned long) p->addr; + regs->tnpc = current_kprobe_orig_tnpc; + regs->tstate = ((regs->tstate & ~TSTATE_PIL) | + current_kprobe_orig_tstate_pil); +} + +static int kprobe_handler(struct pt_regs *regs) +{ + struct kprobe *p; + void *addr = (void *) regs->tpc; + int ret = 0; + + preempt_disable(); + + if (kprobe_running()) { + /* We *are* holding lock here, so this is safe. + * Disarm the probe we just hit, and ignore it. + */ + p = get_kprobe(addr); + if (p) { + disarm_kprobe(p, regs); + ret = 1; + } else { + p = current_kprobe; + if (p->break_handler && p->break_handler(p, regs)) + goto ss_probe; + } + /* If it's not ours, can't be delete race, (we hold lock). */ + goto no_kprobe; + } + + lock_kprobes(); + p = get_kprobe(addr); + if (!p) { + unlock_kprobes(); + if (*(u32 *)addr != BREAKPOINT_INSTRUCTION) { + /* + * The breakpoint instruction was removed right + * after we hit it. Another cpu has removed + * either a probepoint or a debugger breakpoint + * at this address. In either case, no further + * handling of this interrupt is appropriate. + */ + ret = 1; + } + /* Not one of ours: let kernel handle it */ + goto no_kprobe; + } + + kprobe_status = KPROBE_HIT_ACTIVE; + current_kprobe = p; + if (p->pre_handler(p, regs)) + return 1; + +ss_probe: + prepare_singlestep(p, regs); + kprobe_status = KPROBE_HIT_SS; + return 1; + +no_kprobe: + preempt_enable_no_resched(); + return ret; +} + +/* If INSN is a relative control transfer instruction, + * return the corrected branch destination value. + * + * The original INSN location was REAL_PC, it actually + * executed at PC and produced destination address NPC. + */ +static unsigned long relbranch_fixup(u32 insn, unsigned long real_pc, + unsigned long pc, unsigned long npc) +{ + /* Branch not taken, no mods necessary. */ + if (npc == pc + 0x4UL) + return real_pc + 0x4UL; + + /* The three cases are call, branch w/prediction, + * and traditional branch. + */ + if ((insn & 0xc0000000) == 0x40000000 || + (insn & 0xc1c00000) == 0x00400000 || + (insn & 0xc1c00000) == 0x00800000) { + /* The instruction did all the work for us + * already, just apply the offset to the correct + * instruction location. + */ + return (real_pc + (npc - pc)); + } + + return real_pc + 0x4UL; +} + +/* If INSN is an instruction which writes it's PC location + * into a destination register, fix that up. + */ +static void retpc_fixup(struct pt_regs *regs, u32 insn, unsigned long real_pc) +{ + unsigned long *slot = NULL; + + /* Simplest cast is call, which always uses %o7 */ + if ((insn & 0xc0000000) == 0x40000000) { + slot = ®s->u_regs[UREG_I7]; + } + + /* Jmpl encodes the register inside of the opcode */ + if ((insn & 0xc1f80000) == 0x81c00000) { + unsigned long rd = ((insn >> 25) & 0x1f); + + if (rd <= 15) { + slot = ®s->u_regs[rd]; + } else { + /* Hard case, it goes onto the stack. */ + flushw_all(); + + rd -= 16; + slot = (unsigned long *) + (regs->u_regs[UREG_FP] + STACK_BIAS); + slot += rd; + } + } + if (slot != NULL) + *slot = real_pc; +} + +/* + * Called after single-stepping. p->addr is the address of the + * instruction whose first byte has been replaced by the breakpoint + * instruction. To avoid the SMP problems that can occur when we + * temporarily put back the original opcode to single-step, we + * single-stepped a copy of the instruction. The address of this + * copy is p->insn. + * + * This function prepares to return from the post-single-step + * breakpoint trap. + */ +static void resume_execution(struct kprobe *p, struct pt_regs *regs) +{ + u32 insn = p->insn[0]; + + regs->tpc = current_kprobe_orig_tnpc; + regs->tnpc = relbranch_fixup(insn, + (unsigned long) p->addr, + (unsigned long) &p->insn[0], + regs->tnpc); + retpc_fixup(regs, insn, (unsigned long) p->addr); + + regs->tstate = ((regs->tstate & ~TSTATE_PIL) | + current_kprobe_orig_tstate_pil); +} + +static inline int post_kprobe_handler(struct pt_regs *regs) +{ + if (!kprobe_running()) + return 0; + + if (current_kprobe->post_handler) + current_kprobe->post_handler(current_kprobe, regs, 0); + + resume_execution(current_kprobe, regs); + + unlock_kprobes(); + preempt_enable_no_resched(); + + return 1; +} + +/* Interrupts disabled, kprobe_lock held. */ +static inline int kprobe_fault_handler(struct pt_regs *regs, int trapnr) +{ + if (current_kprobe->fault_handler + && current_kprobe->fault_handler(current_kprobe, regs, trapnr)) + return 1; + + if (kprobe_status & KPROBE_HIT_SS) { + resume_execution(current_kprobe, regs); + + unlock_kprobes(); + preempt_enable_no_resched(); + } + return 0; +} + +/* + * Wrapper routine to for handling exceptions. + */ +int kprobe_exceptions_notify(struct notifier_block *self, unsigned long val, + void *data) +{ + struct die_args *args = (struct die_args *)data; + switch (val) { + case DIE_DEBUG: + if (kprobe_handler(args->regs)) + return NOTIFY_STOP; + break; + case DIE_DEBUG_2: + if (post_kprobe_handler(args->regs)) + return NOTIFY_STOP; + break; + case DIE_GPF: + if (kprobe_running() && + kprobe_fault_handler(args->regs, args->trapnr)) + return NOTIFY_STOP; + break; + case DIE_PAGE_FAULT: + if (kprobe_running() && + kprobe_fault_handler(args->regs, args->trapnr)) + return NOTIFY_STOP; + break; + default: + break; + } + return NOTIFY_DONE; +} + +asmlinkage void kprobe_trap(unsigned long trap_level, struct pt_regs *regs) +{ + BUG_ON(trap_level != 0x170 && trap_level != 0x171); + + if (user_mode(regs)) { + local_irq_enable(); + bad_trap(regs, trap_level); + return; + } + + /* trap_level == 0x170 --> ta 0x70 + * trap_level == 0x171 --> ta 0x71 + */ + if (notify_die((trap_level == 0x170) ? DIE_DEBUG : DIE_DEBUG_2, + (trap_level == 0x170) ? "debug" : "debug_2", + regs, 0, trap_level, SIGTRAP) != NOTIFY_STOP) + bad_trap(regs, trap_level); +} + +/* Jprobes support. */ +static struct pt_regs jprobe_saved_regs; +static struct pt_regs *jprobe_saved_regs_location; +static struct sparc_stackf jprobe_saved_stack; + +int setjmp_pre_handler(struct kprobe *p, struct pt_regs *regs) +{ + struct jprobe *jp = container_of(p, struct jprobe, kp); + + jprobe_saved_regs_location = regs; + memcpy(&jprobe_saved_regs, regs, sizeof(*regs)); + + /* Save a whole stack frame, this gets arguments + * pushed onto the stack after using up all the + * arg registers. + */ + memcpy(&jprobe_saved_stack, + (char *) (regs->u_regs[UREG_FP] + STACK_BIAS), + sizeof(jprobe_saved_stack)); + + regs->tpc = (unsigned long) jp->entry; + regs->tnpc = ((unsigned long) jp->entry) + 0x4UL; + regs->tstate |= TSTATE_PIL; + + return 1; +} + +void jprobe_return(void) +{ + preempt_enable_no_resched(); + __asm__ __volatile__( + ".globl jprobe_return_trap_instruction\n" +"jprobe_return_trap_instruction:\n\t" + "ta 0x70"); +} + +extern void jprobe_return_trap_instruction(void); + +extern void __show_regs(struct pt_regs * regs); + +int longjmp_break_handler(struct kprobe *p, struct pt_regs *regs) +{ + u32 *addr = (u32 *) regs->tpc; + + if (addr == (u32 *) jprobe_return_trap_instruction) { + if (jprobe_saved_regs_location != regs) { + printk("JPROBE: Current regs (%p) does not match " + "saved regs (%p).\n", + regs, jprobe_saved_regs_location); + printk("JPROBE: Saved registers\n"); + __show_regs(jprobe_saved_regs_location); + printk("JPROBE: Current registers\n"); + __show_regs(regs); + BUG(); + } + /* Restore old register state. Do pt_regs + * first so that UREG_FP is the original one for + * the stack frame restore. + */ + memcpy(regs, &jprobe_saved_regs, sizeof(*regs)); + + memcpy((char *) (regs->u_regs[UREG_FP] + STACK_BIAS), + &jprobe_saved_stack, + sizeof(jprobe_saved_stack)); + + return 1; + } + return 0; +} diff --git a/arch/sparc64/kernel/pci_common.c b/arch/sparc64/kernel/pci_common.c index fda652414..58310aace 100644 --- a/arch/sparc64/kernel/pci_common.c +++ b/arch/sparc64/kernel/pci_common.c @@ -15,18 +15,13 @@ */ void __init pci_fixup_host_bridge_self(struct pci_bus *pbus) { - struct list_head *walk = &pbus->devices; - - walk = walk->next; - while (walk != &pbus->devices) { - struct pci_dev *pdev = pci_dev_b(walk); + struct pci_dev *pdev; + list_for_each_entry(pdev, &pbus->devices, bus_list) { if (pdev->class >> 8 == PCI_CLASS_BRIDGE_HOST) { pbus->self = pdev; return; } - - walk = walk->next; } prom_printf("PCI: Critical error, cannot find host bridge PDEV.\n"); @@ -217,31 +212,18 @@ void __init pci_fill_in_pbm_cookies(struct pci_bus *pbus, struct pci_pbm_info *pbm, int prom_node) { - struct list_head *walk = &pbus->devices; - - /* This loop is coded like this because the cookie - * fillin routine can delete devices from the tree. - */ - walk = walk->next; - while (walk != &pbus->devices) { - struct pci_dev *pdev = pci_dev_b(walk); - struct list_head *walk_next = walk->next; + struct pci_dev *pdev, *pdev_next; + struct pci_bus *this_pbus, *pbus_next; + /* This must be _safe because the cookie fillin + routine can delete devices from the tree. */ + list_for_each_entry_safe(pdev, pdev_next, &pbus->devices, bus_list) pdev_cookie_fillin(pbm, pdev, prom_node); - walk = walk_next; - } - - walk = &pbus->children; - walk = walk->next; - while (walk != &pbus->children) { - struct pci_bus *this_pbus = pci_bus_b(walk); + list_for_each_entry_safe(this_pbus, pbus_next, &pbus->children, node) { struct pcidev_cookie *pcp = this_pbus->self->sysdata; - struct list_head *walk_next = walk->next; pci_fill_in_pbm_cookies(this_pbus, pbm, pcp->prom_node); - - walk = walk_next; } } @@ -431,14 +413,14 @@ static void __init pdev_record_assignments(struct pci_pbm_info *pbm, void __init pci_record_assignments(struct pci_pbm_info *pbm, struct pci_bus *pbus) { - struct list_head *walk = &pbus->devices; + struct pci_dev *dev; + struct pci_bus *bus; - for (walk = walk->next; walk != &pbus->devices; walk = walk->next) - pdev_record_assignments(pbm, pci_dev_b(walk)); + list_for_each_entry(dev, &pbus->devices, bus_list) + pdev_record_assignments(pbm, dev); - walk = &pbus->children; - for (walk = walk->next; walk != &pbus->children; walk = walk->next) - pci_record_assignments(pbm, pci_bus_b(walk)); + list_for_each_entry(bus, &pbus->children, node) + pci_record_assignments(pbm, bus); } /* Return non-zero if PDEV has implicit I/O resources even @@ -549,14 +531,14 @@ static void __init pdev_assign_unassigned(struct pci_pbm_info *pbm, void __init pci_assign_unassigned(struct pci_pbm_info *pbm, struct pci_bus *pbus) { - struct list_head *walk = &pbus->devices; + struct pci_dev *dev; + struct pci_bus *bus; - for (walk = walk->next; walk != &pbus->devices; walk = walk->next) - pdev_assign_unassigned(pbm, pci_dev_b(walk)); + list_for_each_entry(dev, &pbus->devices, bus_list) + pdev_assign_unassigned(pbm, dev); - walk = &pbus->children; - for (walk = walk->next; walk != &pbus->children; walk = walk->next) - pci_assign_unassigned(pbm, pci_bus_b(walk)); + list_for_each_entry(bus, &pbus->children, node) + pci_assign_unassigned(pbm, bus); } static int __init pci_intmap_match(struct pci_dev *pdev, unsigned int *interrupt) @@ -797,14 +779,14 @@ have_irq: void __init pci_fixup_irq(struct pci_pbm_info *pbm, struct pci_bus *pbus) { - struct list_head *walk = &pbus->devices; + struct pci_dev *dev; + struct pci_bus *bus; - for (walk = walk->next; walk != &pbus->devices; walk = walk->next) - pdev_fixup_irq(pci_dev_b(walk)); + list_for_each_entry(dev, &pbus->devices, bus_list) + pdev_fixup_irq(dev); - walk = &pbus->children; - for (walk = walk->next; walk != &pbus->children; walk = walk->next) - pci_fixup_irq(pbm, pci_bus_b(walk)); + list_for_each_entry(bus, &pbus->children, node) + pci_fixup_irq(pbm, bus); } static void pdev_setup_busmastering(struct pci_dev *pdev, int is_66mhz) @@ -897,7 +879,7 @@ static void pdev_setup_busmastering(struct pci_dev *pdev, int is_66mhz) void pci_determine_66mhz_disposition(struct pci_pbm_info *pbm, struct pci_bus *pbus) { - struct list_head *walk; + struct pci_dev *pdev; int all_are_66mhz; u16 status; @@ -906,11 +888,8 @@ void pci_determine_66mhz_disposition(struct pci_pbm_info *pbm, goto out; } - walk = &pbus->devices; all_are_66mhz = 1; - for (walk = walk->next; walk != &pbus->devices; walk = walk->next) { - struct pci_dev *pdev = pci_dev_b(walk); - + list_for_each_entry(pdev, &pbus->devices, bus_list) { pci_read_config_word(pdev, PCI_STATUS, &status); if (!(status & PCI_STATUS_66MHZ)) { all_are_66mhz = 0; @@ -929,17 +908,17 @@ out: void pci_setup_busmastering(struct pci_pbm_info *pbm, struct pci_bus *pbus) { - struct list_head *walk = &pbus->devices; + struct pci_dev *dev; + struct pci_bus *bus; int is_66mhz; is_66mhz = pbm->is_66mhz_capable && pbm->all_devs_66mhz; - for (walk = walk->next; walk != &pbus->devices; walk = walk->next) - pdev_setup_busmastering(pci_dev_b(walk), is_66mhz); + list_for_each_entry(dev, &pbus->devices, bus_list) + pdev_setup_busmastering(dev, is_66mhz); - walk = &pbus->children; - for (walk = walk->next; walk != &pbus->children; walk = walk->next) - pci_setup_busmastering(pbm, pci_bus_b(walk)); + list_for_each_entry(bus, &pbus->children, node) + pci_setup_busmastering(pbm, bus); } void pci_register_legacy_regions(struct resource *io_res, @@ -987,10 +966,10 @@ void pci_scan_for_target_abort(struct pci_controller_info *p, struct pci_pbm_info *pbm, struct pci_bus *pbus) { - struct list_head *walk = &pbus->devices; + struct pci_dev *pdev; + struct pci_bus *bus; - for (walk = walk->next; walk != &pbus->devices; walk = walk->next) { - struct pci_dev *pdev = pci_dev_b(walk); + list_for_each_entry(pdev, &pbus->devices, bus_list) { u16 status, error_bits; pci_read_config_word(pdev, PCI_STATUS, &status); @@ -1005,19 +984,18 @@ void pci_scan_for_target_abort(struct pci_controller_info *p, } } - walk = &pbus->children; - for (walk = walk->next; walk != &pbus->children; walk = walk->next) - pci_scan_for_target_abort(p, pbm, pci_bus_b(walk)); + list_for_each_entry(bus, &pbus->children, node) + pci_scan_for_target_abort(p, pbm, bus); } void pci_scan_for_master_abort(struct pci_controller_info *p, struct pci_pbm_info *pbm, struct pci_bus *pbus) { - struct list_head *walk = &pbus->devices; + struct pci_dev *pdev; + struct pci_bus *bus; - for (walk = walk->next; walk != &pbus->devices; walk = walk->next) { - struct pci_dev *pdev = pci_dev_b(walk); + list_for_each_entry(pdev, &pbus->devices, bus_list) { u16 status, error_bits; pci_read_config_word(pdev, PCI_STATUS, &status); @@ -1031,19 +1009,18 @@ void pci_scan_for_master_abort(struct pci_controller_info *p, } } - walk = &pbus->children; - for (walk = walk->next; walk != &pbus->children; walk = walk->next) - pci_scan_for_master_abort(p, pbm, pci_bus_b(walk)); + list_for_each_entry(bus, &pbus->children, node) + pci_scan_for_master_abort(p, pbm, bus); } void pci_scan_for_parity_error(struct pci_controller_info *p, struct pci_pbm_info *pbm, struct pci_bus *pbus) { - struct list_head *walk = &pbus->devices; + struct pci_dev *pdev; + struct pci_bus *bus; - for (walk = walk->next; walk != &pbus->devices; walk = walk->next) { - struct pci_dev *pdev = pci_dev_b(walk); + list_for_each_entry(pdev, &pbus->devices, bus_list) { u16 status, error_bits; pci_read_config_word(pdev, PCI_STATUS, &status); @@ -1058,7 +1035,6 @@ void pci_scan_for_parity_error(struct pci_controller_info *p, } } - walk = &pbus->children; - for (walk = walk->next; walk != &pbus->children; walk = walk->next) - pci_scan_for_parity_error(p, pbm, pci_bus_b(walk)); + list_for_each_entry(bus, &pbus->children, node) + pci_scan_for_parity_error(p, pbm, bus); } diff --git a/arch/sparc64/kernel/pci_impl.h b/arch/sparc64/kernel/pci_impl.h index 302a0a2ce..6c3205962 100644 --- a/arch/sparc64/kernel/pci_impl.h +++ b/arch/sparc64/kernel/pci_impl.h @@ -11,7 +11,6 @@ #include #include -extern spinlock_t pci_controller_lock; extern struct pci_controller_info *pci_controller_root; extern int pci_num_controllers; diff --git a/arch/sparc64/kernel/pci_iommu.c b/arch/sparc64/kernel/pci_iommu.c index cca82a12e..94a52983c 100644 --- a/arch/sparc64/kernel/pci_iommu.c +++ b/arch/sparc64/kernel/pci_iommu.c @@ -56,6 +56,39 @@ static void __iommu_flushall(struct pci_iommu *iommu) } } +#define IOPTE_CONSISTENT(CTX) \ + (IOPTE_VALID | IOPTE_CACHE | \ + (((CTX) << 47) & IOPTE_CONTEXT)) + +#define IOPTE_STREAMING(CTX) \ + (IOPTE_CONSISTENT(CTX) | IOPTE_STBUF) + +/* Existing mappings are never marked invalid, instead they + * are pointed to a dummy page. + */ +#define IOPTE_IS_DUMMY(iommu, iopte) \ + ((iopte_val(*iopte) & IOPTE_PAGE) == (iommu)->dummy_page_pa) + +static void inline iopte_make_dummy(struct pci_iommu *iommu, iopte_t *iopte) +{ + unsigned long val = iopte_val(*iopte); + + val &= ~IOPTE_PAGE; + val |= iommu->dummy_page_pa; + + iopte_val(*iopte) = val; +} + +void pci_iommu_table_init(struct pci_iommu *iommu, int tsbsize) +{ + int i; + + tsbsize /= sizeof(iopte_t); + + for (i = 0; i < tsbsize; i++) + iopte_make_dummy(iommu, &iommu->page_table[i]); +} + static iopte_t *alloc_streaming_cluster(struct pci_iommu *iommu, unsigned long npages) { iopte_t *iopte, *limit, *first; @@ -79,7 +112,7 @@ static iopte_t *alloc_streaming_cluster(struct pci_iommu *iommu, unsigned long n first = iopte; for (;;) { - if (iopte_val(*iopte) == 0UL) { + if (IOPTE_IS_DUMMY(iommu, iopte)) { if ((iopte + (1 << cnum)) >= limit) ent = 0; else @@ -142,12 +175,12 @@ static iopte_t *alloc_consistent_cluster(struct pci_iommu *iommu, unsigned long iopte = iommu->page_table + (1 << (iommu->page_table_sz_bits - PBM_LOGCLUSTERS)); while (iopte > iommu->page_table) { iopte--; - if (!(iopte_val(*iopte) & IOPTE_VALID)) { + if (IOPTE_IS_DUMMY(iommu, iopte)) { unsigned long tmp = npages; while (--tmp) { iopte--; - if (iopte_val(*iopte) & IOPTE_VALID) + if (!IOPTE_IS_DUMMY(iommu, iopte)) break; } if (tmp == 0) { @@ -162,15 +195,6 @@ static iopte_t *alloc_consistent_cluster(struct pci_iommu *iommu, unsigned long return NULL; } -#define IOPTE_CONSISTENT(CTX) \ - (IOPTE_VALID | IOPTE_CACHE | \ - (((CTX) << 47) & IOPTE_CONTEXT)) - -#define IOPTE_STREAMING(CTX) \ - (IOPTE_CONSISTENT(CTX) | IOPTE_STBUF) - -#define IOPTE_INVALID 0UL - /* Allocate and map kernel buffer of size SIZE using consistent mode * DMA for PCI device PDEV. Return non-NULL cpu-side address if * successful and set *DMA_ADDRP to the PCI side dma address. @@ -261,7 +285,7 @@ void pci_free_consistent(struct pci_dev *pdev, size_t size, void *cpu, dma_addr_ limit = (iommu->page_table + (1 << (iommu->page_table_sz_bits - PBM_LOGCLUSTERS))); while (walk < limit) { - if (iopte_val(*walk) != IOPTE_INVALID) + if (!IOPTE_IS_DUMMY(iommu, walk)) break; walk++; } @@ -280,7 +304,7 @@ void pci_free_consistent(struct pci_dev *pdev, size_t size, void *cpu, dma_addr_ ctx = (iopte_val(*iopte) & IOPTE_CONTEXT) >> 47UL; for (i = 0; i < npages; i++, iopte++) - iopte_val(*iopte) = IOPTE_INVALID; + iopte_make_dummy(iommu, iopte); if (iommu->iommu_ctxflush) { pci_iommu_write(iommu->iommu_ctxflush, ctx); @@ -376,7 +400,7 @@ void pci_unmap_single(struct pci_dev *pdev, dma_addr_t bus_addr, size_t sz, int base = iommu->page_table + ((bus_addr - iommu->page_table_map_base) >> IO_PAGE_SHIFT); #ifdef DEBUG_PCI_IOMMU - if (iopte_val(*base) == IOPTE_INVALID) + if (IOPTE_IS_DUMMY(iommu, base)) printk("pci_unmap_single called on non-mapped region %08x,%08x from %016lx\n", bus_addr, sz, __builtin_return_address(0)); #endif @@ -415,7 +439,7 @@ void pci_unmap_single(struct pci_dev *pdev, dma_addr_t bus_addr, size_t sz, int } /* Step 2: Clear out first TSB entry. */ - iopte_val(*base) = IOPTE_INVALID; + iopte_make_dummy(iommu, base); free_streaming_cluster(iommu, bus_addr - iommu->page_table_map_base, npages, ctx); @@ -611,7 +635,7 @@ void pci_unmap_sg(struct pci_dev *pdev, struct scatterlist *sglist, int nelems, ((bus_addr - iommu->page_table_map_base) >> IO_PAGE_SHIFT); #ifdef DEBUG_PCI_IOMMU - if (iopte_val(*base) == IOPTE_INVALID) + if (IOPTE_IS_DUMMY(iommu, base)) printk("pci_unmap_sg called on non-mapped region %016lx,%d from %016lx\n", sglist->dma_address, nelems, __builtin_return_address(0)); #endif @@ -648,7 +672,7 @@ void pci_unmap_sg(struct pci_dev *pdev, struct scatterlist *sglist, int nelems, } /* Step 2: Clear out first TSB entry. */ - iopte_val(*base) = IOPTE_INVALID; + iopte_make_dummy(iommu, base); free_streaming_cluster(iommu, bus_addr - iommu->page_table_map_base, npages, ctx); diff --git a/arch/sparc64/kernel/pci_sabre.c b/arch/sparc64/kernel/pci_sabre.c index 6a266a7bf..2bf247792 100644 --- a/arch/sparc64/kernel/pci_sabre.c +++ b/arch/sparc64/kernel/pci_sabre.c @@ -1113,10 +1113,9 @@ static void __init sabre_base_address_update(struct pci_dev *pdev, int resource) static void __init apb_init(struct pci_controller_info *p, struct pci_bus *sabre_bus) { - struct list_head *walk = &sabre_bus->devices; + struct pci_dev *pdev; - for (walk = walk->next; walk != &sabre_bus->devices; walk = walk->next) { - struct pci_dev *pdev = pci_dev_b(walk); + list_for_each_entry(pdev, &sabre_bus->devices, bus_list) { if (pdev->vendor == PCI_VENDOR_ID_SUN && pdev->device == PCI_DEVICE_ID_SUN_SIMBA) { @@ -1178,10 +1177,9 @@ static struct pcidev_cookie *alloc_bridge_cookie(struct pci_pbm_info *pbm) static void __init sabre_scan_bus(struct pci_controller_info *p) { static int once; - struct pci_bus *sabre_bus; + struct pci_bus *sabre_bus, *pbus; struct pci_pbm_info *pbm; struct pcidev_cookie *cookie; - struct list_head *walk; int sabres_scanned; /* The APB bridge speaks to the Sabre host PCI bridge @@ -1217,9 +1215,7 @@ static void __init sabre_scan_bus(struct pci_controller_info *p) sabres_scanned = 0; - walk = &sabre_bus->children; - for (walk = walk->next; walk != &sabre_bus->children; walk = walk->next) { - struct pci_bus *pbus = pci_bus_b(walk); + list_for_each_entry(pbus, &sabre_bus->children, node) { if (pbus->number == p->pbm_A.pci_first_busno) { pbm = &p->pbm_A; @@ -1293,6 +1289,14 @@ static void __init sabre_iommu_init(struct pci_controller_info *p, * in pci_iommu.c */ + iommu->dummy_page = __get_free_pages(GFP_KERNEL, 0); + if (!iommu->dummy_page) { + prom_printf("PSYCHO_IOMMU: Error, gfp(dummy_page) failed.\n"); + prom_halt(); + } + memset((void *)iommu->dummy_page, 0, PAGE_SIZE); + iommu->dummy_page_pa = (unsigned long) __pa(iommu->dummy_page); + tsbbase = __get_free_pages(GFP_KERNEL, order = get_order(tsbsize * 1024 * 8)); if (!tsbbase) { prom_printf("SABRE_IOMMU: Error, gfp(tsb) failed.\n"); @@ -1301,7 +1305,7 @@ static void __init sabre_iommu_init(struct pci_controller_info *p, iommu->page_table = (iopte_t *)tsbbase; iommu->page_table_map_base = dvma_offset; iommu->dma_addr_mask = dma_mask; - memset((char *)tsbbase, 0, PAGE_SIZE << order); + pci_iommu_table_init(iommu, PAGE_SIZE << order); sabre_write(p->pbm_A.controller_regs + SABRE_IOMMU_TSBBASE, __pa(tsbbase)); @@ -1546,7 +1550,6 @@ void __init sabre_init(int pnode, char *model_name) struct linux_prom64_registers pr_regs[2]; struct pci_controller_info *p; struct pci_iommu *iommu; - unsigned long flags; int tsbsize, err; u32 busrange[2]; u32 vdma[2]; @@ -1594,10 +1597,8 @@ void __init sabre_init(int pnode, char *model_name) upa_portid = prom_getintdefault(pnode, "upa-portid", 0xff); - spin_lock_irqsave(&pci_controller_lock, flags); p->next = pci_controller_root; pci_controller_root = p; - spin_unlock_irqrestore(&pci_controller_lock, flags); p->pbm_A.portid = upa_portid; p->pbm_B.portid = upa_portid; diff --git a/arch/sparc64/kernel/ttable.S b/arch/sparc64/kernel/ttable.S index 40f7919aa..491bb3681 100644 --- a/arch/sparc64/kernel/ttable.S +++ b/arch/sparc64/kernel/ttable.S @@ -158,7 +158,7 @@ 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: BTRAP(0x170) BTRAP(0x171) BTRAP(0x172) +tl0_resv170: KPROBES_TRAP(0x170) KPROBES_TRAP(0x171) BTRAP(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) diff --git a/arch/sparc64/kernel/vmlinux.lds.S b/arch/sparc64/kernel/vmlinux.lds.S index 8faeee09f..a710d38d1 100644 --- a/arch/sparc64/kernel/vmlinux.lds.S +++ b/arch/sparc64/kernel/vmlinux.lds.S @@ -16,6 +16,7 @@ SECTIONS { *(.text) SCHED_TEXT + LOCK_TEXT *(.gnu.warning) } =0 _etext = .; @@ -52,9 +53,6 @@ SECTIONS __setup_start = .; .init.setup : { *(.init.setup) } __setup_end = .; - __start___param = .; - __param : { *(__param) } - __stop___param = .; __initcall_start = .; .initcall.init : { *(.initcall1.init) diff --git a/arch/sparc64/lib/PeeCeeI.c b/arch/sparc64/lib/PeeCeeI.c index b64aee993..3008d536e 100644 --- a/arch/sparc64/lib/PeeCeeI.c +++ b/arch/sparc64/lib/PeeCeeI.c @@ -7,7 +7,7 @@ #include #include -void outsb(unsigned long addr, const void *src, unsigned long count) +void outsb(void __iomem *addr, const void *src, unsigned long count) { const u8 *p = src; @@ -15,7 +15,7 @@ void outsb(unsigned long addr, const void *src, unsigned long count) outb(*p++, addr); } -void outsw(unsigned long addr, const void *src, unsigned long count) +void outsw(void __iomem *addr, const void *src, unsigned long count) { if(count) { u16 *ps = (u16 *)src; @@ -44,7 +44,7 @@ void outsw(unsigned long addr, const void *src, unsigned long count) } } -void outsl(unsigned long addr, const void *src, unsigned long count) +void outsl(void __iomem *addr, const void *src, unsigned long count) { if(count) { if((((u64)src) & 0x3) == 0) { @@ -119,7 +119,7 @@ void outsl(unsigned long addr, const void *src, unsigned long count) } } -void insb(unsigned long addr, void *dst, unsigned long count) +void insb(void __iomem *addr, void *dst, unsigned long count) { if(count) { u32 *pi; @@ -144,7 +144,7 @@ void insb(unsigned long addr, void *dst, unsigned long count) } } -void insw(unsigned long addr, void *dst, unsigned long count) +void insw(void __iomem *addr, void *dst, unsigned long count) { if(count) { u16 *ps = dst; @@ -169,7 +169,7 @@ void insw(unsigned long addr, void *dst, unsigned long count) } } -void insl(unsigned long addr, void *dst, unsigned long count) +void insl(void __iomem *addr, void *dst, unsigned long count) { if(count) { if((((unsigned long)dst) & 0x3) == 0) { diff --git a/arch/sparc64/lib/U1copy_from_user.S b/arch/sparc64/lib/U1copy_from_user.S new file mode 100644 index 000000000..93146a81e --- /dev/null +++ b/arch/sparc64/lib/U1copy_from_user.S @@ -0,0 +1,33 @@ +/* U1copy_from_user.S: UltraSparc-I/II/IIi/IIe optimized copy from userspace. + * + * Copyright (C) 1999, 2000, 2004 David S. Miller (davem@redhat.com) + */ + +#define EX_LD(x) \ +98: x; \ + .section .fixup; \ + .align 4; \ +99: retl; \ + mov 1, %o0; \ + .section __ex_table; \ + .align 4; \ + .word 98b, 99b; \ + .text; \ + .align 4; + +#define FUNC_NAME ___copy_from_user +#define LOAD(type,addr,dest) type##a [addr] %asi, dest +#define LOAD_BLK(addr,dest) ldda [addr] ASI_BLK_AIUS, dest +#define EX_RETVAL(x) 0 + + /* Writing to %asi is _expensive_ so we hardcode it. + * Reading %asi to check for KERNEL_DS is comparatively + * cheap. + */ +#define PREAMBLE \ + rd %asi, %g1; \ + cmp %g1, ASI_AIUS; \ + bne,pn %icc, memcpy_user_stub; \ + nop; \ + +#include "U1memcpy.S" diff --git a/arch/sparc64/lib/U1copy_to_user.S b/arch/sparc64/lib/U1copy_to_user.S new file mode 100644 index 000000000..1fccc521e --- /dev/null +++ b/arch/sparc64/lib/U1copy_to_user.S @@ -0,0 +1,33 @@ +/* U1copy_to_user.S: UltraSparc-I/II/IIi/IIe optimized copy to userspace. + * + * Copyright (C) 1999, 2000, 2004 David S. Miller (davem@redhat.com) + */ + +#define EX_ST(x) \ +98: x; \ + .section .fixup; \ + .align 4; \ +99: retl; \ + mov 1, %o0; \ + .section __ex_table; \ + .align 4; \ + .word 98b, 99b; \ + .text; \ + .align 4; + +#define FUNC_NAME ___copy_to_user +#define STORE(type,src,addr) type##a src, [addr] ASI_AIUS +#define STORE_BLK(src,addr) stda src, [addr] ASI_BLK_AIUS +#define EX_RETVAL(x) 0 + + /* Writing to %asi is _expensive_ so we hardcode it. + * Reading %asi to check for KERNEL_DS is comparatively + * cheap. + */ +#define PREAMBLE \ + rd %asi, %g1; \ + cmp %g1, ASI_AIUS; \ + bne,pn %icc, memcpy_user_stub; \ + nop; \ + +#include "U1memcpy.S" diff --git a/arch/sparc64/lib/U1memcpy.S b/arch/sparc64/lib/U1memcpy.S new file mode 100644 index 000000000..06a5bd262 --- /dev/null +++ b/arch/sparc64/lib/U1memcpy.S @@ -0,0 +1,555 @@ +/* U1memcpy.S: UltraSPARC-I/II/IIi/IIe optimized memcpy. + * + * Copyright (C) 1997, 2004 David S. Miller (davem@redhat.com) + * Copyright (C) 1996, 1997, 1998, 1999 Jakub Jelinek (jj@ultra.linux.cz) + */ + +#ifdef __KERNEL__ +#include +#include +#else +#define ASI_BLK_P 0xf0 +#define FPRS_FEF 0x04 +#ifdef MEMCPY_DEBUG +#define VISEntry rd %fprs, %o5; wr %g0, FPRS_FEF, %fprs; \ + clr %g1; clr %g2; clr %g3; subcc %g0, %g0, %g0; +#define VISExit and %o5, FPRS_FEF, %o5; wr %o5, 0x0, %fprs +#else +#define VISEntry rd %fprs, %o5; wr %g0, FPRS_FEF, %fprs +#define VISExit and %o5, FPRS_FEF, %o5; wr %o5, 0x0, %fprs +#endif +#endif + +#ifndef EX_LD +#define EX_LD(x) x +#endif + +#ifndef EX_ST +#define EX_ST(x) x +#endif + +#ifndef EX_RETVAL +#define EX_RETVAL(x) x +#endif + +#ifndef LOAD +#define LOAD(type,addr,dest) type [addr], dest +#endif + +#ifndef LOAD_BLK +#define LOAD_BLK(addr,dest) ldda [addr] ASI_BLK_P, dest +#endif + +#ifndef STORE +#define STORE(type,src,addr) type src, [addr] +#endif + +#ifndef STORE_BLK +#define STORE_BLK(src,addr) stda src, [addr] ASI_BLK_P +#endif + +#ifndef FUNC_NAME +#define FUNC_NAME memcpy +#endif + +#ifndef PREAMBLE +#define PREAMBLE +#endif + +#ifndef XCC +#define XCC xcc +#endif + +#define FREG_FROB(f1, f2, f3, f4, f5, f6, f7, f8, f9) \ + faligndata %f1, %f2, %f48; \ + faligndata %f2, %f3, %f50; \ + faligndata %f3, %f4, %f52; \ + faligndata %f4, %f5, %f54; \ + faligndata %f5, %f6, %f56; \ + faligndata %f6, %f7, %f58; \ + faligndata %f7, %f8, %f60; \ + faligndata %f8, %f9, %f62; + +#define MAIN_LOOP_CHUNK(src, dest, fdest, fsrc, len, jmptgt) \ + EX_LD(LOAD_BLK(%src, %fdest)); \ + EX_ST(STORE_BLK(%fsrc, %dest)); \ + add %src, 0x40, %src; \ + subcc %len, 0x40, %len; \ + be,pn %xcc, jmptgt; \ + add %dest, 0x40, %dest; \ + +#define LOOP_CHUNK1(src, dest, len, branch_dest) \ + MAIN_LOOP_CHUNK(src, dest, f0, f48, len, branch_dest) +#define LOOP_CHUNK2(src, dest, len, branch_dest) \ + MAIN_LOOP_CHUNK(src, dest, f16, f48, len, branch_dest) +#define LOOP_CHUNK3(src, dest, len, branch_dest) \ + MAIN_LOOP_CHUNK(src, dest, f32, f48, len, branch_dest) + +#define STORE_SYNC(dest, fsrc) \ + EX_ST(STORE_BLK(%fsrc, %dest)); \ + add %dest, 0x40, %dest; + +#define STORE_JUMP(dest, fsrc, target) \ + EX_ST(STORE_BLK(%fsrc, %dest)); \ + add %dest, 0x40, %dest; \ + ba,pt %xcc, target; + +#define FINISH_VISCHUNK(dest, f0, f1, left) \ + subcc %left, 8, %left;\ + bl,pn %xcc, 95f; \ + faligndata %f0, %f1, %f48; \ + EX_ST(STORE(std, %f48, %dest)); \ + add %dest, 8, %dest; + +#define UNEVEN_VISCHUNK_LAST(dest, f0, f1, left) \ + subcc %left, 8, %left; \ + bl,pn %xcc, 95f; \ + fsrc1 %f0, %f1; + +#define UNEVEN_VISCHUNK(dest, f0, f1, left) \ + UNEVEN_VISCHUNK_LAST(dest, f0, f1, left) \ + ba,a,pt %xcc, 93f; + + .register %g2,#scratch + .register %g3,#scratch + + .text + .align 64 + + .globl FUNC_NAME + .type FUNC_NAME,#function +FUNC_NAME: /* %o0=dst, %o1=src, %o2=len */ + PREAMBLE + mov %o0, %g5 + cmp %o2, 0 + be,pn %XCC, 85f + or %o0, %o1, %o3 + cmp %o2, 16 + blu,a,pn %XCC, 80f + or %o3, %o2, %o3 + + cmp %o2, (5 * 64) + blu,pt %XCC, 70f + andcc %o3, 0x7, %g0 + + /* Clobbers o5/g1/g2/g3/g7/icc/xcc. */ + VISEntry + + /* Is 'dst' already aligned on an 64-byte boundary? */ + andcc %o0, 0x3f, %g2 + be,pt %XCC, 2f + + /* Compute abs((dst & 0x3f) - 0x40) into %g2. This is the number + * of bytes to copy to make 'dst' 64-byte aligned. We pre- + * subtract this from 'len'. + */ + sub %o0, %o1, %o4 + sub %g2, 0x40, %g2 + sub %g0, %g2, %g2 + sub %o2, %g2, %o2 + andcc %g2, 0x7, %g1 + be,pt %icc, 2f + and %g2, 0x38, %g2 + +1: subcc %g1, 0x1, %g1 + EX_LD(LOAD(ldub, %o1 + 0x00, %o3)) + EX_ST(STORE(stb, %o3, %o1 + %o4)) + bgu,pt %XCC, 1b + add %o1, 0x1, %o1 + + add %o1, %o4, %o0 + +2: cmp %g2, 0x0 + and %o1, 0x7, %g1 + be,pt %icc, 3f + alignaddr %o1, %g0, %o1 + + EX_LD(LOAD(ldd, %o1, %f4)) +1: EX_LD(LOAD(ldd, %o1 + 0x8, %f6)) + add %o1, 0x8, %o1 + subcc %g2, 0x8, %g2 + faligndata %f4, %f6, %f0 + EX_ST(STORE(std, %f0, %o0)) + be,pn %icc, 3f + add %o0, 0x8, %o0 + + EX_LD(LOAD(ldd, %o1 + 0x8, %f4)) + add %o1, 0x8, %o1 + subcc %g2, 0x8, %g2 + faligndata %f6, %f4, %f0 + EX_ST(STORE(std, %f0, %o0)) + bne,pt %icc, 1b + add %o0, 0x8, %o0 + + /* Destination is 64-byte aligned. */ +3: + membar #LoadStore | #StoreStore | #StoreLoad + + subcc %o2, 0x40, %o4 + add %o1, %g1, %g1 + andncc %o4, (0x40 - 1), %o4 + srl %g1, 3, %g2 + sub %o2, %o4, %g3 + andn %o1, (0x40 - 1), %o1 + and %g2, 7, %g2 + andncc %g3, 0x7, %g3 + fmovd %f0, %f2 + sub %g3, 0x8, %g3 + sub %o2, %o4, %o2 + + add %g1, %o4, %g1 + subcc %o2, %g3, %o2 + + EX_LD(LOAD_BLK(%o1, %f0)) + add %o1, 0x40, %o1 + add %g1, %g3, %g1 + EX_LD(LOAD_BLK(%o1, %f16)) + add %o1, 0x40, %o1 + sub %o4, 0x80, %o4 + EX_LD(LOAD_BLK(%o1, %f32)) + add %o1, 0x40, %o1 + + /* There are 8 instances of the unrolled loop, + * one for each possible alignment of the + * source buffer. Each loop instance is 452 + * bytes. + */ + sll %g2, 3, %o3 + sub %o3, %g2, %o3 + sllx %o3, 4, %o3 + add %o3, %g2, %o3 + sllx %o3, 2, %g2 +1: rd %pc, %o3 + add %o3, %lo(1f - 1b), %o3 + jmpl %o3 + %g2, %g0 + nop + + .align 64 +1: FREG_FROB(f0, f2, f4, f6, f8, f10,f12,f14,f16) + LOOP_CHUNK1(o1, o0, o4, 1f) + FREG_FROB(f16,f18,f20,f22,f24,f26,f28,f30,f32) + LOOP_CHUNK2(o1, o0, o4, 2f) + FREG_FROB(f32,f34,f36,f38,f40,f42,f44,f46,f0) + LOOP_CHUNK3(o1, o0, o4, 3f) + ba,pt %xcc, 1b+4 + faligndata %f0, %f2, %f48 +1: FREG_FROB(f16,f18,f20,f22,f24,f26,f28,f30,f32) + STORE_SYNC(o0, f48) membar #Sync + FREG_FROB(f32,f34,f36,f38,f40,f42,f44,f46,f0) + STORE_JUMP(o0, f48, 40f) membar #Sync +2: FREG_FROB(f32,f34,f36,f38,f40,f42,f44,f46,f0) + STORE_SYNC(o0, f48) membar #Sync + FREG_FROB(f0, f2, f4, f6, f8, f10,f12,f14,f16) + STORE_JUMP(o0, f48, 48f) membar #Sync +3: FREG_FROB(f0, f2, f4, f6, f8, f10,f12,f14,f16) + STORE_SYNC(o0, f48) membar #Sync + FREG_FROB(f16,f18,f20,f22,f24,f26,f28,f30,f32) + STORE_JUMP(o0, f48, 56f) membar #Sync + +1: FREG_FROB(f2, f4, f6, f8, f10,f12,f14,f16,f18) + LOOP_CHUNK1(o1, o0, o4, 1f) + FREG_FROB(f18,f20,f22,f24,f26,f28,f30,f32,f34) + LOOP_CHUNK2(o1, o0, o4, 2f) + FREG_FROB(f34,f36,f38,f40,f42,f44,f46,f0, f2) + LOOP_CHUNK3(o1, o0, o4, 3f) + ba,pt %xcc, 1b+4 + faligndata %f2, %f4, %f48 +1: FREG_FROB(f18,f20,f22,f24,f26,f28,f30,f32,f34) + STORE_SYNC(o0, f48) membar #Sync + FREG_FROB(f34,f36,f38,f40,f42,f44,f46,f0, f2) + STORE_JUMP(o0, f48, 41f) membar #Sync +2: FREG_FROB(f34,f36,f38,f40,f42,f44,f46,f0, f2) + STORE_SYNC(o0, f48) membar #Sync + FREG_FROB(f2, f4, f6, f8, f10,f12,f14,f16,f18) + STORE_JUMP(o0, f48, 49f) membar #Sync +3: FREG_FROB(f2, f4, f6, f8, f10,f12,f14,f16,f18) + STORE_SYNC(o0, f48) membar #Sync + FREG_FROB(f18,f20,f22,f24,f26,f28,f30,f32,f34) + STORE_JUMP(o0, f48, 57f) membar #Sync + +1: FREG_FROB(f4, f6, f8, f10,f12,f14,f16,f18,f20) + LOOP_CHUNK1(o1, o0, o4, 1f) + FREG_FROB(f20,f22,f24,f26,f28,f30,f32,f34,f36) + LOOP_CHUNK2(o1, o0, o4, 2f) + FREG_FROB(f36,f38,f40,f42,f44,f46,f0, f2, f4) + LOOP_CHUNK3(o1, o0, o4, 3f) + ba,pt %xcc, 1b+4 + faligndata %f4, %f6, %f48 +1: FREG_FROB(f20,f22,f24,f26,f28,f30,f32,f34,f36) + STORE_SYNC(o0, f48) membar #Sync + FREG_FROB(f36,f38,f40,f42,f44,f46,f0, f2, f4) + STORE_JUMP(o0, f48, 42f) membar #Sync +2: FREG_FROB(f36,f38,f40,f42,f44,f46,f0, f2, f4) + STORE_SYNC(o0, f48) membar #Sync + FREG_FROB(f4, f6, f8, f10,f12,f14,f16,f18,f20) + STORE_JUMP(o0, f48, 50f) membar #Sync +3: FREG_FROB(f4, f6, f8, f10,f12,f14,f16,f18,f20) + STORE_SYNC(o0, f48) membar #Sync + FREG_FROB(f20,f22,f24,f26,f28,f30,f32,f34,f36) + STORE_JUMP(o0, f48, 58f) membar #Sync + +1: FREG_FROB(f6, f8, f10,f12,f14,f16,f18,f20,f22) + LOOP_CHUNK1(o1, o0, o4, 1f) + FREG_FROB(f22,f24,f26,f28,f30,f32,f34,f36,f38) + LOOP_CHUNK2(o1, o0, o4, 2f) + FREG_FROB(f38,f40,f42,f44,f46,f0, f2, f4, f6) + LOOP_CHUNK3(o1, o0, o4, 3f) + ba,pt %xcc, 1b+4 + faligndata %f6, %f8, %f48 +1: FREG_FROB(f22,f24,f26,f28,f30,f32,f34,f36,f38) + STORE_SYNC(o0, f48) membar #Sync + FREG_FROB(f38,f40,f42,f44,f46,f0, f2, f4, f6) + STORE_JUMP(o0, f48, 43f) membar #Sync +2: FREG_FROB(f38,f40,f42,f44,f46,f0, f2, f4, f6) + STORE_SYNC(o0, f48) membar #Sync + FREG_FROB(f6, f8, f10,f12,f14,f16,f18,f20,f22) + STORE_JUMP(o0, f48, 51f) membar #Sync +3: FREG_FROB(f6, f8, f10,f12,f14,f16,f18,f20,f22) + STORE_SYNC(o0, f48) membar #Sync + FREG_FROB(f22,f24,f26,f28,f30,f32,f34,f36,f38) + STORE_JUMP(o0, f48, 59f) membar #Sync + +1: FREG_FROB(f8, f10,f12,f14,f16,f18,f20,f22,f24) + LOOP_CHUNK1(o1, o0, o4, 1f) + FREG_FROB(f24,f26,f28,f30,f32,f34,f36,f38,f40) + LOOP_CHUNK2(o1, o0, o4, 2f) + FREG_FROB(f40,f42,f44,f46,f0, f2, f4, f6, f8) + LOOP_CHUNK3(o1, o0, o4, 3f) + ba,pt %xcc, 1b+4 + faligndata %f8, %f10, %f48 +1: FREG_FROB(f24,f26,f28,f30,f32,f34,f36,f38,f40) + STORE_SYNC(o0, f48) membar #Sync + FREG_FROB(f40,f42,f44,f46,f0, f2, f4, f6, f8) + STORE_JUMP(o0, f48, 44f) membar #Sync +2: FREG_FROB(f40,f42,f44,f46,f0, f2, f4, f6, f8) + STORE_SYNC(o0, f48) membar #Sync + FREG_FROB(f8, f10,f12,f14,f16,f18,f20,f22,f24) + STORE_JUMP(o0, f48, 52f) membar #Sync +3: FREG_FROB(f8, f10,f12,f14,f16,f18,f20,f22,f24) + STORE_SYNC(o0, f48) membar #Sync + FREG_FROB(f24,f26,f28,f30,f32,f34,f36,f38,f40) + STORE_JUMP(o0, f48, 60f) membar #Sync + +1: FREG_FROB(f10,f12,f14,f16,f18,f20,f22,f24,f26) + LOOP_CHUNK1(o1, o0, o4, 1f) + FREG_FROB(f26,f28,f30,f32,f34,f36,f38,f40,f42) + LOOP_CHUNK2(o1, o0, o4, 2f) + FREG_FROB(f42,f44,f46,f0, f2, f4, f6, f8, f10) + LOOP_CHUNK3(o1, o0, o4, 3f) + ba,pt %xcc, 1b+4 + faligndata %f10, %f12, %f48 +1: FREG_FROB(f26,f28,f30,f32,f34,f36,f38,f40,f42) + STORE_SYNC(o0, f48) membar #Sync + FREG_FROB(f42,f44,f46,f0, f2, f4, f6, f8, f10) + STORE_JUMP(o0, f48, 45f) membar #Sync +2: FREG_FROB(f42,f44,f46,f0, f2, f4, f6, f8, f10) + STORE_SYNC(o0, f48) membar #Sync + FREG_FROB(f10,f12,f14,f16,f18,f20,f22,f24,f26) + STORE_JUMP(o0, f48, 53f) membar #Sync +3: FREG_FROB(f10,f12,f14,f16,f18,f20,f22,f24,f26) + STORE_SYNC(o0, f48) membar #Sync + FREG_FROB(f26,f28,f30,f32,f34,f36,f38,f40,f42) + STORE_JUMP(o0, f48, 61f) membar #Sync + +1: FREG_FROB(f12,f14,f16,f18,f20,f22,f24,f26,f28) + LOOP_CHUNK1(o1, o0, o4, 1f) + FREG_FROB(f28,f30,f32,f34,f36,f38,f40,f42,f44) + LOOP_CHUNK2(o1, o0, o4, 2f) + FREG_FROB(f44,f46,f0, f2, f4, f6, f8, f10,f12) + LOOP_CHUNK3(o1, o0, o4, 3f) + ba,pt %xcc, 1b+4 + faligndata %f12, %f14, %f48 +1: FREG_FROB(f28,f30,f32,f34,f36,f38,f40,f42,f44) + STORE_SYNC(o0, f48) membar #Sync + FREG_FROB(f44,f46,f0, f2, f4, f6, f8, f10,f12) + STORE_JUMP(o0, f48, 46f) membar #Sync +2: FREG_FROB(f44,f46,f0, f2, f4, f6, f8, f10,f12) + STORE_SYNC(o0, f48) membar #Sync + FREG_FROB(f12,f14,f16,f18,f20,f22,f24,f26,f28) + STORE_JUMP(o0, f48, 54f) membar #Sync +3: FREG_FROB(f12,f14,f16,f18,f20,f22,f24,f26,f28) + STORE_SYNC(o0, f48) membar #Sync + FREG_FROB(f28,f30,f32,f34,f36,f38,f40,f42,f44) + STORE_JUMP(o0, f48, 62f) membar #Sync + +1: FREG_FROB(f14,f16,f18,f20,f22,f24,f26,f28,f30) + LOOP_CHUNK1(o1, o0, o4, 1f) + FREG_FROB(f30,f32,f34,f36,f38,f40,f42,f44,f46) + LOOP_CHUNK2(o1, o0, o4, 2f) + FREG_FROB(f46,f0, f2, f4, f6, f8, f10,f12,f14) + LOOP_CHUNK3(o1, o0, o4, 3f) + ba,pt %xcc, 1b+4 + faligndata %f14, %f16, %f48 +1: FREG_FROB(f30,f32,f34,f36,f38,f40,f42,f44,f46) + STORE_SYNC(o0, f48) membar #Sync + FREG_FROB(f46,f0, f2, f4, f6, f8, f10,f12,f14) + STORE_JUMP(o0, f48, 47f) membar #Sync +2: FREG_FROB(f46,f0, f2, f4, f6, f8, f10,f12,f14) + STORE_SYNC(o0, f48) membar #Sync + FREG_FROB(f14,f16,f18,f20,f22,f24,f26,f28,f30) + STORE_JUMP(o0, f48, 55f) membar #Sync +3: FREG_FROB(f14,f16,f18,f20,f22,f24,f26,f28,f30) + STORE_SYNC(o0, f48) membar #Sync + FREG_FROB(f30,f32,f34,f36,f38,f40,f42,f44,f46) + STORE_JUMP(o0, f48, 63f) membar #Sync + +40: FINISH_VISCHUNK(o0, f0, f2, g3) +41: FINISH_VISCHUNK(o0, f2, f4, g3) +42: FINISH_VISCHUNK(o0, f4, f6, g3) +43: FINISH_VISCHUNK(o0, f6, f8, g3) +44: FINISH_VISCHUNK(o0, f8, f10, g3) +45: FINISH_VISCHUNK(o0, f10, f12, g3) +46: FINISH_VISCHUNK(o0, f12, f14, g3) +47: UNEVEN_VISCHUNK(o0, f14, f0, g3) +48: FINISH_VISCHUNK(o0, f16, f18, g3) +49: FINISH_VISCHUNK(o0, f18, f20, g3) +50: FINISH_VISCHUNK(o0, f20, f22, g3) +51: FINISH_VISCHUNK(o0, f22, f24, g3) +52: FINISH_VISCHUNK(o0, f24, f26, g3) +53: FINISH_VISCHUNK(o0, f26, f28, g3) +54: FINISH_VISCHUNK(o0, f28, f30, g3) +55: UNEVEN_VISCHUNK(o0, f30, f0, g3) +56: FINISH_VISCHUNK(o0, f32, f34, g3) +57: FINISH_VISCHUNK(o0, f34, f36, g3) +58: FINISH_VISCHUNK(o0, f36, f38, g3) +59: FINISH_VISCHUNK(o0, f38, f40, g3) +60: FINISH_VISCHUNK(o0, f40, f42, g3) +61: FINISH_VISCHUNK(o0, f42, f44, g3) +62: FINISH_VISCHUNK(o0, f44, f46, g3) +63: UNEVEN_VISCHUNK_LAST(o0, f46, f0, g3) + +93: EX_LD(LOAD(ldd, %o1, %f2)) + add %o1, 8, %o1 + subcc %g3, 8, %g3 + faligndata %f0, %f2, %f8 + EX_ST(STORE(std, %f8, %o0)) + bl,pn %xcc, 95f + add %o0, 8, %o0 + EX_LD(LOAD(ldd, %o1, %f0)) + add %o1, 8, %o1 + subcc %g3, 8, %g3 + faligndata %f2, %f0, %f8 + EX_ST(STORE(std, %f8, %o0)) + bge,pt %xcc, 93b + add %o0, 8, %o0 + +95: brz,pt %o2, 2f + mov %g1, %o1 + +1: EX_LD(LOAD(ldub, %o1, %o3)) + add %o1, 1, %o1 + subcc %o2, 1, %o2 + EX_ST(STORE(stb, %o3, %o0)) + bne,pt %xcc, 1b + add %o0, 1, %o0 + +2: membar #StoreLoad | #StoreStore + VISExit + retl + mov EX_RETVAL(%g5), %o0 + + .align 64 +70: /* 16 < len <= (5 * 64) */ + bne,pn %XCC, 75f + sub %o0, %o1, %o3 + +72: andn %o2, 0xf, %o4 + and %o2, 0xf, %o2 +1: EX_LD(LOAD(ldx, %o1 + 0x00, %o5)) + EX_LD(LOAD(ldx, %o1 + 0x08, %g1)) + subcc %o4, 0x10, %o4 + EX_ST(STORE(stx, %o5, %o1 + %o3)) + add %o1, 0x8, %o1 + EX_ST(STORE(stx, %g1, %o1 + %o3)) + bgu,pt %XCC, 1b + add %o1, 0x8, %o1 +73: andcc %o2, 0x8, %g0 + be,pt %XCC, 1f + nop + EX_LD(LOAD(ldx, %o1, %o5)) + sub %o2, 0x8, %o2 + EX_ST(STORE(stx, %o5, %o1 + %o3)) + add %o1, 0x8, %o1 +1: andcc %o2, 0x4, %g0 + be,pt %XCC, 1f + nop + EX_LD(LOAD(lduw, %o1, %o5)) + sub %o2, 0x4, %o2 + EX_ST(STORE(stw, %o5, %o1 + %o3)) + add %o1, 0x4, %o1 +1: cmp %o2, 0 + be,pt %XCC, 85f + nop + ba,pt %xcc, 90f + nop + +75: andcc %o0, 0x7, %g1 + sub %g1, 0x8, %g1 + be,pn %icc, 2f + sub %g0, %g1, %g1 + sub %o2, %g1, %o2 + +1: EX_LD(LOAD(ldub, %o1, %o5)) + subcc %g1, 1, %g1 + EX_ST(STORE(stb, %o5, %o1 + %o3)) + bgu,pt %icc, 1b + add %o1, 1, %o1 + +2: add %o1, %o3, %o0 + andcc %o1, 0x7, %g1 + bne,pt %icc, 8f + sll %g1, 3, %g1 + + cmp %o2, 16 + bgeu,pt %icc, 72b + nop + ba,a,pt %xcc, 73b + +8: mov 64, %o3 + andn %o1, 0x7, %o1 + EX_LD(LOAD(ldx, %o1, %g2)) + sub %o3, %g1, %o3 + andn %o2, 0x7, %o4 + sllx %g2, %g1, %g2 +1: EX_LD(LOAD(ldx, %o1 + 0x8, %g3)) + subcc %o4, 0x8, %o4 + add %o1, 0x8, %o1 + srlx %g3, %o3, %o5 + or %o5, %g2, %o5 + EX_ST(STORE(stx, %o5, %o0)) + add %o0, 0x8, %o0 + bgu,pt %icc, 1b + sllx %g3, %g1, %g2 + + srl %g1, 3, %g1 + andcc %o2, 0x7, %o2 + be,pn %icc, 85f + add %o1, %g1, %o1 + ba,pt %xcc, 90f + sub %o0, %o1, %o3 + + .align 64 +80: /* 0 < len <= 16 */ + andcc %o3, 0x3, %g0 + bne,pn %XCC, 90f + sub %o0, %o1, %o3 + +1: EX_LD(LOAD(lduw, %o1, %g1)) + subcc %o2, 4, %o2 + EX_ST(STORE(stw, %g1, %o1 + %o3)) + bgu,pt %XCC, 1b + add %o1, 4, %o1 + +85: retl + mov EX_RETVAL(%g5), %o0 + + .align 32 +90: EX_LD(LOAD(ldub, %o1, %g1)) + subcc %o2, 1, %o2 + EX_ST(STORE(stb, %g1, %o1 + %o3)) + bgu,pt %XCC, 90b + add %o1, 1, %o1 + retl + mov EX_RETVAL(%g5), %o0 + + .size FUNC_NAME, .-FUNC_NAME diff --git a/arch/sparc64/lib/U3copy_from_user.S b/arch/sparc64/lib/U3copy_from_user.S index 46b446b14..df600b667 100644 --- a/arch/sparc64/lib/U3copy_from_user.S +++ b/arch/sparc64/lib/U3copy_from_user.S @@ -3,410 +3,20 @@ * Copyright (C) 1999, 2000, 2004 David S. Miller (davem@redhat.com) */ -#include -#include -#include -#include - -#define XCC xcc - -#define EXNV_RAW(x,y,a,b) \ -98: x,y; \ - .section .fixup; \ - .align 4; \ -99: ba U3cfu_fixup; \ - a, b, %o1; \ - .section __ex_table; \ - .align 4; \ - .word 98b, 99b; \ - .text; \ - .align 4; -#define EXNV(x,y,a,b) \ -98: x,y; \ - .section .fixup; \ - .align 4; \ -99: add %o1, %o3, %o0; \ - ba U3cfu_fixup; \ - a, b, %o1; \ - .section __ex_table; \ - .align 4; \ - .word 98b, 99b; \ - .text; \ - .align 4; -#define EXNV4(x,y,a,b) \ -98: x,y; \ - .section .fixup; \ - .align 4; \ -99: add %o1, %o3, %o0; \ - a, b, %o1; \ - ba U3cfu_fixup; \ - add %o1, 4, %o1; \ - .section __ex_table; \ - .align 4; \ - .word 98b, 99b; \ - .text; \ - .align 4; -#define EXNV8(x,y,a,b) \ -98: x,y; \ - .section .fixup; \ - .align 4; \ -99: add %o1, %o3, %o0; \ - a, b, %o1; \ - ba U3cfu_fixup; \ - add %o1, 8, %o1; \ - .section __ex_table; \ - .align 4; \ - .word 98b, 99b; \ - .text; \ - .align 4; -#define EX(x,y,a,b) \ -98: x,y; \ - .section .fixup; \ - .align 4; \ -99: VISExitHalf; \ - ba U3cfu_fixup; \ - a, b, %o1; \ - .section __ex_table; \ - .align 4; \ - .word 98b, 99b; \ - .text; \ - .align 4; -#define EX2(x,y) \ -98: x,y; \ - .section .fixup; \ - .align 4; \ -99: VISExitHalf; \ - and %o2, (0x40 - 1), %o1; \ - add %o1, %o4, %o1; \ - ba U3cfu_fixup; \ - add %o1, 0x1c0, %o1; \ - .section __ex_table; \ - .align 4; \ - .word 98b, 99b; \ - .text; \ - .align 4; -#define EX3(x,y) \ -98: x,y; \ - .section .fixup; \ - .align 4; \ -99: VISExitHalf; \ - and %o2, (0x40 - 1), %o1; \ - sll %g3, 6, %g3; \ - add %o1, 0x80, %o1; \ - ba U3cfu_fixup; \ - add %o1, %g3, %o1; \ - .section __ex_table; \ - .align 4; \ - .word 98b, 99b; \ - .text; \ - .align 4; -#define EX4(x,y) \ -98: x,y; \ - .section .fixup; \ - .align 4; \ -99: VISExitHalf; \ - and %o2, (0x40 - 1), %o1; \ - add %o1, 0x40, %o1; \ - ba U3cfu_fixup; \ - add %o1, %g3, %o1; \ - .section __ex_table; \ - .align 4; \ - .word 98b, 99b; \ - .text; \ - .align 4; - - .register %g2,#scratch - .register %g3,#scratch - - /* Special/non-trivial issues of this code: - * - * 1) %o5 is preserved from VISEntryHalf to VISExitHalf - * 2) Only low 32 FPU registers are used so that only the - * lower half of the FPU register set is dirtied by this - * code. This is especially important in the kernel. - * 3) This code never prefetches cachelines past the end - * of the source buffer. - */ - - .text - .align 32 - - /* The cheetah's flexible spine, oversized liver, enlarged heart, - * slender muscular body, and claws make it the swiftest hunter - * in Africa and the fastest animal on land. Can reach speeds - * of up to 2.4GB per second. - */ - - .globl U3copy_from_user -U3copy_from_user: /* %o0=dst, %o1=src, %o2=len */ - cmp %o2, 0 - be,pn %XCC, 85f - or %o0, %o1, %o3 - cmp %o2, 16 - bleu,a,pn %XCC, 80f - or %o3, %o2, %o3 - - cmp %o2, 256 - blu,pt %XCC, 70f - andcc %o3, 0x7, %g0 - - ba,pt %xcc, 1f - andcc %o0, 0x3f, %g2 - - /* Here len >= 256 and condition codes reflect execution - * of "andcc %o0, 0x7, %g2", done by caller. - */ - .align 64 -1: - /* Is 'dst' already aligned on an 64-byte boundary? */ - be,pt %XCC, 2f - - /* Compute abs((dst & 0x3f) - 0x40) into %g2. This is the number - * of bytes to copy to make 'dst' 64-byte aligned. We pre- - * subtract this from 'len'. - */ - sub %g2, 0x40, %g2 - sub %g0, %g2, %g2 - sub %o2, %g2, %o2 - - /* Copy %g2 bytes from src to dst, one byte at a time. */ -1: EXNV_RAW(lduba [%o1 + 0x00] %asi, %o3, add %o2, %g2) - add %o1, 0x1, %o1 - add %o0, 0x1, %o0 - subcc %g2, 0x1, %g2 - - bg,pt %XCC, 1b - stb %o3, [%o0 + -1] - -2: VISEntryHalf - and %o1, 0x7, %g1 - ba,pt %xcc, 1f - alignaddr %o1, %g0, %o1 - - .align 64 -1: - membar #StoreLoad | #StoreStore | #LoadStore - prefetcha [%o1 + 0x000] %asi, #one_read - prefetcha [%o1 + 0x040] %asi, #one_read - andn %o2, (0x40 - 1), %o4 - prefetcha [%o1 + 0x080] %asi, #one_read - prefetcha [%o1 + 0x0c0] %asi, #one_read - EX(ldda [%o1 + 0x000] %asi, %f0, add %o2, %g0) - prefetcha [%o1 + 0x100] %asi, #one_read - EX(ldda [%o1 + 0x008] %asi, %f2, add %o2, %g0) - prefetcha [%o1 + 0x140] %asi, #one_read - EX(ldda [%o1 + 0x010] %asi, %f4, add %o2, %g0) - prefetcha [%o1 + 0x180] %asi, #one_read - faligndata %f0, %f2, %f16 - EX(ldda [%o1 + 0x018] %asi, %f6, add %o2, %g0) - faligndata %f2, %f4, %f18 - EX(ldda [%o1 + 0x020] %asi, %f8, add %o2, %g0) - faligndata %f4, %f6, %f20 - EX(ldda [%o1 + 0x028] %asi, %f10, add %o2, %g0) - faligndata %f6, %f8, %f22 - - EX(ldda [%o1 + 0x030] %asi, %f12, add %o2, %g0) - faligndata %f8, %f10, %f24 - EX(ldda [%o1 + 0x038] %asi, %f14, add %o2, %g0) - faligndata %f10, %f12, %f26 - EX(ldda [%o1 + 0x040] %asi, %f0, add %o2, %g0) - - sub %o4, 0x80, %o4 - add %o1, 0x40, %o1 - ba,pt %xcc, 1f - srl %o4, 6, %o3 - - .align 64 -1: - EX3(ldda [%o1 + 0x008] %asi, %f2) - faligndata %f12, %f14, %f28 - EX3(ldda [%o1 + 0x010] %asi, %f4) - faligndata %f14, %f0, %f30 - stda %f16, [%o0] ASI_BLK_P - EX3(ldda [%o1 + 0x018] %asi, %f6) - faligndata %f0, %f2, %f16 - - EX3(ldda [%o1 + 0x020] %asi, %f8) - faligndata %f2, %f4, %f18 - EX3(ldda [%o1 + 0x028] %asi, %f10) - faligndata %f4, %f6, %f20 - EX3(ldda [%o1 + 0x030] %asi, %f12) - faligndata %f6, %f8, %f22 - EX3(ldda [%o1 + 0x038] %asi, %f14) - faligndata %f8, %f10, %f24 - - EX3(ldda [%o1 + 0x040] %asi, %f0) - prefetcha [%o1 + 0x180] %asi, #one_read - faligndata %f10, %f12, %f26 - subcc %o3, 0x01, %o3 - add %o1, 0x40, %o1 - bg,pt %XCC, 1b - add %o0, 0x40, %o0 - - /* Finally we copy the last full 64-byte block. */ - EX3(ldda [%o1 + 0x008] %asi, %f2) - faligndata %f12, %f14, %f28 - EX3(ldda [%o1 + 0x010] %asi, %f4) - faligndata %f14, %f0, %f30 - stda %f16, [%o0] ASI_BLK_P - EX3(ldda [%o1 + 0x018] %asi, %f6) - faligndata %f0, %f2, %f16 - EX3(ldda [%o1 + 0x020] %asi, %f8) - faligndata %f2, %f4, %f18 - EX3(ldda [%o1 + 0x028] %asi, %f10) - faligndata %f4, %f6, %f20 - EX3(ldda [%o1 + 0x030] %asi, %f12) - faligndata %f6, %f8, %f22 - EX3(ldda [%o1 + 0x038] %asi, %f14) - faligndata %f8, %f10, %f24 - cmp %g1, 0 - be,pt %XCC, 1f - add %o0, 0x40, %o0 - EX4(ldda [%o1 + 0x040] %asi, %f0) -1: faligndata %f10, %f12, %f26 - faligndata %f12, %f14, %f28 - faligndata %f14, %f0, %f30 - stda %f16, [%o0] ASI_BLK_P - add %o0, 0x40, %o0 - add %o1, 0x40, %o1 - - membar #Sync - - /* Now we copy the (len modulo 64) bytes at the end. - * Note how we borrow the %f0 loaded above. - * - * Also notice how this code is careful not to perform a - * load past the end of the src buffer. - */ - and %o2, 0x3f, %o2 - andcc %o2, 0x38, %g2 - be,pn %XCC, 10f - subcc %g2, 0x8, %g2 - be,pn %XCC, 10f - cmp %g1, 0 - - be,a,pt %XCC, 1f - EX(ldda [%o1 + 0x00] %asi, %f0, add %o2, %g0) - -1: EX(ldda [%o1 + 0x08] %asi, %f2, add %o2, %g0) - add %o1, 0x8, %o1 - sub %o2, 0x8, %o2 - subcc %g2, 0x8, %g2 - faligndata %f0, %f2, %f8 - std %f8, [%o0 + 0x00] - be,pn %XCC, 10f - add %o0, 0x8, %o0 - EX(ldda [%o1 + 0x08] %asi, %f0, add %o2, %g0) - add %o1, 0x8, %o1 - sub %o2, 0x8, %o2 - subcc %g2, 0x8, %g2 - faligndata %f2, %f0, %f8 - std %f8, [%o0 + 0x00] - bne,pn %XCC, 1b - add %o0, 0x8, %o0 - - /* If anything is left, we copy it one byte at a time. - * Note that %g1 is (src & 0x3) saved above before the - * alignaddr was performed. - */ -10: - cmp %o2, 0 - add %o1, %g1, %o1 - VISExitHalf - be,pn %XCC, 85f - sub %o0, %o1, %o3 - - andcc %g1, 0x7, %g0 - bne,pn %icc, 90f - andcc %o2, 0x8, %g0 - be,pt %icc, 1f - nop - EXNV(ldxa [%o1] %asi, %o5, add %o2, %g0) - stx %o5, [%o1 + %o3] - add %o1, 0x8, %o1 - -1: andcc %o2, 0x4, %g0 - be,pt %icc, 1f - nop - EXNV(lduwa [%o1] %asi, %o5, and %o2, 0x7) - stw %o5, [%o1 + %o3] - add %o1, 0x4, %o1 - -1: andcc %o2, 0x2, %g0 - be,pt %icc, 1f - nop - EXNV(lduha [%o1] %asi, %o5, and %o2, 0x3) - sth %o5, [%o1 + %o3] - add %o1, 0x2, %o1 - -1: andcc %o2, 0x1, %g0 - be,pt %icc, 85f - nop - EXNV(lduba [%o1] %asi, %o5, and %o2, 0x1) - ba,pt %xcc, 85f - stb %o5, [%o1 + %o3] - -70: /* 16 < len <= 64 */ - bne,pn %XCC, 90f - sub %o0, %o1, %o3 - - andn %o2, 0x7, %o4 - and %o2, 0x7, %o2 -1: subcc %o4, 0x8, %o4 - EXNV8(ldxa [%o1] %asi, %o5, add %o2, %o4) - stx %o5, [%o1 + %o3] - bgu,pt %XCC, 1b - add %o1, 0x8, %o1 - andcc %o2, 0x4, %g0 - be,pt %XCC, 1f - nop - sub %o2, 0x4, %o2 - EXNV4(lduwa [%o1] %asi, %o5, add %o2, %g0) - stw %o5, [%o1 + %o3] - add %o1, 0x4, %o1 -1: cmp %o2, 0 - be,pt %XCC, 85f - nop - ba,pt %xcc, 90f - nop - -80: /* 0 < len <= 16 */ - andcc %o3, 0x3, %g0 - bne,pn %XCC, 90f - sub %o0, %o1, %o3 - -1: - subcc %o2, 4, %o2 - EXNV(lduwa [%o1] %asi, %g1, add %o2, %g0) - stw %g1, [%o1 + %o3] - bgu,pt %XCC, 1b - add %o1, 4, %o1 - -85: retl - clr %o0 - - .align 32 -90: - subcc %o2, 1, %o2 - EXNV(lduba [%o1] %asi, %g1, add %o2, %g0) - stb %g1, [%o1 + %o3] - bgu,pt %XCC, 90b - add %o1, 1, %o1 - retl - clr %o0 - -U3cfu_fixup: - /* Since this is copy_from_user(), zero out the rest of the - * kernel buffer. - */ - cmp %o1, 0 - ble,pn %icc, 2f - mov %o1, %g2 - -1: subcc %g2, 1, %g2 - stb %g0, [%o0] - bne,pt %icc, 1b - add %o0, 1, %o0 - -2: retl - mov %o1, %o0 +#define EX_LD(x) \ +98: x; \ + .section .fixup; \ + .align 4; \ +99: retl; \ + mov 1, %o0; \ + .section __ex_table; \ + .align 4; \ + .word 98b, 99b; \ + .text; \ + .align 4; + +#define FUNC_NAME U3copy_from_user +#define LOAD(type,addr,dest) type##a [addr] %asi, dest +#define EX_RETVAL(x) 0 + +#include "U3memcpy.S" diff --git a/arch/sparc64/lib/U3copy_to_user.S b/arch/sparc64/lib/U3copy_to_user.S index 715227156..f337f22ed 100644 --- a/arch/sparc64/lib/U3copy_to_user.S +++ b/arch/sparc64/lib/U3copy_to_user.S @@ -1,415 +1,33 @@ -/* U3copy_to_user.S: UltraSparc-III optimized memcpy. +/* U3copy_to_user.S: UltraSparc-III optimized copy to userspace. * * Copyright (C) 1999, 2000, 2004 David S. Miller (davem@redhat.com) */ -#include -#include -#include -#include - -#define XCC xcc - -#define EXNV(x,y,a,b) \ -98: x,y; \ - .section .fixup; \ - .align 4; \ -99: retl; \ - a, b, %o0; \ - .section __ex_table; \ - .align 4; \ - .word 98b, 99b; \ - .text; \ - .align 4; -#define EXNV2(x,y,a,b) \ -98: x,y; \ - .section .fixup; \ - .align 4; \ -99: a, b, %o0; \ - retl; \ - add %o0, 1, %o0; \ - .section __ex_table; \ - .align 4; \ - .word 98b, 99b; \ - .text; \ - .align 4; -#define EXNV3(x,y,a,b) \ -98: x,y; \ - .section .fixup; \ - .align 4; \ -99: a, b, %o0; \ - retl; \ - add %o0, 4, %o0; \ - .section __ex_table; \ - .align 4; \ - .word 98b, 99b; \ - .text; \ - .align 4; -#define EXNV4(x,y,a,b) \ -98: x,y; \ - .section .fixup; \ - .align 4; \ -99: a, b, %o0; \ - retl; \ - add %o0, 8, %o0; \ - .section __ex_table; \ - .align 4; \ - .word 98b, 99b; \ - .text; \ - .align 4; -#define EX(x,y,a,b) \ -98: x,y; \ - .section .fixup; \ - .align 4; \ -99: VISExitHalf; \ - retl; \ - a, b, %o0; \ - .section __ex_table; \ - .align 4; \ - .word 98b, 99b; \ - .text; \ +#define EX_ST(x) \ +98: x; \ + .section .fixup; \ + .align 4; \ +99: retl; \ + mov 1, %o0; \ + .section __ex_table; \ + .align 4; \ + .word 98b, 99b; \ + .text; \ .align 4; -#define EXBLK1(x,y) \ -98: x,y; \ - .section .fixup; \ - .align 4; \ -99: VISExitHalf; \ - add %o4, 0x1c0, %o1; \ - and %o2, (0x40 - 1), %o2; \ - retl; \ - add %o1, %o2, %o0; \ - .section __ex_table; \ - .align 4; \ - .word 98b, 99b; \ - .text; \ - .align 4; -#define EXBLK2(x,y) \ -98: x,y; \ - .section .fixup; \ - .align 4; \ -99: VISExitHalf; \ - sll %o3, 6, %o3; \ - and %o2, (0x40 - 1), %o2; \ - add %o3, 0x80, %o1; \ - retl; \ - add %o1, %o2, %o0; \ - .section __ex_table; \ - .align 4; \ - .word 98b, 99b; \ - .text; \ - .align 4; -#define EXBLK3(x,y) \ -98: x,y; \ - .section .fixup; \ - .align 4; \ -99: VISExitHalf; \ - and %o2, (0x40 - 1), %o2; \ - retl; \ - add %o2, 0x80, %o0; \ - .section __ex_table; \ - .align 4; \ - .word 98b, 99b; \ - .text; \ - .align 4; -#define EXBLK4(x,y) \ -98: x,y; \ - .section .fixup; \ - .align 4; \ -99: VISExitHalf; \ - and %o2, (0x40 - 1), %o2; \ - retl; \ - add %o2, 0x40, %o0; \ - .section __ex_table; \ - .align 4; \ - .word 98b, 99b; \ - .text; \ - .align 4; - - .register %g2,#scratch - .register %g3,#scratch - - /* Special/non-trivial issues of this code: - * - * 1) %o5 is preserved from VISEntryHalf to VISExitHalf - * 2) Only low 32 FPU registers are used so that only the - * lower half of the FPU register set is dirtied by this - * code. This is especially important in the kernel. - * 3) This code never prefetches cachelines past the end - * of the source buffer. - */ - - .text - .align 32 - /* The cheetah's flexible spine, oversized liver, enlarged heart, - * slender muscular body, and claws make it the swiftest hunter - * in Africa and the fastest animal on land. Can reach speeds - * of up to 2.4GB per second. - */ +#define FUNC_NAME U3copy_to_user +#define STORE(type,src,addr) type##a src, [addr] ASI_AIUS +#define STORE_BLK(src,addr) stda src, [addr] ASI_BLK_AIUS +#define EX_RETVAL(x) 0 - .globl U3copy_to_user -U3copy_to_user: /* %o0=dst, %o1=src, %o2=len */ /* Writing to %asi is _expensive_ so we hardcode it. * Reading %asi to check for KERNEL_DS is comparatively * cheap. */ - rd %asi, %g1 - cmp %g1, ASI_AIUS - bne,pn %icc, U3memcpy_user_stub - nop - - cmp %o2, 0 - be,pn %XCC, 85f - or %o0, %o1, %o3 - cmp %o2, 16 - bleu,a,pn %XCC, 80f - or %o3, %o2, %o3 - - cmp %o2, 256 - blu,pt %XCC, 70f - andcc %o3, 0x7, %g0 - - ba,pt %xcc, 1f - andcc %o0, 0x3f, %g2 - - /* Here len >= 256 and condition codes reflect execution - * of "andcc %o0, 0x7, %g2", done by caller. - */ - .align 64 -1: - /* Is 'dst' already aligned on an 64-byte boundary? */ - be,pt %XCC, 2f - - /* Compute abs((dst & 0x3f) - 0x40) into %g2. This is the number - * of bytes to copy to make 'dst' 64-byte aligned. We pre- - * subtract this from 'len'. - */ - sub %g2, 0x40, %g2 - sub %g0, %g2, %g2 - sub %o2, %g2, %o2 - - /* Copy %g2 bytes from src to dst, one byte at a time. */ -1: ldub [%o1 + 0x00], %o3 - add %o1, 0x1, %o1 - add %o0, 0x1, %o0 - subcc %g2, 0x1, %g2 - - bg,pt %XCC, 1b - EXNV2(stba %o3, [%o0 + -1] %asi, add %o2, %g2) - -2: VISEntryHalf - and %o1, 0x7, %g1 - ba,pt %xcc, 1f - alignaddr %o1, %g0, %o1 - - .align 64 -1: - membar #StoreLoad | #StoreStore | #LoadStore - prefetch [%o1 + 0x000], #one_read - prefetch [%o1 + 0x040], #one_read - andn %o2, (0x40 - 1), %o4 - prefetch [%o1 + 0x080], #one_read - prefetch [%o1 + 0x0c0], #one_read - ldd [%o1 + 0x000], %f0 - prefetch [%o1 + 0x100], #one_read - ldd [%o1 + 0x008], %f2 - prefetch [%o1 + 0x140], #one_read - ldd [%o1 + 0x010], %f4 - prefetch [%o1 + 0x180], #one_read - faligndata %f0, %f2, %f16 - ldd [%o1 + 0x018], %f6 - faligndata %f2, %f4, %f18 - ldd [%o1 + 0x020], %f8 - faligndata %f4, %f6, %f20 - ldd [%o1 + 0x028], %f10 - faligndata %f6, %f8, %f22 - - ldd [%o1 + 0x030], %f12 - faligndata %f8, %f10, %f24 - ldd [%o1 + 0x038], %f14 - faligndata %f10, %f12, %f26 - ldd [%o1 + 0x040], %f0 - - sub %o4, 0x80, %o4 - add %o1, 0x40, %o1 - ba,pt %xcc, 1f - srl %o4, 6, %o3 - - .align 64 -1: - ldd [%o1 + 0x008], %f2 - faligndata %f12, %f14, %f28 - ldd [%o1 + 0x010], %f4 - faligndata %f14, %f0, %f30 - EXBLK2(stda %f16, [%o0] ASI_BLK_AIUS) - ldd [%o1 + 0x018], %f6 - faligndata %f0, %f2, %f16 - - ldd [%o1 + 0x020], %f8 - faligndata %f2, %f4, %f18 - ldd [%o1 + 0x028], %f10 - faligndata %f4, %f6, %f20 - ldd [%o1 + 0x030], %f12 - faligndata %f6, %f8, %f22 - ldd [%o1 + 0x038], %f14 - faligndata %f8, %f10, %f24 - - ldd [%o1 + 0x040], %f0 - prefetch [%o1 + 0x180], #one_read - faligndata %f10, %f12, %f26 - subcc %o3, 0x01, %o3 - add %o1, 0x40, %o1 - bg,pt %XCC, 1b - add %o0, 0x40, %o0 - - /* Finally we copy the last full 64-byte block. */ - ldd [%o1 + 0x008], %f2 - faligndata %f12, %f14, %f28 - ldd [%o1 + 0x010], %f4 - faligndata %f14, %f0, %f30 - EXBLK3(stda %f16, [%o0] ASI_BLK_AIUS) - ldd [%o1 + 0x018], %f6 - faligndata %f0, %f2, %f16 - ldd [%o1 + 0x020], %f8 - faligndata %f2, %f4, %f18 - ldd [%o1 + 0x028], %f10 - faligndata %f4, %f6, %f20 - ldd [%o1 + 0x030], %f12 - faligndata %f6, %f8, %f22 - ldd [%o1 + 0x038], %f14 - faligndata %f8, %f10, %f24 - cmp %g1, 0 - be,pt %XCC, 1f - add %o0, 0x40, %o0 - ldd [%o1 + 0x040], %f0 -1: faligndata %f10, %f12, %f26 - faligndata %f12, %f14, %f28 - faligndata %f14, %f0, %f30 - EXBLK4(stda %f16, [%o0] ASI_BLK_AIUS) - add %o0, 0x40, %o0 - add %o1, 0x40, %o1 - - membar #Sync - - /* Now we copy the (len modulo 64) bytes at the end. - * Note how we borrow the %f0 loaded above. - * - * Also notice how this code is careful not to perform a - * load past the end of the src buffer. - */ - and %o2, 0x3f, %o2 - andcc %o2, 0x38, %g2 - be,pn %XCC, 2f - subcc %g2, 0x8, %g2 - be,pn %XCC, 2f - cmp %g1, 0 - - be,a,pt %XCC, 1f - ldd [%o1 + 0x00], %f0 - -1: ldd [%o1 + 0x08], %f2 - add %o1, 0x8, %o1 - sub %o2, 0x8, %o2 - subcc %g2, 0x8, %g2 - faligndata %f0, %f2, %f8 - EX(stda %f8, [%o0 + 0x00] %asi, add %o2, 0x8) - be,pn %XCC, 2f - add %o0, 0x8, %o0 - ldd [%o1 + 0x08], %f0 - add %o1, 0x8, %o1 - sub %o2, 0x8, %o2 - subcc %g2, 0x8, %g2 - faligndata %f2, %f0, %f8 - EX(stda %f8, [%o0 + 0x00] %asi, add %o2, 0x8) - bne,pn %XCC, 1b - add %o0, 0x8, %o0 - - /* If anything is left, we copy it one byte at a time. - * Note that %g1 is (src & 0x3) saved above before the - * alignaddr was performed. - */ -2: - cmp %o2, 0 - add %o1, %g1, %o1 - VISExitHalf - be,pn %XCC, 85f - sub %o0, %o1, %o3 - - andcc %g1, 0x7, %g0 - bne,pn %icc, 90f - andcc %o2, 0x8, %g0 - be,pt %icc, 1f - nop - ldx [%o1], %o5 - EXNV(stxa %o5, [%o1 + %o3] ASI_AIUS, add %o2, %g0) - add %o1, 0x8, %o1 - -1: andcc %o2, 0x4, %g0 - be,pt %icc, 1f - nop - lduw [%o1], %o5 - EXNV(stwa %o5, [%o1 + %o3] ASI_AIUS, and %o2, 0x7) - add %o1, 0x4, %o1 - -1: andcc %o2, 0x2, %g0 - be,pt %icc, 1f - nop - lduh [%o1], %o5 - EXNV(stha %o5, [%o1 + %o3] ASI_AIUS, and %o2, 0x3) - add %o1, 0x2, %o1 - -1: andcc %o2, 0x1, %g0 - be,pt %icc, 85f - nop - ldub [%o1], %o5 - ba,pt %xcc, 85f - EXNV(stba %o5, [%o1 + %o3] ASI_AIUS, and %o2, 0x1) - -70: /* 16 < len <= 64 */ - bne,pn %XCC, 90f - sub %o0, %o1, %o3 - - andn %o2, 0x7, %o4 - and %o2, 0x7, %o2 -1: subcc %o4, 0x8, %o4 - ldx [%o1], %o5 - EXNV4(stxa %o5, [%o1 + %o3] ASI_AIUS, add %o2, %o4) - bgu,pt %XCC, 1b - add %o1, 0x8, %o1 - andcc %o2, 0x4, %g0 - be,pt %XCC, 1f - nop - sub %o2, 0x4, %o2 - lduw [%o1], %o5 - EXNV3(stwa %o5, [%o1 + %o3] ASI_AIUS, add %o2, %g0) - add %o1, 0x4, %o1 -1: cmp %o2, 0 - be,pt %XCC, 85f - nop - ba,pt %xcc, 90f - nop - -80: /* 0 < len <= 16 */ - andcc %o3, 0x3, %g0 - bne,pn %XCC, 90f - sub %o0, %o1, %o3 - -1: - subcc %o2, 4, %o2 - lduw [%o1], %g1 - EXNV3(stwa %g1, [%o1 + %o3] ASI_AIUS, add %o2, %g0) - bgu,pt %XCC, 1b - add %o1, 4, %o1 - -85: retl - clr %o0 +#define PREAMBLE \ + rd %asi, %g1; \ + cmp %g1, ASI_AIUS; \ + bne,pn %icc, memcpy_user_stub; \ + nop; \ - .align 32 -90: - subcc %o2, 1, %o2 - ldub [%o1], %g1 - EXNV2(stba %g1, [%o1 + %o3] ASI_AIUS, add %o2, %g0) - bgu,pt %XCC, 90b - add %o1, 1, %o1 - retl - clr %o0 +#include "U3memcpy.S" diff --git a/arch/sparc64/lib/U3memcpy.S b/arch/sparc64/lib/U3memcpy.S index e9982deef..5cde95446 100644 --- a/arch/sparc64/lib/U3memcpy.S +++ b/arch/sparc64/lib/U3memcpy.S @@ -6,14 +6,50 @@ #ifdef __KERNEL__ #include #include -#include -#include #else #define ASI_BLK_P 0xf0 #define FPRS_FEF 0x04 +#ifdef MEMCPY_DEBUG +#define VISEntryHalf rd %fprs, %o5; wr %g0, FPRS_FEF, %fprs; \ + clr %g1; clr %g2; clr %g3; subcc %g0, %g0, %g0; +#define VISExitHalf and %o5, FPRS_FEF, %o5; wr %o5, 0x0, %fprs +#else #define VISEntryHalf rd %fprs, %o5; wr %g0, FPRS_FEF, %fprs #define VISExitHalf and %o5, FPRS_FEF, %o5; wr %o5, 0x0, %fprs #endif +#endif + +#ifndef EX_LD +#define EX_LD(x) x +#endif + +#ifndef EX_ST +#define EX_ST(x) x +#endif + +#ifndef EX_RETVAL +#define EX_RETVAL(x) x +#endif + +#ifndef LOAD +#define LOAD(type,addr,dest) type [addr], dest +#endif + +#ifndef STORE +#define STORE(type,src,addr) type src, [addr] +#endif + +#ifndef STORE_BLK +#define STORE_BLK(src,addr) stda src, [addr] ASI_BLK_P +#endif + +#ifndef FUNC_NAME +#define FUNC_NAME U3memcpy +#endif + +#ifndef PREAMBLE +#define PREAMBLE +#endif #ifndef XCC #define XCC xcc @@ -33,7 +69,7 @@ */ .text - .align 32 + .align 64 /* The cheetah's flexible spine, oversized liver, enlarged heart, * slender muscular body, and claws make it the swiftest hunter @@ -41,137 +77,158 @@ * of up to 2.4GB per second. */ - .globl U3memcpy -U3memcpy: /* %o0=dst, %o1=src, %o2=len */ + .globl FUNC_NAME + .type FUNC_NAME,#function +FUNC_NAME: /* %o0=dst, %o1=src, %o2=len */ + PREAMBLE mov %o0, %g5 cmp %o2, 0 be,pn %XCC, 85f or %o0, %o1, %o3 cmp %o2, 16 - bleu,a,pn %XCC, 70f + blu,a,pn %XCC, 80f or %o3, %o2, %o3 - cmp %o2, 256 - blu,pt %XCC, 80f + cmp %o2, (3 * 64) + blu,pt %XCC, 70f andcc %o3, 0x7, %g0 - ba,pt %xcc, 1f - andcc %o0, 0x3f, %g2 - - /* Here len >= 256 and condition codes reflect execution - * of "andcc %o0, 0x7, %g2", done by caller. + /* Clobbers o5/g1/g2/g3/g7/icc/xcc. We must preserve + * o5 from here until we hit VISExitHalf. */ - .align 64 -1: + VISEntryHalf + /* Is 'dst' already aligned on an 64-byte boundary? */ + andcc %o0, 0x3f, %g2 be,pt %XCC, 2f /* Compute abs((dst & 0x3f) - 0x40) into %g2. This is the number * of bytes to copy to make 'dst' 64-byte aligned. We pre- * subtract this from 'len'. */ - sub %g2, 0x40, %g2 + sub %o0, %o1, %o4 + sub %g2, 0x40, %g2 sub %g0, %g2, %g2 sub %o2, %g2, %o2 + andcc %g2, 0x7, %g1 + be,pt %icc, 2f + and %g2, 0x38, %g2 - /* Copy %g2 bytes from src to dst, one byte at a time. */ -1: ldub [%o1 + 0x00], %o3 - add %o1, 0x1, %o1 - add %o0, 0x1, %o0 - subcc %g2, 0x1, %g2 +1: subcc %g1, 0x1, %g1 + EX_LD(LOAD(ldub, %o1 + 0x00, %o3)) + EX_ST(STORE(stb, %o3, %o1 + %o4)) + bgu,pt %XCC, 1b + add %o1, 0x1, %o1 - bg,pt %XCC, 1b - stb %o3, [%o0 + -1] + add %o1, %o4, %o0 -2: VISEntryHalf +2: cmp %g2, 0x0 and %o1, 0x7, %g1 - ba,pt %xcc, 1f + be,pt %icc, 3f alignaddr %o1, %g0, %o1 - .align 64 -1: - membar #StoreLoad | #StoreStore | #LoadStore - prefetch [%o1 + 0x000], #one_read - prefetch [%o1 + 0x040], #one_read + EX_LD(LOAD(ldd, %o1, %f4)) +1: EX_LD(LOAD(ldd, %o1 + 0x8, %f6)) + add %o1, 0x8, %o1 + subcc %g2, 0x8, %g2 + faligndata %f4, %f6, %f0 + EX_ST(STORE(std, %f0, %o0)) + be,pn %icc, 3f + add %o0, 0x8, %o0 + + EX_LD(LOAD(ldd, %o1 + 0x8, %f4)) + add %o1, 0x8, %o1 + subcc %g2, 0x8, %g2 + faligndata %f6, %f4, %f2 + EX_ST(STORE(std, %f2, %o0)) + bne,pt %icc, 1b + add %o0, 0x8, %o0 + +3: LOAD(prefetch, %o1 + 0x000, #one_read) + LOAD(prefetch, %o1 + 0x040, #one_read) andn %o2, (0x40 - 1), %o4 - prefetch [%o1 + 0x080], #one_read - prefetch [%o1 + 0x0c0], #one_read - ldd [%o1 + 0x000], %f0 - prefetch [%o1 + 0x100], #one_read - ldd [%o1 + 0x008], %f2 - prefetch [%o1 + 0x140], #one_read - ldd [%o1 + 0x010], %f4 - prefetch [%o1 + 0x180], #one_read + LOAD(prefetch, %o1 + 0x080, #one_read) + LOAD(prefetch, %o1 + 0x0c0, #one_read) + LOAD(prefetch, %o1 + 0x100, #one_read) + EX_LD(LOAD(ldd, %o1 + 0x000, %f0)) + LOAD(prefetch, %o1 + 0x140, #one_read) + EX_LD(LOAD(ldd, %o1 + 0x008, %f2)) + LOAD(prefetch, %o1 + 0x180, #one_read) + EX_LD(LOAD(ldd, %o1 + 0x010, %f4)) + LOAD(prefetch, %o1 + 0x1c0, #one_read) faligndata %f0, %f2, %f16 - ldd [%o1 + 0x018], %f6 + EX_LD(LOAD(ldd, %o1 + 0x018, %f6)) faligndata %f2, %f4, %f18 - ldd [%o1 + 0x020], %f8 + EX_LD(LOAD(ldd, %o1 + 0x020, %f8)) faligndata %f4, %f6, %f20 - ldd [%o1 + 0x028], %f10 + EX_LD(LOAD(ldd, %o1 + 0x028, %f10)) faligndata %f6, %f8, %f22 - ldd [%o1 + 0x030], %f12 + EX_LD(LOAD(ldd, %o1 + 0x030, %f12)) faligndata %f8, %f10, %f24 - ldd [%o1 + 0x038], %f14 + EX_LD(LOAD(ldd, %o1 + 0x038, %f14)) faligndata %f10, %f12, %f26 - ldd [%o1 + 0x040], %f0 + EX_LD(LOAD(ldd, %o1 + 0x040, %f0)) - sub %o4, 0x80, %o4 + subcc %o4, 0x80, %o4 add %o1, 0x40, %o1 - ba,pt %xcc, 1f + bgu,pt %XCC, 1f srl %o4, 6, %o3 + ba,pt %xcc, 2f + nop .align 64 1: - ldd [%o1 + 0x008], %f2 + EX_LD(LOAD(ldd, %o1 + 0x008, %f2)) faligndata %f12, %f14, %f28 - ldd [%o1 + 0x010], %f4 + EX_LD(LOAD(ldd, %o1 + 0x010, %f4)) faligndata %f14, %f0, %f30 - stda %f16, [%o0] ASI_BLK_P - ldd [%o1 + 0x018], %f6 + EX_ST(STORE_BLK(%f16, %o0)) + EX_LD(LOAD(ldd, %o1 + 0x018, %f6)) faligndata %f0, %f2, %f16 + add %o0, 0x40, %o0 - ldd [%o1 + 0x020], %f8 + EX_LD(LOAD(ldd, %o1 + 0x020, %f8)) faligndata %f2, %f4, %f18 - ldd [%o1 + 0x028], %f10 + EX_LD(LOAD(ldd, %o1 + 0x028, %f10)) faligndata %f4, %f6, %f20 - ldd [%o1 + 0x030], %f12 + EX_LD(LOAD(ldd, %o1 + 0x030, %f12)) + subcc %o3, 0x01, %o3 faligndata %f6, %f8, %f22 - ldd [%o1 + 0x038], %f14 - faligndata %f8, %f10, %f24 + EX_LD(LOAD(ldd, %o1 + 0x038, %f14)) - ldd [%o1 + 0x040], %f0 - prefetch [%o1 + 0x180], #one_read + faligndata %f8, %f10, %f24 + EX_LD(LOAD(ldd, %o1 + 0x040, %f0)) + LOAD(prefetch, %o1 + 0x1c0, #one_read) faligndata %f10, %f12, %f26 - subcc %o3, 0x01, %o3 - add %o1, 0x40, %o1 bg,pt %XCC, 1b - add %o0, 0x40, %o0 + add %o1, 0x40, %o1 /* Finally we copy the last full 64-byte block. */ - ldd [%o1 + 0x008], %f2 +2: + EX_LD(LOAD(ldd, %o1 + 0x008, %f2)) faligndata %f12, %f14, %f28 - ldd [%o1 + 0x010], %f4 + EX_LD(LOAD(ldd, %o1 + 0x010, %f4)) faligndata %f14, %f0, %f30 - stda %f16, [%o0] ASI_BLK_P - ldd [%o1 + 0x018], %f6 + EX_ST(STORE_BLK(%f16, %o0)) + EX_LD(LOAD(ldd, %o1 + 0x018, %f6)) faligndata %f0, %f2, %f16 - ldd [%o1 + 0x020], %f8 + EX_LD(LOAD(ldd, %o1 + 0x020, %f8)) faligndata %f2, %f4, %f18 - ldd [%o1 + 0x028], %f10 + EX_LD(LOAD(ldd, %o1 + 0x028, %f10)) faligndata %f4, %f6, %f20 - ldd [%o1 + 0x030], %f12 + EX_LD(LOAD(ldd, %o1 + 0x030, %f12)) faligndata %f6, %f8, %f22 - ldd [%o1 + 0x038], %f14 + EX_LD(LOAD(ldd, %o1 + 0x038, %f14)) faligndata %f8, %f10, %f24 cmp %g1, 0 be,pt %XCC, 1f add %o0, 0x40, %o0 - ldd [%o1 + 0x040], %f0 + EX_LD(LOAD(ldd, %o1 + 0x040, %f0)) 1: faligndata %f10, %f12, %f26 faligndata %f12, %f14, %f28 faligndata %f14, %f0, %f30 - stda %f16, [%o0] ASI_BLK_P + EX_ST(STORE_BLK(%f16, %o0)) add %o0, 0x40, %o0 add %o1, 0x40, %o1 membar #Sync @@ -189,23 +246,22 @@ U3memcpy: /* %o0=dst, %o1=src, %o2=len */ be,pn %XCC, 2f cmp %g1, 0 + sub %o2, %g2, %o2 be,a,pt %XCC, 1f - ldd [%o1 + 0x00], %f0 + EX_LD(LOAD(ldd, %o1 + 0x00, %f0)) -1: ldd [%o1 + 0x08], %f2 +1: EX_LD(LOAD(ldd, %o1 + 0x08, %f2)) add %o1, 0x8, %o1 - sub %o2, 0x8, %o2 subcc %g2, 0x8, %g2 faligndata %f0, %f2, %f8 - std %f8, [%o0 + 0x00] + EX_ST(STORE(std, %f8, %o0)) be,pn %XCC, 2f add %o0, 0x8, %o0 - ldd [%o1 + 0x08], %f0 + EX_LD(LOAD(ldd, %o1 + 0x08, %f0)) add %o1, 0x8, %o1 - sub %o2, 0x8, %o2 subcc %g2, 0x8, %g2 faligndata %f2, %f0, %f8 - std %f8, [%o0 + 0x00] + EX_ST(STORE(std, %f8, %o0)) bne,pn %XCC, 1b add %o0, 0x8, %o0 @@ -225,48 +281,60 @@ U3memcpy: /* %o0=dst, %o1=src, %o2=len */ andcc %o2, 0x8, %g0 be,pt %icc, 1f nop - ldx [%o1], %o5 - stx %o5, [%o1 + %o3] + EX_LD(LOAD(ldx, %o1, %o5)) + EX_ST(STORE(stx, %o5, %o1 + %o3)) add %o1, 0x8, %o1 1: andcc %o2, 0x4, %g0 be,pt %icc, 1f nop - lduw [%o1], %o5 - stw %o5, [%o1 + %o3] + EX_LD(LOAD(lduw, %o1, %o5)) + EX_ST(STORE(stw, %o5, %o1 + %o3)) add %o1, 0x4, %o1 1: andcc %o2, 0x2, %g0 be,pt %icc, 1f nop - lduh [%o1], %o5 - sth %o5, [%o1 + %o3] + EX_LD(LOAD(lduh, %o1, %o5)) + EX_ST(STORE(sth, %o5, %o1 + %o3)) add %o1, 0x2, %o1 1: andcc %o2, 0x1, %g0 be,pt %icc, 85f nop - ldub [%o1], %o5 + EX_LD(LOAD(ldub, %o1, %o5)) ba,pt %xcc, 85f - stb %o5, [%o1 + %o3] + EX_ST(STORE(stb, %o5, %o1 + %o3)) + .align 64 70: /* 16 < len <= 64 */ - bne,pn %XCC, 90f + bne,pn %XCC, 75f sub %o0, %o1, %o3 - andn %o2, 0x7, %o4 - and %o2, 0x7, %o2 -1: subcc %o4, 0x8, %o4 - ldx [%o1], %o5 - stx %o5, [%o1 + %o3] +72: + andn %o2, 0xf, %o4 + and %o2, 0xf, %o2 +1: subcc %o4, 0x10, %o4 + EX_LD(LOAD(ldx, %o1 + 0x00, %o5)) + EX_LD(LOAD(ldx, %o1 + 0x08, %g1)) + EX_ST(STORE(stx, %o5, %o1 + %o3)) + add %o1, 0x8, %o1 + EX_ST(STORE(stx, %g1, %o1 + %o3)) bgu,pt %XCC, 1b add %o1, 0x8, %o1 - andcc %o2, 0x4, %g0 +73: andcc %o2, 0x8, %g0 + be,pt %XCC, 1f + nop + sub %o2, 0x8, %o2 + EX_LD(LOAD(ldx, %o1, %o5)) + EX_ST(STORE(stx, %o5, %o1 + %o3)) + add %o1, 0x8, %o1 +1: andcc %o2, 0x4, %g0 be,pt %XCC, 1f nop sub %o2, 0x4, %o2 - lduw [%o1], %o5 - stw %o5, [%o1 + %o3] + EX_LD(LOAD(lduw, %o1, %o5)) + EX_ST(STORE(stw, %o5, %o1 + %o3)) add %o1, 0x4, %o1 1: cmp %o2, 0 be,pt %XCC, 85f @@ -274,6 +342,53 @@ U3memcpy: /* %o0=dst, %o1=src, %o2=len */ ba,pt %xcc, 90f nop +75: + andcc %o0, 0x7, %g1 + sub %g1, 0x8, %g1 + be,pn %icc, 2f + sub %g0, %g1, %g1 + sub %o2, %g1, %o2 + +1: subcc %g1, 1, %g1 + EX_LD(LOAD(ldub, %o1, %o5)) + EX_ST(STORE(stb, %o5, %o1 + %o3)) + bgu,pt %icc, 1b + add %o1, 1, %o1 + +2: add %o1, %o3, %o0 + andcc %o1, 0x7, %g1 + bne,pt %icc, 8f + sll %g1, 3, %g1 + + cmp %o2, 16 + bgeu,pt %icc, 72b + nop + ba,a,pt %xcc, 73b + +8: mov 64, %o3 + andn %o1, 0x7, %o1 + EX_LD(LOAD(ldx, %o1, %g2)) + sub %o3, %g1, %o3 + andn %o2, 0x7, %o4 + sllx %g2, %g1, %g2 +1: EX_LD(LOAD(ldx, %o1 + 0x8, %g3)) + subcc %o4, 0x8, %o4 + add %o1, 0x8, %o1 + srlx %g3, %o3, %o5 + or %o5, %g2, %o5 + EX_ST(STORE(stx, %o5, %o0)) + add %o0, 0x8, %o0 + bgu,pt %icc, 1b + sllx %g3, %g1, %g2 + + srl %g1, 3, %g1 + andcc %o2, 0x7, %o2 + be,pn %icc, 85f + add %o1, %g1, %o1 + ba,pt %xcc, 90f + sub %o0, %o1, %o3 + + .align 64 80: /* 0 < len <= 16 */ andcc %o3, 0x3, %g0 bne,pn %XCC, 90f @@ -281,34 +396,22 @@ U3memcpy: /* %o0=dst, %o1=src, %o2=len */ 1: subcc %o2, 4, %o2 - lduw [%o1], %g1 - stw %g1, [%o1 + %o3] + EX_LD(LOAD(lduw, %o1, %g1)) + EX_ST(STORE(stw, %g1, %o1 + %o3)) bgu,pt %XCC, 1b add %o1, 4, %o1 85: retl - mov %g5, %o0 + mov EX_RETVAL(%g5), %o0 - .align 32 + .align 32 90: subcc %o2, 1, %o2 - ldub [%o1], %g1 - stb %g1, [%o1 + %o3] + EX_LD(LOAD(ldub, %o1, %g1)) + EX_ST(STORE(stb, %g1, %o1 + %o3)) bgu,pt %XCC, 90b add %o1, 1, %o1 retl - mov %g5, %o0 + mov EX_RETVAL(%g5), %o0 - /* Act like copy_{to,in}_user(), ie. return zero instead - * of original destination pointer. This is invoked when - * copy_{to,in}_user() finds that %asi is kernel space. - */ - .globl U3memcpy_user_stub -U3memcpy_user_stub: - save %sp, -192, %sp - mov %i0, %o0 - mov %i1, %o1 - call U3memcpy - mov %i2, %o2 - ret - restore %g0, %g0, %o0 + .size FUNC_NAME, .-FUNC_NAME diff --git a/arch/sparc64/lib/U3patch.S b/arch/sparc64/lib/U3patch.S new file mode 100644 index 000000000..e2b6c5e4b --- /dev/null +++ b/arch/sparc64/lib/U3patch.S @@ -0,0 +1,32 @@ +/* U3patch.S: Patch Ultra-I routines with Ultra-III variant. + * + * Copyright (C) 2004 David S. Miller + */ + +#define BRANCH_ALWAYS 0x10680000 +#define NOP 0x01000000 +#define ULTRA3_DO_PATCH(OLD, NEW) \ + sethi %hi(NEW), %g1; \ + or %g1, %lo(NEW), %g1; \ + sethi %hi(OLD), %g2; \ + or %g2, %lo(OLD), %g2; \ + sub %g1, %g2, %g1; \ + sethi %hi(BRANCH_ALWAYS), %g3; \ + srl %g1, 2, %g1; \ + or %g3, %lo(BRANCH_ALWAYS), %g3; \ + or %g3, %g1, %g3; \ + stw %g3, [%g2]; \ + sethi %hi(NOP), %g3; \ + or %g3, %lo(NOP), %g3; \ + stw %g3, [%g2 + 0x4]; \ + flush %g2; + + .globl cheetah_patch_copyops + .type cheetah_patch_copyops,#function +cheetah_patch_copyops: + ULTRA3_DO_PATCH(memcpy, U3memcpy) + ULTRA3_DO_PATCH(___copy_from_user, U3copy_from_user) + ULTRA3_DO_PATCH(___copy_to_user, U3copy_to_user) + retl + nop + .size cheetah_patch_copyops,.-cheetah_patch_copyops diff --git a/arch/sparc64/lib/atomic.S b/arch/sparc64/lib/atomic.S index a0d6d8ac3..26463d8a4 100644 --- a/arch/sparc64/lib/atomic.S +++ b/arch/sparc64/lib/atomic.S @@ -10,6 +10,7 @@ .align 64 .globl __atomic_add + .type __atomic_add,#function __atomic_add: /* %o0 = increment, %o1 = atomic_ptr */ lduw [%o1], %g5 add %g5, %o0, %g7 @@ -19,8 +20,10 @@ __atomic_add: /* %o0 = increment, %o1 = atomic_ptr */ membar #StoreLoad | #StoreStore retl add %g7, %o0, %o0 + .size __atomic_add, .-__atomic_add .globl __atomic_sub + .type __atomic_sub,#function __atomic_sub: /* %o0 = increment, %o1 = atomic_ptr */ lduw [%o1], %g5 sub %g5, %o0, %g7 @@ -30,8 +33,10 @@ __atomic_sub: /* %o0 = increment, %o1 = atomic_ptr */ membar #StoreLoad | #StoreStore retl sub %g7, %o0, %o0 + .size __atomic_sub, .-__atomic_sub .globl __atomic64_add + .type __atomic64_add,#function __atomic64_add: /* %o0 = increment, %o1 = atomic_ptr */ ldx [%o1], %g5 add %g5, %o0, %g7 @@ -41,8 +46,10 @@ __atomic64_add: /* %o0 = increment, %o1 = atomic_ptr */ membar #StoreLoad | #StoreStore retl add %g7, %o0, %o0 + .size __atomic64_add, .-__atomic64_add .globl __atomic64_sub + .type __atomic64_sub,#function __atomic64_sub: /* %o0 = increment, %o1 = atomic_ptr */ ldx [%o1], %g5 sub %g5, %o0, %g7 @@ -52,4 +59,4 @@ __atomic64_sub: /* %o0 = increment, %o1 = atomic_ptr */ membar #StoreLoad | #StoreStore retl sub %g7, %o0, %o0 - + .size __atomic64_sub, .-__atomic64_sub diff --git a/arch/sparc64/lib/bitops.S b/arch/sparc64/lib/bitops.S index 0c2aacc83..c1e370068 100644 --- a/arch/sparc64/lib/bitops.S +++ b/arch/sparc64/lib/bitops.S @@ -9,6 +9,7 @@ .text .align 64 .globl ___test_and_set_bit + .type ___test_and_set_bit,#function ___test_and_set_bit: /* %o0=nr, %o1=addr */ srlx %o0, 6, %g1 mov 1, %g5 @@ -26,8 +27,10 @@ ___test_and_set_bit: /* %o0=nr, %o1=addr */ ldx [%o1], %g7 2: retl membar #StoreLoad | #StoreStore + .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 */ srlx %o0, 6, %g1 mov 1, %g5 @@ -45,8 +48,10 @@ ___test_and_clear_bit: /* %o0=nr, %o1=addr */ ldx [%o1], %g7 2: retl membar #StoreLoad | #StoreStore + .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 */ srlx %o0, 6, %g1 mov 1, %g5 @@ -64,41 +69,4 @@ ___test_and_change_bit: /* %o0=nr, %o1=addr */ 2: retl membar #StoreLoad | #StoreStore nop - - .globl ___test_and_set_le_bit -___test_and_set_le_bit: /* %o0=nr, %o1=addr */ - srlx %o0, 5, %g1 - mov 1, %g5 - sllx %g1, 2, %g3 - and %o0, 31, %g2 - sllx %g5, %g2, %g5 - add %o1, %g3, %o1 - lduwa [%o1] ASI_PL, %g7 -1: andcc %g7, %g5, %o0 - bne,pn %icc, 2f - xor %g7, %g5, %g1 - casa [%o1] ASI_PL, %g7, %g1 - cmp %g7, %g1 - bne,a,pn %icc, 1b - lduwa [%o1] ASI_PL, %g7 -2: retl - membar #StoreLoad | #StoreStore - - .globl ___test_and_clear_le_bit -___test_and_clear_le_bit: /* %o0=nr, %o1=addr */ - srlx %o0, 5, %g1 - mov 1, %g5 - sllx %g1, 2, %g3 - and %o0, 31, %g2 - sllx %g5, %g2, %g5 - add %o1, %g3, %o1 - lduwa [%o1] ASI_PL, %g7 -1: andcc %g7, %g5, %o0 - be,pn %icc, 2f - xor %g7, %g5, %g1 - casa [%o1] ASI_PL, %g7, %g1 - cmp %g7, %g1 - bne,a,pn %icc, 1b - lduwa [%o1] ASI_PL, %g7 -2: retl - membar #StoreLoad | #StoreStore + .size ___test_and_change_bit, .-___test_and_change_bit diff --git a/arch/sparc64/lib/copy_in_user.S b/arch/sparc64/lib/copy_in_user.S new file mode 100644 index 000000000..816076c0b --- /dev/null +++ b/arch/sparc64/lib/copy_in_user.S @@ -0,0 +1,119 @@ +/* copy_in_user.S: Copy from userspace to userspace. + * + * Copyright (C) 1999, 2000, 2004 David S. Miller (davem@redhat.com) + */ + +#include + +#define XCC xcc + +#define EX(x,y) \ +98: x,y; \ + .section .fixup; \ + .align 4; \ +99: retl; \ + mov 1, %o0; \ + .section __ex_table; \ + .align 4; \ + .word 98b, 99b; \ + .text; \ + .align 4; + + .register %g2,#scratch + .register %g3,#scratch + + .text + .align 32 + + /* Don't try to get too fancy here, just nice and + * simple. This is predominantly used for well aligned + * small copies in the compat layer. It is also used + * to copy register windows around during thread cloning. + */ + + .globl ___copy_in_user + .type ___copy_in_user,#function +___copy_in_user: /* %o0=dst, %o1=src, %o2=len */ + /* Writing to %asi is _expensive_ so we hardcode it. + * Reading %asi to check for KERNEL_DS is comparatively + * cheap. + */ + rd %asi, %g1 + cmp %g1, ASI_AIUS + bne,pn %icc, memcpy_user_stub + nop + + cmp %o2, 0 + be,pn %XCC, 85f + or %o0, %o1, %o3 + cmp %o2, 16 + bleu,a,pn %XCC, 80f + or %o3, %o2, %o3 + + /* 16 < len <= 64 */ + andcc %o3, 0x7, %g0 + bne,pn %XCC, 90f + sub %o0, %o1, %o3 + + andn %o2, 0x7, %o4 + and %o2, 0x7, %o2 +1: subcc %o4, 0x8, %o4 + EX(ldxa [%o1] %asi, %o5) + EX(stxa %o5, [%o1 + %o3] ASI_AIUS) + bgu,pt %XCC, 1b + add %o1, 0x8, %o1 + andcc %o2, 0x4, %g0 + be,pt %XCC, 1f + nop + sub %o2, 0x4, %o2 + EX(lduwa [%o1] %asi, %o5) + EX(stwa %o5, [%o1 + %o3] ASI_AIUS) + add %o1, 0x4, %o1 +1: cmp %o2, 0 + be,pt %XCC, 85f + nop + ba,pt %xcc, 90f + nop + +80: /* 0 < len <= 16 */ + andcc %o3, 0x3, %g0 + bne,pn %XCC, 90f + sub %o0, %o1, %o3 + +82: + subcc %o2, 4, %o2 + EX(lduwa [%o1] %asi, %g1) + EX(stwa %g1, [%o1 + %o3] ASI_AIUS) + bgu,pt %XCC, 82b + add %o1, 4, %o1 + +85: retl + clr %o0 + + .align 32 +90: + subcc %o2, 1, %o2 + EX(lduba [%o1] %asi, %g1) + EX(stba %g1, [%o1 + %o3] ASI_AIUS) + bgu,pt %XCC, 90b + add %o1, 1, %o1 + retl + clr %o0 + + .size ___copy_in_user, .-___copy_in_user + + /* Act like copy_{to,in}_user(), ie. return zero instead + * of original destination pointer. This is invoked when + * copy_{to,in}_user() finds that %asi is kernel space. + */ + .globl memcpy_user_stub + .type memcpy_user_stub,#function +memcpy_user_stub: + save %sp, -192, %sp + mov %i0, %o0 + mov %i1, %o1 + call memcpy + mov %i2, %o2 + ret + restore %g0, %g0, %o0 + .size memcpy_user_stub, .-memcpy_user_stub diff --git a/arch/sparc64/lib/copy_page.S b/arch/sparc64/lib/copy_page.S index 862eefb17..23ebf2c97 100644 --- a/arch/sparc64/lib/copy_page.S +++ b/arch/sparc64/lib/copy_page.S @@ -45,6 +45,7 @@ .align 32 .globl copy_user_page + .type copy_user_page,#function copy_user_page: /* %o0=dest, %o1=src, %o2=vaddr */ lduw [%g6 + TI_PRE_COUNT], %o4 sethi %uhi(PAGE_OFFSET), %g2 @@ -237,3 +238,5 @@ copy_user_page: /* %o0=dest, %o1=src, %o2=vaddr */ retl stw %o4, [%g6 + TI_PRE_COUNT] + + .size copy_user_page, .-copy_user_page diff --git a/arch/sparc64/lib/delay.c b/arch/sparc64/lib/delay.c new file mode 100644 index 000000000..f55f528a7 --- /dev/null +++ b/arch/sparc64/lib/delay.c @@ -0,0 +1,49 @@ +/* delay.c: Delay loops for sparc64 + * + * Copyright (C) 2004 David S. Miller + * + * Based heavily upon x86 variant which is: + * Copyright (C) 1993 Linus Torvalds + * Copyright (C) 1997 Martin Mares + */ + +#include + +void __delay(unsigned long loops) +{ + __asm__ __volatile__( +" b,pt %%xcc, 1f\n" +" cmp %0, 0\n" +" .align 32\n" +"1:\n" +" bne,pt %%xcc, 1b\n" +" subcc %0, 1, %0\n" + : "=&r" (loops) + : "0" (loops) + : "cc"); +} + +/* We used to multiply by HZ after shifting down by 32 bits + * but that runs into problems for higher values of HZ and + * slow cpus. + */ +void __const_udelay(unsigned long n) +{ + n *= 4; + + n *= (cpu_data(smp_processor_id()).udelay_val * (HZ/4)); + n >>= 32; + + __delay(n + 1); +} + +void __udelay(unsigned long n) +{ + __const_udelay(n * 0x10c7UL); +} + + +void __ndelay(unsigned long n) +{ + __const_udelay(n * 0x5UL); +} diff --git a/arch/sparc64/lib/iomap.c b/arch/sparc64/lib/iomap.c new file mode 100644 index 000000000..ac556db06 --- /dev/null +++ b/arch/sparc64/lib/iomap.c @@ -0,0 +1,48 @@ +/* + * Implement the sparc64 iomap interfaces + */ +#include +#include +#include + +/* 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) +{ + unsigned long start = pci_resource_start(dev, bar); + unsigned long 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/ipcsum.S b/arch/sparc64/lib/ipcsum.S index e7d349fac..58ca5b9a8 100644 --- a/arch/sparc64/lib/ipcsum.S +++ b/arch/sparc64/lib/ipcsum.S @@ -1,6 +1,7 @@ .text - .align 32 - .globl ip_fast_csum + .align 32 + .globl ip_fast_csum + .type ip_fast_csum,#function ip_fast_csum: /* %o0 = iph, %o1 = ihl */ sub %o1, 4, %g7 lduw [%o0 + 0x00], %o2 @@ -30,3 +31,4 @@ ip_fast_csum: /* %o0 = iph, %o1 = ihl */ set 0xffff, %o1 retl and %o2, %o1, %o0 + .size ip_fast_csum, .-ip_fast_csum diff --git a/arch/sparc64/lib/memmove.S b/arch/sparc64/lib/memmove.S new file mode 100644 index 000000000..ca8781d0b --- /dev/null +++ b/arch/sparc64/lib/memmove.S @@ -0,0 +1,33 @@ +/* memmove.S: Simple memmove implementation. + * + * Copyright (C) 1997, 2004 David S. Miller (davem@redhat.com) + * Copyright (C) 1996, 1997, 1998, 1999 Jakub Jelinek (jj@ultra.linux.cz) + */ + + .text + .align 32 + .globl memmove + .type memmove,#function +memmove: + mov %o0, %g1 + cmp %o0, %o1 + blu,pt %xcc, memcpy + sub %o0, %o1, %g5 + add %o1, %o2, %g3 + cmp %g3, %o0 + bleu,pt %xcc, memcpy + add %o1, %o2, %g5 + add %o0, %o2, %o5 + + sub %g5, 1, %o1 + sub %o5, 1, %o0 +1: ldub [%o1], %g5 + subcc %o2, 1, %o2 + sub %o1, 1, %o1 + stb %g5, [%o0] + bne,pt %icc, 1b + sub %o0, 1, %o0 + + retl + mov %g1, %o0 + .size memmove, .-memmove diff --git a/arch/sparc64/lib/strlen.S b/arch/sparc64/lib/strlen.S index 55527f639..066ec1ed7 100644 --- a/arch/sparc64/lib/strlen.S +++ b/arch/sparc64/lib/strlen.S @@ -9,9 +9,9 @@ #define HI_MAGIC 0x80808080 .align 32 - .global strlen, __strlen + .globl strlen + .type strlen,#function strlen: -__strlen: mov %o0, %o1 andcc %o0, 3, %g0 be,pt %icc, 9f @@ -76,3 +76,5 @@ __strlen: 13: retl mov 2, %o0 + + .size strlen, .-strlen diff --git a/arch/sparc64/lib/strncmp.S b/arch/sparc64/lib/strncmp.S index 474ba7296..6f14f53db 100644 --- a/arch/sparc64/lib/strncmp.S +++ b/arch/sparc64/lib/strncmp.S @@ -7,9 +7,9 @@ #include .text - .align 4 - .global __strncmp, strncmp -__strncmp: + .align 32 + .globl strncmp + .type strncmp,#function strncmp: brlez,pn %o2, 3f lduba [%o0] (ASI_PNF), %o3 @@ -29,3 +29,4 @@ strncmp: 3: retl clr %o0 + .size strncmp, .-strncmp diff --git a/arch/sparc64/lib/strncpy_from_user.S b/arch/sparc64/lib/strncpy_from_user.S index 9abb371e4..93d600a31 100644 --- a/arch/sparc64/lib/strncpy_from_user.S +++ b/arch/sparc64/lib/strncpy_from_user.S @@ -12,7 +12,7 @@ 0: .xword 0x0101010101010101 .text - .align 4 + .align 32 /* Must return: * @@ -31,6 +31,7 @@ */ .globl __strncpy_from_user + .type __strncpy_from_user,#function __strncpy_from_user: /* %o0=dest, %o1=src, %o2=count */ sethi %hi(0b), %o5 ! IEU0 Group @@ -122,6 +123,7 @@ __strncpy_from_user: mov %o2, %o0 2: retl add %o2, %o3, %o0 + .size __strncpy_from_user, .-__strncpy_from_user .section .fixup,#alloc,#execinstr .align 4 diff --git a/arch/sparc64/lib/user_fixup.c b/arch/sparc64/lib/user_fixup.c new file mode 100644 index 000000000..58ec56050 --- /dev/null +++ b/arch/sparc64/lib/user_fixup.c @@ -0,0 +1,68 @@ +/* user_fixup.c: Fix up user copy faults. + * + * Copyright (C) 2004 David S. Miller + */ + +#include +#include +#include +#include +#include + +/* Calculating the exact fault address when using + * block loads and stores can be very complicated. + * Instead of trying to be clever and handling all + * of the cases, just fix things up simply here. + */ + +unsigned long copy_from_user_fixup(void *to, const void __user *from, unsigned long size) +{ + char *dst = to; + const char __user *src = from; + + while (size--) { + if (__get_user(*dst, src)) + break; + dst++; + src++; + } + + if (size) + memset(dst, 0, size); + + return size; +} + +unsigned long copy_to_user_fixup(void __user *to, const void *from, unsigned long size) +{ + char __user *dst = to; + const char *src = from; + + while (size--) { + if (__put_user(*src, dst)) + break; + dst++; + src++; + } + + return size; +} + +unsigned long copy_in_user_fixup(void __user *to, void __user *from, unsigned long size) +{ + char __user *dst = to; + char __user *src = from; + + while (size--) { + char tmp; + + if (__get_user(tmp, src)) + break; + if (__put_user(tmp, dst)) + break; + dst++; + src++; + } + + return size; +} diff --git a/arch/sparc64/lib/xor.S b/arch/sparc64/lib/xor.S index 8eeba353d..f748fd6bb 100644 --- a/arch/sparc64/lib/xor.S +++ b/arch/sparc64/lib/xor.S @@ -18,8 +18,9 @@ * !(len & 127) && len >= 256 */ .text - .globl xor_vis_2 - .type xor_vis_2,@function + .align 32 + .globl xor_vis_2 + .type xor_vis_2,#function xor_vis_2: rd %fprs, %o5 andcc %o5, FPRS_FEF|FPRS_DU, %g0 @@ -87,11 +88,10 @@ xor_vis_2: wr %g1, %g0, %asi retl wr %g0, 0, %fprs - .size xor_vis_2, .-xor_vis_2 + .size xor_vis_2, .-xor_vis_2 - - .globl xor_vis_3 - .type xor_vis_3,@function + .globl xor_vis_3 + .type xor_vis_3,#function xor_vis_3: rd %fprs, %o5 andcc %o5, FPRS_FEF|FPRS_DU, %g0 @@ -156,11 +156,10 @@ xor_vis_3: wr %g1, %g0, %asi retl wr %g0, 0, %fprs - .size xor_vis_3, .-xor_vis_3 - + .size xor_vis_3, .-xor_vis_3 - .globl xor_vis_4 - .type xor_vis_4,@function + .globl xor_vis_4 + .type xor_vis_4,#function xor_vis_4: rd %fprs, %o5 andcc %o5, FPRS_FEF|FPRS_DU, %g0 @@ -244,11 +243,10 @@ xor_vis_4: wr %g1, %g0, %asi retl wr %g0, 0, %fprs - .size xor_vis_4, .-xor_vis_4 - + .size xor_vis_4, .-xor_vis_4 - .globl xor_vis_5 - .type xor_vis_5,@function + .globl xor_vis_5 + .type xor_vis_5,#function xor_vis_5: mov %o5, %g5 rd %fprs, %o5 @@ -353,4 +351,4 @@ xor_vis_5: wr %g1, %g0, %asi retl wr %g0, 0, %fprs - .size xor_vis_5, .-xor_vis_5 + .size xor_vis_5, .-xor_vis_5 diff --git a/arch/sparc64/solaris/misc.c b/arch/sparc64/solaris/misc.c index 4a3db02e1..15b4cfe07 100644 --- a/arch/sparc64/solaris/misc.c +++ b/arch/sparc64/solaris/misc.c @@ -137,21 +137,34 @@ asmlinkage int solaris_brk(u32 brk) return sunos_brk(brk); } -#define set_utsfield(to, from, dotchop, countfrom) { \ - char *p; \ - int i, len = (countfrom) ? \ - ((sizeof(to) > sizeof(from) ? \ - sizeof(from) : sizeof(to))) : sizeof(to); \ - if (copy_to_user(to, from, len)) \ - return -EFAULT; \ - if (dotchop) \ - for (p=from,i=0; *p && *p != '.' && --len; p++,i++); \ - else \ - i = len - 1; \ - if (__put_user('\0', (char __user *)((to)+i))) \ - return -EFAULT; \ +static int __set_utsfield(char __user *to, int to_size, + const char *from, int from_size, + int dotchop, int countfrom) +{ + int len = countfrom ? (to_size > from_size ? + from_size : to_size) : to_size; + int off; + + if (copy_to_user(to, from, len)) + return -EFAULT; + + off = len < to_size? len: len - 1; + if (dotchop) { + const char *p = strnchr(from, len, '.'); + if (p) off = p - from; + } + + if (__put_user('\0', to + off)) + return -EFAULT; + + return 0; } +#define set_utsfield(to, from, dotchop, countfrom) \ + __set_utsfield((to), sizeof(to), \ + (from), sizeof(from), \ + (dotchop), (countfrom)) + struct sol_uname { char sysname[9]; char nodename[9]; @@ -219,17 +232,20 @@ static char *serial(char *buffer) asmlinkage int solaris_utssys(u32 buf, u32 flags, int which, u32 buf2) { struct sol_uname __user *v = A(buf); + int err; + switch (which) { case 0: /* old uname */ /* Let's cheat */ - set_utsfield(v->sysname, "SunOS", 1, 0); + err = set_utsfield(v->sysname, "SunOS", 1, 0); down_read(&uts_sem); - set_utsfield(v->nodename, system_utsname.nodename, 1, 1); + err |= set_utsfield(v->nodename, system_utsname.nodename, + 1, 1); up_read(&uts_sem); - set_utsfield(v->release, "2.6", 0, 0); - set_utsfield(v->version, "Generic", 0, 0); - set_utsfield(v->machine, machine(), 0, 0); - return 0; + err |= set_utsfield(v->release, "2.6", 0, 0); + err |= set_utsfield(v->version, "Generic", 0, 0); + err |= set_utsfield(v->machine, machine(), 0, 0); + return (err ? -EFAULT : 0); case 2: /* ustat */ return -ENOSYS; case 3: /* fusers */ @@ -242,15 +258,18 @@ asmlinkage int solaris_utssys(u32 buf, u32 flags, int which, u32 buf2) asmlinkage int solaris_utsname(u32 buf) { struct sol_utsname __user *v = A(buf); + int err; + /* Why should we not lie a bit? */ down_read(&uts_sem); - set_utsfield(v->sysname, "SunOS", 0, 0); - set_utsfield(v->nodename, system_utsname.nodename, 1, 1); - set_utsfield(v->release, "5.6", 0, 0); - set_utsfield(v->version, "Generic", 0, 0); - set_utsfield(v->machine, machine(), 0, 0); + err = set_utsfield(v->sysname, "SunOS", 0, 0); + err |= set_utsfield(v->nodename, system_utsname.nodename, 1, 1); + err |= set_utsfield(v->release, "5.6", 0, 0); + err |= set_utsfield(v->version, "Generic", 0, 0); + err |= set_utsfield(v->machine, machine(), 0, 0); up_read(&uts_sem); - return 0; + + return (err ? -EFAULT : 0); } #define SI_SYSNAME 1 /* return name of operating system */ diff --git a/arch/um/Kconfig.debug b/arch/um/Kconfig.debug new file mode 100644 index 000000000..a2d58306d --- /dev/null +++ b/arch/um/Kconfig.debug @@ -0,0 +1,43 @@ +menu "Kernel hacking" + +source "lib/Kconfig.debug" + +config FRAME_POINTER + bool + default y if DEBUG_INFO + +config PT_PROXY + bool "Enable ptrace proxy" + depends on XTERM_CHAN && DEBUG_INFO + help + This option enables a debugging interface which allows gdb to debug + the kernel without needing to actually attach to kernel threads. + If you want to do kernel debugging, say Y here; otherwise say N. + +config GPROF + bool "Enable gprof support" + depends on DEBUG_INFO + help + This allows profiling of a User-Mode Linux kernel with the gprof + utility. + + See for more + details. + + If you're involved in UML kernel development and want to use gprof, + say Y. If you're unsure, say N. + +config GCOV + bool "Enable gcov support" + depends on DEBUG_INFO + help + This option allows developers to retrieve coverage data from a UML + session. + + See for more + details. + + If you're involved in UML kernel development and want to use gcov, + say Y. If you're unsure, say N. + +endmenu diff --git a/arch/um/Makefile-tt b/arch/um/Makefile-tt index 8d58a3083..eed367d9c 100644 --- a/arch/um/Makefile-tt +++ b/arch/um/Makefile-tt @@ -3,5 +3,4 @@ # Licensed under the GPL # -MODE_INCLUDE += -I$(TOPDIR)/$(ARCH_DIR)/kernel/tt/include - +MODE_INCLUDE += -I$(srctree)/$(ARCH_DIR)/kernel/tt/include diff --git a/arch/um/kernel/dyn.lds.S b/arch/um/kernel/dyn.lds.S new file mode 100644 index 000000000..ee1642119 --- /dev/null +++ b/arch/um/kernel/dyn.lds.S @@ -0,0 +1,171 @@ +#include + +OUTPUT_FORMAT(ELF_FORMAT) +OUTPUT_ARCH(ELF_ARCH) +ENTRY(_start) +jiffies = jiffies_64; + +SECTIONS +{ + . = START + SIZEOF_HEADERS; + .interp : { *(.interp) } + __binary_start = .; + . = ALIGN(4096); /* Init code and data */ + _stext = .; + __init_begin = .; + .init.text : { + _sinittext = .; + *(.init.text) + _einittext = .; + } + + . = ALIGN(4096); + + /* Read-only sections, merged into text segment: */ + .hash : { *(.hash) } + .dynsym : { *(.dynsym) } + .dynstr : { *(.dynstr) } + .gnu.version : { *(.gnu.version) } + .gnu.version_d : { *(.gnu.version_d) } + .gnu.version_r : { *(.gnu.version_r) } + .rel.init : { *(.rel.init) } + .rela.init : { *(.rela.init) } + .rel.text : { *(.rel.text .rel.text.* .rel.gnu.linkonce.t.*) } + .rela.text : { *(.rela.text .rela.text.* .rela.gnu.linkonce.t.*) } + .rel.fini : { *(.rel.fini) } + .rela.fini : { *(.rela.fini) } + .rel.rodata : { *(.rel.rodata .rel.rodata.* .rel.gnu.linkonce.r.*) } + .rela.rodata : { *(.rela.rodata .rela.rodata.* .rela.gnu.linkonce.r.*) } + .rel.data : { *(.rel.data .rel.data.* .rel.gnu.linkonce.d.*) } + .rela.data : { *(.rela.data .rela.data.* .rela.gnu.linkonce.d.*) } + .rel.tdata : { *(.rel.tdata .rel.tdata.* .rel.gnu.linkonce.td.*) } + .rela.tdata : { *(.rela.tdata .rela.tdata.* .rela.gnu.linkonce.td.*) } + .rel.tbss : { *(.rel.tbss .rel.tbss.* .rel.gnu.linkonce.tb.*) } + .rela.tbss : { *(.rela.tbss .rela.tbss.* .rela.gnu.linkonce.tb.*) } + .rel.ctors : { *(.rel.ctors) } + .rela.ctors : { *(.rela.ctors) } + .rel.dtors : { *(.rel.dtors) } + .rela.dtors : { *(.rela.dtors) } + .rel.got : { *(.rel.got) } + .rela.got : { *(.rela.got) } + .rel.bss : { *(.rel.bss .rel.bss.* .rel.gnu.linkonce.b.*) } + .rela.bss : { *(.rela.bss .rela.bss.* .rela.gnu.linkonce.b.*) } + .rel.plt : { *(.rel.plt) } + .rela.plt : { *(.rela.plt) } + .init : { + KEEP (*(.init)) + } =0x90909090 + .plt : { *(.plt) } + .text : { + *(.text) + SCHED_TEXT + *(.stub .text.* .gnu.linkonce.t.*) + /* .gnu.warning sections are handled specially by elf32.em. */ + *(.gnu.warning) + } =0x90909090 + .fini : { + KEEP (*(.fini)) + } =0x90909090 + + .kstrtab : { *(.kstrtab) } + + #include "asm/common.lds.S" + + init.data : { *(.init.data) } + + /* Ensure the __preinit_array_start label is properly aligned. We + could instead move the label definition inside the section, but + the linker would then create the section even if it turns out to + be empty, which isn't pretty. */ + . = ALIGN(32 / 8); + .preinit_array : { *(.preinit_array) } + .init_array : { *(.init_array) } + .fini_array : { *(.fini_array) } + .data : { + . = ALIGN(KERNEL_STACK_SIZE); /* init_task */ + *(.data.init_task) + *(.data .data.* .gnu.linkonce.d.*) + SORT(CONSTRUCTORS) + } + .data1 : { *(.data1) } + .tdata : { *(.tdata .tdata.* .gnu.linkonce.td.*) } + .tbss : { *(.tbss .tbss.* .gnu.linkonce.tb.*) *(.tcommon) } + .eh_frame : { KEEP (*(.eh_frame)) } + .gcc_except_table : { *(.gcc_except_table) } + .dynamic : { *(.dynamic) } + .ctors : { + /* gcc uses crtbegin.o to find the start of + the constructors, so we make sure it is + first. Because this is a wildcard, it + doesn't matter if the user does not + actually link against crtbegin.o; the + linker won't look for a file to match a + wildcard. The wildcard also means that it + doesn't matter which directory crtbegin.o + is in. */ + KEEP (*crtbegin.o(.ctors)) + /* We don't want to include the .ctor section from + from the crtend.o file until after the sorted ctors. + The .ctor section from the crtend file contains the + end of ctors marker and it must be last */ + KEEP (*(EXCLUDE_FILE (*crtend.o ) .ctors)) + KEEP (*(SORT(.ctors.*))) + KEEP (*(.ctors)) + } + .dtors : { + KEEP (*crtbegin.o(.dtors)) + KEEP (*(EXCLUDE_FILE (*crtend.o ) .dtors)) + KEEP (*(SORT(.dtors.*))) + KEEP (*(.dtors)) + } + .jcr : { KEEP (*(.jcr)) } + .got : { *(.got.plt) *(.got) } + _edata = .; + PROVIDE (edata = .); + __bss_start = .; + .bss : { + *(.dynbss) + *(.bss .bss.* .gnu.linkonce.b.*) + *(COMMON) + /* Align here to ensure that the .bss section occupies space up to + _end. Align after .bss to ensure correct alignment even if the + .bss section disappears because there are no input sections. */ + . = ALIGN(32 / 8); + . = ALIGN(32 / 8); + } + _end = .; + PROVIDE (end = .); + /* Stabs debugging sections. */ + .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) } +} diff --git a/arch/um/kernel/main.c b/arch/um/kernel/main.c new file mode 100644 index 000000000..e1fd2c583 --- /dev/null +++ b/arch/um/kernel/main.c @@ -0,0 +1,249 @@ +/* + * Copyright (C) 2000, 2001 Jeff Dike (jdike@karaya.com) + * Licensed under the GPL + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "user_util.h" +#include "kern_util.h" +#include "mem_user.h" +#include "signal_user.h" +#include "user.h" +#include "init.h" +#include "mode.h" +#include "choose-mode.h" +#include "uml-config.h" + +/* Set in set_stklim, which is called from main and __wrap_malloc. + * __wrap_malloc only calls it if main hasn't started. + */ +unsigned long stacksizelim; + +/* Set in main */ +char *linux_prog; + +#define PGD_BOUND (4 * 1024 * 1024) +#define STACKSIZE (8 * 1024 * 1024) +#define THREAD_NAME_LEN (256) + +static void set_stklim(void) +{ + struct rlimit lim; + + if(getrlimit(RLIMIT_STACK, &lim) < 0){ + perror("getrlimit"); + exit(1); + } + if((lim.rlim_cur == RLIM_INFINITY) || (lim.rlim_cur > STACKSIZE)){ + lim.rlim_cur = STACKSIZE; + if(setrlimit(RLIMIT_STACK, &lim) < 0){ + perror("setrlimit"); + exit(1); + } + } + stacksizelim = (lim.rlim_cur + PGD_BOUND - 1) & ~(PGD_BOUND - 1); +} + +static __init void do_uml_initcalls(void) +{ + initcall_t *call; + + call = &__uml_initcall_start; + while (call < &__uml_initcall_end){; + (*call)(); + call++; + } +} + +static void last_ditch_exit(int sig) +{ + CHOOSE_MODE(kmalloc_ok = 0, (void) 0); + signal(SIGINT, SIG_DFL); + signal(SIGTERM, SIG_DFL); + signal(SIGHUP, SIG_DFL); + uml_cleanup(); + exit(1); +} + +extern int uml_exitcode; + +int main(int argc, char **argv, char **envp) +{ + char **new_argv; + sigset_t mask; + int ret, i; + + /* Enable all signals except SIGIO - in some environments, we can + * enter with some signals blocked + */ + + sigemptyset(&mask); + sigaddset(&mask, SIGIO); + if(sigprocmask(SIG_SETMASK, &mask, NULL) < 0){ + perror("sigprocmask"); + exit(1); + } + +#ifdef UML_CONFIG_MODE_TT + /* Allocate memory for thread command lines */ + if(argc < 2 || strlen(argv[1]) < THREAD_NAME_LEN - 1){ + + char padding[THREAD_NAME_LEN] = { + [ 0 ... THREAD_NAME_LEN - 2] = ' ', '\0' + }; + + new_argv = malloc((argc + 2) * sizeof(char*)); + if(!new_argv) { + perror("Allocating extended argv"); + exit(1); + } + + new_argv[0] = argv[0]; + new_argv[1] = padding; + + for(i = 2; i <= argc; i++) + new_argv[i] = argv[i - 1]; + new_argv[argc + 1] = NULL; + + execvp(new_argv[0], new_argv); + perror("execing with extended args"); + exit(1); + } +#endif + + linux_prog = argv[0]; + + set_stklim(); + + new_argv = malloc((argc + 1) * sizeof(char *)); + if(new_argv == NULL){ + perror("Mallocing argv"); + exit(1); + } + for(i=0;i= uml_physmem) && (addr < high_physmem)){ + if(CAN_KMALLOC()) + kfree(ptr); + } + else if((addr >= start_vm) && (addr < end_vm)){ + if(CAN_KMALLOC()) + vfree(ptr); + } + else __real_free(ptr); +} + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * Emacs will notice this stuff at the end of the file and automatically + * adjust the settings for this buffer only. This must remain at the end + * of the file. + * --------------------------------------------------------------------------- + * Local variables: + * c-file-style: "linux" + * End: + */ diff --git a/arch/um/kernel/signal_user.c b/arch/um/kernel/signal_user.c index 52cfd60a1..74a69e491 100644 --- a/arch/um/kernel/signal_user.c +++ b/arch/um/kernel/signal_user.c @@ -80,6 +80,12 @@ void unblock_signals(void) change_signals(SIG_UNBLOCK); } +/* These are the asynchronous signals. SIGVTALRM and SIGARLM are handled + * together under SIGVTALRM_BIT. SIGPROF is excluded because we want to + * be able to profile all of UML, not just the non-critical sections. If + * profiling is not thread-safe, then that is not my problem. We can disable + * profiling when SMP is enabled in that case. + */ #define SIGIO_BIT 0 #define SIGVTALRM_BIT 1 @@ -114,6 +120,11 @@ int set_signals(int enable) sigaddset(&mask, SIGVTALRM); sigaddset(&mask, SIGALRM); } + + /* This is safe - sigprocmask is guaranteed to copy locally the + * value of new_set, do his work and then, at the end, write to + * old_set. + */ if(sigprocmask(SIG_UNBLOCK, &mask, &mask) < 0) panic("Failed to enable signals"); ret = enable_mask(&mask); diff --git a/arch/um/kernel/skas/tlb.c b/arch/um/kernel/skas/tlb.c index ed3d6bcd5..02e3e06bb 100644 --- a/arch/um/kernel/skas/tlb.c +++ b/arch/um/kernel/skas/tlb.c @@ -77,7 +77,7 @@ void flush_tlb_kernel_range_skas(unsigned long start, unsigned long end) int updated = 0, err; mm = &init_mm; - for(addr = start_vm; addr < end_vm;){ + for(addr = start; addr < end;){ pgd = pgd_offset(mm, addr); pmd = pmd_offset(pgd, addr); if(pmd_present(*pmd)){ diff --git a/arch/um/kernel/uml.lds.S b/arch/um/kernel/uml.lds.S new file mode 100644 index 000000000..89ed32983 --- /dev/null +++ b/arch/um/kernel/uml.lds.S @@ -0,0 +1,96 @@ +#include + +OUTPUT_FORMAT(ELF_FORMAT) +OUTPUT_ARCH(ELF_ARCH) +ENTRY(_start) +jiffies = jiffies_64; + +SECTIONS +{ + . = START + SIZEOF_HEADERS; + + __binary_start = .; +#ifdef MODE_TT + .thread_private : { + __start_thread_private = .; + errno = .; + . += 4; + arch/um/kernel/tt/unmap_fin.o (.data) + __end_thread_private = .; + } + . = ALIGN(4096); + .remap : { arch/um/kernel/tt/unmap_fin.o (.text) } +#endif + + . = ALIGN(4096); /* Init code and data */ + _stext = .; + __init_begin = .; + .init.text : { + _sinittext = .; + *(.init.text) + _einittext = .; + } + . = ALIGN(4096); + .text : + { + *(.text) + SCHED_TEXT + /* .gnu.warning sections are handled specially by elf32.em. */ + *(.gnu.warning) + *(.gnu.linkonce.t*) + } + + #include "asm/common.lds.S" + + init.data : { *(init.data) } + .data : + { + . = ALIGN(KERNEL_STACK_SIZE); /* init_task */ + *(.data.init_task) + *(.data) + *(.gnu.linkonce.d*) + CONSTRUCTORS + } + .data1 : { *(.data1) } + .ctors : + { + *(.ctors) + } + .dtors : + { + *(.dtors) + } + + .got : { *(.got.plt) *(.got) } + .dynamic : { *(.dynamic) } + /* We want the small data sections together, so single-instruction offsets + can access them all, and initialized data all before uninitialized, so + we can shorten the on-disk segment size. */ + .sdata : { *(.sdata) } + _edata = .; + PROVIDE (edata = .); + . = ALIGN(0x1000); + .sbss : + { + __bss_start = .; + PROVIDE(_bss_start = .); + *(.sbss) + *(.scommon) + } + .bss : + { + *(.dynbss) + *(.bss) + *(COMMON) + } + _end = . ; + PROVIDE (end = .); + /* Stabs debugging sections. */ + .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) } +} diff --git a/arch/um/sys-i386/syscalls.c b/arch/um/sys-i386/syscalls.c index 751549bd7..6ad31749b 100644 --- a/arch/um/sys-i386/syscalls.c +++ b/arch/um/sys-i386/syscalls.c @@ -3,6 +3,7 @@ * Licensed under the GPL */ +#include "linux/sched.h" #include "asm/mman.h" #include "asm/uaccess.h" #include "asm/unistd.h" @@ -56,6 +57,27 @@ int old_select(struct sel_arg_struct *arg) return sys_select(a.n, a.inp, a.outp, a.exp, a.tvp); } +/* The i386 version skips reading from %esi, the fourth argument. So we must do + * this, too. + */ +int sys_clone(unsigned long clone_flags, unsigned long newsp, int *parent_tid, + int unused, int *child_tid) +{ + long ret; + + /* XXX: normal arch do here this pass, and also pass the regs to + * do_fork, instead of NULL. Currently the arch-independent code + * ignores these values, while the UML code (actually it's + * copy_thread) does the right thing. But this should change, + probably. */ + /*if (!newsp) + newsp = UPT_SP(current->thread.regs);*/ + current->thread.forking = 1; + ret = do_fork(clone_flags, newsp, NULL, 0, parent_tid, child_tid); + current->thread.forking = 0; + return(ret); +} + /* * Overrides for Emacs so that we follow Linus's tabbing style. * Emacs will notice this stuff at the end of the file and automatically diff --git a/arch/v850/Kconfig.debug b/arch/v850/Kconfig.debug new file mode 100644 index 000000000..4acfb9cca --- /dev/null +++ b/arch/v850/Kconfig.debug @@ -0,0 +1,10 @@ +menu "Kernel hacking" + +source "lib/Kconfig.debug" + +config NO_KERNEL_MSG + bool "Suppress Kernel BUG Messages" + help + Do not output any debug BUG messages within the kernel. + +endmenu diff --git a/arch/v850/kernel/Makefile b/arch/v850/kernel/Makefile index 117bcaaa0..3930482bd 100644 --- a/arch/v850/kernel/Makefile +++ b/arch/v850/kernel/Makefile @@ -9,7 +9,7 @@ # for more details. # -extra-y := head.o init_task.o vmlinux.lds.s +extra-y := head.o init_task.o vmlinux.lds obj-y += intv.o entry.o process.o syscalls.o time.o semaphore.o setup.o \ signal.o irq.o mach.o ptrace.o bug.o diff --git a/arch/v850/kernel/asm-consts.c b/arch/v850/kernel/asm-consts.c index 666930d9c..24f291369 100644 --- a/arch/v850/kernel/asm-consts.c +++ b/arch/v850/kernel/asm-consts.c @@ -12,8 +12,8 @@ #include #include #include +#include #include -#include #include #define DEFINE(sym, val) \ diff --git a/arch/v850/kernel/rte_mb_a_pci.c b/arch/v850/kernel/rte_mb_a_pci.c index da39beb8e..551c41ba0 100644 --- a/arch/v850/kernel/rte_mb_a_pci.c +++ b/arch/v850/kernel/rte_mb_a_pci.c @@ -322,8 +322,6 @@ pcibios_resource_to_bus(struct pci_dev *dev, struct pci_bus_region *region, /* Stubs for things we don't use. */ -struct pci_fixup pcibios_fixups[] = { { 0 } }; - /* Called after each bus is probed, but before its children are examined. */ void pcibios_fixup_bus(struct pci_bus *b) { diff --git a/arch/v850/kernel/signal.c b/arch/v850/kernel/signal.c index caa9e3a04..60098e4f0 100644 --- a/arch/v850/kernel/signal.c +++ b/arch/v850/kernel/signal.c @@ -344,9 +344,7 @@ static void setup_frame(int sig, struct k_sigaction *ka, return; give_sigsegv: - if (sig == SIGSEGV) - ka->sa.sa_handler = SIG_DFL; - force_sig(SIGSEGV, current); + force_sigsegv(sig, current); } static void setup_rt_frame(int sig, struct k_sigaction *ka, siginfo_t *info, @@ -421,9 +419,7 @@ static void setup_rt_frame(int sig, struct k_sigaction *ka, siginfo_t *info, return; give_sigsegv: - if (sig == SIGSEGV) - ka->sa.sa_handler = SIG_DFL; - force_sig(SIGSEGV, current); + force_sigsegv(sig, current); } /* diff --git a/arch/v850/kernel/v850_ksyms.c b/arch/v850/kernel/v850_ksyms.c index 2336da438..0ca64900d 100644 --- a/arch/v850/kernel/v850_ksyms.c +++ b/arch/v850/kernel/v850_ksyms.c @@ -14,7 +14,6 @@ #include #include #include -#include #include @@ -37,29 +36,29 @@ EXPORT_SYMBOL (ip_compute_csum); EXPORT_SYMBOL (ip_fast_csum); /* string / mem functions */ -EXPORT_SYMBOL_NOVERS (strcpy); -EXPORT_SYMBOL_NOVERS (strncpy); -EXPORT_SYMBOL_NOVERS (strcat); -EXPORT_SYMBOL_NOVERS (strncat); -EXPORT_SYMBOL_NOVERS (strcmp); -EXPORT_SYMBOL_NOVERS (strncmp); -EXPORT_SYMBOL_NOVERS (strchr); -EXPORT_SYMBOL_NOVERS (strlen); -EXPORT_SYMBOL_NOVERS (strnlen); -EXPORT_SYMBOL_NOVERS (strpbrk); -EXPORT_SYMBOL_NOVERS (strrchr); -EXPORT_SYMBOL_NOVERS (strstr); -EXPORT_SYMBOL_NOVERS (memset); -EXPORT_SYMBOL_NOVERS (memcpy); -EXPORT_SYMBOL_NOVERS (memmove); -EXPORT_SYMBOL_NOVERS (memcmp); -EXPORT_SYMBOL_NOVERS (memscan); +EXPORT_SYMBOL (strcpy); +EXPORT_SYMBOL (strncpy); +EXPORT_SYMBOL (strcat); +EXPORT_SYMBOL (strncat); +EXPORT_SYMBOL (strcmp); +EXPORT_SYMBOL (strncmp); +EXPORT_SYMBOL (strchr); +EXPORT_SYMBOL (strlen); +EXPORT_SYMBOL (strnlen); +EXPORT_SYMBOL (strpbrk); +EXPORT_SYMBOL (strrchr); +EXPORT_SYMBOL (strstr); +EXPORT_SYMBOL (memset); +EXPORT_SYMBOL (memcpy); +EXPORT_SYMBOL (memmove); +EXPORT_SYMBOL (memcmp); +EXPORT_SYMBOL (memscan); /* semaphores */ -EXPORT_SYMBOL_NOVERS (__down); -EXPORT_SYMBOL_NOVERS (__down_interruptible); -EXPORT_SYMBOL_NOVERS (__down_trylock); -EXPORT_SYMBOL_NOVERS (__up); +EXPORT_SYMBOL (__down); +EXPORT_SYMBOL (__down_interruptible); +EXPORT_SYMBOL (__down_trylock); +EXPORT_SYMBOL (__up); /* * libgcc functions - functions that are used internally by the @@ -72,8 +71,8 @@ extern void __lshrdi3 (void); extern void __muldi3 (void); extern void __negdi2 (void); -EXPORT_SYMBOL_NOVERS (__ashldi3); -EXPORT_SYMBOL_NOVERS (__ashrdi3); -EXPORT_SYMBOL_NOVERS (__lshrdi3); -EXPORT_SYMBOL_NOVERS (__muldi3); -EXPORT_SYMBOL_NOVERS (__negdi2); +EXPORT_SYMBOL (__ashldi3); +EXPORT_SYMBOL (__ashrdi3); +EXPORT_SYMBOL (__lshrdi3); +EXPORT_SYMBOL (__muldi3); +EXPORT_SYMBOL (__negdi2); diff --git a/arch/x86_64/Kconfig.debug b/arch/x86_64/Kconfig.debug new file mode 100644 index 000000000..2ba7f5a05 --- /dev/null +++ b/arch/x86_64/Kconfig.debug @@ -0,0 +1,59 @@ +menu "Kernel hacking" + +source "lib/Kconfig.debug" + +# !SMP for now because the context switch early causes GPF in segment reloading +# and the GS base checking does the wrong thing then, causing a hang. +config CHECKING + bool "Additional run-time checks" + depends on DEBUG_KERNEL && !SMP + help + Enables some internal consistency checks for kernel debugging. + You should normally say N. + +config INIT_DEBUG + bool "Debug __init statements" + depends on DEBUG_KERNEL + help + Fill __init and __initdata at the end of boot. This helps debugging + illegal uses of __init and __initdata after initialization. + +config SCHEDSTATS + bool "Collect scheduler statistics" + depends on DEBUG_KERNEL && PROC_FS + help + If you say Y here, additional code will be inserted into the + scheduler and related routines to collect statistics about + scheduler behavior and provide them in /proc/schedstat. These + stats may be useful for both tuning and debugging the scheduler + If you aren't debugging the scheduler or trying to tune a specific + application, you can say N to avoid the very slight overhead + this adds. + +config IOMMU_DEBUG + depends on GART_IOMMU && DEBUG_KERNEL + bool "Enable IOMMU debugging" + help + Force the IOMMU to on even when you have less than 4GB of + memory and add debugging code. On overflow always panic. And + allow to enable IOMMU leak tracing. Can be disabled at boot + time with iommu=noforce. This will also enable scatter gather + list merging. Currently not recommended for production + code. When you use it make sure you have a big enough + IOMMU/AGP aperture. Most of the options enabled by this can + be set more finegrained using the iommu= command line + options. See Documentation/x86_64/boot-options.txt for more + details. + +config IOMMU_LEAK + bool "IOMMU leak tracing" + depends on DEBUG_KERNEL + depends on IOMMU_DEBUG + help + Add a simple leak tracer to the IOMMU code. This is useful when you + are debugging a buggy device driver that leaks IOMMU mappings. + +#config X86_REMOTE_DEBUG +# bool "kgdb debugging stub" + +endmenu diff --git a/arch/x86_64/boot/Makefile b/arch/x86_64/boot/Makefile index 1c2d3cbca..99b790439 100644 --- a/arch/x86_64/boot/Makefile +++ b/arch/x86_64/boot/Makefile @@ -30,7 +30,7 @@ targets := vmlinux.bin bootsect bootsect.o \ EXTRA_CFLAGS := -m32 -host-progs := tools/build +hostprogs-y := tools/build subdir- := compressed/ #Let make clean descend in compressed/ # --------------------------------------------------------------------------- diff --git a/arch/x86_64/boot/video.S b/arch/x86_64/boot/video.S index 5d4cdc1ab..fd0cc734a 100644 --- a/arch/x86_64/boot/video.S +++ b/arch/x86_64/boot/video.S @@ -66,6 +66,7 @@ #define VIDEO_80x30 0x0f05 #define VIDEO_80x34 0x0f06 #define VIDEO_80x60 0x0f07 +#define VIDEO_GFX_HACK 0x0f08 #define VIDEO_LAST_SPECIAL 0x0f09 /* Video modes given by resolution */ @@ -97,7 +98,6 @@ #define PARAM_LFB_PAGES 0x32 #define PARAM_VESA_ATTRIB 0x34 - /* Define DO_STORE according to CONFIG_VIDEO_RETAIN */ #ifdef CONFIG_VIDEO_RETAIN #define DO_STORE call store_screen @@ -133,6 +133,7 @@ vid1: #ifdef CONFIG_VIDEO_RETAIN call restore_screen # Restore screen contents #endif /* CONFIG_VIDEO_RETAIN */ + call store_edid #endif /* CONFIG_VIDEO_SELECT */ call mode_params # Store mode parameters popw %ds # Restore original DS @@ -231,6 +232,41 @@ mopar_gr: xorl %eax, %eax movw 18(%di), %ax movl %eax, %fs:(PARAM_LFB_SIZE) + +# switching the DAC to 8-bit is for <= 8 bpp only + movw %fs:(PARAM_LFB_DEPTH), %ax + cmpw $8, %ax + jg dac_done + +# get DAC switching capability + xorl %eax, %eax + movb 10(%di), %al + testb $1, %al + jz dac_set + +# attempt to switch DAC to 8-bit + movw $0x4f08, %ax + movw $0x0800, %bx + int $0x10 + cmpw $0x004f, %ax + jne dac_set + movb %bh, dac_size # store actual DAC size + +dac_set: +# set color size to DAC size + movb dac_size, %al + movb %al, %fs:(PARAM_LFB_COLORS+0) + movb %al, %fs:(PARAM_LFB_COLORS+2) + movb %al, %fs:(PARAM_LFB_COLORS+4) + movb %al, %fs:(PARAM_LFB_COLORS+6) + +# set color offsets to 0 + movb $0, %fs:(PARAM_LFB_COLORS+1) + movb $0, %fs:(PARAM_LFB_COLORS+3) + movb $0, %fs:(PARAM_LFB_COLORS+5) + movb $0, %fs:(PARAM_LFB_COLORS+7) + +dac_done: # get protected mode interface informations movw $0x4f0a, %ax xorw %bx, %bx @@ -440,7 +476,7 @@ setalias: # Setting of user mode (AX=mode ID) => CF=success mode_set: - movw %ax, %fs:(0x01fa) + movw %ax, %fs:(0x01fa) # Store mode for use in acpi_wakeup.S movw %ax, %bx cmpb $0xff, %ah jz setalias @@ -744,7 +780,15 @@ set_80x60: movw $0x503c, force_size jmp setvde +# Special hack for ThinkPad graphics set_gfx: +#ifdef CONFIG_VIDEO_GFX_HACK + movw $VIDEO_GFX_BIOS_AX, %ax + movw $VIDEO_GFX_BIOS_BX, %bx + int $0x10 + movw $VIDEO_GFX_DUMMY_RESOLUTION, force_size + stc +#endif ret #ifdef CONFIG_VIDEO_RETAIN @@ -969,6 +1013,10 @@ vga_modes: .word 0x5022 # 80x34 .word VIDEO_80x60 .word 0x503c # 80x60 +#ifdef CONFIG_VIDEO_GFX_HACK + .word VIDEO_GFX_HACK + .word VIDEO_GFX_DUMMY_RESOLUTION +#endif vga_modes_end: # Detect VESA modes. @@ -1875,6 +1923,39 @@ skip10: movb %ah, %al popw %ax ret +store_edid: + pushw %es # just save all registers + pushw %ax + pushw %bx + pushw %cx + pushw %dx + pushw %di + + pushw %fs + popw %es + + movl $0x13131313, %eax # memset block with 0x13 + movw $32, %cx + movw $0x440, %di + cld + rep + stosl + + movw $0x4f15, %ax # do VBE/DDC + movw $0x01, %bx + movw $0x00, %cx + movw $0x01, %dx + movw $0x440, %di + int $0x10 + + popw %di # restore all registers + popw %dx + popw %cx + popw %bx + popw %ax + popw %es + ret + # VIDEO_SELECT-only variables mt_end: .word 0 # End of video mode table if built edit_buf: .space 6 # Line editor buffer @@ -1883,6 +1964,7 @@ scanning: .byte 0 # Performing mode scan do_restore: .byte 0 # Screen contents altered during mode change svga_prefix: .byte VIDEO_FIRST_BIOS>>8 # Default prefix for BIOS modes graphic_mode: .byte 0 # Graphic mode with a linear frame buffer +dac_size: .byte 6 # DAC bit depth # Status messages keymsg: .ascii "Press to see video modes available, " diff --git a/arch/x86_64/kernel/aperture.c b/arch/x86_64/kernel/aperture.c index 164b6d078..1d8b3912c 100644 --- a/arch/x86_64/kernel/aperture.c +++ b/arch/x86_64/kernel/aperture.c @@ -31,6 +31,8 @@ int iommu_aperture_allowed __initdata = 0; int fallback_aper_order __initdata = 1; /* 64MB */ int fallback_aper_force __initdata = 0; +int fix_aperture __initdata = 1; + /* This code runs before the PCI subsystem is initialized, so just access the northbridge directly. */ @@ -202,7 +204,7 @@ void __init iommu_hole_init(void) u64 aper_base; int valid_agp = 0; - if (iommu_aperture_disabled) + if (iommu_aperture_disabled || !fix_aperture) return; printk("Checking aperture...\n"); @@ -241,20 +243,15 @@ void __init iommu_hole_init(void) /* Got the aperture from the AGP bridge */ } else if ((!no_iommu && end_pfn >= 0xffffffff>>PAGE_SHIFT) || force_iommu || - valid_agp || + valid_agp || fallback_aper_force) { - /* When there is a AGP bridge in the system assume the - user wants to use the AGP driver too and needs an - aperture. However this case (AGP but no good - aperture) should only happen with a more broken than - usual BIOS, because it would even break Windows. */ - - printk("Your BIOS doesn't leave a aperture memory hole\n"); - printk("Please enable the IOMMU option in the BIOS setup\n"); - printk("This costs you %d MB of RAM\n", 32 << fallback_aper_order); - + printk("Your BIOS doesn't leave a aperture memory hole\n"); + printk("Please enable the IOMMU option in the BIOS setup\n"); + printk("This costs you %d MB of RAM\n", + 32 << fallback_aper_order); + aper_order = fallback_aper_order; - aper_alloc = allocate_aperture(); + aper_alloc = allocate_aperture(); if (!aper_alloc) { /* Could disable AGP and IOMMU here, but it's probably not worth it. But the later users cannot deal with diff --git a/arch/x86_64/kernel/apic.c b/arch/x86_64/kernel/apic.c index 6fb84e2c7..ddd8aac2a 100644 --- a/arch/x86_64/kernel/apic.c +++ b/arch/x86_64/kernel/apic.c @@ -33,6 +33,8 @@ #include #include +int apic_verbosity; + int disable_apic_timer __initdata; /* Using APIC to generate smp_local_timer_interrupt? */ @@ -123,7 +125,7 @@ void __init connect_bsp_APIC(void) * PIC mode, enable APIC mode in the IMCR, i.e. * connect BSP's local APIC to INT and NMI lines. */ - printk(KERN_INFO "leaving PIC mode, enabling APIC mode.\n"); + apic_printk(APIC_VERBOSE, "leaving PIC mode, enabling APIC mode.\n"); outb(0x70, 0x22); outb(0x01, 0x23); } @@ -138,7 +140,7 @@ void disconnect_bsp_APIC(void) * interrupts, including IPIs, won't work beyond * this point! The only exception are INIT IPIs. */ - printk(KERN_INFO "disabling APIC mode, entering PIC mode.\n"); + apic_printk(APIC_QUIET, "disabling APIC mode, entering PIC mode.\n"); outb(0x70, 0x22); outb(0x00, 0x23); } @@ -172,10 +174,10 @@ int __init verify_local_APIC(void) * The version register is read-only in a real APIC. */ reg0 = apic_read(APIC_LVR); - Dprintk("Getting VERSION: %x\n", reg0); + apic_printk(APIC_DEBUG, "Getting VERSION: %x\n", reg0); apic_write(APIC_LVR, reg0 ^ APIC_LVR_MASK); reg1 = apic_read(APIC_LVR); - Dprintk("Getting VERSION: %x\n", reg1); + apic_printk(APIC_DEBUG, "Getting VERSION: %x\n", reg1); /* * The two version reads above should print the same @@ -199,10 +201,10 @@ int __init verify_local_APIC(void) * The ID register is read/write in a real APIC. */ reg0 = apic_read(APIC_ID); - Dprintk("Getting ID: %x\n", reg0); + apic_printk(APIC_DEBUG, "Getting ID: %x\n", reg0); apic_write(APIC_ID, reg0 ^ APIC_ID_MASK); reg1 = apic_read(APIC_ID); - Dprintk("Getting ID: %x\n", reg1); + apic_printk(APIC_DEBUG, "Getting ID: %x\n", reg1); apic_write(APIC_ID, reg0); if (reg1 != (reg0 ^ APIC_ID_MASK)) return 0; @@ -213,9 +215,9 @@ int __init verify_local_APIC(void) * compatibility mode, but most boxes are anymore. */ reg0 = apic_read(APIC_LVT0); - Dprintk("Getting LVT0: %x\n", reg0); + apic_printk(APIC_DEBUG,"Getting LVT0: %x\n", reg0); reg1 = apic_read(APIC_LVT1); - Dprintk("Getting LVT1: %x\n", reg1); + apic_printk(APIC_DEBUG, "Getting LVT1: %x\n", reg1); return 1; } @@ -227,7 +229,7 @@ void __init sync_Arb_IDs(void) */ apic_wait_icr_idle(); - Dprintk("Synchronizing Arb IDs.\n"); + apic_printk(APIC_DEBUG, "Synchronizing Arb IDs.\n"); apic_write_around(APIC_ICR, APIC_DEST_ALLINC | APIC_INT_LEVELTRIG | APIC_DM_INIT); } @@ -388,10 +390,10 @@ void __init setup_local_APIC (void) value = apic_read(APIC_LVT0) & APIC_LVT_MASKED; if (!smp_processor_id() && (pic_mode || !value)) { value = APIC_DM_EXTINT; - Dprintk(KERN_INFO "enabled ExtINT on CPU#%d\n", smp_processor_id()); + apic_printk(APIC_VERBOSE, "enabled ExtINT on CPU#%d\n", smp_processor_id()); } else { value = APIC_DM_EXTINT | APIC_LVT_MASKED; - Dprintk(KERN_INFO "masked ExtINT on CPU#%d\n", smp_processor_id()); + apic_printk(APIC_VERBOSE, "masked ExtINT on CPU#%d\n", smp_processor_id()); } apic_write_around(APIC_LVT0, value); @@ -407,12 +409,11 @@ void __init setup_local_APIC (void) apic_write_around(APIC_LVT1, value); if (APIC_INTEGRATED(ver) && !esr_disable) { /* !82489DX */ + unsigned oldvalue; maxlvt = get_maxlvt(); if (maxlvt > 3) /* Due to the Pentium erratum 3AP. */ apic_write(APIC_ESR, 0); - value = apic_read(APIC_ESR); - Dprintk("ESR value before enabling vector: %08x\n", value); - + oldvalue = apic_read(APIC_ESR); value = ERROR_APIC_VECTOR; // enables sending errors apic_write_around(APIC_LVTERR, value); /* @@ -421,7 +422,10 @@ void __init setup_local_APIC (void) if (maxlvt > 3) apic_write(APIC_ESR, 0); value = apic_read(APIC_ESR); - Dprintk("ESR value after enabling vector: %08x\n", value); + if (value != oldvalue) + apic_printk(APIC_VERBOSE, + "ESR value after enabling vector: %08x, after %08x\n", + oldvalue, value); } else { if (esr_disable) /* @@ -430,9 +434,9 @@ void __init setup_local_APIC (void) * ESR disabled - we can't do anything useful with the * errors anyway - mbligh */ - printk("Leaving ESR disabled.\n"); + apic_printk(APIC_DEBUG, "Leaving ESR disabled.\n"); else - printk("No ESR for 82489DX.\n"); + apic_printk(APIC_DEBUG, "No ESR for 82489DX.\n"); } nmi_watchdog_default(); @@ -564,6 +568,21 @@ static void apic_pm_activate(void) { } #endif /* CONFIG_PM */ +static int __init apic_set_verbosity(char *str) +{ + if (strcmp("debug", str) == 0) + apic_verbosity = APIC_DEBUG; + else if (strcmp("verbose", str) == 0) + apic_verbosity = APIC_VERBOSE; + else + printk(KERN_WARNING "APIC Verbosity level %s not recognised" + " use apic=verbose or apic=debug", str); + + return 0; +} + +__setup("apic=", apic_set_verbosity); + /* * Detect and enable local APICs on non-SMP boards. * Original code written by Keir Fraser. @@ -599,7 +618,7 @@ void __init init_apic_mappings(void) apic_phys = mp_lapic_addr; set_fixmap_nocache(FIX_APIC_BASE, apic_phys); - Dprintk("mapped APIC to %16lx (%16lx)\n", APIC_BASE, apic_phys); + apic_printk(APIC_VERBOSE,"mapped APIC to %16lx (%16lx)\n", APIC_BASE, apic_phys); /* * Fetch the APIC ID of the BSP in case we have a @@ -621,7 +640,7 @@ void __init init_apic_mappings(void) ioapic_phys = __pa(ioapic_phys); } set_fixmap_nocache(idx, ioapic_phys); - Dprintk("mapped IOAPIC to %016lx (%016lx)\n", + apic_printk(APIC_VERBOSE,"mapped IOAPIC to %016lx (%016lx)\n", __fix_to_virt(idx), ioapic_phys); idx++; } @@ -836,8 +855,7 @@ void smp_local_timer_interrupt(struct pt_regs *regs) { int cpu = smp_processor_id(); - x86_do_profile(regs); - + profile_tick(CPU_PROFILING, regs); if (--per_cpu(prof_counter, cpu) <= 0) { /* * The multiplier may have changed since the last time we got @@ -962,7 +980,7 @@ asmlinkage void smp_error_interrupt(void) 6: Received illegal vector 7: Illegal register address */ - printk (KERN_INFO "APIC error on CPU%d: %02x(%02x)\n", + printk (KERN_DEBUG "APIC error on CPU%d: %02x(%02x)\n", smp_processor_id(), v , v1); irq_exit(); } diff --git a/arch/x86_64/kernel/asm-offsets.c b/arch/x86_64/kernel/asm-offsets.c index 757f55078..37d12fa5e 100644 --- a/arch/x86_64/kernel/asm-offsets.c +++ b/arch/x86_64/kernel/asm-offsets.c @@ -7,8 +7,8 @@ #include #include #include +#include #include -#include #include #include #include diff --git a/arch/x86_64/kernel/cpufreq/Kconfig b/arch/x86_64/kernel/cpufreq/Kconfig index 1b3aefc8d..dcb518eb2 100644 --- a/arch/x86_64/kernel/cpufreq/Kconfig +++ b/arch/x86_64/kernel/cpufreq/Kconfig @@ -41,4 +41,58 @@ config X86_POWERNOW_K8 If in doubt, say N. +config X86_POWERNOW_K8_ACPI + bool + depends on ((X86_POWERNOW_K8 = "m" && ACPI_PROCESSOR) || (X86_POWERNOW_K8 = "y" && ACPI_PROCESSOR = "y")) + default y + +config X86_SPEEDSTEP_CENTRINO + tristate "Intel Enhanced SpeedStep" + depends on CPU_FREQ_TABLE + help + This adds the CPUFreq driver for Enhanced SpeedStep enabled + mobile CPUs. This means Intel Pentium M (Centrino) CPUs + or 64bit enabled Intel Xeons. + + For details, take a look at . + + If in doubt, say N. + +config X86_SPEEDSTEP_CENTRINO_TABLE + bool + depends on X86_SPEEDSTEP_CENTRINO + default y + +config X86_SPEEDSTEP_CENTRINO_ACPI + bool "Use ACPI tables to decode valid frequency/voltage pairs (EXPERIMENTAL)" + depends on EXPERIMENTAL + depends on ((X86_SPEEDSTEP_CENTRINO = "m" && ACPI_PROCESSOR) || (X86_SPEEDSTEP_CENTRINO = "y" && ACPI_PROCESSOR = "y")) + help + Use primarily the information provided in the BIOS ACPI tables + to determine valid CPU frequency and voltage pairings. + + If in doubt, say Y. + +config X86_ACPI_CPUFREQ + tristate "ACPI Processor P-States driver" + depends on CPU_FREQ_TABLE && ACPI_PROCESSOR + help + This driver adds a CPUFreq driver which utilizes the ACPI + Processor Performance States. + + For details, take a look at . + + If in doubt, say N. + +config X86_ACPI_CPUFREQ_PROC_INTF + bool "/proc/acpi/processor/../performance interface (deprecated)" + depends on X86_ACPI_CPUFREQ && PROC_FS + help + This enables the deprecated /proc/acpi/processor/../performance + interface. While it is helpful for debugging, the generic, + cross-architecture cpufreq interfaces should be used. + + If in doubt, say N. + endmenu + diff --git a/arch/x86_64/kernel/cpufreq/Makefile b/arch/x86_64/kernel/cpufreq/Makefile index f02ed52be..d0f0b2cf3 100644 --- a/arch/x86_64/kernel/cpufreq/Makefile +++ b/arch/x86_64/kernel/cpufreq/Makefile @@ -2,6 +2,12 @@ # Reuse the i386 cpufreq drivers # +SRCDIR := ../../../i386/kernel/cpu/cpufreq + obj-$(CONFIG_X86_POWERNOW_K8) += powernow-k8.o +obj-$(CONFIG_X86_SPEEDSTEP_CENTRINO) += speedstep-centrino.o +obj-$(CONFIG_X86_ACPI_CPUFREQ) += acpi.o -powernow-k8-objs := ../../../i386/kernel/cpu/cpufreq/powernow-k8.o +powernow-k8-objs := ${SRCDIR}/powernow-k8.o +speedstep-centrino-objs := ${SRCDIR}/speedstep-centrino.o +acpi-objs := ${SRCDIR}/acpi.o diff --git a/arch/x86_64/kernel/early_printk.c b/arch/x86_64/kernel/early_printk.c index 98041f50e..600ec815a 100644 --- a/arch/x86_64/kernel/early_printk.c +++ b/arch/x86_64/kernel/early_printk.c @@ -8,7 +8,7 @@ /* Simple VGA output */ #ifdef __i386__ -#define VGABASE __pa(__PAGE_OFFSET + 0xb8000UL) +#define VGABASE (__ISA_IO_base + 0xb8000) #else #define VGABASE 0xffffffff800b8000UL #endif @@ -99,18 +99,17 @@ static void early_serial_write(struct console *con, const char *s, unsigned n) #define DEFAULT_BAUD 9600 -static __init void early_serial_init(char *opt) +static __init void early_serial_init(char *s) { unsigned char c; unsigned divisor; unsigned baud = DEFAULT_BAUD; - char *s, *e; + char *e; - if (*opt == ',') - ++opt; + if (*s == ',') + ++s; - s = strsep(&opt, ","); - if (s != NULL) { + if (*s) { unsigned port; if (!strncmp(s,"0x",2)) { early_serial_base = simple_strtoul(s, &e, 16); @@ -124,6 +123,9 @@ static __init void early_serial_init(char *opt) port = 0; early_serial_base = bases[port]; } + s += strcspn(s, ","); + if (*s == ',') + s++; } outb(0x3, early_serial_base + LCR); /* 8n1 */ @@ -131,8 +133,7 @@ static __init void early_serial_init(char *opt) outb(0, early_serial_base + FCR); /* no fifo */ outb(0x3, early_serial_base + MCR); /* DTR + RTS */ - s = strsep(&opt, ","); - if (s != NULL) { + if (*s) { baud = simple_strtoul(s, &e, 0); if (baud == 0 || s == e) baud = DEFAULT_BAUD; diff --git a/arch/x86_64/kernel/entry.S b/arch/x86_64/kernel/entry.S index 89f74f738..17d74eb66 100644 --- a/arch/x86_64/kernel/entry.S +++ b/arch/x86_64/kernel/entry.S @@ -297,7 +297,7 @@ int_very_careful: sti SAVE_REST /* Check for syscall exit trace */ - testl $(_TIF_SYSCALL_TRACE|_TIF_SYSCALL_AUDIT),%edx + testl $(_TIF_SYSCALL_TRACE|_TIF_SYSCALL_AUDIT|_TIF_SINGLESTEP),%edx jz int_signal pushq %rdi leaq 8(%rsp),%rdi # &ptregs -> arg1 @@ -305,6 +305,7 @@ int_very_careful: popq %rdi btr $TIF_SYSCALL_TRACE,%edi btr $TIF_SYSCALL_AUDIT,%edi + btr $TIF_SINGLESTEP,%edi jmp int_restore_rest int_signal: @@ -323,19 +324,20 @@ int_restore_rest: * Certain special system calls that need to save a complete full stack frame. */ - .macro PTREGSCALL label,func + .macro PTREGSCALL label,func,arg .globl \label \label: leaq \func(%rip),%rax + leaq -ARGOFFSET+8(%rsp),\arg /* 8 for return address */ jmp ptregscall_common .endm - PTREGSCALL stub_clone, sys_clone - PTREGSCALL stub_fork, sys_fork - PTREGSCALL stub_vfork, sys_vfork - PTREGSCALL stub_rt_sigsuspend, sys_rt_sigsuspend - PTREGSCALL stub_sigaltstack, sys_sigaltstack - PTREGSCALL stub_iopl, sys_iopl + PTREGSCALL stub_clone, sys_clone, %r8 + PTREGSCALL stub_fork, sys_fork, %rdi + PTREGSCALL stub_vfork, sys_vfork, %rdi + PTREGSCALL stub_rt_sigsuspend, sys_rt_sigsuspend, %rdx + PTREGSCALL stub_sigaltstack, sys_sigaltstack, %rdx + PTREGSCALL stub_iopl, sys_iopl, %rsi ENTRY(ptregscall_common) CFI_STARTPROC @@ -385,6 +387,7 @@ ENTRY(stub_rt_sigreturn) CFI_STARTPROC addq $8, %rsp SAVE_REST + movq %rsp,%rdi FIXUP_TOP_OF_STACK %r11 call sys_rt_sigreturn movq %rax,RAX(%rsp) # fixme, this could be done at the higher layer diff --git a/arch/x86_64/kernel/ioport.c b/arch/x86_64/kernel/ioport.c index 0a90fd08f..cde0e868e 100644 --- a/arch/x86_64/kernel/ioport.c +++ b/arch/x86_64/kernel/ioport.c @@ -28,12 +28,12 @@ static void set_bitmap(unsigned long *bitmap, unsigned int base, unsigned int ex clear_bit(i, bitmap); } - /* * this changes the io permissions bitmap in the current task. */ asmlinkage long sys_ioperm(unsigned long from, unsigned long num, int turn_on) { + unsigned int i, max_long, bytes, bytes_updated; struct thread_struct * t = ¤t->thread; struct tss_struct * tss; unsigned long *bitmap; @@ -59,16 +59,34 @@ asmlinkage long sys_ioperm(unsigned long from, unsigned long num, int turn_on) /* * do it in the per-thread copy and in the TSS ... + * + * Disable preemption via get_cpu() - we must not switch away + * because the ->io_bitmap_max value must match the bitmap + * contents: */ + tss = &per_cpu(init_tss, get_cpu()); + set_bitmap(t->io_bitmap_ptr, from, num, !turn_on); - tss = init_tss + get_cpu(); - if (tss->io_bitmap_base == IO_BITMAP_OFFSET) { /* already active? */ - set_bitmap(tss->io_bitmap, from, num, !turn_on); - } else { - memcpy(tss->io_bitmap, t->io_bitmap_ptr, IO_BITMAP_BYTES); - tss->io_bitmap_base = IO_BITMAP_OFFSET; /* Activate it in the TSS */ - } + + /* + * Search for a (possibly new) maximum. This is simple and stupid, + * to keep it obviously correct: + */ + max_long = 0; + for (i = 0; i < IO_BITMAP_LONGS; i++) + if (t->io_bitmap_ptr[i] != ~0UL) + max_long = i; + + bytes = (max_long + 1) * sizeof(long); + bytes_updated = max(bytes, t->io_bitmap_max); + + t->io_bitmap_max = bytes; + + /* Update the TSS: */ + memcpy(tss->io_bitmap, t->io_bitmap_ptr, bytes_updated); + put_cpu(); + return 0; } @@ -83,9 +101,9 @@ asmlinkage long sys_ioperm(unsigned long from, unsigned long num, int turn_on) * code. */ -asmlinkage long sys_iopl(unsigned int level, struct pt_regs regs) +asmlinkage long sys_iopl(unsigned int level, struct pt_regs *regs) { - unsigned int old = (regs.eflags >> 12) & 3; + unsigned int old = (regs->eflags >> 12) & 3; if (level > 3) return -EINVAL; @@ -94,6 +112,6 @@ asmlinkage long sys_iopl(unsigned int level, struct pt_regs regs) if (!capable(CAP_SYS_RAWIO)) return -EPERM; } - regs.eflags = (regs.eflags &~ 0x3000UL) | (level << 12); + regs->eflags = (regs->eflags &~ 0x3000UL) | (level << 12); return 0; } diff --git a/arch/x86_64/kernel/machine_kexec.c b/arch/x86_64/kernel/machine_kexec.c new file mode 100644 index 000000000..347387814 --- /dev/null +++ b/arch/x86_64/kernel/machine_kexec.c @@ -0,0 +1,246 @@ +/* + * machine_kexec.c - handle transition of Linux booting another kernel + * Copyright (C) 2002-2004 Eric Biederman + * + * This source code is licensed under the GNU General Public License, + * Version 2. See the file COPYING for more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define LEVEL0_SIZE (1UL << 12UL) +#define LEVEL1_SIZE (1UL << 21UL) +#define LEVEL2_SIZE (1UL << 30UL) +#define LEVEL3_SIZE (1UL << 39UL) +#define LEVEL4_SIZE (1UL << 48UL) + +#define L0_ATTR (_PAGE_PRESENT | _PAGE_RW | _PAGE_ACCESSED | _PAGE_DIRTY) +#define L1_ATTR (_PAGE_PRESENT | _PAGE_RW | _PAGE_ACCESSED | _PAGE_DIRTY | _PAGE_PSE) +#define L2_ATTR (_PAGE_PRESENT | _PAGE_RW | _PAGE_ACCESSED | _PAGE_DIRTY) +#define L3_ATTR (_PAGE_PRESENT | _PAGE_RW | _PAGE_ACCESSED | _PAGE_DIRTY) + +static void init_level2_page( + uint64_t *level2p, unsigned long addr) +{ + unsigned long end_addr; + addr &= PAGE_MASK; + end_addr = addr + LEVEL2_SIZE; + while(addr < end_addr) { + *(level2p++) = addr | L1_ATTR; + addr += LEVEL1_SIZE; + } +} + +static int init_level3_page(struct kimage *image, + uint64_t *level3p, unsigned long addr, unsigned long last_addr) +{ + unsigned long end_addr; + int result; + result = 0; + addr &= PAGE_MASK; + end_addr = addr + LEVEL3_SIZE; + while((addr < last_addr) && (addr < end_addr)) { + struct page *page; + uint64_t *level2p; + page = kimage_alloc_control_pages(image, 0); + if (!page) { + result = -ENOMEM; + goto out; + } + level2p = (uint64_t *)page_address(page); + init_level2_page(level2p, addr); + *(level3p++) = __pa(level2p) | L2_ATTR; + addr += LEVEL2_SIZE; + } + /* clear the unused entries */ + while(addr < end_addr) { + *(level3p++) = 0; + addr += LEVEL2_SIZE; + } +out: + return result; +} + + +static int init_level4_page(struct kimage *image, + uint64_t *level4p, unsigned long addr, unsigned long last_addr) +{ + unsigned long end_addr; + int result; + result = 0; + addr &= PAGE_MASK; + end_addr = addr + LEVEL4_SIZE; + while((addr < last_addr) && (addr < end_addr)) { + struct page *page; + uint64_t *level3p; + page = kimage_alloc_control_pages(image, 0); + if (!page) { + result = -ENOMEM; + goto out; + } + level3p = (uint64_t *)page_address(page); + result = init_level3_page(image, level3p, addr, last_addr); + if (result) { + goto out; + } + *(level4p++) = __pa(level3p) | L3_ATTR; + addr += LEVEL3_SIZE; + } + /* clear the unused entries */ + while(addr < end_addr) { + *(level4p++) = 0; + addr += LEVEL3_SIZE; + } + out: + return result; +} + + +static int init_pgtable(struct kimage *image, unsigned long start_pgtable) +{ + uint64_t *level4p; + level4p = (uint64_t *)__va(start_pgtable); + return init_level4_page(image, level4p, 0, end_pfn << PAGE_SHIFT); +} + +static void set_idt(void *newidt, __u16 limit) +{ + unsigned char curidt[10]; + + /* x86-64 supports unaliged loads & stores */ + (*(__u16 *)(curidt)) = limit; + (*(__u64 *)(curidt +2)) = (unsigned long)(newidt); + + __asm__ __volatile__ ( + "lidt %0\n" + : "=m" (curidt) + ); +}; + + +static void set_gdt(void *newgdt, __u16 limit) +{ + unsigned char curgdt[10]; + + /* x86-64 supports unaligned loads & stores */ + (*(__u16 *)(curgdt)) = limit; + (*(__u64 *)(curgdt +2)) = (unsigned long)(newgdt); + + __asm__ __volatile__ ( + "lgdt %0\n" + : "=m" (curgdt) + ); +}; + +static void load_segments(void) +{ + __asm__ __volatile__ ( + "\tmovl $"STR(__KERNEL_DS)",%eax\n" + "\tmovl %eax,%ds\n" + "\tmovl %eax,%es\n" + "\tmovl %eax,%ss\n" + "\tmovl %eax,%fs\n" + "\tmovl %eax,%gs\n" + ); +#undef STR +#undef __STR +} + +typedef void (*relocate_new_kernel_t)( + unsigned long indirection_page, unsigned long control_code_buffer, + unsigned long start_address, unsigned long pgtable); + +const extern unsigned char relocate_new_kernel[]; +extern void relocate_new_kernel_end(void); +const extern unsigned long relocate_new_kernel_size; + +int machine_kexec_prepare(struct kimage *image) +{ + unsigned long start_pgtable, control_code_buffer; + int result; + + /* Calculate the offsets */ + start_pgtable = page_to_pfn(image->control_code_page) << PAGE_SHIFT; + control_code_buffer = start_pgtable + 4096UL; + + /* Setup the identity mapped 64bit page table */ + result = init_pgtable(image, start_pgtable); + if (result) { + return result; + } + + /* Place the code in the reboot code buffer */ + memcpy(__va(control_code_buffer), relocate_new_kernel, relocate_new_kernel_size); + + return 0; +} + +void machine_kexec_cleanup(struct kimage *image) +{ + return; +} + +/* + * Do not allocate memory (or fail in any way) in machine_kexec(). + * We are past the point of no return, committed to rebooting now. + */ +void machine_kexec(struct kimage *image) +{ + unsigned long indirection_page; + unsigned long control_code_buffer; + unsigned long start_pgtable; + relocate_new_kernel_t rnk; + + /* Interrupts aren't acceptable while we reboot */ + local_irq_disable(); + + /* Calculate the offsets */ + indirection_page = image->head & PAGE_MASK; + start_pgtable = page_to_pfn(image->control_code_page) << PAGE_SHIFT; + control_code_buffer = start_pgtable + 4096UL; + + /* Set the low half of the page table to my identity mapped + * page table for kexec. Leave the high half pointing at the + * kernel pages. Don't bother to flush the global pages + * as that will happen when I fully switch to my identity mapped + * page table anyway. + */ + memcpy((void *)read_pda(level4_pgt), __va(start_pgtable), PAGE_SIZE/2); + __flush_tlb(); + + + /* The segment registers are funny things, they are + * automatically loaded from a table, in memory wherever you + * set them to a specific selector, but this table is never + * accessed again unless you set the segment to a different selector. + * + * The more common model are caches where the behide + * the scenes work is done, but is also dropped at arbitrary + * times. + * + * I take advantage of this here by force loading the + * segments, before I zap the gdt with an invalid value. + */ + load_segments(); + /* The gdt & idt are now invalid. + * If you want to load them you must set up your own idt & gdt. + */ + set_gdt(phys_to_virt(0),0); + set_idt(phys_to_virt(0),0); + /* now call it */ + rnk = (relocate_new_kernel_t) control_code_buffer; + (*rnk)(indirection_page, control_code_buffer, image->start, start_pgtable); +} diff --git a/arch/x86_64/kernel/nmi.c b/arch/x86_64/kernel/nmi.c index 35dd5dfc0..4920c8df7 100644 --- a/arch/x86_64/kernel/nmi.c +++ b/arch/x86_64/kernel/nmi.c @@ -390,7 +390,8 @@ void nmi_watchdog_tick (struct pt_regs * regs, unsigned reason) */ alert_counter[cpu]++; if (alert_counter[cpu] == 5*nmi_hz) { - if (notify_die(DIE_NMI, "nmi", regs, reason, 2, SIGINT) == NOTIFY_BAD) { + if (notify_die(DIE_NMI, "nmi", regs, reason, 2, SIGINT) + == NOTIFY_STOP) { alert_counter[cpu] = 0; return; } diff --git a/arch/x86_64/kernel/pci-dma.c b/arch/x86_64/kernel/pci-dma.c index 7eb3fc72a..cab471cf3 100644 --- a/arch/x86_64/kernel/pci-dma.c +++ b/arch/x86_64/kernel/pci-dma.c @@ -1,5 +1,5 @@ /* - * Dynamic DMA mapping support. Common code + * Dynamic DMA mapping support. */ #include @@ -24,38 +24,37 @@ * Device ownership issues as mentioned above for pci_map_single are * the same here. */ -int pci_map_sg(struct pci_dev *hwdev, struct scatterlist *sg, - int nents, int direction) +int dma_map_sg(struct device *hwdev, struct scatterlist *sg, + int nents, int direction) { int i; - BUG_ON(direction == PCI_DMA_NONE); + BUG_ON(direction == DMA_NONE); for (i = 0; i < nents; i++ ) { struct scatterlist *s = &sg[i]; BUG_ON(!s->page); - s->dma_address = pci_map_page(hwdev, s->page, s->offset, - s->length, direction); + s->dma_address = virt_to_bus(page_address(s->page) +s->offset); s->dma_length = s->length; } return nents; } -EXPORT_SYMBOL(pci_map_sg); +EXPORT_SYMBOL(dma_map_sg); /* Unmap a set of streaming mode DMA translations. * Again, cpu read rules concerning calls here are the same as for * pci_unmap_single() above. */ -void pci_unmap_sg(struct pci_dev *dev, struct scatterlist *sg, - int nents, int dir) +void dma_unmap_sg(struct device *dev, struct scatterlist *sg, + int nents, int dir) { int i; for (i = 0; i < nents; i++) { struct scatterlist *s = &sg[i]; BUG_ON(s->page == NULL); BUG_ON(s->dma_address == 0); - pci_unmap_single(dev, s->dma_address, s->dma_length, dir); + dma_unmap_single(dev, s->dma_address, s->dma_length, dir); } } -EXPORT_SYMBOL(pci_unmap_sg); +EXPORT_SYMBOL(dma_unmap_sg); diff --git a/arch/x86_64/kernel/pci-nommu.c b/arch/x86_64/kernel/pci-nommu.c index 00fb2645c..67d90b89a 100644 --- a/arch/x86_64/kernel/pci-nommu.c +++ b/arch/x86_64/kernel/pci-nommu.c @@ -1,8 +1,11 @@ +/* Fallback functions when the main IOMMU code is not compiled in. This + code is roughly equivalent to i386. */ #include #include #include #include #include +#include int iommu_merge = 0; EXPORT_SYMBOL(iommu_merge); @@ -10,57 +13,80 @@ EXPORT_SYMBOL(iommu_merge); dma_addr_t bad_dma_address; EXPORT_SYMBOL(bad_dma_address); +int iommu_bio_merge = 0; +EXPORT_SYMBOL(iommu_bio_merge); + +int iommu_sac_force = 0; +EXPORT_SYMBOL(iommu_sac_force); + /* * Dummy IO MMU functions */ -void *pci_alloc_consistent(struct pci_dev *hwdev, size_t size, - dma_addr_t *dma_handle) +void *dma_alloc_coherent(struct device *hwdev, size_t size, + dma_addr_t *dma_handle, unsigned gfp) { void *ret; - int gfp = GFP_ATOMIC; - - if (hwdev == NULL || - end_pfn > (hwdev->dma_mask>>PAGE_SHIFT) || /* XXX */ - (u32)hwdev->dma_mask < 0xffffffff) - gfp |= GFP_DMA; - ret = (void *)__get_free_pages(gfp, get_order(size)); + u64 mask; + int order = get_order(size); - if (ret != NULL) { - memset(ret, 0, size); + if (hwdev) + mask = hwdev->coherent_dma_mask & *hwdev->dma_mask; + else + mask = 0xffffffff; + for (;;) { + ret = (void *)__get_free_pages(gfp, order); + if (ret == NULL) + return NULL; *dma_handle = virt_to_bus(ret); + if ((*dma_handle & ~mask) == 0) + break; + free_pages((unsigned long)ret, order); + if (gfp & GFP_DMA) + return NULL; + gfp |= GFP_DMA; } + + memset(ret, 0, size); return ret; } +EXPORT_SYMBOL(dma_alloc_coherent); -void pci_free_consistent(struct pci_dev *hwdev, size_t size, +void dma_free_coherent(struct device *hwdev, size_t size, void *vaddr, dma_addr_t dma_handle) { free_pages((unsigned long)vaddr, get_order(size)); } +EXPORT_SYMBOL(dma_free_coherent); -int pci_dma_supported(struct pci_dev *hwdev, u64 mask) +int dma_supported(struct device *hwdev, u64 mask) { /* * we fall back to GFP_DMA when the mask isn't all 1s, * so we can't guarantee allocations that must be * within a tighter range than GFP_DMA.. - * RED-PEN this won't work for pci_map_single. Caller has to - * use GFP_DMA in the first place. + * RED-PEN this won't work for pci_map_single. Caller has to + * use GFP_DMA in the first place. */ if (mask < 0x00ffffff) return 0; return 1; } +EXPORT_SYMBOL(dma_supported); -EXPORT_SYMBOL(pci_dma_supported); +int dma_get_cache_alignment(void) +{ + return boot_cpu_data.x86_clflush_size; +} +EXPORT_SYMBOL(dma_get_cache_alignment); static int __init check_ram(void) { if (end_pfn >= 0xffffffff>>PAGE_SHIFT) { - printk(KERN_ERR "WARNING more than 4GB of memory but no IOMMU.\n" - KERN_ERR "WARNING 32bit PCI may malfunction.\n"); + printk( + KERN_ERR "WARNING more than 4GB of memory but IOMMU not compiled in.\n" + KERN_ERR "WARNING 32bit PCI may malfunction.\n"); } return 0; } diff --git a/arch/x86_64/kernel/relocate_kernel.S b/arch/x86_64/kernel/relocate_kernel.S new file mode 100644 index 000000000..c944e5958 --- /dev/null +++ b/arch/x86_64/kernel/relocate_kernel.S @@ -0,0 +1,141 @@ +/* + * relocate_kernel.S - put the kernel image in place to boot + * Copyright (C) 2002-2004 Eric Biederman + * + * This source code is licensed under the GNU General Public License, + * Version 2. See the file COPYING for more details. + */ + +#include + + /* + * Must be relocatable PIC code callable as a C function, that once + * it starts can not use the previous processes stack. + */ + .globl relocate_new_kernel + .code64 +relocate_new_kernel: + /* %rdi indirection_page + * %rsi reboot_code_buffer + * %rdx start address + * %rcx page_table + * %r8 arg5 + * %r9 arg6 + */ + + /* zero out flags, and disable interrupts */ + pushq $0 + popfq + + /* set a new stack at the bottom of our page... */ + lea 4096(%rsi), %rsp + + /* store the parameters back on the stack */ + pushq %rdx /* store the start address */ + + /* Set cr0 to a known state: + * 31 1 == Paging enabled + * 18 0 == Alignment check disabled + * 16 0 == Write protect disabled + * 3 0 == No task switch + * 2 0 == Don't do FP software emulation. + * 0 1 == Proctected mode enabled + */ + movq %cr0, %rax + andq $~((1<<18)|(1<<16)|(1<<3)|(1<<2)), %rax + orl $((1<<31)|(1<<0)), %eax + movq %rax, %cr0 + + /* Set cr4 to a known state: + * 10 0 == xmm exceptions disabled + * 9 0 == xmm registers instructions disabled + * 8 0 == performance monitoring counter disabled + * 7 0 == page global disabled + * 6 0 == machine check exceptions disabled + * 5 1 == physical address extension enabled + * 4 0 == page size extensions disabled + * 3 0 == Debug extensions disabled + * 2 0 == Time stamp disable (disabled) + * 1 0 == Protected mode virtual interrupts disabled + * 0 0 == VME disabled + */ + + movq $((1<<5)), %rax + movq %rax, %cr4 + + jmp 1f +1: + + /* Switch to the identity mapped page tables, + * and flush the TLB. + */ + movq %rcx, %cr3 + + /* Do the copies */ + movq %rdi, %rbx /* Put the indirection page in %rbx */ + xorq %rdi, %rdi + xorq %rsi, %rsi + +0: /* top, read another word for the indirection page */ + + movq (%rbx), %rcx + addq $8, %rbx + testq $0x1, %rcx /* is it a destination page? */ + jz 1f + movq %rcx, %rdi + andq $0xfffffffffffff000, %rdi + jmp 0b +1: + testq $0x2, %rcx /* is it an indirection page? */ + jz 1f + movq %rcx, %rbx + andq $0xfffffffffffff000, %rbx + jmp 0b +1: + testq $0x4, %rcx /* is it the done indicator? */ + jz 1f + jmp 2f +1: + testq $0x8, %rcx /* is it the source indicator? */ + jz 0b /* Ignore it otherwise */ + movq %rcx, %rsi /* For ever source page do a copy */ + andq $0xfffffffffffff000, %rsi + + movq $512, %rcx + rep ; movsq + jmp 0b +2: + + /* To be certain of avoiding problems with self-modifying code + * I need to execute a serializing instruction here. + * So I flush the TLB by reloading %cr3 here, it's handy, + * and not processor dependent. + */ + movq %cr3, %rax + movq %rax, %cr3 + + /* set all of the registers to known values */ + /* leave %rsp alone */ + + xorq %rax, %rax + xorq %rbx, %rbx + xorq %rcx, %rcx + xorq %rdx, %rdx + xorq %rsi, %rsi + xorq %rdi, %rdi + xorq %rbp, %rbp + xorq %r8, %r8 + xorq %r9, %r9 + xorq %r10, %r9 + xorq %r11, %r11 + xorq %r12, %r12 + xorq %r13, %r13 + xorq %r14, %r14 + xorq %r15, %r15 + + ret +relocate_new_kernel_end: + + .globl relocate_new_kernel_size +relocate_new_kernel_size: + .quad relocate_new_kernel_end - relocate_new_kernel diff --git a/arch/x86_64/kernel/suspend_asm.S b/arch/x86_64/kernel/suspend_asm.S index 99a1adf86..8a3999810 100644 --- a/arch/x86_64/kernel/suspend_asm.S +++ b/arch/x86_64/kernel/suspend_asm.S @@ -1,22 +1,18 @@ -/* originally gcc generated, but now changed. don't overwrite. */ +/* Originally gcc generated, modified by hand + * + * This may not use any stack, nor any variable that is not "NoSave": + * + * Its rewriting one kernel image with another. What is stack in "old" + * image could very well be data page in "new" image, and overwriting + * your own stack under you is bad idea. + */ .text #include #include #include -/* Input: - * rdi resume flag - */ - -ENTRY(do_magic) -.LFB5: - subq $8, %rsp -.LCFI2: - testl %edi, %edi - jne .L90 - call do_magic_suspend_1 - call save_processor_state +ENTRY(swsusp_arch_suspend) movq %rsp, saved_context_esp(%rip) movq %rax, saved_context_eax(%rip) @@ -36,36 +32,35 @@ ENTRY(do_magic) movq %r15, saved_context_r15(%rip) pushfq ; popq saved_context_eflags(%rip) - addq $8, %rsp - jmp do_magic_suspend_2 -.L90: + call swsusp_save + ret + +ENTRY(swsusp_arch_resume) /* set up cr3 */ leaq init_level4_pgt(%rip),%rax subq $__START_KERNEL_map,%rax - movq %rax,%cr3 + movq %rax,%cr3 movq mmu_cr4_features(%rip), %rax movq %rax, %rdx - andq $~(1<<7), %rdx # PGE - movq %rdx, %cr4; # turn off PGE - movq %cr3, %rcx; # flush TLB - movq %rcx, %cr3; - movq %rax, %cr4; # turn PGE back on + movq %rdx, %cr4; # turn off PGE + movq %cr3, %rcx; # flush TLB + movq %rcx, %cr3; + movq %rax, %cr4; # turn PGE back on - call do_magic_resume_1 movl nr_copy_pages(%rip), %eax xorl %ecx, %ecx - movq $0, loop(%rip) + movq $0, %r10 testl %eax, %eax - je .L108 + jz done .L105: xorl %esi, %esi - movq $0, loop2(%rip) + movq $0, %r11 jmp .L104 .p2align 4,,7 -.L111: - movq loop(%rip), %rcx +copy_one_page: + movq %r10, %rcx .L104: movq pagedir_nosave(%rip), %rdx movq %rcx, %rax @@ -75,27 +70,26 @@ ENTRY(do_magic) movzbl (%rsi,%rax), %eax movb %al, (%rsi,%rcx) - movq %cr3, %rax; # flush TLB - movq %rax, %cr3; + movq %cr3, %rax; # flush TLB + movq %rax, %cr3; - movq loop2(%rip), %rax + movq %r11, %rax incq %rax cmpq $4095, %rax movq %rax, %rsi - movq %rax, loop2(%rip) - jbe .L111 - movq loop(%rip), %rax + movq %rax, %r11 + jbe copy_one_page + movq %r10, %rax incq %rax movq %rax, %rcx - movq %rax, loop(%rip) + movq %rax, %r10 mov nr_copy_pages(%rip), %eax cmpq %rax, %rcx jb .L105 -.L108: - .align 4 +done: movl $24, %eax + movl %eax, %ds - movl %eax, %ds movq saved_context_esp(%rip), %rsp movq saved_context_ebp(%rip), %rbp movq saved_context_eax(%rip), %rax @@ -113,13 +107,5 @@ ENTRY(do_magic) movq saved_context_r14(%rip), %r14 movq saved_context_r15(%rip), %r15 pushq saved_context_eflags(%rip) ; popfq - call restore_processor_state - addq $8, %rsp - jmp do_magic_resume_2 - - .section .data.nosave -loop: - .quad 0 -loop2: - .quad 0 - .previous + call swsusp_restore + ret diff --git a/arch/x86_64/lib/bitops.c b/arch/x86_64/lib/bitops.c new file mode 100644 index 000000000..6060dd908 --- /dev/null +++ b/arch/x86_64/lib/bitops.c @@ -0,0 +1,140 @@ +#include +#include + +#undef find_first_zero_bit +#undef find_next_zero_bit +#undef find_first_bit +#undef find_next_bit + +/** + * find_first_zero_bit - find the first zero 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 zero bit, not the number of the byte + * containing a bit. + */ +inline long find_first_zero_bit(const unsigned long * addr, unsigned long size) +{ + long d0, d1, d2; + long res; + + if (!size) + return 0; + asm volatile( + " repe; scasq\n" + " je 1f\n" + " xorq -8(%%rdi),%%rax\n" + " subq $8,%%rdi\n" + " bsfq %%rax,%%rdx\n" + "1: subq %[addr],%%rdi\n" + " shlq $3,%%rdi\n" + " addq %%rdi,%%rdx" + :"=d" (res), "=&c" (d0), "=&D" (d1), "=&a" (d2) + :"0" (0ULL), "1" ((size + 63) >> 6), "2" (addr), "3" (-1ULL), + [addr] "r" (addr) : "memory"); + return res; +} + +/** + * find_next_zero_bit - find the first zero bit in a memory region + * @addr: The address to base the search on + * @offset: The bitnumber to start searching at + * @size: The maximum size to search + */ +long find_next_zero_bit (const unsigned long * addr, long size, long offset) +{ + unsigned long * p = ((unsigned long *) addr) + (offset >> 6); + unsigned long set = 0; + unsigned long res, bit = offset&63; + + if (bit) { + /* + * Look for zero in first word + */ + asm("bsfq %1,%0\n\t" + "cmoveq %2,%0" + : "=r" (set) + : "r" (~(*p >> bit)), "r"(64L)); + if (set < (64 - bit)) + return set + offset; + set = 64 - bit; + p++; + } + /* + * No zero yet, search remaining full words for a zero + */ + res = find_first_zero_bit ((const unsigned long *)p, + size - 64 * (p - (unsigned long *) addr)); + return (offset + set + res); +} + +static inline long +__find_first_bit(const unsigned long * addr, unsigned long size) +{ + long d0, d1; + long res; + + asm volatile( + " repe; scasq\n" + " jz 1f\n" + " subq $8,%%rdi\n" + " bsfq (%%rdi),%%rax\n" + "1: subq %[addr],%%rdi\n" + " shlq $3,%%rdi\n" + " addq %%rdi,%%rax" + :"=a" (res), "=&c" (d0), "=&D" (d1) + :"0" (0ULL), + "1" ((size + 63) >> 6), "2" (addr), + [addr] "r" (addr) : "memory"); + return res; +} + +/** + * find_first_bit - find the first 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, not the number of the byte + * containing a bit. + */ +long find_first_bit(const unsigned long * addr, unsigned long size) +{ + return __find_first_bit(addr,size); +} + +/** + * find_next_bit - find the first set bit in a memory region + * @addr: The address to base the search on + * @offset: The bitnumber to start searching at + * @size: The maximum size to search + */ +long find_next_bit(const unsigned long * addr, long size, long offset) +{ + const unsigned long * p = addr + (offset >> 6); + unsigned long set = 0, bit = offset & 63, res; + + if (bit) { + /* + * Look for nonzero in the first 64 bits: + */ + asm("bsfq %1,%0\n\t" + "cmoveq %2,%0\n\t" + : "=r" (set) + : "r" (*p >> bit), "r" (64L)); + if (set < (64 - bit)) + return set + offset; + set = 64 - bit; + p++; + } + /* + * No set bit yet, search remaining full words for a bit + */ + res = __find_first_bit (p, size - 64 * (p - addr)); + return (offset + set + res); +} + +EXPORT_SYMBOL(find_next_bit); +EXPORT_SYMBOL(find_first_bit); +EXPORT_SYMBOL(find_first_zero_bit); +EXPORT_SYMBOL(find_next_zero_bit); diff --git a/arch/x86_64/lib/bitstr.c b/arch/x86_64/lib/bitstr.c index 598d79de8..e7ce82a0a 100644 --- a/arch/x86_64/lib/bitstr.c +++ b/arch/x86_64/lib/bitstr.c @@ -1,3 +1,4 @@ +#include #include /* Find string of zero bits in a bitmap */ @@ -23,3 +24,5 @@ find_next_zero_string(unsigned long *bitmap, long start, long nbits, int len) } return n; } + +EXPORT_SYMBOL(find_next_zero_string); diff --git a/arch/x86_64/lib/memmove.c b/arch/x86_64/lib/memmove.c index 12cac5ba7..e93d5255f 100644 --- a/arch/x86_64/lib/memmove.c +++ b/arch/x86_64/lib/memmove.c @@ -10,18 +10,10 @@ void *memmove(void * dest,const void *src,size_t count) if (dest < src) { __inline_memcpy(dest,src,count); } else { - /* Could be more clever and move longs */ - unsigned long d0, d1, d2; - __asm__ __volatile__( - "std\n\t" - "rep\n\t" - "movsb\n\t" - "cld" - : "=&c" (d0), "=&S" (d1), "=&D" (d2) - :"0" (count), - "1" (count-1+(const char *)src), - "2" (count-1+(char *)dest) - :"memory"); + char *p = (char *) dest + count; + char *s = (char *) src + count; + while (count--) + *--p = *--s; } return dest; } diff --git a/arch/x86_64/mm/k8topology.c b/arch/x86_64/mm/k8topology.c index 0ec22c11c..0825e37fd 100644 --- a/arch/x86_64/mm/k8topology.c +++ b/arch/x86_64/mm/k8topology.c @@ -100,8 +100,8 @@ int __init k8_scan_nodes(unsigned long start, unsigned long end) limit <<= 24; limit |= (1<<24)-1; - if (limit > end_pfn_map << PAGE_SHIFT) - limit = end_pfn_map << PAGE_SHIFT; + if (limit > end_pfn << PAGE_SHIFT) + limit = end_pfn << PAGE_SHIFT; if (limit <= base) continue; diff --git a/arch/x86_64/mm/mmap.c b/arch/x86_64/mm/mmap.c index 011bb4cbb..373f7af74 100644 --- a/arch/x86_64/mm/mmap.c +++ b/arch/x86_64/mm/mmap.c @@ -33,7 +33,7 @@ * Leave an at least ~128 MB hole. */ #define MIN_GAP (128*1024*1024) -#define MAX_GAP (TASK_SIZE/6*5) +#define MAX_GAP (TASK_SIZE_3264/6*5) static inline unsigned long mmap_base(void) { @@ -44,7 +44,7 @@ static inline unsigned long mmap_base(void) else if (gap > MAX_GAP) gap = MAX_GAP; - return TASK_SIZE - (gap & PAGE_MASK); + return TASK_SIZE_3264 - (gap & PAGE_MASK); } static inline int mmap_is_legacy(void) @@ -81,7 +81,6 @@ void arch_pick_mmap_layout(struct mm_struct *mm) } else { mm->mmap_base = mmap_base(); mm->get_unmapped_area = arch_get_unmapped_area_topdown; - mm->get_unmapped_exec_area = arch_get_unmapped_exec_area; mm->unmap_area = arch_unmap_area_topdown; } } diff --git a/arch/x86_64/pci/Makefile b/arch/x86_64/pci/Makefile index 291985f0d..37c92e841 100644 --- a/arch/x86_64/pci/Makefile +++ b/arch/x86_64/pci/Makefile @@ -3,7 +3,7 @@ # # Reuse the i386 PCI subsystem # -CFLAGS += -I arch/i386/pci +CFLAGS += -Iarch/i386/pci obj-y := i386.o obj-$(CONFIG_PCI_DIRECT)+= direct.o @@ -13,6 +13,8 @@ obj-y += legacy.o irq.o common.o # mmconfig has a 64bit special obj-$(CONFIG_PCI_MMCONFIG) += mmconfig.o +obj-$(CONFIG_NUMA) += k8-bus.o + direct-y += ../../i386/pci/direct.o acpi-y += ../../i386/pci/acpi.o legacy-y += ../../i386/pci/legacy.o diff --git a/arch/x86_64/pci/Makefile-BUS b/arch/x86_64/pci/Makefile-BUS new file mode 100644 index 000000000..291985f0d --- /dev/null +++ b/arch/x86_64/pci/Makefile-BUS @@ -0,0 +1,22 @@ +# +# Makefile for X86_64 specific PCI routines +# +# Reuse the i386 PCI subsystem +# +CFLAGS += -I arch/i386/pci + +obj-y := i386.o +obj-$(CONFIG_PCI_DIRECT)+= direct.o +obj-y += fixup.o +obj-$(CONFIG_ACPI_PCI) += acpi.o +obj-y += legacy.o irq.o common.o +# mmconfig has a 64bit special +obj-$(CONFIG_PCI_MMCONFIG) += mmconfig.o + +direct-y += ../../i386/pci/direct.o +acpi-y += ../../i386/pci/acpi.o +legacy-y += ../../i386/pci/legacy.o +irq-y += ../../i386/pci/irq.o +common-y += ../../i386/pci/common.o +fixup-y += ../../i386/pci/fixup.o +i386-y += ../../i386/pci/i386.o diff --git a/arch/x86_64/pci/k8-bus.c b/arch/x86_64/pci/k8-bus.c new file mode 100644 index 000000000..d1c728acd --- /dev/null +++ b/arch/x86_64/pci/k8-bus.c @@ -0,0 +1,74 @@ +#include +#include +#include +#include + +/* + * This discovers the pcibus <-> node mapping on AMD K8. + * + * RED-PEN need to call this again on PCI hotplug + * RED-PEN empty cpus get reported wrong + */ + +#define NODE_ID_REGISTER 0x60 +#define NODE_ID(dword) (dword & 0x07) +#define LDT_BUS_NUMBER_REGISTER_0 0x94 +#define LDT_BUS_NUMBER_REGISTER_1 0xB4 +#define LDT_BUS_NUMBER_REGISTER_2 0xD4 +#define NR_LDT_BUS_NUMBER_REGISTERS 3 +#define SECONDARY_LDT_BUS_NUMBER(dword) ((dword >> 8) & 0xFF) +#define SUBORDINATE_LDT_BUS_NUMBER(dword) ((dword >> 16) & 0xFF) +#define PCI_DEVICE_ID_K8HTCONFIG 0x1100 + +/** + * fill_mp_bus_to_cpumask() + * fills the mp_bus_to_cpumask array based according to the LDT Bus Number + * Registers found in the K8 northbridge + */ +__init static int +fill_mp_bus_to_cpumask(void) +{ + struct pci_dev *nb_dev = NULL; + int i, j; + u32 ldtbus, nid; + static int lbnr[3] = { + LDT_BUS_NUMBER_REGISTER_0, + LDT_BUS_NUMBER_REGISTER_1, + LDT_BUS_NUMBER_REGISTER_2 + }; + + while ((nb_dev = pci_get_device(PCI_VENDOR_ID_AMD, + PCI_DEVICE_ID_K8HTCONFIG, nb_dev))) { + pci_read_config_dword(nb_dev, NODE_ID_REGISTER, &nid); + + for (i = 0; i < NR_LDT_BUS_NUMBER_REGISTERS; i++) { + pci_read_config_dword(nb_dev, lbnr[i], &ldtbus); + /* + * if there are no busses hanging off of the current + * ldt link then both the secondary and subordinate + * bus number fields are set to 0. + */ + if (!(SECONDARY_LDT_BUS_NUMBER(ldtbus) == 0 + && SUBORDINATE_LDT_BUS_NUMBER(ldtbus) == 0)) { + for (j = SECONDARY_LDT_BUS_NUMBER(ldtbus); + j <= SUBORDINATE_LDT_BUS_NUMBER(ldtbus); + j++) + pci_bus_to_cpumask[j] = + node_to_cpumask(NODE_ID(nid)); + } + } + } + + /* quick sanity check */ + for (i = 0; i < 256; i++) { + if (cpus_empty(pci_bus_to_cpumask[i])) { + printk(KERN_ERR + "k8-bus.c: bus %i has empty cpu mask\n", i); + pci_bus_to_cpumask[i] = CPU_MASK_ALL; + } + } + + return 0; +} + +fs_initcall(fill_mp_bus_to_cpumask); diff --git a/configs/kernel-2.6.8-i686-planetlab.config b/configs/kernel-2.6.8-i686-planetlab.config index ea66387e5..ffa265fc4 100644 --- a/configs/kernel-2.6.8-i686-planetlab.config +++ b/configs/kernel-2.6.8-i686-planetlab.config @@ -30,8 +30,9 @@ CONFIG_RCFS_FS=y CONFIG_CKRM_TYPE_TASKCLASS=y CONFIG_CKRM_RES_NUMTASKS=y CONFIG_CKRM_CPU_SCHEDULE=y -CONFIG_CKRM_RES_BLKIO=y +# CONFIG_CKRM_RES_BLKIO is not set # CONFIG_CKRM_RES_MEM is not set +# CONFIG_CKRM_CPU_SCHEDULE_AT_BOOT is not set # CONFIG_CKRM_TYPE_SOCKETCLASS is not set CONFIG_CKRM_RBCE=y CONFIG_SYSCTL=y @@ -140,6 +141,12 @@ CONFIG_HIGHPTE=y # CONFIG_MATH_EMULATION is not set CONFIG_MTRR=y CONFIG_REGPARM=y +CONFIG_IRQSTACKS=y +CONFIG_STACK_SIZE_SHIFT=13 +CONFIG_STACK_WARN=4000 +CONFIG_X86_STACK_CHECK=y +CONFIG_STACK_PANIC=512 +CONFIG_KEXEC=y # # Power management options (ACPI, APM) @@ -211,7 +218,7 @@ CONFIG_PREVENT_FIRMWARE_BUILD=y # # Block devices # -# CONFIG_BLK_DEV_FD is not set +CONFIG_BLK_DEV_FD=m # CONFIG_BLK_DEV_XD is not set CONFIG_BLK_CPQ_DA=m CONFIG_BLK_CPQ_CISS_DA=m @@ -540,6 +547,11 @@ CONFIG_IP_NF_TARGET_NOTRACK=m CONFIG_IP_NF_RAW=m CONFIG_IP_NF_MATCH_ADDRTYPE=m CONFIG_IP_NF_MATCH_REALM=m +# CONFIG_IP_NF_CT_ACCT is not set +CONFIG_IP_NF_CT_PROTO_GRE=m +CONFIG_IP_NF_PPTP=m +CONFIG_IP_NF_NAT_PPTP=m +CONFIG_IP_NF_NAT_PROTO_GRE=m # # SCTP Configuration (EXPERIMENTAL) diff --git a/configs/kernel-2.6.9-i586-smp.config b/configs/kernel-2.6.9-i586-smp.config new file mode 100644 index 000000000..6e5307722 --- /dev/null +++ b/configs/kernel-2.6.9-i586-smp.config @@ -0,0 +1,2571 @@ +# +# Automatically generated make config: don't edit +# Linux kernel version: 2.6.9-ac11 +# Tue Jan 4 10:01:15 2005 +# +CONFIG_X86=y +CONFIG_MMU=y +CONFIG_UID16=y +CONFIG_GENERIC_ISA_DMA=y +CONFIG_GENERIC_IOMAP=y + +# +# Code maturity level options +# +CONFIG_EXPERIMENTAL=y +CONFIG_CLEAN_COMPILE=y + +# +# General setup +# +CONFIG_LOCALVERSION="" +CONFIG_SWAP=y +CONFIG_SYSVIPC=y +CONFIG_POSIX_MQUEUE=y +CONFIG_BSD_PROCESS_ACCT=y +# CONFIG_BSD_PROCESS_ACCT_V3 is not set +CONFIG_SYSCTL=y +CONFIG_AUDIT=y +CONFIG_AUDITSYSCALL=y +CONFIG_LOG_BUF_SHIFT=17 +CONFIG_HOTPLUG=y +# CONFIG_IKCONFIG is not set +# CONFIG_EMBEDDED is not set +CONFIG_KALLSYMS=y +# CONFIG_KALLSYMS_ALL is not set +CONFIG_KALLSYMS_EXTRA_PASS=y +CONFIG_FUTEX=y +CONFIG_EPOLL=y +CONFIG_IOSCHED_NOOP=y +CONFIG_IOSCHED_AS=y +CONFIG_IOSCHED_DEADLINE=y +CONFIG_IOSCHED_CFQ=y +CONFIG_CC_OPTIMIZE_FOR_SIZE=y +CONFIG_SHMEM=y +# CONFIG_TINY_SHMEM is not set + +# +# Loadable module support +# +CONFIG_MODULES=y +CONFIG_MODULE_UNLOAD=y +# CONFIG_MODULE_FORCE_UNLOAD is not set +CONFIG_OBSOLETE_MODPARM=y +CONFIG_MODVERSIONS=y +CONFIG_MODULE_SIG=y +# CONFIG_MODULE_SIG_FORCE is not set +CONFIG_KMOD=y +CONFIG_STOP_MACHINE=y + +# +# Processor type and features +# +CONFIG_X86_PC=y +# CONFIG_X86_ELAN is not set +# CONFIG_X86_VOYAGER is not set +# CONFIG_X86_NUMAQ is not set +# CONFIG_X86_SUMMIT is not set +# CONFIG_X86_BIGSMP is not set +# CONFIG_X86_VISWS is not set +# CONFIG_X86_GENERICARCH is not set +# CONFIG_X86_ES7000 is not set +# CONFIG_M386 is not set +# CONFIG_M486 is not set +CONFIG_M586=y +# CONFIG_M586TSC is not set +# CONFIG_M586MMX is not set +# CONFIG_M686 is not set +# CONFIG_MPENTIUMII is not set +# CONFIG_MPENTIUMIII is not set +# CONFIG_MPENTIUMM is not set +# CONFIG_MPENTIUM4 is not set +# CONFIG_MK6 is not set +# CONFIG_MK7 is not set +# CONFIG_MK8 is not set +# CONFIG_MCRUSOE is not set +# CONFIG_MWINCHIPC6 is not set +# CONFIG_MWINCHIP2 is not set +# CONFIG_MWINCHIP3D is not set +# CONFIG_MCYRIXIII is not set +# CONFIG_MVIAC3_2 is not set +CONFIG_X86_GENERIC=y +CONFIG_X86_CMPXCHG=y +CONFIG_X86_XADD=y +CONFIG_X86_L1_CACHE_SHIFT=7 +CONFIG_RWSEM_XCHGADD_ALGORITHM=y +CONFIG_X86_PPRO_FENCE=y +CONFIG_X86_F00F_BUG=y +CONFIG_X86_WP_WORKS_OK=y +CONFIG_X86_INVLPG=y +CONFIG_X86_BSWAP=y +CONFIG_X86_POPAD_OK=y +CONFIG_X86_ALIGNMENT_16=y +CONFIG_X86_INTEL_USERCOPY=y +CONFIG_HPET_TIMER=y +CONFIG_HPET_EMULATE_RTC=y +CONFIG_SMP=y +CONFIG_NR_CPUS=8 +CONFIG_SCHED_SMT=y +# CONFIG_PREEMPT is not set +CONFIG_PREEMPT_VOLUNTARY=y +CONFIG_X86_LOCAL_APIC=y +CONFIG_X86_IO_APIC=y +CONFIG_X86_MCE=y +# CONFIG_X86_MCE_NONFATAL is not set +CONFIG_X86_MCE_P4THERMAL=y +CONFIG_TOSHIBA=m +CONFIG_I8K=m +# CONFIG_MICROCODE is not set +CONFIG_X86_MSR=m +CONFIG_X86_CPUID=m + +# +# Firmware Drivers +# +CONFIG_EDD=m +# CONFIG_NOHIGHMEM is not set +CONFIG_HIGHMEM4G=y +# CONFIG_HIGHMEM64G is not set +CONFIG_HIGHMEM=y +CONFIG_HIGHPTE=y +# CONFIG_MATH_EMULATION is not set +CONFIG_MTRR=y +# CONFIG_EFI is not set +# CONFIG_IRQBALANCE is not set +CONFIG_HAVE_DEC_LOCK=y +CONFIG_REGPARM=y +CONFIG_KEXEC=y + +# +# Power management options (ACPI, APM) +# +CONFIG_PM=y +# CONFIG_PM_DEBUG is not set +# CONFIG_SOFTWARE_SUSPEND is not set + +# +# ACPI (Advanced Configuration and Power Interface) Support +# +CONFIG_ACPI=y +CONFIG_ACPI_BOOT=y +CONFIG_ACPI_INTERPRETER=y +CONFIG_ACPI_SLEEP=y +CONFIG_ACPI_SLEEP_PROC_FS=y +CONFIG_ACPI_AC=m +CONFIG_ACPI_BATTERY=m +CONFIG_ACPI_BUTTON=m +CONFIG_ACPI_FAN=y +CONFIG_ACPI_PROCESSOR=y +CONFIG_ACPI_THERMAL=y +CONFIG_ACPI_ASUS=m +CONFIG_ACPI_TOSHIBA=m +CONFIG_ACPI_BLACKLIST_YEAR=2001 +# CONFIG_ACPI_DEBUG is not set +CONFIG_ACPI_BUS=y +CONFIG_ACPI_EC=y +CONFIG_ACPI_POWER=y +CONFIG_ACPI_PCI=y +CONFIG_ACPI_SYSTEM=y +CONFIG_X86_PM_TIMER=y + +# +# APM (Advanced Power Management) BIOS Support +# +CONFIG_APM=y +# CONFIG_APM_IGNORE_USER_SUSPEND is not set +# CONFIG_APM_DO_ENABLE is not set +CONFIG_APM_CPU_IDLE=y +# CONFIG_APM_DISPLAY_BLANK is not set +CONFIG_APM_RTC_IS_GMT=y +# CONFIG_APM_ALLOW_INTS is not set +# CONFIG_APM_REAL_MODE_POWER_OFF is not set + +# +# CPU Frequency scaling +# +CONFIG_CPU_FREQ=y +# CONFIG_CPU_FREQ_PROC_INTF is not set +# CONFIG_CPU_FREQ_DEFAULT_GOV_PERFORMANCE is not set +CONFIG_CPU_FREQ_DEFAULT_GOV_USERSPACE=y +CONFIG_CPU_FREQ_GOV_PERFORMANCE=y +CONFIG_CPU_FREQ_GOV_POWERSAVE=m +CONFIG_CPU_FREQ_GOV_USERSPACE=y +CONFIG_CPU_FREQ_GOV_ONDEMAND=m +# CONFIG_CPU_FREQ_24_API is not set +CONFIG_CPU_FREQ_TABLE=y + +# +# CPUFreq processor drivers +# +CONFIG_X86_ACPI_CPUFREQ=m +# CONFIG_X86_ACPI_CPUFREQ_PROC_INTF is not set +CONFIG_X86_POWERNOW_K6=m +CONFIG_X86_POWERNOW_K7=y +CONFIG_X86_POWERNOW_K7_ACPI=y +CONFIG_X86_POWERNOW_K8=m +CONFIG_X86_POWERNOW_K8_ACPI=y +# CONFIG_X86_GX_SUSPMOD is not set +CONFIG_X86_SPEEDSTEP_CENTRINO=y +CONFIG_X86_SPEEDSTEP_CENTRINO_TABLE=y +CONFIG_X86_SPEEDSTEP_CENTRINO_ACPI=y +CONFIG_X86_SPEEDSTEP_ICH=y +CONFIG_X86_SPEEDSTEP_SMI=m +CONFIG_X86_P4_CLOCKMOD=m +CONFIG_X86_SPEEDSTEP_LIB=y +# CONFIG_X86_SPEEDSTEP_RELAXED_CAP_CHECK is not set +CONFIG_X86_LONGRUN=y +CONFIG_X86_LONGHAUL=y + +# +# Bus options (PCI, PCMCIA, EISA, MCA, ISA) +# +CONFIG_PCI=y +# CONFIG_PCI_GOBIOS is not set +# CONFIG_PCI_GOMMCONFIG is not set +# CONFIG_PCI_GODIRECT is not set +CONFIG_PCI_GOANY=y +CONFIG_PCI_BIOS=y +CONFIG_PCI_DIRECT=y +CONFIG_PCI_MMCONFIG=y +CONFIG_PCI_MSI=y +CONFIG_PCI_LEGACY_PROC=y +# CONFIG_PCI_NAMES is not set +CONFIG_ISA=y +# CONFIG_EISA is not set +# CONFIG_MCA is not set +# CONFIG_SCx200 is not set + +# +# PCMCIA/CardBus support +# +CONFIG_PCMCIA=m +# CONFIG_PCMCIA_DEBUG is not set +CONFIG_YENTA=m +CONFIG_CARDBUS=y +CONFIG_PD6729=m +CONFIG_I82092=m +CONFIG_I82365=m +CONFIG_TCIC=m +CONFIG_PCMCIA_PROBE=y + +# +# PCI Hotplug Support +# +CONFIG_HOTPLUG_PCI=y +# CONFIG_HOTPLUG_PCI_FAKE is not set +CONFIG_HOTPLUG_PCI_COMPAQ=m +# CONFIG_HOTPLUG_PCI_COMPAQ_NVRAM is not set +CONFIG_HOTPLUG_PCI_IBM=m +CONFIG_HOTPLUG_PCI_ACPI=m +CONFIG_HOTPLUG_PCI_ACPI_IBM=m +# CONFIG_HOTPLUG_PCI_CPCI is not set +CONFIG_HOTPLUG_PCI_PCIE=m +# CONFIG_HOTPLUG_PCI_PCIE_POLL_EVENT_MODE is not set +CONFIG_HOTPLUG_PCI_SHPC=m +# CONFIG_HOTPLUG_PCI_SHPC_POLL_EVENT_MODE is not set + +# +# Executable file formats +# +CONFIG_BINFMT_ELF=y +# CONFIG_BINFMT_AOUT is not set +CONFIG_BINFMT_MISC=y + +# +# Device Drivers +# + +# +# Generic Driver Options +# +CONFIG_STANDALONE=y +CONFIG_PREVENT_FIRMWARE_BUILD=y +CONFIG_FW_LOADER=y +# CONFIG_DEBUG_DRIVER is not set + +# +# Memory Technology Devices (MTD) +# +CONFIG_MTD=m +# CONFIG_MTD_DEBUG is not set +CONFIG_MTD_PARTITIONS=y +CONFIG_MTD_CONCAT=m +CONFIG_MTD_REDBOOT_PARTS=m +# CONFIG_MTD_REDBOOT_PARTS_UNALLOCATED is not set +# CONFIG_MTD_REDBOOT_PARTS_READONLY is not set +CONFIG_MTD_CMDLINE_PARTS=y + +# +# User Modules And Translation Layers +# +CONFIG_MTD_CHAR=m +CONFIG_MTD_BLOCK=m +CONFIG_MTD_BLOCK_RO=m +CONFIG_FTL=m +CONFIG_NFTL=m +CONFIG_NFTL_RW=y +CONFIG_INFTL=m + +# +# RAM/ROM/Flash chip drivers +# +CONFIG_MTD_CFI=m +CONFIG_MTD_JEDECPROBE=m +CONFIG_MTD_GEN_PROBE=m +# 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=m +CONFIG_MTD_CFI_AMDSTD=m +CONFIG_MTD_CFI_AMDSTD_RETRY=3 +CONFIG_MTD_CFI_STAA=m +CONFIG_MTD_CFI_UTIL=m +CONFIG_MTD_RAM=m +CONFIG_MTD_ROM=m +CONFIG_MTD_ABSENT=m + +# +# Mapping drivers for chip access +# +CONFIG_MTD_COMPLEX_MAPPINGS=y +# CONFIG_MTD_PHYSMAP is not set +# CONFIG_MTD_PNC2000 is not set +CONFIG_MTD_SC520CDP=m +CONFIG_MTD_NETSC520=m +CONFIG_MTD_SBC_GXX=m +CONFIG_MTD_ELAN_104NC=m +CONFIG_MTD_SCx200_DOCFLASH=m +# CONFIG_MTD_AMD76XROM is not set +# CONFIG_MTD_ICHXROM is not set +CONFIG_MTD_SCB2_FLASH=m +# CONFIG_MTD_NETtel is not set +# CONFIG_MTD_DILNETPC is not set +# CONFIG_MTD_L440GX is not set +CONFIG_MTD_PCI=m + +# +# Self-contained MTD device drivers +# +CONFIG_MTD_PMC551=m +# CONFIG_MTD_PMC551_BUGFIX is not set +# CONFIG_MTD_PMC551_DEBUG is not set +# CONFIG_MTD_SLRAM is not set +# CONFIG_MTD_PHRAM is not set +CONFIG_MTD_MTDRAM=m +CONFIG_MTDRAM_TOTAL_SIZE=4096 +CONFIG_MTDRAM_ERASE_SIZE=128 +# CONFIG_MTD_BLKMTD is not set + +# +# Disk-On-Chip Device Drivers +# +CONFIG_MTD_DOC2000=m +# CONFIG_MTD_DOC2001 is not set +CONFIG_MTD_DOC2001PLUS=m +CONFIG_MTD_DOCPROBE=m +CONFIG_MTD_DOCECC=m +# CONFIG_MTD_DOCPROBE_ADVANCED is not set +CONFIG_MTD_DOCPROBE_ADDRESS=0 + +# +# NAND Flash Device Drivers +# +CONFIG_MTD_NAND=m +# CONFIG_MTD_NAND_VERIFY_WRITE is not set +CONFIG_MTD_NAND_IDS=m +# CONFIG_MTD_NAND_DISKONCHIP is not set + +# +# Parallel port support +# +CONFIG_PARPORT=m +CONFIG_PARPORT_PC=m +CONFIG_PARPORT_PC_CML1=m +CONFIG_PARPORT_SERIAL=m +# CONFIG_PARPORT_PC_FIFO is not set +# CONFIG_PARPORT_PC_SUPERIO is not set +CONFIG_PARPORT_PC_PCMCIA=m +# CONFIG_PARPORT_OTHER is not set +CONFIG_PARPORT_1284=y + +# +# Plug and Play support +# +CONFIG_PNP=y +# CONFIG_PNP_DEBUG is not set + +# +# Protocols +# +CONFIG_ISAPNP=y +# CONFIG_PNPBIOS is not set + +# +# Block devices +# +CONFIG_BLK_DEV_FD=m +# CONFIG_BLK_DEV_XD is not set +# CONFIG_PARIDE is not set +CONFIG_BLK_CPQ_DA=m +CONFIG_BLK_CPQ_CISS_DA=m +CONFIG_CISS_SCSI_TAPE=y +CONFIG_BLK_DEV_DAC960=m +CONFIG_BLK_DEV_UMEM=m +CONFIG_BLK_DEV_LOOP=m +CONFIG_BLK_DEV_CRYPTOLOOP=m +CONFIG_BLK_DEV_NBD=m +CONFIG_BLK_DEV_SX8=m +# CONFIG_BLK_DEV_UB is not set +CONFIG_BLK_DEV_RAM=y +CONFIG_BLK_DEV_RAM_SIZE=16384 +CONFIG_BLK_DEV_INITRD=y +CONFIG_LBD=y + +# +# ATA/ATAPI/MFM/RLL support +# +CONFIG_IDE=y +CONFIG_BLK_DEV_IDE=y + +# +# Please see Documentation/ide.txt for help/info on IDE drives +# +# CONFIG_BLK_DEV_IDE_SATA is not set +# CONFIG_BLK_DEV_HD_IDE is not set +CONFIG_BLK_DEV_IDEDISK=y +CONFIG_IDEDISK_MULTI_MODE=y +CONFIG_BLK_DEV_IDECS=m +CONFIG_BLK_DEV_DELKIN=m +CONFIG_BLK_DEV_IDECD=y +# CONFIG_BLK_DEV_IDETAPE is not set +CONFIG_BLK_DEV_IDEFLOPPY=y +CONFIG_BLK_DEV_IDESCSI=m +# CONFIG_IDE_TASK_IOCTL is not set +# CONFIG_IDE_TASKFILE_IO is not set + +# +# IDE chipset support/bugfixes +# +CONFIG_IDE_GENERIC=y +# CONFIG_BLK_DEV_CMD640 is not set +CONFIG_BLK_DEV_IDEPNP=y +CONFIG_BLK_DEV_IDEPCI=y +CONFIG_IDEPCI_SHARE_IRQ=y +# CONFIG_BLK_DEV_OFFBOARD is not set +CONFIG_BLK_DEV_GENERIC=y +# CONFIG_BLK_DEV_OPTI621 is not set +CONFIG_BLK_DEV_RZ1000=y +CONFIG_BLK_DEV_IDEDMA_PCI=y +# CONFIG_BLK_DEV_IDEDMA_FORCED is not set +CONFIG_IDEDMA_PCI_AUTO=y +# CONFIG_IDEDMA_ONLYDISK is not set +CONFIG_BLK_DEV_AEC62XX=y +CONFIG_BLK_DEV_ALI15X3=y +# CONFIG_WDC_ALI15X3 is not set +CONFIG_BLK_DEV_AMD74XX=y +CONFIG_BLK_DEV_ATIIXP=y +CONFIG_BLK_DEV_CMD64X=y +CONFIG_BLK_DEV_TRIFLEX=y +CONFIG_BLK_DEV_CY82C693=y +CONFIG_BLK_DEV_CS5520=y +CONFIG_BLK_DEV_CS5530=y +CONFIG_BLK_DEV_HPT34X=y +# CONFIG_HPT34X_AUTODMA is not set +CONFIG_BLK_DEV_HPT366=y +# CONFIG_BLK_DEV_SC1200 is not set +CONFIG_BLK_DEV_PIIX=y +CONFIG_BLK_DEV_IT8212=y +# CONFIG_BLK_DEV_NS87415 is not set +CONFIG_BLK_DEV_PDC202XX_OLD=y +# CONFIG_PDC202XX_BURST is not set +CONFIG_BLK_DEV_PDC202XX_NEW=y +CONFIG_PDC202XX_FORCE=y +CONFIG_BLK_DEV_SVWKS=y +CONFIG_BLK_DEV_SIIMAGE=y +CONFIG_BLK_DEV_SIS5513=y +CONFIG_BLK_DEV_SLC90E66=y +# CONFIG_BLK_DEV_TRM290 is not set +CONFIG_BLK_DEV_VIA82CXXX=y +# CONFIG_IDE_ARM is not set +# CONFIG_IDE_CHIPSETS is not set +CONFIG_BLK_DEV_IDEDMA=y +# CONFIG_IDEDMA_IVB is not set +CONFIG_IDEDMA_AUTO=y +# CONFIG_BLK_DEV_HD is not set + +# +# SCSI device support +# +CONFIG_SCSI=m +CONFIG_SCSI_PROC_FS=y + +# +# SCSI support type (disk, tape, CD-ROM) +# +CONFIG_BLK_DEV_SD=m +CONFIG_CHR_DEV_ST=m +CONFIG_CHR_DEV_OSST=m +CONFIG_BLK_DEV_SR=m +CONFIG_BLK_DEV_SR_VENDOR=y +CONFIG_CHR_DEV_SG=m + +# +# Some SCSI devices (e.g. CD jukebox) support multiple LUNs +# +# CONFIG_SCSI_MULTI_LUN is not set +CONFIG_SCSI_CONSTANTS=y +CONFIG_SCSI_LOGGING=y + +# +# SCSI Transport Attributes +# +CONFIG_SCSI_SPI_ATTRS=m +CONFIG_SCSI_FC_ATTRS=m + +# +# SCSI low-level drivers +# +CONFIG_BLK_DEV_3W_XXXX_RAID=m +CONFIG_SCSI_3W_9XXX=m +# CONFIG_SCSI_7000FASST is not set +CONFIG_SCSI_ACARD=m +CONFIG_SCSI_AHA152X=m +CONFIG_SCSI_AHA1542=m +CONFIG_SCSI_AACRAID=m +CONFIG_SCSI_AIC7XXX=m +CONFIG_AIC7XXX_CMDS_PER_DEVICE=4 +CONFIG_AIC7XXX_RESET_DELAY_MS=15000 +# CONFIG_AIC7XXX_DEBUG_ENABLE is not set +CONFIG_AIC7XXX_DEBUG_MASK=0 +# CONFIG_AIC7XXX_REG_PRETTY_PRINT is not set +CONFIG_SCSI_AIC7XXX_OLD=m +CONFIG_SCSI_AIC79XX=m +CONFIG_AIC79XX_CMDS_PER_DEVICE=4 +CONFIG_AIC79XX_RESET_DELAY_MS=15000 +# CONFIG_AIC79XX_ENABLE_RD_STRM is not set +# CONFIG_AIC79XX_DEBUG_ENABLE is not set +CONFIG_AIC79XX_DEBUG_MASK=0 +# CONFIG_AIC79XX_REG_PRETTY_PRINT is not set +# CONFIG_SCSI_DPT_I2O is not set +CONFIG_SCSI_IN2000=m +CONFIG_MEGARAID_NEWGEN=y +CONFIG_MEGARAID_MM=m +CONFIG_MEGARAID_MAILBOX=m +CONFIG_MEGARAID_LEGACY=m +CONFIG_SCSI_SATA=y +CONFIG_SCSI_SATA_AHCI=m +CONFIG_SCSI_SATA_SVW=m +CONFIG_SCSI_ATA_PIIX=m +CONFIG_SCSI_SATA_NV=m +CONFIG_SCSI_SATA_PROMISE=m +CONFIG_SCSI_SATA_SX4=m +CONFIG_SCSI_SATA_SIL=m +CONFIG_SCSI_SATA_SIS=m +CONFIG_SCSI_SATA_VIA=m +CONFIG_SCSI_SATA_VITESSE=m +CONFIG_SCSI_BUSLOGIC=m +# CONFIG_SCSI_OMIT_FLASHPOINT is not set +# CONFIG_SCSI_DMX3191D is not set +# CONFIG_SCSI_DTC3280 is not set +# CONFIG_SCSI_EATA is not set +# CONFIG_SCSI_EATA_PIO is not set +CONFIG_SCSI_LPFC=m +CONFIG_SCSI_FUTURE_DOMAIN=m +CONFIG_SCSI_GDTH=m +# CONFIG_SCSI_GENERIC_NCR5380 is not set +# CONFIG_SCSI_GENERIC_NCR5380_MMIO is not set +CONFIG_SCSI_IPS=m +CONFIG_SCSI_INITIO=m +CONFIG_SCSI_INIA100=m +CONFIG_SCSI_PPA=m +CONFIG_SCSI_IMM=m +# CONFIG_SCSI_IZIP_EPP16 is not set +# CONFIG_SCSI_IZIP_SLOW_CTR is not set +# CONFIG_SCSI_NCR53C406A is not set +CONFIG_SCSI_SYM53C8XX_2=m +CONFIG_SCSI_SYM53C8XX_DMA_ADDRESSING_MODE=1 +CONFIG_SCSI_SYM53C8XX_DEFAULT_TAGS=16 +CONFIG_SCSI_SYM53C8XX_MAX_TAGS=64 +# CONFIG_SCSI_SYM53C8XX_IOMAPPED is not set +# CONFIG_SCSI_IPR is not set +# CONFIG_SCSI_PAS16 is not set +# CONFIG_SCSI_PSI240I is not set +CONFIG_SCSI_QLOGIC_FAS=m +CONFIG_SCSI_QLOGIC_ISP=m +# CONFIG_SCSI_QLOGIC_FC is not set +CONFIG_SCSI_QLOGIC_1280=m +CONFIG_SCSI_QLA2XXX=m +CONFIG_SCSI_QLA21XX=m +CONFIG_SCSI_QLA22XX=m +CONFIG_SCSI_QLA2300=m +CONFIG_SCSI_QLA2322=m +CONFIG_SCSI_QLA6312=m +CONFIG_SCSI_QLA6322=m +# CONFIG_SCSI_SYM53C416 is not set +# CONFIG_SCSI_DC395x is not set +CONFIG_SCSI_DC390T=m +# CONFIG_SCSI_T128 is not set +# CONFIG_SCSI_U14_34F is not set +# CONFIG_SCSI_ULTRASTOR is not set +# CONFIG_SCSI_NSP32 is not set +# CONFIG_SCSI_DEBUG is not set + +# +# PCMCIA SCSI adapter support +# +CONFIG_PCMCIA_AHA152X=m +CONFIG_PCMCIA_FDOMAIN=m +CONFIG_PCMCIA_NINJA_SCSI=m +CONFIG_PCMCIA_QLOGIC=m +CONFIG_PCMCIA_SYM53C500=m + +# +# Old CD-ROM drivers (not SCSI, not IDE) +# +# CONFIG_CD_NO_IDESCSI is not set + +# +# Multi-device support (RAID and LVM) +# +CONFIG_MD=y +CONFIG_BLK_DEV_MD=y +CONFIG_MD_LINEAR=m +CONFIG_MD_RAID0=m +CONFIG_MD_RAID1=m +CONFIG_MD_RAID10=m +CONFIG_MD_RAID5=m +CONFIG_MD_RAID6=m +CONFIG_MD_MULTIPATH=m +CONFIG_BLK_DEV_DM=m +CONFIG_DM_CRYPT=m +CONFIG_DM_SNAPSHOT=m +CONFIG_DM_MIRROR=m +CONFIG_DM_ZERO=m + +# +# Fusion MPT device support +# +CONFIG_FUSION=m +CONFIG_FUSION_MAX_SGE=40 +CONFIG_FUSION_CTL=m +CONFIG_FUSION_LAN=m + +# +# IEEE 1394 (FireWire) support +# +CONFIG_IEEE1394=m + +# +# Subsystem Options +# +# CONFIG_IEEE1394_VERBOSEDEBUG is not set +CONFIG_IEEE1394_OUI_DB=y +CONFIG_IEEE1394_EXTRA_CONFIG_ROMS=y +CONFIG_IEEE1394_CONFIG_ROM_IP1394=y + +# +# Device Drivers +# +CONFIG_IEEE1394_PCILYNX=m +CONFIG_IEEE1394_OHCI1394=m + +# +# Protocol Drivers +# +CONFIG_IEEE1394_VIDEO1394=m +CONFIG_IEEE1394_SBP2=m +# CONFIG_IEEE1394_SBP2_PHYS_DMA is not set +CONFIG_IEEE1394_ETH1394=m +CONFIG_IEEE1394_DV1394=m +CONFIG_IEEE1394_RAWIO=m +CONFIG_IEEE1394_CMP=m +CONFIG_IEEE1394_AMDTP=m + +# +# I2O device support +# +CONFIG_I2O=m +CONFIG_I2O_CONFIG=m +CONFIG_I2O_BLOCK=m +CONFIG_I2O_SCSI=m +CONFIG_I2O_PROC=m + +# +# Networking support +# +CONFIG_NET=y + +# +# Networking options +# +CONFIG_PACKET=y +CONFIG_PACKET_MMAP=y +CONFIG_NETLINK_DEV=y +CONFIG_UNIX=y +CONFIG_NET_KEY=m +CONFIG_INET=y +CONFIG_IP_MULTICAST=y +CONFIG_IP_ADVANCED_ROUTER=y +CONFIG_IP_MULTIPLE_TABLES=y +CONFIG_IP_ROUTE_FWMARK=y +CONFIG_IP_ROUTE_MULTIPATH=y +CONFIG_IP_ROUTE_VERBOSE=y +# CONFIG_IP_PNP is not set +CONFIG_NET_IPIP=m +CONFIG_NET_IPGRE=m +CONFIG_NET_IPGRE_BROADCAST=y +CONFIG_IP_MROUTE=y +CONFIG_IP_PIMSM_V1=y +CONFIG_IP_PIMSM_V2=y +# CONFIG_ARPD is not set +CONFIG_SYN_COOKIES=y +CONFIG_INET_AH=m +CONFIG_INET_ESP=m +CONFIG_INET_IPCOMP=m +CONFIG_INET_TUNNEL=m + +# +# IP: Virtual Server Configuration +# +CONFIG_IP_VS=m +# CONFIG_IP_VS_DEBUG is not set +CONFIG_IP_VS_TAB_BITS=12 + +# +# IPVS transport protocol load balancing support +# +CONFIG_IP_VS_PROTO_TCP=y +CONFIG_IP_VS_PROTO_UDP=y +CONFIG_IP_VS_PROTO_ESP=y +CONFIG_IP_VS_PROTO_AH=y + +# +# IPVS scheduler +# +CONFIG_IP_VS_RR=m +CONFIG_IP_VS_WRR=m +CONFIG_IP_VS_LC=m +CONFIG_IP_VS_WLC=m +CONFIG_IP_VS_LBLC=m +CONFIG_IP_VS_LBLCR=m +CONFIG_IP_VS_DH=m +CONFIG_IP_VS_SH=m +CONFIG_IP_VS_SED=m +CONFIG_IP_VS_NQ=m + +# +# IPVS application helper +# +CONFIG_IP_VS_FTP=m +CONFIG_IPV6=m +CONFIG_IPV6_PRIVACY=y +CONFIG_INET6_AH=m +CONFIG_INET6_ESP=m +CONFIG_INET6_IPCOMP=m +CONFIG_INET6_TUNNEL=m +CONFIG_IPV6_TUNNEL=m +CONFIG_NETFILTER=y +# CONFIG_NETFILTER_DEBUG is not set +CONFIG_BRIDGE_NETFILTER=y + +# +# IP: Netfilter Configuration +# +CONFIG_IP_NF_CONNTRACK=m +CONFIG_IP_NF_CT_ACCT=y +CONFIG_IP_NF_CT_PROTO_SCTP=m +CONFIG_IP_NF_FTP=m +CONFIG_IP_NF_IRC=m +CONFIG_IP_NF_TFTP=m +CONFIG_IP_NF_AMANDA=m +CONFIG_IP_NF_QUEUE=m +CONFIG_IP_NF_IPTABLES=m +CONFIG_IP_NF_MATCH_LIMIT=m +CONFIG_IP_NF_MATCH_IPRANGE=m +CONFIG_IP_NF_MATCH_MAC=m +CONFIG_IP_NF_MATCH_PKTTYPE=m +CONFIG_IP_NF_MATCH_MARK=m +CONFIG_IP_NF_MATCH_MULTIPORT=m +CONFIG_IP_NF_MATCH_TOS=m +CONFIG_IP_NF_MATCH_RECENT=m +CONFIG_IP_NF_MATCH_ECN=m +CONFIG_IP_NF_MATCH_DSCP=m +CONFIG_IP_NF_MATCH_AH_ESP=m +CONFIG_IP_NF_MATCH_LENGTH=m +CONFIG_IP_NF_MATCH_TTL=m +CONFIG_IP_NF_MATCH_TCPMSS=m +CONFIG_IP_NF_MATCH_HELPER=m +CONFIG_IP_NF_MATCH_STATE=m +CONFIG_IP_NF_MATCH_CONNTRACK=m +CONFIG_IP_NF_MATCH_OWNER=m +CONFIG_IP_NF_MATCH_PHYSDEV=m +CONFIG_IP_NF_MATCH_ADDRTYPE=m +CONFIG_IP_NF_MATCH_REALM=m +CONFIG_IP_NF_MATCH_SCTP=m +CONFIG_IP_NF_MATCH_COMMENT=m +CONFIG_IP_NF_FILTER=m +CONFIG_IP_NF_TARGET_REJECT=m +CONFIG_IP_NF_TARGET_LOG=m +CONFIG_IP_NF_TARGET_ULOG=m +CONFIG_IP_NF_TARGET_TCPMSS=m +CONFIG_IP_NF_NAT=m +CONFIG_IP_NF_NAT_NEEDED=y +CONFIG_IP_NF_TARGET_MASQUERADE=m +CONFIG_IP_NF_TARGET_REDIRECT=m +CONFIG_IP_NF_TARGET_NETMAP=m +CONFIG_IP_NF_TARGET_SAME=m +CONFIG_IP_NF_NAT_LOCAL=y +CONFIG_IP_NF_NAT_SNMP_BASIC=m +CONFIG_IP_NF_NAT_IRC=m +CONFIG_IP_NF_NAT_FTP=m +CONFIG_IP_NF_NAT_TFTP=m +CONFIG_IP_NF_NAT_AMANDA=m +CONFIG_IP_NF_MANGLE=m +CONFIG_IP_NF_TARGET_TOS=m +CONFIG_IP_NF_TARGET_ECN=m +CONFIG_IP_NF_TARGET_DSCP=m +CONFIG_IP_NF_TARGET_MARK=m +CONFIG_IP_NF_TARGET_CLASSIFY=m +CONFIG_IP_NF_RAW=m +CONFIG_IP_NF_TARGET_NOTRACK=m +CONFIG_IP_NF_ARPTABLES=m +CONFIG_IP_NF_ARPFILTER=m +CONFIG_IP_NF_ARP_MANGLE=m +# CONFIG_IP_NF_COMPAT_IPCHAINS is not set +# CONFIG_IP_NF_COMPAT_IPFWADM is not set + +# +# IPv6: Netfilter Configuration +# +# CONFIG_IP6_NF_QUEUE is not set +CONFIG_IP6_NF_IPTABLES=m +CONFIG_IP6_NF_MATCH_LIMIT=m +CONFIG_IP6_NF_MATCH_MAC=m +CONFIG_IP6_NF_MATCH_RT=m +CONFIG_IP6_NF_MATCH_OPTS=m +CONFIG_IP6_NF_MATCH_FRAG=m +CONFIG_IP6_NF_MATCH_HL=m +CONFIG_IP6_NF_MATCH_MULTIPORT=m +CONFIG_IP6_NF_MATCH_OWNER=m +CONFIG_IP6_NF_MATCH_MARK=m +CONFIG_IP6_NF_MATCH_IPV6HEADER=m +CONFIG_IP6_NF_MATCH_AHESP=m +CONFIG_IP6_NF_MATCH_LENGTH=m +CONFIG_IP6_NF_MATCH_EUI64=m +CONFIG_IP6_NF_MATCH_PHYSDEV=m +CONFIG_IP6_NF_FILTER=m +CONFIG_IP6_NF_TARGET_LOG=m +CONFIG_IP6_NF_MANGLE=m +CONFIG_IP6_NF_TARGET_MARK=m +CONFIG_IP6_NF_RAW=m + +# +# Bridge: Netfilter Configuration +# +CONFIG_BRIDGE_NF_EBTABLES=m +CONFIG_BRIDGE_EBT_BROUTE=m +CONFIG_BRIDGE_EBT_T_FILTER=m +CONFIG_BRIDGE_EBT_T_NAT=m +CONFIG_BRIDGE_EBT_802_3=m +CONFIG_BRIDGE_EBT_AMONG=m +CONFIG_BRIDGE_EBT_ARP=m +CONFIG_BRIDGE_EBT_IP=m +CONFIG_BRIDGE_EBT_LIMIT=m +CONFIG_BRIDGE_EBT_MARK=m +CONFIG_BRIDGE_EBT_PKTTYPE=m +CONFIG_BRIDGE_EBT_STP=m +CONFIG_BRIDGE_EBT_VLAN=m +CONFIG_BRIDGE_EBT_ARPREPLY=m +CONFIG_BRIDGE_EBT_DNAT=m +CONFIG_BRIDGE_EBT_MARK_T=m +CONFIG_BRIDGE_EBT_REDIRECT=m +CONFIG_BRIDGE_EBT_SNAT=m +CONFIG_BRIDGE_EBT_LOG=m +CONFIG_XFRM=y +CONFIG_XFRM_USER=y + +# +# SCTP Configuration (EXPERIMENTAL) +# +CONFIG_IP_SCTP=m +# CONFIG_SCTP_DBG_MSG is not set +# CONFIG_SCTP_DBG_OBJCNT is not set +# CONFIG_SCTP_HMAC_NONE is not set +# CONFIG_SCTP_HMAC_SHA1 is not set +CONFIG_SCTP_HMAC_MD5=y +CONFIG_ATM=m +CONFIG_ATM_CLIP=m +# CONFIG_ATM_CLIP_NO_ICMP is not set +CONFIG_ATM_LANE=m +# CONFIG_ATM_MPOA is not set +CONFIG_ATM_BR2684=m +# CONFIG_ATM_BR2684_IPFILTER is not set +CONFIG_BRIDGE=m +CONFIG_VLAN_8021Q=m +# CONFIG_DECNET is not set +CONFIG_LLC=y +# CONFIG_LLC2 is not set +CONFIG_IPX=m +# CONFIG_IPX_INTERN is not set +CONFIG_ATALK=m +CONFIG_DEV_APPLETALK=y +CONFIG_LTPC=m +CONFIG_COPS=m +CONFIG_COPS_DAYNA=y +CONFIG_COPS_TANGENT=y +CONFIG_IPDDP=m +CONFIG_IPDDP_ENCAP=y +CONFIG_IPDDP_DECAP=y +# CONFIG_X25 is not set +# CONFIG_LAPB is not set +CONFIG_NET_DIVERT=y +# CONFIG_ECONET is not set +CONFIG_WAN_ROUTER=m + +# +# QoS and/or fair queueing +# +CONFIG_NET_SCHED=y +CONFIG_NET_SCH_CLK_JIFFIES=y +# CONFIG_NET_SCH_CLK_GETTIMEOFDAY is not set +# CONFIG_NET_SCH_CLK_CPU is not set +CONFIG_NET_SCH_CBQ=m +CONFIG_NET_SCH_HTB=m +CONFIG_NET_SCH_HFSC=m +CONFIG_NET_SCH_ATM=m +CONFIG_NET_SCH_PRIO=m +CONFIG_NET_SCH_RED=m +CONFIG_NET_SCH_SFQ=m +CONFIG_NET_SCH_TEQL=m +CONFIG_NET_SCH_TBF=m +CONFIG_NET_SCH_GRED=m +CONFIG_NET_SCH_DSMARK=m +CONFIG_NET_SCH_NETEM=m +CONFIG_NET_SCH_INGRESS=m +CONFIG_NET_QOS=y +CONFIG_NET_ESTIMATOR=y +CONFIG_NET_CLS=y +CONFIG_NET_CLS_TCINDEX=m +CONFIG_NET_CLS_ROUTE4=m +CONFIG_NET_CLS_ROUTE=y +CONFIG_NET_CLS_FW=m +CONFIG_NET_CLS_U32=m +CONFIG_CLS_U32_PERF=y +CONFIG_NET_CLS_IND=y +CONFIG_NET_CLS_RSVP=m +CONFIG_NET_CLS_RSVP6=m +# CONFIG_NET_CLS_ACT is not set +CONFIG_NET_CLS_POLICE=y + +# +# Network testing +# +# CONFIG_NET_PKTGEN is not set +CONFIG_NETPOLL=y +# CONFIG_NETPOLL_RX is not set +CONFIG_NETPOLL_TRAP=y +CONFIG_NET_POLL_CONTROLLER=y +# CONFIG_HAMRADIO is not set +CONFIG_IRDA=m + +# +# IrDA protocols +# +CONFIG_IRLAN=m +CONFIG_IRNET=m +CONFIG_IRCOMM=m +# CONFIG_IRDA_ULTRA is not set + +# +# IrDA options +# +CONFIG_IRDA_CACHE_LAST_LSAP=y +CONFIG_IRDA_FAST_RR=y +# CONFIG_IRDA_DEBUG is not set + +# +# Infrared-port device drivers +# + +# +# SIR device drivers +# +CONFIG_IRTTY_SIR=m + +# +# Dongle support +# +CONFIG_DONGLE=y +CONFIG_ESI_DONGLE=m +CONFIG_ACTISYS_DONGLE=m +CONFIG_TEKRAM_DONGLE=m +CONFIG_LITELINK_DONGLE=m +CONFIG_MA600_DONGLE=m +CONFIG_GIRBIL_DONGLE=m +CONFIG_MCP2120_DONGLE=m +CONFIG_OLD_BELKIN_DONGLE=m +CONFIG_ACT200L_DONGLE=m + +# +# Old SIR device drivers +# + +# +# Old Serial dongle support +# + +# +# FIR device drivers +# +CONFIG_USB_IRDA=m +CONFIG_SIGMATEL_FIR=m +CONFIG_NSC_FIR=m +# CONFIG_WINBOND_FIR is not set +# CONFIG_TOSHIBA_FIR is not set +# CONFIG_SMC_IRCC_FIR is not set +# CONFIG_ALI_FIR is not set +# CONFIG_VLSI_FIR is not set +# CONFIG_VIA_FIR is not set +CONFIG_BT=m +CONFIG_BT_L2CAP=m +CONFIG_BT_SCO=m +CONFIG_BT_RFCOMM=m +CONFIG_BT_RFCOMM_TTY=y +CONFIG_BT_BNEP=m +CONFIG_BT_BNEP_MC_FILTER=y +CONFIG_BT_BNEP_PROTO_FILTER=y +CONFIG_BT_CMTP=m +CONFIG_BT_HIDP=m + +# +# Bluetooth device drivers +# +CONFIG_BT_HCIUSB=m +CONFIG_BT_HCIUSB_SCO=y +CONFIG_BT_HCIUART=m +CONFIG_BT_HCIUART_H4=y +CONFIG_BT_HCIUART_BCSP=y +CONFIG_BT_HCIUART_BCSP_TXCRC=y +CONFIG_BT_HCIBCM203X=m +CONFIG_BT_HCIBFUSB=m +CONFIG_BT_HCIDTL1=m +CONFIG_BT_HCIBT3C=m +CONFIG_BT_HCIBLUECARD=m +CONFIG_BT_HCIBTUART=m +CONFIG_BT_HCIVHCI=m +CONFIG_TUX=m + +# +# TUX options +# +CONFIG_TUX_EXTCGI=y +# CONFIG_TUX_EXTENDED_LOG is not set +# CONFIG_TUX_DEBUG is not set +CONFIG_NETDEVICES=y +CONFIG_DUMMY=m +CONFIG_BONDING=m +CONFIG_EQUALIZER=m +CONFIG_TUN=m +CONFIG_ETHERTAP=m +CONFIG_NET_SB1000=m + +# +# ARCnet devices +# +# CONFIG_ARCNET is not set + +# +# Ethernet (10 or 100Mbit) +# +CONFIG_NET_ETHERNET=y +CONFIG_MII=m +CONFIG_HAPPYMEAL=m +CONFIG_SUNGEM=m +CONFIG_NET_VENDOR_3COM=y +CONFIG_EL1=m +CONFIG_EL2=m +CONFIG_ELPLUS=m +CONFIG_EL16=m +CONFIG_EL3=m +CONFIG_3C515=m +CONFIG_VORTEX=m +CONFIG_TYPHOON=m +CONFIG_LANCE=m +CONFIG_NET_VENDOR_SMC=y +CONFIG_WD80x3=m +CONFIG_ULTRA=m +CONFIG_SMC9194=m +CONFIG_NET_VENDOR_RACAL=y +CONFIG_NI52=m +CONFIG_NI65=m + +# +# Tulip family network device support +# +CONFIG_NET_TULIP=y +CONFIG_DE2104X=m +CONFIG_TULIP=m +# CONFIG_TULIP_MWI is not set +CONFIG_TULIP_MMIO=y +# CONFIG_TULIP_NAPI is not set +CONFIG_DE4X5=m +CONFIG_WINBOND_840=m +CONFIG_DM9102=m +CONFIG_PCMCIA_XIRCOM=m +# CONFIG_AT1700 is not set +CONFIG_DEPCA=m +CONFIG_HP100=m +CONFIG_NET_ISA=y +CONFIG_E2100=m +CONFIG_EWRK3=m +CONFIG_EEXPRESS=m +CONFIG_EEXPRESS_PRO=m +CONFIG_HPLAN_PLUS=m +CONFIG_HPLAN=m +CONFIG_LP486E=m +CONFIG_ETH16I=m +CONFIG_NE2000=m +CONFIG_ZNET=m +CONFIG_SEEQ8005=m +CONFIG_NET_PCI=y +CONFIG_PCNET32=m +CONFIG_AMD8111_ETH=m +CONFIG_AMD8111E_NAPI=y +CONFIG_ADAPTEC_STARFIRE=m +CONFIG_ADAPTEC_STARFIRE_NAPI=y +CONFIG_AC3200=m +CONFIG_APRICOT=m +CONFIG_B44=m +CONFIG_FORCEDETH=m +CONFIG_CS89x0=m +CONFIG_DGRS=m +CONFIG_EEPRO100=m +# CONFIG_EEPRO100_PIO is not set +CONFIG_E100=m +CONFIG_E100_NAPI=y +CONFIG_FEALNX=m +CONFIG_NATSEMI=m +CONFIG_NE2K_PCI=m +CONFIG_8139CP=m +CONFIG_8139TOO=m +CONFIG_8139TOO_PIO=y +# CONFIG_8139TOO_TUNE_TWISTER is not set +CONFIG_8139TOO_8129=y +# CONFIG_8139_OLD_RX_RESET is not set +CONFIG_SIS900=m +CONFIG_EPIC100=m +CONFIG_SUNDANCE=m +# CONFIG_SUNDANCE_MMIO is not set +CONFIG_TLAN=m +CONFIG_VIA_RHINE=m +CONFIG_VIA_RHINE_MMIO=y +CONFIG_NET_POCKET=y +CONFIG_ATP=m +CONFIG_DE600=m +CONFIG_DE620=m + +# +# Ethernet (1000 Mbit) +# +CONFIG_ACENIC=m +# CONFIG_ACENIC_OMIT_TIGON_I is not set +CONFIG_DL2K=m +CONFIG_E1000=m +CONFIG_E1000_NAPI=y +CONFIG_NS83820=m +CONFIG_HAMACHI=m +CONFIG_YELLOWFIN=m +CONFIG_R8169=m +CONFIG_R8169_NAPI=y +CONFIG_SK98LIN=m +CONFIG_VIA_VELOCITY=m +CONFIG_TIGON3=m + +# +# Ethernet (10000 Mbit) +# +CONFIG_IXGB=m +CONFIG_IXGB_NAPI=y +CONFIG_S2IO=m +CONFIG_S2IO_NAPI=y + +# +# Token Ring devices +# +CONFIG_TR=y +CONFIG_IBMTR=m +CONFIG_IBMOL=m +CONFIG_IBMLS=m +CONFIG_3C359=m +CONFIG_TMS380TR=m +CONFIG_TMSPCI=m +CONFIG_SKISA=m +CONFIG_PROTEON=m +CONFIG_ABYSS=m +CONFIG_SMCTR=m + +# +# Wireless LAN (non-hamradio) +# +CONFIG_NET_RADIO=y + +# +# Obsolete Wireless cards support (pre-802.11) +# +# CONFIG_STRIP is not set +# CONFIG_ARLAN is not set +CONFIG_WAVELAN=m +CONFIG_PCMCIA_WAVELAN=m +CONFIG_PCMCIA_NETWAVE=m + +# +# Wireless 802.11 Frequency Hopping cards support +# +# CONFIG_PCMCIA_RAYCS is not set + +# +# Wireless 802.11b ISA/PCI cards support +# +CONFIG_IEEE80211=m +# CONFIG_IEEE80211_DEBUG is not set +CONFIG_IEEE80211_CRYPT=m +CONFIG_IEEE80211_WPA=m +CONFIG_IEEE80211_CRYPT_CCMP=m +CONFIG_IEEE80211_CRYPT_TKIP=m +CONFIG_IPW2100=m +# CONFIG_IPW_DEBUG is not set +CONFIG_IPW2100_PROMISC=y +# CONFIG_IPW2100_LEGACY_FW_LOAD is not set +CONFIG_IPW2200=m +CONFIG_AIRO=m +CONFIG_HERMES=m +CONFIG_PLX_HERMES=m +CONFIG_TMD_HERMES=m +CONFIG_PCI_HERMES=m +CONFIG_ATMEL=m +CONFIG_PCI_ATMEL=m + +# +# Wireless 802.11b Pcmcia/Cardbus cards support +# +CONFIG_PCMCIA_HERMES=m +CONFIG_AIRO_CS=m +CONFIG_PCMCIA_ATMEL=m +CONFIG_PCMCIA_WL3501=m + +# +# Prism GT/Duette 802.11(a/b/g) PCI/Cardbus support +# +CONFIG_PRISM54=m +CONFIG_NET_WIRELESS=y + +# +# PCMCIA network device support +# +CONFIG_NET_PCMCIA=y +CONFIG_PCMCIA_3C589=m +CONFIG_PCMCIA_3C574=m +CONFIG_PCMCIA_FMVJ18X=m +CONFIG_PCMCIA_PCNET=m +CONFIG_PCMCIA_NMCLAN=m +CONFIG_PCMCIA_SMC91C92=m +CONFIG_PCMCIA_XIRC2PS=m +CONFIG_PCMCIA_AXNET=m +CONFIG_PCMCIA_IBMTR=m + +# +# Wan interfaces +# +# CONFIG_WAN is not set + +# +# ATM drivers +# +CONFIG_ATM_TCP=m +CONFIG_ATM_LANAI=m +CONFIG_ATM_ENI=m +# CONFIG_ATM_ENI_DEBUG is not set +# CONFIG_ATM_ENI_TUNE_BURST is not set +CONFIG_ATM_FIRESTREAM=m +# CONFIG_ATM_ZATM is not set +CONFIG_ATM_NICSTAR=m +# CONFIG_ATM_NICSTAR_USE_SUNI is not set +# CONFIG_ATM_NICSTAR_USE_IDT77105 is not set +CONFIG_ATM_IDT77252=m +# CONFIG_ATM_IDT77252_DEBUG is not set +# CONFIG_ATM_IDT77252_RCV_ALL is not set +CONFIG_ATM_IDT77252_USE_SUNI=y +CONFIG_ATM_AMBASSADOR=m +# CONFIG_ATM_AMBASSADOR_DEBUG is not set +CONFIG_ATM_HORIZON=m +# CONFIG_ATM_HORIZON_DEBUG is not set +# CONFIG_ATM_IA is not set +CONFIG_ATM_FORE200E_MAYBE=m +# CONFIG_ATM_FORE200E_PCA is not set +CONFIG_ATM_HE=m +# CONFIG_ATM_HE_USE_SUNI is not set +CONFIG_FDDI=y +# CONFIG_DEFXX is not set +CONFIG_SKFP=m +# CONFIG_HIPPI is not set +CONFIG_PLIP=m +CONFIG_PPP=m +CONFIG_PPP_MULTILINK=y +CONFIG_PPP_FILTER=y +CONFIG_PPP_ASYNC=m +CONFIG_PPP_SYNC_TTY=m +CONFIG_PPP_DEFLATE=m +# CONFIG_PPP_BSDCOMP is not set +CONFIG_PPPOE=m +CONFIG_PPPOATM=m +CONFIG_SLIP=m +CONFIG_SLIP_COMPRESSED=y +CONFIG_SLIP_SMART=y +# CONFIG_SLIP_MODE_SLIP6 is not set +CONFIG_NET_FC=y +# CONFIG_SHAPER is not set +CONFIG_NETCONSOLE=m + +# +# ISDN subsystem +# +CONFIG_ISDN=m + +# +# Old ISDN4Linux +# +CONFIG_ISDN_I4L=m +CONFIG_ISDN_PPP=y +CONFIG_ISDN_PPP_VJ=y +CONFIG_ISDN_MPP=y +CONFIG_IPPP_FILTER=y +# CONFIG_ISDN_PPP_BSDCOMP is not set +CONFIG_ISDN_AUDIO=y +CONFIG_ISDN_TTY_FAX=y + +# +# ISDN feature submodules +# + +# +# ISDN4Linux hardware drivers +# + +# +# Passive cards +# +CONFIG_ISDN_DRV_HISAX=m + +# +# D-channel protocol features +# +CONFIG_HISAX_EURO=y +CONFIG_DE_AOC=y +CONFIG_HISAX_NO_SENDCOMPLETE=y +CONFIG_HISAX_NO_LLC=y +CONFIG_HISAX_NO_KEYPAD=y +CONFIG_HISAX_1TR6=y +CONFIG_HISAX_NI1=y +CONFIG_HISAX_MAX_CARDS=8 + +# +# HiSax supported cards +# +CONFIG_HISAX_16_0=y +CONFIG_HISAX_16_3=y +CONFIG_HISAX_TELESPCI=y +CONFIG_HISAX_S0BOX=y +CONFIG_HISAX_AVM_A1=y +CONFIG_HISAX_FRITZPCI=y +CONFIG_HISAX_AVM_A1_PCMCIA=y +CONFIG_HISAX_ELSA=y +CONFIG_HISAX_IX1MICROR2=y +CONFIG_HISAX_DIEHLDIVA=y +CONFIG_HISAX_ASUSCOM=y +CONFIG_HISAX_TELEINT=y +CONFIG_HISAX_HFCS=y +CONFIG_HISAX_SEDLBAUER=y +CONFIG_HISAX_SPORTSTER=y +CONFIG_HISAX_MIC=y +CONFIG_HISAX_NETJET=y +CONFIG_HISAX_NETJET_U=y +CONFIG_HISAX_NICCY=y +CONFIG_HISAX_ISURF=y +CONFIG_HISAX_HSTSAPHIR=y +CONFIG_HISAX_BKM_A4T=y +CONFIG_HISAX_SCT_QUADRO=y +CONFIG_HISAX_GAZEL=y +CONFIG_HISAX_HFC_PCI=y +CONFIG_HISAX_W6692=y +CONFIG_HISAX_HFC_SX=y +CONFIG_HISAX_ENTERNOW_PCI=y +# CONFIG_HISAX_DEBUG is not set + +# +# HiSax PCMCIA card service modules +# +CONFIG_HISAX_SEDLBAUER_CS=m +CONFIG_HISAX_ELSA_CS=m +CONFIG_HISAX_AVM_A1_CS=m +CONFIG_HISAX_TELES_CS=m + +# +# HiSax sub driver modules +# +CONFIG_HISAX_ST5481=m +CONFIG_HISAX_HFCUSB=m +CONFIG_HISAX_FRITZ_PCIPNP=m +CONFIG_HISAX_HDLC=y + +# +# Active cards +# +CONFIG_ISDN_DRV_ICN=m +CONFIG_ISDN_DRV_PCBIT=m +CONFIG_ISDN_DRV_SC=m +CONFIG_ISDN_DRV_ACT2000=m +CONFIG_ISDN_DRV_TPAM=m + +# +# CAPI subsystem +# +CONFIG_ISDN_CAPI=m +CONFIG_ISDN_DRV_AVMB1_VERBOSE_REASON=y +CONFIG_ISDN_CAPI_MIDDLEWARE=y +CONFIG_ISDN_CAPI_CAPI20=m +CONFIG_ISDN_CAPI_CAPIFS_BOOL=y +CONFIG_ISDN_CAPI_CAPIFS=m +CONFIG_ISDN_CAPI_CAPIDRV=m + +# +# CAPI hardware drivers +# + +# +# Active AVM cards +# +CONFIG_CAPI_AVM=y + +# +# Active Eicon DIVA Server cards +# +CONFIG_CAPI_EICON=y +CONFIG_ISDN_DIVAS=m +CONFIG_ISDN_DIVAS_BRIPCI=y +CONFIG_ISDN_DIVAS_PRIPCI=y +CONFIG_ISDN_DIVAS_DIVACAPI=m +CONFIG_ISDN_DIVAS_USERIDI=m +CONFIG_ISDN_DIVAS_MAINT=m + +# +# Telephony Support +# +# CONFIG_PHONE is not set + +# +# Input device support +# +CONFIG_INPUT=y + +# +# 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=m +# CONFIG_INPUT_TSDEV is not set +CONFIG_INPUT_EVDEV=y +# CONFIG_INPUT_EVBUG is not set + +# +# Input I/O drivers +# +CONFIG_GAMEPORT=m +CONFIG_SOUND_GAMEPORT=m +CONFIG_GAMEPORT_NS558=m +CONFIG_GAMEPORT_L4=m +CONFIG_GAMEPORT_EMU10K1=m +CONFIG_GAMEPORT_VORTEX=m +CONFIG_GAMEPORT_FM801=m +CONFIG_GAMEPORT_CS461x=m +CONFIG_SERIO=y +CONFIG_SERIO_I8042=y +CONFIG_SERIO_SERPORT=y +# CONFIG_SERIO_CT82C710 is not set +# CONFIG_SERIO_PARKBD is not set +# CONFIG_SERIO_PCIPS2 is not set +# CONFIG_SERIO_RAW 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_INPUT_MOUSE=y +CONFIG_MOUSE_PS2=y +CONFIG_MOUSE_SERIAL=m +CONFIG_MOUSE_INPORT=m +CONFIG_MOUSE_ATIXL=y +CONFIG_MOUSE_LOGIBM=m +CONFIG_MOUSE_PC110PAD=m +CONFIG_MOUSE_VSXXXAA=m +CONFIG_INPUT_JOYSTICK=y +CONFIG_JOYSTICK_ANALOG=m +CONFIG_JOYSTICK_A3D=m +CONFIG_JOYSTICK_ADI=m +CONFIG_JOYSTICK_COBRA=m +CONFIG_JOYSTICK_GF2K=m +CONFIG_JOYSTICK_GRIP=m +CONFIG_JOYSTICK_GRIP_MP=m +CONFIG_JOYSTICK_GUILLEMOT=m +CONFIG_JOYSTICK_INTERACT=m +CONFIG_JOYSTICK_SIDEWINDER=m +CONFIG_JOYSTICK_TMDC=m +CONFIG_JOYSTICK_IFORCE=m +CONFIG_JOYSTICK_IFORCE_USB=y +CONFIG_JOYSTICK_IFORCE_232=y +CONFIG_JOYSTICK_WARRIOR=m +CONFIG_JOYSTICK_MAGELLAN=m +CONFIG_JOYSTICK_SPACEORB=m +CONFIG_JOYSTICK_SPACEBALL=m +CONFIG_JOYSTICK_STINGER=m +CONFIG_JOYSTICK_TWIDDLER=m +CONFIG_JOYSTICK_DB9=m +CONFIG_JOYSTICK_GAMECON=m +CONFIG_JOYSTICK_TURBOGRAFX=m +CONFIG_JOYSTICK_JOYDUMP=m +CONFIG_INPUT_TOUCHSCREEN=y +CONFIG_TOUCHSCREEN_GUNZE=m +CONFIG_INPUT_MISC=y +CONFIG_INPUT_PCSPKR=m +CONFIG_INPUT_UINPUT=m + +# +# Character devices +# +CONFIG_VT=y +CONFIG_VT_CONSOLE=y +CONFIG_HW_CONSOLE=y +CONFIG_SERIAL_NONSTANDARD=y +CONFIG_ROCKETPORT=m +# CONFIG_CYCLADES is not set +CONFIG_SYNCLINK=m +CONFIG_SYNCLINKMP=m +CONFIG_N_HDLC=m +CONFIG_STALDRV=y + +# +# Serial drivers +# +CONFIG_SERIAL_8250=y +CONFIG_SERIAL_8250_CONSOLE=y +CONFIG_SERIAL_8250_CS=m +# CONFIG_SERIAL_8250_ACPI is not set +CONFIG_SERIAL_8250_NR_UARTS=32 +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_MULTIPORT=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_CRASH=m +CONFIG_PRINTER=m +CONFIG_LP_CONSOLE=y +CONFIG_PPDEV=m +CONFIG_TIPAR=m + +# +# IPMI +# +CONFIG_IPMI_HANDLER=m +# CONFIG_IPMI_PANIC_EVENT is not set +CONFIG_IPMI_DEVICE_INTERFACE=m +CONFIG_IPMI_SI=m +CONFIG_IPMI_WATCHDOG=m +CONFIG_IPMI_POWEROFF=m + +# +# Watchdog Cards +# +CONFIG_WATCHDOG=y +# CONFIG_WATCHDOG_NOWAYOUT is not set + +# +# Watchdog Device Drivers +# +CONFIG_SOFT_WATCHDOG=m +CONFIG_ACQUIRE_WDT=m +CONFIG_ADVANTECH_WDT=m +CONFIG_ALIM1535_WDT=m +CONFIG_ALIM7101_WDT=m +CONFIG_SC520_WDT=m +CONFIG_EUROTECH_WDT=m +CONFIG_IB700_WDT=m +CONFIG_WAFER_WDT=m +CONFIG_I8XX_TCO=m +CONFIG_SC1200_WDT=m +# CONFIG_SCx200_WDT is not set +# CONFIG_60XX_WDT is not set +CONFIG_CPU5_WDT=m +CONFIG_W83627HF_WDT=m +CONFIG_W83877F_WDT=m +CONFIG_MACHZ_WDT=m + +# +# ISA-based Watchdog Cards +# +CONFIG_PCWATCHDOG=m +# CONFIG_MIXCOMWD is not set +CONFIG_WDT=m +# CONFIG_WDT_501 is not set + +# +# PCI-based Watchdog Cards +# +CONFIG_PCIPCWATCHDOG=m +CONFIG_WDTPCI=m +CONFIG_WDT_501_PCI=y + +# +# USB-based Watchdog Cards +# +CONFIG_USBPCWATCHDOG=m +CONFIG_HW_RANDOM=m +CONFIG_NVRAM=m +CONFIG_RTC=y +CONFIG_DTLK=m +CONFIG_R3964=m +# CONFIG_APPLICOM is not set +CONFIG_SONYPI=m + +# +# Ftape, the floppy tape device driver +# +CONFIG_AGP=y +CONFIG_AGP_ALI=y +CONFIG_AGP_ATI=y +CONFIG_AGP_AMD=y +CONFIG_AGP_AMD64=y +CONFIG_AGP_INTEL=y +CONFIG_AGP_INTEL_MCH=y +CONFIG_AGP_NVIDIA=y +CONFIG_AGP_SIS=y +CONFIG_AGP_SWORKS=y +CONFIG_AGP_VIA=y +CONFIG_AGP_EFFICEON=y +CONFIG_DRM=y +CONFIG_DRM_TDFX=m +CONFIG_DRM_R128=m +CONFIG_DRM_RADEON=m +CONFIG_DRM_I810=m +CONFIG_DRM_I830=m +CONFIG_DRM_I915=m +CONFIG_DRM_MGA=m +CONFIG_DRM_SIS=m + +# +# PCMCIA character devices +# +CONFIG_SYNCLINK_CS=m +CONFIG_MWAVE=m +CONFIG_RAW_DRIVER=m +# CONFIG_HPET is not set +CONFIG_MAX_RAW_DEVS=8192 +CONFIG_HANGCHECK_TIMER=m + +# +# I2C support +# +CONFIG_I2C=m +CONFIG_I2C_CHARDEV=m + +# +# I2C Algorithms +# +CONFIG_I2C_ALGOBIT=m +CONFIG_I2C_ALGOPCF=m +CONFIG_I2C_ALGOPCA=m + +# +# I2C Hardware Bus support +# +CONFIG_I2C_ALI1535=m +CONFIG_I2C_ALI1563=m +CONFIG_I2C_ALI15X3=m +CONFIG_I2C_AMD756=m +CONFIG_I2C_AMD8111=m +CONFIG_I2C_I801=m +CONFIG_I2C_I810=m +CONFIG_I2C_ISA=m +CONFIG_I2C_NFORCE2=m +# CONFIG_I2C_PARPORT is not set +# CONFIG_I2C_PARPORT_LIGHT is not set +CONFIG_I2C_PIIX4=m +CONFIG_I2C_PROSAVAGE=m +CONFIG_I2C_SAVAGE4=m +# CONFIG_SCx200_ACB is not set +CONFIG_I2C_SIS5595=m +CONFIG_I2C_SIS630=m +CONFIG_I2C_SIS96X=m +CONFIG_I2C_VIA=m +CONFIG_I2C_VIAPRO=m +CONFIG_I2C_VOODOO3=m +CONFIG_I2C_PCA_ISA=m + +# +# Hardware Sensors Chip support +# +CONFIG_I2C_SENSOR=m +CONFIG_SENSORS_ADM1021=m +CONFIG_SENSORS_ADM1025=m +CONFIG_SENSORS_ADM1031=m +CONFIG_SENSORS_ASB100=m +CONFIG_SENSORS_DS1621=m +CONFIG_SENSORS_FSCHER=m +CONFIG_SENSORS_GL518SM=m +CONFIG_SENSORS_IT87=m +CONFIG_SENSORS_LM75=m +CONFIG_SENSORS_LM77=m +CONFIG_SENSORS_LM78=m +CONFIG_SENSORS_LM80=m +CONFIG_SENSORS_LM83=m +CONFIG_SENSORS_LM85=m +CONFIG_SENSORS_LM90=m +CONFIG_SENSORS_MAX1619=m +CONFIG_SENSORS_SMSC47M1=m +CONFIG_SENSORS_VIA686A=m +CONFIG_SENSORS_W83781D=m +CONFIG_SENSORS_W83L785TS=m +CONFIG_SENSORS_W83627HF=m + +# +# Other I2C Chip support +# +CONFIG_SENSORS_EEPROM=m +CONFIG_SENSORS_PCF8574=m +CONFIG_SENSORS_PCF8591=m +CONFIG_SENSORS_RTC8564=m +# 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 + +# +# Dallas's 1-wire bus +# +# CONFIG_W1 is not set + +# +# Misc devices +# +CONFIG_IBM_ASM=m + +# +# Multimedia devices +# +CONFIG_VIDEO_DEV=m + +# +# Video For Linux +# + +# +# Video Adapters +# +CONFIG_VIDEO_BT848=m +CONFIG_VIDEO_PMS=m +CONFIG_VIDEO_BWQCAM=m +CONFIG_VIDEO_CQCAM=m +CONFIG_VIDEO_W9966=m +CONFIG_VIDEO_CPIA=m +CONFIG_VIDEO_CPIA_PP=m +CONFIG_VIDEO_CPIA_USB=m +CONFIG_VIDEO_SAA5246A=m +CONFIG_VIDEO_SAA5249=m +CONFIG_TUNER_3036=m +CONFIG_VIDEO_STRADIS=m +CONFIG_VIDEO_ZORAN=m +CONFIG_VIDEO_ZORAN_BUZ=m +CONFIG_VIDEO_ZORAN_DC10=m +CONFIG_VIDEO_ZORAN_DC30=m +CONFIG_VIDEO_ZORAN_LML33=m +CONFIG_VIDEO_ZORAN_LML33R10=m +CONFIG_VIDEO_MEYE=m +CONFIG_VIDEO_SAA7134=m +CONFIG_VIDEO_MXB=m +CONFIG_VIDEO_DPC=m +CONFIG_VIDEO_HEXIUM_ORION=m +CONFIG_VIDEO_HEXIUM_GEMINI=m +CONFIG_VIDEO_CX88=m +CONFIG_VIDEO_OVCAMCHIP=m + +# +# Radio Adapters +# +CONFIG_RADIO_CADET=m +CONFIG_RADIO_RTRACK=m +CONFIG_RADIO_RTRACK2=m +CONFIG_RADIO_AZTECH=m +CONFIG_RADIO_GEMTEK=m +CONFIG_RADIO_GEMTEK_PCI=m +CONFIG_RADIO_MAXIRADIO=m +CONFIG_RADIO_MAESTRO=m +CONFIG_RADIO_SF16FMI=m +CONFIG_RADIO_SF16FMR2=m +CONFIG_RADIO_TERRATEC=m +CONFIG_RADIO_TRUST=m +CONFIG_RADIO_TYPHOON=m +CONFIG_RADIO_TYPHOON_PROC_FS=y +CONFIG_RADIO_ZOLTRIX=m + +# +# Digital Video Broadcasting Devices +# +CONFIG_DVB=y +CONFIG_DVB_CORE=m + +# +# Supported Frontend Modules +# +CONFIG_DVB_TWINHAN_DST=m +CONFIG_DVB_STV0299=m +# CONFIG_DVB_SP887X is not set +# CONFIG_DVB_ALPS_TDLB7 is not set +CONFIG_DVB_ALPS_TDMB7=m +CONFIG_DVB_ATMEL_AT76C651=m +CONFIG_DVB_CX24110=m +CONFIG_DVB_GRUNDIG_29504_491=m +CONFIG_DVB_GRUNDIG_29504_401=m +CONFIG_DVB_MT312=m +CONFIG_DVB_VES1820=m +CONFIG_DVB_VES1X93=m +# CONFIG_DVB_TDA1004X is not set +CONFIG_DVB_NXT6000=m + +# +# Supported SAA7146 based PCI Adapters +# +CONFIG_DVB_AV7110=m +CONFIG_DVB_AV7110_OSD=y +CONFIG_DVB_BUDGET=m +CONFIG_DVB_BUDGET_CI=m +CONFIG_DVB_BUDGET_AV=m +CONFIG_DVB_BUDGET_PATCH=m + +# +# Supported USB Adapters +# +CONFIG_DVB_TTUSB_BUDGET=m +CONFIG_DVB_TTUSB_DEC=m + +# +# Supported FlexCopII (B2C2) Adapters +# +CONFIG_DVB_B2C2_SKYSTAR=m + +# +# Supported BT878 Adapters +# +CONFIG_DVB_BT8XX=m +CONFIG_VIDEO_SAA7146=m +CONFIG_VIDEO_SAA7146_VV=m +CONFIG_VIDEO_VIDEOBUF=m +CONFIG_VIDEO_TUNER=m +CONFIG_VIDEO_BUF=m +CONFIG_VIDEO_BTCX=m +CONFIG_VIDEO_IR=m + +# +# Graphics support +# +CONFIG_FB=y +CONFIG_FB_MODE_HELPERS=y +CONFIG_FB_CIRRUS=m +# CONFIG_FB_PM2 is not set +# CONFIG_FB_CYBER2000 is not set +# CONFIG_FB_ASILIANT is not set +# CONFIG_FB_IMSTT is not set +CONFIG_FB_VGA16=m +CONFIG_FB_VESA=y +CONFIG_VIDEO_SELECT=y +CONFIG_FB_HGA=m +CONFIG_FB_HGA_ACCEL=y +CONFIG_FB_RIVA=m +# CONFIG_FB_RIVA_I2C is not set +# CONFIG_FB_RIVA_DEBUG is not set +CONFIG_FB_I810=m +CONFIG_FB_I810_GTF=y +CONFIG_FB_MATROX=m +CONFIG_FB_MATROX_MILLENIUM=y +CONFIG_FB_MATROX_MYSTIQUE=y +CONFIG_FB_MATROX_G450=y +CONFIG_FB_MATROX_G100=y +CONFIG_FB_MATROX_I2C=m +CONFIG_FB_MATROX_MAVEN=m +CONFIG_FB_MATROX_MULTIHEAD=y +# CONFIG_FB_RADEON_OLD is not set +CONFIG_FB_RADEON=m +CONFIG_FB_RADEON_I2C=y +# CONFIG_FB_RADEON_DEBUG is not set +CONFIG_FB_ATY128=m +CONFIG_FB_ATY=m +CONFIG_FB_ATY_CT=y +CONFIG_FB_ATY_GX=y +# CONFIG_FB_ATY_XL_INIT is not set +# CONFIG_FB_SIS is not set +CONFIG_FB_NEOMAGIC=m +CONFIG_FB_KYRO=m +CONFIG_FB_3DFX=m +CONFIG_FB_3DFX_ACCEL=y +CONFIG_FB_VOODOO1=m +CONFIG_FB_TRIDENT=m +CONFIG_FB_TRIDENT_ACCEL=y +# CONFIG_FB_VIRTUAL is not set + +# +# Console display driver support +# +CONFIG_VGA_CONSOLE=y +CONFIG_MDA_CONSOLE=m +CONFIG_DUMMY_CONSOLE=y +CONFIG_FRAMEBUFFER_CONSOLE=y +# 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 + +# +# Sound +# +CONFIG_SOUND=m + +# +# Advanced Linux Sound Architecture +# +CONFIG_SND=m +CONFIG_SND_TIMER=m +CONFIG_SND_PCM=m +CONFIG_SND_HWDEP=m +CONFIG_SND_RAWMIDI=m +CONFIG_SND_SEQUENCER=m +CONFIG_SND_SEQ_DUMMY=m +CONFIG_SND_OSSEMUL=y +CONFIG_SND_MIXER_OSS=m +CONFIG_SND_PCM_OSS=m +CONFIG_SND_SEQUENCER_OSS=y +CONFIG_SND_RTCTIMER=m +# CONFIG_SND_VERBOSE_PRINTK is not set +# CONFIG_SND_DEBUG is not set + +# +# Generic devices +# +CONFIG_SND_MPU401_UART=m +CONFIG_SND_OPL3_LIB=m +CONFIG_SND_OPL4_LIB=m +CONFIG_SND_VX_LIB=m +CONFIG_SND_DUMMY=m +CONFIG_SND_VIRMIDI=m +CONFIG_SND_MTPAV=m +# CONFIG_SND_SERIAL_U16550 is not set +CONFIG_SND_MPU401=m + +# +# ISA devices +# +CONFIG_SND_AD1816A=m +CONFIG_SND_AD1848=m +CONFIG_SND_CS4231=m +CONFIG_SND_CS4232=m +CONFIG_SND_CS4236=m +CONFIG_SND_ES968=m +CONFIG_SND_ES1688=m +CONFIG_SND_ES18XX=m +CONFIG_SND_GUS_SYNTH=m +CONFIG_SND_GUSCLASSIC=m +CONFIG_SND_GUSEXTREME=m +CONFIG_SND_GUSMAX=m +CONFIG_SND_INTERWAVE=m +CONFIG_SND_INTERWAVE_STB=m +CONFIG_SND_OPTI92X_AD1848=m +CONFIG_SND_OPTI92X_CS4231=m +CONFIG_SND_OPTI93X=m +CONFIG_SND_SB8=m +CONFIG_SND_SB16=m +CONFIG_SND_SBAWE=m +CONFIG_SND_SB16_CSP=y +# CONFIG_SND_WAVEFRONT is not set +CONFIG_SND_ALS100=m +CONFIG_SND_AZT2320=m +CONFIG_SND_CMI8330=m +CONFIG_SND_DT019X=m +CONFIG_SND_OPL3SA2=m +CONFIG_SND_SGALAXY=m +CONFIG_SND_SSCAPE=m + +# +# PCI devices +# +CONFIG_SND_AC97_CODEC=m +CONFIG_SND_ALI5451=m +CONFIG_SND_ATIIXP=m +CONFIG_SND_ATIIXP_MODEM=m +CONFIG_SND_AU8810=m +CONFIG_SND_AU8820=m +CONFIG_SND_AU8830=m +CONFIG_SND_AZT3328=m +CONFIG_SND_BT87X=m +CONFIG_SND_CS46XX=m +CONFIG_SND_CS46XX_NEW_DSP=y +CONFIG_SND_CS4281=m +CONFIG_SND_EMU10K1=m +CONFIG_SND_KORG1212=m +CONFIG_SND_MIXART=m +CONFIG_SND_NM256=m +CONFIG_SND_RME32=m +CONFIG_SND_RME96=m +CONFIG_SND_RME9652=m +CONFIG_SND_HDSP=m +CONFIG_SND_TRIDENT=m +CONFIG_SND_YMFPCI=m +CONFIG_SND_ALS4000=m +CONFIG_SND_CMIPCI=m +CONFIG_SND_ENS1370=m +CONFIG_SND_ENS1371=m +CONFIG_SND_ES1938=m +CONFIG_SND_ES1968=m +CONFIG_SND_MAESTRO3=m +CONFIG_SND_FM801=m +CONFIG_SND_FM801_TEA575X=m +CONFIG_SND_ICE1712=m +CONFIG_SND_ICE1724=m +CONFIG_SND_INTEL8X0=m +CONFIG_SND_INTEL8X0M=m +CONFIG_SND_SONICVIBES=m +CONFIG_SND_VIA82XX=m +CONFIG_SND_VX222=m + +# +# ALSA USB devices +# +CONFIG_SND_USB_AUDIO=m +CONFIG_SND_USB_USX2Y=m + +# +# PCMCIA devices +# +# CONFIG_SND_VXPOCKET is not set +# CONFIG_SND_VXP440 is not set +CONFIG_SND_PDAUDIOCF=m + +# +# Open Sound System +# +# CONFIG_SOUND_PRIME is not set + +# +# USB support +# +CONFIG_USB=y +# CONFIG_USB_DEBUG is not set + +# +# Miscellaneous USB options +# +CONFIG_USB_DEVICEFS=y +# CONFIG_USB_BANDWIDTH is not set +# CONFIG_USB_DYNAMIC_MINORS is not set +# CONFIG_USB_SUSPEND is not set +# CONFIG_USB_OTG is not set + +# +# USB Host Controller Drivers +# +CONFIG_USB_EHCI_HCD=m +CONFIG_USB_EHCI_SPLIT_ISO=y +CONFIG_USB_EHCI_ROOT_HUB_TT=y +CONFIG_USB_OHCI_HCD=m +CONFIG_USB_UHCI_HCD=m + +# +# USB Device Class drivers +# +# CONFIG_USB_AUDIO is not set + +# +# USB Bluetooth TTY can only be used with disabled Bluetooth subsystem +# +CONFIG_USB_MIDI=m +CONFIG_USB_ACM=m +CONFIG_USB_PRINTER=m +CONFIG_USB_STORAGE=m +# CONFIG_USB_STORAGE_DEBUG is not set +CONFIG_USB_STORAGE_RW_DETECT=y +CONFIG_USB_STORAGE_DATAFAB=y +CONFIG_USB_STORAGE_FREECOM=y +CONFIG_USB_STORAGE_ISD200=y +CONFIG_USB_STORAGE_DPCM=y +CONFIG_USB_STORAGE_HP8200e=y +CONFIG_USB_STORAGE_SDDR09=y +CONFIG_USB_STORAGE_SDDR55=y +CONFIG_USB_STORAGE_JUMPSHOT=y + +# +# USB Human Interface Devices (HID) +# +CONFIG_USB_HID=y +CONFIG_USB_HIDINPUT=y +CONFIG_HID_FF=y +CONFIG_HID_PID=y +CONFIG_LOGITECH_FF=y +CONFIG_THRUSTMASTER_FF=y +CONFIG_USB_HIDDEV=y +CONFIG_USB_AIPTEK=m +CONFIG_USB_WACOM=m +CONFIG_USB_KBTAB=m +CONFIG_USB_POWERMATE=m +CONFIG_USB_MTOUCH=m +CONFIG_USB_EGALAX=m +CONFIG_USB_XPAD=m +CONFIG_USB_ATI_REMOTE=m + +# +# USB Imaging devices +# +CONFIG_USB_MDC800=m +CONFIG_USB_MICROTEK=m +CONFIG_USB_HPUSBSCSI=m + +# +# USB Multimedia devices +# +CONFIG_USB_DABUSB=m +CONFIG_USB_VICAM=m +CONFIG_USB_DSBR=m +CONFIG_USB_IBMCAM=m +CONFIG_USB_KONICAWC=m +CONFIG_USB_OV511=m +CONFIG_USB_SE401=m +CONFIG_USB_SN9C102=m +CONFIG_USB_STV680=m +CONFIG_USB_W9968CF=m +CONFIG_USB_PWC=m + +# +# USB Network adaptors +# +CONFIG_USB_CATC=m +CONFIG_USB_KAWETH=m +CONFIG_USB_PEGASUS=m +CONFIG_USB_RTL8150=m +CONFIG_USB_USBNET=m + +# +# USB Host-to-Host Cables +# +CONFIG_USB_ALI_M5632=y +CONFIG_USB_AN2720=y +CONFIG_USB_BELKIN=y +CONFIG_USB_GENESYS=y +CONFIG_USB_NET1080=y +CONFIG_USB_PL2301=y + +# +# Intelligent USB Devices/Gadgets +# +CONFIG_USB_ARMLINUX=y +CONFIG_USB_EPSON2888=y +CONFIG_USB_ZAURUS=y +CONFIG_USB_CDCETHER=y + +# +# USB Network Adapters +# +CONFIG_USB_AX8817X=y + +# +# USB port drivers +# +CONFIG_USB_USS720=m + +# +# USB Serial Converter support +# +CONFIG_USB_SERIAL=m +CONFIG_USB_SERIAL_GENERIC=y +CONFIG_USB_SERIAL_BELKIN=m +CONFIG_USB_SERIAL_DIGI_ACCELEPORT=m +CONFIG_USB_SERIAL_EMPEG=m +CONFIG_USB_SERIAL_FTDI_SIO=m +CONFIG_USB_SERIAL_VISOR=m +CONFIG_USB_SERIAL_IPAQ=m +CONFIG_USB_SERIAL_IR=m +CONFIG_USB_SERIAL_EDGEPORT=m +CONFIG_USB_SERIAL_EDGEPORT_TI=m +CONFIG_USB_SERIAL_KEYSPAN_PDA=m +CONFIG_USB_SERIAL_KEYSPAN=m +CONFIG_USB_SERIAL_KEYSPAN_MPR=y +CONFIG_USB_SERIAL_KEYSPAN_USA28=y +CONFIG_USB_SERIAL_KEYSPAN_USA28X=y +CONFIG_USB_SERIAL_KEYSPAN_USA28XA=y +CONFIG_USB_SERIAL_KEYSPAN_USA28XB=y +CONFIG_USB_SERIAL_KEYSPAN_USA19=y +CONFIG_USB_SERIAL_KEYSPAN_USA18X=y +CONFIG_USB_SERIAL_KEYSPAN_USA19W=y +CONFIG_USB_SERIAL_KEYSPAN_USA19QW=y +CONFIG_USB_SERIAL_KEYSPAN_USA19QI=y +CONFIG_USB_SERIAL_KEYSPAN_USA49W=y +CONFIG_USB_SERIAL_KEYSPAN_USA49WLC=y +CONFIG_USB_SERIAL_KLSI=m +CONFIG_USB_SERIAL_KOBIL_SCT=m +CONFIG_USB_SERIAL_MCT_U232=m +CONFIG_USB_SERIAL_PL2303=m +CONFIG_USB_SERIAL_SAFE=m +CONFIG_USB_SERIAL_SAFE_PADDED=y +CONFIG_USB_SERIAL_CYBERJACK=m +CONFIG_USB_SERIAL_XIRCOM=m +CONFIG_USB_SERIAL_OMNINET=m +CONFIG_USB_EZUSB=y + +# +# USB Miscellaneous drivers +# +CONFIG_USB_EMI62=m +# CONFIG_USB_EMI26 is not set +# CONFIG_USB_TIGL is not set +CONFIG_USB_AUERSWALD=m +CONFIG_USB_RIO500=m +CONFIG_USB_LEGOTOWER=m +CONFIG_USB_LCD=m +CONFIG_USB_LED=m +# CONFIG_USB_CYTHERM is not set +CONFIG_USB_PHIDGETSERVO=m +CONFIG_USB_TEST=m + +# +# USB ATM/DSL drivers +# +CONFIG_USB_ATM=m +CONFIG_USB_SPEEDTOUCH=m + +# +# USB Gadget Support +# +# CONFIG_USB_GADGET is not set + +# +# File systems +# +CONFIG_EXT2_FS=y +CONFIG_EXT2_FS_XATTR=y +CONFIG_EXT2_FS_POSIX_ACL=y +CONFIG_EXT2_FS_SECURITY=y +CONFIG_EXT3_FS=m +CONFIG_EXT3_FS_XATTR=y +CONFIG_EXT3_FS_POSIX_ACL=y +CONFIG_EXT3_FS_SECURITY=y +CONFIG_JBD=m +# CONFIG_JBD_DEBUG is not set +CONFIG_FS_MBCACHE=y +CONFIG_REISERFS_FS=m +# CONFIG_REISERFS_CHECK is not set +CONFIG_REISERFS_PROC_INFO=y +CONFIG_REISERFS_FS_XATTR=y +CONFIG_REISERFS_FS_POSIX_ACL=y +CONFIG_REISERFS_FS_SECURITY=y +CONFIG_JFS_FS=m +CONFIG_JFS_POSIX_ACL=y +# CONFIG_JFS_DEBUG is not set +# CONFIG_JFS_STATISTICS is not set +CONFIG_FS_POSIX_ACL=y +CONFIG_XFS_FS=m +# CONFIG_XFS_RT is not set +CONFIG_XFS_QUOTA=y +CONFIG_XFS_SECURITY=y +CONFIG_XFS_POSIX_ACL=y +CONFIG_MINIX_FS=m +CONFIG_ROMFS_FS=m +CONFIG_QUOTA=y +# CONFIG_QFMT_V1 is not set +CONFIG_QFMT_V2=y +CONFIG_QUOTACTL=y +CONFIG_AUTOFS_FS=m +CONFIG_AUTOFS4_FS=m + +# +# CD-ROM/DVD Filesystems +# +CONFIG_ISO9660_FS=y +CONFIG_JOLIET=y +CONFIG_ZISOFS=y +CONFIG_ZISOFS_FS=y +CONFIG_UDF_FS=m +CONFIG_UDF_NLS=y + +# +# DOS/FAT/NT Filesystems +# +CONFIG_FAT_FS=m +CONFIG_MSDOS_FS=m +CONFIG_VFAT_FS=m +CONFIG_FAT_DEFAULT_CODEPAGE=437 +CONFIG_FAT_DEFAULT_IOCHARSET="ascii" +# CONFIG_NTFS_FS is not set + +# +# Pseudo filesystems +# +CONFIG_PROC_FS=y +CONFIG_PROC_KCORE=y +CONFIG_SYSFS=y +# CONFIG_DEVFS_FS is not set +CONFIG_DEVPTS_FS_XATTR=y +CONFIG_DEVPTS_FS_SECURITY=y +CONFIG_TMPFS=y +CONFIG_TMPFS_XATTR=y +CONFIG_TMPFS_SECURITY=y +CONFIG_HUGETLBFS=y +CONFIG_HUGETLB_PAGE=y +CONFIG_RAMFS=y + +# +# Miscellaneous filesystems +# +# CONFIG_ADFS_FS is not set +CONFIG_AFFS_FS=m +CONFIG_HFS_FS=m +CONFIG_HFSPLUS_FS=m +CONFIG_BEFS_FS=m +# CONFIG_BEFS_DEBUG is not set +CONFIG_BFS_FS=m +CONFIG_EFS_FS=m +# CONFIG_JFFS_FS is not set +CONFIG_JFFS2_FS=m +CONFIG_JFFS2_FS_DEBUG=0 +CONFIG_JFFS2_FS_NAND=y +# CONFIG_JFFS2_COMPRESSION_OPTIONS is not set +CONFIG_JFFS2_ZLIB=y +CONFIG_JFFS2_RTIME=y +# CONFIG_JFFS2_RUBIN is not set +CONFIG_CRAMFS=m +CONFIG_VXFS_FS=m +# CONFIG_HPFS_FS is not set +CONFIG_QNX4FS_FS=m +# CONFIG_QNX4FS_RW is not set +CONFIG_SYSV_FS=m +CONFIG_UFS_FS=m +# CONFIG_UFS_FS_WRITE is not set + +# +# Network File Systems +# +CONFIG_NFS_FS=m +CONFIG_NFS_V3=y +CONFIG_NFS_V4=y +CONFIG_NFS_DIRECTIO=y +CONFIG_NFSD=m +CONFIG_NFSD_V3=y +CONFIG_NFSD_V4=y +CONFIG_NFSD_TCP=y +CONFIG_LOCKD=m +CONFIG_LOCKD_V4=y +CONFIG_EXPORTFS=m +CONFIG_SUNRPC=m +CONFIG_SUNRPC_GSS=m +CONFIG_RPCSEC_GSS_KRB5=m +CONFIG_RPCSEC_GSS_SPKM3=m +CONFIG_SMB_FS=m +# CONFIG_SMB_NLS_DEFAULT is not set +CONFIG_CIFS=m +# CONFIG_CIFS_STATS is not set +CONFIG_CIFS_XATTR=y +CONFIG_CIFS_POSIX=y +CONFIG_NCP_FS=m +CONFIG_NCPFS_PACKET_SIGNING=y +CONFIG_NCPFS_IOCTL_LOCKING=y +CONFIG_NCPFS_STRONG=y +CONFIG_NCPFS_NFS_NS=y +CONFIG_NCPFS_OS2_NS=y +CONFIG_NCPFS_SMALLDOS=y +CONFIG_NCPFS_NLS=y +CONFIG_NCPFS_EXTRAS=y +# CONFIG_CODA_FS is not set +# CONFIG_AFS_FS is not set + +# +# Partition Types +# +CONFIG_PARTITION_ADVANCED=y +# CONFIG_ACORN_PARTITION is not set +CONFIG_OSF_PARTITION=y +# CONFIG_AMIGA_PARTITION is not set +# CONFIG_ATARI_PARTITION is not set +CONFIG_MAC_PARTITION=y +CONFIG_MSDOS_PARTITION=y +CONFIG_BSD_DISKLABEL=y +CONFIG_MINIX_SUBPARTITION=y +CONFIG_SOLARIS_X86_PARTITION=y +CONFIG_UNIXWARE_DISKLABEL=y +# CONFIG_LDM_PARTITION is not set +CONFIG_SGI_PARTITION=y +# CONFIG_ULTRIX_PARTITION is not set +CONFIG_SUN_PARTITION=y +CONFIG_EFI_PARTITION=y + +# +# Native Language Support +# +CONFIG_NLS=y +CONFIG_NLS_DEFAULT="utf8" +CONFIG_NLS_CODEPAGE_437=y +CONFIG_NLS_CODEPAGE_737=m +CONFIG_NLS_CODEPAGE_775=m +CONFIG_NLS_CODEPAGE_850=m +CONFIG_NLS_CODEPAGE_852=m +CONFIG_NLS_CODEPAGE_855=m +CONFIG_NLS_CODEPAGE_857=m +CONFIG_NLS_CODEPAGE_860=m +CONFIG_NLS_CODEPAGE_861=m +CONFIG_NLS_CODEPAGE_862=m +CONFIG_NLS_CODEPAGE_863=m +CONFIG_NLS_CODEPAGE_864=m +CONFIG_NLS_CODEPAGE_865=m +CONFIG_NLS_CODEPAGE_866=m +CONFIG_NLS_CODEPAGE_869=m +CONFIG_NLS_CODEPAGE_936=m +CONFIG_NLS_CODEPAGE_950=m +CONFIG_NLS_CODEPAGE_932=m +CONFIG_NLS_CODEPAGE_949=m +CONFIG_NLS_CODEPAGE_874=m +CONFIG_NLS_ISO8859_8=m +CONFIG_NLS_CODEPAGE_1250=m +CONFIG_NLS_CODEPAGE_1251=m +CONFIG_NLS_ASCII=y +CONFIG_NLS_ISO8859_1=m +CONFIG_NLS_ISO8859_2=m +CONFIG_NLS_ISO8859_3=m +CONFIG_NLS_ISO8859_4=m +CONFIG_NLS_ISO8859_5=m +CONFIG_NLS_ISO8859_6=m +CONFIG_NLS_ISO8859_7=m +CONFIG_NLS_ISO8859_9=m +CONFIG_NLS_ISO8859_13=m +CONFIG_NLS_ISO8859_14=m +CONFIG_NLS_ISO8859_15=m +CONFIG_NLS_KOI8_R=m +CONFIG_NLS_KOI8_U=m +CONFIG_NLS_UTF8=m + +# +# Profiling support +# +CONFIG_PROFILING=y +CONFIG_OPROFILE=m + +# +# Kernel hacking +# +CONFIG_DEBUG_KERNEL=y +CONFIG_MAGIC_SYSRQ=y +# CONFIG_DEBUG_SLAB is not set +CONFIG_DEBUG_SPINLOCK=y +CONFIG_DEBUG_SPINLOCK_SLEEP=y +CONFIG_DEBUG_HIGHMEM=y +CONFIG_DEBUG_INFO=y +# CONFIG_FRAME_POINTER is not set +CONFIG_EARLY_PRINTK=y +CONFIG_DEBUG_STACKOVERFLOW=y +# CONFIG_KPROBES is not set +CONFIG_DEBUG_STACK_USAGE=y +# CONFIG_DEBUG_PAGEALLOC is not set +# CONFIG_SCHEDSTATS is not set +CONFIG_X86_FIND_SMP_CONFIG=y +CONFIG_X86_MPPARSE=y + +# +# Security options +# +CONFIG_SECURITY=y +CONFIG_SECURITY_NETWORK=y +CONFIG_SECURITY_CAPABILITIES=y +# CONFIG_SECURITY_ROOTPLUG is not set +CONFIG_SECURITY_SELINUX=y +CONFIG_SECURITY_SELINUX_BOOTPARAM=y +CONFIG_SECURITY_SELINUX_BOOTPARAM_VALUE=1 +CONFIG_SECURITY_SELINUX_DISABLE=y +CONFIG_SECURITY_SELINUX_DEVELOP=y +CONFIG_SECURITY_SELINUX_AVC_STATS=y +# CONFIG_SECURITY_SELINUX_MLS is not set + +# +# Cryptographic options +# +CONFIG_CRYPTO=y +CONFIG_CRYPTO_HMAC=y +CONFIG_CRYPTO_NULL=m +CONFIG_CRYPTO_MD4=m +CONFIG_CRYPTO_MD5=m +CONFIG_CRYPTO_SHA1=y +CONFIG_CRYPTO_SHA256=m +CONFIG_CRYPTO_SHA512=m +CONFIG_CRYPTO_WP512=m +CONFIG_CRYPTO_DES=m +CONFIG_CRYPTO_BLOWFISH=m +CONFIG_CRYPTO_TWOFISH=m +CONFIG_CRYPTO_SERPENT=m +CONFIG_CRYPTO_AES_586=m +CONFIG_CRYPTO_CAST5=m +CONFIG_CRYPTO_CAST6=m +CONFIG_CRYPTO_TEA=m +CONFIG_CRYPTO_ARC4=m +CONFIG_CRYPTO_KHAZAD=m +CONFIG_CRYPTO_DEFLATE=m +CONFIG_CRYPTO_MICHAEL_MIC=m +CONFIG_CRYPTO_CRC32C=m +# CONFIG_CRYPTO_TEST is not set +CONFIG_CRYPTO_SIGNATURE=y +CONFIG_CRYPTO_SIGNATURE_DSA=y +CONFIG_CRYPTO_MPILIB=y + +# +# Library routines +# +CONFIG_CRC_CCITT=m +CONFIG_CRC32=y +CONFIG_LIBCRC32C=m +CONFIG_ZLIB_INFLATE=y +CONFIG_ZLIB_DEFLATE=m +CONFIG_X86_SMP=y +CONFIG_X86_HT=y +CONFIG_X86_BIOS_REBOOT=y +CONFIG_X86_TRAMPOLINE=y +CONFIG_PC=y diff --git a/configs/kernel-2.6.9-i586.config b/configs/kernel-2.6.9-i586.config new file mode 100644 index 000000000..e843e6c1a --- /dev/null +++ b/configs/kernel-2.6.9-i586.config @@ -0,0 +1,2588 @@ +# +# Automatically generated make config: don't edit +# Linux kernel version: 2.6.9-ac11 +# Tue Jan 4 10:01:15 2005 +# +CONFIG_X86=y +CONFIG_MMU=y +CONFIG_UID16=y +CONFIG_GENERIC_ISA_DMA=y +CONFIG_GENERIC_IOMAP=y + +# +# Code maturity level options +# +CONFIG_EXPERIMENTAL=y +CONFIG_CLEAN_COMPILE=y +CONFIG_BROKEN_ON_SMP=y + +# +# General setup +# +CONFIG_LOCALVERSION="" +CONFIG_SWAP=y +CONFIG_SYSVIPC=y +CONFIG_POSIX_MQUEUE=y +CONFIG_BSD_PROCESS_ACCT=y +# CONFIG_BSD_PROCESS_ACCT_V3 is not set +CONFIG_SYSCTL=y +CONFIG_AUDIT=y +CONFIG_AUDITSYSCALL=y +CONFIG_LOG_BUF_SHIFT=17 +CONFIG_HOTPLUG=y +# CONFIG_IKCONFIG is not set +# CONFIG_EMBEDDED is not set +CONFIG_KALLSYMS=y +# CONFIG_KALLSYMS_ALL is not set +CONFIG_KALLSYMS_EXTRA_PASS=y +CONFIG_FUTEX=y +CONFIG_EPOLL=y +CONFIG_IOSCHED_NOOP=y +CONFIG_IOSCHED_AS=y +CONFIG_IOSCHED_DEADLINE=y +CONFIG_IOSCHED_CFQ=y +CONFIG_CC_OPTIMIZE_FOR_SIZE=y +CONFIG_SHMEM=y +# CONFIG_TINY_SHMEM is not set + +# +# Loadable module support +# +CONFIG_MODULES=y +CONFIG_MODULE_UNLOAD=y +# CONFIG_MODULE_FORCE_UNLOAD is not set +CONFIG_OBSOLETE_MODPARM=y +CONFIG_MODVERSIONS=y +CONFIG_MODULE_SIG=y +# CONFIG_MODULE_SIG_FORCE is not set +CONFIG_KMOD=y + +# +# Processor type and features +# +CONFIG_X86_PC=y +# CONFIG_X86_ELAN is not set +# CONFIG_X86_VOYAGER is not set +# CONFIG_X86_NUMAQ is not set +# CONFIG_X86_SUMMIT is not set +# CONFIG_X86_BIGSMP is not set +# CONFIG_X86_VISWS is not set +# CONFIG_X86_GENERICARCH is not set +# CONFIG_X86_ES7000 is not set +# CONFIG_M386 is not set +# CONFIG_M486 is not set +CONFIG_M586=y +# CONFIG_M586TSC is not set +# CONFIG_M586MMX is not set +# CONFIG_M686 is not set +# CONFIG_MPENTIUMII is not set +# CONFIG_MPENTIUMIII is not set +# CONFIG_MPENTIUMM is not set +# CONFIG_MPENTIUM4 is not set +# CONFIG_MK6 is not set +# CONFIG_MK7 is not set +# CONFIG_MK8 is not set +# CONFIG_MCRUSOE is not set +# CONFIG_MWINCHIPC6 is not set +# CONFIG_MWINCHIP2 is not set +# CONFIG_MWINCHIP3D is not set +# CONFIG_MCYRIXIII is not set +# CONFIG_MVIAC3_2 is not set +CONFIG_X86_GENERIC=y +CONFIG_X86_CMPXCHG=y +CONFIG_X86_XADD=y +CONFIG_X86_L1_CACHE_SHIFT=7 +CONFIG_RWSEM_XCHGADD_ALGORITHM=y +CONFIG_X86_PPRO_FENCE=y +CONFIG_X86_F00F_BUG=y +CONFIG_X86_WP_WORKS_OK=y +CONFIG_X86_INVLPG=y +CONFIG_X86_BSWAP=y +CONFIG_X86_POPAD_OK=y +CONFIG_X86_ALIGNMENT_16=y +CONFIG_X86_INTEL_USERCOPY=y +CONFIG_HPET_TIMER=y +CONFIG_HPET_EMULATE_RTC=y +# CONFIG_SMP is not set +# CONFIG_PREEMPT is not set +CONFIG_PREEMPT_VOLUNTARY=y +# CONFIG_X86_UP_APIC is not set +CONFIG_X86_MCE=y +# CONFIG_X86_MCE_NONFATAL is not set +CONFIG_TOSHIBA=m +CONFIG_I8K=m +# CONFIG_MICROCODE is not set +CONFIG_X86_MSR=m +CONFIG_X86_CPUID=m + +# +# Firmware Drivers +# +CONFIG_EDD=m +# CONFIG_NOHIGHMEM is not set +CONFIG_HIGHMEM4G=y +# CONFIG_HIGHMEM64G is not set +CONFIG_HIGHMEM=y +CONFIG_HIGHPTE=y +# CONFIG_MATH_EMULATION is not set +CONFIG_MTRR=y +# CONFIG_EFI is not set +CONFIG_REGPARM=y +CONFIG_KEXEC=y + +# +# Power management options (ACPI, APM) +# +CONFIG_PM=y +# CONFIG_PM_DEBUG is not set +# CONFIG_SOFTWARE_SUSPEND is not set + +# +# ACPI (Advanced Configuration and Power Interface) Support +# +CONFIG_ACPI=y +CONFIG_ACPI_BOOT=y +CONFIG_ACPI_INTERPRETER=y +CONFIG_ACPI_SLEEP=y +CONFIG_ACPI_SLEEP_PROC_FS=y +CONFIG_ACPI_AC=m +CONFIG_ACPI_BATTERY=m +CONFIG_ACPI_BUTTON=m +CONFIG_ACPI_FAN=y +CONFIG_ACPI_PROCESSOR=y +CONFIG_ACPI_THERMAL=y +CONFIG_ACPI_ASUS=m +CONFIG_ACPI_TOSHIBA=m +CONFIG_ACPI_BLACKLIST_YEAR=2001 +# CONFIG_ACPI_DEBUG is not set +CONFIG_ACPI_BUS=y +CONFIG_ACPI_EC=y +CONFIG_ACPI_POWER=y +CONFIG_ACPI_PCI=y +CONFIG_ACPI_SYSTEM=y +CONFIG_X86_PM_TIMER=y + +# +# APM (Advanced Power Management) BIOS Support +# +CONFIG_APM=y +# CONFIG_APM_IGNORE_USER_SUSPEND is not set +# CONFIG_APM_DO_ENABLE is not set +CONFIG_APM_CPU_IDLE=y +# CONFIG_APM_DISPLAY_BLANK is not set +CONFIG_APM_RTC_IS_GMT=y +# CONFIG_APM_ALLOW_INTS is not set +# CONFIG_APM_REAL_MODE_POWER_OFF is not set + +# +# CPU Frequency scaling +# +CONFIG_CPU_FREQ=y +# CONFIG_CPU_FREQ_PROC_INTF is not set +# CONFIG_CPU_FREQ_DEFAULT_GOV_PERFORMANCE is not set +CONFIG_CPU_FREQ_DEFAULT_GOV_USERSPACE=y +CONFIG_CPU_FREQ_GOV_PERFORMANCE=y +CONFIG_CPU_FREQ_GOV_POWERSAVE=m +CONFIG_CPU_FREQ_GOV_USERSPACE=y +CONFIG_CPU_FREQ_GOV_ONDEMAND=m +# CONFIG_CPU_FREQ_24_API is not set +CONFIG_CPU_FREQ_TABLE=y + +# +# CPUFreq processor drivers +# +CONFIG_X86_ACPI_CPUFREQ=m +# CONFIG_X86_ACPI_CPUFREQ_PROC_INTF is not set +CONFIG_X86_POWERNOW_K6=m +CONFIG_X86_POWERNOW_K7=y +CONFIG_X86_POWERNOW_K7_ACPI=y +CONFIG_X86_POWERNOW_K8=m +CONFIG_X86_POWERNOW_K8_ACPI=y +# CONFIG_X86_GX_SUSPMOD is not set +CONFIG_X86_SPEEDSTEP_CENTRINO=y +CONFIG_X86_SPEEDSTEP_CENTRINO_TABLE=y +CONFIG_X86_SPEEDSTEP_CENTRINO_ACPI=y +CONFIG_X86_SPEEDSTEP_ICH=y +CONFIG_X86_SPEEDSTEP_SMI=m +CONFIG_X86_P4_CLOCKMOD=m +CONFIG_X86_SPEEDSTEP_LIB=y +# CONFIG_X86_SPEEDSTEP_RELAXED_CAP_CHECK is not set +CONFIG_X86_LONGRUN=y +CONFIG_X86_LONGHAUL=y + +# +# Bus options (PCI, PCMCIA, EISA, MCA, ISA) +# +CONFIG_PCI=y +# CONFIG_PCI_GOBIOS is not set +# CONFIG_PCI_GOMMCONFIG is not set +# CONFIG_PCI_GODIRECT is not set +CONFIG_PCI_GOANY=y +CONFIG_PCI_BIOS=y +CONFIG_PCI_DIRECT=y +CONFIG_PCI_MMCONFIG=y +CONFIG_PCI_LEGACY_PROC=y +# CONFIG_PCI_NAMES is not set +CONFIG_ISA=y +# CONFIG_EISA is not set +# CONFIG_MCA is not set +# CONFIG_SCx200 is not set + +# +# PCMCIA/CardBus support +# +CONFIG_PCMCIA=m +# CONFIG_PCMCIA_DEBUG is not set +CONFIG_YENTA=m +CONFIG_CARDBUS=y +CONFIG_PD6729=m +CONFIG_I82092=m +CONFIG_I82365=m +CONFIG_TCIC=m +CONFIG_PCMCIA_PROBE=y + +# +# PCI Hotplug Support +# +CONFIG_HOTPLUG_PCI=y +# CONFIG_HOTPLUG_PCI_FAKE is not set +CONFIG_HOTPLUG_PCI_COMPAQ=m +# CONFIG_HOTPLUG_PCI_COMPAQ_NVRAM is not set +CONFIG_HOTPLUG_PCI_ACPI=m +CONFIG_HOTPLUG_PCI_ACPI_IBM=m +# CONFIG_HOTPLUG_PCI_CPCI is not set +CONFIG_HOTPLUG_PCI_PCIE=m +# CONFIG_HOTPLUG_PCI_PCIE_POLL_EVENT_MODE is not set +CONFIG_HOTPLUG_PCI_SHPC=m +# CONFIG_HOTPLUG_PCI_SHPC_POLL_EVENT_MODE is not set + +# +# Executable file formats +# +CONFIG_BINFMT_ELF=y +# CONFIG_BINFMT_AOUT is not set +CONFIG_BINFMT_MISC=y + +# +# Device Drivers +# + +# +# Generic Driver Options +# +CONFIG_STANDALONE=y +CONFIG_PREVENT_FIRMWARE_BUILD=y +CONFIG_FW_LOADER=y +# CONFIG_DEBUG_DRIVER is not set + +# +# Memory Technology Devices (MTD) +# +CONFIG_MTD=m +# CONFIG_MTD_DEBUG is not set +CONFIG_MTD_PARTITIONS=y +CONFIG_MTD_CONCAT=m +CONFIG_MTD_REDBOOT_PARTS=m +# CONFIG_MTD_REDBOOT_PARTS_UNALLOCATED is not set +# CONFIG_MTD_REDBOOT_PARTS_READONLY is not set +CONFIG_MTD_CMDLINE_PARTS=y + +# +# User Modules And Translation Layers +# +CONFIG_MTD_CHAR=m +CONFIG_MTD_BLOCK=m +CONFIG_MTD_BLOCK_RO=m +CONFIG_FTL=m +CONFIG_NFTL=m +CONFIG_NFTL_RW=y +CONFIG_INFTL=m + +# +# RAM/ROM/Flash chip drivers +# +CONFIG_MTD_CFI=m +CONFIG_MTD_JEDECPROBE=m +CONFIG_MTD_GEN_PROBE=m +# 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=m +CONFIG_MTD_CFI_AMDSTD=m +CONFIG_MTD_CFI_AMDSTD_RETRY=3 +CONFIG_MTD_CFI_STAA=m +CONFIG_MTD_CFI_UTIL=m +CONFIG_MTD_RAM=m +CONFIG_MTD_ROM=m +CONFIG_MTD_ABSENT=m + +# +# Mapping drivers for chip access +# +CONFIG_MTD_COMPLEX_MAPPINGS=y +# CONFIG_MTD_PHYSMAP is not set +# CONFIG_MTD_PNC2000 is not set +CONFIG_MTD_SC520CDP=m +CONFIG_MTD_NETSC520=m +CONFIG_MTD_SBC_GXX=m +CONFIG_MTD_ELAN_104NC=m +CONFIG_MTD_SCx200_DOCFLASH=m +# CONFIG_MTD_AMD76XROM is not set +# CONFIG_MTD_ICHXROM is not set +CONFIG_MTD_SCB2_FLASH=m +# CONFIG_MTD_NETtel is not set +# CONFIG_MTD_DILNETPC is not set +# CONFIG_MTD_L440GX is not set +CONFIG_MTD_PCI=m + +# +# Self-contained MTD device drivers +# +CONFIG_MTD_PMC551=m +# CONFIG_MTD_PMC551_BUGFIX is not set +# CONFIG_MTD_PMC551_DEBUG is not set +# CONFIG_MTD_SLRAM is not set +# CONFIG_MTD_PHRAM is not set +CONFIG_MTD_MTDRAM=m +CONFIG_MTDRAM_TOTAL_SIZE=4096 +CONFIG_MTDRAM_ERASE_SIZE=128 +# CONFIG_MTD_BLKMTD is not set + +# +# Disk-On-Chip Device Drivers +# +CONFIG_MTD_DOC2000=m +# CONFIG_MTD_DOC2001 is not set +CONFIG_MTD_DOC2001PLUS=m +CONFIG_MTD_DOCPROBE=m +CONFIG_MTD_DOCECC=m +# CONFIG_MTD_DOCPROBE_ADVANCED is not set +CONFIG_MTD_DOCPROBE_ADDRESS=0 + +# +# NAND Flash Device Drivers +# +CONFIG_MTD_NAND=m +# CONFIG_MTD_NAND_VERIFY_WRITE is not set +CONFIG_MTD_NAND_IDS=m +# CONFIG_MTD_NAND_DISKONCHIP is not set + +# +# Parallel port support +# +CONFIG_PARPORT=m +CONFIG_PARPORT_PC=m +CONFIG_PARPORT_PC_CML1=m +CONFIG_PARPORT_SERIAL=m +# CONFIG_PARPORT_PC_FIFO is not set +# CONFIG_PARPORT_PC_SUPERIO is not set +CONFIG_PARPORT_PC_PCMCIA=m +# CONFIG_PARPORT_OTHER is not set +CONFIG_PARPORT_1284=y + +# +# Plug and Play support +# +CONFIG_PNP=y +# CONFIG_PNP_DEBUG is not set + +# +# Protocols +# +CONFIG_ISAPNP=y +# CONFIG_PNPBIOS is not set + +# +# Block devices +# +CONFIG_BLK_DEV_FD=m +# CONFIG_BLK_DEV_XD is not set +# CONFIG_PARIDE is not set +CONFIG_BLK_CPQ_DA=m +CONFIG_BLK_CPQ_CISS_DA=m +CONFIG_CISS_SCSI_TAPE=y +CONFIG_BLK_DEV_DAC960=m +CONFIG_BLK_DEV_UMEM=m +CONFIG_BLK_DEV_LOOP=m +CONFIG_BLK_DEV_CRYPTOLOOP=m +CONFIG_BLK_DEV_NBD=m +CONFIG_BLK_DEV_SX8=m +# CONFIG_BLK_DEV_UB is not set +CONFIG_BLK_DEV_RAM=y +CONFIG_BLK_DEV_RAM_SIZE=16384 +CONFIG_BLK_DEV_INITRD=y +CONFIG_LBD=y + +# +# ATA/ATAPI/MFM/RLL support +# +CONFIG_IDE=y +CONFIG_BLK_DEV_IDE=y + +# +# Please see Documentation/ide.txt for help/info on IDE drives +# +# CONFIG_BLK_DEV_IDE_SATA is not set +# CONFIG_BLK_DEV_HD_IDE is not set +CONFIG_BLK_DEV_IDEDISK=y +CONFIG_IDEDISK_MULTI_MODE=y +CONFIG_BLK_DEV_IDECS=m +CONFIG_BLK_DEV_DELKIN=m +CONFIG_BLK_DEV_IDECD=y +# CONFIG_BLK_DEV_IDETAPE is not set +CONFIG_BLK_DEV_IDEFLOPPY=y +CONFIG_BLK_DEV_IDESCSI=m +# CONFIG_IDE_TASK_IOCTL is not set +# CONFIG_IDE_TASKFILE_IO is not set + +# +# IDE chipset support/bugfixes +# +CONFIG_IDE_GENERIC=y +# CONFIG_BLK_DEV_CMD640 is not set +CONFIG_BLK_DEV_IDEPNP=y +CONFIG_BLK_DEV_IDEPCI=y +CONFIG_IDEPCI_SHARE_IRQ=y +# CONFIG_BLK_DEV_OFFBOARD is not set +CONFIG_BLK_DEV_GENERIC=y +# CONFIG_BLK_DEV_OPTI621 is not set +CONFIG_BLK_DEV_RZ1000=y +CONFIG_BLK_DEV_IDEDMA_PCI=y +# CONFIG_BLK_DEV_IDEDMA_FORCED is not set +CONFIG_IDEDMA_PCI_AUTO=y +# CONFIG_IDEDMA_ONLYDISK is not set +CONFIG_BLK_DEV_AEC62XX=y +CONFIG_BLK_DEV_ALI15X3=y +# CONFIG_WDC_ALI15X3 is not set +CONFIG_BLK_DEV_AMD74XX=y +CONFIG_BLK_DEV_ATIIXP=y +CONFIG_BLK_DEV_CMD64X=y +CONFIG_BLK_DEV_TRIFLEX=y +CONFIG_BLK_DEV_CY82C693=y +CONFIG_BLK_DEV_CS5520=y +CONFIG_BLK_DEV_CS5530=y +CONFIG_BLK_DEV_HPT34X=y +# CONFIG_HPT34X_AUTODMA is not set +CONFIG_BLK_DEV_HPT366=y +# CONFIG_BLK_DEV_SC1200 is not set +CONFIG_BLK_DEV_PIIX=y +CONFIG_BLK_DEV_IT8212=y +# CONFIG_BLK_DEV_NS87415 is not set +CONFIG_BLK_DEV_PDC202XX_OLD=y +# CONFIG_PDC202XX_BURST is not set +CONFIG_BLK_DEV_PDC202XX_NEW=y +CONFIG_PDC202XX_FORCE=y +CONFIG_BLK_DEV_SVWKS=y +CONFIG_BLK_DEV_SIIMAGE=y +CONFIG_BLK_DEV_SIS5513=y +CONFIG_BLK_DEV_SLC90E66=y +# CONFIG_BLK_DEV_TRM290 is not set +CONFIG_BLK_DEV_VIA82CXXX=y +# CONFIG_IDE_ARM is not set +# CONFIG_IDE_CHIPSETS is not set +CONFIG_BLK_DEV_IDEDMA=y +# CONFIG_IDEDMA_IVB is not set +CONFIG_IDEDMA_AUTO=y +# CONFIG_BLK_DEV_HD is not set + +# +# SCSI device support +# +CONFIG_SCSI=m +CONFIG_SCSI_PROC_FS=y + +# +# SCSI support type (disk, tape, CD-ROM) +# +CONFIG_BLK_DEV_SD=m +CONFIG_CHR_DEV_ST=m +CONFIG_CHR_DEV_OSST=m +CONFIG_BLK_DEV_SR=m +CONFIG_BLK_DEV_SR_VENDOR=y +CONFIG_CHR_DEV_SG=m + +# +# Some SCSI devices (e.g. CD jukebox) support multiple LUNs +# +# CONFIG_SCSI_MULTI_LUN is not set +CONFIG_SCSI_CONSTANTS=y +CONFIG_SCSI_LOGGING=y + +# +# SCSI Transport Attributes +# +CONFIG_SCSI_SPI_ATTRS=m +CONFIG_SCSI_FC_ATTRS=m + +# +# SCSI low-level drivers +# +CONFIG_BLK_DEV_3W_XXXX_RAID=m +CONFIG_SCSI_3W_9XXX=m +# CONFIG_SCSI_7000FASST is not set +CONFIG_SCSI_ACARD=m +CONFIG_SCSI_AHA152X=m +CONFIG_SCSI_AHA1542=m +CONFIG_SCSI_AACRAID=m +CONFIG_SCSI_AIC7XXX=m +CONFIG_AIC7XXX_CMDS_PER_DEVICE=4 +CONFIG_AIC7XXX_RESET_DELAY_MS=15000 +# CONFIG_AIC7XXX_DEBUG_ENABLE is not set +CONFIG_AIC7XXX_DEBUG_MASK=0 +# CONFIG_AIC7XXX_REG_PRETTY_PRINT is not set +CONFIG_SCSI_AIC7XXX_OLD=m +CONFIG_SCSI_AIC79XX=m +CONFIG_AIC79XX_CMDS_PER_DEVICE=4 +CONFIG_AIC79XX_RESET_DELAY_MS=15000 +# CONFIG_AIC79XX_ENABLE_RD_STRM is not set +# CONFIG_AIC79XX_DEBUG_ENABLE is not set +CONFIG_AIC79XX_DEBUG_MASK=0 +# CONFIG_AIC79XX_REG_PRETTY_PRINT is not set +# CONFIG_SCSI_DPT_I2O is not set +CONFIG_SCSI_IN2000=m +CONFIG_MEGARAID_NEWGEN=y +CONFIG_MEGARAID_MM=m +CONFIG_MEGARAID_MAILBOX=m +CONFIG_MEGARAID_LEGACY=m +CONFIG_SCSI_SATA=y +CONFIG_SCSI_SATA_AHCI=m +CONFIG_SCSI_SATA_SVW=m +CONFIG_SCSI_ATA_PIIX=m +CONFIG_SCSI_SATA_NV=m +CONFIG_SCSI_SATA_PROMISE=m +CONFIG_SCSI_SATA_SX4=m +CONFIG_SCSI_SATA_SIL=m +CONFIG_SCSI_SATA_SIS=m +CONFIG_SCSI_SATA_VIA=m +CONFIG_SCSI_SATA_VITESSE=m +CONFIG_SCSI_BUSLOGIC=m +# CONFIG_SCSI_OMIT_FLASHPOINT is not set +# CONFIG_SCSI_DMX3191D is not set +# CONFIG_SCSI_DTC3280 is not set +# CONFIG_SCSI_EATA is not set +# CONFIG_SCSI_EATA_PIO is not set +CONFIG_SCSI_LPFC=m +CONFIG_SCSI_FUTURE_DOMAIN=m +CONFIG_SCSI_GDTH=m +# CONFIG_SCSI_GENERIC_NCR5380 is not set +# CONFIG_SCSI_GENERIC_NCR5380_MMIO is not set +CONFIG_SCSI_IPS=m +CONFIG_SCSI_INITIO=m +CONFIG_SCSI_INIA100=m +CONFIG_SCSI_PPA=m +CONFIG_SCSI_IMM=m +# CONFIG_SCSI_IZIP_EPP16 is not set +# CONFIG_SCSI_IZIP_SLOW_CTR is not set +# CONFIG_SCSI_NCR53C406A is not set +CONFIG_SCSI_SYM53C8XX_2=m +CONFIG_SCSI_SYM53C8XX_DMA_ADDRESSING_MODE=1 +CONFIG_SCSI_SYM53C8XX_DEFAULT_TAGS=16 +CONFIG_SCSI_SYM53C8XX_MAX_TAGS=64 +# CONFIG_SCSI_SYM53C8XX_IOMAPPED is not set +# CONFIG_SCSI_IPR is not set +# CONFIG_SCSI_PAS16 is not set +# CONFIG_SCSI_PSI240I is not set +CONFIG_SCSI_QLOGIC_FAS=m +CONFIG_SCSI_QLOGIC_ISP=m +# CONFIG_SCSI_QLOGIC_FC is not set +CONFIG_SCSI_QLOGIC_1280=m +CONFIG_SCSI_QLA2XXX=m +CONFIG_SCSI_QLA21XX=m +CONFIG_SCSI_QLA22XX=m +CONFIG_SCSI_QLA2300=m +CONFIG_SCSI_QLA2322=m +CONFIG_SCSI_QLA6312=m +CONFIG_SCSI_QLA6322=m +# CONFIG_SCSI_SYM53C416 is not set +# CONFIG_SCSI_DC395x is not set +CONFIG_SCSI_DC390T=m +# CONFIG_SCSI_T128 is not set +# CONFIG_SCSI_U14_34F is not set +# CONFIG_SCSI_ULTRASTOR is not set +# CONFIG_SCSI_NSP32 is not set +# CONFIG_SCSI_DEBUG is not set + +# +# PCMCIA SCSI adapter support +# +CONFIG_PCMCIA_AHA152X=m +CONFIG_PCMCIA_FDOMAIN=m +CONFIG_PCMCIA_NINJA_SCSI=m +CONFIG_PCMCIA_QLOGIC=m +CONFIG_PCMCIA_SYM53C500=m + +# +# Old CD-ROM drivers (not SCSI, not IDE) +# +# CONFIG_CD_NO_IDESCSI is not set + +# +# Multi-device support (RAID and LVM) +# +CONFIG_MD=y +CONFIG_BLK_DEV_MD=y +CONFIG_MD_LINEAR=m +CONFIG_MD_RAID0=m +CONFIG_MD_RAID1=m +CONFIG_MD_RAID10=m +CONFIG_MD_RAID5=m +CONFIG_MD_RAID6=m +CONFIG_MD_MULTIPATH=m +CONFIG_BLK_DEV_DM=m +CONFIG_DM_CRYPT=m +CONFIG_DM_SNAPSHOT=m +CONFIG_DM_MIRROR=m +CONFIG_DM_ZERO=m + +# +# Fusion MPT device support +# +CONFIG_FUSION=m +CONFIG_FUSION_MAX_SGE=40 +CONFIG_FUSION_CTL=m +CONFIG_FUSION_LAN=m + +# +# IEEE 1394 (FireWire) support +# +CONFIG_IEEE1394=m + +# +# Subsystem Options +# +# CONFIG_IEEE1394_VERBOSEDEBUG is not set +CONFIG_IEEE1394_OUI_DB=y +CONFIG_IEEE1394_EXTRA_CONFIG_ROMS=y +CONFIG_IEEE1394_CONFIG_ROM_IP1394=y + +# +# Device Drivers +# +CONFIG_IEEE1394_PCILYNX=m +CONFIG_IEEE1394_OHCI1394=m + +# +# Protocol Drivers +# +CONFIG_IEEE1394_VIDEO1394=m +CONFIG_IEEE1394_SBP2=m +# CONFIG_IEEE1394_SBP2_PHYS_DMA is not set +CONFIG_IEEE1394_ETH1394=m +CONFIG_IEEE1394_DV1394=m +CONFIG_IEEE1394_RAWIO=m +CONFIG_IEEE1394_CMP=m +CONFIG_IEEE1394_AMDTP=m + +# +# I2O device support +# +CONFIG_I2O=m +CONFIG_I2O_CONFIG=m +CONFIG_I2O_BLOCK=m +CONFIG_I2O_SCSI=m +CONFIG_I2O_PROC=m + +# +# Networking support +# +CONFIG_NET=y + +# +# Networking options +# +CONFIG_PACKET=y +CONFIG_PACKET_MMAP=y +CONFIG_NETLINK_DEV=y +CONFIG_UNIX=y +CONFIG_NET_KEY=m +CONFIG_INET=y +CONFIG_IP_MULTICAST=y +CONFIG_IP_ADVANCED_ROUTER=y +CONFIG_IP_MULTIPLE_TABLES=y +CONFIG_IP_ROUTE_FWMARK=y +CONFIG_IP_ROUTE_MULTIPATH=y +CONFIG_IP_ROUTE_VERBOSE=y +# CONFIG_IP_PNP is not set +CONFIG_NET_IPIP=m +CONFIG_NET_IPGRE=m +CONFIG_NET_IPGRE_BROADCAST=y +CONFIG_IP_MROUTE=y +CONFIG_IP_PIMSM_V1=y +CONFIG_IP_PIMSM_V2=y +# CONFIG_ARPD is not set +CONFIG_SYN_COOKIES=y +CONFIG_INET_AH=m +CONFIG_INET_ESP=m +CONFIG_INET_IPCOMP=m +CONFIG_INET_TUNNEL=m + +# +# IP: Virtual Server Configuration +# +CONFIG_IP_VS=m +# CONFIG_IP_VS_DEBUG is not set +CONFIG_IP_VS_TAB_BITS=12 + +# +# IPVS transport protocol load balancing support +# +CONFIG_IP_VS_PROTO_TCP=y +CONFIG_IP_VS_PROTO_UDP=y +CONFIG_IP_VS_PROTO_ESP=y +CONFIG_IP_VS_PROTO_AH=y + +# +# IPVS scheduler +# +CONFIG_IP_VS_RR=m +CONFIG_IP_VS_WRR=m +CONFIG_IP_VS_LC=m +CONFIG_IP_VS_WLC=m +CONFIG_IP_VS_LBLC=m +CONFIG_IP_VS_LBLCR=m +CONFIG_IP_VS_DH=m +CONFIG_IP_VS_SH=m +CONFIG_IP_VS_SED=m +CONFIG_IP_VS_NQ=m + +# +# IPVS application helper +# +CONFIG_IP_VS_FTP=m +CONFIG_IPV6=m +CONFIG_IPV6_PRIVACY=y +CONFIG_INET6_AH=m +CONFIG_INET6_ESP=m +CONFIG_INET6_IPCOMP=m +CONFIG_INET6_TUNNEL=m +CONFIG_IPV6_TUNNEL=m +CONFIG_NETFILTER=y +# CONFIG_NETFILTER_DEBUG is not set +CONFIG_BRIDGE_NETFILTER=y + +# +# IP: Netfilter Configuration +# +CONFIG_IP_NF_CONNTRACK=m +CONFIG_IP_NF_CT_ACCT=y +CONFIG_IP_NF_CT_PROTO_SCTP=m +CONFIG_IP_NF_FTP=m +CONFIG_IP_NF_IRC=m +CONFIG_IP_NF_TFTP=m +CONFIG_IP_NF_AMANDA=m +CONFIG_IP_NF_QUEUE=m +CONFIG_IP_NF_IPTABLES=m +CONFIG_IP_NF_MATCH_LIMIT=m +CONFIG_IP_NF_MATCH_IPRANGE=m +CONFIG_IP_NF_MATCH_MAC=m +CONFIG_IP_NF_MATCH_PKTTYPE=m +CONFIG_IP_NF_MATCH_MARK=m +CONFIG_IP_NF_MATCH_MULTIPORT=m +CONFIG_IP_NF_MATCH_TOS=m +CONFIG_IP_NF_MATCH_RECENT=m +CONFIG_IP_NF_MATCH_ECN=m +CONFIG_IP_NF_MATCH_DSCP=m +CONFIG_IP_NF_MATCH_AH_ESP=m +CONFIG_IP_NF_MATCH_LENGTH=m +CONFIG_IP_NF_MATCH_TTL=m +CONFIG_IP_NF_MATCH_TCPMSS=m +CONFIG_IP_NF_MATCH_HELPER=m +CONFIG_IP_NF_MATCH_STATE=m +CONFIG_IP_NF_MATCH_CONNTRACK=m +CONFIG_IP_NF_MATCH_OWNER=m +CONFIG_IP_NF_MATCH_PHYSDEV=m +CONFIG_IP_NF_MATCH_ADDRTYPE=m +CONFIG_IP_NF_MATCH_REALM=m +CONFIG_IP_NF_MATCH_SCTP=m +CONFIG_IP_NF_MATCH_COMMENT=m +CONFIG_IP_NF_FILTER=m +CONFIG_IP_NF_TARGET_REJECT=m +CONFIG_IP_NF_TARGET_LOG=m +CONFIG_IP_NF_TARGET_ULOG=m +CONFIG_IP_NF_TARGET_TCPMSS=m +CONFIG_IP_NF_NAT=m +CONFIG_IP_NF_NAT_NEEDED=y +CONFIG_IP_NF_TARGET_MASQUERADE=m +CONFIG_IP_NF_TARGET_REDIRECT=m +CONFIG_IP_NF_TARGET_NETMAP=m +CONFIG_IP_NF_TARGET_SAME=m +CONFIG_IP_NF_NAT_LOCAL=y +CONFIG_IP_NF_NAT_SNMP_BASIC=m +CONFIG_IP_NF_NAT_IRC=m +CONFIG_IP_NF_NAT_FTP=m +CONFIG_IP_NF_NAT_TFTP=m +CONFIG_IP_NF_NAT_AMANDA=m +CONFIG_IP_NF_MANGLE=m +CONFIG_IP_NF_TARGET_TOS=m +CONFIG_IP_NF_TARGET_ECN=m +CONFIG_IP_NF_TARGET_DSCP=m +CONFIG_IP_NF_TARGET_MARK=m +CONFIG_IP_NF_TARGET_CLASSIFY=m +CONFIG_IP_NF_RAW=m +CONFIG_IP_NF_TARGET_NOTRACK=m +CONFIG_IP_NF_ARPTABLES=m +CONFIG_IP_NF_ARPFILTER=m +CONFIG_IP_NF_ARP_MANGLE=m +# CONFIG_IP_NF_COMPAT_IPCHAINS is not set +# CONFIG_IP_NF_COMPAT_IPFWADM is not set + +# +# IPv6: Netfilter Configuration +# +# CONFIG_IP6_NF_QUEUE is not set +CONFIG_IP6_NF_IPTABLES=m +CONFIG_IP6_NF_MATCH_LIMIT=m +CONFIG_IP6_NF_MATCH_MAC=m +CONFIG_IP6_NF_MATCH_RT=m +CONFIG_IP6_NF_MATCH_OPTS=m +CONFIG_IP6_NF_MATCH_FRAG=m +CONFIG_IP6_NF_MATCH_HL=m +CONFIG_IP6_NF_MATCH_MULTIPORT=m +CONFIG_IP6_NF_MATCH_OWNER=m +CONFIG_IP6_NF_MATCH_MARK=m +CONFIG_IP6_NF_MATCH_IPV6HEADER=m +CONFIG_IP6_NF_MATCH_AHESP=m +CONFIG_IP6_NF_MATCH_LENGTH=m +CONFIG_IP6_NF_MATCH_EUI64=m +CONFIG_IP6_NF_MATCH_PHYSDEV=m +CONFIG_IP6_NF_FILTER=m +CONFIG_IP6_NF_TARGET_LOG=m +CONFIG_IP6_NF_MANGLE=m +CONFIG_IP6_NF_TARGET_MARK=m +CONFIG_IP6_NF_RAW=m + +# +# Bridge: Netfilter Configuration +# +CONFIG_BRIDGE_NF_EBTABLES=m +CONFIG_BRIDGE_EBT_BROUTE=m +CONFIG_BRIDGE_EBT_T_FILTER=m +CONFIG_BRIDGE_EBT_T_NAT=m +CONFIG_BRIDGE_EBT_802_3=m +CONFIG_BRIDGE_EBT_AMONG=m +CONFIG_BRIDGE_EBT_ARP=m +CONFIG_BRIDGE_EBT_IP=m +CONFIG_BRIDGE_EBT_LIMIT=m +CONFIG_BRIDGE_EBT_MARK=m +CONFIG_BRIDGE_EBT_PKTTYPE=m +CONFIG_BRIDGE_EBT_STP=m +CONFIG_BRIDGE_EBT_VLAN=m +CONFIG_BRIDGE_EBT_ARPREPLY=m +CONFIG_BRIDGE_EBT_DNAT=m +CONFIG_BRIDGE_EBT_MARK_T=m +CONFIG_BRIDGE_EBT_REDIRECT=m +CONFIG_BRIDGE_EBT_SNAT=m +CONFIG_BRIDGE_EBT_LOG=m +CONFIG_XFRM=y +CONFIG_XFRM_USER=y + +# +# SCTP Configuration (EXPERIMENTAL) +# +CONFIG_IP_SCTP=m +# CONFIG_SCTP_DBG_MSG is not set +# CONFIG_SCTP_DBG_OBJCNT is not set +# CONFIG_SCTP_HMAC_NONE is not set +# CONFIG_SCTP_HMAC_SHA1 is not set +CONFIG_SCTP_HMAC_MD5=y +CONFIG_ATM=m +CONFIG_ATM_CLIP=m +# CONFIG_ATM_CLIP_NO_ICMP is not set +CONFIG_ATM_LANE=m +# CONFIG_ATM_MPOA is not set +CONFIG_ATM_BR2684=m +# CONFIG_ATM_BR2684_IPFILTER is not set +CONFIG_BRIDGE=m +CONFIG_VLAN_8021Q=m +# CONFIG_DECNET is not set +CONFIG_LLC=y +# CONFIG_LLC2 is not set +CONFIG_IPX=m +# CONFIG_IPX_INTERN is not set +CONFIG_ATALK=m +CONFIG_DEV_APPLETALK=y +CONFIG_LTPC=m +CONFIG_COPS=m +CONFIG_COPS_DAYNA=y +CONFIG_COPS_TANGENT=y +CONFIG_IPDDP=m +CONFIG_IPDDP_ENCAP=y +CONFIG_IPDDP_DECAP=y +# CONFIG_X25 is not set +# CONFIG_LAPB is not set +CONFIG_NET_DIVERT=y +# CONFIG_ECONET is not set +CONFIG_WAN_ROUTER=m + +# +# QoS and/or fair queueing +# +CONFIG_NET_SCHED=y +CONFIG_NET_SCH_CLK_JIFFIES=y +# CONFIG_NET_SCH_CLK_GETTIMEOFDAY is not set +# CONFIG_NET_SCH_CLK_CPU is not set +CONFIG_NET_SCH_CBQ=m +CONFIG_NET_SCH_HTB=m +CONFIG_NET_SCH_HFSC=m +CONFIG_NET_SCH_ATM=m +CONFIG_NET_SCH_PRIO=m +CONFIG_NET_SCH_RED=m +CONFIG_NET_SCH_SFQ=m +CONFIG_NET_SCH_TEQL=m +CONFIG_NET_SCH_TBF=m +CONFIG_NET_SCH_GRED=m +CONFIG_NET_SCH_DSMARK=m +CONFIG_NET_SCH_NETEM=m +CONFIG_NET_SCH_INGRESS=m +CONFIG_NET_QOS=y +CONFIG_NET_ESTIMATOR=y +CONFIG_NET_CLS=y +CONFIG_NET_CLS_TCINDEX=m +CONFIG_NET_CLS_ROUTE4=m +CONFIG_NET_CLS_ROUTE=y +CONFIG_NET_CLS_FW=m +CONFIG_NET_CLS_U32=m +CONFIG_CLS_U32_PERF=y +CONFIG_NET_CLS_IND=y +CONFIG_NET_CLS_RSVP=m +CONFIG_NET_CLS_RSVP6=m +# CONFIG_NET_CLS_ACT is not set +CONFIG_NET_CLS_POLICE=y + +# +# Network testing +# +# CONFIG_NET_PKTGEN is not set +CONFIG_NETPOLL=y +# CONFIG_NETPOLL_RX is not set +CONFIG_NETPOLL_TRAP=y +CONFIG_NET_POLL_CONTROLLER=y +# CONFIG_HAMRADIO is not set +CONFIG_IRDA=m + +# +# IrDA protocols +# +CONFIG_IRLAN=m +CONFIG_IRNET=m +CONFIG_IRCOMM=m +# CONFIG_IRDA_ULTRA is not set + +# +# IrDA options +# +CONFIG_IRDA_CACHE_LAST_LSAP=y +CONFIG_IRDA_FAST_RR=y +# CONFIG_IRDA_DEBUG is not set + +# +# Infrared-port device drivers +# + +# +# SIR device drivers +# +CONFIG_IRTTY_SIR=m + +# +# Dongle support +# +CONFIG_DONGLE=y +CONFIG_ESI_DONGLE=m +CONFIG_ACTISYS_DONGLE=m +CONFIG_TEKRAM_DONGLE=m +CONFIG_LITELINK_DONGLE=m +CONFIG_MA600_DONGLE=m +CONFIG_GIRBIL_DONGLE=m +CONFIG_MCP2120_DONGLE=m +CONFIG_OLD_BELKIN_DONGLE=m +CONFIG_ACT200L_DONGLE=m + +# +# Old SIR device drivers +# +CONFIG_IRPORT_SIR=m + +# +# Old Serial dongle support +# +# CONFIG_DONGLE_OLD is not set + +# +# FIR device drivers +# +CONFIG_USB_IRDA=m +CONFIG_SIGMATEL_FIR=m +CONFIG_NSC_FIR=m +# CONFIG_WINBOND_FIR is not set +# CONFIG_TOSHIBA_FIR is not set +# CONFIG_SMC_IRCC_FIR is not set +# CONFIG_ALI_FIR is not set +# CONFIG_VLSI_FIR is not set +# CONFIG_VIA_FIR is not set +CONFIG_BT=m +CONFIG_BT_L2CAP=m +CONFIG_BT_SCO=m +CONFIG_BT_RFCOMM=m +CONFIG_BT_RFCOMM_TTY=y +CONFIG_BT_BNEP=m +CONFIG_BT_BNEP_MC_FILTER=y +CONFIG_BT_BNEP_PROTO_FILTER=y +CONFIG_BT_CMTP=m +CONFIG_BT_HIDP=m + +# +# Bluetooth device drivers +# +CONFIG_BT_HCIUSB=m +CONFIG_BT_HCIUSB_SCO=y +CONFIG_BT_HCIUART=m +CONFIG_BT_HCIUART_H4=y +CONFIG_BT_HCIUART_BCSP=y +CONFIG_BT_HCIUART_BCSP_TXCRC=y +CONFIG_BT_HCIBCM203X=m +CONFIG_BT_HCIBFUSB=m +CONFIG_BT_HCIDTL1=m +CONFIG_BT_HCIBT3C=m +CONFIG_BT_HCIBLUECARD=m +CONFIG_BT_HCIBTUART=m +CONFIG_BT_HCIVHCI=m +CONFIG_TUX=m + +# +# TUX options +# +CONFIG_TUX_EXTCGI=y +# CONFIG_TUX_EXTENDED_LOG is not set +# CONFIG_TUX_DEBUG is not set +CONFIG_NETDEVICES=y +CONFIG_DUMMY=m +CONFIG_BONDING=m +CONFIG_EQUALIZER=m +CONFIG_TUN=m +CONFIG_ETHERTAP=m +CONFIG_NET_SB1000=m + +# +# ARCnet devices +# +# CONFIG_ARCNET is not set + +# +# Ethernet (10 or 100Mbit) +# +CONFIG_NET_ETHERNET=y +CONFIG_MII=m +CONFIG_HAPPYMEAL=m +CONFIG_SUNGEM=m +CONFIG_NET_VENDOR_3COM=y +CONFIG_EL1=m +CONFIG_EL2=m +CONFIG_ELPLUS=m +CONFIG_EL16=m +CONFIG_EL3=m +CONFIG_3C515=m +CONFIG_VORTEX=m +CONFIG_TYPHOON=m +CONFIG_LANCE=m +CONFIG_NET_VENDOR_SMC=y +CONFIG_WD80x3=m +CONFIG_ULTRA=m +CONFIG_SMC9194=m +CONFIG_NET_VENDOR_RACAL=y +# CONFIG_NI5010 is not set +CONFIG_NI52=m +CONFIG_NI65=m + +# +# Tulip family network device support +# +CONFIG_NET_TULIP=y +CONFIG_DE2104X=m +CONFIG_TULIP=m +# CONFIG_TULIP_MWI is not set +CONFIG_TULIP_MMIO=y +# CONFIG_TULIP_NAPI is not set +CONFIG_DE4X5=m +CONFIG_WINBOND_840=m +CONFIG_DM9102=m +CONFIG_PCMCIA_XIRCOM=m +# CONFIG_PCMCIA_XIRTULIP is not set +# CONFIG_AT1700 is not set +CONFIG_DEPCA=m +CONFIG_HP100=m +CONFIG_NET_ISA=y +CONFIG_E2100=m +CONFIG_EWRK3=m +CONFIG_EEXPRESS=m +CONFIG_EEXPRESS_PRO=m +CONFIG_HPLAN_PLUS=m +CONFIG_HPLAN=m +CONFIG_LP486E=m +CONFIG_ETH16I=m +CONFIG_NE2000=m +CONFIG_ZNET=m +CONFIG_SEEQ8005=m +CONFIG_NET_PCI=y +CONFIG_PCNET32=m +CONFIG_AMD8111_ETH=m +CONFIG_AMD8111E_NAPI=y +CONFIG_ADAPTEC_STARFIRE=m +CONFIG_ADAPTEC_STARFIRE_NAPI=y +CONFIG_AC3200=m +CONFIG_APRICOT=m +CONFIG_B44=m +CONFIG_FORCEDETH=m +CONFIG_CS89x0=m +CONFIG_DGRS=m +CONFIG_EEPRO100=m +# CONFIG_EEPRO100_PIO is not set +CONFIG_E100=m +CONFIG_E100_NAPI=y +CONFIG_FEALNX=m +CONFIG_NATSEMI=m +CONFIG_NE2K_PCI=m +CONFIG_8139CP=m +CONFIG_8139TOO=m +CONFIG_8139TOO_PIO=y +# CONFIG_8139TOO_TUNE_TWISTER is not set +CONFIG_8139TOO_8129=y +# CONFIG_8139_OLD_RX_RESET is not set +CONFIG_SIS900=m +CONFIG_EPIC100=m +CONFIG_SUNDANCE=m +# CONFIG_SUNDANCE_MMIO is not set +CONFIG_TLAN=m +CONFIG_VIA_RHINE=m +CONFIG_VIA_RHINE_MMIO=y +CONFIG_NET_POCKET=y +CONFIG_ATP=m +CONFIG_DE600=m +CONFIG_DE620=m + +# +# Ethernet (1000 Mbit) +# +CONFIG_ACENIC=m +# CONFIG_ACENIC_OMIT_TIGON_I is not set +CONFIG_DL2K=m +CONFIG_E1000=m +CONFIG_E1000_NAPI=y +CONFIG_NS83820=m +CONFIG_HAMACHI=m +CONFIG_YELLOWFIN=m +CONFIG_R8169=m +CONFIG_R8169_NAPI=y +CONFIG_SK98LIN=m +CONFIG_VIA_VELOCITY=m +CONFIG_TIGON3=m + +# +# Ethernet (10000 Mbit) +# +CONFIG_IXGB=m +CONFIG_IXGB_NAPI=y +CONFIG_S2IO=m +CONFIG_S2IO_NAPI=y + +# +# Token Ring devices +# +CONFIG_TR=y +CONFIG_IBMTR=m +CONFIG_IBMOL=m +CONFIG_IBMLS=m +CONFIG_3C359=m +CONFIG_TMS380TR=m +CONFIG_TMSPCI=m +CONFIG_SKISA=m +CONFIG_PROTEON=m +CONFIG_ABYSS=m +CONFIG_SMCTR=m + +# +# Wireless LAN (non-hamradio) +# +CONFIG_NET_RADIO=y + +# +# Obsolete Wireless cards support (pre-802.11) +# +# CONFIG_STRIP is not set +# CONFIG_ARLAN is not set +CONFIG_WAVELAN=m +CONFIG_PCMCIA_WAVELAN=m +CONFIG_PCMCIA_NETWAVE=m + +# +# Wireless 802.11 Frequency Hopping cards support +# +# CONFIG_PCMCIA_RAYCS is not set + +# +# Wireless 802.11b ISA/PCI cards support +# +CONFIG_IEEE80211=m +# CONFIG_IEEE80211_DEBUG is not set +CONFIG_IEEE80211_CRYPT=m +CONFIG_IEEE80211_WPA=m +CONFIG_IEEE80211_CRYPT_CCMP=m +CONFIG_IEEE80211_CRYPT_TKIP=m +CONFIG_IPW2100=m +# CONFIG_IPW_DEBUG is not set +CONFIG_IPW2100_PROMISC=y +# CONFIG_IPW2100_LEGACY_FW_LOAD is not set +CONFIG_IPW2200=m +CONFIG_AIRO=m +CONFIG_HERMES=m +CONFIG_PLX_HERMES=m +CONFIG_TMD_HERMES=m +CONFIG_PCI_HERMES=m +CONFIG_ATMEL=m +CONFIG_PCI_ATMEL=m + +# +# Wireless 802.11b Pcmcia/Cardbus cards support +# +CONFIG_PCMCIA_HERMES=m +CONFIG_AIRO_CS=m +CONFIG_PCMCIA_ATMEL=m +CONFIG_PCMCIA_WL3501=m + +# +# Prism GT/Duette 802.11(a/b/g) PCI/Cardbus support +# +CONFIG_PRISM54=m +CONFIG_NET_WIRELESS=y + +# +# PCMCIA network device support +# +CONFIG_NET_PCMCIA=y +CONFIG_PCMCIA_3C589=m +CONFIG_PCMCIA_3C574=m +CONFIG_PCMCIA_FMVJ18X=m +CONFIG_PCMCIA_PCNET=m +CONFIG_PCMCIA_NMCLAN=m +CONFIG_PCMCIA_SMC91C92=m +CONFIG_PCMCIA_XIRC2PS=m +CONFIG_PCMCIA_AXNET=m +CONFIG_PCMCIA_IBMTR=m + +# +# Wan interfaces +# +# CONFIG_WAN is not set + +# +# ATM drivers +# +CONFIG_ATM_TCP=m +CONFIG_ATM_LANAI=m +CONFIG_ATM_ENI=m +# CONFIG_ATM_ENI_DEBUG is not set +# CONFIG_ATM_ENI_TUNE_BURST is not set +CONFIG_ATM_FIRESTREAM=m +# CONFIG_ATM_ZATM is not set +CONFIG_ATM_NICSTAR=m +# CONFIG_ATM_NICSTAR_USE_SUNI is not set +# CONFIG_ATM_NICSTAR_USE_IDT77105 is not set +CONFIG_ATM_IDT77252=m +# CONFIG_ATM_IDT77252_DEBUG is not set +# CONFIG_ATM_IDT77252_RCV_ALL is not set +CONFIG_ATM_IDT77252_USE_SUNI=y +CONFIG_ATM_AMBASSADOR=m +# CONFIG_ATM_AMBASSADOR_DEBUG is not set +CONFIG_ATM_HORIZON=m +# CONFIG_ATM_HORIZON_DEBUG is not set +# CONFIG_ATM_IA is not set +CONFIG_ATM_FORE200E_MAYBE=m +# CONFIG_ATM_FORE200E_PCA is not set +CONFIG_ATM_HE=m +# CONFIG_ATM_HE_USE_SUNI is not set +CONFIG_FDDI=y +# CONFIG_DEFXX is not set +CONFIG_SKFP=m +# CONFIG_HIPPI is not set +CONFIG_PLIP=m +CONFIG_PPP=m +CONFIG_PPP_MULTILINK=y +CONFIG_PPP_FILTER=y +CONFIG_PPP_ASYNC=m +CONFIG_PPP_SYNC_TTY=m +CONFIG_PPP_DEFLATE=m +# CONFIG_PPP_BSDCOMP is not set +CONFIG_PPPOE=m +CONFIG_PPPOATM=m +CONFIG_SLIP=m +CONFIG_SLIP_COMPRESSED=y +CONFIG_SLIP_SMART=y +# CONFIG_SLIP_MODE_SLIP6 is not set +CONFIG_NET_FC=y +# CONFIG_SHAPER is not set +CONFIG_NETCONSOLE=m + +# +# ISDN subsystem +# +CONFIG_ISDN=m + +# +# Old ISDN4Linux +# +CONFIG_ISDN_I4L=m +CONFIG_ISDN_PPP=y +CONFIG_ISDN_PPP_VJ=y +CONFIG_ISDN_MPP=y +CONFIG_IPPP_FILTER=y +# CONFIG_ISDN_PPP_BSDCOMP is not set +CONFIG_ISDN_AUDIO=y +CONFIG_ISDN_TTY_FAX=y + +# +# ISDN feature submodules +# +CONFIG_ISDN_DRV_LOOP=m + +# +# ISDN4Linux hardware drivers +# + +# +# Passive cards +# +CONFIG_ISDN_DRV_HISAX=m + +# +# D-channel protocol features +# +CONFIG_HISAX_EURO=y +CONFIG_DE_AOC=y +CONFIG_HISAX_NO_SENDCOMPLETE=y +CONFIG_HISAX_NO_LLC=y +CONFIG_HISAX_NO_KEYPAD=y +CONFIG_HISAX_1TR6=y +CONFIG_HISAX_NI1=y +CONFIG_HISAX_MAX_CARDS=8 + +# +# HiSax supported cards +# +CONFIG_HISAX_16_0=y +CONFIG_HISAX_16_3=y +CONFIG_HISAX_TELESPCI=y +CONFIG_HISAX_S0BOX=y +CONFIG_HISAX_AVM_A1=y +CONFIG_HISAX_FRITZPCI=y +CONFIG_HISAX_AVM_A1_PCMCIA=y +CONFIG_HISAX_ELSA=y +CONFIG_HISAX_IX1MICROR2=y +CONFIG_HISAX_DIEHLDIVA=y +CONFIG_HISAX_ASUSCOM=y +CONFIG_HISAX_TELEINT=y +CONFIG_HISAX_HFCS=y +CONFIG_HISAX_SEDLBAUER=y +CONFIG_HISAX_SPORTSTER=y +CONFIG_HISAX_MIC=y +CONFIG_HISAX_NETJET=y +CONFIG_HISAX_NETJET_U=y +CONFIG_HISAX_NICCY=y +CONFIG_HISAX_ISURF=y +CONFIG_HISAX_HSTSAPHIR=y +CONFIG_HISAX_BKM_A4T=y +CONFIG_HISAX_SCT_QUADRO=y +CONFIG_HISAX_GAZEL=y +CONFIG_HISAX_HFC_PCI=y +CONFIG_HISAX_W6692=y +CONFIG_HISAX_HFC_SX=y +CONFIG_HISAX_ENTERNOW_PCI=y +# CONFIG_HISAX_DEBUG is not set + +# +# HiSax PCMCIA card service modules +# +CONFIG_HISAX_SEDLBAUER_CS=m +CONFIG_HISAX_ELSA_CS=m +CONFIG_HISAX_AVM_A1_CS=m +CONFIG_HISAX_TELES_CS=m + +# +# HiSax sub driver modules +# +CONFIG_HISAX_ST5481=m +CONFIG_HISAX_HFCUSB=m +CONFIG_HISAX_FRITZ_PCIPNP=m +CONFIG_HISAX_HDLC=y + +# +# Active cards +# +CONFIG_ISDN_DRV_ICN=m +CONFIG_ISDN_DRV_PCBIT=m +CONFIG_ISDN_DRV_SC=m +CONFIG_ISDN_DRV_ACT2000=m +CONFIG_ISDN_DRV_TPAM=m +CONFIG_HYSDN=m +CONFIG_HYSDN_CAPI=y + +# +# CAPI subsystem +# +CONFIG_ISDN_CAPI=m +CONFIG_ISDN_DRV_AVMB1_VERBOSE_REASON=y +CONFIG_ISDN_CAPI_MIDDLEWARE=y +CONFIG_ISDN_CAPI_CAPI20=m +CONFIG_ISDN_CAPI_CAPIFS_BOOL=y +CONFIG_ISDN_CAPI_CAPIFS=m +CONFIG_ISDN_CAPI_CAPIDRV=m + +# +# CAPI hardware drivers +# + +# +# Active AVM cards +# +CONFIG_CAPI_AVM=y +CONFIG_ISDN_DRV_AVMB1_B1ISA=m +CONFIG_ISDN_DRV_AVMB1_B1PCI=m +CONFIG_ISDN_DRV_AVMB1_B1PCIV4=y +CONFIG_ISDN_DRV_AVMB1_T1ISA=m +CONFIG_ISDN_DRV_AVMB1_B1PCMCIA=m +CONFIG_ISDN_DRV_AVMB1_AVM_CS=m +CONFIG_ISDN_DRV_AVMB1_T1PCI=m +CONFIG_ISDN_DRV_AVMB1_C4=m + +# +# Active Eicon DIVA Server cards +# +CONFIG_CAPI_EICON=y +CONFIG_ISDN_DIVAS=m +CONFIG_ISDN_DIVAS_BRIPCI=y +CONFIG_ISDN_DIVAS_PRIPCI=y +CONFIG_ISDN_DIVAS_DIVACAPI=m +CONFIG_ISDN_DIVAS_USERIDI=m +CONFIG_ISDN_DIVAS_MAINT=m + +# +# Telephony Support +# +# CONFIG_PHONE is not set + +# +# Input device support +# +CONFIG_INPUT=y + +# +# 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=m +# CONFIG_INPUT_TSDEV is not set +CONFIG_INPUT_EVDEV=y +# CONFIG_INPUT_EVBUG is not set + +# +# Input I/O drivers +# +CONFIG_GAMEPORT=m +CONFIG_SOUND_GAMEPORT=m +CONFIG_GAMEPORT_NS558=m +CONFIG_GAMEPORT_L4=m +CONFIG_GAMEPORT_EMU10K1=m +CONFIG_GAMEPORT_VORTEX=m +CONFIG_GAMEPORT_FM801=m +CONFIG_GAMEPORT_CS461x=m +CONFIG_SERIO=y +CONFIG_SERIO_I8042=y +CONFIG_SERIO_SERPORT=y +# CONFIG_SERIO_CT82C710 is not set +# CONFIG_SERIO_PARKBD is not set +# CONFIG_SERIO_PCIPS2 is not set +# CONFIG_SERIO_RAW 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_INPUT_MOUSE=y +CONFIG_MOUSE_PS2=y +CONFIG_MOUSE_SERIAL=m +CONFIG_MOUSE_INPORT=m +CONFIG_MOUSE_ATIXL=y +CONFIG_MOUSE_LOGIBM=m +CONFIG_MOUSE_PC110PAD=m +CONFIG_MOUSE_VSXXXAA=m +CONFIG_INPUT_JOYSTICK=y +CONFIG_JOYSTICK_ANALOG=m +CONFIG_JOYSTICK_A3D=m +CONFIG_JOYSTICK_ADI=m +CONFIG_JOYSTICK_COBRA=m +CONFIG_JOYSTICK_GF2K=m +CONFIG_JOYSTICK_GRIP=m +CONFIG_JOYSTICK_GRIP_MP=m +CONFIG_JOYSTICK_GUILLEMOT=m +CONFIG_JOYSTICK_INTERACT=m +CONFIG_JOYSTICK_SIDEWINDER=m +CONFIG_JOYSTICK_TMDC=m +CONFIG_JOYSTICK_IFORCE=m +CONFIG_JOYSTICK_IFORCE_USB=y +CONFIG_JOYSTICK_IFORCE_232=y +CONFIG_JOYSTICK_WARRIOR=m +CONFIG_JOYSTICK_MAGELLAN=m +CONFIG_JOYSTICK_SPACEORB=m +CONFIG_JOYSTICK_SPACEBALL=m +CONFIG_JOYSTICK_STINGER=m +CONFIG_JOYSTICK_TWIDDLER=m +CONFIG_JOYSTICK_DB9=m +CONFIG_JOYSTICK_GAMECON=m +CONFIG_JOYSTICK_TURBOGRAFX=m +CONFIG_JOYSTICK_JOYDUMP=m +CONFIG_INPUT_TOUCHSCREEN=y +CONFIG_TOUCHSCREEN_GUNZE=m +CONFIG_INPUT_MISC=y +CONFIG_INPUT_PCSPKR=m +CONFIG_INPUT_UINPUT=m + +# +# Character devices +# +CONFIG_VT=y +CONFIG_VT_CONSOLE=y +CONFIG_HW_CONSOLE=y +CONFIG_SERIAL_NONSTANDARD=y +# CONFIG_COMPUTONE is not set +CONFIG_ROCKETPORT=m +# CONFIG_CYCLADES is not set +# CONFIG_DIGIEPCA is not set +# CONFIG_DIGI is not set +# CONFIG_ESPSERIAL is not set +# CONFIG_MOXA_INTELLIO is not set +# CONFIG_MOXA_SMARTIO is not set +# CONFIG_ISI is not set +CONFIG_SYNCLINK=m +CONFIG_SYNCLINKMP=m +CONFIG_N_HDLC=m +# CONFIG_RISCOM8 is not set +# CONFIG_SPECIALIX is not set +# CONFIG_SX is not set +# CONFIG_RIO is not set +CONFIG_STALDRV=y +# CONFIG_STALLION is not set +# CONFIG_ISTALLION is not set + +# +# Serial drivers +# +CONFIG_SERIAL_8250=y +CONFIG_SERIAL_8250_CONSOLE=y +CONFIG_SERIAL_8250_CS=m +# CONFIG_SERIAL_8250_ACPI is not set +CONFIG_SERIAL_8250_NR_UARTS=32 +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_MULTIPORT=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_CRASH=m +CONFIG_PRINTER=m +CONFIG_LP_CONSOLE=y +CONFIG_PPDEV=m +CONFIG_TIPAR=m + +# +# IPMI +# +CONFIG_IPMI_HANDLER=m +# CONFIG_IPMI_PANIC_EVENT is not set +CONFIG_IPMI_DEVICE_INTERFACE=m +CONFIG_IPMI_SI=m +CONFIG_IPMI_WATCHDOG=m +CONFIG_IPMI_POWEROFF=m + +# +# Watchdog Cards +# +CONFIG_WATCHDOG=y +# CONFIG_WATCHDOG_NOWAYOUT is not set + +# +# Watchdog Device Drivers +# +CONFIG_SOFT_WATCHDOG=m +CONFIG_ACQUIRE_WDT=m +CONFIG_ADVANTECH_WDT=m +CONFIG_ALIM1535_WDT=m +CONFIG_ALIM7101_WDT=m +CONFIG_SC520_WDT=m +CONFIG_EUROTECH_WDT=m +CONFIG_IB700_WDT=m +CONFIG_WAFER_WDT=m +CONFIG_I8XX_TCO=m +CONFIG_SC1200_WDT=m +# CONFIG_SCx200_WDT is not set +# CONFIG_60XX_WDT is not set +CONFIG_CPU5_WDT=m +CONFIG_W83627HF_WDT=m +CONFIG_W83877F_WDT=m +CONFIG_MACHZ_WDT=m + +# +# ISA-based Watchdog Cards +# +CONFIG_PCWATCHDOG=m +# CONFIG_MIXCOMWD is not set +CONFIG_WDT=m +# CONFIG_WDT_501 is not set + +# +# PCI-based Watchdog Cards +# +CONFIG_PCIPCWATCHDOG=m +CONFIG_WDTPCI=m +CONFIG_WDT_501_PCI=y + +# +# USB-based Watchdog Cards +# +CONFIG_USBPCWATCHDOG=m +CONFIG_HW_RANDOM=m +CONFIG_NVRAM=m +CONFIG_RTC=y +CONFIG_DTLK=m +CONFIG_R3964=m +# CONFIG_APPLICOM is not set +CONFIG_SONYPI=m + +# +# Ftape, the floppy tape device driver +# +# CONFIG_FTAPE is not set +CONFIG_AGP=y +CONFIG_AGP_ALI=y +CONFIG_AGP_ATI=y +CONFIG_AGP_AMD=y +CONFIG_AGP_AMD64=y +CONFIG_AGP_INTEL=y +CONFIG_AGP_INTEL_MCH=y +CONFIG_AGP_NVIDIA=y +CONFIG_AGP_SIS=y +CONFIG_AGP_SWORKS=y +CONFIG_AGP_VIA=y +CONFIG_AGP_EFFICEON=y +CONFIG_DRM=y +CONFIG_DRM_TDFX=m +CONFIG_DRM_R128=m +CONFIG_DRM_RADEON=m +CONFIG_DRM_I810=m +CONFIG_DRM_I830=m +CONFIG_DRM_I915=m +CONFIG_DRM_MGA=m +CONFIG_DRM_SIS=m + +# +# PCMCIA character devices +# +CONFIG_SYNCLINK_CS=m +CONFIG_MWAVE=m +CONFIG_RAW_DRIVER=m +# CONFIG_HPET is not set +CONFIG_MAX_RAW_DEVS=8192 +CONFIG_HANGCHECK_TIMER=m + +# +# I2C support +# +CONFIG_I2C=m +CONFIG_I2C_CHARDEV=m + +# +# I2C Algorithms +# +CONFIG_I2C_ALGOBIT=m +CONFIG_I2C_ALGOPCF=m +CONFIG_I2C_ALGOPCA=m + +# +# I2C Hardware Bus support +# +CONFIG_I2C_ALI1535=m +CONFIG_I2C_ALI1563=m +CONFIG_I2C_ALI15X3=m +CONFIG_I2C_AMD756=m +CONFIG_I2C_AMD8111=m +# CONFIG_I2C_ELEKTOR is not set +CONFIG_I2C_I801=m +CONFIG_I2C_I810=m +CONFIG_I2C_ISA=m +CONFIG_I2C_NFORCE2=m +# CONFIG_I2C_PARPORT is not set +# CONFIG_I2C_PARPORT_LIGHT is not set +CONFIG_I2C_PIIX4=m +CONFIG_I2C_PROSAVAGE=m +CONFIG_I2C_SAVAGE4=m +# CONFIG_SCx200_ACB is not set +CONFIG_I2C_SIS5595=m +CONFIG_I2C_SIS630=m +CONFIG_I2C_SIS96X=m +CONFIG_I2C_VIA=m +CONFIG_I2C_VIAPRO=m +CONFIG_I2C_VOODOO3=m +CONFIG_I2C_PCA_ISA=m + +# +# Hardware Sensors Chip support +# +CONFIG_I2C_SENSOR=m +CONFIG_SENSORS_ADM1021=m +CONFIG_SENSORS_ADM1025=m +CONFIG_SENSORS_ADM1031=m +CONFIG_SENSORS_ASB100=m +CONFIG_SENSORS_DS1621=m +CONFIG_SENSORS_FSCHER=m +CONFIG_SENSORS_GL518SM=m +CONFIG_SENSORS_IT87=m +CONFIG_SENSORS_LM75=m +CONFIG_SENSORS_LM77=m +CONFIG_SENSORS_LM78=m +CONFIG_SENSORS_LM80=m +CONFIG_SENSORS_LM83=m +CONFIG_SENSORS_LM85=m +CONFIG_SENSORS_LM90=m +CONFIG_SENSORS_MAX1619=m +CONFIG_SENSORS_SMSC47M1=m +CONFIG_SENSORS_VIA686A=m +CONFIG_SENSORS_W83781D=m +CONFIG_SENSORS_W83L785TS=m +CONFIG_SENSORS_W83627HF=m + +# +# Other I2C Chip support +# +CONFIG_SENSORS_EEPROM=m +CONFIG_SENSORS_PCF8574=m +CONFIG_SENSORS_PCF8591=m +CONFIG_SENSORS_RTC8564=m +# 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 + +# +# Dallas's 1-wire bus +# +# CONFIG_W1 is not set + +# +# Misc devices +# +CONFIG_IBM_ASM=m + +# +# Multimedia devices +# +CONFIG_VIDEO_DEV=m + +# +# Video For Linux +# + +# +# Video Adapters +# +CONFIG_VIDEO_BT848=m +CONFIG_VIDEO_PMS=m +CONFIG_VIDEO_BWQCAM=m +CONFIG_VIDEO_CQCAM=m +CONFIG_VIDEO_W9966=m +CONFIG_VIDEO_CPIA=m +CONFIG_VIDEO_CPIA_PP=m +CONFIG_VIDEO_CPIA_USB=m +CONFIG_VIDEO_SAA5246A=m +CONFIG_VIDEO_SAA5249=m +CONFIG_TUNER_3036=m +CONFIG_VIDEO_STRADIS=m +CONFIG_VIDEO_ZORAN=m +CONFIG_VIDEO_ZORAN_BUZ=m +CONFIG_VIDEO_ZORAN_DC10=m +CONFIG_VIDEO_ZORAN_DC30=m +CONFIG_VIDEO_ZORAN_LML33=m +CONFIG_VIDEO_ZORAN_LML33R10=m +CONFIG_VIDEO_MEYE=m +CONFIG_VIDEO_SAA7134=m +CONFIG_VIDEO_MXB=m +CONFIG_VIDEO_DPC=m +CONFIG_VIDEO_HEXIUM_ORION=m +CONFIG_VIDEO_HEXIUM_GEMINI=m +CONFIG_VIDEO_CX88=m +CONFIG_VIDEO_OVCAMCHIP=m + +# +# Radio Adapters +# +CONFIG_RADIO_CADET=m +CONFIG_RADIO_RTRACK=m +CONFIG_RADIO_RTRACK2=m +CONFIG_RADIO_AZTECH=m +CONFIG_RADIO_GEMTEK=m +CONFIG_RADIO_GEMTEK_PCI=m +CONFIG_RADIO_MAXIRADIO=m +CONFIG_RADIO_MAESTRO=m +CONFIG_RADIO_SF16FMI=m +CONFIG_RADIO_SF16FMR2=m +CONFIG_RADIO_TERRATEC=m +CONFIG_RADIO_TRUST=m +CONFIG_RADIO_TYPHOON=m +CONFIG_RADIO_TYPHOON_PROC_FS=y +CONFIG_RADIO_ZOLTRIX=m + +# +# Digital Video Broadcasting Devices +# +CONFIG_DVB=y +CONFIG_DVB_CORE=m + +# +# Supported Frontend Modules +# +CONFIG_DVB_TWINHAN_DST=m +CONFIG_DVB_STV0299=m +# CONFIG_DVB_SP887X is not set +# CONFIG_DVB_ALPS_TDLB7 is not set +CONFIG_DVB_ALPS_TDMB7=m +CONFIG_DVB_ATMEL_AT76C651=m +CONFIG_DVB_CX24110=m +CONFIG_DVB_GRUNDIG_29504_491=m +CONFIG_DVB_GRUNDIG_29504_401=m +CONFIG_DVB_MT312=m +CONFIG_DVB_VES1820=m +CONFIG_DVB_VES1X93=m +# CONFIG_DVB_TDA1004X is not set +CONFIG_DVB_NXT6000=m + +# +# Supported SAA7146 based PCI Adapters +# +CONFIG_DVB_AV7110=m +CONFIG_DVB_AV7110_OSD=y +CONFIG_DVB_BUDGET=m +CONFIG_DVB_BUDGET_CI=m +CONFIG_DVB_BUDGET_AV=m +CONFIG_DVB_BUDGET_PATCH=m + +# +# Supported USB Adapters +# +CONFIG_DVB_TTUSB_BUDGET=m +CONFIG_DVB_TTUSB_DEC=m + +# +# Supported FlexCopII (B2C2) Adapters +# +CONFIG_DVB_B2C2_SKYSTAR=m + +# +# Supported BT878 Adapters +# +CONFIG_DVB_BT8XX=m +CONFIG_VIDEO_SAA7146=m +CONFIG_VIDEO_SAA7146_VV=m +CONFIG_VIDEO_VIDEOBUF=m +CONFIG_VIDEO_TUNER=m +CONFIG_VIDEO_BUF=m +CONFIG_VIDEO_BTCX=m +CONFIG_VIDEO_IR=m + +# +# Graphics support +# +CONFIG_FB=y +CONFIG_FB_MODE_HELPERS=y +CONFIG_FB_CIRRUS=m +# CONFIG_FB_PM2 is not set +# CONFIG_FB_CYBER2000 is not set +# CONFIG_FB_ASILIANT is not set +# CONFIG_FB_IMSTT is not set +CONFIG_FB_VGA16=m +CONFIG_FB_VESA=y +CONFIG_VIDEO_SELECT=y +CONFIG_FB_HGA=m +CONFIG_FB_HGA_ACCEL=y +CONFIG_FB_RIVA=m +# CONFIG_FB_RIVA_I2C is not set +# CONFIG_FB_RIVA_DEBUG is not set +CONFIG_FB_I810=m +CONFIG_FB_I810_GTF=y +CONFIG_FB_MATROX=m +CONFIG_FB_MATROX_MILLENIUM=y +CONFIG_FB_MATROX_MYSTIQUE=y +CONFIG_FB_MATROX_G450=y +CONFIG_FB_MATROX_G100=y +CONFIG_FB_MATROX_I2C=m +CONFIG_FB_MATROX_MAVEN=m +CONFIG_FB_MATROX_MULTIHEAD=y +# CONFIG_FB_RADEON_OLD is not set +CONFIG_FB_RADEON=m +CONFIG_FB_RADEON_I2C=y +# CONFIG_FB_RADEON_DEBUG is not set +CONFIG_FB_ATY128=m +CONFIG_FB_ATY=m +CONFIG_FB_ATY_CT=y +CONFIG_FB_ATY_GX=y +# CONFIG_FB_ATY_XL_INIT is not set +# CONFIG_FB_SIS is not set +CONFIG_FB_NEOMAGIC=m +CONFIG_FB_KYRO=m +CONFIG_FB_3DFX=m +CONFIG_FB_3DFX_ACCEL=y +CONFIG_FB_VOODOO1=m +CONFIG_FB_TRIDENT=m +CONFIG_FB_TRIDENT_ACCEL=y +# CONFIG_FB_VIRTUAL is not set + +# +# Console display driver support +# +CONFIG_VGA_CONSOLE=y +CONFIG_MDA_CONSOLE=m +CONFIG_DUMMY_CONSOLE=y +CONFIG_FRAMEBUFFER_CONSOLE=y +# 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 + +# +# Sound +# +CONFIG_SOUND=m + +# +# Advanced Linux Sound Architecture +# +CONFIG_SND=m +CONFIG_SND_TIMER=m +CONFIG_SND_PCM=m +CONFIG_SND_HWDEP=m +CONFIG_SND_RAWMIDI=m +CONFIG_SND_SEQUENCER=m +CONFIG_SND_SEQ_DUMMY=m +CONFIG_SND_OSSEMUL=y +CONFIG_SND_MIXER_OSS=m +CONFIG_SND_PCM_OSS=m +CONFIG_SND_SEQUENCER_OSS=y +CONFIG_SND_RTCTIMER=m +# CONFIG_SND_VERBOSE_PRINTK is not set +# CONFIG_SND_DEBUG is not set + +# +# Generic devices +# +CONFIG_SND_MPU401_UART=m +CONFIG_SND_OPL3_LIB=m +CONFIG_SND_OPL4_LIB=m +CONFIG_SND_VX_LIB=m +CONFIG_SND_DUMMY=m +CONFIG_SND_VIRMIDI=m +CONFIG_SND_MTPAV=m +# CONFIG_SND_SERIAL_U16550 is not set +CONFIG_SND_MPU401=m + +# +# ISA devices +# +CONFIG_SND_AD1816A=m +CONFIG_SND_AD1848=m +CONFIG_SND_CS4231=m +CONFIG_SND_CS4232=m +CONFIG_SND_CS4236=m +CONFIG_SND_ES968=m +CONFIG_SND_ES1688=m +CONFIG_SND_ES18XX=m +CONFIG_SND_GUS_SYNTH=m +CONFIG_SND_GUSCLASSIC=m +CONFIG_SND_GUSEXTREME=m +CONFIG_SND_GUSMAX=m +CONFIG_SND_INTERWAVE=m +CONFIG_SND_INTERWAVE_STB=m +CONFIG_SND_OPTI92X_AD1848=m +CONFIG_SND_OPTI92X_CS4231=m +CONFIG_SND_OPTI93X=m +CONFIG_SND_SB8=m +CONFIG_SND_SB16=m +CONFIG_SND_SBAWE=m +CONFIG_SND_SB16_CSP=y +# CONFIG_SND_WAVEFRONT is not set +CONFIG_SND_ALS100=m +CONFIG_SND_AZT2320=m +CONFIG_SND_CMI8330=m +CONFIG_SND_DT019X=m +CONFIG_SND_OPL3SA2=m +CONFIG_SND_SGALAXY=m +CONFIG_SND_SSCAPE=m + +# +# PCI devices +# +CONFIG_SND_AC97_CODEC=m +CONFIG_SND_ALI5451=m +CONFIG_SND_ATIIXP=m +CONFIG_SND_ATIIXP_MODEM=m +CONFIG_SND_AU8810=m +CONFIG_SND_AU8820=m +CONFIG_SND_AU8830=m +CONFIG_SND_AZT3328=m +CONFIG_SND_BT87X=m +CONFIG_SND_CS46XX=m +CONFIG_SND_CS46XX_NEW_DSP=y +CONFIG_SND_CS4281=m +CONFIG_SND_EMU10K1=m +CONFIG_SND_KORG1212=m +CONFIG_SND_MIXART=m +CONFIG_SND_NM256=m +CONFIG_SND_RME32=m +CONFIG_SND_RME96=m +CONFIG_SND_RME9652=m +CONFIG_SND_HDSP=m +CONFIG_SND_TRIDENT=m +CONFIG_SND_YMFPCI=m +CONFIG_SND_ALS4000=m +CONFIG_SND_CMIPCI=m +CONFIG_SND_ENS1370=m +CONFIG_SND_ENS1371=m +CONFIG_SND_ES1938=m +CONFIG_SND_ES1968=m +CONFIG_SND_MAESTRO3=m +CONFIG_SND_FM801=m +CONFIG_SND_FM801_TEA575X=m +CONFIG_SND_ICE1712=m +CONFIG_SND_ICE1724=m +CONFIG_SND_INTEL8X0=m +CONFIG_SND_INTEL8X0M=m +CONFIG_SND_SONICVIBES=m +CONFIG_SND_VIA82XX=m +CONFIG_SND_VX222=m + +# +# ALSA USB devices +# +CONFIG_SND_USB_AUDIO=m +CONFIG_SND_USB_USX2Y=m + +# +# PCMCIA devices +# +# CONFIG_SND_VXPOCKET is not set +# CONFIG_SND_VXP440 is not set +CONFIG_SND_PDAUDIOCF=m + +# +# Open Sound System +# +# CONFIG_SOUND_PRIME is not set + +# +# USB support +# +CONFIG_USB=y +# CONFIG_USB_DEBUG is not set + +# +# Miscellaneous USB options +# +CONFIG_USB_DEVICEFS=y +# CONFIG_USB_BANDWIDTH is not set +# CONFIG_USB_DYNAMIC_MINORS is not set +# CONFIG_USB_SUSPEND is not set +# CONFIG_USB_OTG is not set + +# +# USB Host Controller Drivers +# +CONFIG_USB_EHCI_HCD=m +CONFIG_USB_EHCI_SPLIT_ISO=y +CONFIG_USB_EHCI_ROOT_HUB_TT=y +CONFIG_USB_OHCI_HCD=m +CONFIG_USB_UHCI_HCD=m + +# +# USB Device Class drivers +# +# CONFIG_USB_AUDIO is not set + +# +# USB Bluetooth TTY can only be used with disabled Bluetooth subsystem +# +CONFIG_USB_MIDI=m +CONFIG_USB_ACM=m +CONFIG_USB_PRINTER=m +CONFIG_USB_STORAGE=m +# CONFIG_USB_STORAGE_DEBUG is not set +CONFIG_USB_STORAGE_RW_DETECT=y +CONFIG_USB_STORAGE_DATAFAB=y +CONFIG_USB_STORAGE_FREECOM=y +CONFIG_USB_STORAGE_ISD200=y +CONFIG_USB_STORAGE_DPCM=y +CONFIG_USB_STORAGE_HP8200e=y +CONFIG_USB_STORAGE_SDDR09=y +CONFIG_USB_STORAGE_SDDR55=y +CONFIG_USB_STORAGE_JUMPSHOT=y + +# +# USB Human Interface Devices (HID) +# +CONFIG_USB_HID=y +CONFIG_USB_HIDINPUT=y +CONFIG_HID_FF=y +CONFIG_HID_PID=y +CONFIG_LOGITECH_FF=y +CONFIG_THRUSTMASTER_FF=y +CONFIG_USB_HIDDEV=y +CONFIG_USB_AIPTEK=m +CONFIG_USB_WACOM=m +CONFIG_USB_KBTAB=m +CONFIG_USB_POWERMATE=m +CONFIG_USB_MTOUCH=m +CONFIG_USB_EGALAX=m +CONFIG_USB_XPAD=m +CONFIG_USB_ATI_REMOTE=m + +# +# USB Imaging devices +# +CONFIG_USB_MDC800=m +CONFIG_USB_MICROTEK=m +CONFIG_USB_HPUSBSCSI=m + +# +# USB Multimedia devices +# +CONFIG_USB_DABUSB=m +CONFIG_USB_VICAM=m +CONFIG_USB_DSBR=m +CONFIG_USB_IBMCAM=m +CONFIG_USB_KONICAWC=m +CONFIG_USB_OV511=m +CONFIG_USB_SE401=m +CONFIG_USB_SN9C102=m +CONFIG_USB_STV680=m +CONFIG_USB_W9968CF=m +CONFIG_USB_PWC=m + +# +# USB Network adaptors +# +CONFIG_USB_CATC=m +CONFIG_USB_KAWETH=m +CONFIG_USB_PEGASUS=m +CONFIG_USB_RTL8150=m +CONFIG_USB_USBNET=m + +# +# USB Host-to-Host Cables +# +CONFIG_USB_ALI_M5632=y +CONFIG_USB_AN2720=y +CONFIG_USB_BELKIN=y +CONFIG_USB_GENESYS=y +CONFIG_USB_NET1080=y +CONFIG_USB_PL2301=y + +# +# Intelligent USB Devices/Gadgets +# +CONFIG_USB_ARMLINUX=y +CONFIG_USB_EPSON2888=y +CONFIG_USB_ZAURUS=y +CONFIG_USB_CDCETHER=y + +# +# USB Network Adapters +# +CONFIG_USB_AX8817X=y + +# +# USB port drivers +# +CONFIG_USB_USS720=m + +# +# USB Serial Converter support +# +CONFIG_USB_SERIAL=m +CONFIG_USB_SERIAL_GENERIC=y +CONFIG_USB_SERIAL_BELKIN=m +CONFIG_USB_SERIAL_WHITEHEAT=m +CONFIG_USB_SERIAL_DIGI_ACCELEPORT=m +CONFIG_USB_SERIAL_EMPEG=m +CONFIG_USB_SERIAL_FTDI_SIO=m +CONFIG_USB_SERIAL_VISOR=m +CONFIG_USB_SERIAL_IPAQ=m +CONFIG_USB_SERIAL_IR=m +CONFIG_USB_SERIAL_EDGEPORT=m +CONFIG_USB_SERIAL_EDGEPORT_TI=m +CONFIG_USB_SERIAL_KEYSPAN_PDA=m +CONFIG_USB_SERIAL_KEYSPAN=m +CONFIG_USB_SERIAL_KEYSPAN_MPR=y +CONFIG_USB_SERIAL_KEYSPAN_USA28=y +CONFIG_USB_SERIAL_KEYSPAN_USA28X=y +CONFIG_USB_SERIAL_KEYSPAN_USA28XA=y +CONFIG_USB_SERIAL_KEYSPAN_USA28XB=y +CONFIG_USB_SERIAL_KEYSPAN_USA19=y +CONFIG_USB_SERIAL_KEYSPAN_USA18X=y +CONFIG_USB_SERIAL_KEYSPAN_USA19W=y +CONFIG_USB_SERIAL_KEYSPAN_USA19QW=y +CONFIG_USB_SERIAL_KEYSPAN_USA19QI=y +CONFIG_USB_SERIAL_KEYSPAN_USA49W=y +CONFIG_USB_SERIAL_KEYSPAN_USA49WLC=y +CONFIG_USB_SERIAL_KLSI=m +CONFIG_USB_SERIAL_KOBIL_SCT=m +CONFIG_USB_SERIAL_MCT_U232=m +CONFIG_USB_SERIAL_PL2303=m +CONFIG_USB_SERIAL_SAFE=m +CONFIG_USB_SERIAL_SAFE_PADDED=y +CONFIG_USB_SERIAL_CYBERJACK=m +CONFIG_USB_SERIAL_XIRCOM=m +CONFIG_USB_SERIAL_OMNINET=m +CONFIG_USB_EZUSB=y + +# +# USB Miscellaneous drivers +# +CONFIG_USB_EMI62=m +# CONFIG_USB_EMI26 is not set +# CONFIG_USB_TIGL is not set +CONFIG_USB_AUERSWALD=m +CONFIG_USB_RIO500=m +CONFIG_USB_LEGOTOWER=m +CONFIG_USB_LCD=m +CONFIG_USB_LED=m +# CONFIG_USB_CYTHERM is not set +CONFIG_USB_PHIDGETSERVO=m +CONFIG_USB_TEST=m + +# +# USB ATM/DSL drivers +# +CONFIG_USB_ATM=m +CONFIG_USB_SPEEDTOUCH=m + +# +# USB Gadget Support +# +# CONFIG_USB_GADGET is not set + +# +# File systems +# +CONFIG_EXT2_FS=y +CONFIG_EXT2_FS_XATTR=y +CONFIG_EXT2_FS_POSIX_ACL=y +CONFIG_EXT2_FS_SECURITY=y +CONFIG_EXT3_FS=m +CONFIG_EXT3_FS_XATTR=y +CONFIG_EXT3_FS_POSIX_ACL=y +CONFIG_EXT3_FS_SECURITY=y +CONFIG_JBD=m +# CONFIG_JBD_DEBUG is not set +CONFIG_FS_MBCACHE=y +CONFIG_REISERFS_FS=m +# CONFIG_REISERFS_CHECK is not set +CONFIG_REISERFS_PROC_INFO=y +CONFIG_REISERFS_FS_XATTR=y +CONFIG_REISERFS_FS_POSIX_ACL=y +CONFIG_REISERFS_FS_SECURITY=y +CONFIG_JFS_FS=m +CONFIG_JFS_POSIX_ACL=y +# CONFIG_JFS_DEBUG is not set +# CONFIG_JFS_STATISTICS is not set +CONFIG_FS_POSIX_ACL=y +CONFIG_XFS_FS=m +# CONFIG_XFS_RT is not set +CONFIG_XFS_QUOTA=y +CONFIG_XFS_SECURITY=y +CONFIG_XFS_POSIX_ACL=y +CONFIG_MINIX_FS=m +CONFIG_ROMFS_FS=m +CONFIG_QUOTA=y +# CONFIG_QFMT_V1 is not set +CONFIG_QFMT_V2=y +CONFIG_QUOTACTL=y +CONFIG_AUTOFS_FS=m +CONFIG_AUTOFS4_FS=m + +# +# CD-ROM/DVD Filesystems +# +CONFIG_ISO9660_FS=y +CONFIG_JOLIET=y +CONFIG_ZISOFS=y +CONFIG_ZISOFS_FS=y +CONFIG_UDF_FS=m +CONFIG_UDF_NLS=y + +# +# DOS/FAT/NT Filesystems +# +CONFIG_FAT_FS=m +CONFIG_MSDOS_FS=m +CONFIG_VFAT_FS=m +CONFIG_FAT_DEFAULT_CODEPAGE=437 +CONFIG_FAT_DEFAULT_IOCHARSET="ascii" +# CONFIG_NTFS_FS is not set + +# +# Pseudo filesystems +# +CONFIG_PROC_FS=y +CONFIG_PROC_KCORE=y +CONFIG_SYSFS=y +# CONFIG_DEVFS_FS is not set +CONFIG_DEVPTS_FS_XATTR=y +CONFIG_DEVPTS_FS_SECURITY=y +CONFIG_TMPFS=y +CONFIG_TMPFS_XATTR=y +CONFIG_TMPFS_SECURITY=y +CONFIG_HUGETLBFS=y +CONFIG_HUGETLB_PAGE=y +CONFIG_RAMFS=y + +# +# Miscellaneous filesystems +# +# CONFIG_ADFS_FS is not set +CONFIG_AFFS_FS=m +CONFIG_HFS_FS=m +CONFIG_HFSPLUS_FS=m +CONFIG_BEFS_FS=m +# CONFIG_BEFS_DEBUG is not set +CONFIG_BFS_FS=m +CONFIG_EFS_FS=m +# CONFIG_JFFS_FS is not set +CONFIG_JFFS2_FS=m +CONFIG_JFFS2_FS_DEBUG=0 +CONFIG_JFFS2_FS_NAND=y +# CONFIG_JFFS2_COMPRESSION_OPTIONS is not set +CONFIG_JFFS2_ZLIB=y +CONFIG_JFFS2_RTIME=y +# CONFIG_JFFS2_RUBIN is not set +CONFIG_CRAMFS=m +CONFIG_VXFS_FS=m +# CONFIG_HPFS_FS is not set +CONFIG_QNX4FS_FS=m +# CONFIG_QNX4FS_RW is not set +CONFIG_SYSV_FS=m +CONFIG_UFS_FS=m +# CONFIG_UFS_FS_WRITE is not set + +# +# Network File Systems +# +CONFIG_NFS_FS=m +CONFIG_NFS_V3=y +CONFIG_NFS_V4=y +CONFIG_NFS_DIRECTIO=y +CONFIG_NFSD=m +CONFIG_NFSD_V3=y +CONFIG_NFSD_V4=y +CONFIG_NFSD_TCP=y +CONFIG_LOCKD=m +CONFIG_LOCKD_V4=y +CONFIG_EXPORTFS=m +CONFIG_SUNRPC=m +CONFIG_SUNRPC_GSS=m +CONFIG_RPCSEC_GSS_KRB5=m +CONFIG_RPCSEC_GSS_SPKM3=m +CONFIG_SMB_FS=m +# CONFIG_SMB_NLS_DEFAULT is not set +CONFIG_CIFS=m +# CONFIG_CIFS_STATS is not set +CONFIG_CIFS_XATTR=y +CONFIG_CIFS_POSIX=y +CONFIG_NCP_FS=m +CONFIG_NCPFS_PACKET_SIGNING=y +CONFIG_NCPFS_IOCTL_LOCKING=y +CONFIG_NCPFS_STRONG=y +CONFIG_NCPFS_NFS_NS=y +CONFIG_NCPFS_OS2_NS=y +CONFIG_NCPFS_SMALLDOS=y +CONFIG_NCPFS_NLS=y +CONFIG_NCPFS_EXTRAS=y +# CONFIG_CODA_FS is not set +# CONFIG_AFS_FS is not set + +# +# Partition Types +# +CONFIG_PARTITION_ADVANCED=y +# CONFIG_ACORN_PARTITION is not set +CONFIG_OSF_PARTITION=y +# CONFIG_AMIGA_PARTITION is not set +# CONFIG_ATARI_PARTITION is not set +CONFIG_MAC_PARTITION=y +CONFIG_MSDOS_PARTITION=y +CONFIG_BSD_DISKLABEL=y +CONFIG_MINIX_SUBPARTITION=y +CONFIG_SOLARIS_X86_PARTITION=y +CONFIG_UNIXWARE_DISKLABEL=y +# CONFIG_LDM_PARTITION is not set +CONFIG_SGI_PARTITION=y +# CONFIG_ULTRIX_PARTITION is not set +CONFIG_SUN_PARTITION=y +CONFIG_EFI_PARTITION=y + +# +# Native Language Support +# +CONFIG_NLS=y +CONFIG_NLS_DEFAULT="utf8" +CONFIG_NLS_CODEPAGE_437=y +CONFIG_NLS_CODEPAGE_737=m +CONFIG_NLS_CODEPAGE_775=m +CONFIG_NLS_CODEPAGE_850=m +CONFIG_NLS_CODEPAGE_852=m +CONFIG_NLS_CODEPAGE_855=m +CONFIG_NLS_CODEPAGE_857=m +CONFIG_NLS_CODEPAGE_860=m +CONFIG_NLS_CODEPAGE_861=m +CONFIG_NLS_CODEPAGE_862=m +CONFIG_NLS_CODEPAGE_863=m +CONFIG_NLS_CODEPAGE_864=m +CONFIG_NLS_CODEPAGE_865=m +CONFIG_NLS_CODEPAGE_866=m +CONFIG_NLS_CODEPAGE_869=m +CONFIG_NLS_CODEPAGE_936=m +CONFIG_NLS_CODEPAGE_950=m +CONFIG_NLS_CODEPAGE_932=m +CONFIG_NLS_CODEPAGE_949=m +CONFIG_NLS_CODEPAGE_874=m +CONFIG_NLS_ISO8859_8=m +CONFIG_NLS_CODEPAGE_1250=m +CONFIG_NLS_CODEPAGE_1251=m +CONFIG_NLS_ASCII=y +CONFIG_NLS_ISO8859_1=m +CONFIG_NLS_ISO8859_2=m +CONFIG_NLS_ISO8859_3=m +CONFIG_NLS_ISO8859_4=m +CONFIG_NLS_ISO8859_5=m +CONFIG_NLS_ISO8859_6=m +CONFIG_NLS_ISO8859_7=m +CONFIG_NLS_ISO8859_9=m +CONFIG_NLS_ISO8859_13=m +CONFIG_NLS_ISO8859_14=m +CONFIG_NLS_ISO8859_15=m +CONFIG_NLS_KOI8_R=m +CONFIG_NLS_KOI8_U=m +CONFIG_NLS_UTF8=m + +# +# Profiling support +# +# CONFIG_PROFILING is not set + +# +# Kernel hacking +# +CONFIG_DEBUG_KERNEL=y +CONFIG_MAGIC_SYSRQ=y +# CONFIG_DEBUG_SLAB is not set +CONFIG_DEBUG_SPINLOCK=y +CONFIG_DEBUG_SPINLOCK_SLEEP=y +CONFIG_DEBUG_HIGHMEM=y +CONFIG_DEBUG_INFO=y +# CONFIG_FRAME_POINTER is not set +CONFIG_EARLY_PRINTK=y +CONFIG_DEBUG_STACKOVERFLOW=y +# CONFIG_KPROBES is not set +CONFIG_DEBUG_STACK_USAGE=y +# CONFIG_DEBUG_PAGEALLOC is not set +# CONFIG_SCHEDSTATS is not set + +# +# Security options +# +CONFIG_SECURITY=y +CONFIG_SECURITY_NETWORK=y +CONFIG_SECURITY_CAPABILITIES=y +# CONFIG_SECURITY_ROOTPLUG is not set +CONFIG_SECURITY_SELINUX=y +CONFIG_SECURITY_SELINUX_BOOTPARAM=y +CONFIG_SECURITY_SELINUX_BOOTPARAM_VALUE=1 +CONFIG_SECURITY_SELINUX_DISABLE=y +CONFIG_SECURITY_SELINUX_DEVELOP=y +CONFIG_SECURITY_SELINUX_AVC_STATS=y +# CONFIG_SECURITY_SELINUX_MLS is not set + +# +# Cryptographic options +# +CONFIG_CRYPTO=y +CONFIG_CRYPTO_HMAC=y +CONFIG_CRYPTO_NULL=m +CONFIG_CRYPTO_MD4=m +CONFIG_CRYPTO_MD5=m +CONFIG_CRYPTO_SHA1=y +CONFIG_CRYPTO_SHA256=m +CONFIG_CRYPTO_SHA512=m +CONFIG_CRYPTO_WP512=m +CONFIG_CRYPTO_DES=m +CONFIG_CRYPTO_BLOWFISH=m +CONFIG_CRYPTO_TWOFISH=m +CONFIG_CRYPTO_SERPENT=m +CONFIG_CRYPTO_AES_586=m +CONFIG_CRYPTO_CAST5=m +CONFIG_CRYPTO_CAST6=m +CONFIG_CRYPTO_TEA=m +CONFIG_CRYPTO_ARC4=m +CONFIG_CRYPTO_KHAZAD=m +CONFIG_CRYPTO_DEFLATE=m +CONFIG_CRYPTO_MICHAEL_MIC=m +CONFIG_CRYPTO_CRC32C=m +# CONFIG_CRYPTO_TEST is not set +CONFIG_CRYPTO_SIGNATURE=y +CONFIG_CRYPTO_SIGNATURE_DSA=y +CONFIG_CRYPTO_MPILIB=y + +# +# Library routines +# +CONFIG_CRC_CCITT=m +CONFIG_CRC32=y +CONFIG_LIBCRC32C=m +CONFIG_ZLIB_INFLATE=y +CONFIG_ZLIB_DEFLATE=m +CONFIG_X86_BIOS_REBOOT=y +CONFIG_PC=y diff --git a/configs/kernel-2.6.9-i686-smp.config b/configs/kernel-2.6.9-i686-smp.config new file mode 100644 index 000000000..1785c6185 --- /dev/null +++ b/configs/kernel-2.6.9-i686-smp.config @@ -0,0 +1,2574 @@ +# +# Automatically generated make config: don't edit +# Linux kernel version: 2.6.9-ac11 +# Tue Jan 4 10:01:15 2005 +# +CONFIG_X86=y +CONFIG_MMU=y +CONFIG_UID16=y +CONFIG_GENERIC_ISA_DMA=y +CONFIG_GENERIC_IOMAP=y + +# +# Code maturity level options +# +CONFIG_EXPERIMENTAL=y +CONFIG_CLEAN_COMPILE=y + +# +# General setup +# +CONFIG_LOCALVERSION="" +CONFIG_SWAP=y +CONFIG_SYSVIPC=y +CONFIG_POSIX_MQUEUE=y +CONFIG_BSD_PROCESS_ACCT=y +# CONFIG_BSD_PROCESS_ACCT_V3 is not set +CONFIG_SYSCTL=y +CONFIG_AUDIT=y +CONFIG_AUDITSYSCALL=y +CONFIG_LOG_BUF_SHIFT=17 +CONFIG_HOTPLUG=y +# CONFIG_IKCONFIG is not set +# CONFIG_EMBEDDED is not set +CONFIG_KALLSYMS=y +# CONFIG_KALLSYMS_ALL is not set +CONFIG_KALLSYMS_EXTRA_PASS=y +CONFIG_FUTEX=y +CONFIG_EPOLL=y +CONFIG_IOSCHED_NOOP=y +CONFIG_IOSCHED_AS=y +CONFIG_IOSCHED_DEADLINE=y +CONFIG_IOSCHED_CFQ=y +CONFIG_CC_OPTIMIZE_FOR_SIZE=y +CONFIG_SHMEM=y +# CONFIG_TINY_SHMEM is not set + +# +# Loadable module support +# +CONFIG_MODULES=y +CONFIG_MODULE_UNLOAD=y +# CONFIG_MODULE_FORCE_UNLOAD is not set +CONFIG_OBSOLETE_MODPARM=y +CONFIG_MODVERSIONS=y +CONFIG_MODULE_SIG=y +# CONFIG_MODULE_SIG_FORCE is not set +CONFIG_KMOD=y +CONFIG_STOP_MACHINE=y + +# +# Processor type and features +# +# CONFIG_X86_PC is not set +# CONFIG_X86_ELAN is not set +# CONFIG_X86_VOYAGER is not set +# CONFIG_X86_NUMAQ is not set +# CONFIG_X86_SUMMIT is not set +# CONFIG_X86_BIGSMP is not set +# CONFIG_X86_VISWS is not set +CONFIG_X86_GENERICARCH=y +# CONFIG_X86_ES7000 is not set +CONFIG_X86_CYCLONE_TIMER=y +# CONFIG_M386 is not set +# CONFIG_M486 is not set +# CONFIG_M586 is not set +# CONFIG_M586TSC is not set +# CONFIG_M586MMX is not set +CONFIG_M686=y +# CONFIG_MPENTIUMII is not set +# CONFIG_MPENTIUMIII is not set +# CONFIG_MPENTIUMM is not set +# CONFIG_MPENTIUM4 is not set +# CONFIG_MK6 is not set +# CONFIG_MK7 is not set +# CONFIG_MK8 is not set +# CONFIG_MCRUSOE is not set +# CONFIG_MWINCHIPC6 is not set +# CONFIG_MWINCHIP2 is not set +# CONFIG_MWINCHIP3D is not set +# CONFIG_MCYRIXIII is not set +# CONFIG_MVIAC3_2 is not set +CONFIG_X86_GENERIC=y +CONFIG_X86_CMPXCHG=y +CONFIG_X86_XADD=y +CONFIG_X86_L1_CACHE_SHIFT=7 +CONFIG_RWSEM_XCHGADD_ALGORITHM=y +CONFIG_X86_PPRO_FENCE=y +CONFIG_X86_WP_WORKS_OK=y +CONFIG_X86_INVLPG=y +CONFIG_X86_BSWAP=y +CONFIG_X86_POPAD_OK=y +CONFIG_X86_GOOD_APIC=y +CONFIG_X86_INTEL_USERCOPY=y +CONFIG_X86_USE_PPRO_CHECKSUM=y +CONFIG_HPET_TIMER=y +CONFIG_HPET_EMULATE_RTC=y +CONFIG_SMP=y +CONFIG_NR_CPUS=32 +CONFIG_SCHED_SMT=y +# CONFIG_PREEMPT is not set +CONFIG_PREEMPT_VOLUNTARY=y +CONFIG_X86_LOCAL_APIC=y +CONFIG_X86_IO_APIC=y +CONFIG_X86_TSC=y +CONFIG_X86_MCE=y +# CONFIG_X86_MCE_NONFATAL is not set +CONFIG_X86_MCE_P4THERMAL=y +CONFIG_TOSHIBA=m +CONFIG_I8K=m +CONFIG_MICROCODE=m +CONFIG_X86_MSR=m +CONFIG_X86_CPUID=m + +# +# Firmware Drivers +# +CONFIG_EDD=m +# CONFIG_NOHIGHMEM is not set +# CONFIG_HIGHMEM4G is not set +CONFIG_HIGHMEM64G=y +CONFIG_HIGHMEM=y +CONFIG_X86_PAE=y +# CONFIG_NUMA is not set +CONFIG_HIGHPTE=y +# CONFIG_MATH_EMULATION is not set +CONFIG_MTRR=y +# CONFIG_EFI is not set +# CONFIG_IRQBALANCE is not set +CONFIG_HAVE_DEC_LOCK=y +CONFIG_REGPARM=y +CONFIG_KEXEC=y + +# +# Power management options (ACPI, APM) +# +CONFIG_PM=y +# CONFIG_PM_DEBUG is not set +# CONFIG_SOFTWARE_SUSPEND is not set + +# +# ACPI (Advanced Configuration and Power Interface) Support +# +CONFIG_ACPI=y +CONFIG_ACPI_BOOT=y +CONFIG_ACPI_INTERPRETER=y +CONFIG_ACPI_SLEEP=y +CONFIG_ACPI_SLEEP_PROC_FS=y +CONFIG_ACPI_AC=m +CONFIG_ACPI_BATTERY=m +CONFIG_ACPI_BUTTON=m +CONFIG_ACPI_FAN=y +CONFIG_ACPI_PROCESSOR=y +CONFIG_ACPI_THERMAL=y +CONFIG_ACPI_ASUS=m +CONFIG_ACPI_TOSHIBA=m +CONFIG_ACPI_BLACKLIST_YEAR=2001 +# CONFIG_ACPI_DEBUG is not set +CONFIG_ACPI_BUS=y +CONFIG_ACPI_EC=y +CONFIG_ACPI_POWER=y +CONFIG_ACPI_PCI=y +CONFIG_ACPI_SYSTEM=y +CONFIG_X86_PM_TIMER=y + +# +# APM (Advanced Power Management) BIOS Support +# +CONFIG_APM=y +# CONFIG_APM_IGNORE_USER_SUSPEND is not set +# CONFIG_APM_DO_ENABLE is not set +CONFIG_APM_CPU_IDLE=y +# CONFIG_APM_DISPLAY_BLANK is not set +CONFIG_APM_RTC_IS_GMT=y +# CONFIG_APM_ALLOW_INTS is not set +# CONFIG_APM_REAL_MODE_POWER_OFF is not set + +# +# CPU Frequency scaling +# +CONFIG_CPU_FREQ=y +# CONFIG_CPU_FREQ_PROC_INTF is not set +# CONFIG_CPU_FREQ_DEFAULT_GOV_PERFORMANCE is not set +CONFIG_CPU_FREQ_DEFAULT_GOV_USERSPACE=y +CONFIG_CPU_FREQ_GOV_PERFORMANCE=y +CONFIG_CPU_FREQ_GOV_POWERSAVE=m +CONFIG_CPU_FREQ_GOV_USERSPACE=y +CONFIG_CPU_FREQ_GOV_ONDEMAND=m +# CONFIG_CPU_FREQ_24_API is not set +CONFIG_CPU_FREQ_TABLE=y + +# +# CPUFreq processor drivers +# +CONFIG_X86_ACPI_CPUFREQ=m +# CONFIG_X86_ACPI_CPUFREQ_PROC_INTF is not set +CONFIG_X86_POWERNOW_K6=m +CONFIG_X86_POWERNOW_K7=y +CONFIG_X86_POWERNOW_K7_ACPI=y +CONFIG_X86_POWERNOW_K8=m +CONFIG_X86_POWERNOW_K8_ACPI=y +# CONFIG_X86_GX_SUSPMOD is not set +CONFIG_X86_SPEEDSTEP_CENTRINO=y +CONFIG_X86_SPEEDSTEP_CENTRINO_TABLE=y +CONFIG_X86_SPEEDSTEP_CENTRINO_ACPI=y +CONFIG_X86_SPEEDSTEP_ICH=y +CONFIG_X86_SPEEDSTEP_SMI=m +CONFIG_X86_P4_CLOCKMOD=m +CONFIG_X86_SPEEDSTEP_LIB=y +# CONFIG_X86_SPEEDSTEP_RELAXED_CAP_CHECK is not set +CONFIG_X86_LONGRUN=y +CONFIG_X86_LONGHAUL=y + +# +# Bus options (PCI, PCMCIA, EISA, MCA, ISA) +# +CONFIG_PCI=y +# CONFIG_PCI_GOBIOS is not set +# CONFIG_PCI_GOMMCONFIG is not set +# CONFIG_PCI_GODIRECT is not set +CONFIG_PCI_GOANY=y +CONFIG_PCI_BIOS=y +CONFIG_PCI_DIRECT=y +CONFIG_PCI_MMCONFIG=y +CONFIG_PCI_MSI=y +CONFIG_PCI_LEGACY_PROC=y +# CONFIG_PCI_NAMES is not set +CONFIG_ISA=y +# CONFIG_EISA is not set +# CONFIG_MCA is not set +# CONFIG_SCx200 is not set + +# +# PCMCIA/CardBus support +# +CONFIG_PCMCIA=m +# CONFIG_PCMCIA_DEBUG is not set +CONFIG_YENTA=m +CONFIG_CARDBUS=y +CONFIG_PD6729=m +CONFIG_I82092=m +CONFIG_I82365=m +CONFIG_TCIC=m +CONFIG_PCMCIA_PROBE=y + +# +# PCI Hotplug Support +# +CONFIG_HOTPLUG_PCI=y +# CONFIG_HOTPLUG_PCI_FAKE is not set +CONFIG_HOTPLUG_PCI_COMPAQ=m +# CONFIG_HOTPLUG_PCI_COMPAQ_NVRAM is not set +CONFIG_HOTPLUG_PCI_IBM=m +CONFIG_HOTPLUG_PCI_ACPI=m +CONFIG_HOTPLUG_PCI_ACPI_IBM=m +# CONFIG_HOTPLUG_PCI_CPCI is not set +CONFIG_HOTPLUG_PCI_PCIE=m +# CONFIG_HOTPLUG_PCI_PCIE_POLL_EVENT_MODE is not set +CONFIG_HOTPLUG_PCI_SHPC=m +# CONFIG_HOTPLUG_PCI_SHPC_POLL_EVENT_MODE is not set + +# +# Executable file formats +# +CONFIG_BINFMT_ELF=y +# CONFIG_BINFMT_AOUT is not set +CONFIG_BINFMT_MISC=y + +# +# Device Drivers +# + +# +# Generic Driver Options +# +CONFIG_STANDALONE=y +CONFIG_PREVENT_FIRMWARE_BUILD=y +CONFIG_FW_LOADER=y +# CONFIG_DEBUG_DRIVER is not set + +# +# Memory Technology Devices (MTD) +# +CONFIG_MTD=m +# CONFIG_MTD_DEBUG is not set +CONFIG_MTD_PARTITIONS=y +CONFIG_MTD_CONCAT=m +CONFIG_MTD_REDBOOT_PARTS=m +# CONFIG_MTD_REDBOOT_PARTS_UNALLOCATED is not set +# CONFIG_MTD_REDBOOT_PARTS_READONLY is not set +CONFIG_MTD_CMDLINE_PARTS=y + +# +# User Modules And Translation Layers +# +CONFIG_MTD_CHAR=m +CONFIG_MTD_BLOCK=m +CONFIG_MTD_BLOCK_RO=m +CONFIG_FTL=m +CONFIG_NFTL=m +CONFIG_NFTL_RW=y +CONFIG_INFTL=m + +# +# RAM/ROM/Flash chip drivers +# +CONFIG_MTD_CFI=m +CONFIG_MTD_JEDECPROBE=m +CONFIG_MTD_GEN_PROBE=m +# 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=m +CONFIG_MTD_CFI_AMDSTD=m +CONFIG_MTD_CFI_AMDSTD_RETRY=3 +CONFIG_MTD_CFI_STAA=m +CONFIG_MTD_CFI_UTIL=m +CONFIG_MTD_RAM=m +CONFIG_MTD_ROM=m +CONFIG_MTD_ABSENT=m + +# +# Mapping drivers for chip access +# +CONFIG_MTD_COMPLEX_MAPPINGS=y +# CONFIG_MTD_PHYSMAP is not set +# CONFIG_MTD_PNC2000 is not set +CONFIG_MTD_SC520CDP=m +CONFIG_MTD_NETSC520=m +CONFIG_MTD_SBC_GXX=m +CONFIG_MTD_ELAN_104NC=m +CONFIG_MTD_SCx200_DOCFLASH=m +# CONFIG_MTD_AMD76XROM is not set +# CONFIG_MTD_ICHXROM is not set +CONFIG_MTD_SCB2_FLASH=m +# CONFIG_MTD_NETtel is not set +# CONFIG_MTD_DILNETPC is not set +# CONFIG_MTD_L440GX is not set +CONFIG_MTD_PCI=m + +# +# Self-contained MTD device drivers +# +CONFIG_MTD_PMC551=m +# CONFIG_MTD_PMC551_BUGFIX is not set +# CONFIG_MTD_PMC551_DEBUG is not set +# CONFIG_MTD_SLRAM is not set +# CONFIG_MTD_PHRAM is not set +CONFIG_MTD_MTDRAM=m +CONFIG_MTDRAM_TOTAL_SIZE=4096 +CONFIG_MTDRAM_ERASE_SIZE=128 +# CONFIG_MTD_BLKMTD is not set + +# +# Disk-On-Chip Device Drivers +# +CONFIG_MTD_DOC2000=m +# CONFIG_MTD_DOC2001 is not set +CONFIG_MTD_DOC2001PLUS=m +CONFIG_MTD_DOCPROBE=m +CONFIG_MTD_DOCECC=m +# CONFIG_MTD_DOCPROBE_ADVANCED is not set +CONFIG_MTD_DOCPROBE_ADDRESS=0 + +# +# NAND Flash Device Drivers +# +CONFIG_MTD_NAND=m +# CONFIG_MTD_NAND_VERIFY_WRITE is not set +CONFIG_MTD_NAND_IDS=m +# CONFIG_MTD_NAND_DISKONCHIP is not set + +# +# Parallel port support +# +CONFIG_PARPORT=m +CONFIG_PARPORT_PC=m +CONFIG_PARPORT_PC_CML1=m +CONFIG_PARPORT_SERIAL=m +# CONFIG_PARPORT_PC_FIFO is not set +# CONFIG_PARPORT_PC_SUPERIO is not set +CONFIG_PARPORT_PC_PCMCIA=m +# CONFIG_PARPORT_OTHER is not set +CONFIG_PARPORT_1284=y + +# +# Plug and Play support +# +CONFIG_PNP=y +# CONFIG_PNP_DEBUG is not set + +# +# Protocols +# +CONFIG_ISAPNP=y +# CONFIG_PNPBIOS is not set + +# +# Block devices +# +CONFIG_BLK_DEV_FD=m +# CONFIG_BLK_DEV_XD is not set +# CONFIG_PARIDE is not set +CONFIG_BLK_CPQ_DA=m +CONFIG_BLK_CPQ_CISS_DA=m +CONFIG_CISS_SCSI_TAPE=y +CONFIG_BLK_DEV_DAC960=m +CONFIG_BLK_DEV_UMEM=m +CONFIG_BLK_DEV_LOOP=m +CONFIG_BLK_DEV_CRYPTOLOOP=m +CONFIG_BLK_DEV_NBD=m +CONFIG_BLK_DEV_SX8=m +# CONFIG_BLK_DEV_UB is not set +CONFIG_BLK_DEV_RAM=y +CONFIG_BLK_DEV_RAM_SIZE=16384 +CONFIG_BLK_DEV_INITRD=y +CONFIG_LBD=y + +# +# ATA/ATAPI/MFM/RLL support +# +CONFIG_IDE=y +CONFIG_BLK_DEV_IDE=y + +# +# Please see Documentation/ide.txt for help/info on IDE drives +# +# CONFIG_BLK_DEV_IDE_SATA is not set +# CONFIG_BLK_DEV_HD_IDE is not set +CONFIG_BLK_DEV_IDEDISK=y +CONFIG_IDEDISK_MULTI_MODE=y +CONFIG_BLK_DEV_IDECS=m +CONFIG_BLK_DEV_DELKIN=m +CONFIG_BLK_DEV_IDECD=y +# CONFIG_BLK_DEV_IDETAPE is not set +CONFIG_BLK_DEV_IDEFLOPPY=y +CONFIG_BLK_DEV_IDESCSI=m +# CONFIG_IDE_TASK_IOCTL is not set +# CONFIG_IDE_TASKFILE_IO is not set + +# +# IDE chipset support/bugfixes +# +CONFIG_IDE_GENERIC=y +# CONFIG_BLK_DEV_CMD640 is not set +CONFIG_BLK_DEV_IDEPNP=y +CONFIG_BLK_DEV_IDEPCI=y +CONFIG_IDEPCI_SHARE_IRQ=y +# CONFIG_BLK_DEV_OFFBOARD is not set +CONFIG_BLK_DEV_GENERIC=y +# CONFIG_BLK_DEV_OPTI621 is not set +CONFIG_BLK_DEV_RZ1000=y +CONFIG_BLK_DEV_IDEDMA_PCI=y +# CONFIG_BLK_DEV_IDEDMA_FORCED is not set +CONFIG_IDEDMA_PCI_AUTO=y +# CONFIG_IDEDMA_ONLYDISK is not set +CONFIG_BLK_DEV_AEC62XX=y +CONFIG_BLK_DEV_ALI15X3=y +# CONFIG_WDC_ALI15X3 is not set +CONFIG_BLK_DEV_AMD74XX=y +CONFIG_BLK_DEV_ATIIXP=y +CONFIG_BLK_DEV_CMD64X=y +CONFIG_BLK_DEV_TRIFLEX=y +CONFIG_BLK_DEV_CY82C693=y +CONFIG_BLK_DEV_CS5520=y +CONFIG_BLK_DEV_CS5530=y +CONFIG_BLK_DEV_HPT34X=y +# CONFIG_HPT34X_AUTODMA is not set +CONFIG_BLK_DEV_HPT366=y +# CONFIG_BLK_DEV_SC1200 is not set +CONFIG_BLK_DEV_PIIX=y +CONFIG_BLK_DEV_IT8212=y +# CONFIG_BLK_DEV_NS87415 is not set +CONFIG_BLK_DEV_PDC202XX_OLD=y +# CONFIG_PDC202XX_BURST is not set +CONFIG_BLK_DEV_PDC202XX_NEW=y +CONFIG_PDC202XX_FORCE=y +CONFIG_BLK_DEV_SVWKS=y +CONFIG_BLK_DEV_SIIMAGE=y +CONFIG_BLK_DEV_SIS5513=y +CONFIG_BLK_DEV_SLC90E66=y +# CONFIG_BLK_DEV_TRM290 is not set +CONFIG_BLK_DEV_VIA82CXXX=y +# CONFIG_IDE_ARM is not set +# CONFIG_IDE_CHIPSETS is not set +CONFIG_BLK_DEV_IDEDMA=y +# CONFIG_IDEDMA_IVB is not set +CONFIG_IDEDMA_AUTO=y +# CONFIG_BLK_DEV_HD is not set + +# +# SCSI device support +# +CONFIG_SCSI=m +CONFIG_SCSI_PROC_FS=y + +# +# SCSI support type (disk, tape, CD-ROM) +# +CONFIG_BLK_DEV_SD=m +CONFIG_CHR_DEV_ST=m +CONFIG_CHR_DEV_OSST=m +CONFIG_BLK_DEV_SR=m +CONFIG_BLK_DEV_SR_VENDOR=y +CONFIG_CHR_DEV_SG=m + +# +# Some SCSI devices (e.g. CD jukebox) support multiple LUNs +# +# CONFIG_SCSI_MULTI_LUN is not set +CONFIG_SCSI_CONSTANTS=y +CONFIG_SCSI_LOGGING=y + +# +# SCSI Transport Attributes +# +CONFIG_SCSI_SPI_ATTRS=m +CONFIG_SCSI_FC_ATTRS=m + +# +# SCSI low-level drivers +# +CONFIG_BLK_DEV_3W_XXXX_RAID=m +CONFIG_SCSI_3W_9XXX=m +# CONFIG_SCSI_7000FASST is not set +CONFIG_SCSI_ACARD=m +CONFIG_SCSI_AHA152X=m +CONFIG_SCSI_AHA1542=m +CONFIG_SCSI_AACRAID=m +CONFIG_SCSI_AIC7XXX=m +CONFIG_AIC7XXX_CMDS_PER_DEVICE=4 +CONFIG_AIC7XXX_RESET_DELAY_MS=15000 +# CONFIG_AIC7XXX_DEBUG_ENABLE is not set +CONFIG_AIC7XXX_DEBUG_MASK=0 +# CONFIG_AIC7XXX_REG_PRETTY_PRINT is not set +CONFIG_SCSI_AIC7XXX_OLD=m +CONFIG_SCSI_AIC79XX=m +CONFIG_AIC79XX_CMDS_PER_DEVICE=4 +CONFIG_AIC79XX_RESET_DELAY_MS=15000 +# CONFIG_AIC79XX_ENABLE_RD_STRM is not set +# CONFIG_AIC79XX_DEBUG_ENABLE is not set +CONFIG_AIC79XX_DEBUG_MASK=0 +# CONFIG_AIC79XX_REG_PRETTY_PRINT is not set +# CONFIG_SCSI_DPT_I2O is not set +CONFIG_SCSI_IN2000=m +CONFIG_MEGARAID_NEWGEN=y +CONFIG_MEGARAID_MM=m +CONFIG_MEGARAID_MAILBOX=m +CONFIG_MEGARAID_LEGACY=m +CONFIG_SCSI_SATA=y +CONFIG_SCSI_SATA_AHCI=m +CONFIG_SCSI_SATA_SVW=m +CONFIG_SCSI_ATA_PIIX=m +CONFIG_SCSI_SATA_NV=m +CONFIG_SCSI_SATA_PROMISE=m +CONFIG_SCSI_SATA_SX4=m +CONFIG_SCSI_SATA_SIL=m +CONFIG_SCSI_SATA_SIS=m +CONFIG_SCSI_SATA_VIA=m +CONFIG_SCSI_SATA_VITESSE=m +CONFIG_SCSI_BUSLOGIC=m +# CONFIG_SCSI_OMIT_FLASHPOINT is not set +# CONFIG_SCSI_DMX3191D is not set +# CONFIG_SCSI_DTC3280 is not set +# CONFIG_SCSI_EATA is not set +# CONFIG_SCSI_EATA_PIO is not set +CONFIG_SCSI_LPFC=m +CONFIG_SCSI_FUTURE_DOMAIN=m +CONFIG_SCSI_GDTH=m +# CONFIG_SCSI_GENERIC_NCR5380 is not set +# CONFIG_SCSI_GENERIC_NCR5380_MMIO is not set +CONFIG_SCSI_IPS=m +CONFIG_SCSI_INITIO=m +CONFIG_SCSI_INIA100=m +CONFIG_SCSI_PPA=m +CONFIG_SCSI_IMM=m +# CONFIG_SCSI_IZIP_EPP16 is not set +# CONFIG_SCSI_IZIP_SLOW_CTR is not set +# CONFIG_SCSI_NCR53C406A is not set +CONFIG_SCSI_SYM53C8XX_2=m +CONFIG_SCSI_SYM53C8XX_DMA_ADDRESSING_MODE=1 +CONFIG_SCSI_SYM53C8XX_DEFAULT_TAGS=16 +CONFIG_SCSI_SYM53C8XX_MAX_TAGS=64 +# CONFIG_SCSI_SYM53C8XX_IOMAPPED is not set +# CONFIG_SCSI_IPR is not set +# CONFIG_SCSI_PAS16 is not set +# CONFIG_SCSI_PSI240I is not set +CONFIG_SCSI_QLOGIC_FAS=m +CONFIG_SCSI_QLOGIC_ISP=m +# CONFIG_SCSI_QLOGIC_FC is not set +CONFIG_SCSI_QLOGIC_1280=m +CONFIG_SCSI_QLA2XXX=m +CONFIG_SCSI_QLA21XX=m +CONFIG_SCSI_QLA22XX=m +CONFIG_SCSI_QLA2300=m +CONFIG_SCSI_QLA2322=m +CONFIG_SCSI_QLA6312=m +CONFIG_SCSI_QLA6322=m +# CONFIG_SCSI_SYM53C416 is not set +# CONFIG_SCSI_DC395x is not set +CONFIG_SCSI_DC390T=m +# CONFIG_SCSI_T128 is not set +# CONFIG_SCSI_U14_34F is not set +# CONFIG_SCSI_ULTRASTOR is not set +# CONFIG_SCSI_NSP32 is not set +# CONFIG_SCSI_DEBUG is not set + +# +# PCMCIA SCSI adapter support +# +CONFIG_PCMCIA_AHA152X=m +CONFIG_PCMCIA_FDOMAIN=m +CONFIG_PCMCIA_NINJA_SCSI=m +CONFIG_PCMCIA_QLOGIC=m +CONFIG_PCMCIA_SYM53C500=m + +# +# Old CD-ROM drivers (not SCSI, not IDE) +# +# CONFIG_CD_NO_IDESCSI is not set + +# +# Multi-device support (RAID and LVM) +# +CONFIG_MD=y +CONFIG_BLK_DEV_MD=y +CONFIG_MD_LINEAR=m +CONFIG_MD_RAID0=m +CONFIG_MD_RAID1=m +CONFIG_MD_RAID10=m +CONFIG_MD_RAID5=m +CONFIG_MD_RAID6=m +CONFIG_MD_MULTIPATH=m +CONFIG_BLK_DEV_DM=m +CONFIG_DM_CRYPT=m +CONFIG_DM_SNAPSHOT=m +CONFIG_DM_MIRROR=m +CONFIG_DM_ZERO=m + +# +# Fusion MPT device support +# +CONFIG_FUSION=m +CONFIG_FUSION_MAX_SGE=40 +CONFIG_FUSION_CTL=m +CONFIG_FUSION_LAN=m + +# +# IEEE 1394 (FireWire) support +# +CONFIG_IEEE1394=m + +# +# Subsystem Options +# +# CONFIG_IEEE1394_VERBOSEDEBUG is not set +CONFIG_IEEE1394_OUI_DB=y +CONFIG_IEEE1394_EXTRA_CONFIG_ROMS=y +CONFIG_IEEE1394_CONFIG_ROM_IP1394=y + +# +# Device Drivers +# +CONFIG_IEEE1394_PCILYNX=m +CONFIG_IEEE1394_OHCI1394=m + +# +# Protocol Drivers +# +CONFIG_IEEE1394_VIDEO1394=m +CONFIG_IEEE1394_SBP2=m +# CONFIG_IEEE1394_SBP2_PHYS_DMA is not set +CONFIG_IEEE1394_ETH1394=m +CONFIG_IEEE1394_DV1394=m +CONFIG_IEEE1394_RAWIO=m +CONFIG_IEEE1394_CMP=m +CONFIG_IEEE1394_AMDTP=m + +# +# I2O device support +# +CONFIG_I2O=m +CONFIG_I2O_CONFIG=m +CONFIG_I2O_BLOCK=m +CONFIG_I2O_SCSI=m +CONFIG_I2O_PROC=m + +# +# Networking support +# +CONFIG_NET=y + +# +# Networking options +# +CONFIG_PACKET=y +CONFIG_PACKET_MMAP=y +CONFIG_NETLINK_DEV=y +CONFIG_UNIX=y +CONFIG_NET_KEY=m +CONFIG_INET=y +CONFIG_IP_MULTICAST=y +CONFIG_IP_ADVANCED_ROUTER=y +CONFIG_IP_MULTIPLE_TABLES=y +CONFIG_IP_ROUTE_FWMARK=y +CONFIG_IP_ROUTE_MULTIPATH=y +CONFIG_IP_ROUTE_VERBOSE=y +# CONFIG_IP_PNP is not set +CONFIG_NET_IPIP=m +CONFIG_NET_IPGRE=m +CONFIG_NET_IPGRE_BROADCAST=y +CONFIG_IP_MROUTE=y +CONFIG_IP_PIMSM_V1=y +CONFIG_IP_PIMSM_V2=y +# CONFIG_ARPD is not set +CONFIG_SYN_COOKIES=y +CONFIG_INET_AH=m +CONFIG_INET_ESP=m +CONFIG_INET_IPCOMP=m +CONFIG_INET_TUNNEL=m + +# +# IP: Virtual Server Configuration +# +CONFIG_IP_VS=m +# CONFIG_IP_VS_DEBUG is not set +CONFIG_IP_VS_TAB_BITS=12 + +# +# IPVS transport protocol load balancing support +# +CONFIG_IP_VS_PROTO_TCP=y +CONFIG_IP_VS_PROTO_UDP=y +CONFIG_IP_VS_PROTO_ESP=y +CONFIG_IP_VS_PROTO_AH=y + +# +# IPVS scheduler +# +CONFIG_IP_VS_RR=m +CONFIG_IP_VS_WRR=m +CONFIG_IP_VS_LC=m +CONFIG_IP_VS_WLC=m +CONFIG_IP_VS_LBLC=m +CONFIG_IP_VS_LBLCR=m +CONFIG_IP_VS_DH=m +CONFIG_IP_VS_SH=m +CONFIG_IP_VS_SED=m +CONFIG_IP_VS_NQ=m + +# +# IPVS application helper +# +CONFIG_IP_VS_FTP=m +CONFIG_IPV6=m +CONFIG_IPV6_PRIVACY=y +CONFIG_INET6_AH=m +CONFIG_INET6_ESP=m +CONFIG_INET6_IPCOMP=m +CONFIG_INET6_TUNNEL=m +CONFIG_IPV6_TUNNEL=m +CONFIG_NETFILTER=y +# CONFIG_NETFILTER_DEBUG is not set +CONFIG_BRIDGE_NETFILTER=y + +# +# IP: Netfilter Configuration +# +CONFIG_IP_NF_CONNTRACK=m +CONFIG_IP_NF_CT_ACCT=y +CONFIG_IP_NF_CT_PROTO_SCTP=m +CONFIG_IP_NF_FTP=m +CONFIG_IP_NF_IRC=m +CONFIG_IP_NF_TFTP=m +CONFIG_IP_NF_AMANDA=m +CONFIG_IP_NF_QUEUE=m +CONFIG_IP_NF_IPTABLES=m +CONFIG_IP_NF_MATCH_LIMIT=m +CONFIG_IP_NF_MATCH_IPRANGE=m +CONFIG_IP_NF_MATCH_MAC=m +CONFIG_IP_NF_MATCH_PKTTYPE=m +CONFIG_IP_NF_MATCH_MARK=m +CONFIG_IP_NF_MATCH_MULTIPORT=m +CONFIG_IP_NF_MATCH_TOS=m +CONFIG_IP_NF_MATCH_RECENT=m +CONFIG_IP_NF_MATCH_ECN=m +CONFIG_IP_NF_MATCH_DSCP=m +CONFIG_IP_NF_MATCH_AH_ESP=m +CONFIG_IP_NF_MATCH_LENGTH=m +CONFIG_IP_NF_MATCH_TTL=m +CONFIG_IP_NF_MATCH_TCPMSS=m +CONFIG_IP_NF_MATCH_HELPER=m +CONFIG_IP_NF_MATCH_STATE=m +CONFIG_IP_NF_MATCH_CONNTRACK=m +CONFIG_IP_NF_MATCH_OWNER=m +CONFIG_IP_NF_MATCH_PHYSDEV=m +CONFIG_IP_NF_MATCH_ADDRTYPE=m +CONFIG_IP_NF_MATCH_REALM=m +CONFIG_IP_NF_MATCH_SCTP=m +CONFIG_IP_NF_MATCH_COMMENT=m +CONFIG_IP_NF_FILTER=m +CONFIG_IP_NF_TARGET_REJECT=m +CONFIG_IP_NF_TARGET_LOG=m +CONFIG_IP_NF_TARGET_ULOG=m +CONFIG_IP_NF_TARGET_TCPMSS=m +CONFIG_IP_NF_NAT=m +CONFIG_IP_NF_NAT_NEEDED=y +CONFIG_IP_NF_TARGET_MASQUERADE=m +CONFIG_IP_NF_TARGET_REDIRECT=m +CONFIG_IP_NF_TARGET_NETMAP=m +CONFIG_IP_NF_TARGET_SAME=m +CONFIG_IP_NF_NAT_LOCAL=y +CONFIG_IP_NF_NAT_SNMP_BASIC=m +CONFIG_IP_NF_NAT_IRC=m +CONFIG_IP_NF_NAT_FTP=m +CONFIG_IP_NF_NAT_TFTP=m +CONFIG_IP_NF_NAT_AMANDA=m +CONFIG_IP_NF_MANGLE=m +CONFIG_IP_NF_TARGET_TOS=m +CONFIG_IP_NF_TARGET_ECN=m +CONFIG_IP_NF_TARGET_DSCP=m +CONFIG_IP_NF_TARGET_MARK=m +CONFIG_IP_NF_TARGET_CLASSIFY=m +CONFIG_IP_NF_RAW=m +CONFIG_IP_NF_TARGET_NOTRACK=m +CONFIG_IP_NF_ARPTABLES=m +CONFIG_IP_NF_ARPFILTER=m +CONFIG_IP_NF_ARP_MANGLE=m +# CONFIG_IP_NF_COMPAT_IPCHAINS is not set +# CONFIG_IP_NF_COMPAT_IPFWADM is not set + +# +# IPv6: Netfilter Configuration +# +# CONFIG_IP6_NF_QUEUE is not set +CONFIG_IP6_NF_IPTABLES=m +CONFIG_IP6_NF_MATCH_LIMIT=m +CONFIG_IP6_NF_MATCH_MAC=m +CONFIG_IP6_NF_MATCH_RT=m +CONFIG_IP6_NF_MATCH_OPTS=m +CONFIG_IP6_NF_MATCH_FRAG=m +CONFIG_IP6_NF_MATCH_HL=m +CONFIG_IP6_NF_MATCH_MULTIPORT=m +CONFIG_IP6_NF_MATCH_OWNER=m +CONFIG_IP6_NF_MATCH_MARK=m +CONFIG_IP6_NF_MATCH_IPV6HEADER=m +CONFIG_IP6_NF_MATCH_AHESP=m +CONFIG_IP6_NF_MATCH_LENGTH=m +CONFIG_IP6_NF_MATCH_EUI64=m +CONFIG_IP6_NF_MATCH_PHYSDEV=m +CONFIG_IP6_NF_FILTER=m +CONFIG_IP6_NF_TARGET_LOG=m +CONFIG_IP6_NF_MANGLE=m +CONFIG_IP6_NF_TARGET_MARK=m +CONFIG_IP6_NF_RAW=m + +# +# Bridge: Netfilter Configuration +# +CONFIG_BRIDGE_NF_EBTABLES=m +CONFIG_BRIDGE_EBT_BROUTE=m +CONFIG_BRIDGE_EBT_T_FILTER=m +CONFIG_BRIDGE_EBT_T_NAT=m +CONFIG_BRIDGE_EBT_802_3=m +CONFIG_BRIDGE_EBT_AMONG=m +CONFIG_BRIDGE_EBT_ARP=m +CONFIG_BRIDGE_EBT_IP=m +CONFIG_BRIDGE_EBT_LIMIT=m +CONFIG_BRIDGE_EBT_MARK=m +CONFIG_BRIDGE_EBT_PKTTYPE=m +CONFIG_BRIDGE_EBT_STP=m +CONFIG_BRIDGE_EBT_VLAN=m +CONFIG_BRIDGE_EBT_ARPREPLY=m +CONFIG_BRIDGE_EBT_DNAT=m +CONFIG_BRIDGE_EBT_MARK_T=m +CONFIG_BRIDGE_EBT_REDIRECT=m +CONFIG_BRIDGE_EBT_SNAT=m +CONFIG_BRIDGE_EBT_LOG=m +CONFIG_XFRM=y +CONFIG_XFRM_USER=y + +# +# SCTP Configuration (EXPERIMENTAL) +# +CONFIG_IP_SCTP=m +# CONFIG_SCTP_DBG_MSG is not set +# CONFIG_SCTP_DBG_OBJCNT is not set +# CONFIG_SCTP_HMAC_NONE is not set +# CONFIG_SCTP_HMAC_SHA1 is not set +CONFIG_SCTP_HMAC_MD5=y +CONFIG_ATM=m +CONFIG_ATM_CLIP=m +# CONFIG_ATM_CLIP_NO_ICMP is not set +CONFIG_ATM_LANE=m +# CONFIG_ATM_MPOA is not set +CONFIG_ATM_BR2684=m +# CONFIG_ATM_BR2684_IPFILTER is not set +CONFIG_BRIDGE=m +CONFIG_VLAN_8021Q=m +# CONFIG_DECNET is not set +CONFIG_LLC=y +# CONFIG_LLC2 is not set +CONFIG_IPX=m +# CONFIG_IPX_INTERN is not set +CONFIG_ATALK=m +CONFIG_DEV_APPLETALK=y +CONFIG_LTPC=m +CONFIG_COPS=m +CONFIG_COPS_DAYNA=y +CONFIG_COPS_TANGENT=y +CONFIG_IPDDP=m +CONFIG_IPDDP_ENCAP=y +CONFIG_IPDDP_DECAP=y +# CONFIG_X25 is not set +# CONFIG_LAPB is not set +CONFIG_NET_DIVERT=y +# CONFIG_ECONET is not set +CONFIG_WAN_ROUTER=m + +# +# QoS and/or fair queueing +# +CONFIG_NET_SCHED=y +CONFIG_NET_SCH_CLK_JIFFIES=y +# CONFIG_NET_SCH_CLK_GETTIMEOFDAY is not set +# CONFIG_NET_SCH_CLK_CPU is not set +CONFIG_NET_SCH_CBQ=m +CONFIG_NET_SCH_HTB=m +CONFIG_NET_SCH_HFSC=m +CONFIG_NET_SCH_ATM=m +CONFIG_NET_SCH_PRIO=m +CONFIG_NET_SCH_RED=m +CONFIG_NET_SCH_SFQ=m +CONFIG_NET_SCH_TEQL=m +CONFIG_NET_SCH_TBF=m +CONFIG_NET_SCH_GRED=m +CONFIG_NET_SCH_DSMARK=m +CONFIG_NET_SCH_NETEM=m +CONFIG_NET_SCH_INGRESS=m +CONFIG_NET_QOS=y +CONFIG_NET_ESTIMATOR=y +CONFIG_NET_CLS=y +CONFIG_NET_CLS_TCINDEX=m +CONFIG_NET_CLS_ROUTE4=m +CONFIG_NET_CLS_ROUTE=y +CONFIG_NET_CLS_FW=m +CONFIG_NET_CLS_U32=m +CONFIG_CLS_U32_PERF=y +CONFIG_NET_CLS_IND=y +CONFIG_NET_CLS_RSVP=m +CONFIG_NET_CLS_RSVP6=m +# CONFIG_NET_CLS_ACT is not set +CONFIG_NET_CLS_POLICE=y + +# +# Network testing +# +# CONFIG_NET_PKTGEN is not set +CONFIG_NETPOLL=y +# CONFIG_NETPOLL_RX is not set +CONFIG_NETPOLL_TRAP=y +CONFIG_NET_POLL_CONTROLLER=y +# CONFIG_HAMRADIO is not set +CONFIG_IRDA=m + +# +# IrDA protocols +# +CONFIG_IRLAN=m +CONFIG_IRNET=m +CONFIG_IRCOMM=m +# CONFIG_IRDA_ULTRA is not set + +# +# IrDA options +# +CONFIG_IRDA_CACHE_LAST_LSAP=y +CONFIG_IRDA_FAST_RR=y +# CONFIG_IRDA_DEBUG is not set + +# +# Infrared-port device drivers +# + +# +# SIR device drivers +# +CONFIG_IRTTY_SIR=m + +# +# Dongle support +# +CONFIG_DONGLE=y +CONFIG_ESI_DONGLE=m +CONFIG_ACTISYS_DONGLE=m +CONFIG_TEKRAM_DONGLE=m +CONFIG_LITELINK_DONGLE=m +CONFIG_MA600_DONGLE=m +CONFIG_GIRBIL_DONGLE=m +CONFIG_MCP2120_DONGLE=m +CONFIG_OLD_BELKIN_DONGLE=m +CONFIG_ACT200L_DONGLE=m + +# +# Old SIR device drivers +# + +# +# Old Serial dongle support +# + +# +# FIR device drivers +# +CONFIG_USB_IRDA=m +CONFIG_SIGMATEL_FIR=m +CONFIG_NSC_FIR=m +# CONFIG_WINBOND_FIR is not set +# CONFIG_TOSHIBA_FIR is not set +# CONFIG_SMC_IRCC_FIR is not set +# CONFIG_ALI_FIR is not set +# CONFIG_VLSI_FIR is not set +# CONFIG_VIA_FIR is not set +CONFIG_BT=m +CONFIG_BT_L2CAP=m +CONFIG_BT_SCO=m +CONFIG_BT_RFCOMM=m +CONFIG_BT_RFCOMM_TTY=y +CONFIG_BT_BNEP=m +CONFIG_BT_BNEP_MC_FILTER=y +CONFIG_BT_BNEP_PROTO_FILTER=y +CONFIG_BT_CMTP=m +CONFIG_BT_HIDP=m + +# +# Bluetooth device drivers +# +CONFIG_BT_HCIUSB=m +CONFIG_BT_HCIUSB_SCO=y +CONFIG_BT_HCIUART=m +CONFIG_BT_HCIUART_H4=y +CONFIG_BT_HCIUART_BCSP=y +CONFIG_BT_HCIUART_BCSP_TXCRC=y +CONFIG_BT_HCIBCM203X=m +CONFIG_BT_HCIBFUSB=m +CONFIG_BT_HCIDTL1=m +CONFIG_BT_HCIBT3C=m +CONFIG_BT_HCIBLUECARD=m +CONFIG_BT_HCIBTUART=m +CONFIG_BT_HCIVHCI=m +CONFIG_TUX=m + +# +# TUX options +# +CONFIG_TUX_EXTCGI=y +# CONFIG_TUX_EXTENDED_LOG is not set +# CONFIG_TUX_DEBUG is not set +CONFIG_NETDEVICES=y +CONFIG_DUMMY=m +CONFIG_BONDING=m +CONFIG_EQUALIZER=m +CONFIG_TUN=m +CONFIG_ETHERTAP=m +CONFIG_NET_SB1000=m + +# +# ARCnet devices +# +# CONFIG_ARCNET is not set + +# +# Ethernet (10 or 100Mbit) +# +CONFIG_NET_ETHERNET=y +CONFIG_MII=m +CONFIG_HAPPYMEAL=m +CONFIG_SUNGEM=m +CONFIG_NET_VENDOR_3COM=y +CONFIG_EL1=m +CONFIG_EL2=m +CONFIG_ELPLUS=m +CONFIG_EL16=m +CONFIG_EL3=m +CONFIG_3C515=m +CONFIG_VORTEX=m +CONFIG_TYPHOON=m +CONFIG_LANCE=m +CONFIG_NET_VENDOR_SMC=y +CONFIG_WD80x3=m +CONFIG_ULTRA=m +CONFIG_SMC9194=m +CONFIG_NET_VENDOR_RACAL=y +CONFIG_NI52=m +CONFIG_NI65=m + +# +# Tulip family network device support +# +CONFIG_NET_TULIP=y +CONFIG_DE2104X=m +CONFIG_TULIP=m +# CONFIG_TULIP_MWI is not set +CONFIG_TULIP_MMIO=y +# CONFIG_TULIP_NAPI is not set +CONFIG_DE4X5=m +CONFIG_WINBOND_840=m +CONFIG_DM9102=m +CONFIG_PCMCIA_XIRCOM=m +# CONFIG_AT1700 is not set +CONFIG_DEPCA=m +CONFIG_HP100=m +CONFIG_NET_ISA=y +CONFIG_E2100=m +# CONFIG_EWRK3 is not set +CONFIG_EEXPRESS=m +CONFIG_EEXPRESS_PRO=m +CONFIG_HPLAN_PLUS=m +CONFIG_HPLAN=m +CONFIG_LP486E=m +CONFIG_ETH16I=m +CONFIG_NE2000=m +CONFIG_ZNET=m +CONFIG_SEEQ8005=m +CONFIG_NET_PCI=y +CONFIG_PCNET32=m +CONFIG_AMD8111_ETH=m +CONFIG_AMD8111E_NAPI=y +CONFIG_ADAPTEC_STARFIRE=m +CONFIG_ADAPTEC_STARFIRE_NAPI=y +CONFIG_AC3200=m +CONFIG_APRICOT=m +CONFIG_B44=m +CONFIG_FORCEDETH=m +CONFIG_CS89x0=m +CONFIG_DGRS=m +CONFIG_EEPRO100=m +# CONFIG_EEPRO100_PIO is not set +CONFIG_E100=m +CONFIG_E100_NAPI=y +CONFIG_FEALNX=m +CONFIG_NATSEMI=m +CONFIG_NE2K_PCI=m +CONFIG_8139CP=m +CONFIG_8139TOO=m +CONFIG_8139TOO_PIO=y +# CONFIG_8139TOO_TUNE_TWISTER is not set +CONFIG_8139TOO_8129=y +# CONFIG_8139_OLD_RX_RESET is not set +CONFIG_SIS900=m +CONFIG_EPIC100=m +CONFIG_SUNDANCE=m +# CONFIG_SUNDANCE_MMIO is not set +CONFIG_TLAN=m +CONFIG_VIA_RHINE=m +CONFIG_VIA_RHINE_MMIO=y +CONFIG_NET_POCKET=y +CONFIG_ATP=m +CONFIG_DE600=m +CONFIG_DE620=m + +# +# Ethernet (1000 Mbit) +# +CONFIG_ACENIC=m +# CONFIG_ACENIC_OMIT_TIGON_I is not set +CONFIG_DL2K=m +CONFIG_E1000=m +CONFIG_E1000_NAPI=y +CONFIG_NS83820=m +CONFIG_HAMACHI=m +CONFIG_YELLOWFIN=m +CONFIG_R8169=m +CONFIG_R8169_NAPI=y +CONFIG_SK98LIN=m +CONFIG_VIA_VELOCITY=m +CONFIG_TIGON3=m + +# +# Ethernet (10000 Mbit) +# +CONFIG_IXGB=m +CONFIG_IXGB_NAPI=y +CONFIG_S2IO=m +CONFIG_S2IO_NAPI=y + +# +# Token Ring devices +# +CONFIG_TR=y +CONFIG_IBMTR=m +CONFIG_IBMOL=m +CONFIG_IBMLS=m +CONFIG_3C359=m +CONFIG_TMS380TR=m +CONFIG_TMSPCI=m +CONFIG_SKISA=m +CONFIG_PROTEON=m +CONFIG_ABYSS=m +CONFIG_SMCTR=m + +# +# Wireless LAN (non-hamradio) +# +CONFIG_NET_RADIO=y + +# +# Obsolete Wireless cards support (pre-802.11) +# +# CONFIG_STRIP is not set +# CONFIG_ARLAN is not set +CONFIG_WAVELAN=m +CONFIG_PCMCIA_WAVELAN=m +CONFIG_PCMCIA_NETWAVE=m + +# +# Wireless 802.11 Frequency Hopping cards support +# +# CONFIG_PCMCIA_RAYCS is not set + +# +# Wireless 802.11b ISA/PCI cards support +# +CONFIG_IEEE80211=m +# CONFIG_IEEE80211_DEBUG is not set +CONFIG_IEEE80211_CRYPT=m +CONFIG_IEEE80211_WPA=m +CONFIG_IEEE80211_CRYPT_CCMP=m +CONFIG_IEEE80211_CRYPT_TKIP=m +CONFIG_IPW2100=m +# CONFIG_IPW_DEBUG is not set +CONFIG_IPW2100_PROMISC=y +# CONFIG_IPW2100_LEGACY_FW_LOAD is not set +CONFIG_IPW2200=m +CONFIG_AIRO=m +CONFIG_HERMES=m +CONFIG_PLX_HERMES=m +CONFIG_TMD_HERMES=m +CONFIG_PCI_HERMES=m +CONFIG_ATMEL=m +CONFIG_PCI_ATMEL=m + +# +# Wireless 802.11b Pcmcia/Cardbus cards support +# +CONFIG_PCMCIA_HERMES=m +CONFIG_AIRO_CS=m +CONFIG_PCMCIA_ATMEL=m +CONFIG_PCMCIA_WL3501=m + +# +# Prism GT/Duette 802.11(a/b/g) PCI/Cardbus support +# +CONFIG_PRISM54=m +CONFIG_NET_WIRELESS=y + +# +# PCMCIA network device support +# +CONFIG_NET_PCMCIA=y +CONFIG_PCMCIA_3C589=m +CONFIG_PCMCIA_3C574=m +CONFIG_PCMCIA_FMVJ18X=m +CONFIG_PCMCIA_PCNET=m +CONFIG_PCMCIA_NMCLAN=m +CONFIG_PCMCIA_SMC91C92=m +CONFIG_PCMCIA_XIRC2PS=m +CONFIG_PCMCIA_AXNET=m +CONFIG_PCMCIA_IBMTR=m + +# +# Wan interfaces +# +# CONFIG_WAN is not set + +# +# ATM drivers +# +CONFIG_ATM_TCP=m +CONFIG_ATM_LANAI=m +CONFIG_ATM_ENI=m +# CONFIG_ATM_ENI_DEBUG is not set +# CONFIG_ATM_ENI_TUNE_BURST is not set +CONFIG_ATM_FIRESTREAM=m +# CONFIG_ATM_ZATM is not set +CONFIG_ATM_NICSTAR=m +# CONFIG_ATM_NICSTAR_USE_SUNI is not set +# CONFIG_ATM_NICSTAR_USE_IDT77105 is not set +CONFIG_ATM_IDT77252=m +# CONFIG_ATM_IDT77252_DEBUG is not set +# CONFIG_ATM_IDT77252_RCV_ALL is not set +CONFIG_ATM_IDT77252_USE_SUNI=y +CONFIG_ATM_AMBASSADOR=m +# CONFIG_ATM_AMBASSADOR_DEBUG is not set +CONFIG_ATM_HORIZON=m +# CONFIG_ATM_HORIZON_DEBUG is not set +# CONFIG_ATM_IA is not set +CONFIG_ATM_FORE200E_MAYBE=m +# CONFIG_ATM_FORE200E_PCA is not set +CONFIG_ATM_HE=m +# CONFIG_ATM_HE_USE_SUNI is not set +CONFIG_FDDI=y +# CONFIG_DEFXX is not set +CONFIG_SKFP=m +# CONFIG_HIPPI is not set +CONFIG_PLIP=m +CONFIG_PPP=m +CONFIG_PPP_MULTILINK=y +CONFIG_PPP_FILTER=y +CONFIG_PPP_ASYNC=m +CONFIG_PPP_SYNC_TTY=m +CONFIG_PPP_DEFLATE=m +# CONFIG_PPP_BSDCOMP is not set +CONFIG_PPPOE=m +CONFIG_PPPOATM=m +CONFIG_SLIP=m +CONFIG_SLIP_COMPRESSED=y +CONFIG_SLIP_SMART=y +# CONFIG_SLIP_MODE_SLIP6 is not set +CONFIG_NET_FC=y +# CONFIG_SHAPER is not set +CONFIG_NETCONSOLE=m + +# +# ISDN subsystem +# +CONFIG_ISDN=m + +# +# Old ISDN4Linux +# +CONFIG_ISDN_I4L=m +CONFIG_ISDN_PPP=y +CONFIG_ISDN_PPP_VJ=y +CONFIG_ISDN_MPP=y +CONFIG_IPPP_FILTER=y +# CONFIG_ISDN_PPP_BSDCOMP is not set +CONFIG_ISDN_AUDIO=y +CONFIG_ISDN_TTY_FAX=y + +# +# ISDN feature submodules +# + +# +# ISDN4Linux hardware drivers +# + +# +# Passive cards +# +CONFIG_ISDN_DRV_HISAX=m + +# +# D-channel protocol features +# +CONFIG_HISAX_EURO=y +CONFIG_DE_AOC=y +CONFIG_HISAX_NO_SENDCOMPLETE=y +CONFIG_HISAX_NO_LLC=y +CONFIG_HISAX_NO_KEYPAD=y +CONFIG_HISAX_1TR6=y +CONFIG_HISAX_NI1=y +CONFIG_HISAX_MAX_CARDS=8 + +# +# HiSax supported cards +# +CONFIG_HISAX_16_0=y +CONFIG_HISAX_16_3=y +CONFIG_HISAX_TELESPCI=y +CONFIG_HISAX_S0BOX=y +CONFIG_HISAX_AVM_A1=y +CONFIG_HISAX_FRITZPCI=y +CONFIG_HISAX_AVM_A1_PCMCIA=y +CONFIG_HISAX_ELSA=y +CONFIG_HISAX_IX1MICROR2=y +CONFIG_HISAX_DIEHLDIVA=y +CONFIG_HISAX_ASUSCOM=y +CONFIG_HISAX_TELEINT=y +CONFIG_HISAX_HFCS=y +CONFIG_HISAX_SEDLBAUER=y +CONFIG_HISAX_SPORTSTER=y +CONFIG_HISAX_MIC=y +CONFIG_HISAX_NETJET=y +CONFIG_HISAX_NETJET_U=y +CONFIG_HISAX_NICCY=y +CONFIG_HISAX_ISURF=y +CONFIG_HISAX_HSTSAPHIR=y +CONFIG_HISAX_BKM_A4T=y +CONFIG_HISAX_SCT_QUADRO=y +CONFIG_HISAX_GAZEL=y +CONFIG_HISAX_HFC_PCI=y +CONFIG_HISAX_W6692=y +CONFIG_HISAX_HFC_SX=y +CONFIG_HISAX_ENTERNOW_PCI=y +# CONFIG_HISAX_DEBUG is not set + +# +# HiSax PCMCIA card service modules +# +CONFIG_HISAX_SEDLBAUER_CS=m +CONFIG_HISAX_ELSA_CS=m +CONFIG_HISAX_AVM_A1_CS=m +CONFIG_HISAX_TELES_CS=m + +# +# HiSax sub driver modules +# +CONFIG_HISAX_ST5481=m +CONFIG_HISAX_HFCUSB=m +CONFIG_HISAX_FRITZ_PCIPNP=m +CONFIG_HISAX_HDLC=y + +# +# Active cards +# +CONFIG_ISDN_DRV_ICN=m +CONFIG_ISDN_DRV_PCBIT=m +CONFIG_ISDN_DRV_SC=m +CONFIG_ISDN_DRV_ACT2000=m +CONFIG_ISDN_DRV_TPAM=m + +# +# CAPI subsystem +# +CONFIG_ISDN_CAPI=m +CONFIG_ISDN_DRV_AVMB1_VERBOSE_REASON=y +CONFIG_ISDN_CAPI_MIDDLEWARE=y +CONFIG_ISDN_CAPI_CAPI20=m +CONFIG_ISDN_CAPI_CAPIFS_BOOL=y +CONFIG_ISDN_CAPI_CAPIFS=m +CONFIG_ISDN_CAPI_CAPIDRV=m + +# +# CAPI hardware drivers +# + +# +# Active AVM cards +# +CONFIG_CAPI_AVM=y + +# +# Active Eicon DIVA Server cards +# +CONFIG_CAPI_EICON=y +CONFIG_ISDN_DIVAS=m +CONFIG_ISDN_DIVAS_BRIPCI=y +CONFIG_ISDN_DIVAS_PRIPCI=y +CONFIG_ISDN_DIVAS_DIVACAPI=m +CONFIG_ISDN_DIVAS_USERIDI=m +CONFIG_ISDN_DIVAS_MAINT=m + +# +# Telephony Support +# +# CONFIG_PHONE is not set + +# +# Input device support +# +CONFIG_INPUT=y + +# +# 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=m +# CONFIG_INPUT_TSDEV is not set +CONFIG_INPUT_EVDEV=y +# CONFIG_INPUT_EVBUG is not set + +# +# Input I/O drivers +# +CONFIG_GAMEPORT=m +CONFIG_SOUND_GAMEPORT=m +CONFIG_GAMEPORT_NS558=m +CONFIG_GAMEPORT_L4=m +CONFIG_GAMEPORT_EMU10K1=m +CONFIG_GAMEPORT_VORTEX=m +CONFIG_GAMEPORT_FM801=m +CONFIG_GAMEPORT_CS461x=m +CONFIG_SERIO=y +CONFIG_SERIO_I8042=y +CONFIG_SERIO_SERPORT=y +# CONFIG_SERIO_CT82C710 is not set +# CONFIG_SERIO_PARKBD is not set +# CONFIG_SERIO_PCIPS2 is not set +# CONFIG_SERIO_RAW 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_INPUT_MOUSE=y +CONFIG_MOUSE_PS2=y +CONFIG_MOUSE_SERIAL=m +CONFIG_MOUSE_INPORT=m +CONFIG_MOUSE_ATIXL=y +CONFIG_MOUSE_LOGIBM=m +CONFIG_MOUSE_PC110PAD=m +CONFIG_MOUSE_VSXXXAA=m +CONFIG_INPUT_JOYSTICK=y +CONFIG_JOYSTICK_ANALOG=m +CONFIG_JOYSTICK_A3D=m +CONFIG_JOYSTICK_ADI=m +CONFIG_JOYSTICK_COBRA=m +CONFIG_JOYSTICK_GF2K=m +CONFIG_JOYSTICK_GRIP=m +CONFIG_JOYSTICK_GRIP_MP=m +CONFIG_JOYSTICK_GUILLEMOT=m +CONFIG_JOYSTICK_INTERACT=m +CONFIG_JOYSTICK_SIDEWINDER=m +CONFIG_JOYSTICK_TMDC=m +CONFIG_JOYSTICK_IFORCE=m +CONFIG_JOYSTICK_IFORCE_USB=y +CONFIG_JOYSTICK_IFORCE_232=y +CONFIG_JOYSTICK_WARRIOR=m +CONFIG_JOYSTICK_MAGELLAN=m +CONFIG_JOYSTICK_SPACEORB=m +CONFIG_JOYSTICK_SPACEBALL=m +CONFIG_JOYSTICK_STINGER=m +CONFIG_JOYSTICK_TWIDDLER=m +CONFIG_JOYSTICK_DB9=m +CONFIG_JOYSTICK_GAMECON=m +CONFIG_JOYSTICK_TURBOGRAFX=m +CONFIG_JOYSTICK_JOYDUMP=m +CONFIG_INPUT_TOUCHSCREEN=y +CONFIG_TOUCHSCREEN_GUNZE=m +CONFIG_INPUT_MISC=y +CONFIG_INPUT_PCSPKR=m +CONFIG_INPUT_UINPUT=m + +# +# Character devices +# +CONFIG_VT=y +CONFIG_VT_CONSOLE=y +CONFIG_HW_CONSOLE=y +CONFIG_SERIAL_NONSTANDARD=y +CONFIG_ROCKETPORT=m +# CONFIG_CYCLADES is not set +CONFIG_SYNCLINK=m +CONFIG_SYNCLINKMP=m +CONFIG_N_HDLC=m +CONFIG_STALDRV=y + +# +# Serial drivers +# +CONFIG_SERIAL_8250=y +CONFIG_SERIAL_8250_CONSOLE=y +CONFIG_SERIAL_8250_CS=m +# CONFIG_SERIAL_8250_ACPI is not set +CONFIG_SERIAL_8250_NR_UARTS=32 +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_MULTIPORT=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_CRASH=m +CONFIG_PRINTER=m +CONFIG_LP_CONSOLE=y +CONFIG_PPDEV=m +CONFIG_TIPAR=m + +# +# IPMI +# +CONFIG_IPMI_HANDLER=m +# CONFIG_IPMI_PANIC_EVENT is not set +CONFIG_IPMI_DEVICE_INTERFACE=m +CONFIG_IPMI_SI=m +CONFIG_IPMI_WATCHDOG=m +CONFIG_IPMI_POWEROFF=m + +# +# Watchdog Cards +# +CONFIG_WATCHDOG=y +# CONFIG_WATCHDOG_NOWAYOUT is not set + +# +# Watchdog Device Drivers +# +CONFIG_SOFT_WATCHDOG=m +CONFIG_ACQUIRE_WDT=m +CONFIG_ADVANTECH_WDT=m +CONFIG_ALIM1535_WDT=m +CONFIG_ALIM7101_WDT=m +CONFIG_SC520_WDT=m +CONFIG_EUROTECH_WDT=m +CONFIG_IB700_WDT=m +CONFIG_WAFER_WDT=m +CONFIG_I8XX_TCO=m +CONFIG_SC1200_WDT=m +# CONFIG_SCx200_WDT is not set +# CONFIG_60XX_WDT is not set +CONFIG_CPU5_WDT=m +CONFIG_W83627HF_WDT=m +CONFIG_W83877F_WDT=m +CONFIG_MACHZ_WDT=m + +# +# ISA-based Watchdog Cards +# +CONFIG_PCWATCHDOG=m +# CONFIG_MIXCOMWD is not set +CONFIG_WDT=m +# CONFIG_WDT_501 is not set + +# +# PCI-based Watchdog Cards +# +CONFIG_PCIPCWATCHDOG=m +CONFIG_WDTPCI=m +CONFIG_WDT_501_PCI=y + +# +# USB-based Watchdog Cards +# +CONFIG_USBPCWATCHDOG=m +CONFIG_HW_RANDOM=m +CONFIG_NVRAM=m +CONFIG_RTC=y +CONFIG_DTLK=m +CONFIG_R3964=m +# CONFIG_APPLICOM is not set +CONFIG_SONYPI=m + +# +# Ftape, the floppy tape device driver +# +CONFIG_AGP=y +CONFIG_AGP_ALI=y +CONFIG_AGP_ATI=y +CONFIG_AGP_AMD=y +CONFIG_AGP_AMD64=y +CONFIG_AGP_INTEL=y +CONFIG_AGP_INTEL_MCH=y +CONFIG_AGP_NVIDIA=y +CONFIG_AGP_SIS=y +CONFIG_AGP_SWORKS=y +CONFIG_AGP_VIA=y +CONFIG_AGP_EFFICEON=y +CONFIG_DRM=y +CONFIG_DRM_TDFX=m +CONFIG_DRM_R128=m +CONFIG_DRM_RADEON=m +CONFIG_DRM_I810=m +CONFIG_DRM_I830=m +CONFIG_DRM_I915=m +CONFIG_DRM_MGA=m +CONFIG_DRM_SIS=m + +# +# PCMCIA character devices +# +CONFIG_SYNCLINK_CS=m +CONFIG_MWAVE=m +CONFIG_RAW_DRIVER=m +# CONFIG_HPET is not set +CONFIG_MAX_RAW_DEVS=8192 +CONFIG_HANGCHECK_TIMER=m + +# +# I2C support +# +CONFIG_I2C=m +CONFIG_I2C_CHARDEV=m + +# +# I2C Algorithms +# +CONFIG_I2C_ALGOBIT=m +CONFIG_I2C_ALGOPCF=m +CONFIG_I2C_ALGOPCA=m + +# +# I2C Hardware Bus support +# +CONFIG_I2C_ALI1535=m +CONFIG_I2C_ALI1563=m +CONFIG_I2C_ALI15X3=m +CONFIG_I2C_AMD756=m +CONFIG_I2C_AMD8111=m +CONFIG_I2C_I801=m +CONFIG_I2C_I810=m +CONFIG_I2C_ISA=m +CONFIG_I2C_NFORCE2=m +# CONFIG_I2C_PARPORT is not set +# CONFIG_I2C_PARPORT_LIGHT is not set +CONFIG_I2C_PIIX4=m +CONFIG_I2C_PROSAVAGE=m +CONFIG_I2C_SAVAGE4=m +# CONFIG_SCx200_ACB is not set +CONFIG_I2C_SIS5595=m +CONFIG_I2C_SIS630=m +CONFIG_I2C_SIS96X=m +CONFIG_I2C_VIA=m +CONFIG_I2C_VIAPRO=m +CONFIG_I2C_VOODOO3=m +CONFIG_I2C_PCA_ISA=m + +# +# Hardware Sensors Chip support +# +CONFIG_I2C_SENSOR=m +CONFIG_SENSORS_ADM1021=m +CONFIG_SENSORS_ADM1025=m +CONFIG_SENSORS_ADM1031=m +CONFIG_SENSORS_ASB100=m +CONFIG_SENSORS_DS1621=m +CONFIG_SENSORS_FSCHER=m +CONFIG_SENSORS_GL518SM=m +CONFIG_SENSORS_IT87=m +CONFIG_SENSORS_LM75=m +CONFIG_SENSORS_LM77=m +CONFIG_SENSORS_LM78=m +CONFIG_SENSORS_LM80=m +CONFIG_SENSORS_LM83=m +CONFIG_SENSORS_LM85=m +CONFIG_SENSORS_LM90=m +CONFIG_SENSORS_MAX1619=m +CONFIG_SENSORS_SMSC47M1=m +CONFIG_SENSORS_VIA686A=m +CONFIG_SENSORS_W83781D=m +CONFIG_SENSORS_W83L785TS=m +CONFIG_SENSORS_W83627HF=m + +# +# Other I2C Chip support +# +CONFIG_SENSORS_EEPROM=m +CONFIG_SENSORS_PCF8574=m +CONFIG_SENSORS_PCF8591=m +CONFIG_SENSORS_RTC8564=m +# 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 + +# +# Dallas's 1-wire bus +# +# CONFIG_W1 is not set + +# +# Misc devices +# +CONFIG_IBM_ASM=m + +# +# Multimedia devices +# +CONFIG_VIDEO_DEV=m + +# +# Video For Linux +# + +# +# Video Adapters +# +CONFIG_VIDEO_BT848=m +CONFIG_VIDEO_PMS=m +CONFIG_VIDEO_BWQCAM=m +CONFIG_VIDEO_CQCAM=m +CONFIG_VIDEO_W9966=m +CONFIG_VIDEO_CPIA=m +CONFIG_VIDEO_CPIA_PP=m +CONFIG_VIDEO_CPIA_USB=m +CONFIG_VIDEO_SAA5246A=m +CONFIG_VIDEO_SAA5249=m +CONFIG_TUNER_3036=m +CONFIG_VIDEO_STRADIS=m +CONFIG_VIDEO_ZORAN=m +CONFIG_VIDEO_ZORAN_BUZ=m +CONFIG_VIDEO_ZORAN_DC10=m +CONFIG_VIDEO_ZORAN_DC30=m +CONFIG_VIDEO_ZORAN_LML33=m +CONFIG_VIDEO_ZORAN_LML33R10=m +CONFIG_VIDEO_SAA7134=m +CONFIG_VIDEO_MXB=m +CONFIG_VIDEO_DPC=m +CONFIG_VIDEO_HEXIUM_ORION=m +CONFIG_VIDEO_HEXIUM_GEMINI=m +CONFIG_VIDEO_CX88=m +CONFIG_VIDEO_OVCAMCHIP=m + +# +# Radio Adapters +# +CONFIG_RADIO_CADET=m +CONFIG_RADIO_RTRACK=m +CONFIG_RADIO_RTRACK2=m +CONFIG_RADIO_AZTECH=m +CONFIG_RADIO_GEMTEK=m +CONFIG_RADIO_GEMTEK_PCI=m +CONFIG_RADIO_MAXIRADIO=m +CONFIG_RADIO_MAESTRO=m +CONFIG_RADIO_SF16FMI=m +CONFIG_RADIO_SF16FMR2=m +CONFIG_RADIO_TERRATEC=m +CONFIG_RADIO_TRUST=m +CONFIG_RADIO_TYPHOON=m +CONFIG_RADIO_TYPHOON_PROC_FS=y +CONFIG_RADIO_ZOLTRIX=m + +# +# Digital Video Broadcasting Devices +# +CONFIG_DVB=y +CONFIG_DVB_CORE=m + +# +# Supported Frontend Modules +# +CONFIG_DVB_TWINHAN_DST=m +CONFIG_DVB_STV0299=m +# CONFIG_DVB_SP887X is not set +# CONFIG_DVB_ALPS_TDLB7 is not set +CONFIG_DVB_ALPS_TDMB7=m +CONFIG_DVB_ATMEL_AT76C651=m +CONFIG_DVB_CX24110=m +CONFIG_DVB_GRUNDIG_29504_491=m +CONFIG_DVB_GRUNDIG_29504_401=m +CONFIG_DVB_MT312=m +CONFIG_DVB_VES1820=m +CONFIG_DVB_VES1X93=m +# CONFIG_DVB_TDA1004X is not set +CONFIG_DVB_NXT6000=m + +# +# Supported SAA7146 based PCI Adapters +# +CONFIG_DVB_AV7110=m +CONFIG_DVB_AV7110_OSD=y +CONFIG_DVB_BUDGET=m +CONFIG_DVB_BUDGET_CI=m +CONFIG_DVB_BUDGET_AV=m +CONFIG_DVB_BUDGET_PATCH=m + +# +# Supported USB Adapters +# +CONFIG_DVB_TTUSB_BUDGET=m +CONFIG_DVB_TTUSB_DEC=m + +# +# Supported FlexCopII (B2C2) Adapters +# +CONFIG_DVB_B2C2_SKYSTAR=m + +# +# Supported BT878 Adapters +# +CONFIG_DVB_BT8XX=m +CONFIG_VIDEO_SAA7146=m +CONFIG_VIDEO_SAA7146_VV=m +CONFIG_VIDEO_VIDEOBUF=m +CONFIG_VIDEO_TUNER=m +CONFIG_VIDEO_BUF=m +CONFIG_VIDEO_BTCX=m +CONFIG_VIDEO_IR=m + +# +# Graphics support +# +CONFIG_FB=y +CONFIG_FB_MODE_HELPERS=y +CONFIG_FB_CIRRUS=m +# CONFIG_FB_PM2 is not set +# CONFIG_FB_CYBER2000 is not set +# CONFIG_FB_ASILIANT is not set +# CONFIG_FB_IMSTT is not set +CONFIG_FB_VGA16=m +CONFIG_FB_VESA=y +CONFIG_VIDEO_SELECT=y +CONFIG_FB_HGA=m +CONFIG_FB_HGA_ACCEL=y +CONFIG_FB_RIVA=m +# CONFIG_FB_RIVA_I2C is not set +# CONFIG_FB_RIVA_DEBUG is not set +CONFIG_FB_I810=m +CONFIG_FB_I810_GTF=y +CONFIG_FB_MATROX=m +CONFIG_FB_MATROX_MILLENIUM=y +CONFIG_FB_MATROX_MYSTIQUE=y +CONFIG_FB_MATROX_G450=y +CONFIG_FB_MATROX_G100=y +CONFIG_FB_MATROX_I2C=m +CONFIG_FB_MATROX_MAVEN=m +CONFIG_FB_MATROX_MULTIHEAD=y +# CONFIG_FB_RADEON_OLD is not set +CONFIG_FB_RADEON=m +CONFIG_FB_RADEON_I2C=y +# CONFIG_FB_RADEON_DEBUG is not set +CONFIG_FB_ATY128=m +CONFIG_FB_ATY=m +CONFIG_FB_ATY_CT=y +CONFIG_FB_ATY_GX=y +# CONFIG_FB_ATY_XL_INIT is not set +# CONFIG_FB_SIS is not set +CONFIG_FB_NEOMAGIC=m +CONFIG_FB_KYRO=m +CONFIG_FB_3DFX=m +CONFIG_FB_3DFX_ACCEL=y +CONFIG_FB_VOODOO1=m +CONFIG_FB_TRIDENT=m +CONFIG_FB_TRIDENT_ACCEL=y +# CONFIG_FB_VIRTUAL is not set + +# +# Console display driver support +# +CONFIG_VGA_CONSOLE=y +CONFIG_MDA_CONSOLE=m +CONFIG_DUMMY_CONSOLE=y +CONFIG_FRAMEBUFFER_CONSOLE=y +# 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 + +# +# Sound +# +CONFIG_SOUND=m + +# +# Advanced Linux Sound Architecture +# +CONFIG_SND=m +CONFIG_SND_TIMER=m +CONFIG_SND_PCM=m +CONFIG_SND_HWDEP=m +CONFIG_SND_RAWMIDI=m +CONFIG_SND_SEQUENCER=m +CONFIG_SND_SEQ_DUMMY=m +CONFIG_SND_OSSEMUL=y +CONFIG_SND_MIXER_OSS=m +CONFIG_SND_PCM_OSS=m +CONFIG_SND_SEQUENCER_OSS=y +CONFIG_SND_RTCTIMER=m +# CONFIG_SND_VERBOSE_PRINTK is not set +# CONFIG_SND_DEBUG is not set + +# +# Generic devices +# +CONFIG_SND_MPU401_UART=m +CONFIG_SND_OPL3_LIB=m +CONFIG_SND_OPL4_LIB=m +CONFIG_SND_VX_LIB=m +CONFIG_SND_DUMMY=m +CONFIG_SND_VIRMIDI=m +CONFIG_SND_MTPAV=m +# CONFIG_SND_SERIAL_U16550 is not set +CONFIG_SND_MPU401=m + +# +# ISA devices +# +CONFIG_SND_AD1816A=m +CONFIG_SND_AD1848=m +CONFIG_SND_CS4231=m +CONFIG_SND_CS4232=m +CONFIG_SND_CS4236=m +CONFIG_SND_ES968=m +CONFIG_SND_ES1688=m +CONFIG_SND_ES18XX=m +CONFIG_SND_GUS_SYNTH=m +CONFIG_SND_GUSCLASSIC=m +CONFIG_SND_GUSEXTREME=m +CONFIG_SND_GUSMAX=m +CONFIG_SND_INTERWAVE=m +CONFIG_SND_INTERWAVE_STB=m +CONFIG_SND_OPTI92X_AD1848=m +CONFIG_SND_OPTI92X_CS4231=m +CONFIG_SND_OPTI93X=m +CONFIG_SND_SB8=m +CONFIG_SND_SB16=m +CONFIG_SND_SBAWE=m +CONFIG_SND_SB16_CSP=y +# CONFIG_SND_WAVEFRONT is not set +CONFIG_SND_ALS100=m +CONFIG_SND_AZT2320=m +CONFIG_SND_CMI8330=m +CONFIG_SND_DT019X=m +CONFIG_SND_OPL3SA2=m +CONFIG_SND_SGALAXY=m +CONFIG_SND_SSCAPE=m + +# +# PCI devices +# +CONFIG_SND_AC97_CODEC=m +CONFIG_SND_ALI5451=m +CONFIG_SND_ATIIXP=m +CONFIG_SND_ATIIXP_MODEM=m +CONFIG_SND_AU8810=m +CONFIG_SND_AU8820=m +CONFIG_SND_AU8830=m +CONFIG_SND_AZT3328=m +CONFIG_SND_BT87X=m +CONFIG_SND_CS46XX=m +CONFIG_SND_CS46XX_NEW_DSP=y +CONFIG_SND_CS4281=m +CONFIG_SND_EMU10K1=m +CONFIG_SND_KORG1212=m +CONFIG_SND_MIXART=m +CONFIG_SND_NM256=m +CONFIG_SND_RME32=m +CONFIG_SND_RME96=m +CONFIG_SND_RME9652=m +CONFIG_SND_HDSP=m +CONFIG_SND_TRIDENT=m +CONFIG_SND_YMFPCI=m +CONFIG_SND_ALS4000=m +CONFIG_SND_CMIPCI=m +CONFIG_SND_ENS1370=m +CONFIG_SND_ENS1371=m +CONFIG_SND_ES1938=m +CONFIG_SND_ES1968=m +CONFIG_SND_MAESTRO3=m +CONFIG_SND_FM801=m +CONFIG_SND_FM801_TEA575X=m +CONFIG_SND_ICE1712=m +CONFIG_SND_ICE1724=m +CONFIG_SND_INTEL8X0=m +CONFIG_SND_INTEL8X0M=m +CONFIG_SND_SONICVIBES=m +CONFIG_SND_VIA82XX=m +CONFIG_SND_VX222=m + +# +# ALSA USB devices +# +CONFIG_SND_USB_AUDIO=m +CONFIG_SND_USB_USX2Y=m + +# +# PCMCIA devices +# +# CONFIG_SND_VXPOCKET is not set +# CONFIG_SND_VXP440 is not set +CONFIG_SND_PDAUDIOCF=m + +# +# Open Sound System +# +# CONFIG_SOUND_PRIME is not set + +# +# USB support +# +CONFIG_USB=y +# CONFIG_USB_DEBUG is not set + +# +# Miscellaneous USB options +# +CONFIG_USB_DEVICEFS=y +# CONFIG_USB_BANDWIDTH is not set +# CONFIG_USB_DYNAMIC_MINORS is not set +# CONFIG_USB_SUSPEND is not set +# CONFIG_USB_OTG is not set + +# +# USB Host Controller Drivers +# +CONFIG_USB_EHCI_HCD=m +CONFIG_USB_EHCI_SPLIT_ISO=y +CONFIG_USB_EHCI_ROOT_HUB_TT=y +CONFIG_USB_OHCI_HCD=m +CONFIG_USB_UHCI_HCD=m + +# +# USB Device Class drivers +# +# CONFIG_USB_AUDIO is not set + +# +# USB Bluetooth TTY can only be used with disabled Bluetooth subsystem +# +CONFIG_USB_MIDI=m +CONFIG_USB_ACM=m +CONFIG_USB_PRINTER=m +CONFIG_USB_STORAGE=m +# CONFIG_USB_STORAGE_DEBUG is not set +CONFIG_USB_STORAGE_RW_DETECT=y +CONFIG_USB_STORAGE_DATAFAB=y +CONFIG_USB_STORAGE_FREECOM=y +CONFIG_USB_STORAGE_ISD200=y +CONFIG_USB_STORAGE_DPCM=y +CONFIG_USB_STORAGE_HP8200e=y +CONFIG_USB_STORAGE_SDDR09=y +CONFIG_USB_STORAGE_SDDR55=y +CONFIG_USB_STORAGE_JUMPSHOT=y + +# +# USB Human Interface Devices (HID) +# +CONFIG_USB_HID=y +CONFIG_USB_HIDINPUT=y +CONFIG_HID_FF=y +CONFIG_HID_PID=y +CONFIG_LOGITECH_FF=y +CONFIG_THRUSTMASTER_FF=y +CONFIG_USB_HIDDEV=y +CONFIG_USB_AIPTEK=m +CONFIG_USB_WACOM=m +CONFIG_USB_KBTAB=m +CONFIG_USB_POWERMATE=m +CONFIG_USB_MTOUCH=m +CONFIG_USB_EGALAX=m +CONFIG_USB_XPAD=m +CONFIG_USB_ATI_REMOTE=m + +# +# USB Imaging devices +# +CONFIG_USB_MDC800=m +CONFIG_USB_MICROTEK=m +CONFIG_USB_HPUSBSCSI=m + +# +# USB Multimedia devices +# +CONFIG_USB_DABUSB=m +CONFIG_USB_VICAM=m +CONFIG_USB_DSBR=m +CONFIG_USB_IBMCAM=m +CONFIG_USB_KONICAWC=m +CONFIG_USB_OV511=m +CONFIG_USB_SE401=m +CONFIG_USB_SN9C102=m +CONFIG_USB_STV680=m +CONFIG_USB_W9968CF=m +CONFIG_USB_PWC=m + +# +# USB Network adaptors +# +CONFIG_USB_CATC=m +CONFIG_USB_KAWETH=m +CONFIG_USB_PEGASUS=m +CONFIG_USB_RTL8150=m +CONFIG_USB_USBNET=m + +# +# USB Host-to-Host Cables +# +CONFIG_USB_ALI_M5632=y +CONFIG_USB_AN2720=y +CONFIG_USB_BELKIN=y +CONFIG_USB_GENESYS=y +CONFIG_USB_NET1080=y +CONFIG_USB_PL2301=y + +# +# Intelligent USB Devices/Gadgets +# +CONFIG_USB_ARMLINUX=y +CONFIG_USB_EPSON2888=y +CONFIG_USB_ZAURUS=y +CONFIG_USB_CDCETHER=y + +# +# USB Network Adapters +# +CONFIG_USB_AX8817X=y + +# +# USB port drivers +# +CONFIG_USB_USS720=m + +# +# USB Serial Converter support +# +CONFIG_USB_SERIAL=m +CONFIG_USB_SERIAL_GENERIC=y +CONFIG_USB_SERIAL_BELKIN=m +CONFIG_USB_SERIAL_DIGI_ACCELEPORT=m +CONFIG_USB_SERIAL_EMPEG=m +CONFIG_USB_SERIAL_FTDI_SIO=m +CONFIG_USB_SERIAL_VISOR=m +CONFIG_USB_SERIAL_IPAQ=m +CONFIG_USB_SERIAL_IR=m +CONFIG_USB_SERIAL_EDGEPORT=m +CONFIG_USB_SERIAL_EDGEPORT_TI=m +CONFIG_USB_SERIAL_KEYSPAN_PDA=m +CONFIG_USB_SERIAL_KEYSPAN=m +CONFIG_USB_SERIAL_KEYSPAN_MPR=y +CONFIG_USB_SERIAL_KEYSPAN_USA28=y +CONFIG_USB_SERIAL_KEYSPAN_USA28X=y +CONFIG_USB_SERIAL_KEYSPAN_USA28XA=y +CONFIG_USB_SERIAL_KEYSPAN_USA28XB=y +CONFIG_USB_SERIAL_KEYSPAN_USA19=y +CONFIG_USB_SERIAL_KEYSPAN_USA18X=y +CONFIG_USB_SERIAL_KEYSPAN_USA19W=y +CONFIG_USB_SERIAL_KEYSPAN_USA19QW=y +CONFIG_USB_SERIAL_KEYSPAN_USA19QI=y +CONFIG_USB_SERIAL_KEYSPAN_USA49W=y +CONFIG_USB_SERIAL_KEYSPAN_USA49WLC=y +CONFIG_USB_SERIAL_KLSI=m +CONFIG_USB_SERIAL_KOBIL_SCT=m +CONFIG_USB_SERIAL_MCT_U232=m +CONFIG_USB_SERIAL_PL2303=m +CONFIG_USB_SERIAL_SAFE=m +CONFIG_USB_SERIAL_SAFE_PADDED=y +CONFIG_USB_SERIAL_CYBERJACK=m +CONFIG_USB_SERIAL_XIRCOM=m +CONFIG_USB_SERIAL_OMNINET=m +CONFIG_USB_EZUSB=y + +# +# USB Miscellaneous drivers +# +CONFIG_USB_EMI62=m +# CONFIG_USB_EMI26 is not set +# CONFIG_USB_TIGL is not set +CONFIG_USB_AUERSWALD=m +CONFIG_USB_RIO500=m +CONFIG_USB_LEGOTOWER=m +CONFIG_USB_LCD=m +CONFIG_USB_LED=m +# CONFIG_USB_CYTHERM is not set +CONFIG_USB_PHIDGETSERVO=m +CONFIG_USB_TEST=m + +# +# USB ATM/DSL drivers +# +CONFIG_USB_ATM=m +CONFIG_USB_SPEEDTOUCH=m + +# +# USB Gadget Support +# +# CONFIG_USB_GADGET is not set + +# +# File systems +# +CONFIG_EXT2_FS=y +CONFIG_EXT2_FS_XATTR=y +CONFIG_EXT2_FS_POSIX_ACL=y +CONFIG_EXT2_FS_SECURITY=y +CONFIG_EXT3_FS=m +CONFIG_EXT3_FS_XATTR=y +CONFIG_EXT3_FS_POSIX_ACL=y +CONFIG_EXT3_FS_SECURITY=y +CONFIG_JBD=m +# CONFIG_JBD_DEBUG is not set +CONFIG_FS_MBCACHE=y +CONFIG_REISERFS_FS=m +# CONFIG_REISERFS_CHECK is not set +CONFIG_REISERFS_PROC_INFO=y +CONFIG_REISERFS_FS_XATTR=y +CONFIG_REISERFS_FS_POSIX_ACL=y +CONFIG_REISERFS_FS_SECURITY=y +CONFIG_JFS_FS=m +CONFIG_JFS_POSIX_ACL=y +# CONFIG_JFS_DEBUG is not set +# CONFIG_JFS_STATISTICS is not set +CONFIG_FS_POSIX_ACL=y +CONFIG_XFS_FS=m +# CONFIG_XFS_RT is not set +CONFIG_XFS_QUOTA=y +CONFIG_XFS_SECURITY=y +CONFIG_XFS_POSIX_ACL=y +CONFIG_MINIX_FS=m +CONFIG_ROMFS_FS=m +CONFIG_QUOTA=y +# CONFIG_QFMT_V1 is not set +CONFIG_QFMT_V2=y +CONFIG_QUOTACTL=y +CONFIG_AUTOFS_FS=m +CONFIG_AUTOFS4_FS=m + +# +# CD-ROM/DVD Filesystems +# +CONFIG_ISO9660_FS=y +CONFIG_JOLIET=y +CONFIG_ZISOFS=y +CONFIG_ZISOFS_FS=y +CONFIG_UDF_FS=m +CONFIG_UDF_NLS=y + +# +# DOS/FAT/NT Filesystems +# +CONFIG_FAT_FS=m +CONFIG_MSDOS_FS=m +CONFIG_VFAT_FS=m +CONFIG_FAT_DEFAULT_CODEPAGE=437 +CONFIG_FAT_DEFAULT_IOCHARSET="ascii" +# CONFIG_NTFS_FS is not set + +# +# Pseudo filesystems +# +CONFIG_PROC_FS=y +CONFIG_PROC_KCORE=y +CONFIG_SYSFS=y +# CONFIG_DEVFS_FS is not set +CONFIG_DEVPTS_FS_XATTR=y +CONFIG_DEVPTS_FS_SECURITY=y +CONFIG_TMPFS=y +CONFIG_TMPFS_XATTR=y +CONFIG_TMPFS_SECURITY=y +CONFIG_HUGETLBFS=y +CONFIG_HUGETLB_PAGE=y +CONFIG_RAMFS=y + +# +# Miscellaneous filesystems +# +# CONFIG_ADFS_FS is not set +CONFIG_AFFS_FS=m +CONFIG_HFS_FS=m +CONFIG_HFSPLUS_FS=m +CONFIG_BEFS_FS=m +# CONFIG_BEFS_DEBUG is not set +CONFIG_BFS_FS=m +CONFIG_EFS_FS=m +# CONFIG_JFFS_FS is not set +CONFIG_JFFS2_FS=m +CONFIG_JFFS2_FS_DEBUG=0 +CONFIG_JFFS2_FS_NAND=y +# CONFIG_JFFS2_COMPRESSION_OPTIONS is not set +CONFIG_JFFS2_ZLIB=y +CONFIG_JFFS2_RTIME=y +# CONFIG_JFFS2_RUBIN is not set +CONFIG_CRAMFS=m +CONFIG_VXFS_FS=m +# CONFIG_HPFS_FS is not set +CONFIG_QNX4FS_FS=m +# CONFIG_QNX4FS_RW is not set +CONFIG_SYSV_FS=m +CONFIG_UFS_FS=m +# CONFIG_UFS_FS_WRITE is not set + +# +# Network File Systems +# +CONFIG_NFS_FS=m +CONFIG_NFS_V3=y +CONFIG_NFS_V4=y +CONFIG_NFS_DIRECTIO=y +CONFIG_NFSD=m +CONFIG_NFSD_V3=y +CONFIG_NFSD_V4=y +CONFIG_NFSD_TCP=y +CONFIG_LOCKD=m +CONFIG_LOCKD_V4=y +CONFIG_EXPORTFS=m +CONFIG_SUNRPC=m +CONFIG_SUNRPC_GSS=m +CONFIG_RPCSEC_GSS_KRB5=m +CONFIG_RPCSEC_GSS_SPKM3=m +CONFIG_SMB_FS=m +# CONFIG_SMB_NLS_DEFAULT is not set +CONFIG_CIFS=m +# CONFIG_CIFS_STATS is not set +CONFIG_CIFS_XATTR=y +CONFIG_CIFS_POSIX=y +CONFIG_NCP_FS=m +CONFIG_NCPFS_PACKET_SIGNING=y +CONFIG_NCPFS_IOCTL_LOCKING=y +CONFIG_NCPFS_STRONG=y +CONFIG_NCPFS_NFS_NS=y +CONFIG_NCPFS_OS2_NS=y +CONFIG_NCPFS_SMALLDOS=y +CONFIG_NCPFS_NLS=y +CONFIG_NCPFS_EXTRAS=y +# CONFIG_CODA_FS is not set +# CONFIG_AFS_FS is not set + +# +# Partition Types +# +CONFIG_PARTITION_ADVANCED=y +# CONFIG_ACORN_PARTITION is not set +CONFIG_OSF_PARTITION=y +# CONFIG_AMIGA_PARTITION is not set +# CONFIG_ATARI_PARTITION is not set +CONFIG_MAC_PARTITION=y +CONFIG_MSDOS_PARTITION=y +CONFIG_BSD_DISKLABEL=y +CONFIG_MINIX_SUBPARTITION=y +CONFIG_SOLARIS_X86_PARTITION=y +CONFIG_UNIXWARE_DISKLABEL=y +# CONFIG_LDM_PARTITION is not set +CONFIG_SGI_PARTITION=y +# CONFIG_ULTRIX_PARTITION is not set +CONFIG_SUN_PARTITION=y +CONFIG_EFI_PARTITION=y + +# +# Native Language Support +# +CONFIG_NLS=y +CONFIG_NLS_DEFAULT="utf8" +CONFIG_NLS_CODEPAGE_437=y +CONFIG_NLS_CODEPAGE_737=m +CONFIG_NLS_CODEPAGE_775=m +CONFIG_NLS_CODEPAGE_850=m +CONFIG_NLS_CODEPAGE_852=m +CONFIG_NLS_CODEPAGE_855=m +CONFIG_NLS_CODEPAGE_857=m +CONFIG_NLS_CODEPAGE_860=m +CONFIG_NLS_CODEPAGE_861=m +CONFIG_NLS_CODEPAGE_862=m +CONFIG_NLS_CODEPAGE_863=m +CONFIG_NLS_CODEPAGE_864=m +CONFIG_NLS_CODEPAGE_865=m +CONFIG_NLS_CODEPAGE_866=m +CONFIG_NLS_CODEPAGE_869=m +CONFIG_NLS_CODEPAGE_936=m +CONFIG_NLS_CODEPAGE_950=m +CONFIG_NLS_CODEPAGE_932=m +CONFIG_NLS_CODEPAGE_949=m +CONFIG_NLS_CODEPAGE_874=m +CONFIG_NLS_ISO8859_8=m +CONFIG_NLS_CODEPAGE_1250=m +CONFIG_NLS_CODEPAGE_1251=m +CONFIG_NLS_ASCII=y +CONFIG_NLS_ISO8859_1=m +CONFIG_NLS_ISO8859_2=m +CONFIG_NLS_ISO8859_3=m +CONFIG_NLS_ISO8859_4=m +CONFIG_NLS_ISO8859_5=m +CONFIG_NLS_ISO8859_6=m +CONFIG_NLS_ISO8859_7=m +CONFIG_NLS_ISO8859_9=m +CONFIG_NLS_ISO8859_13=m +CONFIG_NLS_ISO8859_14=m +CONFIG_NLS_ISO8859_15=m +CONFIG_NLS_KOI8_R=m +CONFIG_NLS_KOI8_U=m +CONFIG_NLS_UTF8=m + +# +# Profiling support +# +CONFIG_PROFILING=y +CONFIG_OPROFILE=m + +# +# Kernel hacking +# +CONFIG_DEBUG_KERNEL=y +CONFIG_MAGIC_SYSRQ=y +# CONFIG_DEBUG_SLAB is not set +CONFIG_DEBUG_SPINLOCK=y +CONFIG_DEBUG_SPINLOCK_SLEEP=y +CONFIG_DEBUG_HIGHMEM=y +CONFIG_DEBUG_INFO=y +# CONFIG_FRAME_POINTER is not set +CONFIG_EARLY_PRINTK=y +CONFIG_DEBUG_STACKOVERFLOW=y +# CONFIG_KPROBES is not set +CONFIG_DEBUG_STACK_USAGE=y +# CONFIG_DEBUG_PAGEALLOC is not set +# CONFIG_SCHEDSTATS is not set +CONFIG_X86_FIND_SMP_CONFIG=y +CONFIG_X86_MPPARSE=y + +# +# Security options +# +CONFIG_SECURITY=y +CONFIG_SECURITY_NETWORK=y +CONFIG_SECURITY_CAPABILITIES=y +# CONFIG_SECURITY_ROOTPLUG is not set +CONFIG_SECURITY_SELINUX=y +CONFIG_SECURITY_SELINUX_BOOTPARAM=y +CONFIG_SECURITY_SELINUX_BOOTPARAM_VALUE=1 +CONFIG_SECURITY_SELINUX_DISABLE=y +CONFIG_SECURITY_SELINUX_DEVELOP=y +CONFIG_SECURITY_SELINUX_AVC_STATS=y +# CONFIG_SECURITY_SELINUX_MLS is not set + +# +# Cryptographic options +# +CONFIG_CRYPTO=y +CONFIG_CRYPTO_HMAC=y +CONFIG_CRYPTO_NULL=m +CONFIG_CRYPTO_MD4=m +CONFIG_CRYPTO_MD5=m +CONFIG_CRYPTO_SHA1=y +CONFIG_CRYPTO_SHA256=m +CONFIG_CRYPTO_SHA512=m +CONFIG_CRYPTO_WP512=m +CONFIG_CRYPTO_DES=m +CONFIG_CRYPTO_BLOWFISH=m +CONFIG_CRYPTO_TWOFISH=m +CONFIG_CRYPTO_SERPENT=m +CONFIG_CRYPTO_AES_586=m +CONFIG_CRYPTO_CAST5=m +CONFIG_CRYPTO_CAST6=m +CONFIG_CRYPTO_TEA=m +CONFIG_CRYPTO_ARC4=m +CONFIG_CRYPTO_KHAZAD=m +CONFIG_CRYPTO_DEFLATE=m +CONFIG_CRYPTO_MICHAEL_MIC=m +CONFIG_CRYPTO_CRC32C=m +# CONFIG_CRYPTO_TEST is not set +CONFIG_CRYPTO_SIGNATURE=y +CONFIG_CRYPTO_SIGNATURE_DSA=y +CONFIG_CRYPTO_MPILIB=y + +# +# Library routines +# +CONFIG_CRC_CCITT=m +CONFIG_CRC32=y +CONFIG_LIBCRC32C=m +CONFIG_ZLIB_INFLATE=y +CONFIG_ZLIB_DEFLATE=m +CONFIG_X86_SMP=y +CONFIG_X86_HT=y +CONFIG_X86_BIOS_REBOOT=y +CONFIG_X86_TRAMPOLINE=y +CONFIG_PC=y diff --git a/configs/kernel-2.6.9-i686.config b/configs/kernel-2.6.9-i686.config new file mode 100644 index 000000000..5df4b4b69 --- /dev/null +++ b/configs/kernel-2.6.9-i686.config @@ -0,0 +1,2590 @@ +# +# Automatically generated make config: don't edit +# Linux kernel version: 2.6.9-ac11 +# Tue Jan 4 10:01:15 2005 +# +CONFIG_X86=y +CONFIG_MMU=y +CONFIG_UID16=y +CONFIG_GENERIC_ISA_DMA=y +CONFIG_GENERIC_IOMAP=y + +# +# Code maturity level options +# +CONFIG_EXPERIMENTAL=y +CONFIG_CLEAN_COMPILE=y +CONFIG_BROKEN_ON_SMP=y + +# +# General setup +# +CONFIG_LOCALVERSION="" +CONFIG_SWAP=y +CONFIG_SYSVIPC=y +CONFIG_POSIX_MQUEUE=y +CONFIG_BSD_PROCESS_ACCT=y +# CONFIG_BSD_PROCESS_ACCT_V3 is not set +CONFIG_SYSCTL=y +CONFIG_AUDIT=y +CONFIG_AUDITSYSCALL=y +CONFIG_LOG_BUF_SHIFT=17 +CONFIG_HOTPLUG=y +# CONFIG_IKCONFIG is not set +# CONFIG_EMBEDDED is not set +CONFIG_KALLSYMS=y +# CONFIG_KALLSYMS_ALL is not set +CONFIG_KALLSYMS_EXTRA_PASS=y +CONFIG_FUTEX=y +CONFIG_EPOLL=y +CONFIG_IOSCHED_NOOP=y +CONFIG_IOSCHED_AS=y +CONFIG_IOSCHED_DEADLINE=y +CONFIG_IOSCHED_CFQ=y +CONFIG_CC_OPTIMIZE_FOR_SIZE=y +CONFIG_SHMEM=y +# CONFIG_TINY_SHMEM is not set + +# +# Loadable module support +# +CONFIG_MODULES=y +CONFIG_MODULE_UNLOAD=y +# CONFIG_MODULE_FORCE_UNLOAD is not set +CONFIG_OBSOLETE_MODPARM=y +CONFIG_MODVERSIONS=y +CONFIG_MODULE_SIG=y +# CONFIG_MODULE_SIG_FORCE is not set +CONFIG_KMOD=y + +# +# Processor type and features +# +CONFIG_X86_PC=y +# CONFIG_X86_ELAN is not set +# CONFIG_X86_VOYAGER is not set +# CONFIG_X86_NUMAQ is not set +# CONFIG_X86_SUMMIT is not set +# CONFIG_X86_BIGSMP is not set +# CONFIG_X86_VISWS is not set +# CONFIG_X86_GENERICARCH is not set +# CONFIG_X86_ES7000 is not set +# CONFIG_M386 is not set +# CONFIG_M486 is not set +# CONFIG_M586 is not set +# CONFIG_M586TSC is not set +# CONFIG_M586MMX is not set +CONFIG_M686=y +# CONFIG_MPENTIUMII is not set +# CONFIG_MPENTIUMIII is not set +# CONFIG_MPENTIUMM is not set +# CONFIG_MPENTIUM4 is not set +# CONFIG_MK6 is not set +# CONFIG_MK7 is not set +# CONFIG_MK8 is not set +# CONFIG_MCRUSOE is not set +# CONFIG_MWINCHIPC6 is not set +# CONFIG_MWINCHIP2 is not set +# CONFIG_MWINCHIP3D is not set +# CONFIG_MCYRIXIII is not set +# CONFIG_MVIAC3_2 is not set +CONFIG_X86_GENERIC=y +CONFIG_X86_CMPXCHG=y +CONFIG_X86_XADD=y +CONFIG_X86_L1_CACHE_SHIFT=7 +CONFIG_RWSEM_XCHGADD_ALGORITHM=y +CONFIG_X86_PPRO_FENCE=y +CONFIG_X86_WP_WORKS_OK=y +CONFIG_X86_INVLPG=y +CONFIG_X86_BSWAP=y +CONFIG_X86_POPAD_OK=y +CONFIG_X86_GOOD_APIC=y +CONFIG_X86_INTEL_USERCOPY=y +CONFIG_X86_USE_PPRO_CHECKSUM=y +CONFIG_HPET_TIMER=y +CONFIG_HPET_EMULATE_RTC=y +# CONFIG_SMP is not set +# CONFIG_PREEMPT is not set +CONFIG_PREEMPT_VOLUNTARY=y +# CONFIG_X86_UP_APIC is not set +CONFIG_X86_TSC=y +CONFIG_X86_MCE=y +# CONFIG_X86_MCE_NONFATAL is not set +CONFIG_TOSHIBA=m +CONFIG_I8K=m +CONFIG_MICROCODE=m +CONFIG_X86_MSR=m +CONFIG_X86_CPUID=m + +# +# Firmware Drivers +# +CONFIG_EDD=m +# CONFIG_NOHIGHMEM is not set +CONFIG_HIGHMEM4G=y +# CONFIG_HIGHMEM64G is not set +CONFIG_HIGHMEM=y +CONFIG_HIGHPTE=y +# CONFIG_MATH_EMULATION is not set +CONFIG_MTRR=y +# CONFIG_EFI is not set +CONFIG_REGPARM=y +CONFIG_KEXEC=y + +# +# Power management options (ACPI, APM) +# +CONFIG_PM=y +# CONFIG_PM_DEBUG is not set +# CONFIG_SOFTWARE_SUSPEND is not set + +# +# ACPI (Advanced Configuration and Power Interface) Support +# +CONFIG_ACPI=y +CONFIG_ACPI_BOOT=y +CONFIG_ACPI_INTERPRETER=y +CONFIG_ACPI_SLEEP=y +CONFIG_ACPI_SLEEP_PROC_FS=y +CONFIG_ACPI_AC=m +CONFIG_ACPI_BATTERY=m +CONFIG_ACPI_BUTTON=m +CONFIG_ACPI_FAN=y +CONFIG_ACPI_PROCESSOR=y +CONFIG_ACPI_THERMAL=y +CONFIG_ACPI_ASUS=m +CONFIG_ACPI_TOSHIBA=m +CONFIG_ACPI_BLACKLIST_YEAR=2001 +# CONFIG_ACPI_DEBUG is not set +CONFIG_ACPI_BUS=y +CONFIG_ACPI_EC=y +CONFIG_ACPI_POWER=y +CONFIG_ACPI_PCI=y +CONFIG_ACPI_SYSTEM=y +CONFIG_X86_PM_TIMER=y + +# +# APM (Advanced Power Management) BIOS Support +# +CONFIG_APM=y +# CONFIG_APM_IGNORE_USER_SUSPEND is not set +# CONFIG_APM_DO_ENABLE is not set +CONFIG_APM_CPU_IDLE=y +# CONFIG_APM_DISPLAY_BLANK is not set +CONFIG_APM_RTC_IS_GMT=y +# CONFIG_APM_ALLOW_INTS is not set +# CONFIG_APM_REAL_MODE_POWER_OFF is not set + +# +# CPU Frequency scaling +# +CONFIG_CPU_FREQ=y +# CONFIG_CPU_FREQ_PROC_INTF is not set +# CONFIG_CPU_FREQ_DEFAULT_GOV_PERFORMANCE is not set +CONFIG_CPU_FREQ_DEFAULT_GOV_USERSPACE=y +CONFIG_CPU_FREQ_GOV_PERFORMANCE=y +CONFIG_CPU_FREQ_GOV_POWERSAVE=m +CONFIG_CPU_FREQ_GOV_USERSPACE=y +CONFIG_CPU_FREQ_GOV_ONDEMAND=m +# CONFIG_CPU_FREQ_24_API is not set +CONFIG_CPU_FREQ_TABLE=y + +# +# CPUFreq processor drivers +# +CONFIG_X86_ACPI_CPUFREQ=m +# CONFIG_X86_ACPI_CPUFREQ_PROC_INTF is not set +CONFIG_X86_POWERNOW_K6=m +CONFIG_X86_POWERNOW_K7=y +CONFIG_X86_POWERNOW_K7_ACPI=y +CONFIG_X86_POWERNOW_K8=m +CONFIG_X86_POWERNOW_K8_ACPI=y +# CONFIG_X86_GX_SUSPMOD is not set +CONFIG_X86_SPEEDSTEP_CENTRINO=y +CONFIG_X86_SPEEDSTEP_CENTRINO_TABLE=y +CONFIG_X86_SPEEDSTEP_CENTRINO_ACPI=y +CONFIG_X86_SPEEDSTEP_ICH=y +CONFIG_X86_SPEEDSTEP_SMI=m +CONFIG_X86_P4_CLOCKMOD=m +CONFIG_X86_SPEEDSTEP_LIB=y +# CONFIG_X86_SPEEDSTEP_RELAXED_CAP_CHECK is not set +CONFIG_X86_LONGRUN=y +CONFIG_X86_LONGHAUL=y + +# +# Bus options (PCI, PCMCIA, EISA, MCA, ISA) +# +CONFIG_PCI=y +# CONFIG_PCI_GOBIOS is not set +# CONFIG_PCI_GOMMCONFIG is not set +# CONFIG_PCI_GODIRECT is not set +CONFIG_PCI_GOANY=y +CONFIG_PCI_BIOS=y +CONFIG_PCI_DIRECT=y +CONFIG_PCI_MMCONFIG=y +CONFIG_PCI_LEGACY_PROC=y +# CONFIG_PCI_NAMES is not set +CONFIG_ISA=y +# CONFIG_EISA is not set +# CONFIG_MCA is not set +# CONFIG_SCx200 is not set + +# +# PCMCIA/CardBus support +# +CONFIG_PCMCIA=m +# CONFIG_PCMCIA_DEBUG is not set +CONFIG_YENTA=m +CONFIG_CARDBUS=y +CONFIG_PD6729=m +CONFIG_I82092=m +CONFIG_I82365=m +CONFIG_TCIC=m +CONFIG_PCMCIA_PROBE=y + +# +# PCI Hotplug Support +# +CONFIG_HOTPLUG_PCI=y +# CONFIG_HOTPLUG_PCI_FAKE is not set +CONFIG_HOTPLUG_PCI_COMPAQ=m +# CONFIG_HOTPLUG_PCI_COMPAQ_NVRAM is not set +CONFIG_HOTPLUG_PCI_ACPI=m +CONFIG_HOTPLUG_PCI_ACPI_IBM=m +# CONFIG_HOTPLUG_PCI_CPCI is not set +CONFIG_HOTPLUG_PCI_PCIE=m +# CONFIG_HOTPLUG_PCI_PCIE_POLL_EVENT_MODE is not set +CONFIG_HOTPLUG_PCI_SHPC=m +# CONFIG_HOTPLUG_PCI_SHPC_POLL_EVENT_MODE is not set + +# +# Executable file formats +# +CONFIG_BINFMT_ELF=y +# CONFIG_BINFMT_AOUT is not set +CONFIG_BINFMT_MISC=y + +# +# Device Drivers +# + +# +# Generic Driver Options +# +CONFIG_STANDALONE=y +CONFIG_PREVENT_FIRMWARE_BUILD=y +CONFIG_FW_LOADER=y +# CONFIG_DEBUG_DRIVER is not set + +# +# Memory Technology Devices (MTD) +# +CONFIG_MTD=m +# CONFIG_MTD_DEBUG is not set +CONFIG_MTD_PARTITIONS=y +CONFIG_MTD_CONCAT=m +CONFIG_MTD_REDBOOT_PARTS=m +# CONFIG_MTD_REDBOOT_PARTS_UNALLOCATED is not set +# CONFIG_MTD_REDBOOT_PARTS_READONLY is not set +CONFIG_MTD_CMDLINE_PARTS=y + +# +# User Modules And Translation Layers +# +CONFIG_MTD_CHAR=m +CONFIG_MTD_BLOCK=m +CONFIG_MTD_BLOCK_RO=m +CONFIG_FTL=m +CONFIG_NFTL=m +CONFIG_NFTL_RW=y +CONFIG_INFTL=m + +# +# RAM/ROM/Flash chip drivers +# +CONFIG_MTD_CFI=m +CONFIG_MTD_JEDECPROBE=m +CONFIG_MTD_GEN_PROBE=m +# 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=m +CONFIG_MTD_CFI_AMDSTD=m +CONFIG_MTD_CFI_AMDSTD_RETRY=3 +CONFIG_MTD_CFI_STAA=m +CONFIG_MTD_CFI_UTIL=m +CONFIG_MTD_RAM=m +CONFIG_MTD_ROM=m +CONFIG_MTD_ABSENT=m + +# +# Mapping drivers for chip access +# +CONFIG_MTD_COMPLEX_MAPPINGS=y +# CONFIG_MTD_PHYSMAP is not set +# CONFIG_MTD_PNC2000 is not set +CONFIG_MTD_SC520CDP=m +CONFIG_MTD_NETSC520=m +CONFIG_MTD_SBC_GXX=m +CONFIG_MTD_ELAN_104NC=m +CONFIG_MTD_SCx200_DOCFLASH=m +# CONFIG_MTD_AMD76XROM is not set +# CONFIG_MTD_ICHXROM is not set +CONFIG_MTD_SCB2_FLASH=m +# CONFIG_MTD_NETtel is not set +# CONFIG_MTD_DILNETPC is not set +# CONFIG_MTD_L440GX is not set +CONFIG_MTD_PCI=m + +# +# Self-contained MTD device drivers +# +CONFIG_MTD_PMC551=m +# CONFIG_MTD_PMC551_BUGFIX is not set +# CONFIG_MTD_PMC551_DEBUG is not set +# CONFIG_MTD_SLRAM is not set +# CONFIG_MTD_PHRAM is not set +CONFIG_MTD_MTDRAM=m +CONFIG_MTDRAM_TOTAL_SIZE=4096 +CONFIG_MTDRAM_ERASE_SIZE=128 +# CONFIG_MTD_BLKMTD is not set + +# +# Disk-On-Chip Device Drivers +# +CONFIG_MTD_DOC2000=m +# CONFIG_MTD_DOC2001 is not set +CONFIG_MTD_DOC2001PLUS=m +CONFIG_MTD_DOCPROBE=m +CONFIG_MTD_DOCECC=m +# CONFIG_MTD_DOCPROBE_ADVANCED is not set +CONFIG_MTD_DOCPROBE_ADDRESS=0 + +# +# NAND Flash Device Drivers +# +CONFIG_MTD_NAND=m +# CONFIG_MTD_NAND_VERIFY_WRITE is not set +CONFIG_MTD_NAND_IDS=m +# CONFIG_MTD_NAND_DISKONCHIP is not set + +# +# Parallel port support +# +CONFIG_PARPORT=m +CONFIG_PARPORT_PC=m +CONFIG_PARPORT_PC_CML1=m +CONFIG_PARPORT_SERIAL=m +# CONFIG_PARPORT_PC_FIFO is not set +# CONFIG_PARPORT_PC_SUPERIO is not set +CONFIG_PARPORT_PC_PCMCIA=m +# CONFIG_PARPORT_OTHER is not set +CONFIG_PARPORT_1284=y + +# +# Plug and Play support +# +CONFIG_PNP=y +# CONFIG_PNP_DEBUG is not set + +# +# Protocols +# +CONFIG_ISAPNP=y +# CONFIG_PNPBIOS is not set + +# +# Block devices +# +CONFIG_BLK_DEV_FD=m +# CONFIG_BLK_DEV_XD is not set +# CONFIG_PARIDE is not set +CONFIG_BLK_CPQ_DA=m +CONFIG_BLK_CPQ_CISS_DA=m +CONFIG_CISS_SCSI_TAPE=y +CONFIG_BLK_DEV_DAC960=m +CONFIG_BLK_DEV_UMEM=m +CONFIG_BLK_DEV_LOOP=m +CONFIG_BLK_DEV_CRYPTOLOOP=m +CONFIG_BLK_DEV_NBD=m +CONFIG_BLK_DEV_SX8=m +# CONFIG_BLK_DEV_UB is not set +CONFIG_BLK_DEV_RAM=y +CONFIG_BLK_DEV_RAM_SIZE=16384 +CONFIG_BLK_DEV_INITRD=y +CONFIG_LBD=y + +# +# ATA/ATAPI/MFM/RLL support +# +CONFIG_IDE=y +CONFIG_BLK_DEV_IDE=y + +# +# Please see Documentation/ide.txt for help/info on IDE drives +# +# CONFIG_BLK_DEV_IDE_SATA is not set +# CONFIG_BLK_DEV_HD_IDE is not set +CONFIG_BLK_DEV_IDEDISK=y +CONFIG_IDEDISK_MULTI_MODE=y +CONFIG_BLK_DEV_IDECS=m +CONFIG_BLK_DEV_DELKIN=m +CONFIG_BLK_DEV_IDECD=y +# CONFIG_BLK_DEV_IDETAPE is not set +CONFIG_BLK_DEV_IDEFLOPPY=y +CONFIG_BLK_DEV_IDESCSI=m +# CONFIG_IDE_TASK_IOCTL is not set +# CONFIG_IDE_TASKFILE_IO is not set + +# +# IDE chipset support/bugfixes +# +CONFIG_IDE_GENERIC=y +# CONFIG_BLK_DEV_CMD640 is not set +CONFIG_BLK_DEV_IDEPNP=y +CONFIG_BLK_DEV_IDEPCI=y +CONFIG_IDEPCI_SHARE_IRQ=y +# CONFIG_BLK_DEV_OFFBOARD is not set +CONFIG_BLK_DEV_GENERIC=y +# CONFIG_BLK_DEV_OPTI621 is not set +CONFIG_BLK_DEV_RZ1000=y +CONFIG_BLK_DEV_IDEDMA_PCI=y +# CONFIG_BLK_DEV_IDEDMA_FORCED is not set +CONFIG_IDEDMA_PCI_AUTO=y +# CONFIG_IDEDMA_ONLYDISK is not set +CONFIG_BLK_DEV_AEC62XX=y +CONFIG_BLK_DEV_ALI15X3=y +# CONFIG_WDC_ALI15X3 is not set +CONFIG_BLK_DEV_AMD74XX=y +CONFIG_BLK_DEV_ATIIXP=y +CONFIG_BLK_DEV_CMD64X=y +CONFIG_BLK_DEV_TRIFLEX=y +CONFIG_BLK_DEV_CY82C693=y +CONFIG_BLK_DEV_CS5520=y +CONFIG_BLK_DEV_CS5530=y +CONFIG_BLK_DEV_HPT34X=y +# CONFIG_HPT34X_AUTODMA is not set +CONFIG_BLK_DEV_HPT366=y +# CONFIG_BLK_DEV_SC1200 is not set +CONFIG_BLK_DEV_PIIX=y +CONFIG_BLK_DEV_IT8212=y +# CONFIG_BLK_DEV_NS87415 is not set +CONFIG_BLK_DEV_PDC202XX_OLD=y +# CONFIG_PDC202XX_BURST is not set +CONFIG_BLK_DEV_PDC202XX_NEW=y +CONFIG_PDC202XX_FORCE=y +CONFIG_BLK_DEV_SVWKS=y +CONFIG_BLK_DEV_SIIMAGE=y +CONFIG_BLK_DEV_SIS5513=y +CONFIG_BLK_DEV_SLC90E66=y +# CONFIG_BLK_DEV_TRM290 is not set +CONFIG_BLK_DEV_VIA82CXXX=y +# CONFIG_IDE_ARM is not set +# CONFIG_IDE_CHIPSETS is not set +CONFIG_BLK_DEV_IDEDMA=y +# CONFIG_IDEDMA_IVB is not set +CONFIG_IDEDMA_AUTO=y +# CONFIG_BLK_DEV_HD is not set + +# +# SCSI device support +# +CONFIG_SCSI=m +CONFIG_SCSI_PROC_FS=y + +# +# SCSI support type (disk, tape, CD-ROM) +# +CONFIG_BLK_DEV_SD=m +CONFIG_CHR_DEV_ST=m +CONFIG_CHR_DEV_OSST=m +CONFIG_BLK_DEV_SR=m +CONFIG_BLK_DEV_SR_VENDOR=y +CONFIG_CHR_DEV_SG=m + +# +# Some SCSI devices (e.g. CD jukebox) support multiple LUNs +# +# CONFIG_SCSI_MULTI_LUN is not set +CONFIG_SCSI_CONSTANTS=y +CONFIG_SCSI_LOGGING=y + +# +# SCSI Transport Attributes +# +CONFIG_SCSI_SPI_ATTRS=m +CONFIG_SCSI_FC_ATTRS=m + +# +# SCSI low-level drivers +# +CONFIG_BLK_DEV_3W_XXXX_RAID=m +CONFIG_SCSI_3W_9XXX=m +# CONFIG_SCSI_7000FASST is not set +CONFIG_SCSI_ACARD=m +CONFIG_SCSI_AHA152X=m +CONFIG_SCSI_AHA1542=m +CONFIG_SCSI_AACRAID=m +CONFIG_SCSI_AIC7XXX=m +CONFIG_AIC7XXX_CMDS_PER_DEVICE=4 +CONFIG_AIC7XXX_RESET_DELAY_MS=15000 +# CONFIG_AIC7XXX_DEBUG_ENABLE is not set +CONFIG_AIC7XXX_DEBUG_MASK=0 +# CONFIG_AIC7XXX_REG_PRETTY_PRINT is not set +CONFIG_SCSI_AIC7XXX_OLD=m +CONFIG_SCSI_AIC79XX=m +CONFIG_AIC79XX_CMDS_PER_DEVICE=4 +CONFIG_AIC79XX_RESET_DELAY_MS=15000 +# CONFIG_AIC79XX_ENABLE_RD_STRM is not set +# CONFIG_AIC79XX_DEBUG_ENABLE is not set +CONFIG_AIC79XX_DEBUG_MASK=0 +# CONFIG_AIC79XX_REG_PRETTY_PRINT is not set +# CONFIG_SCSI_DPT_I2O is not set +CONFIG_SCSI_IN2000=m +CONFIG_MEGARAID_NEWGEN=y +CONFIG_MEGARAID_MM=m +CONFIG_MEGARAID_MAILBOX=m +CONFIG_MEGARAID_LEGACY=m +CONFIG_SCSI_SATA=y +CONFIG_SCSI_SATA_AHCI=m +CONFIG_SCSI_SATA_SVW=m +CONFIG_SCSI_ATA_PIIX=m +CONFIG_SCSI_SATA_NV=m +CONFIG_SCSI_SATA_PROMISE=m +CONFIG_SCSI_SATA_SX4=m +CONFIG_SCSI_SATA_SIL=m +CONFIG_SCSI_SATA_SIS=m +CONFIG_SCSI_SATA_VIA=m +CONFIG_SCSI_SATA_VITESSE=m +CONFIG_SCSI_BUSLOGIC=m +# CONFIG_SCSI_OMIT_FLASHPOINT is not set +# CONFIG_SCSI_DMX3191D is not set +# CONFIG_SCSI_DTC3280 is not set +# CONFIG_SCSI_EATA is not set +# CONFIG_SCSI_EATA_PIO is not set +CONFIG_SCSI_LPFC=m +CONFIG_SCSI_FUTURE_DOMAIN=m +CONFIG_SCSI_GDTH=m +# CONFIG_SCSI_GENERIC_NCR5380 is not set +# CONFIG_SCSI_GENERIC_NCR5380_MMIO is not set +CONFIG_SCSI_IPS=m +CONFIG_SCSI_INITIO=m +CONFIG_SCSI_INIA100=m +CONFIG_SCSI_PPA=m +CONFIG_SCSI_IMM=m +# CONFIG_SCSI_IZIP_EPP16 is not set +# CONFIG_SCSI_IZIP_SLOW_CTR is not set +# CONFIG_SCSI_NCR53C406A is not set +CONFIG_SCSI_SYM53C8XX_2=m +CONFIG_SCSI_SYM53C8XX_DMA_ADDRESSING_MODE=1 +CONFIG_SCSI_SYM53C8XX_DEFAULT_TAGS=16 +CONFIG_SCSI_SYM53C8XX_MAX_TAGS=64 +# CONFIG_SCSI_SYM53C8XX_IOMAPPED is not set +# CONFIG_SCSI_IPR is not set +# CONFIG_SCSI_PAS16 is not set +# CONFIG_SCSI_PSI240I is not set +CONFIG_SCSI_QLOGIC_FAS=m +CONFIG_SCSI_QLOGIC_ISP=m +# CONFIG_SCSI_QLOGIC_FC is not set +CONFIG_SCSI_QLOGIC_1280=m +CONFIG_SCSI_QLA2XXX=m +CONFIG_SCSI_QLA21XX=m +CONFIG_SCSI_QLA22XX=m +CONFIG_SCSI_QLA2300=m +CONFIG_SCSI_QLA2322=m +CONFIG_SCSI_QLA6312=m +CONFIG_SCSI_QLA6322=m +# CONFIG_SCSI_SYM53C416 is not set +# CONFIG_SCSI_DC395x is not set +CONFIG_SCSI_DC390T=m +# CONFIG_SCSI_T128 is not set +# CONFIG_SCSI_U14_34F is not set +# CONFIG_SCSI_ULTRASTOR is not set +# CONFIG_SCSI_NSP32 is not set +# CONFIG_SCSI_DEBUG is not set + +# +# PCMCIA SCSI adapter support +# +CONFIG_PCMCIA_AHA152X=m +CONFIG_PCMCIA_FDOMAIN=m +CONFIG_PCMCIA_NINJA_SCSI=m +CONFIG_PCMCIA_QLOGIC=m +CONFIG_PCMCIA_SYM53C500=m + +# +# Old CD-ROM drivers (not SCSI, not IDE) +# +# CONFIG_CD_NO_IDESCSI is not set + +# +# Multi-device support (RAID and LVM) +# +CONFIG_MD=y +CONFIG_BLK_DEV_MD=y +CONFIG_MD_LINEAR=m +CONFIG_MD_RAID0=m +CONFIG_MD_RAID1=m +CONFIG_MD_RAID10=m +CONFIG_MD_RAID5=m +CONFIG_MD_RAID6=m +CONFIG_MD_MULTIPATH=m +CONFIG_BLK_DEV_DM=m +CONFIG_DM_CRYPT=m +CONFIG_DM_SNAPSHOT=m +CONFIG_DM_MIRROR=m +CONFIG_DM_ZERO=m + +# +# Fusion MPT device support +# +CONFIG_FUSION=m +CONFIG_FUSION_MAX_SGE=40 +CONFIG_FUSION_CTL=m +CONFIG_FUSION_LAN=m + +# +# IEEE 1394 (FireWire) support +# +CONFIG_IEEE1394=m + +# +# Subsystem Options +# +# CONFIG_IEEE1394_VERBOSEDEBUG is not set +CONFIG_IEEE1394_OUI_DB=y +CONFIG_IEEE1394_EXTRA_CONFIG_ROMS=y +CONFIG_IEEE1394_CONFIG_ROM_IP1394=y + +# +# Device Drivers +# +CONFIG_IEEE1394_PCILYNX=m +CONFIG_IEEE1394_OHCI1394=m + +# +# Protocol Drivers +# +CONFIG_IEEE1394_VIDEO1394=m +CONFIG_IEEE1394_SBP2=m +# CONFIG_IEEE1394_SBP2_PHYS_DMA is not set +CONFIG_IEEE1394_ETH1394=m +CONFIG_IEEE1394_DV1394=m +CONFIG_IEEE1394_RAWIO=m +CONFIG_IEEE1394_CMP=m +CONFIG_IEEE1394_AMDTP=m + +# +# I2O device support +# +CONFIG_I2O=m +CONFIG_I2O_CONFIG=m +CONFIG_I2O_BLOCK=m +CONFIG_I2O_SCSI=m +CONFIG_I2O_PROC=m + +# +# Networking support +# +CONFIG_NET=y + +# +# Networking options +# +CONFIG_PACKET=y +CONFIG_PACKET_MMAP=y +CONFIG_NETLINK_DEV=y +CONFIG_UNIX=y +CONFIG_NET_KEY=m +CONFIG_INET=y +CONFIG_IP_MULTICAST=y +CONFIG_IP_ADVANCED_ROUTER=y +CONFIG_IP_MULTIPLE_TABLES=y +CONFIG_IP_ROUTE_FWMARK=y +CONFIG_IP_ROUTE_MULTIPATH=y +CONFIG_IP_ROUTE_VERBOSE=y +# CONFIG_IP_PNP is not set +CONFIG_NET_IPIP=m +CONFIG_NET_IPGRE=m +CONFIG_NET_IPGRE_BROADCAST=y +CONFIG_IP_MROUTE=y +CONFIG_IP_PIMSM_V1=y +CONFIG_IP_PIMSM_V2=y +# CONFIG_ARPD is not set +CONFIG_SYN_COOKIES=y +CONFIG_INET_AH=m +CONFIG_INET_ESP=m +CONFIG_INET_IPCOMP=m +CONFIG_INET_TUNNEL=m + +# +# IP: Virtual Server Configuration +# +CONFIG_IP_VS=m +# CONFIG_IP_VS_DEBUG is not set +CONFIG_IP_VS_TAB_BITS=12 + +# +# IPVS transport protocol load balancing support +# +CONFIG_IP_VS_PROTO_TCP=y +CONFIG_IP_VS_PROTO_UDP=y +CONFIG_IP_VS_PROTO_ESP=y +CONFIG_IP_VS_PROTO_AH=y + +# +# IPVS scheduler +# +CONFIG_IP_VS_RR=m +CONFIG_IP_VS_WRR=m +CONFIG_IP_VS_LC=m +CONFIG_IP_VS_WLC=m +CONFIG_IP_VS_LBLC=m +CONFIG_IP_VS_LBLCR=m +CONFIG_IP_VS_DH=m +CONFIG_IP_VS_SH=m +CONFIG_IP_VS_SED=m +CONFIG_IP_VS_NQ=m + +# +# IPVS application helper +# +CONFIG_IP_VS_FTP=m +CONFIG_IPV6=m +CONFIG_IPV6_PRIVACY=y +CONFIG_INET6_AH=m +CONFIG_INET6_ESP=m +CONFIG_INET6_IPCOMP=m +CONFIG_INET6_TUNNEL=m +CONFIG_IPV6_TUNNEL=m +CONFIG_NETFILTER=y +# CONFIG_NETFILTER_DEBUG is not set +CONFIG_BRIDGE_NETFILTER=y + +# +# IP: Netfilter Configuration +# +CONFIG_IP_NF_CONNTRACK=m +CONFIG_IP_NF_CT_ACCT=y +CONFIG_IP_NF_CT_PROTO_SCTP=m +CONFIG_IP_NF_FTP=m +CONFIG_IP_NF_IRC=m +CONFIG_IP_NF_TFTP=m +CONFIG_IP_NF_AMANDA=m +CONFIG_IP_NF_QUEUE=m +CONFIG_IP_NF_IPTABLES=m +CONFIG_IP_NF_MATCH_LIMIT=m +CONFIG_IP_NF_MATCH_IPRANGE=m +CONFIG_IP_NF_MATCH_MAC=m +CONFIG_IP_NF_MATCH_PKTTYPE=m +CONFIG_IP_NF_MATCH_MARK=m +CONFIG_IP_NF_MATCH_MULTIPORT=m +CONFIG_IP_NF_MATCH_TOS=m +CONFIG_IP_NF_MATCH_RECENT=m +CONFIG_IP_NF_MATCH_ECN=m +CONFIG_IP_NF_MATCH_DSCP=m +CONFIG_IP_NF_MATCH_AH_ESP=m +CONFIG_IP_NF_MATCH_LENGTH=m +CONFIG_IP_NF_MATCH_TTL=m +CONFIG_IP_NF_MATCH_TCPMSS=m +CONFIG_IP_NF_MATCH_HELPER=m +CONFIG_IP_NF_MATCH_STATE=m +CONFIG_IP_NF_MATCH_CONNTRACK=m +CONFIG_IP_NF_MATCH_OWNER=m +CONFIG_IP_NF_MATCH_PHYSDEV=m +CONFIG_IP_NF_MATCH_ADDRTYPE=m +CONFIG_IP_NF_MATCH_REALM=m +CONFIG_IP_NF_MATCH_SCTP=m +CONFIG_IP_NF_MATCH_COMMENT=m +CONFIG_IP_NF_FILTER=m +CONFIG_IP_NF_TARGET_REJECT=m +CONFIG_IP_NF_TARGET_LOG=m +CONFIG_IP_NF_TARGET_ULOG=m +CONFIG_IP_NF_TARGET_TCPMSS=m +CONFIG_IP_NF_NAT=m +CONFIG_IP_NF_NAT_NEEDED=y +CONFIG_IP_NF_TARGET_MASQUERADE=m +CONFIG_IP_NF_TARGET_REDIRECT=m +CONFIG_IP_NF_TARGET_NETMAP=m +CONFIG_IP_NF_TARGET_SAME=m +CONFIG_IP_NF_NAT_LOCAL=y +CONFIG_IP_NF_NAT_SNMP_BASIC=m +CONFIG_IP_NF_NAT_IRC=m +CONFIG_IP_NF_NAT_FTP=m +CONFIG_IP_NF_NAT_TFTP=m +CONFIG_IP_NF_NAT_AMANDA=m +CONFIG_IP_NF_MANGLE=m +CONFIG_IP_NF_TARGET_TOS=m +CONFIG_IP_NF_TARGET_ECN=m +CONFIG_IP_NF_TARGET_DSCP=m +CONFIG_IP_NF_TARGET_MARK=m +CONFIG_IP_NF_TARGET_CLASSIFY=m +CONFIG_IP_NF_RAW=m +CONFIG_IP_NF_TARGET_NOTRACK=m +CONFIG_IP_NF_ARPTABLES=m +CONFIG_IP_NF_ARPFILTER=m +CONFIG_IP_NF_ARP_MANGLE=m +# CONFIG_IP_NF_COMPAT_IPCHAINS is not set +# CONFIG_IP_NF_COMPAT_IPFWADM is not set + +# +# IPv6: Netfilter Configuration +# +# CONFIG_IP6_NF_QUEUE is not set +CONFIG_IP6_NF_IPTABLES=m +CONFIG_IP6_NF_MATCH_LIMIT=m +CONFIG_IP6_NF_MATCH_MAC=m +CONFIG_IP6_NF_MATCH_RT=m +CONFIG_IP6_NF_MATCH_OPTS=m +CONFIG_IP6_NF_MATCH_FRAG=m +CONFIG_IP6_NF_MATCH_HL=m +CONFIG_IP6_NF_MATCH_MULTIPORT=m +CONFIG_IP6_NF_MATCH_OWNER=m +CONFIG_IP6_NF_MATCH_MARK=m +CONFIG_IP6_NF_MATCH_IPV6HEADER=m +CONFIG_IP6_NF_MATCH_AHESP=m +CONFIG_IP6_NF_MATCH_LENGTH=m +CONFIG_IP6_NF_MATCH_EUI64=m +CONFIG_IP6_NF_MATCH_PHYSDEV=m +CONFIG_IP6_NF_FILTER=m +CONFIG_IP6_NF_TARGET_LOG=m +CONFIG_IP6_NF_MANGLE=m +CONFIG_IP6_NF_TARGET_MARK=m +CONFIG_IP6_NF_RAW=m + +# +# Bridge: Netfilter Configuration +# +CONFIG_BRIDGE_NF_EBTABLES=m +CONFIG_BRIDGE_EBT_BROUTE=m +CONFIG_BRIDGE_EBT_T_FILTER=m +CONFIG_BRIDGE_EBT_T_NAT=m +CONFIG_BRIDGE_EBT_802_3=m +CONFIG_BRIDGE_EBT_AMONG=m +CONFIG_BRIDGE_EBT_ARP=m +CONFIG_BRIDGE_EBT_IP=m +CONFIG_BRIDGE_EBT_LIMIT=m +CONFIG_BRIDGE_EBT_MARK=m +CONFIG_BRIDGE_EBT_PKTTYPE=m +CONFIG_BRIDGE_EBT_STP=m +CONFIG_BRIDGE_EBT_VLAN=m +CONFIG_BRIDGE_EBT_ARPREPLY=m +CONFIG_BRIDGE_EBT_DNAT=m +CONFIG_BRIDGE_EBT_MARK_T=m +CONFIG_BRIDGE_EBT_REDIRECT=m +CONFIG_BRIDGE_EBT_SNAT=m +CONFIG_BRIDGE_EBT_LOG=m +CONFIG_XFRM=y +CONFIG_XFRM_USER=y + +# +# SCTP Configuration (EXPERIMENTAL) +# +CONFIG_IP_SCTP=m +# CONFIG_SCTP_DBG_MSG is not set +# CONFIG_SCTP_DBG_OBJCNT is not set +# CONFIG_SCTP_HMAC_NONE is not set +# CONFIG_SCTP_HMAC_SHA1 is not set +CONFIG_SCTP_HMAC_MD5=y +CONFIG_ATM=m +CONFIG_ATM_CLIP=m +# CONFIG_ATM_CLIP_NO_ICMP is not set +CONFIG_ATM_LANE=m +# CONFIG_ATM_MPOA is not set +CONFIG_ATM_BR2684=m +# CONFIG_ATM_BR2684_IPFILTER is not set +CONFIG_BRIDGE=m +CONFIG_VLAN_8021Q=m +# CONFIG_DECNET is not set +CONFIG_LLC=y +# CONFIG_LLC2 is not set +CONFIG_IPX=m +# CONFIG_IPX_INTERN is not set +CONFIG_ATALK=m +CONFIG_DEV_APPLETALK=y +CONFIG_LTPC=m +CONFIG_COPS=m +CONFIG_COPS_DAYNA=y +CONFIG_COPS_TANGENT=y +CONFIG_IPDDP=m +CONFIG_IPDDP_ENCAP=y +CONFIG_IPDDP_DECAP=y +# CONFIG_X25 is not set +# CONFIG_LAPB is not set +CONFIG_NET_DIVERT=y +# CONFIG_ECONET is not set +CONFIG_WAN_ROUTER=m + +# +# QoS and/or fair queueing +# +CONFIG_NET_SCHED=y +CONFIG_NET_SCH_CLK_JIFFIES=y +# CONFIG_NET_SCH_CLK_GETTIMEOFDAY is not set +# CONFIG_NET_SCH_CLK_CPU is not set +CONFIG_NET_SCH_CBQ=m +CONFIG_NET_SCH_HTB=m +CONFIG_NET_SCH_HFSC=m +CONFIG_NET_SCH_ATM=m +CONFIG_NET_SCH_PRIO=m +CONFIG_NET_SCH_RED=m +CONFIG_NET_SCH_SFQ=m +CONFIG_NET_SCH_TEQL=m +CONFIG_NET_SCH_TBF=m +CONFIG_NET_SCH_GRED=m +CONFIG_NET_SCH_DSMARK=m +CONFIG_NET_SCH_NETEM=m +CONFIG_NET_SCH_INGRESS=m +CONFIG_NET_QOS=y +CONFIG_NET_ESTIMATOR=y +CONFIG_NET_CLS=y +CONFIG_NET_CLS_TCINDEX=m +CONFIG_NET_CLS_ROUTE4=m +CONFIG_NET_CLS_ROUTE=y +CONFIG_NET_CLS_FW=m +CONFIG_NET_CLS_U32=m +CONFIG_CLS_U32_PERF=y +CONFIG_NET_CLS_IND=y +CONFIG_NET_CLS_RSVP=m +CONFIG_NET_CLS_RSVP6=m +# CONFIG_NET_CLS_ACT is not set +CONFIG_NET_CLS_POLICE=y + +# +# Network testing +# +# CONFIG_NET_PKTGEN is not set +CONFIG_NETPOLL=y +# CONFIG_NETPOLL_RX is not set +CONFIG_NETPOLL_TRAP=y +CONFIG_NET_POLL_CONTROLLER=y +# CONFIG_HAMRADIO is not set +CONFIG_IRDA=m + +# +# IrDA protocols +# +CONFIG_IRLAN=m +CONFIG_IRNET=m +CONFIG_IRCOMM=m +# CONFIG_IRDA_ULTRA is not set + +# +# IrDA options +# +CONFIG_IRDA_CACHE_LAST_LSAP=y +CONFIG_IRDA_FAST_RR=y +# CONFIG_IRDA_DEBUG is not set + +# +# Infrared-port device drivers +# + +# +# SIR device drivers +# +CONFIG_IRTTY_SIR=m + +# +# Dongle support +# +CONFIG_DONGLE=y +CONFIG_ESI_DONGLE=m +CONFIG_ACTISYS_DONGLE=m +CONFIG_TEKRAM_DONGLE=m +CONFIG_LITELINK_DONGLE=m +CONFIG_MA600_DONGLE=m +CONFIG_GIRBIL_DONGLE=m +CONFIG_MCP2120_DONGLE=m +CONFIG_OLD_BELKIN_DONGLE=m +CONFIG_ACT200L_DONGLE=m + +# +# Old SIR device drivers +# +CONFIG_IRPORT_SIR=m + +# +# Old Serial dongle support +# +# CONFIG_DONGLE_OLD is not set + +# +# FIR device drivers +# +CONFIG_USB_IRDA=m +CONFIG_SIGMATEL_FIR=m +CONFIG_NSC_FIR=m +# CONFIG_WINBOND_FIR is not set +# CONFIG_TOSHIBA_FIR is not set +# CONFIG_SMC_IRCC_FIR is not set +# CONFIG_ALI_FIR is not set +# CONFIG_VLSI_FIR is not set +# CONFIG_VIA_FIR is not set +CONFIG_BT=m +CONFIG_BT_L2CAP=m +CONFIG_BT_SCO=m +CONFIG_BT_RFCOMM=m +CONFIG_BT_RFCOMM_TTY=y +CONFIG_BT_BNEP=m +CONFIG_BT_BNEP_MC_FILTER=y +CONFIG_BT_BNEP_PROTO_FILTER=y +CONFIG_BT_CMTP=m +CONFIG_BT_HIDP=m + +# +# Bluetooth device drivers +# +CONFIG_BT_HCIUSB=m +CONFIG_BT_HCIUSB_SCO=y +CONFIG_BT_HCIUART=m +CONFIG_BT_HCIUART_H4=y +CONFIG_BT_HCIUART_BCSP=y +CONFIG_BT_HCIUART_BCSP_TXCRC=y +CONFIG_BT_HCIBCM203X=m +CONFIG_BT_HCIBFUSB=m +CONFIG_BT_HCIDTL1=m +CONFIG_BT_HCIBT3C=m +CONFIG_BT_HCIBLUECARD=m +CONFIG_BT_HCIBTUART=m +CONFIG_BT_HCIVHCI=m +CONFIG_TUX=m + +# +# TUX options +# +CONFIG_TUX_EXTCGI=y +# CONFIG_TUX_EXTENDED_LOG is not set +# CONFIG_TUX_DEBUG is not set +CONFIG_NETDEVICES=y +CONFIG_DUMMY=m +CONFIG_BONDING=m +CONFIG_EQUALIZER=m +CONFIG_TUN=m +CONFIG_ETHERTAP=m +CONFIG_NET_SB1000=m + +# +# ARCnet devices +# +# CONFIG_ARCNET is not set + +# +# Ethernet (10 or 100Mbit) +# +CONFIG_NET_ETHERNET=y +CONFIG_MII=m +CONFIG_HAPPYMEAL=m +CONFIG_SUNGEM=m +CONFIG_NET_VENDOR_3COM=y +CONFIG_EL1=m +CONFIG_EL2=m +CONFIG_ELPLUS=m +CONFIG_EL16=m +CONFIG_EL3=m +CONFIG_3C515=m +CONFIG_VORTEX=m +CONFIG_TYPHOON=m +CONFIG_LANCE=m +CONFIG_NET_VENDOR_SMC=y +CONFIG_WD80x3=m +CONFIG_ULTRA=m +CONFIG_SMC9194=m +CONFIG_NET_VENDOR_RACAL=y +# CONFIG_NI5010 is not set +CONFIG_NI52=m +CONFIG_NI65=m + +# +# Tulip family network device support +# +CONFIG_NET_TULIP=y +CONFIG_DE2104X=m +CONFIG_TULIP=m +# CONFIG_TULIP_MWI is not set +CONFIG_TULIP_MMIO=y +# CONFIG_TULIP_NAPI is not set +CONFIG_DE4X5=m +CONFIG_WINBOND_840=m +CONFIG_DM9102=m +CONFIG_PCMCIA_XIRCOM=m +# CONFIG_PCMCIA_XIRTULIP is not set +# CONFIG_AT1700 is not set +CONFIG_DEPCA=m +CONFIG_HP100=m +CONFIG_NET_ISA=y +CONFIG_E2100=m +CONFIG_EWRK3=m +CONFIG_EEXPRESS=m +CONFIG_EEXPRESS_PRO=m +CONFIG_HPLAN_PLUS=m +CONFIG_HPLAN=m +CONFIG_LP486E=m +CONFIG_ETH16I=m +CONFIG_NE2000=m +CONFIG_ZNET=m +CONFIG_SEEQ8005=m +CONFIG_NET_PCI=y +CONFIG_PCNET32=m +CONFIG_AMD8111_ETH=m +CONFIG_AMD8111E_NAPI=y +CONFIG_ADAPTEC_STARFIRE=m +CONFIG_ADAPTEC_STARFIRE_NAPI=y +CONFIG_AC3200=m +CONFIG_APRICOT=m +CONFIG_B44=m +CONFIG_FORCEDETH=m +CONFIG_CS89x0=m +CONFIG_DGRS=m +CONFIG_EEPRO100=m +# CONFIG_EEPRO100_PIO is not set +CONFIG_E100=m +CONFIG_E100_NAPI=y +CONFIG_FEALNX=m +CONFIG_NATSEMI=m +CONFIG_NE2K_PCI=m +CONFIG_8139CP=m +CONFIG_8139TOO=m +CONFIG_8139TOO_PIO=y +# CONFIG_8139TOO_TUNE_TWISTER is not set +CONFIG_8139TOO_8129=y +# CONFIG_8139_OLD_RX_RESET is not set +CONFIG_SIS900=m +CONFIG_EPIC100=m +CONFIG_SUNDANCE=m +# CONFIG_SUNDANCE_MMIO is not set +CONFIG_TLAN=m +CONFIG_VIA_RHINE=m +CONFIG_VIA_RHINE_MMIO=y +CONFIG_NET_POCKET=y +CONFIG_ATP=m +CONFIG_DE600=m +CONFIG_DE620=m + +# +# Ethernet (1000 Mbit) +# +CONFIG_ACENIC=m +# CONFIG_ACENIC_OMIT_TIGON_I is not set +CONFIG_DL2K=m +CONFIG_E1000=m +CONFIG_E1000_NAPI=y +CONFIG_NS83820=m +CONFIG_HAMACHI=m +CONFIG_YELLOWFIN=m +CONFIG_R8169=m +CONFIG_R8169_NAPI=y +CONFIG_SK98LIN=m +CONFIG_VIA_VELOCITY=m +CONFIG_TIGON3=m + +# +# Ethernet (10000 Mbit) +# +CONFIG_IXGB=m +CONFIG_IXGB_NAPI=y +CONFIG_S2IO=m +CONFIG_S2IO_NAPI=y + +# +# Token Ring devices +# +CONFIG_TR=y +CONFIG_IBMTR=m +CONFIG_IBMOL=m +CONFIG_IBMLS=m +CONFIG_3C359=m +CONFIG_TMS380TR=m +CONFIG_TMSPCI=m +CONFIG_SKISA=m +CONFIG_PROTEON=m +CONFIG_ABYSS=m +CONFIG_SMCTR=m + +# +# Wireless LAN (non-hamradio) +# +CONFIG_NET_RADIO=y + +# +# Obsolete Wireless cards support (pre-802.11) +# +# CONFIG_STRIP is not set +# CONFIG_ARLAN is not set +CONFIG_WAVELAN=m +CONFIG_PCMCIA_WAVELAN=m +CONFIG_PCMCIA_NETWAVE=m + +# +# Wireless 802.11 Frequency Hopping cards support +# +# CONFIG_PCMCIA_RAYCS is not set + +# +# Wireless 802.11b ISA/PCI cards support +# +CONFIG_IEEE80211=m +# CONFIG_IEEE80211_DEBUG is not set +CONFIG_IEEE80211_CRYPT=m +CONFIG_IEEE80211_WPA=m +CONFIG_IEEE80211_CRYPT_CCMP=m +CONFIG_IEEE80211_CRYPT_TKIP=m +CONFIG_IPW2100=m +# CONFIG_IPW_DEBUG is not set +CONFIG_IPW2100_PROMISC=y +# CONFIG_IPW2100_LEGACY_FW_LOAD is not set +CONFIG_IPW2200=m +CONFIG_AIRO=m +CONFIG_HERMES=m +CONFIG_PLX_HERMES=m +CONFIG_TMD_HERMES=m +CONFIG_PCI_HERMES=m +CONFIG_ATMEL=m +CONFIG_PCI_ATMEL=m + +# +# Wireless 802.11b Pcmcia/Cardbus cards support +# +CONFIG_PCMCIA_HERMES=m +CONFIG_AIRO_CS=m +CONFIG_PCMCIA_ATMEL=m +CONFIG_PCMCIA_WL3501=m + +# +# Prism GT/Duette 802.11(a/b/g) PCI/Cardbus support +# +CONFIG_PRISM54=m +CONFIG_NET_WIRELESS=y + +# +# PCMCIA network device support +# +CONFIG_NET_PCMCIA=y +CONFIG_PCMCIA_3C589=m +CONFIG_PCMCIA_3C574=m +CONFIG_PCMCIA_FMVJ18X=m +CONFIG_PCMCIA_PCNET=m +CONFIG_PCMCIA_NMCLAN=m +CONFIG_PCMCIA_SMC91C92=m +CONFIG_PCMCIA_XIRC2PS=m +CONFIG_PCMCIA_AXNET=m +CONFIG_PCMCIA_IBMTR=m + +# +# Wan interfaces +# +# CONFIG_WAN is not set + +# +# ATM drivers +# +CONFIG_ATM_TCP=m +CONFIG_ATM_LANAI=m +CONFIG_ATM_ENI=m +# CONFIG_ATM_ENI_DEBUG is not set +# CONFIG_ATM_ENI_TUNE_BURST is not set +CONFIG_ATM_FIRESTREAM=m +# CONFIG_ATM_ZATM is not set +CONFIG_ATM_NICSTAR=m +# CONFIG_ATM_NICSTAR_USE_SUNI is not set +# CONFIG_ATM_NICSTAR_USE_IDT77105 is not set +CONFIG_ATM_IDT77252=m +# CONFIG_ATM_IDT77252_DEBUG is not set +# CONFIG_ATM_IDT77252_RCV_ALL is not set +CONFIG_ATM_IDT77252_USE_SUNI=y +CONFIG_ATM_AMBASSADOR=m +# CONFIG_ATM_AMBASSADOR_DEBUG is not set +CONFIG_ATM_HORIZON=m +# CONFIG_ATM_HORIZON_DEBUG is not set +# CONFIG_ATM_IA is not set +CONFIG_ATM_FORE200E_MAYBE=m +# CONFIG_ATM_FORE200E_PCA is not set +CONFIG_ATM_HE=m +# CONFIG_ATM_HE_USE_SUNI is not set +CONFIG_FDDI=y +# CONFIG_DEFXX is not set +CONFIG_SKFP=m +# CONFIG_HIPPI is not set +CONFIG_PLIP=m +CONFIG_PPP=m +CONFIG_PPP_MULTILINK=y +CONFIG_PPP_FILTER=y +CONFIG_PPP_ASYNC=m +CONFIG_PPP_SYNC_TTY=m +CONFIG_PPP_DEFLATE=m +# CONFIG_PPP_BSDCOMP is not set +CONFIG_PPPOE=m +CONFIG_PPPOATM=m +CONFIG_SLIP=m +CONFIG_SLIP_COMPRESSED=y +CONFIG_SLIP_SMART=y +# CONFIG_SLIP_MODE_SLIP6 is not set +CONFIG_NET_FC=y +# CONFIG_SHAPER is not set +CONFIG_NETCONSOLE=m + +# +# ISDN subsystem +# +CONFIG_ISDN=m + +# +# Old ISDN4Linux +# +CONFIG_ISDN_I4L=m +CONFIG_ISDN_PPP=y +CONFIG_ISDN_PPP_VJ=y +CONFIG_ISDN_MPP=y +CONFIG_IPPP_FILTER=y +# CONFIG_ISDN_PPP_BSDCOMP is not set +CONFIG_ISDN_AUDIO=y +CONFIG_ISDN_TTY_FAX=y + +# +# ISDN feature submodules +# +CONFIG_ISDN_DRV_LOOP=m + +# +# ISDN4Linux hardware drivers +# + +# +# Passive cards +# +CONFIG_ISDN_DRV_HISAX=m + +# +# D-channel protocol features +# +CONFIG_HISAX_EURO=y +CONFIG_DE_AOC=y +CONFIG_HISAX_NO_SENDCOMPLETE=y +CONFIG_HISAX_NO_LLC=y +CONFIG_HISAX_NO_KEYPAD=y +CONFIG_HISAX_1TR6=y +CONFIG_HISAX_NI1=y +CONFIG_HISAX_MAX_CARDS=8 + +# +# HiSax supported cards +# +CONFIG_HISAX_16_0=y +CONFIG_HISAX_16_3=y +CONFIG_HISAX_TELESPCI=y +CONFIG_HISAX_S0BOX=y +CONFIG_HISAX_AVM_A1=y +CONFIG_HISAX_FRITZPCI=y +CONFIG_HISAX_AVM_A1_PCMCIA=y +CONFIG_HISAX_ELSA=y +CONFIG_HISAX_IX1MICROR2=y +CONFIG_HISAX_DIEHLDIVA=y +CONFIG_HISAX_ASUSCOM=y +CONFIG_HISAX_TELEINT=y +CONFIG_HISAX_HFCS=y +CONFIG_HISAX_SEDLBAUER=y +CONFIG_HISAX_SPORTSTER=y +CONFIG_HISAX_MIC=y +CONFIG_HISAX_NETJET=y +CONFIG_HISAX_NETJET_U=y +CONFIG_HISAX_NICCY=y +CONFIG_HISAX_ISURF=y +CONFIG_HISAX_HSTSAPHIR=y +CONFIG_HISAX_BKM_A4T=y +CONFIG_HISAX_SCT_QUADRO=y +CONFIG_HISAX_GAZEL=y +CONFIG_HISAX_HFC_PCI=y +CONFIG_HISAX_W6692=y +CONFIG_HISAX_HFC_SX=y +CONFIG_HISAX_ENTERNOW_PCI=y +# CONFIG_HISAX_DEBUG is not set + +# +# HiSax PCMCIA card service modules +# +CONFIG_HISAX_SEDLBAUER_CS=m +CONFIG_HISAX_ELSA_CS=m +CONFIG_HISAX_AVM_A1_CS=m +CONFIG_HISAX_TELES_CS=m + +# +# HiSax sub driver modules +# +CONFIG_HISAX_ST5481=m +CONFIG_HISAX_HFCUSB=m +CONFIG_HISAX_FRITZ_PCIPNP=m +CONFIG_HISAX_HDLC=y + +# +# Active cards +# +CONFIG_ISDN_DRV_ICN=m +CONFIG_ISDN_DRV_PCBIT=m +CONFIG_ISDN_DRV_SC=m +CONFIG_ISDN_DRV_ACT2000=m +CONFIG_ISDN_DRV_TPAM=m +CONFIG_HYSDN=m +CONFIG_HYSDN_CAPI=y + +# +# CAPI subsystem +# +CONFIG_ISDN_CAPI=m +CONFIG_ISDN_DRV_AVMB1_VERBOSE_REASON=y +CONFIG_ISDN_CAPI_MIDDLEWARE=y +CONFIG_ISDN_CAPI_CAPI20=m +CONFIG_ISDN_CAPI_CAPIFS_BOOL=y +CONFIG_ISDN_CAPI_CAPIFS=m +CONFIG_ISDN_CAPI_CAPIDRV=m + +# +# CAPI hardware drivers +# + +# +# Active AVM cards +# +CONFIG_CAPI_AVM=y +CONFIG_ISDN_DRV_AVMB1_B1ISA=m +CONFIG_ISDN_DRV_AVMB1_B1PCI=m +CONFIG_ISDN_DRV_AVMB1_B1PCIV4=y +CONFIG_ISDN_DRV_AVMB1_T1ISA=m +CONFIG_ISDN_DRV_AVMB1_B1PCMCIA=m +CONFIG_ISDN_DRV_AVMB1_AVM_CS=m +CONFIG_ISDN_DRV_AVMB1_T1PCI=m +CONFIG_ISDN_DRV_AVMB1_C4=m + +# +# Active Eicon DIVA Server cards +# +CONFIG_CAPI_EICON=y +CONFIG_ISDN_DIVAS=m +CONFIG_ISDN_DIVAS_BRIPCI=y +CONFIG_ISDN_DIVAS_PRIPCI=y +CONFIG_ISDN_DIVAS_DIVACAPI=m +CONFIG_ISDN_DIVAS_USERIDI=m +CONFIG_ISDN_DIVAS_MAINT=m + +# +# Telephony Support +# +# CONFIG_PHONE is not set + +# +# Input device support +# +CONFIG_INPUT=y + +# +# 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=m +# CONFIG_INPUT_TSDEV is not set +CONFIG_INPUT_EVDEV=y +# CONFIG_INPUT_EVBUG is not set + +# +# Input I/O drivers +# +CONFIG_GAMEPORT=m +CONFIG_SOUND_GAMEPORT=m +CONFIG_GAMEPORT_NS558=m +CONFIG_GAMEPORT_L4=m +CONFIG_GAMEPORT_EMU10K1=m +CONFIG_GAMEPORT_VORTEX=m +CONFIG_GAMEPORT_FM801=m +CONFIG_GAMEPORT_CS461x=m +CONFIG_SERIO=y +CONFIG_SERIO_I8042=y +CONFIG_SERIO_SERPORT=y +# CONFIG_SERIO_CT82C710 is not set +# CONFIG_SERIO_PARKBD is not set +# CONFIG_SERIO_PCIPS2 is not set +# CONFIG_SERIO_RAW 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_INPUT_MOUSE=y +CONFIG_MOUSE_PS2=y +CONFIG_MOUSE_SERIAL=m +CONFIG_MOUSE_INPORT=m +CONFIG_MOUSE_ATIXL=y +CONFIG_MOUSE_LOGIBM=m +CONFIG_MOUSE_PC110PAD=m +CONFIG_MOUSE_VSXXXAA=m +CONFIG_INPUT_JOYSTICK=y +CONFIG_JOYSTICK_ANALOG=m +CONFIG_JOYSTICK_A3D=m +CONFIG_JOYSTICK_ADI=m +CONFIG_JOYSTICK_COBRA=m +CONFIG_JOYSTICK_GF2K=m +CONFIG_JOYSTICK_GRIP=m +CONFIG_JOYSTICK_GRIP_MP=m +CONFIG_JOYSTICK_GUILLEMOT=m +CONFIG_JOYSTICK_INTERACT=m +CONFIG_JOYSTICK_SIDEWINDER=m +CONFIG_JOYSTICK_TMDC=m +CONFIG_JOYSTICK_IFORCE=m +CONFIG_JOYSTICK_IFORCE_USB=y +CONFIG_JOYSTICK_IFORCE_232=y +CONFIG_JOYSTICK_WARRIOR=m +CONFIG_JOYSTICK_MAGELLAN=m +CONFIG_JOYSTICK_SPACEORB=m +CONFIG_JOYSTICK_SPACEBALL=m +CONFIG_JOYSTICK_STINGER=m +CONFIG_JOYSTICK_TWIDDLER=m +CONFIG_JOYSTICK_DB9=m +CONFIG_JOYSTICK_GAMECON=m +CONFIG_JOYSTICK_TURBOGRAFX=m +CONFIG_JOYSTICK_JOYDUMP=m +CONFIG_INPUT_TOUCHSCREEN=y +CONFIG_TOUCHSCREEN_GUNZE=m +CONFIG_INPUT_MISC=y +CONFIG_INPUT_PCSPKR=m +CONFIG_INPUT_UINPUT=m + +# +# Character devices +# +CONFIG_VT=y +CONFIG_VT_CONSOLE=y +CONFIG_HW_CONSOLE=y +CONFIG_SERIAL_NONSTANDARD=y +# CONFIG_COMPUTONE is not set +CONFIG_ROCKETPORT=m +# CONFIG_CYCLADES is not set +# CONFIG_DIGIEPCA is not set +# CONFIG_DIGI is not set +# CONFIG_ESPSERIAL is not set +# CONFIG_MOXA_INTELLIO is not set +# CONFIG_MOXA_SMARTIO is not set +# CONFIG_ISI is not set +CONFIG_SYNCLINK=m +CONFIG_SYNCLINKMP=m +CONFIG_N_HDLC=m +# CONFIG_RISCOM8 is not set +# CONFIG_SPECIALIX is not set +# CONFIG_SX is not set +# CONFIG_RIO is not set +CONFIG_STALDRV=y +# CONFIG_STALLION is not set +# CONFIG_ISTALLION is not set + +# +# Serial drivers +# +CONFIG_SERIAL_8250=y +CONFIG_SERIAL_8250_CONSOLE=y +CONFIG_SERIAL_8250_CS=m +# CONFIG_SERIAL_8250_ACPI is not set +CONFIG_SERIAL_8250_NR_UARTS=32 +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_MULTIPORT=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_CRASH=m +CONFIG_PRINTER=m +CONFIG_LP_CONSOLE=y +CONFIG_PPDEV=m +CONFIG_TIPAR=m + +# +# IPMI +# +CONFIG_IPMI_HANDLER=m +# CONFIG_IPMI_PANIC_EVENT is not set +CONFIG_IPMI_DEVICE_INTERFACE=m +CONFIG_IPMI_SI=m +CONFIG_IPMI_WATCHDOG=m +CONFIG_IPMI_POWEROFF=m + +# +# Watchdog Cards +# +CONFIG_WATCHDOG=y +# CONFIG_WATCHDOG_NOWAYOUT is not set + +# +# Watchdog Device Drivers +# +CONFIG_SOFT_WATCHDOG=m +CONFIG_ACQUIRE_WDT=m +CONFIG_ADVANTECH_WDT=m +CONFIG_ALIM1535_WDT=m +CONFIG_ALIM7101_WDT=m +CONFIG_SC520_WDT=m +CONFIG_EUROTECH_WDT=m +CONFIG_IB700_WDT=m +CONFIG_WAFER_WDT=m +CONFIG_I8XX_TCO=m +CONFIG_SC1200_WDT=m +# CONFIG_SCx200_WDT is not set +# CONFIG_60XX_WDT is not set +CONFIG_CPU5_WDT=m +CONFIG_W83627HF_WDT=m +CONFIG_W83877F_WDT=m +CONFIG_MACHZ_WDT=m + +# +# ISA-based Watchdog Cards +# +CONFIG_PCWATCHDOG=m +# CONFIG_MIXCOMWD is not set +CONFIG_WDT=m +# CONFIG_WDT_501 is not set + +# +# PCI-based Watchdog Cards +# +CONFIG_PCIPCWATCHDOG=m +CONFIG_WDTPCI=m +CONFIG_WDT_501_PCI=y + +# +# USB-based Watchdog Cards +# +CONFIG_USBPCWATCHDOG=m +CONFIG_HW_RANDOM=m +CONFIG_NVRAM=m +CONFIG_RTC=y +CONFIG_DTLK=m +CONFIG_R3964=m +# CONFIG_APPLICOM is not set +CONFIG_SONYPI=m + +# +# Ftape, the floppy tape device driver +# +# CONFIG_FTAPE is not set +CONFIG_AGP=y +CONFIG_AGP_ALI=y +CONFIG_AGP_ATI=y +CONFIG_AGP_AMD=y +CONFIG_AGP_AMD64=y +CONFIG_AGP_INTEL=y +CONFIG_AGP_INTEL_MCH=y +CONFIG_AGP_NVIDIA=y +CONFIG_AGP_SIS=y +CONFIG_AGP_SWORKS=y +CONFIG_AGP_VIA=y +CONFIG_AGP_EFFICEON=y +CONFIG_DRM=y +CONFIG_DRM_TDFX=m +CONFIG_DRM_R128=m +CONFIG_DRM_RADEON=m +CONFIG_DRM_I810=m +CONFIG_DRM_I830=m +CONFIG_DRM_I915=m +CONFIG_DRM_MGA=m +CONFIG_DRM_SIS=m + +# +# PCMCIA character devices +# +CONFIG_SYNCLINK_CS=m +CONFIG_MWAVE=m +CONFIG_RAW_DRIVER=m +# CONFIG_HPET is not set +CONFIG_MAX_RAW_DEVS=8192 +CONFIG_HANGCHECK_TIMER=m + +# +# I2C support +# +CONFIG_I2C=m +CONFIG_I2C_CHARDEV=m + +# +# I2C Algorithms +# +CONFIG_I2C_ALGOBIT=m +CONFIG_I2C_ALGOPCF=m +CONFIG_I2C_ALGOPCA=m + +# +# I2C Hardware Bus support +# +CONFIG_I2C_ALI1535=m +CONFIG_I2C_ALI1563=m +CONFIG_I2C_ALI15X3=m +CONFIG_I2C_AMD756=m +CONFIG_I2C_AMD8111=m +# CONFIG_I2C_ELEKTOR is not set +CONFIG_I2C_I801=m +CONFIG_I2C_I810=m +CONFIG_I2C_ISA=m +CONFIG_I2C_NFORCE2=m +# CONFIG_I2C_PARPORT is not set +# CONFIG_I2C_PARPORT_LIGHT is not set +CONFIG_I2C_PIIX4=m +CONFIG_I2C_PROSAVAGE=m +CONFIG_I2C_SAVAGE4=m +# CONFIG_SCx200_ACB is not set +CONFIG_I2C_SIS5595=m +CONFIG_I2C_SIS630=m +CONFIG_I2C_SIS96X=m +CONFIG_I2C_VIA=m +CONFIG_I2C_VIAPRO=m +CONFIG_I2C_VOODOO3=m +CONFIG_I2C_PCA_ISA=m + +# +# Hardware Sensors Chip support +# +CONFIG_I2C_SENSOR=m +CONFIG_SENSORS_ADM1021=m +CONFIG_SENSORS_ADM1025=m +CONFIG_SENSORS_ADM1031=m +CONFIG_SENSORS_ASB100=m +CONFIG_SENSORS_DS1621=m +CONFIG_SENSORS_FSCHER=m +CONFIG_SENSORS_GL518SM=m +CONFIG_SENSORS_IT87=m +CONFIG_SENSORS_LM75=m +CONFIG_SENSORS_LM77=m +CONFIG_SENSORS_LM78=m +CONFIG_SENSORS_LM80=m +CONFIG_SENSORS_LM83=m +CONFIG_SENSORS_LM85=m +CONFIG_SENSORS_LM90=m +CONFIG_SENSORS_MAX1619=m +CONFIG_SENSORS_SMSC47M1=m +CONFIG_SENSORS_VIA686A=m +CONFIG_SENSORS_W83781D=m +CONFIG_SENSORS_W83L785TS=m +CONFIG_SENSORS_W83627HF=m + +# +# Other I2C Chip support +# +CONFIG_SENSORS_EEPROM=m +CONFIG_SENSORS_PCF8574=m +CONFIG_SENSORS_PCF8591=m +CONFIG_SENSORS_RTC8564=m +# 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 + +# +# Dallas's 1-wire bus +# +# CONFIG_W1 is not set + +# +# Misc devices +# +CONFIG_IBM_ASM=m + +# +# Multimedia devices +# +CONFIG_VIDEO_DEV=m + +# +# Video For Linux +# + +# +# Video Adapters +# +CONFIG_VIDEO_BT848=m +CONFIG_VIDEO_PMS=m +CONFIG_VIDEO_BWQCAM=m +CONFIG_VIDEO_CQCAM=m +CONFIG_VIDEO_W9966=m +CONFIG_VIDEO_CPIA=m +CONFIG_VIDEO_CPIA_PP=m +CONFIG_VIDEO_CPIA_USB=m +CONFIG_VIDEO_SAA5246A=m +CONFIG_VIDEO_SAA5249=m +CONFIG_TUNER_3036=m +CONFIG_VIDEO_STRADIS=m +CONFIG_VIDEO_ZORAN=m +CONFIG_VIDEO_ZORAN_BUZ=m +CONFIG_VIDEO_ZORAN_DC10=m +CONFIG_VIDEO_ZORAN_DC30=m +CONFIG_VIDEO_ZORAN_LML33=m +CONFIG_VIDEO_ZORAN_LML33R10=m +CONFIG_VIDEO_MEYE=m +CONFIG_VIDEO_SAA7134=m +CONFIG_VIDEO_MXB=m +CONFIG_VIDEO_DPC=m +CONFIG_VIDEO_HEXIUM_ORION=m +CONFIG_VIDEO_HEXIUM_GEMINI=m +CONFIG_VIDEO_CX88=m +CONFIG_VIDEO_OVCAMCHIP=m + +# +# Radio Adapters +# +CONFIG_RADIO_CADET=m +CONFIG_RADIO_RTRACK=m +CONFIG_RADIO_RTRACK2=m +CONFIG_RADIO_AZTECH=m +CONFIG_RADIO_GEMTEK=m +CONFIG_RADIO_GEMTEK_PCI=m +CONFIG_RADIO_MAXIRADIO=m +CONFIG_RADIO_MAESTRO=m +CONFIG_RADIO_SF16FMI=m +CONFIG_RADIO_SF16FMR2=m +CONFIG_RADIO_TERRATEC=m +CONFIG_RADIO_TRUST=m +CONFIG_RADIO_TYPHOON=m +CONFIG_RADIO_TYPHOON_PROC_FS=y +CONFIG_RADIO_ZOLTRIX=m + +# +# Digital Video Broadcasting Devices +# +CONFIG_DVB=y +CONFIG_DVB_CORE=m + +# +# Supported Frontend Modules +# +CONFIG_DVB_TWINHAN_DST=m +CONFIG_DVB_STV0299=m +# CONFIG_DVB_SP887X is not set +# CONFIG_DVB_ALPS_TDLB7 is not set +CONFIG_DVB_ALPS_TDMB7=m +CONFIG_DVB_ATMEL_AT76C651=m +CONFIG_DVB_CX24110=m +CONFIG_DVB_GRUNDIG_29504_491=m +CONFIG_DVB_GRUNDIG_29504_401=m +CONFIG_DVB_MT312=m +CONFIG_DVB_VES1820=m +CONFIG_DVB_VES1X93=m +# CONFIG_DVB_TDA1004X is not set +CONFIG_DVB_NXT6000=m + +# +# Supported SAA7146 based PCI Adapters +# +CONFIG_DVB_AV7110=m +CONFIG_DVB_AV7110_OSD=y +CONFIG_DVB_BUDGET=m +CONFIG_DVB_BUDGET_CI=m +CONFIG_DVB_BUDGET_AV=m +CONFIG_DVB_BUDGET_PATCH=m + +# +# Supported USB Adapters +# +CONFIG_DVB_TTUSB_BUDGET=m +CONFIG_DVB_TTUSB_DEC=m + +# +# Supported FlexCopII (B2C2) Adapters +# +CONFIG_DVB_B2C2_SKYSTAR=m + +# +# Supported BT878 Adapters +# +CONFIG_DVB_BT8XX=m +CONFIG_VIDEO_SAA7146=m +CONFIG_VIDEO_SAA7146_VV=m +CONFIG_VIDEO_VIDEOBUF=m +CONFIG_VIDEO_TUNER=m +CONFIG_VIDEO_BUF=m +CONFIG_VIDEO_BTCX=m +CONFIG_VIDEO_IR=m + +# +# Graphics support +# +CONFIG_FB=y +CONFIG_FB_MODE_HELPERS=y +CONFIG_FB_CIRRUS=m +# CONFIG_FB_PM2 is not set +# CONFIG_FB_CYBER2000 is not set +# CONFIG_FB_ASILIANT is not set +# CONFIG_FB_IMSTT is not set +CONFIG_FB_VGA16=m +CONFIG_FB_VESA=y +CONFIG_VIDEO_SELECT=y +CONFIG_FB_HGA=m +CONFIG_FB_HGA_ACCEL=y +CONFIG_FB_RIVA=m +# CONFIG_FB_RIVA_I2C is not set +# CONFIG_FB_RIVA_DEBUG is not set +CONFIG_FB_I810=m +CONFIG_FB_I810_GTF=y +CONFIG_FB_MATROX=m +CONFIG_FB_MATROX_MILLENIUM=y +CONFIG_FB_MATROX_MYSTIQUE=y +CONFIG_FB_MATROX_G450=y +CONFIG_FB_MATROX_G100=y +CONFIG_FB_MATROX_I2C=m +CONFIG_FB_MATROX_MAVEN=m +CONFIG_FB_MATROX_MULTIHEAD=y +# CONFIG_FB_RADEON_OLD is not set +CONFIG_FB_RADEON=m +CONFIG_FB_RADEON_I2C=y +# CONFIG_FB_RADEON_DEBUG is not set +CONFIG_FB_ATY128=m +CONFIG_FB_ATY=m +CONFIG_FB_ATY_CT=y +CONFIG_FB_ATY_GX=y +# CONFIG_FB_ATY_XL_INIT is not set +# CONFIG_FB_SIS is not set +CONFIG_FB_NEOMAGIC=m +CONFIG_FB_KYRO=m +CONFIG_FB_3DFX=m +CONFIG_FB_3DFX_ACCEL=y +CONFIG_FB_VOODOO1=m +CONFIG_FB_TRIDENT=m +CONFIG_FB_TRIDENT_ACCEL=y +# CONFIG_FB_VIRTUAL is not set + +# +# Console display driver support +# +CONFIG_VGA_CONSOLE=y +CONFIG_MDA_CONSOLE=m +CONFIG_DUMMY_CONSOLE=y +CONFIG_FRAMEBUFFER_CONSOLE=y +# 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 + +# +# Sound +# +CONFIG_SOUND=m + +# +# Advanced Linux Sound Architecture +# +CONFIG_SND=m +CONFIG_SND_TIMER=m +CONFIG_SND_PCM=m +CONFIG_SND_HWDEP=m +CONFIG_SND_RAWMIDI=m +CONFIG_SND_SEQUENCER=m +CONFIG_SND_SEQ_DUMMY=m +CONFIG_SND_OSSEMUL=y +CONFIG_SND_MIXER_OSS=m +CONFIG_SND_PCM_OSS=m +CONFIG_SND_SEQUENCER_OSS=y +CONFIG_SND_RTCTIMER=m +# CONFIG_SND_VERBOSE_PRINTK is not set +# CONFIG_SND_DEBUG is not set + +# +# Generic devices +# +CONFIG_SND_MPU401_UART=m +CONFIG_SND_OPL3_LIB=m +CONFIG_SND_OPL4_LIB=m +CONFIG_SND_VX_LIB=m +CONFIG_SND_DUMMY=m +CONFIG_SND_VIRMIDI=m +CONFIG_SND_MTPAV=m +# CONFIG_SND_SERIAL_U16550 is not set +CONFIG_SND_MPU401=m + +# +# ISA devices +# +CONFIG_SND_AD1816A=m +CONFIG_SND_AD1848=m +CONFIG_SND_CS4231=m +CONFIG_SND_CS4232=m +CONFIG_SND_CS4236=m +CONFIG_SND_ES968=m +CONFIG_SND_ES1688=m +CONFIG_SND_ES18XX=m +CONFIG_SND_GUS_SYNTH=m +CONFIG_SND_GUSCLASSIC=m +CONFIG_SND_GUSEXTREME=m +CONFIG_SND_GUSMAX=m +CONFIG_SND_INTERWAVE=m +CONFIG_SND_INTERWAVE_STB=m +CONFIG_SND_OPTI92X_AD1848=m +CONFIG_SND_OPTI92X_CS4231=m +CONFIG_SND_OPTI93X=m +CONFIG_SND_SB8=m +CONFIG_SND_SB16=m +CONFIG_SND_SBAWE=m +CONFIG_SND_SB16_CSP=y +# CONFIG_SND_WAVEFRONT is not set +CONFIG_SND_ALS100=m +CONFIG_SND_AZT2320=m +CONFIG_SND_CMI8330=m +CONFIG_SND_DT019X=m +CONFIG_SND_OPL3SA2=m +CONFIG_SND_SGALAXY=m +CONFIG_SND_SSCAPE=m + +# +# PCI devices +# +CONFIG_SND_AC97_CODEC=m +CONFIG_SND_ALI5451=m +CONFIG_SND_ATIIXP=m +CONFIG_SND_ATIIXP_MODEM=m +CONFIG_SND_AU8810=m +CONFIG_SND_AU8820=m +CONFIG_SND_AU8830=m +CONFIG_SND_AZT3328=m +CONFIG_SND_BT87X=m +CONFIG_SND_CS46XX=m +CONFIG_SND_CS46XX_NEW_DSP=y +CONFIG_SND_CS4281=m +CONFIG_SND_EMU10K1=m +CONFIG_SND_KORG1212=m +CONFIG_SND_MIXART=m +CONFIG_SND_NM256=m +CONFIG_SND_RME32=m +CONFIG_SND_RME96=m +CONFIG_SND_RME9652=m +CONFIG_SND_HDSP=m +CONFIG_SND_TRIDENT=m +CONFIG_SND_YMFPCI=m +CONFIG_SND_ALS4000=m +CONFIG_SND_CMIPCI=m +CONFIG_SND_ENS1370=m +CONFIG_SND_ENS1371=m +CONFIG_SND_ES1938=m +CONFIG_SND_ES1968=m +CONFIG_SND_MAESTRO3=m +CONFIG_SND_FM801=m +CONFIG_SND_FM801_TEA575X=m +CONFIG_SND_ICE1712=m +CONFIG_SND_ICE1724=m +CONFIG_SND_INTEL8X0=m +CONFIG_SND_INTEL8X0M=m +CONFIG_SND_SONICVIBES=m +CONFIG_SND_VIA82XX=m +CONFIG_SND_VX222=m + +# +# ALSA USB devices +# +CONFIG_SND_USB_AUDIO=m +CONFIG_SND_USB_USX2Y=m + +# +# PCMCIA devices +# +# CONFIG_SND_VXPOCKET is not set +# CONFIG_SND_VXP440 is not set +CONFIG_SND_PDAUDIOCF=m + +# +# Open Sound System +# +# CONFIG_SOUND_PRIME is not set + +# +# USB support +# +CONFIG_USB=y +# CONFIG_USB_DEBUG is not set + +# +# Miscellaneous USB options +# +CONFIG_USB_DEVICEFS=y +# CONFIG_USB_BANDWIDTH is not set +# CONFIG_USB_DYNAMIC_MINORS is not set +# CONFIG_USB_SUSPEND is not set +# CONFIG_USB_OTG is not set + +# +# USB Host Controller Drivers +# +CONFIG_USB_EHCI_HCD=m +CONFIG_USB_EHCI_SPLIT_ISO=y +CONFIG_USB_EHCI_ROOT_HUB_TT=y +CONFIG_USB_OHCI_HCD=m +CONFIG_USB_UHCI_HCD=m + +# +# USB Device Class drivers +# +# CONFIG_USB_AUDIO is not set + +# +# USB Bluetooth TTY can only be used with disabled Bluetooth subsystem +# +CONFIG_USB_MIDI=m +CONFIG_USB_ACM=m +CONFIG_USB_PRINTER=m +CONFIG_USB_STORAGE=m +# CONFIG_USB_STORAGE_DEBUG is not set +CONFIG_USB_STORAGE_RW_DETECT=y +CONFIG_USB_STORAGE_DATAFAB=y +CONFIG_USB_STORAGE_FREECOM=y +CONFIG_USB_STORAGE_ISD200=y +CONFIG_USB_STORAGE_DPCM=y +CONFIG_USB_STORAGE_HP8200e=y +CONFIG_USB_STORAGE_SDDR09=y +CONFIG_USB_STORAGE_SDDR55=y +CONFIG_USB_STORAGE_JUMPSHOT=y + +# +# USB Human Interface Devices (HID) +# +CONFIG_USB_HID=y +CONFIG_USB_HIDINPUT=y +CONFIG_HID_FF=y +CONFIG_HID_PID=y +CONFIG_LOGITECH_FF=y +CONFIG_THRUSTMASTER_FF=y +CONFIG_USB_HIDDEV=y +CONFIG_USB_AIPTEK=m +CONFIG_USB_WACOM=m +CONFIG_USB_KBTAB=m +CONFIG_USB_POWERMATE=m +CONFIG_USB_MTOUCH=m +CONFIG_USB_EGALAX=m +CONFIG_USB_XPAD=m +CONFIG_USB_ATI_REMOTE=m + +# +# USB Imaging devices +# +CONFIG_USB_MDC800=m +CONFIG_USB_MICROTEK=m +CONFIG_USB_HPUSBSCSI=m + +# +# USB Multimedia devices +# +CONFIG_USB_DABUSB=m +CONFIG_USB_VICAM=m +CONFIG_USB_DSBR=m +CONFIG_USB_IBMCAM=m +CONFIG_USB_KONICAWC=m +CONFIG_USB_OV511=m +CONFIG_USB_SE401=m +CONFIG_USB_SN9C102=m +CONFIG_USB_STV680=m +CONFIG_USB_W9968CF=m +CONFIG_USB_PWC=m + +# +# USB Network adaptors +# +CONFIG_USB_CATC=m +CONFIG_USB_KAWETH=m +CONFIG_USB_PEGASUS=m +CONFIG_USB_RTL8150=m +CONFIG_USB_USBNET=m + +# +# USB Host-to-Host Cables +# +CONFIG_USB_ALI_M5632=y +CONFIG_USB_AN2720=y +CONFIG_USB_BELKIN=y +CONFIG_USB_GENESYS=y +CONFIG_USB_NET1080=y +CONFIG_USB_PL2301=y + +# +# Intelligent USB Devices/Gadgets +# +CONFIG_USB_ARMLINUX=y +CONFIG_USB_EPSON2888=y +CONFIG_USB_ZAURUS=y +CONFIG_USB_CDCETHER=y + +# +# USB Network Adapters +# +CONFIG_USB_AX8817X=y + +# +# USB port drivers +# +CONFIG_USB_USS720=m + +# +# USB Serial Converter support +# +CONFIG_USB_SERIAL=m +CONFIG_USB_SERIAL_GENERIC=y +CONFIG_USB_SERIAL_BELKIN=m +CONFIG_USB_SERIAL_WHITEHEAT=m +CONFIG_USB_SERIAL_DIGI_ACCELEPORT=m +CONFIG_USB_SERIAL_EMPEG=m +CONFIG_USB_SERIAL_FTDI_SIO=m +CONFIG_USB_SERIAL_VISOR=m +CONFIG_USB_SERIAL_IPAQ=m +CONFIG_USB_SERIAL_IR=m +CONFIG_USB_SERIAL_EDGEPORT=m +CONFIG_USB_SERIAL_EDGEPORT_TI=m +CONFIG_USB_SERIAL_KEYSPAN_PDA=m +CONFIG_USB_SERIAL_KEYSPAN=m +CONFIG_USB_SERIAL_KEYSPAN_MPR=y +CONFIG_USB_SERIAL_KEYSPAN_USA28=y +CONFIG_USB_SERIAL_KEYSPAN_USA28X=y +CONFIG_USB_SERIAL_KEYSPAN_USA28XA=y +CONFIG_USB_SERIAL_KEYSPAN_USA28XB=y +CONFIG_USB_SERIAL_KEYSPAN_USA19=y +CONFIG_USB_SERIAL_KEYSPAN_USA18X=y +CONFIG_USB_SERIAL_KEYSPAN_USA19W=y +CONFIG_USB_SERIAL_KEYSPAN_USA19QW=y +CONFIG_USB_SERIAL_KEYSPAN_USA19QI=y +CONFIG_USB_SERIAL_KEYSPAN_USA49W=y +CONFIG_USB_SERIAL_KEYSPAN_USA49WLC=y +CONFIG_USB_SERIAL_KLSI=m +CONFIG_USB_SERIAL_KOBIL_SCT=m +CONFIG_USB_SERIAL_MCT_U232=m +CONFIG_USB_SERIAL_PL2303=m +CONFIG_USB_SERIAL_SAFE=m +CONFIG_USB_SERIAL_SAFE_PADDED=y +CONFIG_USB_SERIAL_CYBERJACK=m +CONFIG_USB_SERIAL_XIRCOM=m +CONFIG_USB_SERIAL_OMNINET=m +CONFIG_USB_EZUSB=y + +# +# USB Miscellaneous drivers +# +CONFIG_USB_EMI62=m +# CONFIG_USB_EMI26 is not set +# CONFIG_USB_TIGL is not set +CONFIG_USB_AUERSWALD=m +CONFIG_USB_RIO500=m +CONFIG_USB_LEGOTOWER=m +CONFIG_USB_LCD=m +CONFIG_USB_LED=m +# CONFIG_USB_CYTHERM is not set +CONFIG_USB_PHIDGETSERVO=m +CONFIG_USB_TEST=m + +# +# USB ATM/DSL drivers +# +CONFIG_USB_ATM=m +CONFIG_USB_SPEEDTOUCH=m + +# +# USB Gadget Support +# +# CONFIG_USB_GADGET is not set + +# +# File systems +# +CONFIG_EXT2_FS=y +CONFIG_EXT2_FS_XATTR=y +CONFIG_EXT2_FS_POSIX_ACL=y +CONFIG_EXT2_FS_SECURITY=y +CONFIG_EXT3_FS=m +CONFIG_EXT3_FS_XATTR=y +CONFIG_EXT3_FS_POSIX_ACL=y +CONFIG_EXT3_FS_SECURITY=y +CONFIG_JBD=m +# CONFIG_JBD_DEBUG is not set +CONFIG_FS_MBCACHE=y +CONFIG_REISERFS_FS=m +# CONFIG_REISERFS_CHECK is not set +CONFIG_REISERFS_PROC_INFO=y +CONFIG_REISERFS_FS_XATTR=y +CONFIG_REISERFS_FS_POSIX_ACL=y +CONFIG_REISERFS_FS_SECURITY=y +CONFIG_JFS_FS=m +CONFIG_JFS_POSIX_ACL=y +# CONFIG_JFS_DEBUG is not set +# CONFIG_JFS_STATISTICS is not set +CONFIG_FS_POSIX_ACL=y +CONFIG_XFS_FS=m +# CONFIG_XFS_RT is not set +CONFIG_XFS_QUOTA=y +CONFIG_XFS_SECURITY=y +CONFIG_XFS_POSIX_ACL=y +CONFIG_MINIX_FS=m +CONFIG_ROMFS_FS=m +CONFIG_QUOTA=y +# CONFIG_QFMT_V1 is not set +CONFIG_QFMT_V2=y +CONFIG_QUOTACTL=y +CONFIG_AUTOFS_FS=m +CONFIG_AUTOFS4_FS=m + +# +# CD-ROM/DVD Filesystems +# +CONFIG_ISO9660_FS=y +CONFIG_JOLIET=y +CONFIG_ZISOFS=y +CONFIG_ZISOFS_FS=y +CONFIG_UDF_FS=m +CONFIG_UDF_NLS=y + +# +# DOS/FAT/NT Filesystems +# +CONFIG_FAT_FS=m +CONFIG_MSDOS_FS=m +CONFIG_VFAT_FS=m +CONFIG_FAT_DEFAULT_CODEPAGE=437 +CONFIG_FAT_DEFAULT_IOCHARSET="ascii" +# CONFIG_NTFS_FS is not set + +# +# Pseudo filesystems +# +CONFIG_PROC_FS=y +CONFIG_PROC_KCORE=y +CONFIG_SYSFS=y +# CONFIG_DEVFS_FS is not set +CONFIG_DEVPTS_FS_XATTR=y +CONFIG_DEVPTS_FS_SECURITY=y +CONFIG_TMPFS=y +CONFIG_TMPFS_XATTR=y +CONFIG_TMPFS_SECURITY=y +CONFIG_HUGETLBFS=y +CONFIG_HUGETLB_PAGE=y +CONFIG_RAMFS=y + +# +# Miscellaneous filesystems +# +# CONFIG_ADFS_FS is not set +CONFIG_AFFS_FS=m +CONFIG_HFS_FS=m +CONFIG_HFSPLUS_FS=m +CONFIG_BEFS_FS=m +# CONFIG_BEFS_DEBUG is not set +CONFIG_BFS_FS=m +CONFIG_EFS_FS=m +# CONFIG_JFFS_FS is not set +CONFIG_JFFS2_FS=m +CONFIG_JFFS2_FS_DEBUG=0 +CONFIG_JFFS2_FS_NAND=y +# CONFIG_JFFS2_COMPRESSION_OPTIONS is not set +CONFIG_JFFS2_ZLIB=y +CONFIG_JFFS2_RTIME=y +# CONFIG_JFFS2_RUBIN is not set +CONFIG_CRAMFS=m +CONFIG_VXFS_FS=m +# CONFIG_HPFS_FS is not set +CONFIG_QNX4FS_FS=m +# CONFIG_QNX4FS_RW is not set +CONFIG_SYSV_FS=m +CONFIG_UFS_FS=m +# CONFIG_UFS_FS_WRITE is not set + +# +# Network File Systems +# +CONFIG_NFS_FS=m +CONFIG_NFS_V3=y +CONFIG_NFS_V4=y +CONFIG_NFS_DIRECTIO=y +CONFIG_NFSD=m +CONFIG_NFSD_V3=y +CONFIG_NFSD_V4=y +CONFIG_NFSD_TCP=y +CONFIG_LOCKD=m +CONFIG_LOCKD_V4=y +CONFIG_EXPORTFS=m +CONFIG_SUNRPC=m +CONFIG_SUNRPC_GSS=m +CONFIG_RPCSEC_GSS_KRB5=m +CONFIG_RPCSEC_GSS_SPKM3=m +CONFIG_SMB_FS=m +# CONFIG_SMB_NLS_DEFAULT is not set +CONFIG_CIFS=m +# CONFIG_CIFS_STATS is not set +CONFIG_CIFS_XATTR=y +CONFIG_CIFS_POSIX=y +CONFIG_NCP_FS=m +CONFIG_NCPFS_PACKET_SIGNING=y +CONFIG_NCPFS_IOCTL_LOCKING=y +CONFIG_NCPFS_STRONG=y +CONFIG_NCPFS_NFS_NS=y +CONFIG_NCPFS_OS2_NS=y +CONFIG_NCPFS_SMALLDOS=y +CONFIG_NCPFS_NLS=y +CONFIG_NCPFS_EXTRAS=y +# CONFIG_CODA_FS is not set +# CONFIG_AFS_FS is not set + +# +# Partition Types +# +CONFIG_PARTITION_ADVANCED=y +# CONFIG_ACORN_PARTITION is not set +CONFIG_OSF_PARTITION=y +# CONFIG_AMIGA_PARTITION is not set +# CONFIG_ATARI_PARTITION is not set +CONFIG_MAC_PARTITION=y +CONFIG_MSDOS_PARTITION=y +CONFIG_BSD_DISKLABEL=y +CONFIG_MINIX_SUBPARTITION=y +CONFIG_SOLARIS_X86_PARTITION=y +CONFIG_UNIXWARE_DISKLABEL=y +# CONFIG_LDM_PARTITION is not set +CONFIG_SGI_PARTITION=y +# CONFIG_ULTRIX_PARTITION is not set +CONFIG_SUN_PARTITION=y +CONFIG_EFI_PARTITION=y + +# +# Native Language Support +# +CONFIG_NLS=y +CONFIG_NLS_DEFAULT="utf8" +CONFIG_NLS_CODEPAGE_437=y +CONFIG_NLS_CODEPAGE_737=m +CONFIG_NLS_CODEPAGE_775=m +CONFIG_NLS_CODEPAGE_850=m +CONFIG_NLS_CODEPAGE_852=m +CONFIG_NLS_CODEPAGE_855=m +CONFIG_NLS_CODEPAGE_857=m +CONFIG_NLS_CODEPAGE_860=m +CONFIG_NLS_CODEPAGE_861=m +CONFIG_NLS_CODEPAGE_862=m +CONFIG_NLS_CODEPAGE_863=m +CONFIG_NLS_CODEPAGE_864=m +CONFIG_NLS_CODEPAGE_865=m +CONFIG_NLS_CODEPAGE_866=m +CONFIG_NLS_CODEPAGE_869=m +CONFIG_NLS_CODEPAGE_936=m +CONFIG_NLS_CODEPAGE_950=m +CONFIG_NLS_CODEPAGE_932=m +CONFIG_NLS_CODEPAGE_949=m +CONFIG_NLS_CODEPAGE_874=m +CONFIG_NLS_ISO8859_8=m +CONFIG_NLS_CODEPAGE_1250=m +CONFIG_NLS_CODEPAGE_1251=m +CONFIG_NLS_ASCII=y +CONFIG_NLS_ISO8859_1=m +CONFIG_NLS_ISO8859_2=m +CONFIG_NLS_ISO8859_3=m +CONFIG_NLS_ISO8859_4=m +CONFIG_NLS_ISO8859_5=m +CONFIG_NLS_ISO8859_6=m +CONFIG_NLS_ISO8859_7=m +CONFIG_NLS_ISO8859_9=m +CONFIG_NLS_ISO8859_13=m +CONFIG_NLS_ISO8859_14=m +CONFIG_NLS_ISO8859_15=m +CONFIG_NLS_KOI8_R=m +CONFIG_NLS_KOI8_U=m +CONFIG_NLS_UTF8=m + +# +# Profiling support +# +CONFIG_PROFILING=y +CONFIG_OPROFILE=m + +# +# Kernel hacking +# +CONFIG_DEBUG_KERNEL=y +CONFIG_MAGIC_SYSRQ=y +# CONFIG_DEBUG_SLAB is not set +CONFIG_DEBUG_SPINLOCK=y +CONFIG_DEBUG_SPINLOCK_SLEEP=y +CONFIG_DEBUG_HIGHMEM=y +CONFIG_DEBUG_INFO=y +# CONFIG_FRAME_POINTER is not set +CONFIG_EARLY_PRINTK=y +CONFIG_DEBUG_STACKOVERFLOW=y +# CONFIG_KPROBES is not set +CONFIG_DEBUG_STACK_USAGE=y +# CONFIG_DEBUG_PAGEALLOC is not set +# CONFIG_SCHEDSTATS is not set + +# +# Security options +# +CONFIG_SECURITY=y +CONFIG_SECURITY_NETWORK=y +CONFIG_SECURITY_CAPABILITIES=y +# CONFIG_SECURITY_ROOTPLUG is not set +CONFIG_SECURITY_SELINUX=y +CONFIG_SECURITY_SELINUX_BOOTPARAM=y +CONFIG_SECURITY_SELINUX_BOOTPARAM_VALUE=1 +CONFIG_SECURITY_SELINUX_DISABLE=y +CONFIG_SECURITY_SELINUX_DEVELOP=y +CONFIG_SECURITY_SELINUX_AVC_STATS=y +# CONFIG_SECURITY_SELINUX_MLS is not set + +# +# Cryptographic options +# +CONFIG_CRYPTO=y +CONFIG_CRYPTO_HMAC=y +CONFIG_CRYPTO_NULL=m +CONFIG_CRYPTO_MD4=m +CONFIG_CRYPTO_MD5=m +CONFIG_CRYPTO_SHA1=y +CONFIG_CRYPTO_SHA256=m +CONFIG_CRYPTO_SHA512=m +CONFIG_CRYPTO_WP512=m +CONFIG_CRYPTO_DES=m +CONFIG_CRYPTO_BLOWFISH=m +CONFIG_CRYPTO_TWOFISH=m +CONFIG_CRYPTO_SERPENT=m +CONFIG_CRYPTO_AES_586=m +CONFIG_CRYPTO_CAST5=m +CONFIG_CRYPTO_CAST6=m +CONFIG_CRYPTO_TEA=m +CONFIG_CRYPTO_ARC4=m +CONFIG_CRYPTO_KHAZAD=m +CONFIG_CRYPTO_DEFLATE=m +CONFIG_CRYPTO_MICHAEL_MIC=m +CONFIG_CRYPTO_CRC32C=m +# CONFIG_CRYPTO_TEST is not set +CONFIG_CRYPTO_SIGNATURE=y +CONFIG_CRYPTO_SIGNATURE_DSA=y +CONFIG_CRYPTO_MPILIB=y + +# +# Library routines +# +CONFIG_CRC_CCITT=m +CONFIG_CRC32=y +CONFIG_LIBCRC32C=m +CONFIG_ZLIB_INFLATE=y +CONFIG_ZLIB_DEFLATE=m +CONFIG_X86_BIOS_REBOOT=y +CONFIG_PC=y diff --git a/crypto/aes.c b/crypto/aes.c index 13b6daa37..94b89a9c7 100644 --- a/crypto/aes.c +++ b/crypto/aes.c @@ -102,10 +102,10 @@ struct aes_ctx { #define E_KEY ctx->E #define D_KEY ctx->D -static u8 pow_tab[256]; -static u8 log_tab[256]; -static u8 sbx_tab[256]; -static u8 isb_tab[256]; +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]; static u32 ft_tab[4][256]; static u32 it_tab[4][256]; @@ -113,7 +113,7 @@ static u32 it_tab[4][256]; static u32 fl_tab[4][256]; static u32 il_tab[4][256]; -static inline u8 +static inline u8 __init f_mult (u8 a, u8 b) { u8 aa = log_tab[a], cc = aa + log_tab[b]; @@ -153,14 +153,14 @@ f_mult (u8 a, u8 b) il_tab[2][byte(bi[(n + 2) & 3],2)] ^ \ il_tab[3][byte(bi[(n + 1) & 3],3)] ^ *(k + n) -static void +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 prmitive + 0x011b as modular polynomial - the simplest primitive root is 0x03, used here to generate the tables */ for (i = 0, p = 1; i < 256; ++i) { diff --git a/crypto/arc4.c b/crypto/arc4.c index b66eb1f0d..9efbcaae8 100644 --- a/crypto/arc4.c +++ b/crypto/arc4.c @@ -3,7 +3,7 @@ * * ARC4 Cipher Algorithm * - * Jon Oberheide + * Jon Oberheide * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -100,4 +100,4 @@ module_exit(arc4_exit); MODULE_LICENSE("GPL"); MODULE_DESCRIPTION("ARC4 Cipher Algorithm"); -MODULE_AUTHOR("Jon Oberheide "); +MODULE_AUTHOR("Jon Oberheide "); diff --git a/crypto/blowfish.c b/crypto/blowfish.c index 8fd18dcdb..b6bea821c 100644 --- a/crypto/blowfish.c +++ b/crypto/blowfish.c @@ -3,9 +3,9 @@ * * Blowfish Cipher Algorithm, by Bruce Schneier. * http://www.counterpane.com/blowfish.html - * - * Adapated from Kerneli implementation. - * + * + * Adapted from Kerneli implementation. + * * Copyright (c) Herbert Valerio Riedel * Copyright (c) Kyle McMartin * Copyright (c) 2002 James Morris diff --git a/crypto/internal.h b/crypto/internal.h index b51fad1f9..e68e43886 100644 --- a/crypto/internal.h +++ b/crypto/internal.h @@ -17,7 +17,6 @@ #include #include #include -#include #include extern enum km_type crypto_km_types[]; diff --git a/crypto/scatterwalk.c b/crypto/scatterwalk.c index 540dd9ac1..f6a5c9e5b 100644 --- a/crypto/scatterwalk.c +++ b/crypto/scatterwalk.c @@ -70,7 +70,7 @@ static void scatterwalk_pagedone(struct scatter_walk *walk, int out, { /* walk->data may be pointing the first byte of the next page; however, we know we transfered at least one byte. So, - walk->data - 1 will be a virutual address in the mapped page. */ + walk->data - 1 will be a virtual address in the mapped page. */ if (out) flush_dcache_page(walk->page); diff --git a/crypto/signature/key.h b/crypto/signature/key.h index a1ecccb9a..72979680e 100644 --- a/crypto/signature/key.h +++ b/crypto/signature/key.h @@ -1,7 +1,7 @@ - +const int ksign_def_public_key_size = 0; /* automatically generated by bin2hex */ static unsigned char ksign_def_public_key[] __initdata = { - 0x00, 0x09, 0x00, 0x00, 0x00, 0x00 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; diff --git a/crypto/signature/ksign-keyring.c b/crypto/signature/ksign-keyring.c index ee288b656..a1bbd85f0 100644 --- a/crypto/signature/ksign-keyring.c +++ b/crypto/signature/ksign-keyring.c @@ -30,8 +30,8 @@ static int add_keyblock_key(struct ksign_public_key *pk, void *data) { printk("- Added public key %X%X\n", pk->keyid[0], pk->keyid[1]); -// if (pk->expiredate && pk->expiredate < xtime.tv_sec) -// printk(" - public key has expired\n"); + if (pk->expiredate && pk->expiredate < xtime.tv_sec) + printk(" - public key has expired\n"); if (pk->timestamp > xtime.tv_sec ) printk(" - key was been created %lu seconds in future\n", diff --git a/crypto/signature/ksign-parse.c b/crypto/signature/ksign-parse.c index acc2d1f4b..45b671ee7 100644 --- a/crypto/signature/ksign-parse.c +++ b/crypto/signature/ksign-parse.c @@ -447,7 +447,7 @@ static int ksign_parse_signature(const uint8_t *datap, const uint8_t *endp, } for (i = 0; i < DSA_NSIG; i++) { - size_t remaining = endp - datap; + unsigned remaining = endp - datap; sig->data[i] = mpi_read_from_buffer(datap, &remaining); datap += remaining; } diff --git a/crypto/signature/ksign-publickey.c b/crypto/signature/ksign-publickey.c index 762e2a838..276be079c 100644 --- a/crypto/signature/ksign-publickey.c +++ b/crypto/signature/ksign-publickey.c @@ -9,7 +9,7 @@ static int __init ksign_init(void) printk("ksign: Installing public key data\n"); rc = ksign_load_keyring_from_buffer(ksign_def_public_key, - sizeof(ksign_def_public_key)); + ksign_def_public_key_size); if (rc < 0) printk("Unable to load default keyring: error=%d\n", -rc); diff --git a/crypto/tea.c b/crypto/tea.c index bf943294d..588a143c1 100644 --- a/crypto/tea.c +++ b/crypto/tea.c @@ -154,7 +154,7 @@ static void xtea_encrypt(void *ctx_arg, u8 *dst, const u8 *src) while (sum != limit) { y += (z << 4 ^ z >> 5) + (z ^ sum) + ctx->KEY[sum&3]; - sum += TEA_DELTA; + sum += XTEA_DELTA; z += (y << 4 ^ y >> 5) + (y ^ sum) + ctx->KEY[sum>>11 &3]; } diff --git a/crypto/wp512.c b/crypto/wp512.c new file mode 100644 index 000000000..fd6e20e1f --- /dev/null +++ b/crypto/wp512.c @@ -0,0 +1,1208 @@ +/* + * Cryptographic API. + * + * Whirlpool hashing Algorithm + * + * The Whirlpool algorithm was developed by Paulo S. L. M. Barreto and + * Vincent Rijmen. It has been selected as one of cryptographic + * primitives by the NESSIE project http://www.cryptonessie.org/ + * + * The original authors have disclaimed all copyright interest in this + * code and thus put it in the public domain. The subsequent authors + * have put this under the GNU General Public License. + * + * By Aaron Grothe ajgrothe@yahoo.com, August 23, 2004 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the 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 +#include +#include +#include +#include + +#define WP512_DIGEST_SIZE 64 +#define WP384_DIGEST_SIZE 48 +#define WP256_DIGEST_SIZE 32 + +#define WP512_BLOCK_SIZE 64 +#define WP512_LENGTHBYTES 32 + +#define WHIRLPOOL_ROUNDS 10 + +struct wp512_ctx { + u8 bitLength[WP512_LENGTHBYTES]; + u8 buffer[WP512_BLOCK_SIZE]; + int bufferBits; + int bufferPos; + u64 hash[WP512_DIGEST_SIZE/8]; +}; + +/* + * Though Whirlpool is endianness-neutral, the encryption tables are listed + * in BIG-ENDIAN format, which is adopted throughout this implementation + * (but little-endian notation would be equally suitable if consistently + * employed). + */ + +static const u64 C0[256] = { + 0x18186018c07830d8ULL, 0x23238c2305af4626ULL, 0xc6c63fc67ef991b8ULL, + 0xe8e887e8136fcdfbULL, 0x878726874ca113cbULL, 0xb8b8dab8a9626d11ULL, + 0x0101040108050209ULL, 0x4f4f214f426e9e0dULL, 0x3636d836adee6c9bULL, + 0xa6a6a2a6590451ffULL, 0xd2d26fd2debdb90cULL, 0xf5f5f3f5fb06f70eULL, + 0x7979f979ef80f296ULL, 0x6f6fa16f5fcede30ULL, 0x91917e91fcef3f6dULL, + 0x52525552aa07a4f8ULL, 0x60609d6027fdc047ULL, 0xbcbccabc89766535ULL, + 0x9b9b569baccd2b37ULL, 0x8e8e028e048c018aULL, 0xa3a3b6a371155bd2ULL, + 0x0c0c300c603c186cULL, 0x7b7bf17bff8af684ULL, 0x3535d435b5e16a80ULL, + 0x1d1d741de8693af5ULL, 0xe0e0a7e05347ddb3ULL, 0xd7d77bd7f6acb321ULL, + 0xc2c22fc25eed999cULL, 0x2e2eb82e6d965c43ULL, 0x4b4b314b627a9629ULL, + 0xfefedffea321e15dULL, 0x575741578216aed5ULL, 0x15155415a8412abdULL, + 0x7777c1779fb6eee8ULL, 0x3737dc37a5eb6e92ULL, 0xe5e5b3e57b56d79eULL, + 0x9f9f469f8cd92313ULL, 0xf0f0e7f0d317fd23ULL, 0x4a4a354a6a7f9420ULL, + 0xdada4fda9e95a944ULL, 0x58587d58fa25b0a2ULL, 0xc9c903c906ca8fcfULL, + 0x2929a429558d527cULL, 0x0a0a280a5022145aULL, 0xb1b1feb1e14f7f50ULL, + 0xa0a0baa0691a5dc9ULL, 0x6b6bb16b7fdad614ULL, 0x85852e855cab17d9ULL, + 0xbdbdcebd8173673cULL, 0x5d5d695dd234ba8fULL, 0x1010401080502090ULL, + 0xf4f4f7f4f303f507ULL, 0xcbcb0bcb16c08bddULL, 0x3e3ef83eedc67cd3ULL, + 0x0505140528110a2dULL, 0x676781671fe6ce78ULL, 0xe4e4b7e47353d597ULL, + 0x27279c2725bb4e02ULL, 0x4141194132588273ULL, 0x8b8b168b2c9d0ba7ULL, + 0xa7a7a6a7510153f6ULL, 0x7d7de97dcf94fab2ULL, 0x95956e95dcfb3749ULL, + 0xd8d847d88e9fad56ULL, 0xfbfbcbfb8b30eb70ULL, 0xeeee9fee2371c1cdULL, + 0x7c7ced7cc791f8bbULL, 0x6666856617e3cc71ULL, 0xdddd53dda68ea77bULL, + 0x17175c17b84b2eafULL, 0x4747014702468e45ULL, 0x9e9e429e84dc211aULL, + 0xcaca0fca1ec589d4ULL, 0x2d2db42d75995a58ULL, 0xbfbfc6bf9179632eULL, + 0x07071c07381b0e3fULL, 0xadad8ead012347acULL, 0x5a5a755aea2fb4b0ULL, + 0x838336836cb51befULL, 0x3333cc3385ff66b6ULL, 0x636391633ff2c65cULL, + 0x02020802100a0412ULL, 0xaaaa92aa39384993ULL, 0x7171d971afa8e2deULL, + 0xc8c807c80ecf8dc6ULL, 0x19196419c87d32d1ULL, 0x494939497270923bULL, + 0xd9d943d9869aaf5fULL, 0xf2f2eff2c31df931ULL, 0xe3e3abe34b48dba8ULL, + 0x5b5b715be22ab6b9ULL, 0x88881a8834920dbcULL, 0x9a9a529aa4c8293eULL, + 0x262698262dbe4c0bULL, 0x3232c8328dfa64bfULL, 0xb0b0fab0e94a7d59ULL, + 0xe9e983e91b6acff2ULL, 0x0f0f3c0f78331e77ULL, 0xd5d573d5e6a6b733ULL, + 0x80803a8074ba1df4ULL, 0xbebec2be997c6127ULL, 0xcdcd13cd26de87ebULL, + 0x3434d034bde46889ULL, 0x48483d487a759032ULL, 0xffffdbffab24e354ULL, + 0x7a7af57af78ff48dULL, 0x90907a90f4ea3d64ULL, 0x5f5f615fc23ebe9dULL, + 0x202080201da0403dULL, 0x6868bd6867d5d00fULL, 0x1a1a681ad07234caULL, + 0xaeae82ae192c41b7ULL, 0xb4b4eab4c95e757dULL, 0x54544d549a19a8ceULL, + 0x93937693ece53b7fULL, 0x222288220daa442fULL, 0x64648d6407e9c863ULL, + 0xf1f1e3f1db12ff2aULL, 0x7373d173bfa2e6ccULL, 0x12124812905a2482ULL, + 0x40401d403a5d807aULL, 0x0808200840281048ULL, 0xc3c32bc356e89b95ULL, + 0xecec97ec337bc5dfULL, 0xdbdb4bdb9690ab4dULL, 0xa1a1bea1611f5fc0ULL, + 0x8d8d0e8d1c830791ULL, 0x3d3df43df5c97ac8ULL, 0x97976697ccf1335bULL, + 0x0000000000000000ULL, 0xcfcf1bcf36d483f9ULL, 0x2b2bac2b4587566eULL, + 0x7676c57697b3ece1ULL, 0x8282328264b019e6ULL, 0xd6d67fd6fea9b128ULL, + 0x1b1b6c1bd87736c3ULL, 0xb5b5eeb5c15b7774ULL, 0xafaf86af112943beULL, + 0x6a6ab56a77dfd41dULL, 0x50505d50ba0da0eaULL, 0x45450945124c8a57ULL, + 0xf3f3ebf3cb18fb38ULL, 0x3030c0309df060adULL, 0xefef9bef2b74c3c4ULL, + 0x3f3ffc3fe5c37edaULL, 0x55554955921caac7ULL, 0xa2a2b2a2791059dbULL, + 0xeaea8fea0365c9e9ULL, 0x656589650fecca6aULL, 0xbabad2bab9686903ULL, + 0x2f2fbc2f65935e4aULL, 0xc0c027c04ee79d8eULL, 0xdede5fdebe81a160ULL, + 0x1c1c701ce06c38fcULL, 0xfdfdd3fdbb2ee746ULL, 0x4d4d294d52649a1fULL, + 0x92927292e4e03976ULL, 0x7575c9758fbceafaULL, 0x06061806301e0c36ULL, + 0x8a8a128a249809aeULL, 0xb2b2f2b2f940794bULL, 0xe6e6bfe66359d185ULL, + 0x0e0e380e70361c7eULL, 0x1f1f7c1ff8633ee7ULL, 0x6262956237f7c455ULL, + 0xd4d477d4eea3b53aULL, 0xa8a89aa829324d81ULL, 0x96966296c4f43152ULL, + 0xf9f9c3f99b3aef62ULL, 0xc5c533c566f697a3ULL, 0x2525942535b14a10ULL, + 0x59597959f220b2abULL, 0x84842a8454ae15d0ULL, 0x7272d572b7a7e4c5ULL, + 0x3939e439d5dd72ecULL, 0x4c4c2d4c5a619816ULL, 0x5e5e655eca3bbc94ULL, + 0x7878fd78e785f09fULL, 0x3838e038ddd870e5ULL, 0x8c8c0a8c14860598ULL, + 0xd1d163d1c6b2bf17ULL, 0xa5a5aea5410b57e4ULL, 0xe2e2afe2434dd9a1ULL, + 0x616199612ff8c24eULL, 0xb3b3f6b3f1457b42ULL, 0x2121842115a54234ULL, + 0x9c9c4a9c94d62508ULL, 0x1e1e781ef0663ceeULL, 0x4343114322528661ULL, + 0xc7c73bc776fc93b1ULL, 0xfcfcd7fcb32be54fULL, 0x0404100420140824ULL, + 0x51515951b208a2e3ULL, 0x99995e99bcc72f25ULL, 0x6d6da96d4fc4da22ULL, + 0x0d0d340d68391a65ULL, 0xfafacffa8335e979ULL, 0xdfdf5bdfb684a369ULL, + 0x7e7ee57ed79bfca9ULL, 0x242490243db44819ULL, 0x3b3bec3bc5d776feULL, + 0xabab96ab313d4b9aULL, 0xcece1fce3ed181f0ULL, 0x1111441188552299ULL, + 0x8f8f068f0c890383ULL, 0x4e4e254e4a6b9c04ULL, 0xb7b7e6b7d1517366ULL, + 0xebeb8beb0b60cbe0ULL, 0x3c3cf03cfdcc78c1ULL, 0x81813e817cbf1ffdULL, + 0x94946a94d4fe3540ULL, 0xf7f7fbf7eb0cf31cULL, 0xb9b9deb9a1676f18ULL, + 0x13134c13985f268bULL, 0x2c2cb02c7d9c5851ULL, 0xd3d36bd3d6b8bb05ULL, + 0xe7e7bbe76b5cd38cULL, 0x6e6ea56e57cbdc39ULL, 0xc4c437c46ef395aaULL, + 0x03030c03180f061bULL, 0x565645568a13acdcULL, 0x44440d441a49885eULL, + 0x7f7fe17fdf9efea0ULL, 0xa9a99ea921374f88ULL, 0x2a2aa82a4d825467ULL, + 0xbbbbd6bbb16d6b0aULL, 0xc1c123c146e29f87ULL, 0x53535153a202a6f1ULL, + 0xdcdc57dcae8ba572ULL, 0x0b0b2c0b58271653ULL, 0x9d9d4e9d9cd32701ULL, + 0x6c6cad6c47c1d82bULL, 0x3131c43195f562a4ULL, 0x7474cd7487b9e8f3ULL, + 0xf6f6fff6e309f115ULL, 0x464605460a438c4cULL, 0xacac8aac092645a5ULL, + 0x89891e893c970fb5ULL, 0x14145014a04428b4ULL, 0xe1e1a3e15b42dfbaULL, + 0x16165816b04e2ca6ULL, 0x3a3ae83acdd274f7ULL, 0x6969b9696fd0d206ULL, + 0x09092409482d1241ULL, 0x7070dd70a7ade0d7ULL, 0xb6b6e2b6d954716fULL, + 0xd0d067d0ceb7bd1eULL, 0xeded93ed3b7ec7d6ULL, 0xcccc17cc2edb85e2ULL, + 0x424215422a578468ULL, 0x98985a98b4c22d2cULL, 0xa4a4aaa4490e55edULL, + 0x2828a0285d885075ULL, 0x5c5c6d5cda31b886ULL, 0xf8f8c7f8933fed6bULL, + 0x8686228644a411c2ULL, +}; + +static const u64 C1[256] = { + 0xd818186018c07830ULL, 0x2623238c2305af46ULL, 0xb8c6c63fc67ef991ULL, + 0xfbe8e887e8136fcdULL, 0xcb878726874ca113ULL, 0x11b8b8dab8a9626dULL, + 0x0901010401080502ULL, 0x0d4f4f214f426e9eULL, 0x9b3636d836adee6cULL, + 0xffa6a6a2a6590451ULL, 0x0cd2d26fd2debdb9ULL, 0x0ef5f5f3f5fb06f7ULL, + 0x967979f979ef80f2ULL, 0x306f6fa16f5fcedeULL, 0x6d91917e91fcef3fULL, + 0xf852525552aa07a4ULL, 0x4760609d6027fdc0ULL, 0x35bcbccabc897665ULL, + 0x379b9b569baccd2bULL, 0x8a8e8e028e048c01ULL, 0xd2a3a3b6a371155bULL, + 0x6c0c0c300c603c18ULL, 0x847b7bf17bff8af6ULL, 0x803535d435b5e16aULL, + 0xf51d1d741de8693aULL, 0xb3e0e0a7e05347ddULL, 0x21d7d77bd7f6acb3ULL, + 0x9cc2c22fc25eed99ULL, 0x432e2eb82e6d965cULL, 0x294b4b314b627a96ULL, + 0x5dfefedffea321e1ULL, 0xd5575741578216aeULL, 0xbd15155415a8412aULL, + 0xe87777c1779fb6eeULL, 0x923737dc37a5eb6eULL, 0x9ee5e5b3e57b56d7ULL, + 0x139f9f469f8cd923ULL, 0x23f0f0e7f0d317fdULL, 0x204a4a354a6a7f94ULL, + 0x44dada4fda9e95a9ULL, 0xa258587d58fa25b0ULL, 0xcfc9c903c906ca8fULL, + 0x7c2929a429558d52ULL, 0x5a0a0a280a502214ULL, 0x50b1b1feb1e14f7fULL, + 0xc9a0a0baa0691a5dULL, 0x146b6bb16b7fdad6ULL, 0xd985852e855cab17ULL, + 0x3cbdbdcebd817367ULL, 0x8f5d5d695dd234baULL, 0x9010104010805020ULL, + 0x07f4f4f7f4f303f5ULL, 0xddcbcb0bcb16c08bULL, 0xd33e3ef83eedc67cULL, + 0x2d0505140528110aULL, 0x78676781671fe6ceULL, 0x97e4e4b7e47353d5ULL, + 0x0227279c2725bb4eULL, 0x7341411941325882ULL, 0xa78b8b168b2c9d0bULL, + 0xf6a7a7a6a7510153ULL, 0xb27d7de97dcf94faULL, 0x4995956e95dcfb37ULL, + 0x56d8d847d88e9fadULL, 0x70fbfbcbfb8b30ebULL, 0xcdeeee9fee2371c1ULL, + 0xbb7c7ced7cc791f8ULL, 0x716666856617e3ccULL, 0x7bdddd53dda68ea7ULL, + 0xaf17175c17b84b2eULL, 0x454747014702468eULL, 0x1a9e9e429e84dc21ULL, + 0xd4caca0fca1ec589ULL, 0x582d2db42d75995aULL, 0x2ebfbfc6bf917963ULL, + 0x3f07071c07381b0eULL, 0xacadad8ead012347ULL, 0xb05a5a755aea2fb4ULL, + 0xef838336836cb51bULL, 0xb63333cc3385ff66ULL, 0x5c636391633ff2c6ULL, + 0x1202020802100a04ULL, 0x93aaaa92aa393849ULL, 0xde7171d971afa8e2ULL, + 0xc6c8c807c80ecf8dULL, 0xd119196419c87d32ULL, 0x3b49493949727092ULL, + 0x5fd9d943d9869aafULL, 0x31f2f2eff2c31df9ULL, 0xa8e3e3abe34b48dbULL, + 0xb95b5b715be22ab6ULL, 0xbc88881a8834920dULL, 0x3e9a9a529aa4c829ULL, + 0x0b262698262dbe4cULL, 0xbf3232c8328dfa64ULL, 0x59b0b0fab0e94a7dULL, + 0xf2e9e983e91b6acfULL, 0x770f0f3c0f78331eULL, 0x33d5d573d5e6a6b7ULL, + 0xf480803a8074ba1dULL, 0x27bebec2be997c61ULL, 0xebcdcd13cd26de87ULL, + 0x893434d034bde468ULL, 0x3248483d487a7590ULL, 0x54ffffdbffab24e3ULL, + 0x8d7a7af57af78ff4ULL, 0x6490907a90f4ea3dULL, 0x9d5f5f615fc23ebeULL, + 0x3d202080201da040ULL, 0x0f6868bd6867d5d0ULL, 0xca1a1a681ad07234ULL, + 0xb7aeae82ae192c41ULL, 0x7db4b4eab4c95e75ULL, 0xce54544d549a19a8ULL, + 0x7f93937693ece53bULL, 0x2f222288220daa44ULL, 0x6364648d6407e9c8ULL, + 0x2af1f1e3f1db12ffULL, 0xcc7373d173bfa2e6ULL, 0x8212124812905a24ULL, + 0x7a40401d403a5d80ULL, 0x4808082008402810ULL, 0x95c3c32bc356e89bULL, + 0xdfecec97ec337bc5ULL, 0x4ddbdb4bdb9690abULL, 0xc0a1a1bea1611f5fULL, + 0x918d8d0e8d1c8307ULL, 0xc83d3df43df5c97aULL, 0x5b97976697ccf133ULL, + 0x0000000000000000ULL, 0xf9cfcf1bcf36d483ULL, 0x6e2b2bac2b458756ULL, + 0xe17676c57697b3ecULL, 0xe68282328264b019ULL, 0x28d6d67fd6fea9b1ULL, + 0xc31b1b6c1bd87736ULL, 0x74b5b5eeb5c15b77ULL, 0xbeafaf86af112943ULL, + 0x1d6a6ab56a77dfd4ULL, 0xea50505d50ba0da0ULL, 0x5745450945124c8aULL, + 0x38f3f3ebf3cb18fbULL, 0xad3030c0309df060ULL, 0xc4efef9bef2b74c3ULL, + 0xda3f3ffc3fe5c37eULL, 0xc755554955921caaULL, 0xdba2a2b2a2791059ULL, + 0xe9eaea8fea0365c9ULL, 0x6a656589650feccaULL, 0x03babad2bab96869ULL, + 0x4a2f2fbc2f65935eULL, 0x8ec0c027c04ee79dULL, 0x60dede5fdebe81a1ULL, + 0xfc1c1c701ce06c38ULL, 0x46fdfdd3fdbb2ee7ULL, 0x1f4d4d294d52649aULL, + 0x7692927292e4e039ULL, 0xfa7575c9758fbceaULL, 0x3606061806301e0cULL, + 0xae8a8a128a249809ULL, 0x4bb2b2f2b2f94079ULL, 0x85e6e6bfe66359d1ULL, + 0x7e0e0e380e70361cULL, 0xe71f1f7c1ff8633eULL, 0x556262956237f7c4ULL, + 0x3ad4d477d4eea3b5ULL, 0x81a8a89aa829324dULL, 0x5296966296c4f431ULL, + 0x62f9f9c3f99b3aefULL, 0xa3c5c533c566f697ULL, 0x102525942535b14aULL, + 0xab59597959f220b2ULL, 0xd084842a8454ae15ULL, 0xc57272d572b7a7e4ULL, + 0xec3939e439d5dd72ULL, 0x164c4c2d4c5a6198ULL, 0x945e5e655eca3bbcULL, + 0x9f7878fd78e785f0ULL, 0xe53838e038ddd870ULL, 0x988c8c0a8c148605ULL, + 0x17d1d163d1c6b2bfULL, 0xe4a5a5aea5410b57ULL, 0xa1e2e2afe2434dd9ULL, + 0x4e616199612ff8c2ULL, 0x42b3b3f6b3f1457bULL, 0x342121842115a542ULL, + 0x089c9c4a9c94d625ULL, 0xee1e1e781ef0663cULL, 0x6143431143225286ULL, + 0xb1c7c73bc776fc93ULL, 0x4ffcfcd7fcb32be5ULL, 0x2404041004201408ULL, + 0xe351515951b208a2ULL, 0x2599995e99bcc72fULL, 0x226d6da96d4fc4daULL, + 0x650d0d340d68391aULL, 0x79fafacffa8335e9ULL, 0x69dfdf5bdfb684a3ULL, + 0xa97e7ee57ed79bfcULL, 0x19242490243db448ULL, 0xfe3b3bec3bc5d776ULL, + 0x9aabab96ab313d4bULL, 0xf0cece1fce3ed181ULL, 0x9911114411885522ULL, + 0x838f8f068f0c8903ULL, 0x044e4e254e4a6b9cULL, 0x66b7b7e6b7d15173ULL, + 0xe0ebeb8beb0b60cbULL, 0xc13c3cf03cfdcc78ULL, 0xfd81813e817cbf1fULL, + 0x4094946a94d4fe35ULL, 0x1cf7f7fbf7eb0cf3ULL, 0x18b9b9deb9a1676fULL, + 0x8b13134c13985f26ULL, 0x512c2cb02c7d9c58ULL, 0x05d3d36bd3d6b8bbULL, + 0x8ce7e7bbe76b5cd3ULL, 0x396e6ea56e57cbdcULL, 0xaac4c437c46ef395ULL, + 0x1b03030c03180f06ULL, 0xdc565645568a13acULL, 0x5e44440d441a4988ULL, + 0xa07f7fe17fdf9efeULL, 0x88a9a99ea921374fULL, 0x672a2aa82a4d8254ULL, + 0x0abbbbd6bbb16d6bULL, 0x87c1c123c146e29fULL, 0xf153535153a202a6ULL, + 0x72dcdc57dcae8ba5ULL, 0x530b0b2c0b582716ULL, 0x019d9d4e9d9cd327ULL, + 0x2b6c6cad6c47c1d8ULL, 0xa43131c43195f562ULL, 0xf37474cd7487b9e8ULL, + 0x15f6f6fff6e309f1ULL, 0x4c464605460a438cULL, 0xa5acac8aac092645ULL, + 0xb589891e893c970fULL, 0xb414145014a04428ULL, 0xbae1e1a3e15b42dfULL, + 0xa616165816b04e2cULL, 0xf73a3ae83acdd274ULL, 0x066969b9696fd0d2ULL, + 0x4109092409482d12ULL, 0xd77070dd70a7ade0ULL, 0x6fb6b6e2b6d95471ULL, + 0x1ed0d067d0ceb7bdULL, 0xd6eded93ed3b7ec7ULL, 0xe2cccc17cc2edb85ULL, + 0x68424215422a5784ULL, 0x2c98985a98b4c22dULL, 0xeda4a4aaa4490e55ULL, + 0x752828a0285d8850ULL, 0x865c5c6d5cda31b8ULL, 0x6bf8f8c7f8933fedULL, + 0xc28686228644a411ULL, +}; + +static const u64 C2[256] = { + 0x30d818186018c078ULL, 0x462623238c2305afULL, 0x91b8c6c63fc67ef9ULL, + 0xcdfbe8e887e8136fULL, 0x13cb878726874ca1ULL, 0x6d11b8b8dab8a962ULL, + 0x0209010104010805ULL, 0x9e0d4f4f214f426eULL, 0x6c9b3636d836adeeULL, + 0x51ffa6a6a2a65904ULL, 0xb90cd2d26fd2debdULL, 0xf70ef5f5f3f5fb06ULL, + 0xf2967979f979ef80ULL, 0xde306f6fa16f5fceULL, 0x3f6d91917e91fcefULL, + 0xa4f852525552aa07ULL, 0xc04760609d6027fdULL, 0x6535bcbccabc8976ULL, + 0x2b379b9b569baccdULL, 0x018a8e8e028e048cULL, 0x5bd2a3a3b6a37115ULL, + 0x186c0c0c300c603cULL, 0xf6847b7bf17bff8aULL, 0x6a803535d435b5e1ULL, + 0x3af51d1d741de869ULL, 0xddb3e0e0a7e05347ULL, 0xb321d7d77bd7f6acULL, + 0x999cc2c22fc25eedULL, 0x5c432e2eb82e6d96ULL, 0x96294b4b314b627aULL, + 0xe15dfefedffea321ULL, 0xaed5575741578216ULL, 0x2abd15155415a841ULL, + 0xeee87777c1779fb6ULL, 0x6e923737dc37a5ebULL, 0xd79ee5e5b3e57b56ULL, + 0x23139f9f469f8cd9ULL, 0xfd23f0f0e7f0d317ULL, 0x94204a4a354a6a7fULL, + 0xa944dada4fda9e95ULL, 0xb0a258587d58fa25ULL, 0x8fcfc9c903c906caULL, + 0x527c2929a429558dULL, 0x145a0a0a280a5022ULL, 0x7f50b1b1feb1e14fULL, + 0x5dc9a0a0baa0691aULL, 0xd6146b6bb16b7fdaULL, 0x17d985852e855cabULL, + 0x673cbdbdcebd8173ULL, 0xba8f5d5d695dd234ULL, 0x2090101040108050ULL, + 0xf507f4f4f7f4f303ULL, 0x8bddcbcb0bcb16c0ULL, 0x7cd33e3ef83eedc6ULL, + 0x0a2d050514052811ULL, 0xce78676781671fe6ULL, 0xd597e4e4b7e47353ULL, + 0x4e0227279c2725bbULL, 0x8273414119413258ULL, 0x0ba78b8b168b2c9dULL, + 0x53f6a7a7a6a75101ULL, 0xfab27d7de97dcf94ULL, 0x374995956e95dcfbULL, + 0xad56d8d847d88e9fULL, 0xeb70fbfbcbfb8b30ULL, 0xc1cdeeee9fee2371ULL, + 0xf8bb7c7ced7cc791ULL, 0xcc716666856617e3ULL, 0xa77bdddd53dda68eULL, + 0x2eaf17175c17b84bULL, 0x8e45474701470246ULL, 0x211a9e9e429e84dcULL, + 0x89d4caca0fca1ec5ULL, 0x5a582d2db42d7599ULL, 0x632ebfbfc6bf9179ULL, + 0x0e3f07071c07381bULL, 0x47acadad8ead0123ULL, 0xb4b05a5a755aea2fULL, + 0x1bef838336836cb5ULL, 0x66b63333cc3385ffULL, 0xc65c636391633ff2ULL, + 0x041202020802100aULL, 0x4993aaaa92aa3938ULL, 0xe2de7171d971afa8ULL, + 0x8dc6c8c807c80ecfULL, 0x32d119196419c87dULL, 0x923b494939497270ULL, + 0xaf5fd9d943d9869aULL, 0xf931f2f2eff2c31dULL, 0xdba8e3e3abe34b48ULL, + 0xb6b95b5b715be22aULL, 0x0dbc88881a883492ULL, 0x293e9a9a529aa4c8ULL, + 0x4c0b262698262dbeULL, 0x64bf3232c8328dfaULL, 0x7d59b0b0fab0e94aULL, + 0xcff2e9e983e91b6aULL, 0x1e770f0f3c0f7833ULL, 0xb733d5d573d5e6a6ULL, + 0x1df480803a8074baULL, 0x6127bebec2be997cULL, 0x87ebcdcd13cd26deULL, + 0x68893434d034bde4ULL, 0x903248483d487a75ULL, 0xe354ffffdbffab24ULL, + 0xf48d7a7af57af78fULL, 0x3d6490907a90f4eaULL, 0xbe9d5f5f615fc23eULL, + 0x403d202080201da0ULL, 0xd00f6868bd6867d5ULL, 0x34ca1a1a681ad072ULL, + 0x41b7aeae82ae192cULL, 0x757db4b4eab4c95eULL, 0xa8ce54544d549a19ULL, + 0x3b7f93937693ece5ULL, 0x442f222288220daaULL, 0xc86364648d6407e9ULL, + 0xff2af1f1e3f1db12ULL, 0xe6cc7373d173bfa2ULL, 0x248212124812905aULL, + 0x807a40401d403a5dULL, 0x1048080820084028ULL, 0x9b95c3c32bc356e8ULL, + 0xc5dfecec97ec337bULL, 0xab4ddbdb4bdb9690ULL, 0x5fc0a1a1bea1611fULL, + 0x07918d8d0e8d1c83ULL, 0x7ac83d3df43df5c9ULL, 0x335b97976697ccf1ULL, + 0x0000000000000000ULL, 0x83f9cfcf1bcf36d4ULL, 0x566e2b2bac2b4587ULL, + 0xece17676c57697b3ULL, 0x19e68282328264b0ULL, 0xb128d6d67fd6fea9ULL, + 0x36c31b1b6c1bd877ULL, 0x7774b5b5eeb5c15bULL, 0x43beafaf86af1129ULL, + 0xd41d6a6ab56a77dfULL, 0xa0ea50505d50ba0dULL, 0x8a5745450945124cULL, + 0xfb38f3f3ebf3cb18ULL, 0x60ad3030c0309df0ULL, 0xc3c4efef9bef2b74ULL, + 0x7eda3f3ffc3fe5c3ULL, 0xaac755554955921cULL, 0x59dba2a2b2a27910ULL, + 0xc9e9eaea8fea0365ULL, 0xca6a656589650fecULL, 0x6903babad2bab968ULL, + 0x5e4a2f2fbc2f6593ULL, 0x9d8ec0c027c04ee7ULL, 0xa160dede5fdebe81ULL, + 0x38fc1c1c701ce06cULL, 0xe746fdfdd3fdbb2eULL, 0x9a1f4d4d294d5264ULL, + 0x397692927292e4e0ULL, 0xeafa7575c9758fbcULL, 0x0c3606061806301eULL, + 0x09ae8a8a128a2498ULL, 0x794bb2b2f2b2f940ULL, 0xd185e6e6bfe66359ULL, + 0x1c7e0e0e380e7036ULL, 0x3ee71f1f7c1ff863ULL, 0xc4556262956237f7ULL, + 0xb53ad4d477d4eea3ULL, 0x4d81a8a89aa82932ULL, 0x315296966296c4f4ULL, + 0xef62f9f9c3f99b3aULL, 0x97a3c5c533c566f6ULL, 0x4a102525942535b1ULL, + 0xb2ab59597959f220ULL, 0x15d084842a8454aeULL, 0xe4c57272d572b7a7ULL, + 0x72ec3939e439d5ddULL, 0x98164c4c2d4c5a61ULL, 0xbc945e5e655eca3bULL, + 0xf09f7878fd78e785ULL, 0x70e53838e038ddd8ULL, 0x05988c8c0a8c1486ULL, + 0xbf17d1d163d1c6b2ULL, 0x57e4a5a5aea5410bULL, 0xd9a1e2e2afe2434dULL, + 0xc24e616199612ff8ULL, 0x7b42b3b3f6b3f145ULL, 0x42342121842115a5ULL, + 0x25089c9c4a9c94d6ULL, 0x3cee1e1e781ef066ULL, 0x8661434311432252ULL, + 0x93b1c7c73bc776fcULL, 0xe54ffcfcd7fcb32bULL, 0x0824040410042014ULL, + 0xa2e351515951b208ULL, 0x2f2599995e99bcc7ULL, 0xda226d6da96d4fc4ULL, + 0x1a650d0d340d6839ULL, 0xe979fafacffa8335ULL, 0xa369dfdf5bdfb684ULL, + 0xfca97e7ee57ed79bULL, 0x4819242490243db4ULL, 0x76fe3b3bec3bc5d7ULL, + 0x4b9aabab96ab313dULL, 0x81f0cece1fce3ed1ULL, 0x2299111144118855ULL, + 0x03838f8f068f0c89ULL, 0x9c044e4e254e4a6bULL, 0x7366b7b7e6b7d151ULL, + 0xcbe0ebeb8beb0b60ULL, 0x78c13c3cf03cfdccULL, 0x1ffd81813e817cbfULL, + 0x354094946a94d4feULL, 0xf31cf7f7fbf7eb0cULL, 0x6f18b9b9deb9a167ULL, + 0x268b13134c13985fULL, 0x58512c2cb02c7d9cULL, 0xbb05d3d36bd3d6b8ULL, + 0xd38ce7e7bbe76b5cULL, 0xdc396e6ea56e57cbULL, 0x95aac4c437c46ef3ULL, + 0x061b03030c03180fULL, 0xacdc565645568a13ULL, 0x885e44440d441a49ULL, + 0xfea07f7fe17fdf9eULL, 0x4f88a9a99ea92137ULL, 0x54672a2aa82a4d82ULL, + 0x6b0abbbbd6bbb16dULL, 0x9f87c1c123c146e2ULL, 0xa6f153535153a202ULL, + 0xa572dcdc57dcae8bULL, 0x16530b0b2c0b5827ULL, 0x27019d9d4e9d9cd3ULL, + 0xd82b6c6cad6c47c1ULL, 0x62a43131c43195f5ULL, 0xe8f37474cd7487b9ULL, + 0xf115f6f6fff6e309ULL, 0x8c4c464605460a43ULL, 0x45a5acac8aac0926ULL, + 0x0fb589891e893c97ULL, 0x28b414145014a044ULL, 0xdfbae1e1a3e15b42ULL, + 0x2ca616165816b04eULL, 0x74f73a3ae83acdd2ULL, 0xd2066969b9696fd0ULL, + 0x124109092409482dULL, 0xe0d77070dd70a7adULL, 0x716fb6b6e2b6d954ULL, + 0xbd1ed0d067d0ceb7ULL, 0xc7d6eded93ed3b7eULL, 0x85e2cccc17cc2edbULL, + 0x8468424215422a57ULL, 0x2d2c98985a98b4c2ULL, 0x55eda4a4aaa4490eULL, + 0x50752828a0285d88ULL, 0xb8865c5c6d5cda31ULL, 0xed6bf8f8c7f8933fULL, + 0x11c28686228644a4ULL, +}; + +static const u64 C3[256] = { + 0x7830d818186018c0ULL, 0xaf462623238c2305ULL, 0xf991b8c6c63fc67eULL, + 0x6fcdfbe8e887e813ULL, 0xa113cb878726874cULL, 0x626d11b8b8dab8a9ULL, + 0x0502090101040108ULL, 0x6e9e0d4f4f214f42ULL, 0xee6c9b3636d836adULL, + 0x0451ffa6a6a2a659ULL, 0xbdb90cd2d26fd2deULL, 0x06f70ef5f5f3f5fbULL, + 0x80f2967979f979efULL, 0xcede306f6fa16f5fULL, 0xef3f6d91917e91fcULL, + 0x07a4f852525552aaULL, 0xfdc04760609d6027ULL, 0x766535bcbccabc89ULL, + 0xcd2b379b9b569bacULL, 0x8c018a8e8e028e04ULL, 0x155bd2a3a3b6a371ULL, + 0x3c186c0c0c300c60ULL, 0x8af6847b7bf17bffULL, 0xe16a803535d435b5ULL, + 0x693af51d1d741de8ULL, 0x47ddb3e0e0a7e053ULL, 0xacb321d7d77bd7f6ULL, + 0xed999cc2c22fc25eULL, 0x965c432e2eb82e6dULL, 0x7a96294b4b314b62ULL, + 0x21e15dfefedffea3ULL, 0x16aed55757415782ULL, 0x412abd15155415a8ULL, + 0xb6eee87777c1779fULL, 0xeb6e923737dc37a5ULL, 0x56d79ee5e5b3e57bULL, + 0xd923139f9f469f8cULL, 0x17fd23f0f0e7f0d3ULL, 0x7f94204a4a354a6aULL, + 0x95a944dada4fda9eULL, 0x25b0a258587d58faULL, 0xca8fcfc9c903c906ULL, + 0x8d527c2929a42955ULL, 0x22145a0a0a280a50ULL, 0x4f7f50b1b1feb1e1ULL, + 0x1a5dc9a0a0baa069ULL, 0xdad6146b6bb16b7fULL, 0xab17d985852e855cULL, + 0x73673cbdbdcebd81ULL, 0x34ba8f5d5d695dd2ULL, 0x5020901010401080ULL, + 0x03f507f4f4f7f4f3ULL, 0xc08bddcbcb0bcb16ULL, 0xc67cd33e3ef83eedULL, + 0x110a2d0505140528ULL, 0xe6ce78676781671fULL, 0x53d597e4e4b7e473ULL, + 0xbb4e0227279c2725ULL, 0x5882734141194132ULL, 0x9d0ba78b8b168b2cULL, + 0x0153f6a7a7a6a751ULL, 0x94fab27d7de97dcfULL, 0xfb374995956e95dcULL, + 0x9fad56d8d847d88eULL, 0x30eb70fbfbcbfb8bULL, 0x71c1cdeeee9fee23ULL, + 0x91f8bb7c7ced7cc7ULL, 0xe3cc716666856617ULL, 0x8ea77bdddd53dda6ULL, + 0x4b2eaf17175c17b8ULL, 0x468e454747014702ULL, 0xdc211a9e9e429e84ULL, + 0xc589d4caca0fca1eULL, 0x995a582d2db42d75ULL, 0x79632ebfbfc6bf91ULL, + 0x1b0e3f07071c0738ULL, 0x2347acadad8ead01ULL, 0x2fb4b05a5a755aeaULL, + 0xb51bef838336836cULL, 0xff66b63333cc3385ULL, 0xf2c65c636391633fULL, + 0x0a04120202080210ULL, 0x384993aaaa92aa39ULL, 0xa8e2de7171d971afULL, + 0xcf8dc6c8c807c80eULL, 0x7d32d119196419c8ULL, 0x70923b4949394972ULL, + 0x9aaf5fd9d943d986ULL, 0x1df931f2f2eff2c3ULL, 0x48dba8e3e3abe34bULL, + 0x2ab6b95b5b715be2ULL, 0x920dbc88881a8834ULL, 0xc8293e9a9a529aa4ULL, + 0xbe4c0b262698262dULL, 0xfa64bf3232c8328dULL, 0x4a7d59b0b0fab0e9ULL, + 0x6acff2e9e983e91bULL, 0x331e770f0f3c0f78ULL, 0xa6b733d5d573d5e6ULL, + 0xba1df480803a8074ULL, 0x7c6127bebec2be99ULL, 0xde87ebcdcd13cd26ULL, + 0xe468893434d034bdULL, 0x75903248483d487aULL, 0x24e354ffffdbffabULL, + 0x8ff48d7a7af57af7ULL, 0xea3d6490907a90f4ULL, 0x3ebe9d5f5f615fc2ULL, + 0xa0403d202080201dULL, 0xd5d00f6868bd6867ULL, 0x7234ca1a1a681ad0ULL, + 0x2c41b7aeae82ae19ULL, 0x5e757db4b4eab4c9ULL, 0x19a8ce54544d549aULL, + 0xe53b7f93937693ecULL, 0xaa442f222288220dULL, 0xe9c86364648d6407ULL, + 0x12ff2af1f1e3f1dbULL, 0xa2e6cc7373d173bfULL, 0x5a24821212481290ULL, + 0x5d807a40401d403aULL, 0x2810480808200840ULL, 0xe89b95c3c32bc356ULL, + 0x7bc5dfecec97ec33ULL, 0x90ab4ddbdb4bdb96ULL, 0x1f5fc0a1a1bea161ULL, + 0x8307918d8d0e8d1cULL, 0xc97ac83d3df43df5ULL, 0xf1335b97976697ccULL, + 0x0000000000000000ULL, 0xd483f9cfcf1bcf36ULL, 0x87566e2b2bac2b45ULL, + 0xb3ece17676c57697ULL, 0xb019e68282328264ULL, 0xa9b128d6d67fd6feULL, + 0x7736c31b1b6c1bd8ULL, 0x5b7774b5b5eeb5c1ULL, 0x2943beafaf86af11ULL, + 0xdfd41d6a6ab56a77ULL, 0x0da0ea50505d50baULL, 0x4c8a574545094512ULL, + 0x18fb38f3f3ebf3cbULL, 0xf060ad3030c0309dULL, 0x74c3c4efef9bef2bULL, + 0xc37eda3f3ffc3fe5ULL, 0x1caac75555495592ULL, 0x1059dba2a2b2a279ULL, + 0x65c9e9eaea8fea03ULL, 0xecca6a656589650fULL, 0x686903babad2bab9ULL, + 0x935e4a2f2fbc2f65ULL, 0xe79d8ec0c027c04eULL, 0x81a160dede5fdebeULL, + 0x6c38fc1c1c701ce0ULL, 0x2ee746fdfdd3fdbbULL, 0x649a1f4d4d294d52ULL, + 0xe0397692927292e4ULL, 0xbceafa7575c9758fULL, 0x1e0c360606180630ULL, + 0x9809ae8a8a128a24ULL, 0x40794bb2b2f2b2f9ULL, 0x59d185e6e6bfe663ULL, + 0x361c7e0e0e380e70ULL, 0x633ee71f1f7c1ff8ULL, 0xf7c4556262956237ULL, + 0xa3b53ad4d477d4eeULL, 0x324d81a8a89aa829ULL, 0xf4315296966296c4ULL, + 0x3aef62f9f9c3f99bULL, 0xf697a3c5c533c566ULL, 0xb14a102525942535ULL, + 0x20b2ab59597959f2ULL, 0xae15d084842a8454ULL, 0xa7e4c57272d572b7ULL, + 0xdd72ec3939e439d5ULL, 0x6198164c4c2d4c5aULL, 0x3bbc945e5e655ecaULL, + 0x85f09f7878fd78e7ULL, 0xd870e53838e038ddULL, 0x8605988c8c0a8c14ULL, + 0xb2bf17d1d163d1c6ULL, 0x0b57e4a5a5aea541ULL, 0x4dd9a1e2e2afe243ULL, + 0xf8c24e616199612fULL, 0x457b42b3b3f6b3f1ULL, 0xa542342121842115ULL, + 0xd625089c9c4a9c94ULL, 0x663cee1e1e781ef0ULL, 0x5286614343114322ULL, + 0xfc93b1c7c73bc776ULL, 0x2be54ffcfcd7fcb3ULL, 0x1408240404100420ULL, + 0x08a2e351515951b2ULL, 0xc72f2599995e99bcULL, 0xc4da226d6da96d4fULL, + 0x391a650d0d340d68ULL, 0x35e979fafacffa83ULL, 0x84a369dfdf5bdfb6ULL, + 0x9bfca97e7ee57ed7ULL, 0xb44819242490243dULL, 0xd776fe3b3bec3bc5ULL, + 0x3d4b9aabab96ab31ULL, 0xd181f0cece1fce3eULL, 0x5522991111441188ULL, + 0x8903838f8f068f0cULL, 0x6b9c044e4e254e4aULL, 0x517366b7b7e6b7d1ULL, + 0x60cbe0ebeb8beb0bULL, 0xcc78c13c3cf03cfdULL, 0xbf1ffd81813e817cULL, + 0xfe354094946a94d4ULL, 0x0cf31cf7f7fbf7ebULL, 0x676f18b9b9deb9a1ULL, + 0x5f268b13134c1398ULL, 0x9c58512c2cb02c7dULL, 0xb8bb05d3d36bd3d6ULL, + 0x5cd38ce7e7bbe76bULL, 0xcbdc396e6ea56e57ULL, 0xf395aac4c437c46eULL, + 0x0f061b03030c0318ULL, 0x13acdc565645568aULL, 0x49885e44440d441aULL, + 0x9efea07f7fe17fdfULL, 0x374f88a9a99ea921ULL, 0x8254672a2aa82a4dULL, + 0x6d6b0abbbbd6bbb1ULL, 0xe29f87c1c123c146ULL, 0x02a6f153535153a2ULL, + 0x8ba572dcdc57dcaeULL, 0x2716530b0b2c0b58ULL, 0xd327019d9d4e9d9cULL, + 0xc1d82b6c6cad6c47ULL, 0xf562a43131c43195ULL, 0xb9e8f37474cd7487ULL, + 0x09f115f6f6fff6e3ULL, 0x438c4c464605460aULL, 0x2645a5acac8aac09ULL, + 0x970fb589891e893cULL, 0x4428b414145014a0ULL, 0x42dfbae1e1a3e15bULL, + 0x4e2ca616165816b0ULL, 0xd274f73a3ae83acdULL, 0xd0d2066969b9696fULL, + 0x2d12410909240948ULL, 0xade0d77070dd70a7ULL, 0x54716fb6b6e2b6d9ULL, + 0xb7bd1ed0d067d0ceULL, 0x7ec7d6eded93ed3bULL, 0xdb85e2cccc17cc2eULL, + 0x578468424215422aULL, 0xc22d2c98985a98b4ULL, 0x0e55eda4a4aaa449ULL, + 0x8850752828a0285dULL, 0x31b8865c5c6d5cdaULL, 0x3fed6bf8f8c7f893ULL, + 0xa411c28686228644ULL, +}; + +static const u64 C4[256] = { + 0xc07830d818186018ULL, 0x05af462623238c23ULL, 0x7ef991b8c6c63fc6ULL, + 0x136fcdfbe8e887e8ULL, 0x4ca113cb87872687ULL, 0xa9626d11b8b8dab8ULL, + 0x0805020901010401ULL, 0x426e9e0d4f4f214fULL, 0xadee6c9b3636d836ULL, + 0x590451ffa6a6a2a6ULL, 0xdebdb90cd2d26fd2ULL, 0xfb06f70ef5f5f3f5ULL, + 0xef80f2967979f979ULL, 0x5fcede306f6fa16fULL, 0xfcef3f6d91917e91ULL, + 0xaa07a4f852525552ULL, 0x27fdc04760609d60ULL, 0x89766535bcbccabcULL, + 0xaccd2b379b9b569bULL, 0x048c018a8e8e028eULL, 0x71155bd2a3a3b6a3ULL, + 0x603c186c0c0c300cULL, 0xff8af6847b7bf17bULL, 0xb5e16a803535d435ULL, + 0xe8693af51d1d741dULL, 0x5347ddb3e0e0a7e0ULL, 0xf6acb321d7d77bd7ULL, + 0x5eed999cc2c22fc2ULL, 0x6d965c432e2eb82eULL, 0x627a96294b4b314bULL, + 0xa321e15dfefedffeULL, 0x8216aed557574157ULL, 0xa8412abd15155415ULL, + 0x9fb6eee87777c177ULL, 0xa5eb6e923737dc37ULL, 0x7b56d79ee5e5b3e5ULL, + 0x8cd923139f9f469fULL, 0xd317fd23f0f0e7f0ULL, 0x6a7f94204a4a354aULL, + 0x9e95a944dada4fdaULL, 0xfa25b0a258587d58ULL, 0x06ca8fcfc9c903c9ULL, + 0x558d527c2929a429ULL, 0x5022145a0a0a280aULL, 0xe14f7f50b1b1feb1ULL, + 0x691a5dc9a0a0baa0ULL, 0x7fdad6146b6bb16bULL, 0x5cab17d985852e85ULL, + 0x8173673cbdbdcebdULL, 0xd234ba8f5d5d695dULL, 0x8050209010104010ULL, + 0xf303f507f4f4f7f4ULL, 0x16c08bddcbcb0bcbULL, 0xedc67cd33e3ef83eULL, + 0x28110a2d05051405ULL, 0x1fe6ce7867678167ULL, 0x7353d597e4e4b7e4ULL, + 0x25bb4e0227279c27ULL, 0x3258827341411941ULL, 0x2c9d0ba78b8b168bULL, + 0x510153f6a7a7a6a7ULL, 0xcf94fab27d7de97dULL, 0xdcfb374995956e95ULL, + 0x8e9fad56d8d847d8ULL, 0x8b30eb70fbfbcbfbULL, 0x2371c1cdeeee9feeULL, + 0xc791f8bb7c7ced7cULL, 0x17e3cc7166668566ULL, 0xa68ea77bdddd53ddULL, + 0xb84b2eaf17175c17ULL, 0x02468e4547470147ULL, 0x84dc211a9e9e429eULL, + 0x1ec589d4caca0fcaULL, 0x75995a582d2db42dULL, 0x9179632ebfbfc6bfULL, + 0x381b0e3f07071c07ULL, 0x012347acadad8eadULL, 0xea2fb4b05a5a755aULL, + 0x6cb51bef83833683ULL, 0x85ff66b63333cc33ULL, 0x3ff2c65c63639163ULL, + 0x100a041202020802ULL, 0x39384993aaaa92aaULL, 0xafa8e2de7171d971ULL, + 0x0ecf8dc6c8c807c8ULL, 0xc87d32d119196419ULL, 0x7270923b49493949ULL, + 0x869aaf5fd9d943d9ULL, 0xc31df931f2f2eff2ULL, 0x4b48dba8e3e3abe3ULL, + 0xe22ab6b95b5b715bULL, 0x34920dbc88881a88ULL, 0xa4c8293e9a9a529aULL, + 0x2dbe4c0b26269826ULL, 0x8dfa64bf3232c832ULL, 0xe94a7d59b0b0fab0ULL, + 0x1b6acff2e9e983e9ULL, 0x78331e770f0f3c0fULL, 0xe6a6b733d5d573d5ULL, + 0x74ba1df480803a80ULL, 0x997c6127bebec2beULL, 0x26de87ebcdcd13cdULL, + 0xbde468893434d034ULL, 0x7a75903248483d48ULL, 0xab24e354ffffdbffULL, + 0xf78ff48d7a7af57aULL, 0xf4ea3d6490907a90ULL, 0xc23ebe9d5f5f615fULL, + 0x1da0403d20208020ULL, 0x67d5d00f6868bd68ULL, 0xd07234ca1a1a681aULL, + 0x192c41b7aeae82aeULL, 0xc95e757db4b4eab4ULL, 0x9a19a8ce54544d54ULL, + 0xece53b7f93937693ULL, 0x0daa442f22228822ULL, 0x07e9c86364648d64ULL, + 0xdb12ff2af1f1e3f1ULL, 0xbfa2e6cc7373d173ULL, 0x905a248212124812ULL, + 0x3a5d807a40401d40ULL, 0x4028104808082008ULL, 0x56e89b95c3c32bc3ULL, + 0x337bc5dfecec97ecULL, 0x9690ab4ddbdb4bdbULL, 0x611f5fc0a1a1bea1ULL, + 0x1c8307918d8d0e8dULL, 0xf5c97ac83d3df43dULL, 0xccf1335b97976697ULL, + 0x0000000000000000ULL, 0x36d483f9cfcf1bcfULL, 0x4587566e2b2bac2bULL, + 0x97b3ece17676c576ULL, 0x64b019e682823282ULL, 0xfea9b128d6d67fd6ULL, + 0xd87736c31b1b6c1bULL, 0xc15b7774b5b5eeb5ULL, 0x112943beafaf86afULL, + 0x77dfd41d6a6ab56aULL, 0xba0da0ea50505d50ULL, 0x124c8a5745450945ULL, + 0xcb18fb38f3f3ebf3ULL, 0x9df060ad3030c030ULL, 0x2b74c3c4efef9befULL, + 0xe5c37eda3f3ffc3fULL, 0x921caac755554955ULL, 0x791059dba2a2b2a2ULL, + 0x0365c9e9eaea8feaULL, 0x0fecca6a65658965ULL, 0xb9686903babad2baULL, + 0x65935e4a2f2fbc2fULL, 0x4ee79d8ec0c027c0ULL, 0xbe81a160dede5fdeULL, + 0xe06c38fc1c1c701cULL, 0xbb2ee746fdfdd3fdULL, 0x52649a1f4d4d294dULL, + 0xe4e0397692927292ULL, 0x8fbceafa7575c975ULL, 0x301e0c3606061806ULL, + 0x249809ae8a8a128aULL, 0xf940794bb2b2f2b2ULL, 0x6359d185e6e6bfe6ULL, + 0x70361c7e0e0e380eULL, 0xf8633ee71f1f7c1fULL, 0x37f7c45562629562ULL, + 0xeea3b53ad4d477d4ULL, 0x29324d81a8a89aa8ULL, 0xc4f4315296966296ULL, + 0x9b3aef62f9f9c3f9ULL, 0x66f697a3c5c533c5ULL, 0x35b14a1025259425ULL, + 0xf220b2ab59597959ULL, 0x54ae15d084842a84ULL, 0xb7a7e4c57272d572ULL, + 0xd5dd72ec3939e439ULL, 0x5a6198164c4c2d4cULL, 0xca3bbc945e5e655eULL, + 0xe785f09f7878fd78ULL, 0xddd870e53838e038ULL, 0x148605988c8c0a8cULL, + 0xc6b2bf17d1d163d1ULL, 0x410b57e4a5a5aea5ULL, 0x434dd9a1e2e2afe2ULL, + 0x2ff8c24e61619961ULL, 0xf1457b42b3b3f6b3ULL, 0x15a5423421218421ULL, + 0x94d625089c9c4a9cULL, 0xf0663cee1e1e781eULL, 0x2252866143431143ULL, + 0x76fc93b1c7c73bc7ULL, 0xb32be54ffcfcd7fcULL, 0x2014082404041004ULL, + 0xb208a2e351515951ULL, 0xbcc72f2599995e99ULL, 0x4fc4da226d6da96dULL, + 0x68391a650d0d340dULL, 0x8335e979fafacffaULL, 0xb684a369dfdf5bdfULL, + 0xd79bfca97e7ee57eULL, 0x3db4481924249024ULL, 0xc5d776fe3b3bec3bULL, + 0x313d4b9aabab96abULL, 0x3ed181f0cece1fceULL, 0x8855229911114411ULL, + 0x0c8903838f8f068fULL, 0x4a6b9c044e4e254eULL, 0xd1517366b7b7e6b7ULL, + 0x0b60cbe0ebeb8bebULL, 0xfdcc78c13c3cf03cULL, 0x7cbf1ffd81813e81ULL, + 0xd4fe354094946a94ULL, 0xeb0cf31cf7f7fbf7ULL, 0xa1676f18b9b9deb9ULL, + 0x985f268b13134c13ULL, 0x7d9c58512c2cb02cULL, 0xd6b8bb05d3d36bd3ULL, + 0x6b5cd38ce7e7bbe7ULL, 0x57cbdc396e6ea56eULL, 0x6ef395aac4c437c4ULL, + 0x180f061b03030c03ULL, 0x8a13acdc56564556ULL, 0x1a49885e44440d44ULL, + 0xdf9efea07f7fe17fULL, 0x21374f88a9a99ea9ULL, 0x4d8254672a2aa82aULL, + 0xb16d6b0abbbbd6bbULL, 0x46e29f87c1c123c1ULL, 0xa202a6f153535153ULL, + 0xae8ba572dcdc57dcULL, 0x582716530b0b2c0bULL, 0x9cd327019d9d4e9dULL, + 0x47c1d82b6c6cad6cULL, 0x95f562a43131c431ULL, 0x87b9e8f37474cd74ULL, + 0xe309f115f6f6fff6ULL, 0x0a438c4c46460546ULL, 0x092645a5acac8aacULL, + 0x3c970fb589891e89ULL, 0xa04428b414145014ULL, 0x5b42dfbae1e1a3e1ULL, + 0xb04e2ca616165816ULL, 0xcdd274f73a3ae83aULL, 0x6fd0d2066969b969ULL, + 0x482d124109092409ULL, 0xa7ade0d77070dd70ULL, 0xd954716fb6b6e2b6ULL, + 0xceb7bd1ed0d067d0ULL, 0x3b7ec7d6eded93edULL, 0x2edb85e2cccc17ccULL, + 0x2a57846842421542ULL, 0xb4c22d2c98985a98ULL, 0x490e55eda4a4aaa4ULL, + 0x5d8850752828a028ULL, 0xda31b8865c5c6d5cULL, 0x933fed6bf8f8c7f8ULL, + 0x44a411c286862286ULL, +}; + +static const u64 C5[256] = { + 0x18c07830d8181860ULL, 0x2305af462623238cULL, 0xc67ef991b8c6c63fULL, + 0xe8136fcdfbe8e887ULL, 0x874ca113cb878726ULL, 0xb8a9626d11b8b8daULL, + 0x0108050209010104ULL, 0x4f426e9e0d4f4f21ULL, 0x36adee6c9b3636d8ULL, + 0xa6590451ffa6a6a2ULL, 0xd2debdb90cd2d26fULL, 0xf5fb06f70ef5f5f3ULL, + 0x79ef80f2967979f9ULL, 0x6f5fcede306f6fa1ULL, 0x91fcef3f6d91917eULL, + 0x52aa07a4f8525255ULL, 0x6027fdc04760609dULL, 0xbc89766535bcbccaULL, + 0x9baccd2b379b9b56ULL, 0x8e048c018a8e8e02ULL, 0xa371155bd2a3a3b6ULL, + 0x0c603c186c0c0c30ULL, 0x7bff8af6847b7bf1ULL, 0x35b5e16a803535d4ULL, + 0x1de8693af51d1d74ULL, 0xe05347ddb3e0e0a7ULL, 0xd7f6acb321d7d77bULL, + 0xc25eed999cc2c22fULL, 0x2e6d965c432e2eb8ULL, 0x4b627a96294b4b31ULL, + 0xfea321e15dfefedfULL, 0x578216aed5575741ULL, 0x15a8412abd151554ULL, + 0x779fb6eee87777c1ULL, 0x37a5eb6e923737dcULL, 0xe57b56d79ee5e5b3ULL, + 0x9f8cd923139f9f46ULL, 0xf0d317fd23f0f0e7ULL, 0x4a6a7f94204a4a35ULL, + 0xda9e95a944dada4fULL, 0x58fa25b0a258587dULL, 0xc906ca8fcfc9c903ULL, + 0x29558d527c2929a4ULL, 0x0a5022145a0a0a28ULL, 0xb1e14f7f50b1b1feULL, + 0xa0691a5dc9a0a0baULL, 0x6b7fdad6146b6bb1ULL, 0x855cab17d985852eULL, + 0xbd8173673cbdbdceULL, 0x5dd234ba8f5d5d69ULL, 0x1080502090101040ULL, + 0xf4f303f507f4f4f7ULL, 0xcb16c08bddcbcb0bULL, 0x3eedc67cd33e3ef8ULL, + 0x0528110a2d050514ULL, 0x671fe6ce78676781ULL, 0xe47353d597e4e4b7ULL, + 0x2725bb4e0227279cULL, 0x4132588273414119ULL, 0x8b2c9d0ba78b8b16ULL, + 0xa7510153f6a7a7a6ULL, 0x7dcf94fab27d7de9ULL, 0x95dcfb374995956eULL, + 0xd88e9fad56d8d847ULL, 0xfb8b30eb70fbfbcbULL, 0xee2371c1cdeeee9fULL, + 0x7cc791f8bb7c7cedULL, 0x6617e3cc71666685ULL, 0xdda68ea77bdddd53ULL, + 0x17b84b2eaf17175cULL, 0x4702468e45474701ULL, 0x9e84dc211a9e9e42ULL, + 0xca1ec589d4caca0fULL, 0x2d75995a582d2db4ULL, 0xbf9179632ebfbfc6ULL, + 0x07381b0e3f07071cULL, 0xad012347acadad8eULL, 0x5aea2fb4b05a5a75ULL, + 0x836cb51bef838336ULL, 0x3385ff66b63333ccULL, 0x633ff2c65c636391ULL, + 0x02100a0412020208ULL, 0xaa39384993aaaa92ULL, 0x71afa8e2de7171d9ULL, + 0xc80ecf8dc6c8c807ULL, 0x19c87d32d1191964ULL, 0x497270923b494939ULL, + 0xd9869aaf5fd9d943ULL, 0xf2c31df931f2f2efULL, 0xe34b48dba8e3e3abULL, + 0x5be22ab6b95b5b71ULL, 0x8834920dbc88881aULL, 0x9aa4c8293e9a9a52ULL, + 0x262dbe4c0b262698ULL, 0x328dfa64bf3232c8ULL, 0xb0e94a7d59b0b0faULL, + 0xe91b6acff2e9e983ULL, 0x0f78331e770f0f3cULL, 0xd5e6a6b733d5d573ULL, + 0x8074ba1df480803aULL, 0xbe997c6127bebec2ULL, 0xcd26de87ebcdcd13ULL, + 0x34bde468893434d0ULL, 0x487a75903248483dULL, 0xffab24e354ffffdbULL, + 0x7af78ff48d7a7af5ULL, 0x90f4ea3d6490907aULL, 0x5fc23ebe9d5f5f61ULL, + 0x201da0403d202080ULL, 0x6867d5d00f6868bdULL, 0x1ad07234ca1a1a68ULL, + 0xae192c41b7aeae82ULL, 0xb4c95e757db4b4eaULL, 0x549a19a8ce54544dULL, + 0x93ece53b7f939376ULL, 0x220daa442f222288ULL, 0x6407e9c86364648dULL, + 0xf1db12ff2af1f1e3ULL, 0x73bfa2e6cc7373d1ULL, 0x12905a2482121248ULL, + 0x403a5d807a40401dULL, 0x0840281048080820ULL, 0xc356e89b95c3c32bULL, + 0xec337bc5dfecec97ULL, 0xdb9690ab4ddbdb4bULL, 0xa1611f5fc0a1a1beULL, + 0x8d1c8307918d8d0eULL, 0x3df5c97ac83d3df4ULL, 0x97ccf1335b979766ULL, + 0x0000000000000000ULL, 0xcf36d483f9cfcf1bULL, 0x2b4587566e2b2bacULL, + 0x7697b3ece17676c5ULL, 0x8264b019e6828232ULL, 0xd6fea9b128d6d67fULL, + 0x1bd87736c31b1b6cULL, 0xb5c15b7774b5b5eeULL, 0xaf112943beafaf86ULL, + 0x6a77dfd41d6a6ab5ULL, 0x50ba0da0ea50505dULL, 0x45124c8a57454509ULL, + 0xf3cb18fb38f3f3ebULL, 0x309df060ad3030c0ULL, 0xef2b74c3c4efef9bULL, + 0x3fe5c37eda3f3ffcULL, 0x55921caac7555549ULL, 0xa2791059dba2a2b2ULL, + 0xea0365c9e9eaea8fULL, 0x650fecca6a656589ULL, 0xbab9686903babad2ULL, + 0x2f65935e4a2f2fbcULL, 0xc04ee79d8ec0c027ULL, 0xdebe81a160dede5fULL, + 0x1ce06c38fc1c1c70ULL, 0xfdbb2ee746fdfdd3ULL, 0x4d52649a1f4d4d29ULL, + 0x92e4e03976929272ULL, 0x758fbceafa7575c9ULL, 0x06301e0c36060618ULL, + 0x8a249809ae8a8a12ULL, 0xb2f940794bb2b2f2ULL, 0xe66359d185e6e6bfULL, + 0x0e70361c7e0e0e38ULL, 0x1ff8633ee71f1f7cULL, 0x6237f7c455626295ULL, + 0xd4eea3b53ad4d477ULL, 0xa829324d81a8a89aULL, 0x96c4f43152969662ULL, + 0xf99b3aef62f9f9c3ULL, 0xc566f697a3c5c533ULL, 0x2535b14a10252594ULL, + 0x59f220b2ab595979ULL, 0x8454ae15d084842aULL, 0x72b7a7e4c57272d5ULL, + 0x39d5dd72ec3939e4ULL, 0x4c5a6198164c4c2dULL, 0x5eca3bbc945e5e65ULL, + 0x78e785f09f7878fdULL, 0x38ddd870e53838e0ULL, 0x8c148605988c8c0aULL, + 0xd1c6b2bf17d1d163ULL, 0xa5410b57e4a5a5aeULL, 0xe2434dd9a1e2e2afULL, + 0x612ff8c24e616199ULL, 0xb3f1457b42b3b3f6ULL, 0x2115a54234212184ULL, + 0x9c94d625089c9c4aULL, 0x1ef0663cee1e1e78ULL, 0x4322528661434311ULL, + 0xc776fc93b1c7c73bULL, 0xfcb32be54ffcfcd7ULL, 0x0420140824040410ULL, + 0x51b208a2e3515159ULL, 0x99bcc72f2599995eULL, 0x6d4fc4da226d6da9ULL, + 0x0d68391a650d0d34ULL, 0xfa8335e979fafacfULL, 0xdfb684a369dfdf5bULL, + 0x7ed79bfca97e7ee5ULL, 0x243db44819242490ULL, 0x3bc5d776fe3b3becULL, + 0xab313d4b9aabab96ULL, 0xce3ed181f0cece1fULL, 0x1188552299111144ULL, + 0x8f0c8903838f8f06ULL, 0x4e4a6b9c044e4e25ULL, 0xb7d1517366b7b7e6ULL, + 0xeb0b60cbe0ebeb8bULL, 0x3cfdcc78c13c3cf0ULL, 0x817cbf1ffd81813eULL, + 0x94d4fe354094946aULL, 0xf7eb0cf31cf7f7fbULL, 0xb9a1676f18b9b9deULL, + 0x13985f268b13134cULL, 0x2c7d9c58512c2cb0ULL, 0xd3d6b8bb05d3d36bULL, + 0xe76b5cd38ce7e7bbULL, 0x6e57cbdc396e6ea5ULL, 0xc46ef395aac4c437ULL, + 0x03180f061b03030cULL, 0x568a13acdc565645ULL, 0x441a49885e44440dULL, + 0x7fdf9efea07f7fe1ULL, 0xa921374f88a9a99eULL, 0x2a4d8254672a2aa8ULL, + 0xbbb16d6b0abbbbd6ULL, 0xc146e29f87c1c123ULL, 0x53a202a6f1535351ULL, + 0xdcae8ba572dcdc57ULL, 0x0b582716530b0b2cULL, 0x9d9cd327019d9d4eULL, + 0x6c47c1d82b6c6cadULL, 0x3195f562a43131c4ULL, 0x7487b9e8f37474cdULL, + 0xf6e309f115f6f6ffULL, 0x460a438c4c464605ULL, 0xac092645a5acac8aULL, + 0x893c970fb589891eULL, 0x14a04428b4141450ULL, 0xe15b42dfbae1e1a3ULL, + 0x16b04e2ca6161658ULL, 0x3acdd274f73a3ae8ULL, 0x696fd0d2066969b9ULL, + 0x09482d1241090924ULL, 0x70a7ade0d77070ddULL, 0xb6d954716fb6b6e2ULL, + 0xd0ceb7bd1ed0d067ULL, 0xed3b7ec7d6eded93ULL, 0xcc2edb85e2cccc17ULL, + 0x422a578468424215ULL, 0x98b4c22d2c98985aULL, 0xa4490e55eda4a4aaULL, + 0x285d8850752828a0ULL, 0x5cda31b8865c5c6dULL, 0xf8933fed6bf8f8c7ULL, + 0x8644a411c2868622ULL, +}; + +static const u64 C6[256] = { + 0x6018c07830d81818ULL, 0x8c2305af46262323ULL, 0x3fc67ef991b8c6c6ULL, + 0x87e8136fcdfbe8e8ULL, 0x26874ca113cb8787ULL, 0xdab8a9626d11b8b8ULL, + 0x0401080502090101ULL, 0x214f426e9e0d4f4fULL, 0xd836adee6c9b3636ULL, + 0xa2a6590451ffa6a6ULL, 0x6fd2debdb90cd2d2ULL, 0xf3f5fb06f70ef5f5ULL, + 0xf979ef80f2967979ULL, 0xa16f5fcede306f6fULL, 0x7e91fcef3f6d9191ULL, + 0x5552aa07a4f85252ULL, 0x9d6027fdc0476060ULL, 0xcabc89766535bcbcULL, + 0x569baccd2b379b9bULL, 0x028e048c018a8e8eULL, 0xb6a371155bd2a3a3ULL, + 0x300c603c186c0c0cULL, 0xf17bff8af6847b7bULL, 0xd435b5e16a803535ULL, + 0x741de8693af51d1dULL, 0xa7e05347ddb3e0e0ULL, 0x7bd7f6acb321d7d7ULL, + 0x2fc25eed999cc2c2ULL, 0xb82e6d965c432e2eULL, 0x314b627a96294b4bULL, + 0xdffea321e15dfefeULL, 0x41578216aed55757ULL, 0x5415a8412abd1515ULL, + 0xc1779fb6eee87777ULL, 0xdc37a5eb6e923737ULL, 0xb3e57b56d79ee5e5ULL, + 0x469f8cd923139f9fULL, 0xe7f0d317fd23f0f0ULL, 0x354a6a7f94204a4aULL, + 0x4fda9e95a944dadaULL, 0x7d58fa25b0a25858ULL, 0x03c906ca8fcfc9c9ULL, + 0xa429558d527c2929ULL, 0x280a5022145a0a0aULL, 0xfeb1e14f7f50b1b1ULL, + 0xbaa0691a5dc9a0a0ULL, 0xb16b7fdad6146b6bULL, 0x2e855cab17d98585ULL, + 0xcebd8173673cbdbdULL, 0x695dd234ba8f5d5dULL, 0x4010805020901010ULL, + 0xf7f4f303f507f4f4ULL, 0x0bcb16c08bddcbcbULL, 0xf83eedc67cd33e3eULL, + 0x140528110a2d0505ULL, 0x81671fe6ce786767ULL, 0xb7e47353d597e4e4ULL, + 0x9c2725bb4e022727ULL, 0x1941325882734141ULL, 0x168b2c9d0ba78b8bULL, + 0xa6a7510153f6a7a7ULL, 0xe97dcf94fab27d7dULL, 0x6e95dcfb37499595ULL, + 0x47d88e9fad56d8d8ULL, 0xcbfb8b30eb70fbfbULL, 0x9fee2371c1cdeeeeULL, + 0xed7cc791f8bb7c7cULL, 0x856617e3cc716666ULL, 0x53dda68ea77bddddULL, + 0x5c17b84b2eaf1717ULL, 0x014702468e454747ULL, 0x429e84dc211a9e9eULL, + 0x0fca1ec589d4cacaULL, 0xb42d75995a582d2dULL, 0xc6bf9179632ebfbfULL, + 0x1c07381b0e3f0707ULL, 0x8ead012347acadadULL, 0x755aea2fb4b05a5aULL, + 0x36836cb51bef8383ULL, 0xcc3385ff66b63333ULL, 0x91633ff2c65c6363ULL, + 0x0802100a04120202ULL, 0x92aa39384993aaaaULL, 0xd971afa8e2de7171ULL, + 0x07c80ecf8dc6c8c8ULL, 0x6419c87d32d11919ULL, 0x39497270923b4949ULL, + 0x43d9869aaf5fd9d9ULL, 0xeff2c31df931f2f2ULL, 0xabe34b48dba8e3e3ULL, + 0x715be22ab6b95b5bULL, 0x1a8834920dbc8888ULL, 0x529aa4c8293e9a9aULL, + 0x98262dbe4c0b2626ULL, 0xc8328dfa64bf3232ULL, 0xfab0e94a7d59b0b0ULL, + 0x83e91b6acff2e9e9ULL, 0x3c0f78331e770f0fULL, 0x73d5e6a6b733d5d5ULL, + 0x3a8074ba1df48080ULL, 0xc2be997c6127bebeULL, 0x13cd26de87ebcdcdULL, + 0xd034bde468893434ULL, 0x3d487a7590324848ULL, 0xdbffab24e354ffffULL, + 0xf57af78ff48d7a7aULL, 0x7a90f4ea3d649090ULL, 0x615fc23ebe9d5f5fULL, + 0x80201da0403d2020ULL, 0xbd6867d5d00f6868ULL, 0x681ad07234ca1a1aULL, + 0x82ae192c41b7aeaeULL, 0xeab4c95e757db4b4ULL, 0x4d549a19a8ce5454ULL, + 0x7693ece53b7f9393ULL, 0x88220daa442f2222ULL, 0x8d6407e9c8636464ULL, + 0xe3f1db12ff2af1f1ULL, 0xd173bfa2e6cc7373ULL, 0x4812905a24821212ULL, + 0x1d403a5d807a4040ULL, 0x2008402810480808ULL, 0x2bc356e89b95c3c3ULL, + 0x97ec337bc5dfececULL, 0x4bdb9690ab4ddbdbULL, 0xbea1611f5fc0a1a1ULL, + 0x0e8d1c8307918d8dULL, 0xf43df5c97ac83d3dULL, 0x6697ccf1335b9797ULL, + 0x0000000000000000ULL, 0x1bcf36d483f9cfcfULL, 0xac2b4587566e2b2bULL, + 0xc57697b3ece17676ULL, 0x328264b019e68282ULL, 0x7fd6fea9b128d6d6ULL, + 0x6c1bd87736c31b1bULL, 0xeeb5c15b7774b5b5ULL, 0x86af112943beafafULL, + 0xb56a77dfd41d6a6aULL, 0x5d50ba0da0ea5050ULL, 0x0945124c8a574545ULL, + 0xebf3cb18fb38f3f3ULL, 0xc0309df060ad3030ULL, 0x9bef2b74c3c4efefULL, + 0xfc3fe5c37eda3f3fULL, 0x4955921caac75555ULL, 0xb2a2791059dba2a2ULL, + 0x8fea0365c9e9eaeaULL, 0x89650fecca6a6565ULL, 0xd2bab9686903babaULL, + 0xbc2f65935e4a2f2fULL, 0x27c04ee79d8ec0c0ULL, 0x5fdebe81a160dedeULL, + 0x701ce06c38fc1c1cULL, 0xd3fdbb2ee746fdfdULL, 0x294d52649a1f4d4dULL, + 0x7292e4e039769292ULL, 0xc9758fbceafa7575ULL, 0x1806301e0c360606ULL, + 0x128a249809ae8a8aULL, 0xf2b2f940794bb2b2ULL, 0xbfe66359d185e6e6ULL, + 0x380e70361c7e0e0eULL, 0x7c1ff8633ee71f1fULL, 0x956237f7c4556262ULL, + 0x77d4eea3b53ad4d4ULL, 0x9aa829324d81a8a8ULL, 0x6296c4f431529696ULL, + 0xc3f99b3aef62f9f9ULL, 0x33c566f697a3c5c5ULL, 0x942535b14a102525ULL, + 0x7959f220b2ab5959ULL, 0x2a8454ae15d08484ULL, 0xd572b7a7e4c57272ULL, + 0xe439d5dd72ec3939ULL, 0x2d4c5a6198164c4cULL, 0x655eca3bbc945e5eULL, + 0xfd78e785f09f7878ULL, 0xe038ddd870e53838ULL, 0x0a8c148605988c8cULL, + 0x63d1c6b2bf17d1d1ULL, 0xaea5410b57e4a5a5ULL, 0xafe2434dd9a1e2e2ULL, + 0x99612ff8c24e6161ULL, 0xf6b3f1457b42b3b3ULL, 0x842115a542342121ULL, + 0x4a9c94d625089c9cULL, 0x781ef0663cee1e1eULL, 0x1143225286614343ULL, + 0x3bc776fc93b1c7c7ULL, 0xd7fcb32be54ffcfcULL, 0x1004201408240404ULL, + 0x5951b208a2e35151ULL, 0x5e99bcc72f259999ULL, 0xa96d4fc4da226d6dULL, + 0x340d68391a650d0dULL, 0xcffa8335e979fafaULL, 0x5bdfb684a369dfdfULL, + 0xe57ed79bfca97e7eULL, 0x90243db448192424ULL, 0xec3bc5d776fe3b3bULL, + 0x96ab313d4b9aababULL, 0x1fce3ed181f0ceceULL, 0x4411885522991111ULL, + 0x068f0c8903838f8fULL, 0x254e4a6b9c044e4eULL, 0xe6b7d1517366b7b7ULL, + 0x8beb0b60cbe0ebebULL, 0xf03cfdcc78c13c3cULL, 0x3e817cbf1ffd8181ULL, + 0x6a94d4fe35409494ULL, 0xfbf7eb0cf31cf7f7ULL, 0xdeb9a1676f18b9b9ULL, + 0x4c13985f268b1313ULL, 0xb02c7d9c58512c2cULL, 0x6bd3d6b8bb05d3d3ULL, + 0xbbe76b5cd38ce7e7ULL, 0xa56e57cbdc396e6eULL, 0x37c46ef395aac4c4ULL, + 0x0c03180f061b0303ULL, 0x45568a13acdc5656ULL, 0x0d441a49885e4444ULL, + 0xe17fdf9efea07f7fULL, 0x9ea921374f88a9a9ULL, 0xa82a4d8254672a2aULL, + 0xd6bbb16d6b0abbbbULL, 0x23c146e29f87c1c1ULL, 0x5153a202a6f15353ULL, + 0x57dcae8ba572dcdcULL, 0x2c0b582716530b0bULL, 0x4e9d9cd327019d9dULL, + 0xad6c47c1d82b6c6cULL, 0xc43195f562a43131ULL, 0xcd7487b9e8f37474ULL, + 0xfff6e309f115f6f6ULL, 0x05460a438c4c4646ULL, 0x8aac092645a5acacULL, + 0x1e893c970fb58989ULL, 0x5014a04428b41414ULL, 0xa3e15b42dfbae1e1ULL, + 0x5816b04e2ca61616ULL, 0xe83acdd274f73a3aULL, 0xb9696fd0d2066969ULL, + 0x2409482d12410909ULL, 0xdd70a7ade0d77070ULL, 0xe2b6d954716fb6b6ULL, + 0x67d0ceb7bd1ed0d0ULL, 0x93ed3b7ec7d6ededULL, 0x17cc2edb85e2ccccULL, + 0x15422a5784684242ULL, 0x5a98b4c22d2c9898ULL, 0xaaa4490e55eda4a4ULL, + 0xa0285d8850752828ULL, 0x6d5cda31b8865c5cULL, 0xc7f8933fed6bf8f8ULL, + 0x228644a411c28686ULL, +}; + +static const u64 C7[256] = { + 0x186018c07830d818ULL, 0x238c2305af462623ULL, 0xc63fc67ef991b8c6ULL, + 0xe887e8136fcdfbe8ULL, 0x8726874ca113cb87ULL, 0xb8dab8a9626d11b8ULL, + 0x0104010805020901ULL, 0x4f214f426e9e0d4fULL, 0x36d836adee6c9b36ULL, + 0xa6a2a6590451ffa6ULL, 0xd26fd2debdb90cd2ULL, 0xf5f3f5fb06f70ef5ULL, + 0x79f979ef80f29679ULL, 0x6fa16f5fcede306fULL, 0x917e91fcef3f6d91ULL, + 0x525552aa07a4f852ULL, 0x609d6027fdc04760ULL, 0xbccabc89766535bcULL, + 0x9b569baccd2b379bULL, 0x8e028e048c018a8eULL, 0xa3b6a371155bd2a3ULL, + 0x0c300c603c186c0cULL, 0x7bf17bff8af6847bULL, 0x35d435b5e16a8035ULL, + 0x1d741de8693af51dULL, 0xe0a7e05347ddb3e0ULL, 0xd77bd7f6acb321d7ULL, + 0xc22fc25eed999cc2ULL, 0x2eb82e6d965c432eULL, 0x4b314b627a96294bULL, + 0xfedffea321e15dfeULL, 0x5741578216aed557ULL, 0x155415a8412abd15ULL, + 0x77c1779fb6eee877ULL, 0x37dc37a5eb6e9237ULL, 0xe5b3e57b56d79ee5ULL, + 0x9f469f8cd923139fULL, 0xf0e7f0d317fd23f0ULL, 0x4a354a6a7f94204aULL, + 0xda4fda9e95a944daULL, 0x587d58fa25b0a258ULL, 0xc903c906ca8fcfc9ULL, + 0x29a429558d527c29ULL, 0x0a280a5022145a0aULL, 0xb1feb1e14f7f50b1ULL, + 0xa0baa0691a5dc9a0ULL, 0x6bb16b7fdad6146bULL, 0x852e855cab17d985ULL, + 0xbdcebd8173673cbdULL, 0x5d695dd234ba8f5dULL, 0x1040108050209010ULL, + 0xf4f7f4f303f507f4ULL, 0xcb0bcb16c08bddcbULL, 0x3ef83eedc67cd33eULL, + 0x05140528110a2d05ULL, 0x6781671fe6ce7867ULL, 0xe4b7e47353d597e4ULL, + 0x279c2725bb4e0227ULL, 0x4119413258827341ULL, 0x8b168b2c9d0ba78bULL, + 0xa7a6a7510153f6a7ULL, 0x7de97dcf94fab27dULL, 0x956e95dcfb374995ULL, + 0xd847d88e9fad56d8ULL, 0xfbcbfb8b30eb70fbULL, 0xee9fee2371c1cdeeULL, + 0x7ced7cc791f8bb7cULL, 0x66856617e3cc7166ULL, 0xdd53dda68ea77bddULL, + 0x175c17b84b2eaf17ULL, 0x47014702468e4547ULL, 0x9e429e84dc211a9eULL, + 0xca0fca1ec589d4caULL, 0x2db42d75995a582dULL, 0xbfc6bf9179632ebfULL, + 0x071c07381b0e3f07ULL, 0xad8ead012347acadULL, 0x5a755aea2fb4b05aULL, + 0x8336836cb51bef83ULL, 0x33cc3385ff66b633ULL, 0x6391633ff2c65c63ULL, + 0x020802100a041202ULL, 0xaa92aa39384993aaULL, 0x71d971afa8e2de71ULL, + 0xc807c80ecf8dc6c8ULL, 0x196419c87d32d119ULL, 0x4939497270923b49ULL, + 0xd943d9869aaf5fd9ULL, 0xf2eff2c31df931f2ULL, 0xe3abe34b48dba8e3ULL, + 0x5b715be22ab6b95bULL, 0x881a8834920dbc88ULL, 0x9a529aa4c8293e9aULL, + 0x2698262dbe4c0b26ULL, 0x32c8328dfa64bf32ULL, 0xb0fab0e94a7d59b0ULL, + 0xe983e91b6acff2e9ULL, 0x0f3c0f78331e770fULL, 0xd573d5e6a6b733d5ULL, + 0x803a8074ba1df480ULL, 0xbec2be997c6127beULL, 0xcd13cd26de87ebcdULL, + 0x34d034bde4688934ULL, 0x483d487a75903248ULL, 0xffdbffab24e354ffULL, + 0x7af57af78ff48d7aULL, 0x907a90f4ea3d6490ULL, 0x5f615fc23ebe9d5fULL, + 0x2080201da0403d20ULL, 0x68bd6867d5d00f68ULL, 0x1a681ad07234ca1aULL, + 0xae82ae192c41b7aeULL, 0xb4eab4c95e757db4ULL, 0x544d549a19a8ce54ULL, + 0x937693ece53b7f93ULL, 0x2288220daa442f22ULL, 0x648d6407e9c86364ULL, + 0xf1e3f1db12ff2af1ULL, 0x73d173bfa2e6cc73ULL, 0x124812905a248212ULL, + 0x401d403a5d807a40ULL, 0x0820084028104808ULL, 0xc32bc356e89b95c3ULL, + 0xec97ec337bc5dfecULL, 0xdb4bdb9690ab4ddbULL, 0xa1bea1611f5fc0a1ULL, + 0x8d0e8d1c8307918dULL, 0x3df43df5c97ac83dULL, 0x976697ccf1335b97ULL, + 0x0000000000000000ULL, 0xcf1bcf36d483f9cfULL, 0x2bac2b4587566e2bULL, + 0x76c57697b3ece176ULL, 0x82328264b019e682ULL, 0xd67fd6fea9b128d6ULL, + 0x1b6c1bd87736c31bULL, 0xb5eeb5c15b7774b5ULL, 0xaf86af112943beafULL, + 0x6ab56a77dfd41d6aULL, 0x505d50ba0da0ea50ULL, 0x450945124c8a5745ULL, + 0xf3ebf3cb18fb38f3ULL, 0x30c0309df060ad30ULL, 0xef9bef2b74c3c4efULL, + 0x3ffc3fe5c37eda3fULL, 0x554955921caac755ULL, 0xa2b2a2791059dba2ULL, + 0xea8fea0365c9e9eaULL, 0x6589650fecca6a65ULL, 0xbad2bab9686903baULL, + 0x2fbc2f65935e4a2fULL, 0xc027c04ee79d8ec0ULL, 0xde5fdebe81a160deULL, + 0x1c701ce06c38fc1cULL, 0xfdd3fdbb2ee746fdULL, 0x4d294d52649a1f4dULL, + 0x927292e4e0397692ULL, 0x75c9758fbceafa75ULL, 0x061806301e0c3606ULL, + 0x8a128a249809ae8aULL, 0xb2f2b2f940794bb2ULL, 0xe6bfe66359d185e6ULL, + 0x0e380e70361c7e0eULL, 0x1f7c1ff8633ee71fULL, 0x62956237f7c45562ULL, + 0xd477d4eea3b53ad4ULL, 0xa89aa829324d81a8ULL, 0x966296c4f4315296ULL, + 0xf9c3f99b3aef62f9ULL, 0xc533c566f697a3c5ULL, 0x25942535b14a1025ULL, + 0x597959f220b2ab59ULL, 0x842a8454ae15d084ULL, 0x72d572b7a7e4c572ULL, + 0x39e439d5dd72ec39ULL, 0x4c2d4c5a6198164cULL, 0x5e655eca3bbc945eULL, + 0x78fd78e785f09f78ULL, 0x38e038ddd870e538ULL, 0x8c0a8c148605988cULL, + 0xd163d1c6b2bf17d1ULL, 0xa5aea5410b57e4a5ULL, 0xe2afe2434dd9a1e2ULL, + 0x6199612ff8c24e61ULL, 0xb3f6b3f1457b42b3ULL, 0x21842115a5423421ULL, + 0x9c4a9c94d625089cULL, 0x1e781ef0663cee1eULL, 0x4311432252866143ULL, + 0xc73bc776fc93b1c7ULL, 0xfcd7fcb32be54ffcULL, 0x0410042014082404ULL, + 0x515951b208a2e351ULL, 0x995e99bcc72f2599ULL, 0x6da96d4fc4da226dULL, + 0x0d340d68391a650dULL, 0xfacffa8335e979faULL, 0xdf5bdfb684a369dfULL, + 0x7ee57ed79bfca97eULL, 0x2490243db4481924ULL, 0x3bec3bc5d776fe3bULL, + 0xab96ab313d4b9aabULL, 0xce1fce3ed181f0ceULL, 0x1144118855229911ULL, + 0x8f068f0c8903838fULL, 0x4e254e4a6b9c044eULL, 0xb7e6b7d1517366b7ULL, + 0xeb8beb0b60cbe0ebULL, 0x3cf03cfdcc78c13cULL, 0x813e817cbf1ffd81ULL, + 0x946a94d4fe354094ULL, 0xf7fbf7eb0cf31cf7ULL, 0xb9deb9a1676f18b9ULL, + 0x134c13985f268b13ULL, 0x2cb02c7d9c58512cULL, 0xd36bd3d6b8bb05d3ULL, + 0xe7bbe76b5cd38ce7ULL, 0x6ea56e57cbdc396eULL, 0xc437c46ef395aac4ULL, + 0x030c03180f061b03ULL, 0x5645568a13acdc56ULL, 0x440d441a49885e44ULL, + 0x7fe17fdf9efea07fULL, 0xa99ea921374f88a9ULL, 0x2aa82a4d8254672aULL, + 0xbbd6bbb16d6b0abbULL, 0xc123c146e29f87c1ULL, 0x535153a202a6f153ULL, + 0xdc57dcae8ba572dcULL, 0x0b2c0b582716530bULL, 0x9d4e9d9cd327019dULL, + 0x6cad6c47c1d82b6cULL, 0x31c43195f562a431ULL, 0x74cd7487b9e8f374ULL, + 0xf6fff6e309f115f6ULL, 0x4605460a438c4c46ULL, 0xac8aac092645a5acULL, + 0x891e893c970fb589ULL, 0x145014a04428b414ULL, 0xe1a3e15b42dfbae1ULL, + 0x165816b04e2ca616ULL, 0x3ae83acdd274f73aULL, 0x69b9696fd0d20669ULL, + 0x092409482d124109ULL, 0x70dd70a7ade0d770ULL, 0xb6e2b6d954716fb6ULL, + 0xd067d0ceb7bd1ed0ULL, 0xed93ed3b7ec7d6edULL, 0xcc17cc2edb85e2ccULL, + 0x4215422a57846842ULL, 0x985a98b4c22d2c98ULL, 0xa4aaa4490e55eda4ULL, + 0x28a0285d88507528ULL, 0x5c6d5cda31b8865cULL, 0xf8c7f8933fed6bf8ULL, + 0x86228644a411c286ULL, +}; + +static const u64 rc[WHIRLPOOL_ROUNDS + 1] = { + 0x0000000000000000ULL, 0x1823c6e887b8014fULL, 0x36a6d2f5796f9152ULL, + 0x60bc9b8ea30c7b35ULL, 0x1de0d7c22e4bfe57ULL, 0x157737e59ff04adaULL, + 0x58c9290ab1a06b85ULL, 0xbd5d10f4cb3e0567ULL, 0xe427418ba77d95d8ULL, + 0xfbee7c66dd17479eULL, 0xca2dbf07ad5a8333ULL, +}; + +/** + * The core Whirlpool transform. + */ + +static void wp512_process_buffer(struct wp512_ctx *wctx) { + int i, r; + u64 K[8]; /* the round key */ + u64 block[8]; /* mu(buffer) */ + u64 state[8]; /* the cipher state */ + u64 L[8]; + u8 *buffer = wctx->buffer; + + for (i = 0; i < 8; i++, buffer += 8) { + block[i] = + (((u64)buffer[0] ) << 56) ^ + (((u64)buffer[1] & 0xffL) << 48) ^ + (((u64)buffer[2] & 0xffL) << 40) ^ + (((u64)buffer[3] & 0xffL) << 32) ^ + (((u64)buffer[4] & 0xffL) << 24) ^ + (((u64)buffer[5] & 0xffL) << 16) ^ + (((u64)buffer[6] & 0xffL) << 8) ^ + (((u64)buffer[7] & 0xffL) ); + } + + state[0] = block[0] ^ (K[0] = wctx->hash[0]); + state[1] = block[1] ^ (K[1] = wctx->hash[1]); + state[2] = block[2] ^ (K[2] = wctx->hash[2]); + state[3] = block[3] ^ (K[3] = wctx->hash[3]); + state[4] = block[4] ^ (K[4] = wctx->hash[4]); + state[5] = block[5] ^ (K[5] = wctx->hash[5]); + state[6] = block[6] ^ (K[6] = wctx->hash[6]); + state[7] = block[7] ^ (K[7] = wctx->hash[7]); + + for (r = 1; r <= WHIRLPOOL_ROUNDS; r++) { + + L[0] = C0[(int)(K[0] >> 56) ] ^ + C1[(int)(K[7] >> 48) & 0xff] ^ + C2[(int)(K[6] >> 40) & 0xff] ^ + C3[(int)(K[5] >> 32) & 0xff] ^ + C4[(int)(K[4] >> 24) & 0xff] ^ + C5[(int)(K[3] >> 16) & 0xff] ^ + C6[(int)(K[2] >> 8) & 0xff] ^ + C7[(int)(K[1] ) & 0xff] ^ + rc[r]; + + L[1] = C0[(int)(K[1] >> 56) ] ^ + C1[(int)(K[0] >> 48) & 0xff] ^ + C2[(int)(K[7] >> 40) & 0xff] ^ + C3[(int)(K[6] >> 32) & 0xff] ^ + C4[(int)(K[5] >> 24) & 0xff] ^ + C5[(int)(K[4] >> 16) & 0xff] ^ + C6[(int)(K[3] >> 8) & 0xff] ^ + C7[(int)(K[2] ) & 0xff]; + + L[2] = C0[(int)(K[2] >> 56) ] ^ + C1[(int)(K[1] >> 48) & 0xff] ^ + C2[(int)(K[0] >> 40) & 0xff] ^ + C3[(int)(K[7] >> 32) & 0xff] ^ + C4[(int)(K[6] >> 24) & 0xff] ^ + C5[(int)(K[5] >> 16) & 0xff] ^ + C6[(int)(K[4] >> 8) & 0xff] ^ + C7[(int)(K[3] ) & 0xff]; + + L[3] = C0[(int)(K[3] >> 56) ] ^ + C1[(int)(K[2] >> 48) & 0xff] ^ + C2[(int)(K[1] >> 40) & 0xff] ^ + C3[(int)(K[0] >> 32) & 0xff] ^ + C4[(int)(K[7] >> 24) & 0xff] ^ + C5[(int)(K[6] >> 16) & 0xff] ^ + C6[(int)(K[5] >> 8) & 0xff] ^ + C7[(int)(K[4] ) & 0xff]; + + L[4] = C0[(int)(K[4] >> 56) ] ^ + C1[(int)(K[3] >> 48) & 0xff] ^ + C2[(int)(K[2] >> 40) & 0xff] ^ + C3[(int)(K[1] >> 32) & 0xff] ^ + C4[(int)(K[0] >> 24) & 0xff] ^ + C5[(int)(K[7] >> 16) & 0xff] ^ + C6[(int)(K[6] >> 8) & 0xff] ^ + C7[(int)(K[5] ) & 0xff]; + + L[5] = C0[(int)(K[5] >> 56) ] ^ + C1[(int)(K[4] >> 48) & 0xff] ^ + C2[(int)(K[3] >> 40) & 0xff] ^ + C3[(int)(K[2] >> 32) & 0xff] ^ + C4[(int)(K[1] >> 24) & 0xff] ^ + C5[(int)(K[0] >> 16) & 0xff] ^ + C6[(int)(K[7] >> 8) & 0xff] ^ + C7[(int)(K[6] ) & 0xff]; + + L[6] = C0[(int)(K[6] >> 56) ] ^ + C1[(int)(K[5] >> 48) & 0xff] ^ + C2[(int)(K[4] >> 40) & 0xff] ^ + C3[(int)(K[3] >> 32) & 0xff] ^ + C4[(int)(K[2] >> 24) & 0xff] ^ + C5[(int)(K[1] >> 16) & 0xff] ^ + C6[(int)(K[0] >> 8) & 0xff] ^ + C7[(int)(K[7] ) & 0xff]; + + L[7] = C0[(int)(K[7] >> 56) ] ^ + C1[(int)(K[6] >> 48) & 0xff] ^ + C2[(int)(K[5] >> 40) & 0xff] ^ + C3[(int)(K[4] >> 32) & 0xff] ^ + C4[(int)(K[3] >> 24) & 0xff] ^ + C5[(int)(K[2] >> 16) & 0xff] ^ + C6[(int)(K[1] >> 8) & 0xff] ^ + C7[(int)(K[0] ) & 0xff]; + + K[0] = L[0]; + K[1] = L[1]; + K[2] = L[2]; + K[3] = L[3]; + K[4] = L[4]; + K[5] = L[5]; + K[6] = L[6]; + K[7] = L[7]; + + L[0] = C0[(int)(state[0] >> 56) ] ^ + C1[(int)(state[7] >> 48) & 0xff] ^ + C2[(int)(state[6] >> 40) & 0xff] ^ + C3[(int)(state[5] >> 32) & 0xff] ^ + C4[(int)(state[4] >> 24) & 0xff] ^ + C5[(int)(state[3] >> 16) & 0xff] ^ + C6[(int)(state[2] >> 8) & 0xff] ^ + C7[(int)(state[1] ) & 0xff] ^ + K[0]; + + L[1] = C0[(int)(state[1] >> 56) ] ^ + C1[(int)(state[0] >> 48) & 0xff] ^ + C2[(int)(state[7] >> 40) & 0xff] ^ + C3[(int)(state[6] >> 32) & 0xff] ^ + C4[(int)(state[5] >> 24) & 0xff] ^ + C5[(int)(state[4] >> 16) & 0xff] ^ + C6[(int)(state[3] >> 8) & 0xff] ^ + C7[(int)(state[2] ) & 0xff] ^ + K[1]; + + L[2] = C0[(int)(state[2] >> 56) ] ^ + C1[(int)(state[1] >> 48) & 0xff] ^ + C2[(int)(state[0] >> 40) & 0xff] ^ + C3[(int)(state[7] >> 32) & 0xff] ^ + C4[(int)(state[6] >> 24) & 0xff] ^ + C5[(int)(state[5] >> 16) & 0xff] ^ + C6[(int)(state[4] >> 8) & 0xff] ^ + C7[(int)(state[3] ) & 0xff] ^ + K[2]; + + L[3] = C0[(int)(state[3] >> 56) ] ^ + C1[(int)(state[2] >> 48) & 0xff] ^ + C2[(int)(state[1] >> 40) & 0xff] ^ + C3[(int)(state[0] >> 32) & 0xff] ^ + C4[(int)(state[7] >> 24) & 0xff] ^ + C5[(int)(state[6] >> 16) & 0xff] ^ + C6[(int)(state[5] >> 8) & 0xff] ^ + C7[(int)(state[4] ) & 0xff] ^ + K[3]; + + L[4] = C0[(int)(state[4] >> 56) ] ^ + C1[(int)(state[3] >> 48) & 0xff] ^ + C2[(int)(state[2] >> 40) & 0xff] ^ + C3[(int)(state[1] >> 32) & 0xff] ^ + C4[(int)(state[0] >> 24) & 0xff] ^ + C5[(int)(state[7] >> 16) & 0xff] ^ + C6[(int)(state[6] >> 8) & 0xff] ^ + C7[(int)(state[5] ) & 0xff] ^ + K[4]; + + L[5] = C0[(int)(state[5] >> 56) ] ^ + C1[(int)(state[4] >> 48) & 0xff] ^ + C2[(int)(state[3] >> 40) & 0xff] ^ + C3[(int)(state[2] >> 32) & 0xff] ^ + C4[(int)(state[1] >> 24) & 0xff] ^ + C5[(int)(state[0] >> 16) & 0xff] ^ + C6[(int)(state[7] >> 8) & 0xff] ^ + C7[(int)(state[6] ) & 0xff] ^ + K[5]; + + L[6] = C0[(int)(state[6] >> 56) ] ^ + C1[(int)(state[5] >> 48) & 0xff] ^ + C2[(int)(state[4] >> 40) & 0xff] ^ + C3[(int)(state[3] >> 32) & 0xff] ^ + C4[(int)(state[2] >> 24) & 0xff] ^ + C5[(int)(state[1] >> 16) & 0xff] ^ + C6[(int)(state[0] >> 8) & 0xff] ^ + C7[(int)(state[7] ) & 0xff] ^ + K[6]; + + L[7] = C0[(int)(state[7] >> 56) ] ^ + C1[(int)(state[6] >> 48) & 0xff] ^ + C2[(int)(state[5] >> 40) & 0xff] ^ + C3[(int)(state[4] >> 32) & 0xff] ^ + C4[(int)(state[3] >> 24) & 0xff] ^ + C5[(int)(state[2] >> 16) & 0xff] ^ + C6[(int)(state[1] >> 8) & 0xff] ^ + C7[(int)(state[0] ) & 0xff] ^ + K[7]; + + state[0] = L[0]; + state[1] = L[1]; + state[2] = L[2]; + state[3] = L[3]; + state[4] = L[4]; + state[5] = L[5]; + state[6] = L[6]; + state[7] = L[7]; + } + /* + * apply the Miyaguchi-Preneel compression function: + */ + wctx->hash[0] ^= state[0] ^ block[0]; + wctx->hash[1] ^= state[1] ^ block[1]; + wctx->hash[2] ^= state[2] ^ block[2]; + wctx->hash[3] ^= state[3] ^ block[3]; + wctx->hash[4] ^= state[4] ^ block[4]; + wctx->hash[5] ^= state[5] ^ block[5]; + wctx->hash[6] ^= state[6] ^ block[6]; + wctx->hash[7] ^= state[7] ^ block[7]; + +} + +static void wp512_init (void *ctx) { + int i; + struct wp512_ctx *wctx = ctx; + + memset(wctx->bitLength, 0, 32); + wctx->bufferBits = wctx->bufferPos = 0; + wctx->buffer[0] = 0; + for (i = 0; i < 8; i++) { + wctx->hash[i] = 0L; + } +} + +static void wp512_update(void *ctx, const u8 *source, unsigned int len) +{ + + struct wp512_ctx *wctx = ctx; + int sourcePos = 0; + unsigned int bits_len = len * 8; // convert to number of bits + int sourceGap = (8 - ((int)bits_len & 7)) & 7; + int bufferRem = wctx->bufferBits & 7; + int i; + u32 b, carry; + u8 *buffer = wctx->buffer; + u8 *bitLength = wctx->bitLength; + int bufferBits = wctx->bufferBits; + int bufferPos = wctx->bufferPos; + + u64 value = bits_len; + for (i = 31, carry = 0; i >= 0 && (carry != 0 || value != 0ULL); i--) { + carry += bitLength[i] + ((u32)value & 0xff); + bitLength[i] = (u8)carry; + carry >>= 8; + value >>= 8; + } + while (bits_len > 8) { + b = ((source[sourcePos] << sourceGap) & 0xff) | + ((source[sourcePos + 1] & 0xff) >> (8 - sourceGap)); + buffer[bufferPos++] |= (u8)(b >> bufferRem); + bufferBits += 8 - bufferRem; + if (bufferBits == WP512_BLOCK_SIZE * 8) { + wp512_process_buffer(wctx); + bufferBits = bufferPos = 0; + } + buffer[bufferPos] = b << (8 - bufferRem); + bufferBits += bufferRem; + bits_len -= 8; + sourcePos++; + } + if (bits_len > 0) { + b = (source[sourcePos] << sourceGap) & 0xff; + buffer[bufferPos] |= b >> bufferRem; + } else { + b = 0; + } + if (bufferRem + bits_len < 8) { + bufferBits += bits_len; + } else { + bufferPos++; + bufferBits += 8 - bufferRem; + bits_len -= 8 - bufferRem; + if (bufferBits == WP512_BLOCK_SIZE * 8) { + wp512_process_buffer(wctx); + bufferBits = bufferPos = 0; + } + buffer[bufferPos] = b << (8 - bufferRem); + bufferBits += (int)bits_len; + } + + wctx->bufferBits = bufferBits; + wctx->bufferPos = bufferPos; + +} + +static void wp512_final(void *ctx, u8 *out) +{ + struct wp512_ctx *wctx = ctx; + int i; + u8 *buffer = wctx->buffer; + u8 *bitLength = wctx->bitLength; + int bufferBits = wctx->bufferBits; + int bufferPos = wctx->bufferPos; + u8 *digest = out; + + buffer[bufferPos] |= 0x80U >> (bufferBits & 7); + bufferPos++; + if (bufferPos > WP512_BLOCK_SIZE - WP512_LENGTHBYTES) { + if (bufferPos < WP512_BLOCK_SIZE) { + memset(&buffer[bufferPos], 0, WP512_BLOCK_SIZE - bufferPos); + } + wp512_process_buffer(wctx); + bufferPos = 0; + } + if (bufferPos < WP512_BLOCK_SIZE - WP512_LENGTHBYTES) { + memset(&buffer[bufferPos], 0, + (WP512_BLOCK_SIZE - WP512_LENGTHBYTES) - bufferPos); + } + bufferPos = WP512_BLOCK_SIZE - WP512_LENGTHBYTES; + memcpy(&buffer[WP512_BLOCK_SIZE - WP512_LENGTHBYTES], + bitLength, WP512_LENGTHBYTES); + wp512_process_buffer(wctx); + for (i = 0; i < WP512_DIGEST_SIZE/8; i++) { + digest[0] = (u8)(wctx->hash[i] >> 56); + digest[1] = (u8)(wctx->hash[i] >> 48); + digest[2] = (u8)(wctx->hash[i] >> 40); + digest[3] = (u8)(wctx->hash[i] >> 32); + digest[4] = (u8)(wctx->hash[i] >> 24); + digest[5] = (u8)(wctx->hash[i] >> 16); + digest[6] = (u8)(wctx->hash[i] >> 8); + digest[7] = (u8)(wctx->hash[i] ); + digest += 8; + } + wctx->bufferBits = bufferBits; + wctx->bufferPos = bufferPos; +} + +static void wp384_final(void *ctx, u8 *out) +{ + struct wp512_ctx *wctx = ctx; + u8 D[64]; + + wp512_final (wctx, D); + memcpy (out, D, WP384_DIGEST_SIZE); + memset (D, 0, WP512_DIGEST_SIZE); +} + +static void wp256_final(void *ctx, u8 *out) +{ + struct wp512_ctx *wctx = ctx; + u8 D[64]; + + wp512_final (wctx, D); + memcpy (out, D, WP256_DIGEST_SIZE); + memset (D, 0, WP512_DIGEST_SIZE); +} + +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 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 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 int __init init(void) +{ + int ret = 0; + + ret = crypto_register_alg(&wp512); + + if (ret < 0) + goto out; + + ret = crypto_register_alg(&wp384); + if (ret < 0) + { + crypto_unregister_alg(&wp512); + goto out; + } + + ret = crypto_register_alg(&wp256); + if (ret < 0) + { + crypto_unregister_alg(&wp512); + crypto_unregister_alg(&wp384); + } +out: + return ret; +} + +static void __exit fini(void) +{ + crypto_unregister_alg(&wp512); + crypto_unregister_alg(&wp384); + crypto_unregister_alg(&wp256); +} + +MODULE_ALIAS("wp384"); +MODULE_ALIAS("wp256"); + +module_init(init); +module_exit(fini); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Whirlpool Message Digest Algorithm"); diff --git a/drivers/acpi/Makefile b/drivers/acpi/Makefile index beb93c60d..bcc35ca79 100644 --- a/drivers/acpi/Makefile +++ b/drivers/acpi/Makefile @@ -47,4 +47,4 @@ obj-$(CONFIG_ACPI_DEBUG) += debug.o obj-$(CONFIG_ACPI_NUMA) += numa.o obj-$(CONFIG_ACPI_ASUS) += asus_acpi.o obj-$(CONFIG_ACPI_TOSHIBA) += toshiba_acpi.o -obj-$(CONFIG_ACPI_BUS) += scan.o +obj-$(CONFIG_ACPI_BUS) += scan.o motherboard.o diff --git a/drivers/acpi/blacklist.c b/drivers/acpi/blacklist.c index 9cf61ba9d..4c010e7f1 100644 --- a/drivers/acpi/blacklist.c +++ b/drivers/acpi/blacklist.c @@ -2,7 +2,9 @@ * blacklist.c * * Check to see if the given machine has a known bad ACPI BIOS + * or if the BIOS is too old. * + * Copyright (C) 2004 Len Brown * Copyright (C) 2002 Andy Grover * * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -30,6 +32,7 @@ #include #include #include +#include enum acpi_blacklist_predicates { @@ -56,20 +59,6 @@ struct acpi_blacklist_item */ static struct acpi_blacklist_item acpi_blacklist[] __initdata = { - /* Portege 7020, BIOS 8.10 */ - {"TOSHIB", "7020CT ", 0x19991112, ACPI_DSDT, all_versions, "Implicit Return", 0}, - /* Portege 4030 */ - {"TOSHIB", "4030 ", 0x19991112, ACPI_DSDT, all_versions, "Implicit Return", 0}, - /* Portege 310/320, BIOS 7.1 */ - {"TOSHIB", "310 ", 0x19990511, ACPI_DSDT, all_versions, "Implicit Return", 0}, - /* Seattle 2, old bios rev. */ - {"INTEL ", "440BX ", 0x00001000, ACPI_DSDT, less_than_or_equal, "Field beyond end of region", 0}, - /* ASUS K7M */ - {"ASUS ", "K7M ", 0x00001000, ACPI_DSDT, less_than_or_equal, "Field beyond end of region", 0}, - /* Intel 810 Motherboard? */ - {"MNTRAL", "MO81010A", 0x00000012, ACPI_DSDT, less_than_or_equal, "Field beyond end of region", 0}, - /* Compaq Presario 711FR */ - {"COMAPQ", "EAGLES", 0x06040000, ACPI_DSDT, less_than_or_equal, "SCI issues (C2 disabled)", 0}, /* Compaq Presario 1700 */ {"PTLTD ", " DSDT ", 0x06040000, ACPI_DSDT, less_than_or_equal, "Multiple problems", 1}, /* Sony FX120, FX140, FX150? */ @@ -84,6 +73,45 @@ static struct acpi_blacklist_item acpi_blacklist[] __initdata = }; +#if CONFIG_ACPI_BLACKLIST_YEAR + +static int __init +blacklist_by_year(void) +{ + int year; + char *s = dmi_get_system_info(DMI_BIOS_DATE); + + if (!s) + return 0; + if (!*s) + return 0; + + s = strrchr(s, '/'); + if (!s) + return 0; + + s += 1; + + year = simple_strtoul(s,NULL,0); + + if (year < 100) { /* 2-digit year */ + year += 1900; + if (year < 1996) /* no dates < spec 1.0 */ + year += 100; + } + + if (year < CONFIG_ACPI_BLACKLIST_YEAR) { + printk(KERN_ERR PREFIX "BIOS age (%d) fails cutoff (%d), " + "acpi=force is required to enable ACPI\n", + year, CONFIG_ACPI_BLACKLIST_YEAR); + return 1; + } + return 0; +} +#else +static inline int blacklist_by_year(void) { return 0; } +#endif + int __init acpi_blacklisted(void) { @@ -134,6 +162,8 @@ acpi_blacklisted(void) } } + blacklisted += blacklist_by_year(); + return blacklisted; } diff --git a/drivers/acpi/dispatcher/dsmethod.c b/drivers/acpi/dispatcher/dsmethod.c index 360fbe5e8..4fd369018 100644 --- a/drivers/acpi/dispatcher/dsmethod.c +++ b/drivers/acpi/dispatcher/dsmethod.c @@ -58,15 +58,12 @@ * * FUNCTION: acpi_ds_parse_method * - * PARAMETERS: obj_handle - Node of the method - * Level - Current nesting level - * Context - Points to a method counter - * return_value - Not used + * PARAMETERS: obj_handle - Method node * * RETURN: Status * - * DESCRIPTION: Call the parser and parse the AML that is - * associated with the method. + * DESCRIPTION: Call the parser and parse the AML that is associated with the + * method. * * MUTEX: Assumes parser is locked * @@ -145,8 +142,9 @@ acpi_ds_parse_method ( return_ACPI_STATUS (AE_NO_MEMORY); } - status = acpi_ds_init_aml_walk (walk_state, op, node, obj_desc->method.aml_start, - obj_desc->method.aml_length, NULL, NULL, 1); + status = acpi_ds_init_aml_walk (walk_state, op, node, + obj_desc->method.aml_start, + obj_desc->method.aml_length, NULL, 1); if (ACPI_FAILURE (status)) { acpi_ds_delete_walk_state (walk_state); return_ACPI_STATUS (status); @@ -190,8 +188,6 @@ acpi_ds_parse_method ( * increments the thread count, and waits at the method semaphore * for clearance to execute. * - * MUTEX: Locks/unlocks parser. - * ******************************************************************************/ acpi_status @@ -250,7 +246,8 @@ acpi_ds_begin_method_execution ( * * FUNCTION: acpi_ds_call_control_method * - * PARAMETERS: walk_state - Current state of the walk + * PARAMETERS: Thread - Info for this thread + * this_walk_state - Current walk state * Op - Current Op to be walked * * RETURN: Status @@ -267,8 +264,9 @@ acpi_ds_call_control_method ( { acpi_status status; struct acpi_namespace_node *method_node; - union acpi_operand_object *obj_desc; struct acpi_walk_state *next_walk_state; + union acpi_operand_object *obj_desc; + struct acpi_parameter_info info; u32 i; @@ -309,7 +307,6 @@ acpi_ds_call_control_method ( return_ACPI_STATUS (AE_NO_MEMORY); } - /* Create and init a Root Node */ op = acpi_ps_create_scope_op (); @@ -320,7 +317,7 @@ acpi_ds_call_control_method ( status = acpi_ds_init_aml_walk (next_walk_state, op, method_node, obj_desc->method.aml_start, obj_desc->method.aml_length, - NULL, NULL, 1); + NULL, 1); if (ACPI_FAILURE (status)) { acpi_ds_delete_walk_state (next_walk_state); goto cleanup; @@ -348,9 +345,12 @@ acpi_ds_call_control_method ( */ this_walk_state->operands [this_walk_state->num_operands] = NULL; + info.parameters = &this_walk_state->operands[0]; + info.parameter_type = ACPI_PARAM_ARGS; + status = acpi_ds_init_aml_walk (next_walk_state, NULL, method_node, obj_desc->method.aml_start, obj_desc->method.aml_length, - &this_walk_state->operands[0], NULL, 3); + &info, 3); if (ACPI_FAILURE (status)) { goto cleanup; } @@ -382,7 +382,7 @@ acpi_ds_call_control_method ( /* On error, we must delete the new walk state */ cleanup: - if (next_walk_state->method_desc) { + if (next_walk_state && (next_walk_state->method_desc)) { /* Decrement the thread count on the method parse tree */ next_walk_state->method_desc->method.thread_count--; @@ -397,12 +397,13 @@ cleanup: * * FUNCTION: acpi_ds_restart_control_method * - * PARAMETERS: walk_state - State of the method when it was preempted - * Op - Pointer to new current op + * PARAMETERS: walk_state - State for preempted method (caller) + * return_desc - Return value from the called method * * RETURN: Status * - * DESCRIPTION: Restart a method that was preempted + * DESCRIPTION: Restart a method that was preempted by another (nested) method + * invocation. Handle the return value (if any) from the callee. * ******************************************************************************/ @@ -417,17 +418,35 @@ acpi_ds_restart_control_method ( ACPI_FUNCTION_TRACE_PTR ("ds_restart_control_method", walk_state); + ACPI_DEBUG_PRINT ((ACPI_DB_DISPATCH, + "****Restart [%4.4s] Op %p return_value_from_callee %p\n", + (char *) &walk_state->method_node->name, walk_state->method_call_op, + return_desc)); + + ACPI_DEBUG_PRINT ((ACPI_DB_DISPATCH, + " return_from_this_method_used?=%X res_stack %p Walk %p\n", + walk_state->return_used, + walk_state->results, walk_state)); + + /* Did the called method return a value? */ + if (return_desc) { + /* Are we actually going to use the return value? */ + if (walk_state->return_used) { - /* - * Get the return value (if any) from the previous method. - * NULL if no return value - */ + /* Save the return value from the previous method */ + status = acpi_ds_result_push (return_desc, walk_state); if (ACPI_FAILURE (status)) { acpi_ut_remove_reference (return_desc); return_ACPI_STATUS (status); } + + /* + * Save as THIS method's return value in case it is returned + * immediately to yet another method + */ + walk_state->return_desc = return_desc; } else { /* @@ -438,11 +457,6 @@ acpi_ds_restart_control_method ( } } - ACPI_DEBUG_PRINT ((ACPI_DB_DISPATCH, - "Method=%p Return=%p return_used?=%X res_stack=%p State=%p\n", - walk_state->method_call_op, return_desc, walk_state->return_used, - walk_state->results, walk_state)); - return_ACPI_STATUS (AE_OK); } diff --git a/drivers/acpi/dispatcher/dsmthdat.c b/drivers/acpi/dispatcher/dsmthdat.c index e1926534b..befe750a7 100644 --- a/drivers/acpi/dispatcher/dsmthdat.c +++ b/drivers/acpi/dispatcher/dsmthdat.c @@ -656,11 +656,13 @@ acpi_ds_store_object_to_local ( new_obj_desc, current_obj_desc)); /* - * Store this object to the Node - * (perform the indirect store) + * Store this object to the Node (perform the indirect store) + * NOTE: No implicit conversion is performed, as per the ACPI + * specification rules on storing to Locals/Args. */ status = acpi_ex_store_object_to_node (new_obj_desc, - current_obj_desc->reference.object, walk_state); + current_obj_desc->reference.object, walk_state, + ACPI_NO_IMPLICIT_CONVERSION); /* Remove local reference if we copied the object above */ diff --git a/drivers/acpi/dispatcher/dsutils.c b/drivers/acpi/dispatcher/dsutils.c index 5c6a37e05..24462789a 100644 --- a/drivers/acpi/dispatcher/dsutils.c +++ b/drivers/acpi/dispatcher/dsutils.c @@ -60,11 +60,10 @@ * * FUNCTION: acpi_ds_is_result_used * - * PARAMETERS: Op - * result_obj - * walk_state + * PARAMETERS: Op - Current Op + * walk_state - Current State * - * RETURN: Status + * RETURN: TRUE if result is used, FALSE otherwise * * DESCRIPTION: Check if a result object will be used by the parent * @@ -89,18 +88,39 @@ acpi_ds_is_result_used ( } /* - * If there is no parent, the result can't possibly be used! - * (An executing method typically has no parent, since each - * method is parsed separately) However, a method that is - * invoked from another method has a parent. + * If there is no parent, we are executing at the method level. + * An executing method typically has no parent, since each method + * is parsed separately. */ if (!op->common.parent) { + /* + * If this is the last statement in the method, we know it is not a + * Return() operator (would not come here.) The following code is the + * optional support for a so-called "implicit return". Some AML code + * assumes that the last value of the method is "implicitly" returned + * to the caller. Just save the last result as the return value. + * NOTE: this is optional because the ASL language does not actually + * support this behavior. + */ + if ((acpi_gbl_enable_interpreter_slack) && + (walk_state->parser_state.aml >= walk_state->parser_state.aml_end)) { + ACPI_DEBUG_PRINT ((ACPI_DB_DISPATCH, + "Result of [%s] will be implicitly returned\n", + acpi_ps_get_opcode_name (op->common.aml_opcode))); + + /* Use the top of the result stack as the implicit return value */ + + walk_state->return_desc = walk_state->results->results.obj_desc[0]; + return_VALUE (TRUE); + } + + /* No parent, the return value cannot possibly be used */ + return_VALUE (FALSE); } - /* - * Get info on the parent. The root Op is AML_SCOPE - */ + /* Get info on the parent. The root_op is AML_SCOPE */ + parent_info = acpi_ps_get_opcode_info (op->common.parent->common.aml_opcode); if (parent_info->class == AML_CLASS_UNKNOWN) { ACPI_DEBUG_PRINT ((ACPI_DB_ERROR, "Unknown parent opcode. Op=%p\n", op)); @@ -204,9 +224,9 @@ result_not_used: * * FUNCTION: acpi_ds_delete_result_if_not_used * - * PARAMETERS: Op - * result_obj - * walk_state + * PARAMETERS: Op - Current parse Op + * result_obj - Result of the operation + * walk_state - Current state * * RETURN: Status * @@ -338,8 +358,9 @@ acpi_ds_clear_operands ( * * FUNCTION: acpi_ds_create_operand * - * PARAMETERS: walk_state - * Arg + * PARAMETERS: walk_state - Current walk state + * Arg - Parse object for the argument + * arg_index - Which argument (zero based) * * RETURN: Status * diff --git a/drivers/acpi/dispatcher/dswload.c b/drivers/acpi/dispatcher/dswload.c index 409aa6e95..9c0d99e09 100644 --- a/drivers/acpi/dispatcher/dswload.c +++ b/drivers/acpi/dispatcher/dswload.c @@ -50,6 +50,9 @@ #include #include +#ifdef _ACPI_ASL_COMPILER +#include +#endif #define _COMPONENT ACPI_DISPATCHER ACPI_MODULE_NAME ("dswload") @@ -180,7 +183,17 @@ acpi_ds_load1_begin_op ( status = acpi_ns_lookup (walk_state->scope_info, path, object_type, ACPI_IMODE_EXECUTE, ACPI_NS_SEARCH_PARENT, walk_state, &(node)); if (ACPI_FAILURE (status)) { +#ifdef _ACPI_ASL_COMPILER + if (status == AE_NOT_FOUND) { + acpi_dm_add_to_external_list (path); + status = AE_OK; + } + else { + ACPI_REPORT_NSERROR (path, status); + } +#else ACPI_REPORT_NSERROR (path, status); +#endif return (status); } @@ -529,7 +542,16 @@ acpi_ds_load2_begin_op ( status = acpi_ns_lookup (walk_state->scope_info, buffer_ptr, object_type, ACPI_IMODE_EXECUTE, ACPI_NS_SEARCH_PARENT, walk_state, &(node)); if (ACPI_FAILURE (status)) { +#ifdef _ACPI_ASL_COMPILER + if (status == AE_NOT_FOUND) { + status = AE_OK; + } + else { + ACPI_REPORT_NSERROR (buffer_ptr, status); + } +#else ACPI_REPORT_NSERROR (buffer_ptr, status); +#endif return_ACPI_STATUS (status); } /* diff --git a/drivers/acpi/dispatcher/dswstate.c b/drivers/acpi/dispatcher/dswstate.c index 2b3956226..81c6b46ac 100644 --- a/drivers/acpi/dispatcher/dswstate.c +++ b/drivers/acpi/dispatcher/dswstate.c @@ -906,8 +906,7 @@ acpi_ds_init_aml_walk ( struct acpi_namespace_node *method_node, u8 *aml_start, u32 aml_length, - union acpi_operand_object **params, - union acpi_operand_object **return_obj_desc, + struct acpi_parameter_info *info, u32 pass_number) { acpi_status status; @@ -926,8 +925,17 @@ acpi_ds_init_aml_walk ( /* The next_op of the next_walk will be the beginning of the method */ walk_state->next_op = NULL; - walk_state->params = params; - walk_state->caller_return_desc = return_obj_desc; + + if (info) { + if (info->parameter_type == ACPI_PARAM_GPE) { + walk_state->gpe_event_info = ACPI_CAST_PTR (struct acpi_gpe_event_info, + info->parameters); + } + else { + walk_state->params = info->parameters; + walk_state->caller_return_desc = &info->return_object; + } + } status = acpi_ps_init_scope (&walk_state->parser_state, op); if (ACPI_FAILURE (status)) { @@ -949,7 +957,7 @@ acpi_ds_init_aml_walk ( /* Init the method arguments */ - status = acpi_ds_method_data_init_args (params, ACPI_METHOD_NUM_ARGS, walk_state); + status = acpi_ds_method_data_init_args (walk_state->params, ACPI_METHOD_NUM_ARGS, walk_state); if (ACPI_FAILURE (status)) { return_ACPI_STATUS (status); } diff --git a/drivers/acpi/events/evevent.c b/drivers/acpi/events/evevent.c index abdb3a5a2..558ec7b33 100644 --- a/drivers/acpi/events/evevent.c +++ b/drivers/acpi/events/evevent.c @@ -50,7 +50,7 @@ /******************************************************************************* * - * FUNCTION: acpi_ev_initialize + * FUNCTION: acpi_ev_initialize_events * * PARAMETERS: None * @@ -61,13 +61,13 @@ ******************************************************************************/ acpi_status -acpi_ev_initialize ( +acpi_ev_initialize_events ( void) { acpi_status status; - ACPI_FUNCTION_TRACE ("ev_initialize"); + ACPI_FUNCTION_TRACE ("ev_initialize_events"); /* Make sure we have ACPI tables */ @@ -104,7 +104,7 @@ acpi_ev_initialize ( /******************************************************************************* * - * FUNCTION: acpi_ev_handler_initialize + * FUNCTION: acpi_ev_install_xrupt_handlers * * PARAMETERS: None * @@ -115,13 +115,13 @@ acpi_ev_initialize ( ******************************************************************************/ acpi_status -acpi_ev_handler_initialize ( +acpi_ev_install_xrupt_handlers ( void) { acpi_status status; - ACPI_FUNCTION_TRACE ("ev_handler_initialize"); + ACPI_FUNCTION_TRACE ("ev_install_xrupt_handlers"); /* Install the SCI handler */ diff --git a/drivers/acpi/events/evgpe.c b/drivers/acpi/events/evgpe.c index 221dca54d..afb36886b 100644 --- a/drivers/acpi/events/evgpe.c +++ b/drivers/acpi/events/evgpe.c @@ -49,6 +49,253 @@ ACPI_MODULE_NAME ("evgpe") +/******************************************************************************* + * + * FUNCTION: acpi_ev_set_gpe_type + * + * PARAMETERS: gpe_event_info - GPE to set + * Type - New type + * + * RETURN: Status + * + * DESCRIPTION: Sets the new type for the GPE (wake, run, or wake/run) + * + ******************************************************************************/ + +acpi_status +acpi_ev_set_gpe_type ( + struct acpi_gpe_event_info *gpe_event_info, + u8 type) +{ + acpi_status status; + + + ACPI_FUNCTION_TRACE ("ev_set_gpe_type"); + + + /* Validate type and update register enable masks */ + + switch (type) { + case ACPI_GPE_TYPE_WAKE: + case ACPI_GPE_TYPE_RUNTIME: + case ACPI_GPE_TYPE_WAKE_RUN: + break; + + default: + return_ACPI_STATUS (AE_BAD_PARAMETER); + } + + /* Disable the GPE if currently enabled */ + + status = acpi_ev_disable_gpe (gpe_event_info); + + /* Type was validated above */ + + gpe_event_info->flags &= ~ACPI_GPE_TYPE_MASK; /* Clear type bits */ + gpe_event_info->flags |= type; /* Insert type */ + return_ACPI_STATUS (status); +} + + +/******************************************************************************* + * + * FUNCTION: acpi_ev_update_gpe_enable_masks + * + * PARAMETERS: gpe_event_info - GPE to update + * Type - What to do: ACPI_GPE_DISABLE or + * ACPI_GPE_ENABLE + * + * RETURN: Status + * + * DESCRIPTION: Updates GPE register enable masks based on the GPE type + * + ******************************************************************************/ + +acpi_status +acpi_ev_update_gpe_enable_masks ( + struct acpi_gpe_event_info *gpe_event_info, + u8 type) +{ + struct acpi_gpe_register_info *gpe_register_info; + u8 register_bit; + + + ACPI_FUNCTION_TRACE ("ev_update_gpe_enable_masks"); + + + gpe_register_info = gpe_event_info->register_info; + if (!gpe_register_info) { + return_ACPI_STATUS (AE_NOT_EXIST); + } + register_bit = gpe_event_info->register_bit; + + /* 1) Disable case. Simply clear all enable bits */ + + if (type == ACPI_GPE_DISABLE) { + ACPI_CLEAR_BIT (gpe_register_info->enable_for_wake, register_bit); + ACPI_CLEAR_BIT (gpe_register_info->enable_for_run, register_bit); + return_ACPI_STATUS (AE_OK); + } + + /* 2) Enable case. Set/Clear the appropriate enable bits */ + + switch (gpe_event_info->flags & ACPI_GPE_TYPE_MASK) { + case ACPI_GPE_TYPE_WAKE: + ACPI_SET_BIT (gpe_register_info->enable_for_wake, register_bit); + ACPI_CLEAR_BIT (gpe_register_info->enable_for_run, register_bit); + break; + + case ACPI_GPE_TYPE_RUNTIME: + ACPI_CLEAR_BIT (gpe_register_info->enable_for_wake, register_bit); + ACPI_SET_BIT (gpe_register_info->enable_for_run, register_bit); + break; + + case ACPI_GPE_TYPE_WAKE_RUN: + ACPI_SET_BIT (gpe_register_info->enable_for_wake, register_bit); + ACPI_SET_BIT (gpe_register_info->enable_for_run, register_bit); + break; + + default: + return_ACPI_STATUS (AE_BAD_PARAMETER); + } + + return_ACPI_STATUS (AE_OK); +} + + +/******************************************************************************* + * + * FUNCTION: acpi_ev_enable_gpe + * + * PARAMETERS: gpe_event_info - GPE to enable + * write_to_hardware - Enable now, or just mark data structs + * (WAKE GPEs should be deferred) + * + * RETURN: Status + * + * DESCRIPTION: Enable a GPE based on the GPE type + * + ******************************************************************************/ + +acpi_status +acpi_ev_enable_gpe ( + struct acpi_gpe_event_info *gpe_event_info, + u8 write_to_hardware) +{ + acpi_status status; + + + ACPI_FUNCTION_TRACE ("ev_enable_gpe"); + + + /* Make sure HW enable masks are updated */ + + status = acpi_ev_update_gpe_enable_masks (gpe_event_info, ACPI_GPE_ENABLE); + if (ACPI_FAILURE (status)) { + return_ACPI_STATUS (status); + } + + /* Mark wake-enabled or HW enable, or both */ + + switch (gpe_event_info->flags & ACPI_GPE_TYPE_MASK) { + case ACPI_GPE_TYPE_WAKE: + + ACPI_SET_BIT (gpe_event_info->flags, ACPI_GPE_WAKE_ENABLED); + break; + + case ACPI_GPE_TYPE_WAKE_RUN: + + ACPI_SET_BIT (gpe_event_info->flags, ACPI_GPE_WAKE_ENABLED); + + /*lint -fallthrough */ + + case ACPI_GPE_TYPE_RUNTIME: + + ACPI_SET_BIT (gpe_event_info->flags, ACPI_GPE_RUN_ENABLED); + + if (write_to_hardware) { + /* Clear the GPE (of stale events), then enable it */ + + status = acpi_hw_clear_gpe (gpe_event_info); + if (ACPI_FAILURE (status)) { + return_ACPI_STATUS (status); + } + + /* Enable the requested runtime GPE */ + + status = acpi_hw_write_gpe_enable_reg (gpe_event_info); + } + break; + + default: + return_ACPI_STATUS (AE_BAD_PARAMETER); + } + + return_ACPI_STATUS (AE_OK); +} + + +/******************************************************************************* + * + * FUNCTION: acpi_ev_disable_gpe + * + * PARAMETERS: gpe_event_info - GPE to disable + * + * RETURN: Status + * + * DESCRIPTION: Disable a GPE based on the GPE type + * + ******************************************************************************/ + +acpi_status +acpi_ev_disable_gpe ( + struct acpi_gpe_event_info *gpe_event_info) +{ + acpi_status status; + + + ACPI_FUNCTION_TRACE ("ev_disable_gpe"); + + + if (!(gpe_event_info->flags & ACPI_GPE_ENABLE_MASK)) { + return_ACPI_STATUS (AE_OK); + } + + /* Make sure HW enable masks are updated */ + + status = acpi_ev_update_gpe_enable_masks (gpe_event_info, ACPI_GPE_DISABLE); + if (ACPI_FAILURE (status)) { + return_ACPI_STATUS (status); + } + + /* Mark wake-disabled or HW disable, or both */ + + switch (gpe_event_info->flags & ACPI_GPE_TYPE_MASK) { + case ACPI_GPE_TYPE_WAKE: + ACPI_CLEAR_BIT (gpe_event_info->flags, ACPI_GPE_WAKE_ENABLED); + break; + + case ACPI_GPE_TYPE_WAKE_RUN: + ACPI_CLEAR_BIT (gpe_event_info->flags, ACPI_GPE_WAKE_ENABLED); + + /*lint -fallthrough */ + + case ACPI_GPE_TYPE_RUNTIME: + + /* Disable the requested runtime GPE */ + + ACPI_CLEAR_BIT (gpe_event_info->flags, ACPI_GPE_RUN_ENABLED); + status = acpi_hw_write_gpe_enable_reg (gpe_event_info); + break; + + default: + return_ACPI_STATUS (AE_BAD_PARAMETER); + } + + return_ACPI_STATUS (AE_OK); +} + + /******************************************************************************* * * FUNCTION: acpi_ev_get_gpe_event_info @@ -139,11 +386,12 @@ acpi_ev_gpe_detect ( u32 int_status = ACPI_INTERRUPT_NOT_HANDLED; u8 enabled_status_byte; struct acpi_gpe_register_info *gpe_register_info; - u32 in_value; + u32 status_reg; + u32 enable_reg; acpi_status status; struct acpi_gpe_block_info *gpe_block; - u32 i; - u32 j; + acpi_native_uint i; + acpi_native_uint j; ACPI_FUNCTION_NAME ("ev_gpe_detect"); @@ -171,33 +419,32 @@ acpi_ev_gpe_detect ( /* Read the Status Register */ - status = acpi_hw_low_level_read (ACPI_GPE_REGISTER_WIDTH, &in_value, + status = acpi_hw_low_level_read (ACPI_GPE_REGISTER_WIDTH, &status_reg, &gpe_register_info->status_address); - gpe_register_info->status = (u8) in_value; if (ACPI_FAILURE (status)) { goto unlock_and_exit; } /* Read the Enable Register */ - status = acpi_hw_low_level_read (ACPI_GPE_REGISTER_WIDTH, &in_value, + status = acpi_hw_low_level_read (ACPI_GPE_REGISTER_WIDTH, &enable_reg, &gpe_register_info->enable_address); - gpe_register_info->enable = (u8) in_value; if (ACPI_FAILURE (status)) { goto unlock_and_exit; } ACPI_DEBUG_PRINT ((ACPI_DB_INTERRUPTS, "GPE pair: Status %8.8X%8.8X = %02X, Enable %8.8X%8.8X = %02X\n", - ACPI_FORMAT_UINT64 (gpe_register_info->status_address.address), - gpe_register_info->status, - ACPI_FORMAT_UINT64 (gpe_register_info->enable_address.address), - gpe_register_info->enable)); + ACPI_FORMAT_UINT64 ( + gpe_register_info->status_address.address), + status_reg, + ACPI_FORMAT_UINT64 ( + gpe_register_info->enable_address.address), + enable_reg)); /* First check if there is anything active at all in this register */ - enabled_status_byte = (u8) (gpe_register_info->status & - gpe_register_info->enable); + enabled_status_byte = (u8) (status_reg & enable_reg); if (!enabled_status_byte) { /* No active GPEs in this register, move on */ @@ -216,7 +463,7 @@ acpi_ev_gpe_detect ( */ int_status |= acpi_ev_gpe_dispatch ( &gpe_block->event_info[(i * ACPI_GPE_REGISTER_WIDTH) + j], - j + gpe_register_info->base_gpe_number); + (u32) j + gpe_register_info->base_gpe_number); } } } @@ -255,6 +502,7 @@ acpi_ev_asynch_execute_gpe_method ( u32 gpe_number = 0; acpi_status status; struct acpi_gpe_event_info local_gpe_event_info; + struct acpi_parameter_info info; ACPI_FUNCTION_TRACE ("ev_asynch_execute_gpe_method"); @@ -272,6 +520,10 @@ acpi_ev_asynch_execute_gpe_method ( return_VOID; } + /* Set the GPE flags for return to enabled state */ + + (void) acpi_ev_enable_gpe (gpe_event_info, FALSE); + /* * Take a snapshot of the GPE info for this level - we copy the * info to prevent a race condition with remove_handler/remove_block. @@ -283,23 +535,33 @@ acpi_ev_asynch_execute_gpe_method ( return_VOID; } - if (local_gpe_event_info.method_node) { + /* + * Must check for control method type dispatch one more + * time to avoid race with ev_gpe_install_handler + */ + if ((local_gpe_event_info.flags & ACPI_GPE_DISPATCH_MASK) == ACPI_GPE_DISPATCH_METHOD) { /* - * Invoke the GPE Method (_Lxx, _Exx): - * (Evaluate the _Lxx/_Exx control method that corresponds to this GPE.) + * Invoke the GPE Method (_Lxx, _Exx) i.e., evaluate the _Lxx/_Exx + * control method that corresponds to this GPE */ - status = acpi_ns_evaluate_by_handle (local_gpe_event_info.method_node, NULL, NULL); + info.node = local_gpe_event_info.dispatch.method_node; + info.parameters = ACPI_CAST_PTR (union acpi_operand_object *, gpe_event_info); + info.parameter_type = ACPI_PARAM_GPE; + + status = acpi_ns_evaluate_by_handle (&info); if (ACPI_FAILURE (status)) { - ACPI_REPORT_ERROR (("%s while evaluating method [%4.4s] for GPE[%2X]\n", + ACPI_REPORT_ERROR (( + "%s while evaluating method [%4.4s] for GPE[%2X]\n", acpi_format_exception (status), - acpi_ut_get_node_name (local_gpe_event_info.method_node), gpe_number)); + acpi_ut_get_node_name (local_gpe_event_info.dispatch.method_node), + gpe_number)); } } if ((local_gpe_event_info.flags & ACPI_GPE_XRUPT_TYPE_MASK) == ACPI_GPE_LEVEL_TRIGGERED) { /* - * GPE is level-triggered, we clear the GPE status bit after handling - * the event. + * GPE is level-triggered, we clear the GPE status bit after + * handling the event. */ status = acpi_hw_clear_gpe (&local_gpe_event_info); if (ACPI_FAILURE (status)) { @@ -309,7 +571,7 @@ acpi_ev_asynch_execute_gpe_method ( /* Enable this GPE */ - (void) acpi_hw_enable_gpe (&local_gpe_event_info); + (void) acpi_hw_write_gpe_enable_reg (&local_gpe_event_info); return_VOID; } @@ -354,6 +616,15 @@ acpi_ev_gpe_dispatch ( } } + /* Save current system state */ + + if (acpi_gbl_system_awake_and_running) { + ACPI_SET_BIT (gpe_event_info->flags, ACPI_GPE_SYSTEM_RUNNING); + } + else { + ACPI_CLEAR_BIT (gpe_event_info->flags, ACPI_GPE_SYSTEM_RUNNING); + } + /* * Dispatch the GPE to either an installed handler, or the control * method associated with this GPE (_Lxx or _Exx). @@ -361,10 +632,15 @@ acpi_ev_gpe_dispatch ( * If there is neither a handler nor a method, we disable the level to * prevent further events from coming in here. */ - if (gpe_event_info->handler) { - /* Invoke the installed handler (at interrupt level) */ + switch (gpe_event_info->flags & ACPI_GPE_DISPATCH_MASK) { + case ACPI_GPE_DISPATCH_HANDLER: - gpe_event_info->handler (gpe_event_info->context); + /* + * Invoke the installed handler (at interrupt level) + * Ignore return status for now. TBD: leave GPE disabled on error? + */ + (void) gpe_event_info->dispatch.handler->address ( + gpe_event_info->dispatch.handler->context); /* It is now safe to clear level-triggered events. */ @@ -377,13 +653,15 @@ acpi_ev_gpe_dispatch ( return_VALUE (ACPI_INTERRUPT_NOT_HANDLED); } } - } - else if (gpe_event_info->method_node) { + break; + + case ACPI_GPE_DISPATCH_METHOD: + /* * Disable GPE, so it doesn't keep firing before the method has a * chance to run. */ - status = acpi_hw_disable_gpe (gpe_event_info); + status = acpi_ev_disable_gpe (gpe_event_info); if (ACPI_FAILURE (status)) { ACPI_REPORT_ERROR (( "acpi_ev_gpe_dispatch: Unable to disable GPE[%2X]\n", @@ -402,8 +680,10 @@ acpi_ev_gpe_dispatch ( "acpi_ev_gpe_dispatch: Unable to queue handler for GPE[%2X], event is disabled\n", gpe_number)); } - } - else { + break; + + default: + /* No handler or method to run! */ ACPI_REPORT_ERROR (( @@ -414,15 +694,68 @@ acpi_ev_gpe_dispatch ( * Disable the GPE. The GPE will remain disabled until the ACPI * Core Subsystem is restarted, or a handler is installed. */ - status = acpi_hw_disable_gpe (gpe_event_info); + status = acpi_ev_disable_gpe (gpe_event_info); if (ACPI_FAILURE (status)) { ACPI_REPORT_ERROR (( "acpi_ev_gpe_dispatch: Unable to disable GPE[%2X]\n", gpe_number)); return_VALUE (ACPI_INTERRUPT_NOT_HANDLED); } + break; } return_VALUE (ACPI_INTERRUPT_HANDLED); } + +#ifdef ACPI_GPE_NOTIFY_CHECK + +/******************************************************************************* + * TBD: NOT USED, PROTOTYPE ONLY AND WILL PROBABLY BE REMOVED + * + * FUNCTION: acpi_ev_check_for_wake_only_gpe + * + * PARAMETERS: gpe_event_info - info for this GPE + * + * RETURN: Status + * + * DESCRIPTION: Determine if a a GPE is "wake-only". + * + * Called from Notify() code in interpreter when a "device_wake" + * Notify comes in. + * + ******************************************************************************/ + +acpi_status +acpi_ev_check_for_wake_only_gpe ( + struct acpi_gpe_event_info *gpe_event_info) +{ + acpi_status status; + + + ACPI_FUNCTION_TRACE ("ev_check_for_wake_only_gpe"); + + + if ((gpe_event_info) && /* Only >0 for _Lxx/_Exx */ + ((gpe_event_info->flags & ACPI_GPE_SYSTEM_MASK) == ACPI_GPE_SYSTEM_RUNNING)) /* System state at GPE time */ { + /* This must be a wake-only GPE, disable it */ + + status = acpi_ev_disable_gpe (gpe_event_info); + + /* Set GPE to wake-only. Do not change wake disabled/enabled status */ + + acpi_ev_set_gpe_type (gpe_event_info, ACPI_GPE_TYPE_WAKE); + + ACPI_REPORT_INFO (("GPE %p was updated from wake/run to wake-only\n", + gpe_event_info)); + + /* This was a wake-only GPE */ + + return_ACPI_STATUS (AE_WAKE_ONLY_GPE); + } + + return_ACPI_STATUS (AE_OK); +} +#endif + + diff --git a/drivers/acpi/events/evgpeblk.c b/drivers/acpi/events/evgpeblk.c index b1fbf7dec..1913f4efe 100644 --- a/drivers/acpi/events/evgpeblk.c +++ b/drivers/acpi/events/evgpeblk.c @@ -53,7 +53,7 @@ * * FUNCTION: acpi_ev_valid_gpe_event * - * PARAMETERS: gpe_event_info - Info for this GPE + * PARAMETERS: gpe_event_info - Info for this GPE * * RETURN: TRUE if the gpe_event is valid * @@ -105,17 +105,18 @@ acpi_ev_valid_gpe_event ( * FUNCTION: acpi_ev_walk_gpe_list * * PARAMETERS: gpe_walk_callback - Routine called for each GPE block + * Flags - ACPI_NOT_ISR or ACPI_ISR * * RETURN: Status * * DESCRIPTION: Walk the GPE lists. - * FUNCTION MUST BE CALLED WITH INTERRUPTS DISABLED * ******************************************************************************/ acpi_status acpi_ev_walk_gpe_list ( - ACPI_GPE_CALLBACK gpe_walk_callback) + ACPI_GPE_CALLBACK gpe_walk_callback, + u32 flags) { struct acpi_gpe_block_info *gpe_block; struct acpi_gpe_xrupt_info *gpe_xrupt_info; @@ -125,7 +126,7 @@ acpi_ev_walk_gpe_list ( ACPI_FUNCTION_TRACE ("ev_walk_gpe_list"); - acpi_os_acquire_lock (acpi_gbl_gpe_lock, ACPI_ISR); + acpi_os_acquire_lock (acpi_gbl_gpe_lock, flags); /* Walk the interrupt level descriptor list */ @@ -149,11 +150,58 @@ acpi_ev_walk_gpe_list ( } unlock_and_exit: - acpi_os_release_lock (acpi_gbl_gpe_lock, ACPI_ISR); + acpi_os_release_lock (acpi_gbl_gpe_lock, flags); return_ACPI_STATUS (status); } +/****************************************************************************** + * + * FUNCTION: acpi_ev_delete_gpe_handlers + * + * PARAMETERS: gpe_xrupt_info - GPE Interrupt info + * gpe_block - Gpe Block info + * + * RETURN: Status + * + * DESCRIPTION: Delete all Handler objects found in the GPE data structs. + * Used only prior to termination. + * + ******************************************************************************/ + +acpi_status +acpi_ev_delete_gpe_handlers ( + struct acpi_gpe_xrupt_info *gpe_xrupt_info, + struct acpi_gpe_block_info *gpe_block) +{ + struct acpi_gpe_event_info *gpe_event_info; + acpi_native_uint i; + acpi_native_uint j; + + + ACPI_FUNCTION_TRACE ("ev_delete_gpe_handlers"); + + + /* Examine each GPE Register within the block */ + + for (i = 0; i < gpe_block->register_count; i++) { + /* Now look at the individual GPEs in this byte register */ + + for (j = 0; j < ACPI_GPE_REGISTER_WIDTH; j++) { + gpe_event_info = &gpe_block->event_info[(i * ACPI_GPE_REGISTER_WIDTH) + j]; + + if ((gpe_event_info->flags & ACPI_GPE_DISPATCH_MASK) == ACPI_GPE_DISPATCH_HANDLER) { + ACPI_MEM_FREE (gpe_event_info->dispatch.handler); + gpe_event_info->dispatch.handler = NULL; + gpe_event_info->flags &= ~ACPI_GPE_DISPATCH_MASK; + } + } + } + + return_ACPI_STATUS (AE_OK); +} + + /******************************************************************************* * * FUNCTION: acpi_ev_save_method_info @@ -188,6 +236,7 @@ acpi_ev_save_method_info ( u32 gpe_number; char name[ACPI_NAME_SIZE + 1]; u8 type; + acpi_status status; ACPI_FUNCTION_TRACE ("ev_save_method_info"); @@ -206,16 +255,16 @@ acpi_ev_save_method_info ( * 2) Edge/Level determination is based on the 2nd character * of the method name * - * NOTE: Default GPE type is RUNTIME. May be changed later to WAKE if a - * _PRW object is found that points to this GPE. + * NOTE: Default GPE type is RUNTIME. May be changed later to WAKE + * if a _PRW object is found that points to this GPE. */ switch (name[1]) { case 'L': - type = ACPI_GPE_LEVEL_TRIGGERED | ACPI_GPE_TYPE_RUNTIME; + type = ACPI_GPE_LEVEL_TRIGGERED; break; case 'E': - type = ACPI_GPE_EDGE_TRIGGERED | ACPI_GPE_TYPE_RUNTIME; + type = ACPI_GPE_EDGE_TRIGGERED; break; default: @@ -253,27 +302,35 @@ acpi_ev_save_method_info ( /* * Now we can add this information to the gpe_event_info block - * for use during dispatch of this GPE. + * for use during dispatch of this GPE. Default type is RUNTIME, although + * this may change when the _PRW methods are executed later. */ gpe_event_info = &gpe_block->event_info[gpe_number - gpe_block->block_base_number]; - gpe_event_info->flags = type; - gpe_event_info->method_node = (struct acpi_namespace_node *) obj_handle; + gpe_event_info->flags = (u8) (type | ACPI_GPE_DISPATCH_METHOD | + ACPI_GPE_TYPE_RUNTIME); + + gpe_event_info->dispatch.method_node = (struct acpi_namespace_node *) obj_handle; + + /* Update enable mask, but don't enable the HW GPE as of yet */ + + status = acpi_ev_enable_gpe (gpe_event_info, FALSE); ACPI_DEBUG_PRINT ((ACPI_DB_LOAD, "Registered GPE method %s as GPE number 0x%.2X\n", name, gpe_number)); - return_ACPI_STATUS (AE_OK); + return_ACPI_STATUS (status); } /******************************************************************************* * - * FUNCTION: acpi_ev_get_gpe_type + * FUNCTION: acpi_ev_match_prw_and_gpe * * PARAMETERS: Callback from walk_namespace * - * RETURN: Status + * RETURN: Status. NOTE: We ignore errors so that the _PRW walk is + * not aborted on a single _PRW failure. * * DESCRIPTION: Called from acpi_walk_namespace. Expects each object to be a * Device. Run the _PRW method. If present, extract the GPE @@ -282,7 +339,7 @@ acpi_ev_save_method_info ( ******************************************************************************/ static acpi_status -acpi_ev_get_gpe_type ( +acpi_ev_match_prw_and_gpe ( acpi_handle obj_handle, u32 level, void *info, @@ -299,19 +356,18 @@ acpi_ev_get_gpe_type ( acpi_status status; - ACPI_FUNCTION_TRACE ("ev_get_gpe_type"); + ACPI_FUNCTION_TRACE ("ev_match_prw_and_gpe"); /* Check for a _PRW method under this device */ status = acpi_ut_evaluate_object (obj_handle, METHOD_NAME__PRW, ACPI_BTYPE_PACKAGE, &pkg_desc); - if (status == AE_NOT_FOUND) { + if (ACPI_FAILURE (status)) { + /* Ignore all errors from _PRW, we don't want to abort the subsystem */ + return_ACPI_STATUS (AE_OK); } - else if (ACPI_FAILURE (status)) { - return_ACPI_STATUS (status); - } /* The returned _PRW package must have at least two elements */ @@ -370,16 +426,21 @@ acpi_ev_get_gpe_type ( if ((gpe_device == target_gpe_device) && (gpe_number >= gpe_block->block_base_number) && (gpe_number < gpe_block->block_base_number + (gpe_block->register_count * 8))) { - /* Mark GPE for WAKE but DISABLED (even for wake) */ - gpe_event_info = &gpe_block->event_info[gpe_number - gpe_block->block_base_number]; - gpe_event_info->flags |= ACPI_GPE_TYPE_WAKE; + + /* Mark GPE for WAKE-ONLY but WAKE_DISABLED */ + + gpe_event_info->flags &= ~(ACPI_GPE_WAKE_ENABLED | ACPI_GPE_RUN_ENABLED); + status = acpi_ev_set_gpe_type (gpe_event_info, ACPI_GPE_TYPE_WAKE); + if (ACPI_FAILURE (status)) { + goto cleanup; + } + status = acpi_ev_update_gpe_enable_masks (gpe_event_info, ACPI_GPE_DISABLE); } cleanup: acpi_ut_remove_reference (pkg_desc); - - return_ACPI_STATUS (status); + return_ACPI_STATUS (AE_OK); } @@ -742,7 +803,7 @@ acpi_ev_create_gpe_info_blocks ( /* Init the event_info for each GPE within this register */ for (j = 0; j < ACPI_GPE_REGISTER_WIDTH; j++) { - this_event->bit_mask = acpi_gbl_decode_to8bit[j]; + this_event->register_bit = acpi_gbl_decode_to8bit[j]; this_event->register_info = this_register; this_event++; } @@ -817,6 +878,7 @@ acpi_ev_create_gpe_block ( acpi_status status; struct acpi_gpe_walk_info gpe_info; + ACPI_FUNCTION_TRACE ("ev_create_gpe_block"); @@ -835,6 +897,7 @@ acpi_ev_create_gpe_block ( gpe_block->register_count = register_count; gpe_block->block_base_number = gpe_block_base_number; + gpe_block->node = gpe_device; ACPI_MEMCPY (&gpe_block->block_address, gpe_block_address, sizeof (struct acpi_generic_address)); @@ -854,18 +917,6 @@ acpi_ev_create_gpe_block ( return_ACPI_STATUS (status); } - /* Dump info about this GPE block */ - - ACPI_DEBUG_PRINT ((ACPI_DB_INIT, - "GPE %02d to %02d [%4.4s] %d regs at %8.8X%8.8X on int %d\n", - gpe_block->block_base_number, - (u32) (gpe_block->block_base_number + - ((gpe_block->register_count * ACPI_GPE_REGISTER_WIDTH) -1)), - gpe_device->name.ascii, - gpe_block->register_count, - ACPI_FORMAT_UINT64 (gpe_block->block_address.address), - interrupt_level)); - /* Find all GPE methods (_Lxx, _Exx) for this block */ status = acpi_ns_walk_namespace (ACPI_TYPE_METHOD, gpe_device, @@ -873,27 +924,28 @@ acpi_ev_create_gpe_block ( gpe_block, NULL); /* - * Runtime option: Should Wake GPEs be enabled at runtime? The default is - * No,they should only be enabled just as the machine goes to sleep. + * Runtime option: Should Wake GPEs be enabled at runtime? The default + * is No,they should only be enabled just as the machine goes to sleep. */ if (acpi_gbl_leave_wake_gpes_disabled) { /* - * Differentiate RUNTIME vs WAKE GPEs, via the _PRW control methods. (Each - * GPE that has one or more _PRWs that reference it is by definition a - * WAKE GPE and will not be enabled while the machine is running.) + * Differentiate RUNTIME vs WAKE GPEs, via the _PRW control methods. + * (Each GPE that has one or more _PRWs that reference it is by + * definition a WAKE GPE and will not be enabled while the machine + * is running.) */ gpe_info.gpe_block = gpe_block; gpe_info.gpe_device = gpe_device; status = acpi_ns_walk_namespace (ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT, - ACPI_UINT32_MAX, ACPI_NS_WALK_UNLOCK, acpi_ev_get_gpe_type, + ACPI_UINT32_MAX, ACPI_NS_WALK_UNLOCK, acpi_ev_match_prw_and_gpe, &gpe_info, NULL); } /* - * Enable all GPEs in this block that are 1) "runtime" GPEs, and 2) have - * a corresponding _Lxx or _Exx method. All other GPEs must be enabled via - * the acpi_enable_gpe() external interface. + * Enable all GPEs in this block that are 1) "runtime" or "run/wake" GPEs, + * and 2) have a corresponding _Lxx or _Exx method. All other GPEs must + * be enabled via the acpi_enable_gpe() external interface. */ wake_gpe_count = 0; gpe_enabled_count = 0; @@ -903,23 +955,35 @@ acpi_ev_create_gpe_block ( /* Get the info block for this particular GPE */ gpe_event_info = &gpe_block->event_info[(i * ACPI_GPE_REGISTER_WIDTH) + j]; - if ((gpe_event_info->method_node) && - ((gpe_event_info->flags & ACPI_GPE_TYPE_MASK) == ACPI_GPE_TYPE_RUNTIME)) { - /* Enable this GPE, it is 1) RUNTIME and 2) has an _Lxx or _Exx method */ - - status = acpi_hw_enable_gpe (gpe_event_info); - if (ACPI_FAILURE (status)) { - return_ACPI_STATUS (status); - } + + if (((gpe_event_info->flags & ACPI_GPE_DISPATCH_MASK) == ACPI_GPE_DISPATCH_METHOD) && + (gpe_event_info->flags & ACPI_GPE_TYPE_RUNTIME)) { gpe_enabled_count++; } - if ((gpe_event_info->flags & ACPI_GPE_TYPE_MASK) == ACPI_GPE_TYPE_WAKE) { + if (gpe_event_info->flags & ACPI_GPE_TYPE_WAKE) { wake_gpe_count++; } } } + /* Dump info about this GPE block */ + + ACPI_DEBUG_PRINT ((ACPI_DB_INIT, + "GPE %02X to %02X [%4.4s] %u regs at %8.8X%8.8X on int 0x%X\n", + (u32) gpe_block->block_base_number, + (u32) (gpe_block->block_base_number + + ((gpe_block->register_count * ACPI_GPE_REGISTER_WIDTH) -1)), + gpe_device->name.ascii, + gpe_block->register_count, + ACPI_FORMAT_UINT64 (gpe_block->block_address.address), + interrupt_level)); + + + /* Enable all valid GPEs found above */ + + status = acpi_hw_enable_runtime_gpe_block (NULL, gpe_block); + ACPI_DEBUG_PRINT ((ACPI_DB_INIT, "Found %u Wake, Enabled %u Runtime GPEs in this block\n", wake_gpe_count, gpe_enabled_count)); @@ -1056,7 +1120,8 @@ acpi_ev_gpe_initialize ( if ((register_count0 + register_count1) == 0) { /* GPEs are not required by ACPI, this is OK */ - ACPI_REPORT_INFO (("There are no GPE blocks defined in the FADT\n")); + ACPI_DEBUG_PRINT ((ACPI_DB_INIT, + "There are no GPE blocks defined in the FADT\n")); status = AE_OK; goto cleanup; } diff --git a/drivers/acpi/events/evmisc.c b/drivers/acpi/events/evmisc.c index 1bfb10df3..35e61d522 100644 --- a/drivers/acpi/events/evmisc.c +++ b/drivers/acpi/events/evmisc.c @@ -88,9 +88,10 @@ acpi_ev_is_notify_object ( * * FUNCTION: acpi_ev_queue_notify_request * - * PARAMETERS: + * PARAMETERS: Node - NS node for the notified object + * notify_value - Value from the Notify() request * - * RETURN: None. + * RETURN: Status * * DESCRIPTION: Dispatch a device notification event to a previously * installed handler. @@ -139,14 +140,12 @@ acpi_ev_queue_notify_request ( acpi_notify_value_names[notify_value])); } else { - ACPI_DEBUG_PRINT ((ACPI_DB_INFO, - "notify value: 0x%2.2x **Device Specific**\n", + ACPI_DEBUG_PRINT ((ACPI_DB_INFO, "Notify value: 0x%2.2X **Device Specific**\n", notify_value)); } - /* - * Get the notify object attached to the NS Node - */ + /* Get the notify object attached to the NS Node */ + obj_desc = acpi_ns_get_attached_object (node); if (obj_desc) { /* We have the notify object, Get the right handler */ @@ -194,11 +193,13 @@ acpi_ev_queue_notify_request ( } if (!handler_obj) { - /* There is no per-device notify handler for this device */ - + /* + * There is no per-device notify handler for this device. + * This may or may not be a problem. + */ ACPI_DEBUG_PRINT ((ACPI_DB_INFO, - "No notify handler for [%4.4s] node %p\n", - acpi_ut_get_node_name (node), node)); + "No notify handler for Notify(%4.4s, %X) node %p\n", + acpi_ut_get_node_name (node), notify_value, node)); } return (status); @@ -209,7 +210,7 @@ acpi_ev_queue_notify_request ( * * FUNCTION: acpi_ev_notify_dispatch * - * PARAMETERS: + * PARAMETERS: Context - To be passsed to the notify handler * * RETURN: None. * @@ -276,6 +277,8 @@ acpi_ev_notify_dispatch ( * * FUNCTION: acpi_ev_global_lock_thread * + * PARAMETERS: Context - From thread interface, not used + * * RETURN: None * * DESCRIPTION: Invoked by SCI interrupt handler upon acquisition of the @@ -309,7 +312,9 @@ acpi_ev_global_lock_thread ( * * FUNCTION: acpi_ev_global_lock_handler * - * RETURN: Status + * PARAMETERS: Context - From thread interface, not used + * + * RETURN: ACPI_INTERRUPT_HANDLED or ACPI_INTERRUPT_NOT_HANDLED * * DESCRIPTION: Invoked directly from the SCI handler when a global lock * release interrupt occurs. Grab the global lock and queue @@ -356,6 +361,8 @@ acpi_ev_global_lock_handler ( * * FUNCTION: acpi_ev_init_global_lock_handler * + * PARAMETERS: None + * * RETURN: Status * * DESCRIPTION: Install a handler for the global lock release event @@ -395,6 +402,8 @@ acpi_ev_init_global_lock_handler (void) * * FUNCTION: acpi_ev_acquire_global_lock * + * PARAMETERS: Timeout - Max time to wait for the lock, in millisec. + * * RETURN: Status * * DESCRIPTION: Attempt to gain ownership of the Global Lock. @@ -462,6 +471,10 @@ acpi_ev_acquire_global_lock ( * * FUNCTION: acpi_ev_release_global_lock * + * PARAMETERS: None + * + * RETURN: Status + * * DESCRIPTION: Releases ownership of the Global Lock. * ******************************************************************************/ @@ -548,7 +561,7 @@ acpi_ev_terminate (void) /* Disable all GPEs in all GPE blocks */ - status = acpi_ev_walk_gpe_list (acpi_hw_disable_gpe_block); + status = acpi_ev_walk_gpe_list (acpi_hw_disable_gpe_block, ACPI_NOT_ISR); /* Remove SCI handler */ @@ -558,6 +571,10 @@ acpi_ev_terminate (void) } } + /* Deallocate all handler objects installed within GPE info structs */ + + status = acpi_ev_walk_gpe_list (acpi_ev_delete_gpe_handlers, ACPI_NOT_ISR); + /* Return to original mode if necessary */ if (acpi_gbl_original_mode == ACPI_SYS_MODE_LEGACY) { diff --git a/drivers/acpi/events/evregion.c b/drivers/acpi/events/evregion.c index ec81e587f..b7ff3b162 100644 --- a/drivers/acpi/events/evregion.c +++ b/drivers/acpi/events/evregion.c @@ -61,7 +61,7 @@ static u8 acpi_gbl_default_address_spaces[ACPI_NUM_DEFAULT_SPA /******************************************************************************* * - * FUNCTION: acpi_ev_init_address_spaces + * FUNCTION: acpi_ev_install_region_handlers * * PARAMETERS: None * @@ -72,15 +72,20 @@ static u8 acpi_gbl_default_address_spaces[ACPI_NUM_DEFAULT_SPA ******************************************************************************/ acpi_status -acpi_ev_init_address_spaces ( +acpi_ev_install_region_handlers ( void) { acpi_status status; acpi_native_uint i; - ACPI_FUNCTION_TRACE ("ev_init_address_spaces"); + ACPI_FUNCTION_TRACE ("ev_install_region_handlers"); + status = acpi_ut_acquire_mutex (ACPI_MTX_NAMESPACE); + if (ACPI_FAILURE (status)) { + return_ACPI_STATUS (status); + } + /* * All address spaces (PCI Config, EC, SMBus) are scope dependent * and registration must occur for a specific device. @@ -99,9 +104,8 @@ acpi_ev_init_address_spaces ( * has already been installed (via acpi_install_address_space_handler). * Similar for AE_SAME_HANDLER. */ - for (i = 0; i < ACPI_NUM_DEFAULT_SPACES; i++) { - status = acpi_install_address_space_handler ((acpi_handle) acpi_gbl_root_node, + status = acpi_ev_install_space_handler (acpi_gbl_root_node, acpi_gbl_default_address_spaces[i], ACPI_DEFAULT_HANDLER, NULL, NULL); switch (status) { @@ -111,15 +115,63 @@ acpi_ev_init_address_spaces ( /* These exceptions are all OK */ + status = AE_OK; break; default: - return_ACPI_STATUS (status); + goto unlock_and_exit; } } - return_ACPI_STATUS (AE_OK); +unlock_and_exit: + (void) acpi_ut_release_mutex (ACPI_MTX_NAMESPACE); + return_ACPI_STATUS (status); +} + + +/******************************************************************************* + * + * FUNCTION: acpi_ev_initialize_op_regions + * + * PARAMETERS: None + * + * RETURN: Status + * + * DESCRIPTION: Execute _REG methods for all Operation Regions that have + * an installed default region handler. + * + ******************************************************************************/ + +acpi_status +acpi_ev_initialize_op_regions ( + void) +{ + acpi_status status; + acpi_native_uint i; + + + ACPI_FUNCTION_TRACE ("ev_initialize_op_regions"); + + + status = acpi_ut_acquire_mutex (ACPI_MTX_NAMESPACE); + if (ACPI_FAILURE (status)) { + return_ACPI_STATUS (status); + } + + /* + * Run the _REG methods for op_regions in each default address space + */ + for (i = 0; i < ACPI_NUM_DEFAULT_SPACES; i++) { + /* TBD: Make sure handler is the DEFAULT handler, otherwise + * _REG will have already been run. + */ + status = acpi_ev_execute_reg_methods (acpi_gbl_root_node, + acpi_gbl_default_address_spaces[i]); + } + + (void) acpi_ut_release_mutex (ACPI_MTX_NAMESPACE); + return_ACPI_STATUS (status); } @@ -128,7 +180,7 @@ acpi_ev_init_address_spaces ( * FUNCTION: acpi_ev_execute_reg_method * * PARAMETERS: region_obj - Object structure - * Function - On (1) or Off (0) + * Function - Passed to _REG: On (1) or Off (0) * * RETURN: Status * @@ -138,11 +190,12 @@ acpi_ev_init_address_spaces ( acpi_status acpi_ev_execute_reg_method ( - union acpi_operand_object *region_obj, + union acpi_operand_object *region_obj, u32 function) { - union acpi_operand_object *params[3]; - union acpi_operand_object *region_obj2; + struct acpi_parameter_info info; + union acpi_operand_object *params[3]; + union acpi_operand_object *region_obj2; acpi_status status; @@ -159,10 +212,11 @@ acpi_ev_execute_reg_method ( } /* - * _REG method has two arguments - * Arg0: Integer: Operation region space ID + * The _REG method has two arguments: + * + * Arg0, Integer: Operation region space ID * Same value as region_obj->Region.space_id - * Arg1: Integer: connection status + * Arg1, Integer: connection status * 1 for connecting the handler, * 0 for disconnecting the handler * Passed as a parameter @@ -178,16 +232,21 @@ acpi_ev_execute_reg_method ( goto cleanup; } - /* Set up the parameter objects */ + /* Setup the parameter objects */ params[0]->integer.value = region_obj->region.space_id; params[1]->integer.value = function; params[2] = NULL; + info.node = region_obj2->extra.method_REG; + info.parameters = params; + info.parameter_type = ACPI_PARAM_ARGS; + /* Execute the method, no return value */ - ACPI_DEBUG_EXEC(acpi_ut_display_init_pathname (ACPI_TYPE_METHOD, region_obj2->extra.method_REG, NULL)); - status = acpi_ns_evaluate_by_handle (region_obj2->extra.method_REG, params, NULL); + ACPI_DEBUG_EXEC (acpi_ut_display_init_pathname ( + ACPI_TYPE_METHOD, info.node, NULL)); + status = acpi_ns_evaluate_by_handle (&info); acpi_ut_remove_reference (params[1]); @@ -203,7 +262,6 @@ cleanup: * FUNCTION: acpi_ev_address_space_dispatch * * PARAMETERS: region_obj - Internal region object - * space_id - ID of the address space (0-255) * Function - Read or Write operation * Address - Where in the space to read or write * bit_width - Field width in bits (8, 16, 32, or 64) @@ -326,7 +384,7 @@ acpi_ev_address_space_dispatch ( ACPI_FORMAT_UINT64 (address), acpi_ut_get_region_name (region_obj->region.space_id))); - if (!(handler_desc->address_space.flags & ACPI_ADDR_HANDLER_DEFAULT_INSTALLED)) { + if (!(handler_desc->address_space.hflags & ACPI_ADDR_HANDLER_DEFAULT_INSTALLED)) { /* * For handlers other than the default (supplied) handlers, we must * exit the interpreter because the handler *might* block -- we don't @@ -347,7 +405,7 @@ acpi_ev_address_space_dispatch ( acpi_format_exception (status))); } - if (!(handler_desc->address_space.flags & ACPI_ADDR_HANDLER_DEFAULT_INSTALLED)) { + if (!(handler_desc->address_space.hflags & ACPI_ADDR_HANDLER_DEFAULT_INSTALLED)) { /* * We just returned from a non-default handler, we must re-enter the * interpreter @@ -366,8 +424,8 @@ acpi_ev_address_space_dispatch ( * * FUNCTION: acpi_ev_detach_region * - * PARAMETERS: region_obj - Region Object - * acpi_ns_is_locked - Namespace Region Already Locked? + * PARAMETERS: region_obj - Region Object + * acpi_ns_is_locked - Namespace Region Already Locked? * * RETURN: None * @@ -501,9 +559,9 @@ acpi_ev_detach_region( * * FUNCTION: acpi_ev_attach_region * - * PARAMETERS: handler_obj - Handler Object - * region_obj - Region Object - * acpi_ns_is_locked - Namespace Region Already Locked? + * PARAMETERS: handler_obj - Handler Object + * region_obj - Region Object + * acpi_ns_is_locked - Namespace Region Already Locked? * * RETURN: None * @@ -676,6 +734,273 @@ acpi_ev_install_handler ( return (status); } + +/******************************************************************************* + * + * FUNCTION: acpi_ev_install_space_handler + * + * PARAMETERS: Node - Namespace node for the device + * space_id - The address space ID + * Handler - Address of the handler + * Setup - Address of the setup function + * Context - Value passed to the handler on each access + * + * RETURN: Status + * + * DESCRIPTION: Install a handler for all op_regions of a given space_id. + * Assumes namespace is locked + * + ******************************************************************************/ + +acpi_status +acpi_ev_install_space_handler ( + struct acpi_namespace_node *node, + acpi_adr_space_type space_id, + acpi_adr_space_handler handler, + acpi_adr_space_setup setup, + void *context) +{ + union acpi_operand_object *obj_desc; + union acpi_operand_object *handler_obj; + acpi_status status; + acpi_object_type type; + u16 flags = 0; + + + ACPI_FUNCTION_TRACE ("ev_install_space_handler"); + + + /* + * This registration is valid for only the types below + * and the root. This is where the default handlers + * get placed. + */ + if ((node->type != ACPI_TYPE_DEVICE) && + (node->type != ACPI_TYPE_PROCESSOR) && + (node->type != ACPI_TYPE_THERMAL) && + (node != acpi_gbl_root_node)) { + status = AE_BAD_PARAMETER; + goto unlock_and_exit; + } + + if (handler == ACPI_DEFAULT_HANDLER) { + flags = ACPI_ADDR_HANDLER_DEFAULT_INSTALLED; + + switch (space_id) { + case ACPI_ADR_SPACE_SYSTEM_MEMORY: + handler = acpi_ex_system_memory_space_handler; + setup = acpi_ev_system_memory_region_setup; + break; + + case ACPI_ADR_SPACE_SYSTEM_IO: + handler = acpi_ex_system_io_space_handler; + setup = acpi_ev_io_space_region_setup; + break; + + case ACPI_ADR_SPACE_PCI_CONFIG: + handler = acpi_ex_pci_config_space_handler; + setup = acpi_ev_pci_config_region_setup; + break; + + case ACPI_ADR_SPACE_CMOS: + handler = acpi_ex_cmos_space_handler; + setup = acpi_ev_cmos_region_setup; + break; + + case ACPI_ADR_SPACE_PCI_BAR_TARGET: + handler = acpi_ex_pci_bar_space_handler; + setup = acpi_ev_pci_bar_region_setup; + break; + + case ACPI_ADR_SPACE_DATA_TABLE: + handler = acpi_ex_data_table_space_handler; + setup = NULL; + break; + + default: + status = AE_BAD_PARAMETER; + goto unlock_and_exit; + } + } + + /* If the caller hasn't specified a setup routine, use the default */ + + if (!setup) { + setup = acpi_ev_default_region_setup; + } + + /* Check for an existing internal object */ + + obj_desc = acpi_ns_get_attached_object (node); + if (obj_desc) { + /* + * The attached device object already exists. + * Make sure the handler is not already installed. + */ + handler_obj = obj_desc->device.handler; + + /* Walk the handler list for this device */ + + while (handler_obj) { + /* Same space_id indicates a handler already installed */ + + if (handler_obj->address_space.space_id == space_id) { + if (handler_obj->address_space.handler == handler) { + /* + * It is (relatively) OK to attempt to install the SAME + * handler twice. This can easily happen with PCI_Config space. + */ + status = AE_SAME_HANDLER; + goto unlock_and_exit; + } + else { + /* A handler is already installed */ + + status = AE_ALREADY_EXISTS; + } + goto unlock_and_exit; + } + + /* Walk the linked list of handlers */ + + handler_obj = handler_obj->address_space.next; + } + } + else { + ACPI_DEBUG_PRINT ((ACPI_DB_OPREGION, + "Creating object on Device %p while installing handler\n", node)); + + /* obj_desc does not exist, create one */ + + if (node->type == ACPI_TYPE_ANY) { + type = ACPI_TYPE_DEVICE; + } + else { + type = node->type; + } + + obj_desc = acpi_ut_create_internal_object (type); + if (!obj_desc) { + status = AE_NO_MEMORY; + goto unlock_and_exit; + } + + /* Init new descriptor */ + + obj_desc->common.type = (u8) type; + + /* Attach the new object to the Node */ + + status = acpi_ns_attach_object (node, obj_desc, type); + + /* Remove local reference to the object */ + + acpi_ut_remove_reference (obj_desc); + + if (ACPI_FAILURE (status)) { + goto unlock_and_exit; + } + } + + ACPI_DEBUG_PRINT ((ACPI_DB_OPREGION, + "Installing address handler for region %s(%X) on Device %4.4s %p(%p)\n", + acpi_ut_get_region_name (space_id), space_id, + acpi_ut_get_node_name (node), node, obj_desc)); + + /* + * Install the handler + * + * At this point there is no existing handler. + * Just allocate the object for the handler and link it + * into the list. + */ + handler_obj = acpi_ut_create_internal_object (ACPI_TYPE_LOCAL_ADDRESS_HANDLER); + if (!handler_obj) { + status = AE_NO_MEMORY; + goto unlock_and_exit; + } + + /* Init handler obj */ + + handler_obj->address_space.space_id = (u8) space_id; + handler_obj->address_space.hflags = flags; + handler_obj->address_space.region_list = NULL; + handler_obj->address_space.node = node; + handler_obj->address_space.handler = handler; + handler_obj->address_space.context = context; + handler_obj->address_space.setup = setup; + + /* Install at head of Device.address_space list */ + + handler_obj->address_space.next = obj_desc->device.handler; + + /* + * The Device object is the first reference on the handler_obj. + * Each region that uses the handler adds a reference. + */ + obj_desc->device.handler = handler_obj; + + /* + * Walk the namespace finding all of the regions this + * handler will manage. + * + * Start at the device and search the branch toward + * the leaf nodes until either the leaf is encountered or + * a device is detected that has an address handler of the + * same type. + * + * In either case, back up and search down the remainder + * of the branch + */ + status = acpi_ns_walk_namespace (ACPI_TYPE_ANY, node, ACPI_UINT32_MAX, + ACPI_NS_WALK_UNLOCK, acpi_ev_install_handler, + handler_obj, NULL); + +unlock_and_exit: + return_ACPI_STATUS (status); +} + + +/******************************************************************************* + * + * FUNCTION: acpi_ev_execute_reg_methods + * + * PARAMETERS: Node - Namespace node for the device + * space_id - The address space ID + * + * RETURN: Status + * + * DESCRIPTION: Run all _REG methods for the input Space ID; + * Note: assumes namespace is locked, or system init time. + * + ******************************************************************************/ + +acpi_status +acpi_ev_execute_reg_methods ( + struct acpi_namespace_node *node, + acpi_adr_space_type space_id) +{ + acpi_status status; + + + ACPI_FUNCTION_TRACE ("ev_execute_reg_methods"); + + + /* + * Run all _REG methods for all Operation Regions for this + * space ID. This is a separate walk in order to handle any + * interdependencies between regions and _REG methods. (i.e. handlers + * must be installed for all regions of this Space ID before we + * can run any _REG methods) + */ + status = acpi_ns_walk_namespace (ACPI_TYPE_ANY, node, ACPI_UINT32_MAX, + ACPI_NS_WALK_UNLOCK, acpi_ev_reg_run, + &space_id, NULL); + + return_ACPI_STATUS (status); +} + + /******************************************************************************* * * FUNCTION: acpi_ev_reg_run @@ -693,19 +1018,13 @@ acpi_ev_reg_run ( void *context, void **return_value) { - union acpi_operand_object *handler_obj; union acpi_operand_object *obj_desc; struct acpi_namespace_node *node; + acpi_adr_space_type space_id; acpi_status status; - handler_obj = (union acpi_operand_object *) context; - - /* Parameter validation */ - - if (!handler_obj) { - return (AE_OK); - } + space_id = *ACPI_CAST_PTR (acpi_adr_space_type, context); /* Convert and validate the device handle */ @@ -732,10 +1051,9 @@ acpi_ev_reg_run ( return (AE_OK); } - /* Object is a Region */ - if (obj_desc->region.space_id != handler_obj->address_space.space_id) { + if (obj_desc->region.space_id != space_id) { /* * This region is for a different address space * -- just ignore it diff --git a/drivers/acpi/events/evrgnini.c b/drivers/acpi/events/evrgnini.c index 2e480d301..e19d82297 100644 --- a/drivers/acpi/events/evrgnini.c +++ b/drivers/acpi/events/evrgnini.c @@ -54,7 +54,7 @@ * * FUNCTION: acpi_ev_system_memory_region_setup * - * PARAMETERS: region_obj - Region we are interested in + * PARAMETERS: Handle - Region we are interested in * Function - Start or stop * handler_context - Address space handler context * region_context - Region specific context @@ -108,7 +108,7 @@ acpi_ev_system_memory_region_setup ( * * FUNCTION: acpi_ev_io_space_region_setup * - * PARAMETERS: region_obj - Region we are interested in + * PARAMETERS: Handle - Region we are interested in * Function - Start or stop * handler_context - Address space handler context * region_context - Region specific context @@ -144,7 +144,7 @@ acpi_ev_io_space_region_setup ( * * FUNCTION: acpi_ev_pci_config_region_setup * - * PARAMETERS: region_obj - Region we are interested in + * PARAMETERS: Handle - Region we are interested in * Function - Start or stop * handler_context - Address space handler context * region_context - Region specific context @@ -317,7 +317,7 @@ acpi_ev_pci_config_region_setup ( * * FUNCTION: acpi_ev_pci_bar_region_setup * - * PARAMETERS: region_obj - Region we are interested in + * PARAMETERS: Handle - Region we are interested in * Function - Start or stop * handler_context - Address space handler context * region_context - Region specific context @@ -348,7 +348,7 @@ acpi_ev_pci_bar_region_setup ( * * FUNCTION: acpi_ev_cmos_region_setup * - * PARAMETERS: region_obj - Region we are interested in + * PARAMETERS: Handle - Region we are interested in * Function - Start or stop * handler_context - Address space handler context * region_context - Region specific context @@ -379,7 +379,7 @@ acpi_ev_cmos_region_setup ( * * FUNCTION: acpi_ev_default_region_setup * - * PARAMETERS: region_obj - Region we are interested in + * PARAMETERS: Handle - Region we are interested in * Function - Start or stop * handler_context - Address space handler context * region_context - Region specific context diff --git a/drivers/acpi/events/evxfevnt.c b/drivers/acpi/events/evxfevnt.c index 640f51fb0..e941678ce 100644 --- a/drivers/acpi/events/evxfevnt.c +++ b/drivers/acpi/events/evxfevnt.c @@ -204,12 +204,11 @@ acpi_enable_event ( /******************************************************************************* * - * FUNCTION: acpi_enable_gpe + * FUNCTION: acpi_set_gpe_type * * PARAMETERS: gpe_device - Parent GPE Device * gpe_number - GPE level within the GPE block - * Flags - Just enable, or also wake enable? - * Called from ISR or not + * Type - New GPE type * * RETURN: Status * @@ -218,26 +217,17 @@ acpi_enable_event ( ******************************************************************************/ acpi_status -acpi_enable_gpe ( +acpi_set_gpe_type ( acpi_handle gpe_device, u32 gpe_number, - u32 flags) + u8 type) { acpi_status status = AE_OK; struct acpi_gpe_event_info *gpe_event_info; - ACPI_FUNCTION_TRACE ("acpi_enable_gpe"); - + ACPI_FUNCTION_TRACE ("acpi_set_gpe_type"); - /* Use semaphore lock if not executing at interrupt level */ - - if (flags & ACPI_NOT_ISR) { - status = acpi_ut_acquire_mutex (ACPI_MTX_EVENTS); - if (ACPI_FAILURE (status)) { - return_ACPI_STATUS (status); - } - } /* Ensure that we have a valid GPE number */ @@ -247,91 +237,72 @@ acpi_enable_gpe ( goto unlock_and_exit; } - /* Check for Wake vs Runtime GPE */ - - if (flags & ACPI_EVENT_WAKE_ENABLE) { - /* Ensure the requested wake GPE is disabled */ - - status = acpi_hw_disable_gpe (gpe_event_info); - if (ACPI_FAILURE (status)) { - goto unlock_and_exit; - } - - /* Defer Enable of Wake GPE until sleep time */ - - acpi_hw_enable_gpe_for_wakeup (gpe_event_info); + if ((gpe_event_info->flags & ACPI_GPE_TYPE_MASK) == type) { + return_ACPI_STATUS (AE_OK); } - else { - /* Enable the requested runtime GPE */ - status = acpi_hw_enable_gpe (gpe_event_info); - if (ACPI_FAILURE (status)) { - goto unlock_and_exit; - } - } + /* Set the new type (will disable GPE if currently enabled) */ + status = acpi_ev_set_gpe_type (gpe_event_info, type); unlock_and_exit: - if (flags & ACPI_NOT_ISR) { - (void) acpi_ut_release_mutex (ACPI_MTX_EVENTS); - } return_ACPI_STATUS (status); } /******************************************************************************* * - * FUNCTION: acpi_disable_event + * FUNCTION: acpi_enable_gpe * - * PARAMETERS: Event - The fixed eventto be enabled - * Flags - Reserved + * PARAMETERS: gpe_device - Parent GPE Device + * gpe_number - GPE level within the GPE block + * Flags - Just enable, or also wake enable? + * Called from ISR or not * * RETURN: Status * - * DESCRIPTION: Disable an ACPI event (fixed) + * DESCRIPTION: Enable an ACPI event (general purpose) * ******************************************************************************/ acpi_status -acpi_disable_event ( - u32 event, +acpi_enable_gpe ( + acpi_handle gpe_device, + u32 gpe_number, u32 flags) { acpi_status status = AE_OK; - u32 value; + struct acpi_gpe_event_info *gpe_event_info; - ACPI_FUNCTION_TRACE ("acpi_disable_event"); + ACPI_FUNCTION_TRACE ("acpi_enable_gpe"); - /* Decode the Fixed Event */ + /* Use semaphore lock if not executing at interrupt level */ - if (event > ACPI_EVENT_MAX) { - return_ACPI_STATUS (AE_BAD_PARAMETER); + if (flags & ACPI_NOT_ISR) { + status = acpi_ut_acquire_mutex (ACPI_MTX_EVENTS); + if (ACPI_FAILURE (status)) { + return_ACPI_STATUS (status); + } } - /* - * Disable the requested fixed event (by writing a zero to the - * enable register bit) - */ - status = acpi_set_register (acpi_gbl_fixed_event_info[event].enable_register_id, - 0, ACPI_MTX_LOCK); - if (ACPI_FAILURE (status)) { - return_ACPI_STATUS (status); - } + /* Ensure that we have a valid GPE number */ - status = acpi_get_register (acpi_gbl_fixed_event_info[event].enable_register_id, - &value, ACPI_MTX_LOCK); - if (ACPI_FAILURE (status)) { - return_ACPI_STATUS (status); + gpe_event_info = acpi_ev_get_gpe_event_info (gpe_device, gpe_number); + if (!gpe_event_info) { + status = AE_BAD_PARAMETER; + goto unlock_and_exit; } - if (value != 0) { - ACPI_DEBUG_PRINT ((ACPI_DB_ERROR, - "Could not disable %s events\n", acpi_ut_get_event_name (event))); - return_ACPI_STATUS (AE_NO_HARDWARE_RESPONSE); - } + /* Perform the enable */ + + status = acpi_ev_enable_gpe (gpe_event_info, TRUE); +unlock_and_exit: + if (flags & ACPI_NOT_ISR) { + (void) acpi_ut_release_mutex (ACPI_MTX_EVENTS); + } return_ACPI_STATUS (status); } @@ -342,7 +313,7 @@ acpi_disable_event ( * * PARAMETERS: gpe_device - Parent GPE Device * gpe_number - GPE level within the GPE block - * Flags - Just enable, or also wake enable? + * Flags - Just disable, or also wake disable? * Called from ISR or not * * RETURN: Status @@ -381,21 +352,69 @@ acpi_disable_gpe ( goto unlock_and_exit; } + status = acpi_ev_disable_gpe (gpe_event_info); + +unlock_and_exit: + if (flags & ACPI_NOT_ISR) { + (void) acpi_ut_release_mutex (ACPI_MTX_EVENTS); + } + return_ACPI_STATUS (status); +} + + +/******************************************************************************* + * + * FUNCTION: acpi_disable_event + * + * PARAMETERS: Event - The fixed eventto be enabled + * Flags - Reserved + * + * RETURN: Status + * + * DESCRIPTION: Disable an ACPI event (fixed) + * + ******************************************************************************/ + +acpi_status +acpi_disable_event ( + u32 event, + u32 flags) +{ + acpi_status status = AE_OK; + u32 value; + + + ACPI_FUNCTION_TRACE ("acpi_disable_event"); + + + /* Decode the Fixed Event */ + + if (event > ACPI_EVENT_MAX) { + return_ACPI_STATUS (AE_BAD_PARAMETER); + } + /* - * Only disable the requested GPE number for wake if specified. - * Otherwise, turn it totally off + * Disable the requested fixed event (by writing a zero to the + * enable register bit) */ - if (flags & ACPI_EVENT_WAKE_DISABLE) { - acpi_hw_disable_gpe_for_wakeup (gpe_event_info); + status = acpi_set_register (acpi_gbl_fixed_event_info[event].enable_register_id, + 0, ACPI_MTX_LOCK); + if (ACPI_FAILURE (status)) { + return_ACPI_STATUS (status); } - else { - status = acpi_hw_disable_gpe (gpe_event_info); + + status = acpi_get_register (acpi_gbl_fixed_event_info[event].enable_register_id, + &value, ACPI_MTX_LOCK); + if (ACPI_FAILURE (status)) { + return_ACPI_STATUS (status); } -unlock_and_exit: - if (flags & ACPI_NOT_ISR) { - (void) acpi_ut_release_mutex (ACPI_MTX_EVENTS); + if (value != 0) { + ACPI_DEBUG_PRINT ((ACPI_DB_ERROR, + "Could not disable %s events\n", acpi_ut_get_event_name (event))); + return_ACPI_STATUS (AE_NO_HARDWARE_RESPONSE); } + return_ACPI_STATUS (status); } diff --git a/drivers/acpi/events/evxfregn.c b/drivers/acpi/events/evxfregn.c index b31d4fdfe..8eeb938f7 100644 --- a/drivers/acpi/events/evxfregn.c +++ b/drivers/acpi/events/evxfregn.c @@ -46,7 +46,6 @@ #include #include #include -#include #define _COMPONENT ACPI_EVENTS ACPI_MODULE_NAME ("evxfregn") @@ -76,12 +75,8 @@ acpi_install_address_space_handler ( acpi_adr_space_setup setup, void *context) { - union acpi_operand_object *obj_desc; - union acpi_operand_object *handler_obj; struct acpi_namespace_node *node; acpi_status status; - acpi_object_type type; - u16 flags = 0; ACPI_FUNCTION_TRACE ("acpi_install_address_space_handler"); @@ -106,202 +101,16 @@ acpi_install_address_space_handler ( goto unlock_and_exit; } - /* - * This registration is valid for only the types below - * and the root. This is where the default handlers - * get placed. - */ - if ((node->type != ACPI_TYPE_DEVICE) && - (node->type != ACPI_TYPE_PROCESSOR) && - (node->type != ACPI_TYPE_THERMAL) && - (node != acpi_gbl_root_node)) { - status = AE_BAD_PARAMETER; - goto unlock_and_exit; - } - - if (handler == ACPI_DEFAULT_HANDLER) { - flags = ACPI_ADDR_HANDLER_DEFAULT_INSTALLED; - - switch (space_id) { - case ACPI_ADR_SPACE_SYSTEM_MEMORY: - handler = acpi_ex_system_memory_space_handler; - setup = acpi_ev_system_memory_region_setup; - break; - - case ACPI_ADR_SPACE_SYSTEM_IO: - handler = acpi_ex_system_io_space_handler; - setup = acpi_ev_io_space_region_setup; - break; - - case ACPI_ADR_SPACE_PCI_CONFIG: - handler = acpi_ex_pci_config_space_handler; - setup = acpi_ev_pci_config_region_setup; - break; - - case ACPI_ADR_SPACE_CMOS: - handler = acpi_ex_cmos_space_handler; - setup = acpi_ev_cmos_region_setup; - break; - - case ACPI_ADR_SPACE_PCI_BAR_TARGET: - handler = acpi_ex_pci_bar_space_handler; - setup = acpi_ev_pci_bar_region_setup; - break; - - case ACPI_ADR_SPACE_DATA_TABLE: - handler = acpi_ex_data_table_space_handler; - setup = NULL; - break; - - default: - status = AE_BAD_PARAMETER; - goto unlock_and_exit; - } - } - - /* If the caller hasn't specified a setup routine, use the default */ - - if (!setup) { - setup = acpi_ev_default_region_setup; - } - - /* Check for an existing internal object */ - - obj_desc = acpi_ns_get_attached_object (node); - if (obj_desc) { - /* - * The attached device object already exists. - * Make sure the handler is not already installed. - */ - handler_obj = obj_desc->device.handler; - - /* Walk the handler list for this device */ - - while (handler_obj) { - /* Same space_id indicates a handler already installed */ - - if(handler_obj->address_space.space_id == space_id) { - if (handler_obj->address_space.handler == handler) { - /* - * It is (relatively) OK to attempt to install the SAME - * handler twice. This can easily happen with PCI_Config space. - */ - status = AE_SAME_HANDLER; - goto unlock_and_exit; - } - else { - /* A handler is already installed */ - - status = AE_ALREADY_EXISTS; - } - goto unlock_and_exit; - } - - /* Walk the linked list of handlers */ - - handler_obj = handler_obj->address_space.next; - } - } - else { - ACPI_DEBUG_PRINT ((ACPI_DB_OPREGION, - "Creating object on Device %p while installing handler\n", node)); - - /* obj_desc does not exist, create one */ - - if (node->type == ACPI_TYPE_ANY) { - type = ACPI_TYPE_DEVICE; - } - else { - type = node->type; - } - - obj_desc = acpi_ut_create_internal_object (type); - if (!obj_desc) { - status = AE_NO_MEMORY; - goto unlock_and_exit; - } - - /* Init new descriptor */ - - obj_desc->common.type = (u8) type; - - /* Attach the new object to the Node */ + /* Install the handler for all Regions for this Space ID */ - status = acpi_ns_attach_object (node, obj_desc, type); - - /* Remove local reference to the object */ - - acpi_ut_remove_reference (obj_desc); - - if (ACPI_FAILURE (status)) { - goto unlock_and_exit; - } - } - - ACPI_DEBUG_PRINT ((ACPI_DB_OPREGION, - "Installing address handler for region %s(%X) on Device %4.4s %p(%p)\n", - acpi_ut_get_region_name (space_id), space_id, - acpi_ut_get_node_name (node), node, obj_desc)); - - /* - * Install the handler - * - * At this point there is no existing handler. - * Just allocate the object for the handler and link it - * into the list. - */ - handler_obj = acpi_ut_create_internal_object (ACPI_TYPE_LOCAL_ADDRESS_HANDLER); - if (!handler_obj) { - status = AE_NO_MEMORY; + status = acpi_ev_install_space_handler (node, space_id, handler, setup, context); + if (ACPI_FAILURE (status)) { goto unlock_and_exit; } - /* Init handler obj */ - - handler_obj->address_space.space_id = (u8) space_id; - handler_obj->address_space.hflags = flags; - handler_obj->address_space.region_list = NULL; - handler_obj->address_space.node = node; - handler_obj->address_space.handler = handler; - handler_obj->address_space.context = context; - handler_obj->address_space.setup = setup; - - /* Install at head of Device.address_space list */ - - handler_obj->address_space.next = obj_desc->device.handler; - - /* - * The Device object is the first reference on the handler_obj. - * Each region that uses the handler adds a reference. - */ - obj_desc->device.handler = handler_obj; - - /* - * Walk the namespace finding all of the regions this - * handler will manage. - * - * Start at the device and search the branch toward - * the leaf nodes until either the leaf is encountered or - * a device is detected that has an address handler of the - * same type. - * - * In either case, back up and search down the remainder - * of the branch - */ - status = acpi_ns_walk_namespace (ACPI_TYPE_ANY, device, ACPI_UINT32_MAX, - ACPI_NS_WALK_UNLOCK, acpi_ev_install_handler, - handler_obj, NULL); - - /* - * Now we can run the _REG methods for all Regions for this - * space ID. This is a separate walk in order to handle any - * interdependencies between regions and _REG methods. (i.e. handlers - * must be installed for all regions of this Space ID before we - * can run any _REG methods. - */ - status = acpi_ns_walk_namespace (ACPI_TYPE_ANY, device, ACPI_UINT32_MAX, - ACPI_NS_WALK_UNLOCK, acpi_ev_reg_run, - handler_obj, NULL); + /* Run all _REG methods for this address space */ + + status = acpi_ev_execute_reg_methods (node, space_id); unlock_and_exit: (void) acpi_ut_release_mutex (ACPI_MTX_NAMESPACE); diff --git a/drivers/acpi/executer/exconfig.c b/drivers/acpi/executer/exconfig.c index dfe7e0463..1e6a89805 100644 --- a/drivers/acpi/executer/exconfig.c +++ b/drivers/acpi/executer/exconfig.c @@ -48,6 +48,7 @@ #include #include #include +#include #define _COMPONENT ACPI_EXECUTER @@ -285,7 +286,7 @@ acpi_ex_load_op ( union acpi_operand_object *ddb_handle; union acpi_operand_object *buffer_desc = NULL; struct acpi_table_header *table_ptr = NULL; - u8 *table_data_ptr; + acpi_physical_address address; struct acpi_table_header table_header; u32 i; @@ -300,18 +301,39 @@ acpi_ex_load_op ( ACPI_DEBUG_PRINT ((ACPI_DB_EXEC, "Load from Region %p %s\n", obj_desc, acpi_ut_get_object_type_name (obj_desc))); - /* Get the table header */ + /* + * If the Region Address and Length have not been previously evaluated, + * evaluate them now and save the results. + */ + if (!(obj_desc->common.flags & AOPOBJ_DATA_VALID)) { + status = acpi_ds_get_region_arguments (obj_desc); + if (ACPI_FAILURE (status)) { + return_ACPI_STATUS (status); + } + } + + /* Get the base physical address of the region */ + + address = obj_desc->region.address; + + /* Get the table length from the table header */ table_header.length = 0; - for (i = 0; i < sizeof (struct acpi_table_header); i++) { + for (i = 0; i < 8; i++) { status = acpi_ev_address_space_dispatch (obj_desc, ACPI_READ, - (acpi_physical_address) i, 8, + (acpi_physical_address) (i + address), 8, ((u8 *) &table_header) + i); if (ACPI_FAILURE (status)) { return_ACPI_STATUS (status); } } + /* Sanity check the table length */ + + if (table_header.length < sizeof (struct acpi_table_header)) { + return_ACPI_STATUS (AE_BAD_HEADER); + } + /* Allocate a buffer for the entire table */ table_ptr = ACPI_MEM_ALLOCATE (table_header.length); @@ -319,17 +341,12 @@ acpi_ex_load_op ( return_ACPI_STATUS (AE_NO_MEMORY); } - /* Copy the header to the buffer */ - - ACPI_MEMCPY (table_ptr, &table_header, sizeof (struct acpi_table_header)); - table_data_ptr = ACPI_PTR_ADD (u8, table_ptr, sizeof (struct acpi_table_header)); - - /* Get the table from the op region */ + /* Get the entire table from the op region */ for (i = 0; i < table_header.length; i++) { status = acpi_ev_address_space_dispatch (obj_desc, ACPI_READ, - (acpi_physical_address) i, 8, - ((u8 *) table_data_ptr + i)); + (acpi_physical_address) (i + address), 8, + ((u8 *) table_ptr + i)); if (ACPI_FAILURE (status)) { goto cleanup; } @@ -355,6 +372,12 @@ acpi_ex_load_op ( } table_ptr = ACPI_CAST_PTR (struct acpi_table_header, buffer_desc->buffer.pointer); + + /* Sanity check the table length */ + + if (table_ptr->length < sizeof (struct acpi_table_header)) { + return_ACPI_STATUS (AE_BAD_HEADER); + } break; diff --git a/drivers/acpi/executer/exfldio.c b/drivers/acpi/executer/exfldio.c index 4ce49b9bf..03ccfb9e1 100644 --- a/drivers/acpi/executer/exfldio.c +++ b/drivers/acpi/executer/exfldio.c @@ -130,6 +130,21 @@ acpi_ex_setup_region ( if (rgn_desc->region.length < (obj_desc->common_field.base_byte_offset + field_datum_byte_offset + obj_desc->common_field.access_byte_width)) { + if (acpi_gbl_enable_interpreter_slack) { + /* + * Slack mode only: We will go ahead and allow access to this + * field if it is within the region length rounded up to the next + * access width boundary. + */ + if (ACPI_ROUND_UP (rgn_desc->region.length, + obj_desc->common_field.access_byte_width) >= + (obj_desc->common_field.base_byte_offset + + obj_desc->common_field.access_byte_width + + field_datum_byte_offset)) { + return_ACPI_STATUS (AE_OK); + } + } + if (rgn_desc->region.length < obj_desc->common_field.access_byte_width) { /* * This is the case where the access_type (acc_word, etc.) is wider @@ -154,40 +169,7 @@ acpi_ex_setup_region ( field_datum_byte_offset, obj_desc->common_field.access_byte_width, acpi_ut_get_node_name (rgn_desc->region.node), rgn_desc->region.length)); - if (!acpi_strict) { - /* - * Allow access to the field if it is within the region size - * rounded up to a multiple of the access byte width. This - * overcomes "off-by-one" programming errors in the AML often - * found in Toshiba laptops. These errors were allowed by - * the Microsoft ASL compiler. - */ - u32 rounded_length = ACPI_ROUND_UP(rgn_desc->region.length, - obj_desc->common_field.access_byte_width); - - if (rounded_length < (obj_desc->common_field.base_byte_offset - + field_datum_byte_offset - + obj_desc->common_field.access_byte_width)) { - return_ACPI_STATUS (AE_AML_REGION_LIMIT); - } else { - static int warn_once = 1; - if (warn_once) { - // Could also associate a flag with each field, and - // warn once for each field. - ACPI_REPORT_WARNING(( - "The ACPI AML in your computer contains errors, " - "please nag the manufacturer to correct it.\n")); - ACPI_REPORT_WARNING(( - "Allowing relaxed access to fields; " - "turn on CONFIG_ACPI_DEBUG for details.\n")); - warn_once = 0; - } - return_ACPI_STATUS (AE_OK); - } - } - else { - return_ACPI_STATUS (AE_AML_REGION_LIMIT); - } + return_ACPI_STATUS (AE_AML_REGION_LIMIT); } return_ACPI_STATUS (AE_OK); @@ -277,7 +259,7 @@ acpi_ex_access_region ( rgn_desc->region.space_id)); } else if (status == AE_NOT_EXIST) { - ACPI_DEBUG_PRINT ((ACPI_DB_ERROR, + ACPI_REPORT_ERROR (( "Region %s(%X) has no handler\n", acpi_ut_get_region_name (rgn_desc->region.space_id), rgn_desc->region.space_id)); @@ -764,16 +746,85 @@ acpi_ex_set_buffer_datum ( } +/******************************************************************************* + * + * FUNCTION: acpi_ex_common_buffer_setup + * + * PARAMETERS: obj_desc - Field object + * buffer_length - Length of caller's buffer + * datum_count - Where the datum_count is returned + * + * RETURN: Status, datum_count + * + * DESCRIPTION: Common code to validate the incoming buffer size and compute + * the number of field "datums" that must be read or written. + * A "datum" is the smallest unit that can be read or written + * to the field, it is either 1,2,4, or 8 bytes. + * + ******************************************************************************/ + +acpi_status +acpi_ex_common_buffer_setup ( + union acpi_operand_object *obj_desc, + u32 buffer_length, + u32 *datum_count) +{ + u32 byte_field_length; + u32 actual_byte_field_length; + + + ACPI_FUNCTION_TRACE ("ex_common_buffer_setup"); + + + /* + * Incoming buffer must be at least as long as the field, we do not + * allow "partial" field reads/writes. We do not care if the buffer is + * larger than the field, this typically happens when an integer is + * read/written to a field that is actually smaller than an integer. + */ + byte_field_length = ACPI_ROUND_BITS_UP_TO_BYTES ( + obj_desc->common_field.bit_length); + if (byte_field_length > buffer_length) { + ACPI_DEBUG_PRINT ((ACPI_DB_BFIELD, + "Field size %X (bytes) is too large for buffer (%X)\n", + byte_field_length, buffer_length)); + + return_ACPI_STATUS (AE_BUFFER_OVERFLOW); + } + + /* + * Create "actual" field byte count (minimum number of bytes that + * must be read), then convert to datum count (minimum number + * of datum-sized units that must be read) + */ + actual_byte_field_length = ACPI_ROUND_BITS_UP_TO_BYTES ( + obj_desc->common_field.start_field_bit_offset + + obj_desc->common_field.bit_length); + + + *datum_count = ACPI_ROUND_UP_TO (actual_byte_field_length, + obj_desc->common_field.access_byte_width); + + ACPI_DEBUG_PRINT ((ACPI_DB_BFIELD, + "buffer_bytes %X, actual_bytes %X, Datums %X, byte_gran %X\n", + byte_field_length, actual_byte_field_length, + *datum_count, obj_desc->common_field.access_byte_width)); + + return_ACPI_STATUS (AE_OK); +} + + /******************************************************************************* * * FUNCTION: acpi_ex_extract_from_field * - * PARAMETERS: *obj_desc - Field to be read - * *Value - Where to store value + * PARAMETERS: obj_desc - Field to be read + * Buffer - Where to store the field data + * buffer_length - Length of Buffer * * RETURN: Status * - * DESCRIPTION: Retrieve the value of the given field + * DESCRIPTION: Retrieve the current value of the given field * ******************************************************************************/ @@ -789,7 +840,6 @@ acpi_ex_extract_from_field ( acpi_integer previous_raw_datum = 0; acpi_integer this_raw_datum = 0; acpi_integer merged_datum = 0; - u32 byte_field_length; u32 datum_count; u32 i; @@ -797,39 +847,13 @@ acpi_ex_extract_from_field ( ACPI_FUNCTION_TRACE ("ex_extract_from_field"); - /* - * The field must fit within the caller's buffer - */ - byte_field_length = ACPI_ROUND_BITS_UP_TO_BYTES (obj_desc->common_field.bit_length); - if (byte_field_length > buffer_length) { - ACPI_DEBUG_PRINT ((ACPI_DB_BFIELD, - "Field size %X (bytes) too large for buffer (%X)\n", - byte_field_length, buffer_length)); - - return_ACPI_STATUS (AE_BUFFER_OVERFLOW); - } - - /* Convert field byte count to datum count, round up if necessary */ - - datum_count = ACPI_ROUND_UP_TO (byte_field_length, - obj_desc->common_field.access_byte_width); + /* Validate buffer, compute number of datums */ - /* - * If the field is not aligned on a datum boundary and does not - * fit within a single datum, we must read an extra datum. - * - * We could just split the aligned and non-aligned cases since the - * aligned case is so very simple, but this would require more code. - */ - if ((obj_desc->common_field.end_field_valid_bits != 0) && - (!(obj_desc->common_field.flags & AOPOBJ_SINGLE_DATUM))) { - datum_count++; + status = acpi_ex_common_buffer_setup (obj_desc, buffer_length, &datum_count); + if (ACPI_FAILURE (status)) { + return_ACPI_STATUS (status); } - ACPI_DEBUG_PRINT ((ACPI_DB_BFIELD, - "byte_len %X, datum_len %X, byte_gran %X\n", - byte_field_length, datum_count,obj_desc->common_field.access_byte_width)); - /* * Clear the caller's buffer (the whole buffer length as given) * This is very important, especially in the cases where the buffer @@ -942,12 +966,13 @@ acpi_ex_extract_from_field ( * * FUNCTION: acpi_ex_insert_into_field * - * PARAMETERS: *obj_desc - Field to be set - * Buffer - Value to store + * PARAMETERS: obj_desc - Field to be written + * Buffer - Data to be written + * buffer_length - Length of Buffer * * RETURN: Status * - * DESCRIPTION: Store the value into the given field + * DESCRIPTION: Store the Buffer contents into the given field * ******************************************************************************/ @@ -964,42 +989,19 @@ acpi_ex_insert_into_field ( acpi_integer merged_datum; acpi_integer previous_raw_datum; acpi_integer this_raw_datum; - u32 byte_field_length; u32 datum_count; ACPI_FUNCTION_TRACE ("ex_insert_into_field"); - /* - * Incoming buffer must be at least as long as the field, we do not - * allow "partial" field writes. We do not care if the buffer is - * larger than the field, this typically happens when an integer is - * written to a field that is actually smaller than an integer. - */ - byte_field_length = ACPI_ROUND_BITS_UP_TO_BYTES ( - obj_desc->common_field.bit_length); - if (buffer_length < byte_field_length) { - ACPI_DEBUG_PRINT ((ACPI_DB_BFIELD, - "Buffer length %X too small for field %X\n", - buffer_length, byte_field_length)); + /* Validate buffer, compute number of datums */ - return_ACPI_STATUS (AE_BUFFER_OVERFLOW); + status = acpi_ex_common_buffer_setup (obj_desc, buffer_length, &datum_count); + if (ACPI_FAILURE (status)) { + return_ACPI_STATUS (status); } - byte_field_length = ACPI_ROUND_BITS_UP_TO_BYTES ( - obj_desc->common_field.start_field_bit_offset + - obj_desc->common_field.bit_length); - - /* Convert byte count to datum count, round up if necessary */ - - datum_count = ACPI_ROUND_UP_TO (byte_field_length, - obj_desc->common_field.access_byte_width); - - ACPI_DEBUG_PRINT ((ACPI_DB_BFIELD, - "Bytes %X, Datums %X, byte_gran %X\n", - byte_field_length, datum_count, obj_desc->common_field.access_byte_width)); - /* * Break the request into up to three parts (similar to an I/O request): * 1) non-aligned part at start diff --git a/drivers/acpi/executer/exmisc.c b/drivers/acpi/executer/exmisc.c index 74fd68491..961a949e9 100644 --- a/drivers/acpi/executer/exmisc.c +++ b/drivers/acpi/executer/exmisc.c @@ -389,6 +389,8 @@ acpi_ex_do_math_op ( acpi_integer operand1) { + ACPI_FUNCTION_ENTRY (); + switch (opcode) { case AML_ADD_OP: /* Add (Operand0, Operand1, Result) */ @@ -452,15 +454,17 @@ acpi_ex_do_math_op ( * FUNCTION: acpi_ex_do_logical_op * * PARAMETERS: Opcode - AML opcode - * Operand0 - Integer operand #0 - * Operand1 - Integer operand #1 + * obj_desc0 - operand #0 + * obj_desc1 - operand #1 * * RETURN: TRUE/FALSE result of the operation * * DESCRIPTION: Execute a logical AML opcode. The purpose of having all of the * functions here is to prevent a lot of pointer dereferencing * to obtain the operands and to simplify the generation of the - * logical value. + * logical value. Both operands must already be validated as + * 1) Both the same type, and + * 2) Either Integer, Buffer, or String type. * * Note: cleanest machine code seems to be produced by the code * below, rather than using statements of the form: @@ -471,54 +475,137 @@ acpi_ex_do_math_op ( u8 acpi_ex_do_logical_op ( u16 opcode, - acpi_integer operand0, - acpi_integer operand1) + union acpi_operand_object *obj_desc0, + union acpi_operand_object *obj_desc1) { + acpi_integer operand0; + acpi_integer operand1; + u8 *ptr0; + u8 *ptr1; + u32 length0; + u32 length1; + u32 i; - switch (opcode) { + ACPI_FUNCTION_ENTRY (); - case AML_LAND_OP: /* LAnd (Operand0, Operand1) */ - if (operand0 && operand1) { - return (TRUE); - } - break; + if (ACPI_GET_OBJECT_TYPE (obj_desc0) == ACPI_TYPE_INTEGER) { + /* Both operands are of type integer */ + operand0 = obj_desc0->integer.value; + operand1 = obj_desc1->integer.value; - case AML_LEQUAL_OP: /* LEqual (Operand0, Operand1) */ + switch (opcode) { + case AML_LAND_OP: /* LAnd (Operand0, Operand1) */ - if (operand0 == operand1) { - return (TRUE); - } - break; + if (operand0 && operand1) { + return (TRUE); + } + break; + case AML_LEQUAL_OP: /* LEqual (Operand0, Operand1) */ - case AML_LGREATER_OP: /* LGreater (Operand0, Operand1) */ + if (operand0 == operand1) { + return (TRUE); + } + break; - if (operand0 > operand1) { - return (TRUE); - } - break; + case AML_LGREATER_OP: /* LGreater (Operand0, Operand1) */ + + if (operand0 > operand1) { + return (TRUE); + } + break; + + case AML_LLESS_OP: /* LLess (Operand0, Operand1) */ + if (operand0 < operand1) { + return (TRUE); + } + break; - case AML_LLESS_OP: /* LLess (Operand0, Operand1) */ + case AML_LOR_OP: /* LOr (Operand0, Operand1) */ - if (operand0 < operand1) { - return (TRUE); + if (operand0 || operand1) { + return (TRUE); + } + break; + + default: + break; } - break; + } + else { + /* + * Case for Buffer/String objects. + * NOTE: takes advantage of common Buffer/String object fields + */ + length0 = obj_desc0->buffer.length; + ptr0 = obj_desc0->buffer.pointer; + + length1 = obj_desc1->buffer.length; + ptr1 = obj_desc1->buffer.pointer; + + switch (opcode) { + case AML_LEQUAL_OP: /* LEqual (Operand0, Operand1) */ + /* Length and all bytes must be equal */ - case AML_LOR_OP: /* LOr (Operand0, Operand1) */ + if (length0 != length1) { + return (FALSE); + } - if (operand0 || operand1) { + for (i = 0; i < length0; i++) { + if (ptr0[i] != ptr1[i]) { + return (FALSE); + } + } return (TRUE); - } - break; - default: - break; + case AML_LGREATER_OP: /* LGreater (Operand0, Operand1) */ + + /* Lexicographic compare: Scan the 1-to-1 data */ + + for (i = 0; (i < length0) && (i < length1); i++) { + if (ptr0[i] > ptr1[i]) { + return (TRUE); + } + } + + /* Bytes match, now check lengths */ + + if (length0 > length1) { + return (TRUE); + } + + /* Length0 <= Length1 */ + + return (FALSE); + + case AML_LLESS_OP: /* LLess (Operand0, Operand1) */ + + /* Lexicographic compare: Scan the 1-to-1 data */ + + for (i = 0; (i < length0) && (i < length1); i++) { + if (ptr0[i] < ptr1[i]) { + return (TRUE); + } + } + + /* Bytes match, now check lengths */ + + if (length0 < length1) { + return (TRUE); + } + + /* Length0 >= Length1 */ + + return (FALSE); + + default: + break; + } } return (FALSE); diff --git a/drivers/acpi/executer/exmutex.c b/drivers/acpi/executer/exmutex.c index cee2c126e..c709ed2f5 100644 --- a/drivers/acpi/executer/exmutex.c +++ b/drivers/acpi/executer/exmutex.c @@ -54,7 +54,7 @@ * * FUNCTION: acpi_ex_unlink_mutex * - * PARAMETERS: *obj_desc - The mutex to be unlinked + * PARAMETERS: obj_desc - The mutex to be unlinked * * RETURN: Status * @@ -73,6 +73,8 @@ acpi_ex_unlink_mutex ( return; } + /* Doubly linked list */ + if (obj_desc->mutex.next) { (obj_desc->mutex.next)->mutex.prev = obj_desc->mutex.prev; } @@ -90,8 +92,8 @@ acpi_ex_unlink_mutex ( * * FUNCTION: acpi_ex_link_mutex * - * PARAMETERS: *obj_desc - The mutex to be linked - * *list_head - head of the "acquired_mutex" list + * PARAMETERS: obj_desc - The mutex to be linked + * list_head - head of the "acquired_mutex" list * * RETURN: Status * @@ -130,8 +132,8 @@ acpi_ex_link_mutex ( * * FUNCTION: acpi_ex_acquire_mutex * - * PARAMETERS: *time_desc - The 'time to delay' object descriptor - * *obj_desc - The object descriptor for this op + * PARAMETERS: time_desc - The 'time to delay' object descriptor + * obj_desc - The object descriptor for this op * * RETURN: Status * @@ -173,9 +175,8 @@ acpi_ex_acquire_mutex ( return_ACPI_STATUS (AE_AML_MUTEX_ORDER); } - /* - * Support for multiple acquires by the owning thread - */ + /* Support for multiple acquires by the owning thread */ + if (obj_desc->mutex.owner_thread) { /* Special case for Global Lock, allow all threads */ @@ -199,10 +200,11 @@ acpi_ex_acquire_mutex ( return_ACPI_STATUS (status); } - /* Have the mutex, update mutex and walk info */ + /* Have the mutex: update mutex and walk info and save the sync_level */ - obj_desc->mutex.owner_thread = walk_state->thread; + obj_desc->mutex.owner_thread = walk_state->thread; obj_desc->mutex.acquisition_depth = 1; + obj_desc->mutex.original_sync_level = walk_state->thread->current_sync_level; walk_state->thread->current_sync_level = obj_desc->mutex.sync_level; @@ -218,7 +220,7 @@ acpi_ex_acquire_mutex ( * * FUNCTION: acpi_ex_release_mutex * - * PARAMETERS: *obj_desc - The object descriptor for this op + * PARAMETERS: obj_desc - The object descriptor for this op * * RETURN: Status * @@ -281,9 +283,8 @@ acpi_ex_release_mutex ( return_ACPI_STATUS (AE_AML_MUTEX_ORDER); } - /* - * Match multiple Acquires with multiple Releases - */ + /* Match multiple Acquires with multiple Releases */ + obj_desc->mutex.acquisition_depth--; if (obj_desc->mutex.acquisition_depth != 0) { /* Just decrement the depth and return */ @@ -299,10 +300,10 @@ acpi_ex_release_mutex ( status = acpi_ex_system_release_mutex (obj_desc); - /* Update the mutex and walk state */ + /* Update the mutex and walk state, restore sync_level before acquire */ obj_desc->mutex.owner_thread = NULL; - walk_state->thread->current_sync_level = obj_desc->mutex.sync_level; + walk_state->thread->current_sync_level = obj_desc->mutex.original_sync_level; return_ACPI_STATUS (status); } @@ -312,7 +313,7 @@ acpi_ex_release_mutex ( * * FUNCTION: acpi_ex_release_all_mutexes * - * PARAMETERS: *mutex_list - Head of the mutex list + * PARAMETERS: mutex_list - Head of the mutex list * * RETURN: Status * @@ -332,9 +333,8 @@ acpi_ex_release_all_mutexes ( ACPI_FUNCTION_ENTRY (); - /* - * Traverse the list of owned mutexes, releasing each one. - */ + /* Traverse the list of owned mutexes, releasing each one */ + while (next) { this = next; next = this->mutex.next; @@ -352,7 +352,11 @@ acpi_ex_release_all_mutexes ( /* Mark mutex unowned */ - this->mutex.owner_thread = NULL; + this->mutex.owner_thread = NULL; + + /* Update Thread sync_level (Last mutex is the important one) */ + + thread->current_sync_level = this->mutex.original_sync_level; } } diff --git a/drivers/acpi/executer/exoparg2.c b/drivers/acpi/executer/exoparg2.c index 70602ad44..0b6f86a91 100644 --- a/drivers/acpi/executer/exoparg2.c +++ b/drivers/acpi/executer/exoparg2.c @@ -97,6 +97,7 @@ acpi_ex_opcode_2A_0T_0R ( { union acpi_operand_object **operand = &walk_state->operands[0]; struct acpi_namespace_node *node; + u32 value; acpi_status status = AE_OK; @@ -113,16 +114,46 @@ acpi_ex_opcode_2A_0T_0R ( node = (struct acpi_namespace_node *) operand[0]; + /* Second value is the notify value */ + + value = (u32) operand[1]->integer.value; + /* Notifies allowed on this object? */ if (!acpi_ev_is_notify_object (node)) { - ACPI_DEBUG_PRINT ((ACPI_DB_ERROR, "Unexpected notify object type [%s]\n", + ACPI_DEBUG_PRINT ((ACPI_DB_ERROR, + "Unexpected notify object type [%s]\n", acpi_ut_get_type_name (node->type))); status = AE_AML_OPERAND_TYPE; break; } +#ifdef ACPI_GPE_NOTIFY_CHECK + /* + * GPE method wake/notify check. Here, we want to ensure that we + * don't receive any "device_wake" Notifies from a GPE _Lxx or _Exx + * GPE method during system runtime. If we do, the GPE is marked + * as "wake-only" and disabled. + * + * 1) Is the Notify() value == device_wake? + * 2) Is this a GPE deferred method? (An _Lxx or _Exx method) + * 3) Did the original GPE happen at system runtime? + * (versus during wake) + * + * If all three cases are true, this is a wake-only GPE that should + * be disabled at runtime. + */ + if (value == 2) /* device_wake */ { + status = acpi_ev_check_for_wake_only_gpe (walk_state->gpe_event_info); + if (ACPI_FAILURE (status)) { + /* AE_WAKE_ONLY_GPE only error, means ignore this notify */ + + return_ACPI_STATUS (AE_OK) + } + } +#endif + /* * Dispatch the notify to the appropriate handler * NOTE: the request is queued for execution after this method @@ -130,8 +161,7 @@ acpi_ex_opcode_2A_0T_0R ( * from this thread -- because handlers may in turn run other * control methods. */ - status = acpi_ev_queue_notify_request (node, - (u32) operand[1]->integer.value); + status = acpi_ev_queue_notify_request (node, value); break; @@ -543,9 +573,17 @@ acpi_ex_opcode_2A_0T_1R ( * Execute the Opcode */ if (walk_state->op_info->flags & AML_LOGICAL) /* logical_op (Operand0, Operand1) */ { + /* Both operands must be of the same type */ + + if (ACPI_GET_OBJECT_TYPE (operand[0]) != + ACPI_GET_OBJECT_TYPE (operand[1])) { + status = AE_AML_OPERAND_TYPE; + goto cleanup; + } + logical_result = acpi_ex_do_logical_op (walk_state->opcode, - operand[0]->integer.value, - operand[1]->integer.value); + operand[0], + operand[1]); goto store_logical_result; } diff --git a/drivers/acpi/executer/exresolv.c b/drivers/acpi/executer/exresolv.c index e998da7cd..504d53f4a 100644 --- a/drivers/acpi/executer/exresolv.c +++ b/drivers/acpi/executer/exresolv.c @@ -187,15 +187,15 @@ acpi_ex_resolve_object_to_value ( return_ACPI_STATUS (status); } + ACPI_DEBUG_PRINT ((ACPI_DB_EXEC, "[Arg/Local %X] value_obj is %p\n", + stack_desc->reference.offset, obj_desc)); + /* * Now we can delete the original Reference Object and - * replace it with the resolve value + * replace it with the resolved value */ acpi_ut_remove_reference (stack_desc); *stack_ptr = obj_desc; - - ACPI_DEBUG_PRINT ((ACPI_DB_EXEC, "[Arg/Local %d] value_obj is %p\n", - stack_desc->reference.offset, obj_desc)); break; diff --git a/drivers/acpi/executer/exstore.c b/drivers/acpi/executer/exstore.c index 8c884652c..25464f751 100644 --- a/drivers/acpi/executer/exstore.c +++ b/drivers/acpi/executer/exstore.c @@ -102,7 +102,8 @@ acpi_ex_store ( * Storing an object into a Named node. */ status = acpi_ex_store_object_to_node (source_desc, - (struct acpi_namespace_node *) dest_desc, walk_state); + (struct acpi_namespace_node *) dest_desc, walk_state, + ACPI_IMPLICIT_CONVERSION); return_ACPI_STATUS (status); } @@ -153,7 +154,7 @@ acpi_ex_store ( /* Storing an object into a Name "container" */ status = acpi_ex_store_object_to_node (source_desc, ref_desc->reference.object, - walk_state); + walk_state, ACPI_IMPLICIT_CONVERSION); break; @@ -399,6 +400,7 @@ acpi_ex_store_object_to_index ( * PARAMETERS: source_desc - Value to be stored * Node - Named object to receive the value * walk_state - Current walk state + * implicit_conversion - Perform implicit conversion (yes/no) * * RETURN: Status * @@ -421,7 +423,8 @@ acpi_status acpi_ex_store_object_to_node ( union acpi_operand_object *source_desc, struct acpi_namespace_node *node, - struct acpi_walk_state *walk_state) + struct acpi_walk_state *walk_state, + u8 implicit_conversion) { acpi_status status = AE_OK; union acpi_operand_object *target_desc; @@ -451,6 +454,14 @@ acpi_ex_store_object_to_node ( return_ACPI_STATUS (status); } + /* If no implicit conversion, drop into the default case below */ + + if (!implicit_conversion) { + /* Force execution of default (no implicit conversion) */ + + target_type = ACPI_TYPE_ANY; + } + /* * Do the actual store operation */ diff --git a/drivers/acpi/hardware/hwgpe.c b/drivers/acpi/hardware/hwgpe.c index f3fddc274..3910b5ec9 100644 --- a/drivers/acpi/hardware/hwgpe.c +++ b/drivers/acpi/hardware/hwgpe.c @@ -51,104 +51,24 @@ /****************************************************************************** * - * FUNCTION: acpi_hw_enable_gpe + * FUNCTION: acpi_hw_write_gpe_enable_reg * * PARAMETERS: gpe_event_info - Info block for the GPE to be enabled * * RETURN: Status * - * DESCRIPTION: Enable a single GPE. + * DESCRIPTION: Write a GPE enable register. Note: The bit for this GPE must + * already be cleared or set in the parent register + * enable_for_run mask. * ******************************************************************************/ acpi_status -acpi_hw_enable_gpe ( - struct acpi_gpe_event_info *gpe_event_info) -{ - u32 in_byte; - acpi_status status; - - - ACPI_FUNCTION_ENTRY (); - - - /* - * Read the current value of the register, set the appropriate bit - * to enable the GPE, and write out the new register. - */ - status = acpi_hw_low_level_read (8, &in_byte, - &gpe_event_info->register_info->enable_address); - if (ACPI_FAILURE (status)) { - return (status); - } - - /* Write with the new GPE bit enabled */ - - status = acpi_hw_low_level_write (8, (in_byte | gpe_event_info->bit_mask), - &gpe_event_info->register_info->enable_address); - - return (status); -} - - -/****************************************************************************** - * - * FUNCTION: acpi_hw_enable_gpe_for_wakeup - * - * PARAMETERS: gpe_event_info - Info block for the GPE to be enabled - * - * RETURN: None - * - * DESCRIPTION: Keep track of which GPEs the OS has requested not be - * disabled when going to sleep. - * - ******************************************************************************/ - -void -acpi_hw_enable_gpe_for_wakeup ( +acpi_hw_write_gpe_enable_reg ( struct acpi_gpe_event_info *gpe_event_info) { struct acpi_gpe_register_info *gpe_register_info; - - - ACPI_FUNCTION_ENTRY (); - - - /* Get the info block for the entire GPE register */ - - gpe_register_info = gpe_event_info->register_info; - if (!gpe_register_info) { - return; - } - - /* - * Set the bit so we will not enable this GPE when sleeping (and disable - * it upon wake) - */ - gpe_register_info->wake_enable |= gpe_event_info->bit_mask; - gpe_event_info->flags |= (ACPI_GPE_TYPE_WAKE | ACPI_GPE_ENABLED); -} - - -/****************************************************************************** - * - * FUNCTION: acpi_hw_disable_gpe - * - * PARAMETERS: gpe_event_info - Info block for the GPE to be disabled - * - * RETURN: Status - * - * DESCRIPTION: Disable a single GPE. - * - ******************************************************************************/ - -acpi_status -acpi_hw_disable_gpe ( - struct acpi_gpe_event_info *gpe_event_info) -{ - u32 in_byte; acpi_status status; - struct acpi_gpe_register_info *gpe_register_info; ACPI_FUNCTION_ENTRY (); @@ -158,67 +78,15 @@ acpi_hw_disable_gpe ( gpe_register_info = gpe_event_info->register_info; if (!gpe_register_info) { - return (AE_BAD_PARAMETER); - } - - /* - * Read the current value of the register, clear the appropriate bit, - * and write out the new register value to disable the GPE. - */ - status = acpi_hw_low_level_read (8, &in_byte, - &gpe_register_info->enable_address); - if (ACPI_FAILURE (status)) { - return (status); + return (AE_NOT_EXIST); } - /* Write the byte with this GPE bit cleared */ + /* Write the entire GPE (runtime) enable register */ - status = acpi_hw_low_level_write (8, (in_byte & ~(gpe_event_info->bit_mask)), + status = acpi_hw_low_level_write (8, gpe_register_info->enable_for_run, &gpe_register_info->enable_address); - if (ACPI_FAILURE (status)) { - return (status); - } - - /* Make sure this GPE is disabled for wake, also */ - - acpi_hw_disable_gpe_for_wakeup (gpe_event_info); - return (AE_OK); -} - - -/****************************************************************************** - * - * FUNCTION: acpi_hw_disable_gpe_for_wakeup - * - * PARAMETERS: gpe_event_info - Info block for the GPE to be disabled - * - * RETURN: None - * - * DESCRIPTION: Keep track of which GPEs the OS has requested not be - * disabled when going to sleep. - * - ******************************************************************************/ -void -acpi_hw_disable_gpe_for_wakeup ( - struct acpi_gpe_event_info *gpe_event_info) -{ - struct acpi_gpe_register_info *gpe_register_info; - - - ACPI_FUNCTION_ENTRY (); - - - /* Get the info block for the entire GPE register */ - - gpe_register_info = gpe_event_info->register_info; - if (!gpe_register_info) { - return; - } - - /* Clear the bit so we will disable this when sleeping */ - - gpe_register_info->wake_enable &= ~(gpe_event_info->bit_mask); + return (status); } @@ -228,7 +96,7 @@ acpi_hw_disable_gpe_for_wakeup ( * * PARAMETERS: gpe_event_info - Info block for the GPE to be cleared * - * RETURN: status_status + * RETURN: Status * * DESCRIPTION: Clear the status bit for a single GPE. * @@ -248,7 +116,7 @@ acpi_hw_clear_gpe ( * Write a one to the appropriate bit in the status register to * clear this GPE. */ - status = acpi_hw_low_level_write (8, gpe_event_info->bit_mask, + status = acpi_hw_low_level_write (8, gpe_event_info->register_bit, &gpe_event_info->register_info->status_address); return (status); @@ -274,7 +142,7 @@ acpi_hw_get_gpe_status ( acpi_event_status *event_status) { u32 in_byte; - u8 bit_mask; + u8 register_bit; struct acpi_gpe_register_info *gpe_register_info; acpi_status status; acpi_event_status local_event_status = 0; @@ -293,33 +161,28 @@ acpi_hw_get_gpe_status ( /* Get the register bitmask for this GPE */ - bit_mask = gpe_event_info->bit_mask; - - /* GPE Enabled? */ + register_bit = gpe_event_info->register_bit; - status = acpi_hw_low_level_read (8, &in_byte, &gpe_register_info->enable_address); - if (ACPI_FAILURE (status)) { - goto unlock_and_exit; - } + /* GPE currently enabled? (enabled for runtime?) */ - if (bit_mask & in_byte) { + if (register_bit & gpe_register_info->enable_for_run) { local_event_status |= ACPI_EVENT_FLAG_ENABLED; } - /* GPE Enabled for wake? */ + /* GPE enabled for wake? */ - if (bit_mask & gpe_register_info->wake_enable) { + if (register_bit & gpe_register_info->enable_for_wake) { local_event_status |= ACPI_EVENT_FLAG_WAKE_ENABLED; } - /* GPE active (set)? */ + /* GPE currently active (status bit == 1)? */ status = acpi_hw_low_level_read (8, &in_byte, &gpe_register_info->status_address); if (ACPI_FAILURE (status)) { goto unlock_and_exit; } - if (bit_mask & in_byte) { + if (register_bit & in_byte) { local_event_status |= ACPI_EVENT_FLAG_SET; } @@ -411,64 +274,43 @@ acpi_hw_clear_gpe_block ( /****************************************************************************** * - * FUNCTION: acpi_hw_prepare_gpe_block_for_sleep + * FUNCTION: acpi_hw_enable_runtime_gpe_block * * PARAMETERS: gpe_xrupt_info - GPE Interrupt info * gpe_block - Gpe Block info * * RETURN: Status * - * DESCRIPTION: Disable all runtime GPEs and enable all wakeup GPEs -- within - * a single GPE block + * DESCRIPTION: Enable all "runtime" GPEs within a GPE block. (Includes + * combination wake/run GPEs.) * ******************************************************************************/ -static acpi_status -acpi_hw_prepare_gpe_block_for_sleep ( +acpi_status +acpi_hw_enable_runtime_gpe_block ( struct acpi_gpe_xrupt_info *gpe_xrupt_info, struct acpi_gpe_block_info *gpe_block) { u32 i; - struct acpi_gpe_register_info *gpe_register_info; - u32 in_value; acpi_status status; - /* Get the register info for the entire GPE block */ - - gpe_register_info = gpe_block->register_info; + /* NOTE: assumes that all GPEs are currently disabled */ /* Examine each GPE Register within the block */ for (i = 0; i < gpe_block->register_count; i++) { - /* - * Read the enabled/disabled status of all GPEs. We - * will be using it to restore all the GPEs later. - * - * NOTE: Wake GPEs are are ALL disabled at this time, so when we wake - * and restore this register, they will be automatically disabled. - */ - status = acpi_hw_low_level_read (8, &in_value, - &gpe_register_info->enable_address); - if (ACPI_FAILURE (status)) { - return (status); + if (!gpe_block->register_info[i].enable_for_run) { + continue; } - gpe_register_info->enable = (u8) in_value; + /* Enable all "runtime" GPEs in this register */ - /* - * 1) Disable all runtime GPEs - * 2) Enable all wakeup GPEs - */ - status = acpi_hw_low_level_write (8, gpe_register_info->wake_enable, - &gpe_register_info->enable_address); + status = acpi_hw_low_level_write (8, gpe_block->register_info[i].enable_for_run, + &gpe_block->register_info[i].enable_address); if (ACPI_FAILURE (status)) { return (status); } - - /* Point to next GPE register */ - - gpe_register_info++; } return (AE_OK); @@ -477,122 +319,125 @@ acpi_hw_prepare_gpe_block_for_sleep ( /****************************************************************************** * - * FUNCTION: acpi_hw_prepare_gpes_for_sleep + * FUNCTION: acpi_hw_enable_wakeup_gpe_block * - * PARAMETERS: None + * PARAMETERS: gpe_xrupt_info - GPE Interrupt info + * gpe_block - Gpe Block info * * RETURN: Status * - * DESCRIPTION: Disable all runtime GPEs, enable all wake GPEs. - * Called with interrupts disabled. The interrupt handler also - * modifies gpe_register_info->Enable, so it should not be - * given the chance to run until after the runtime GPEs are - * re-enabled. + * DESCRIPTION: Enable all "wake" GPEs within a GPE block. (Includes + * combination wake/run GPEs.) * ******************************************************************************/ acpi_status -acpi_hw_prepare_gpes_for_sleep ( - void) +acpi_hw_enable_wakeup_gpe_block ( + struct acpi_gpe_xrupt_info *gpe_xrupt_info, + struct acpi_gpe_block_info *gpe_block) { + u32 i; acpi_status status; - ACPI_FUNCTION_ENTRY (); + /* Examine each GPE Register within the block */ + for (i = 0; i < gpe_block->register_count; i++) { + if (!gpe_block->register_info[i].enable_for_wake) { + continue; + } - status = acpi_ev_walk_gpe_list (acpi_hw_prepare_gpe_block_for_sleep); - return (status); + /* Enable all "wake" GPEs in this register */ + + status = acpi_hw_low_level_write (8, gpe_block->register_info[i].enable_for_wake, + &gpe_block->register_info[i].enable_address); + if (ACPI_FAILURE (status)) { + return (status); + } + } + + return (AE_OK); } /****************************************************************************** * - * FUNCTION: acpi_hw_restore_gpe_block_on_wake + * FUNCTION: acpi_hw_disable_all_gpes * - * PARAMETERS: gpe_xrupt_info - GPE Interrupt info - * gpe_block - Gpe Block info + * PARAMETERS: Flags - ACPI_NOT_ISR or ACPI_ISR * * RETURN: Status * - * DESCRIPTION: Enable all runtime GPEs and disable all wake GPEs -- in one - * GPE block + * DESCRIPTION: Disable and clear all GPEs * ******************************************************************************/ -static acpi_status -acpi_hw_restore_gpe_block_on_wake ( - struct acpi_gpe_xrupt_info *gpe_xrupt_info, - struct acpi_gpe_block_info *gpe_block) +acpi_status +acpi_hw_disable_all_gpes ( + u32 flags) { - u32 i; - struct acpi_gpe_register_info *gpe_register_info; acpi_status status; - /* This callback processes one entire GPE block */ + ACPI_FUNCTION_TRACE ("hw_disable_all_gpes"); - /* Get the register info for the entire GPE block */ - gpe_register_info = gpe_block->register_info; + status = acpi_ev_walk_gpe_list (acpi_hw_disable_gpe_block, flags); + status = acpi_ev_walk_gpe_list (acpi_hw_clear_gpe_block, flags); + return_ACPI_STATUS (status); +} - /* Examine each GPE register within the block */ - for (i = 0; i < gpe_block->register_count; i++) { - /* Clear the entire status register */ +/****************************************************************************** + * + * FUNCTION: acpi_hw_enable_all_runtime_gpes + * + * PARAMETERS: Flags - ACPI_NOT_ISR or ACPI_ISR + * + * RETURN: Status + * + * DESCRIPTION: Enable all GPEs of the given type + * + ******************************************************************************/ - status = acpi_hw_low_level_write (8, 0xFF, - &gpe_block->register_info[i].status_address); - if (ACPI_FAILURE (status)) { - return (status); - } +acpi_status +acpi_hw_enable_all_runtime_gpes ( + u32 flags) +{ + acpi_status status; - /* - * Restore the GPE Enable register, which will do the following: - * - * 1) Disable all wakeup GPEs - * 2) Enable all runtime GPEs - * - * (On sleep, we saved the enabled status of all GPEs) - */ - status = acpi_hw_low_level_write (8, gpe_register_info->enable, - &gpe_register_info->enable_address); - if (ACPI_FAILURE (status)) { - return (status); - } - /* Point to next GPE register */ + ACPI_FUNCTION_TRACE ("hw_enable_all_runtime_gpes"); - gpe_register_info++; - } - return (AE_OK); + status = acpi_ev_walk_gpe_list (acpi_hw_enable_runtime_gpe_block, flags); + return_ACPI_STATUS (status); } /****************************************************************************** * - * FUNCTION: acpi_hw_restore_gpes_on_wake + * FUNCTION: acpi_hw_enable_all_wakeup_gpes * - * PARAMETERS: None + * PARAMETERS: Flags - ACPI_NOT_ISR or ACPI_ISR * * RETURN: Status * - * DESCRIPTION: Enable all runtime GPEs and disable all wake GPEs -- in all - * GPE blocks + * DESCRIPTION: Enable all GPEs of the given type * ******************************************************************************/ acpi_status -acpi_hw_restore_gpes_on_wake ( - void) +acpi_hw_enable_all_wakeup_gpes ( + u32 flags) { acpi_status status; - ACPI_FUNCTION_ENTRY (); + ACPI_FUNCTION_TRACE ("hw_enable_all_wakeup_gpes"); - status = acpi_ev_walk_gpe_list (acpi_hw_restore_gpe_block_on_wake); - return (status); + status = acpi_ev_walk_gpe_list (acpi_hw_enable_wakeup_gpe_block, flags); + return_ACPI_STATUS (status); } + diff --git a/drivers/acpi/hardware/hwregs.c b/drivers/acpi/hardware/hwregs.c index a7c651316..cb998fe9e 100644 --- a/drivers/acpi/hardware/hwregs.c +++ b/drivers/acpi/hardware/hwregs.c @@ -61,6 +61,7 @@ * RETURN: none * * DESCRIPTION: Clears all fixed and general purpose status bits + * THIS FUNCTION MUST BE CALLED WITH INTERRUPTS DISABLED * ******************************************************************************/ @@ -103,7 +104,7 @@ acpi_hw_clear_acpi_status ( /* Clear the GPE Bits in all GPE registers in all GPE blocks */ - status = acpi_ev_walk_gpe_list (acpi_hw_clear_gpe_block); + status = acpi_ev_walk_gpe_list (acpi_hw_clear_gpe_block, ACPI_ISR); unlock_and_exit: if (flags & ACPI_MTX_LOCK) { @@ -135,7 +136,7 @@ acpi_get_sleep_type_data ( u8 *sleep_type_b) { acpi_status status = AE_OK; - union acpi_operand_object *obj_desc; + struct acpi_parameter_info info; ACPI_FUNCTION_TRACE ("acpi_get_sleep_type_data"); @@ -152,8 +153,9 @@ acpi_get_sleep_type_data ( /* * Evaluate the namespace object containing the values for this state */ + info.parameters = NULL; status = acpi_ns_evaluate_by_name ((char *) acpi_gbl_sleep_state_names[sleep_state], - NULL, &obj_desc); + &info); if (ACPI_FAILURE (status)) { ACPI_DEBUG_PRINT ((ACPI_DB_EXEC, "%s while evaluating sleep_state [%s]\n", acpi_format_exception (status), acpi_gbl_sleep_state_names[sleep_state])); @@ -163,48 +165,50 @@ acpi_get_sleep_type_data ( /* Must have a return object */ - if (!obj_desc) { + if (!info.return_object) { ACPI_REPORT_ERROR (("Missing Sleep State object\n")); status = AE_NOT_EXIST; } /* It must be of type Package */ - else if (ACPI_GET_OBJECT_TYPE (obj_desc) != ACPI_TYPE_PACKAGE) { + else if (ACPI_GET_OBJECT_TYPE (info.return_object) != ACPI_TYPE_PACKAGE) { ACPI_REPORT_ERROR (("Sleep State object not a Package\n")); status = AE_AML_OPERAND_TYPE; } /* The package must have at least two elements */ - else if (obj_desc->package.count < 2) { + else if (info.return_object->package.count < 2) { ACPI_REPORT_ERROR (("Sleep State package does not have at least two elements\n")); status = AE_AML_NO_OPERAND; } /* The first two elements must both be of type Integer */ - else if ((ACPI_GET_OBJECT_TYPE (obj_desc->package.elements[0]) != ACPI_TYPE_INTEGER) || - (ACPI_GET_OBJECT_TYPE (obj_desc->package.elements[1]) != ACPI_TYPE_INTEGER)) { + else if ((ACPI_GET_OBJECT_TYPE (info.return_object->package.elements[0]) != ACPI_TYPE_INTEGER) || + (ACPI_GET_OBJECT_TYPE (info.return_object->package.elements[1]) != ACPI_TYPE_INTEGER)) { ACPI_REPORT_ERROR (("Sleep State package elements are not both Integers (%s, %s)\n", - acpi_ut_get_object_type_name (obj_desc->package.elements[0]), - acpi_ut_get_object_type_name (obj_desc->package.elements[1]))); + acpi_ut_get_object_type_name (info.return_object->package.elements[0]), + acpi_ut_get_object_type_name (info.return_object->package.elements[1]))); status = AE_AML_OPERAND_TYPE; } else { /* * Valid _Sx_ package size, type, and value */ - *sleep_type_a = (u8) (obj_desc->package.elements[0])->integer.value; - *sleep_type_b = (u8) (obj_desc->package.elements[1])->integer.value; + *sleep_type_a = (u8) (info.return_object->package.elements[0])->integer.value; + *sleep_type_b = (u8) (info.return_object->package.elements[1])->integer.value; } if (ACPI_FAILURE (status)) { - ACPI_DEBUG_PRINT ((ACPI_DB_ERROR, "While evaluating sleep_state [%s], bad Sleep object %p type %s\n", - acpi_gbl_sleep_state_names[sleep_state], obj_desc, acpi_ut_get_object_type_name (obj_desc))); + ACPI_DEBUG_PRINT ((ACPI_DB_ERROR, + "While evaluating sleep_state [%s], bad Sleep object %p type %s\n", + acpi_gbl_sleep_state_names[sleep_state], info.return_object, + acpi_ut_get_object_type_name (info.return_object))); } - acpi_ut_remove_reference (obj_desc); + acpi_ut_remove_reference (info.return_object); return_ACPI_STATUS (status); } @@ -245,8 +249,8 @@ acpi_hw_get_bit_register_info ( * return_value - Value that was read from the register * Flags - Lock the hardware or not * - * RETURN: Value is read from specified Register. Value returned is - * normalized to bit0 (is shifted all the way right) + * RETURN: Status and the value read from specified Register. Value + * returned is normalized to bit0 (is shifted all the way right) * * DESCRIPTION: ACPI bit_register read function. * @@ -280,6 +284,8 @@ acpi_get_register ( } } + /* Read from the register */ + status = acpi_hw_register_read (ACPI_MTX_DO_NOT_LOCK, bit_reg_info->parent_register, ®ister_value); @@ -309,10 +315,10 @@ acpi_get_register ( * * PARAMETERS: register_id - ID of ACPI bit_register to access * Value - (only used on write) value to write to the - * Register, NOT pre-normalized to the bit pos. + * Register, NOT pre-normalized to the bit pos * Flags - Lock the hardware or not * - * RETURN: None + * RETURN: Status * * DESCRIPTION: ACPI Bit Register write function. * @@ -457,10 +463,11 @@ unlock_and_exit: * * FUNCTION: acpi_hw_register_read * - * PARAMETERS: use_lock - Mutex hw access. - * register_id - register_iD + Offset. + * PARAMETERS: use_lock - Mutex hw access + * register_id - register_iD + Offset + * return_value - Value that was read from the register * - * RETURN: Value read or written. + * RETURN: Status and the value read. * * DESCRIPTION: Acpi register read function. Registers are read at the * given offset. @@ -568,10 +575,11 @@ unlock_and_exit: * * FUNCTION: acpi_hw_register_write * - * PARAMETERS: use_lock - Mutex hw access. - * register_id - register_iD + Offset. + * PARAMETERS: use_lock - Mutex hw access + * register_id - register_iD + Offset + * Value - The value to write * - * RETURN: Value read or written. + * RETURN: Status * * DESCRIPTION: Acpi register Write function. Registers are written at the * given offset. @@ -687,11 +695,11 @@ unlock_and_exit: * * PARAMETERS: Width - 8, 16, or 32 * Value - Where the value is returned - * Register - GAS register structure + * Reg - GAS register structure * * RETURN: Status * - * DESCRIPTION: Read from either memory, IO, or PCI config space. + * DESCRIPTION: Read from either memory or IO space. * ******************************************************************************/ @@ -701,8 +709,6 @@ acpi_hw_low_level_read ( u32 *value, struct acpi_generic_address *reg) { - struct acpi_pci_id pci_id; - u16 pci_register; acpi_status status; @@ -721,8 +727,8 @@ acpi_hw_low_level_read ( *value = 0; /* - * Three address spaces supported: - * Memory, IO, or PCI_Config. + * Two address spaces supported: Memory or IO. + * PCI_Config is not supported here because the GAS struct is insufficient */ switch (reg->address_space_id) { case ACPI_ADR_SPACE_SYSTEM_MEMORY: @@ -740,19 +746,6 @@ acpi_hw_low_level_read ( break; - case ACPI_ADR_SPACE_PCI_CONFIG: - - pci_id.segment = 0; - pci_id.bus = 0; - pci_id.device = ACPI_PCI_DEVICE (reg->address); - pci_id.function = ACPI_PCI_FUNCTION (reg->address); - pci_register = (u16) ACPI_PCI_REGISTER (reg->address); - - status = acpi_os_read_pci_configuration (&pci_id, pci_register, - value, width); - break; - - default: ACPI_DEBUG_PRINT ((ACPI_DB_ERROR, "Unsupported address space: %X\n", reg->address_space_id)); @@ -774,11 +767,11 @@ acpi_hw_low_level_read ( * * PARAMETERS: Width - 8, 16, or 32 * Value - To be written - * Register - GAS register structure + * Reg - GAS register structure * * RETURN: Status * - * DESCRIPTION: Write to either memory, IO, or PCI config space. + * DESCRIPTION: Write to either memory or IO space. * ******************************************************************************/ @@ -788,8 +781,6 @@ acpi_hw_low_level_write ( u32 value, struct acpi_generic_address *reg) { - struct acpi_pci_id pci_id; - u16 pci_register; acpi_status status; @@ -807,8 +798,8 @@ acpi_hw_low_level_write ( } /* - * Three address spaces supported: - * Memory, IO, or PCI_Config. + * Two address spaces supported: Memory or IO. + * PCI_Config is not supported here because the GAS struct is insufficient */ switch (reg->address_space_id) { case ACPI_ADR_SPACE_SYSTEM_MEMORY: @@ -826,19 +817,6 @@ acpi_hw_low_level_write ( break; - case ACPI_ADR_SPACE_PCI_CONFIG: - - pci_id.segment = 0; - pci_id.bus = 0; - pci_id.device = ACPI_PCI_DEVICE (reg->address); - pci_id.function = ACPI_PCI_FUNCTION (reg->address); - pci_register = (u16) ACPI_PCI_REGISTER (reg->address); - - status = acpi_os_write_pci_configuration (&pci_id, pci_register, - (acpi_integer) value, width); - break; - - default: ACPI_DEBUG_PRINT ((ACPI_DB_ERROR, "Unsupported address space: %X\n", reg->address_space_id)); diff --git a/drivers/acpi/hardware/hwsleep.c b/drivers/acpi/hardware/hwsleep.c index f0c04149f..6ffa80ba2 100644 --- a/drivers/acpi/hardware/hwsleep.c +++ b/drivers/acpi/hardware/hwsleep.c @@ -265,19 +265,21 @@ acpi_enter_sleep_state ( sleep_type_reg_info = acpi_hw_get_bit_register_info (ACPI_BITREG_SLEEP_TYPE_A); sleep_enable_reg_info = acpi_hw_get_bit_register_info (ACPI_BITREG_SLEEP_ENABLE); - if (sleep_state != ACPI_STATE_S5) { - /* Clear wake status */ + /* Clear wake status */ - status = acpi_set_register (ACPI_BITREG_WAKE_STATUS, 1, ACPI_MTX_DO_NOT_LOCK); - if (ACPI_FAILURE (status)) { - return_ACPI_STATUS (status); - } + status = acpi_set_register (ACPI_BITREG_WAKE_STATUS, 1, ACPI_MTX_DO_NOT_LOCK); + if (ACPI_FAILURE (status)) { + return_ACPI_STATUS (status); + } - status = acpi_hw_clear_acpi_status (ACPI_MTX_DO_NOT_LOCK); - if (ACPI_FAILURE (status)) { - return_ACPI_STATUS (status); - } + /* Clear all fixed and general purpose status bits */ + status = acpi_hw_clear_acpi_status (ACPI_MTX_DO_NOT_LOCK); + if (ACPI_FAILURE (status)) { + return_ACPI_STATUS (status); + } + + if (sleep_state != ACPI_STATE_S5) { /* Disable BM arbitration */ status = acpi_set_register (ACPI_BITREG_ARB_DISABLE, 1, ACPI_MTX_DO_NOT_LOCK); @@ -287,10 +289,16 @@ acpi_enter_sleep_state ( } /* - * 1) Disable all runtime GPEs + * 1) Disable/Clear all GPEs * 2) Enable all wakeup GPEs */ - status = acpi_hw_prepare_gpes_for_sleep (); + status = acpi_hw_disable_all_gpes (ACPI_ISR); + if (ACPI_FAILURE (status)) { + return_ACPI_STATUS (status); + } + acpi_gbl_system_awake_and_running = FALSE; + + status = acpi_hw_enable_all_wakeup_gpes (ACPI_ISR); if (ACPI_FAILURE (status)) { return_ACPI_STATUS (status); } @@ -420,10 +428,16 @@ acpi_enter_sleep_state_s4bios ( } /* - * 1) Disable all runtime GPEs + * 1) Disable/Clear all GPEs * 2) Enable all wakeup GPEs */ - status = acpi_hw_prepare_gpes_for_sleep (); + status = acpi_hw_disable_all_gpes (ACPI_ISR); + if (ACPI_FAILURE (status)) { + return_ACPI_STATUS (status); + } + acpi_gbl_system_awake_and_running = FALSE; + + status = acpi_hw_enable_all_wakeup_gpes (ACPI_ISR); if (ACPI_FAILURE (status)) { return_ACPI_STATUS (status); } @@ -453,6 +467,7 @@ acpi_enter_sleep_state_s4bios ( * RETURN: Status * * DESCRIPTION: Perform OS-independent ACPI cleanup after a sleep + * Called with interrupts ENABLED. * ******************************************************************************/ @@ -540,19 +555,25 @@ acpi_leave_sleep_state ( /* * Restore the GPEs: - * 1) Disable all wakeup GPEs + * 1) Disable/Clear all GPEs * 2) Enable all runtime GPEs */ - status = acpi_hw_restore_gpes_on_wake (); + status = acpi_hw_disable_all_gpes (ACPI_NOT_ISR); + if (ACPI_FAILURE (status)) { + return_ACPI_STATUS (status); + } + acpi_gbl_system_awake_and_running = TRUE; + + status = acpi_hw_enable_all_runtime_gpes (ACPI_NOT_ISR); if (ACPI_FAILURE (status)) { return_ACPI_STATUS (status); } /* Enable power button */ - acpi_set_register(acpi_gbl_fixed_event_info[ACPI_EVENT_POWER_BUTTON].enable_register_id, + (void) acpi_set_register(acpi_gbl_fixed_event_info[ACPI_EVENT_POWER_BUTTON].enable_register_id, 1, ACPI_MTX_DO_NOT_LOCK); - acpi_set_register(acpi_gbl_fixed_event_info[ACPI_EVENT_POWER_BUTTON].status_register_id, + (void) acpi_set_register(acpi_gbl_fixed_event_info[ACPI_EVENT_POWER_BUTTON].status_register_id, 1, ACPI_MTX_DO_NOT_LOCK); /* Enable BM arbitration */ diff --git a/drivers/acpi/hardware/hwtimer.c b/drivers/acpi/hardware/hwtimer.c index 8878aff9d..1f94d6ffa 100644 --- a/drivers/acpi/hardware/hwtimer.c +++ b/drivers/acpi/hardware/hwtimer.c @@ -52,11 +52,11 @@ * * FUNCTION: acpi_get_timer_resolution * - * PARAMETERS: none + * PARAMETERS: Resolution - Where the resolution is returned * - * RETURN: Number of bits of resolution in the PM Timer (24 or 32). + * RETURN: Status and timer resolution * - * DESCRIPTION: Obtains resolution of the ACPI PM Timer. + * DESCRIPTION: Obtains resolution of the ACPI PM Timer (24 or 32 bits). * ******************************************************************************/ @@ -86,11 +86,11 @@ acpi_get_timer_resolution ( * * FUNCTION: acpi_get_timer * - * PARAMETERS: none + * PARAMETERS: Ticks - Where the timer value is returned * - * RETURN: Current value of the ACPI PM Timer (in ticks). + * RETURN: Status and current ticks * - * DESCRIPTION: Obtains current value of ACPI PM Timer. + * DESCRIPTION: Obtains current value of ACPI PM Timer (in ticks). * ******************************************************************************/ @@ -118,11 +118,11 @@ acpi_get_timer ( * * FUNCTION: acpi_get_timer_duration * - * PARAMETERS: start_ticks - * end_ticks - * time_elapsed + * PARAMETERS: start_ticks - Starting timestamp + * end_ticks - End timestamp + * time_elapsed - Where the elapsed time is returned * - * RETURN: time_elapsed + * RETURN: Status and time_elapsed * * DESCRIPTION: Computes the time elapsed (in microseconds) between two * PM Timer time stamps, taking into account the possibility of @@ -136,7 +136,7 @@ acpi_get_timer ( * Note that this function accommodates only a single timer * rollover. Thus for 24-bit timers, this function should only * be used for calculating durations less than ~4.6 seconds - * (~20 minutes for 32-bit timers) -- calculations below + * (~20 minutes for 32-bit timers) -- calculations below: * * 2**24 Ticks / 3,600,000 Ticks/Sec = 4.66 sec * 2**32 Ticks / 3,600,000 Ticks/Sec = 1193 sec or 19.88 minutes @@ -164,7 +164,6 @@ acpi_get_timer_duration ( /* * Compute Tick Delta: - * ------------------- * Handle (max one) timer rollovers on 24- versus 32-bit timers. */ if (start_ticks < end_ticks) { @@ -188,10 +187,7 @@ acpi_get_timer_duration ( } /* - * Compute Duration: - * ----------------- - * - * Requires a 64-bit divide: + * Compute Duration (Requires a 64-bit divide): * * time_elapsed = (delta_ticks * 1000000) / PM_TIMER_FREQUENCY; */ diff --git a/drivers/acpi/motherboard.c b/drivers/acpi/motherboard.c new file mode 100644 index 000000000..ee9c5d13e --- /dev/null +++ b/drivers/acpi/motherboard.c @@ -0,0 +1,177 @@ +/* + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * This program is free software; you can redistribute it and/or modify + * it under the terms of the 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. + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + */ + +/* Purpose: Prevent PCMCIA cards from using motherboard resources. */ + +#include +#include +#include +#include +#include +#include + +#include +#include + +#define _COMPONENT ACPI_SYSTEM_COMPONENT +ACPI_MODULE_NAME ("acpi_motherboard") + +/* Dell use PNP0C01 instead of PNP0C02 */ +#define ACPI_MB_HID1 "PNP0C01" +#define ACPI_MB_HID2 "PNP0C02" + +/** + * Doesn't care about legacy IO ports, only IO ports beyond 0x1000 are reserved + * Doesn't care about the failure of 'request_region', since other may reserve + * the io ports as well + */ +#define IS_RESERVED_ADDR(base, len) \ + (((len) > 0) && ((base) > 0) && ((base) + (len) < IO_SPACE_LIMIT) \ + && ((base) + (len) > PCIBIOS_MIN_IO)) + +/* + * Clearing the flag (IORESOURCE_BUSY) allows drivers to use + * the io ports if they really know they can use it, while + * still preventing hotplug PCI devices from using it. + */ + +static acpi_status +acpi_reserve_io_ranges (struct acpi_resource *res, void *data) +{ + struct resource *requested_res = NULL; + + ACPI_FUNCTION_TRACE("acpi_reserve_io_ranges"); + + if (res->id == ACPI_RSTYPE_IO) { + struct acpi_resource_io *io_res = &res->data.io; + + if (io_res->min_base_address != io_res->max_base_address) + return AE_OK; + if (IS_RESERVED_ADDR(io_res->min_base_address, io_res->range_length)) { + ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Motherboard resources 0x%08x - 0x%08x\n", + io_res->min_base_address, + io_res->min_base_address + io_res->range_length)); + requested_res = request_region(io_res->min_base_address, + io_res->range_length, "motherboard"); + } + } else if (res->id == ACPI_RSTYPE_FIXED_IO) { + struct acpi_resource_fixed_io *fixed_io_res = &res->data.fixed_io; + + if (IS_RESERVED_ADDR(fixed_io_res->base_address, fixed_io_res->range_length)) { + ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Motherboard resources 0x%08x - 0x%08x\n", + fixed_io_res->base_address, + fixed_io_res->base_address + fixed_io_res->range_length)); + requested_res = request_region(fixed_io_res->base_address, + fixed_io_res->range_length, "motherboard"); + } + } else { + /* Memory mapped IO? */ + } + + if (requested_res) + requested_res->flags &= ~IORESOURCE_BUSY; + return AE_OK; +} + +static int acpi_motherboard_add (struct acpi_device *device) +{ + if (!device) + return -EINVAL; + acpi_walk_resources(device->handle, METHOD_NAME__CRS, + acpi_reserve_io_ranges, NULL); + + return 0; +} + +static struct acpi_driver acpi_motherboard_driver1 = { + .name = "motherboard", + .class = "", + .ids = ACPI_MB_HID1, + .ops = { + .add = acpi_motherboard_add, + }, +}; + +static struct acpi_driver acpi_motherboard_driver2 = { + .name = "motherboard", + .class = "", + .ids = ACPI_MB_HID2, + .ops = { + .add = acpi_motherboard_add, + }, +}; + +static void __init +acpi_reserve_resources (void) +{ + if (acpi_gbl_FADT->xpm1a_evt_blk.address && acpi_gbl_FADT->pm1_evt_len) + request_region(acpi_gbl_FADT->xpm1a_evt_blk.address, + acpi_gbl_FADT->pm1_evt_len, "PM1a_EVT_BLK"); + + if (acpi_gbl_FADT->xpm1b_evt_blk.address && acpi_gbl_FADT->pm1_evt_len) + request_region(acpi_gbl_FADT->xpm1b_evt_blk.address, + acpi_gbl_FADT->pm1_evt_len, "PM1b_EVT_BLK"); + + if (acpi_gbl_FADT->xpm1a_cnt_blk.address && acpi_gbl_FADT->pm1_cnt_len) + request_region(acpi_gbl_FADT->xpm1a_cnt_blk.address, + acpi_gbl_FADT->pm1_cnt_len, "PM1a_CNT_BLK"); + + if (acpi_gbl_FADT->xpm1b_cnt_blk.address && acpi_gbl_FADT->pm1_cnt_len) + request_region(acpi_gbl_FADT->xpm1b_cnt_blk.address, + acpi_gbl_FADT->pm1_cnt_len, "PM1b_CNT_BLK"); + + if (acpi_gbl_FADT->xpm_tmr_blk.address && acpi_gbl_FADT->pm_tm_len == 4) + request_region(acpi_gbl_FADT->xpm_tmr_blk.address, + 4, "PM_TMR"); + + if (acpi_gbl_FADT->xpm2_cnt_blk.address && acpi_gbl_FADT->pm2_cnt_len) + request_region(acpi_gbl_FADT->xpm2_cnt_blk.address, + acpi_gbl_FADT->pm2_cnt_len, "PM2_CNT_BLK"); + + /* Length of GPE blocks must be a non-negative multiple of 2 */ + + if (acpi_gbl_FADT->xgpe0_blk.address && acpi_gbl_FADT->gpe0_blk_len && + !(acpi_gbl_FADT->gpe0_blk_len & 0x1)) + request_region(acpi_gbl_FADT->xgpe0_blk.address, + acpi_gbl_FADT->gpe0_blk_len, "GPE0_BLK"); + + if (acpi_gbl_FADT->xgpe1_blk.address && acpi_gbl_FADT->gpe1_blk_len && + !(acpi_gbl_FADT->gpe1_blk_len & 0x1)) + request_region(acpi_gbl_FADT->xgpe1_blk.address, + acpi_gbl_FADT->gpe1_blk_len, "GPE1_BLK"); +} + +static int __init acpi_motherboard_init(void) +{ + acpi_bus_register_driver(&acpi_motherboard_driver1); + acpi_bus_register_driver(&acpi_motherboard_driver2); + /* + * Guarantee motherboard IO reservation first + * This module must run after scan.c + */ + if (!acpi_disabled) + acpi_reserve_resources (); + return 0; +} + +/** + * Reserve motherboard resources after PCI claim BARs, + * but before PCI assign resources for uninitialized PCI devices + */ +fs_initcall(acpi_motherboard_init); diff --git a/drivers/acpi/namespace/nsaccess.c b/drivers/acpi/namespace/nsaccess.c index 23baf93bf..765217be4 100644 --- a/drivers/acpi/namespace/nsaccess.c +++ b/drivers/acpi/namespace/nsaccess.c @@ -193,7 +193,7 @@ acpi_ns_root_initialize (void) case ACPI_TYPE_MUTEX: obj_desc->mutex.node = new_node; - obj_desc->mutex.sync_level = (u16) ACPI_STRTOUL + obj_desc->mutex.sync_level = (u8) ACPI_STRTOUL (val, NULL, 10); if (ACPI_STRCMP (init_val->name, "_GL_") == 0) { diff --git a/drivers/acpi/namespace/nseval.c b/drivers/acpi/namespace/nseval.c index b054b365c..418f03ee2 100644 --- a/drivers/acpi/namespace/nseval.c +++ b/drivers/acpi/namespace/nseval.c @@ -77,13 +77,10 @@ acpi_status acpi_ns_evaluate_relative ( - struct acpi_namespace_node *handle, char *pathname, - union acpi_operand_object **params, - union acpi_operand_object **return_object) + struct acpi_parameter_info *info) { acpi_status status; - struct acpi_namespace_node *prefix_node; struct acpi_namespace_node *node = NULL; union acpi_generic_state *scope_info; char *internal_path = NULL; @@ -95,7 +92,7 @@ acpi_ns_evaluate_relative ( /* * Must have a valid object handle */ - if (!handle) { + if (!info || !info->node) { return_ACPI_STATUS (AE_BAD_PARAMETER); } @@ -118,8 +115,8 @@ acpi_ns_evaluate_relative ( goto cleanup; } - prefix_node = acpi_ns_map_handle_to_node (handle); - if (!prefix_node) { + info->node = acpi_ns_map_handle_to_node (info->node); + if (!info->node) { (void) acpi_ut_release_mutex (ACPI_MTX_NAMESPACE); status = AE_BAD_PARAMETER; goto cleanup; @@ -127,7 +124,7 @@ acpi_ns_evaluate_relative ( /* Lookup the name in the namespace */ - scope_info->scope.node = prefix_node; + scope_info->scope.node = info->node; status = acpi_ns_lookup (scope_info, internal_path, ACPI_TYPE_ANY, ACPI_IMODE_EXECUTE, ACPI_NS_NO_UPSEARCH, NULL, &node); @@ -147,7 +144,8 @@ acpi_ns_evaluate_relative ( ACPI_DEBUG_PRINT ((ACPI_DB_NAMES, "%s [%p] Value %p\n", pathname, node, acpi_ns_get_attached_object (node))); - status = acpi_ns_evaluate_by_handle (node, params, return_object); + info->node = node; + status = acpi_ns_evaluate_by_handle (info); ACPI_DEBUG_PRINT ((ACPI_DB_NAMES, "*** Completed eval of object %s ***\n", pathname)); @@ -166,6 +164,7 @@ cleanup1: * FUNCTION: acpi_ns_evaluate_by_name * * PARAMETERS: Pathname - Fully qualified pathname to the object + * Info - Contains: * return_object - Where to put method's return value (if * any). If NULL, no value is returned. * Params - List of parameters to pass to the method, @@ -184,11 +183,9 @@ cleanup1: acpi_status acpi_ns_evaluate_by_name ( char *pathname, - union acpi_operand_object **params, - union acpi_operand_object **return_object) + struct acpi_parameter_info *info) { acpi_status status; - struct acpi_namespace_node *node = NULL; char *internal_path = NULL; @@ -211,7 +208,7 @@ acpi_ns_evaluate_by_name ( status = acpi_ns_lookup (NULL, internal_path, ACPI_TYPE_ANY, ACPI_IMODE_EXECUTE, ACPI_NS_NO_UPSEARCH, NULL, - &node); + &info->node); (void) acpi_ut_release_mutex (ACPI_MTX_NAMESPACE); @@ -226,9 +223,9 @@ acpi_ns_evaluate_by_name ( * to evaluate it. */ ACPI_DEBUG_PRINT ((ACPI_DB_NAMES, "%s [%p] Value %p\n", - pathname, node, acpi_ns_get_attached_object (node))); + pathname, info->node, acpi_ns_get_attached_object (info->node))); - status = acpi_ns_evaluate_by_handle (node, params, return_object); + status = acpi_ns_evaluate_by_handle (info); ACPI_DEBUG_PRINT ((ACPI_DB_NAMES, "*** Completed eval of object %s ***\n", pathname)); @@ -254,6 +251,7 @@ cleanup: * Params - List of parameters to pass to the method, * terminated by NULL. Params itself may be * NULL if no parameters are being passed. + * param_type - Type of Parameter list * return_object - Where to put method's return value (if * any). If NULL, no value is returned. * @@ -267,13 +265,9 @@ cleanup: acpi_status acpi_ns_evaluate_by_handle ( - struct acpi_namespace_node *handle, - union acpi_operand_object **params, - union acpi_operand_object **return_object) + struct acpi_parameter_info *info) { - struct acpi_namespace_node *node; acpi_status status; - union acpi_operand_object *local_return_object; ACPI_FUNCTION_TRACE ("ns_evaluate_by_handle"); @@ -287,15 +281,13 @@ acpi_ns_evaluate_by_handle ( /* Parameter Validation */ - if (!handle) { + if (!info) { return_ACPI_STATUS (AE_BAD_PARAMETER); } - if (return_object) { - /* Initialize the return value to an invalid object */ + /* Initialize the return value to an invalid object */ - *return_object = NULL; - } + info->return_object = NULL; /* Get the prefix handle and Node */ @@ -304,8 +296,8 @@ acpi_ns_evaluate_by_handle ( return_ACPI_STATUS (status); } - node = acpi_ns_map_handle_to_node (handle); - if (!node) { + info->node = acpi_ns_map_handle_to_node (info->node); + if (!info->node) { (void) acpi_ut_release_mutex (ACPI_MTX_NAMESPACE); return_ACPI_STATUS (AE_BAD_PARAMETER); } @@ -315,8 +307,8 @@ acpi_ns_evaluate_by_handle ( * so that proper scoping context will be established * before execution. */ - if (acpi_ns_get_type (node) == ACPI_TYPE_LOCAL_METHOD_ALIAS) { - node = ACPI_CAST_PTR (struct acpi_namespace_node, node->object); + if (acpi_ns_get_type (info->node) == ACPI_TYPE_LOCAL_METHOD_ALIAS) { + info->node = ACPI_CAST_PTR (struct acpi_namespace_node, info->node->object); } /* @@ -328,19 +320,18 @@ acpi_ns_evaluate_by_handle ( * In both cases, the namespace is unlocked by the * acpi_ns* procedure */ - if (acpi_ns_get_type (node) == ACPI_TYPE_METHOD) { + if (acpi_ns_get_type (info->node) == ACPI_TYPE_METHOD) { /* * Case 1) We have an actual control method to execute */ - status = acpi_ns_execute_control_method (node, params, - &local_return_object); + status = acpi_ns_execute_control_method (info); } else { /* * Case 2) Object is NOT a method, just return its * current value */ - status = acpi_ns_get_object_value (node, &local_return_object); + status = acpi_ns_get_object_value (info); } /* @@ -348,20 +339,6 @@ acpi_ns_evaluate_by_handle ( * be dealt with */ if (status == AE_CTRL_RETURN_VALUE) { - /* - * If the Method returned a value and the caller - * provided a place to store a returned value, Copy - * the returned value to the object descriptor provided - * by the caller. - */ - if (return_object) { - /* - * Valid return object, copy the pointer to - * the returned object - */ - *return_object = local_return_object; - } - /* Map AE_CTRL_RETURN_VALUE to AE_OK, we are done with it */ status = AE_OK; @@ -396,9 +373,7 @@ acpi_ns_evaluate_by_handle ( acpi_status acpi_ns_execute_control_method ( - struct acpi_namespace_node *method_node, - union acpi_operand_object **params, - union acpi_operand_object **return_obj_desc) + struct acpi_parameter_info *info) { acpi_status status; union acpi_operand_object *obj_desc; @@ -409,7 +384,7 @@ acpi_ns_execute_control_method ( /* Verify that there is a method associated with this object */ - obj_desc = acpi_ns_get_attached_object (method_node); + obj_desc = acpi_ns_get_attached_object (info->node); if (!obj_desc) { ACPI_DEBUG_PRINT ((ACPI_DB_ERROR, "No attached method object\n")); @@ -417,7 +392,7 @@ acpi_ns_execute_control_method ( return_ACPI_STATUS (AE_NULL_OBJECT); } - ACPI_DUMP_PATHNAME (method_node, "Execute Method:", + ACPI_DUMP_PATHNAME (info->node, "Execute Method:", ACPI_LV_INFO, _COMPONENT); ACPI_DEBUG_PRINT ((ACPI_DB_EXEC, "Method at AML address %p Length %X\n", @@ -444,7 +419,7 @@ acpi_ns_execute_control_method ( return_ACPI_STATUS (status); } - status = acpi_psx_execute (method_node, params, return_obj_desc); + status = acpi_psx_execute (info); acpi_ex_exit_interpreter (); return_ACPI_STATUS (status); @@ -468,11 +443,10 @@ acpi_ns_execute_control_method ( acpi_status acpi_ns_get_object_value ( - struct acpi_namespace_node *node, - union acpi_operand_object **return_obj_desc) + struct acpi_parameter_info *info) { acpi_status status = AE_OK; - struct acpi_namespace_node *resolved_node = node; + struct acpi_namespace_node *resolved_node = info->node; ACPI_FUNCTION_TRACE ("ns_get_object_value"); @@ -518,9 +492,9 @@ acpi_ns_get_object_value ( if (ACPI_SUCCESS (status)) { status = AE_CTRL_RETURN_VALUE; - *return_obj_desc = ACPI_CAST_PTR (union acpi_operand_object, resolved_node); + info->return_object = ACPI_CAST_PTR (union acpi_operand_object, resolved_node); ACPI_DEBUG_PRINT ((ACPI_DB_NAMES, "Returning object %p [%s]\n", - *return_obj_desc, acpi_ut_get_object_type_name (*return_obj_desc))); + info->return_object, acpi_ut_get_object_type_name (info->return_object))); } } diff --git a/drivers/acpi/namespace/nsinit.c b/drivers/acpi/namespace/nsinit.c index c8e9317ab..861787520 100644 --- a/drivers/acpi/namespace/nsinit.c +++ b/drivers/acpi/namespace/nsinit.c @@ -149,7 +149,7 @@ acpi_ns_initialize_devices ( return_ACPI_STATUS (status); } - /* Walk namespace for all objects of type Device or Processor */ + /* Walk namespace for all objects */ status = acpi_ns_walk_namespace (ACPI_TYPE_ANY, ACPI_ROOT_OBJECT, ACPI_UINT32_MAX, TRUE, acpi_ns_init_one_device, &info, NULL); @@ -337,25 +337,29 @@ acpi_ns_init_one_device ( void *context, void **return_value) { - acpi_status status; - struct acpi_namespace_node *node; - u32 flags; struct acpi_device_walk_info *info = (struct acpi_device_walk_info *) context; + struct acpi_parameter_info pinfo; + u32 flags; + acpi_status status; ACPI_FUNCTION_TRACE ("ns_init_one_device"); - node = acpi_ns_map_handle_to_node (obj_handle); - if (!node) { + pinfo.parameters = NULL; + pinfo.parameter_type = ACPI_PARAM_ARGS; + + pinfo.node = acpi_ns_map_handle_to_node (obj_handle); + if (!pinfo.node) { return_ACPI_STATUS (AE_BAD_PARAMETER); } /* - * We will run _STA/_INI on Devices and Processors only + * We will run _STA/_INI on Devices, Processors and thermal_zones only */ - if ((node->type != ACPI_TYPE_DEVICE) && - (node->type != ACPI_TYPE_PROCESSOR)) { + if ((pinfo.node->type != ACPI_TYPE_DEVICE) && + (pinfo.node->type != ACPI_TYPE_PROCESSOR) && + (pinfo.node->type != ACPI_TYPE_THERMAL)) { return_ACPI_STATUS (AE_OK); } @@ -368,17 +372,17 @@ acpi_ns_init_one_device ( /* * Run _STA to determine if we can run _INI on the device. */ - ACPI_DEBUG_EXEC (acpi_ut_display_init_pathname (ACPI_TYPE_METHOD, node, "_STA")); - status = acpi_ut_execute_STA (node, &flags); + ACPI_DEBUG_EXEC (acpi_ut_display_init_pathname (ACPI_TYPE_METHOD, pinfo.node, "_STA")); + status = acpi_ut_execute_STA (pinfo.node, &flags); if (ACPI_FAILURE (status)) { - if (node->type == ACPI_TYPE_DEVICE) { + if (pinfo.node->type == ACPI_TYPE_DEVICE) { /* Ignore error and move on to next device */ return_ACPI_STATUS (AE_OK); } - /* _STA is not required for Processor objects */ + /* _STA is not required for Processor or thermal_zone objects */ } else { info->num_STA++; @@ -393,22 +397,22 @@ acpi_ns_init_one_device ( /* * The device is present. Run _INI. */ - ACPI_DEBUG_EXEC (acpi_ut_display_init_pathname (ACPI_TYPE_METHOD, obj_handle, "_INI")); - status = acpi_ns_evaluate_relative (obj_handle, "_INI", NULL, NULL); + ACPI_DEBUG_EXEC (acpi_ut_display_init_pathname (ACPI_TYPE_METHOD, pinfo.node, "_INI")); + status = acpi_ns_evaluate_relative ("_INI", &pinfo); if (ACPI_FAILURE (status)) { /* No _INI (AE_NOT_FOUND) means device requires no initialization */ if (status != AE_NOT_FOUND) { /* Ignore error and move on to next device */ - #ifdef ACPI_DEBUG_OUTPUT - char *scope_name = acpi_ns_get_external_pathname (obj_handle); +#ifdef ACPI_DEBUG_OUTPUT + char *scope_name = acpi_ns_get_external_pathname (pinfo.node); ACPI_DEBUG_PRINT ((ACPI_DB_WARN, "%s._INI failed: %s\n", scope_name, acpi_format_exception (status))); ACPI_MEM_FREE (scope_name); - #endif +#endif } status = AE_OK; @@ -422,7 +426,7 @@ acpi_ns_init_one_device ( if (acpi_gbl_init_handler) { /* External initialization handler is present, call it */ - status = acpi_gbl_init_handler (obj_handle, ACPI_INIT_DEVICE_INI); + status = acpi_gbl_init_handler (pinfo.node, ACPI_INIT_DEVICE_INI); } diff --git a/drivers/acpi/namespace/nsparse.c b/drivers/acpi/namespace/nsparse.c index f550ce5c1..530982a38 100644 --- a/drivers/acpi/namespace/nsparse.c +++ b/drivers/acpi/namespace/nsparse.c @@ -94,8 +94,9 @@ acpi_ns_one_complete_parse ( return_ACPI_STATUS (AE_NO_MEMORY); } - status = acpi_ds_init_aml_walk (walk_state, parse_root, NULL, table_desc->aml_start, - table_desc->aml_length, NULL, NULL, pass_number); + status = acpi_ds_init_aml_walk (walk_state, parse_root, NULL, + table_desc->aml_start, table_desc->aml_length, + NULL, pass_number); if (ACPI_FAILURE (status)) { acpi_ds_delete_walk_state (walk_state); return_ACPI_STATUS (status); diff --git a/drivers/acpi/namespace/nsxfeval.c b/drivers/acpi/namespace/nsxfeval.c index 312835443..8e916b5e2 100644 --- a/drivers/acpi/namespace/nsxfeval.c +++ b/drivers/acpi/namespace/nsxfeval.c @@ -174,8 +174,7 @@ acpi_evaluate_object ( { acpi_status status; acpi_status status2; - union acpi_operand_object **internal_params = NULL; - union acpi_operand_object *internal_return_obj = NULL; + struct acpi_parameter_info info; acpi_size buffer_space_needed; u32 i; @@ -183,6 +182,11 @@ acpi_evaluate_object ( ACPI_FUNCTION_TRACE ("acpi_evaluate_object"); + info.node = handle; + info.parameters = NULL; + info.return_object = NULL; + info.parameter_type = ACPI_PARAM_ARGS; + /* * If there are parameters to be passed to the object * (which must be a control method), the external objects @@ -193,9 +197,10 @@ acpi_evaluate_object ( * Allocate a new parameter block for the internal objects * Add 1 to count to allow for null terminated internal list */ - internal_params = ACPI_MEM_CALLOCATE (((acpi_size) external_params->count + 1) * - sizeof (void *)); - if (!internal_params) { + info.parameters = ACPI_MEM_CALLOCATE ( + ((acpi_size) external_params->count + 1) * + sizeof (void *)); + if (!info.parameters) { return_ACPI_STATUS (AE_NO_MEMORY); } @@ -205,15 +210,16 @@ acpi_evaluate_object ( */ for (i = 0; i < external_params->count; i++) { status = acpi_ut_copy_eobject_to_iobject (&external_params->pointer[i], - &internal_params[i]); + &info.parameters[i]); if (ACPI_FAILURE (status)) { - acpi_ut_delete_internal_object_list (internal_params); + acpi_ut_delete_internal_object_list (info.parameters); return_ACPI_STATUS (status); } } - internal_params[external_params->count] = NULL; + info.parameters[external_params->count] = NULL; } + /* * Three major cases: * 1) Fully qualified pathname @@ -225,8 +231,7 @@ acpi_evaluate_object ( /* * The path is fully qualified, just evaluate by name */ - status = acpi_ns_evaluate_by_name (pathname, internal_params, - &internal_return_obj); + status = acpi_ns_evaluate_by_name (pathname, &info); } else if (!handle) { /* @@ -256,15 +261,13 @@ acpi_evaluate_object ( * The null pathname case means the handle is for * the actual object to be evaluated */ - status = acpi_ns_evaluate_by_handle (handle, internal_params, - &internal_return_obj); + status = acpi_ns_evaluate_by_handle (&info); } else { /* * Both a Handle and a relative Pathname */ - status = acpi_ns_evaluate_relative (handle, pathname, internal_params, - &internal_return_obj); + status = acpi_ns_evaluate_relative (pathname, &info); } } @@ -274,11 +277,11 @@ acpi_evaluate_object ( * copy the return value to an external object. */ if (return_buffer) { - if (!internal_return_obj) { + if (!info.return_object) { return_buffer->length = 0; } else { - if (ACPI_GET_DESCRIPTOR_TYPE (internal_return_obj) == ACPI_DESC_TYPE_NAMED) { + if (ACPI_GET_DESCRIPTOR_TYPE (info.return_object) == ACPI_DESC_TYPE_NAMED) { /* * If we received a NS Node as a return object, this means that * the object we are evaluating has nothing interesting to @@ -288,7 +291,7 @@ acpi_evaluate_object ( * support for various types at a later date if necessary. */ status = AE_TYPE; - internal_return_obj = NULL; /* No need to delete a NS Node */ + info.return_object = NULL; /* No need to delete a NS Node */ return_buffer->length = 0; } @@ -297,7 +300,7 @@ acpi_evaluate_object ( * Find out how large a buffer is needed * to contain the returned object */ - status = acpi_ut_get_object_size (internal_return_obj, + status = acpi_ut_get_object_size (info.return_object, &buffer_space_needed); if (ACPI_SUCCESS (status)) { /* Validate/Allocate/Clear caller buffer */ @@ -309,13 +312,14 @@ acpi_evaluate_object ( */ ACPI_DEBUG_PRINT ((ACPI_DB_INFO, "Needed buffer size %X, %s\n", - (u32) buffer_space_needed, acpi_format_exception (status))); + (u32) buffer_space_needed, + acpi_format_exception (status))); } else { /* * We have enough space for the object, build it */ - status = acpi_ut_copy_iobject_to_eobject (internal_return_obj, + status = acpi_ut_copy_iobject_to_eobject (info.return_object, return_buffer); } } @@ -323,7 +327,7 @@ acpi_evaluate_object ( } } - if (internal_return_obj) { + if (info.return_object) { /* * Delete the internal return object. NOTE: Interpreter * must be locked to avoid race condition. @@ -334,7 +338,7 @@ acpi_evaluate_object ( * Delete the internal return object. (Or at least * decrement the reference count by one) */ - acpi_ut_remove_reference (internal_return_obj); + acpi_ut_remove_reference (info.return_object); acpi_ex_exit_interpreter (); } } @@ -342,10 +346,10 @@ acpi_evaluate_object ( /* * Free the input parameter list (if we created one), */ - if (internal_params) { + if (info.parameters) { /* Free the allocated parameter block */ - acpi_ut_delete_internal_object_list (internal_params); + acpi_ut_delete_internal_object_list (info.parameters); } return_ACPI_STATUS (status); diff --git a/drivers/acpi/namespace/nsxfname.c b/drivers/acpi/namespace/nsxfname.c index a1acb9f12..0508b4d79 100644 --- a/drivers/acpi/namespace/nsxfname.c +++ b/drivers/acpi/namespace/nsxfname.c @@ -281,7 +281,7 @@ acpi_get_object_info ( if (info.type == ACPI_TYPE_DEVICE) { /* * Get extra info for ACPI Devices objects only: - * Run the Device _HID, _UID, _CID, _STA, and _ADR methods. + * Run the Device _HID, _UID, _CID, _STA, _ADR and _sx_d methods. * * Note: none of these methods are required, so they may or may * not be present for this device. The Info.Valid bitfield is used @@ -330,7 +330,7 @@ acpi_get_object_info ( status = acpi_ut_execute_sxds (node, info.highest_dstates); if (ACPI_SUCCESS (status)) { - info.valid |= ACPI_VALID_STA; + info.valid |= ACPI_VALID_SXDS; } status = AE_OK; diff --git a/drivers/acpi/numa.c b/drivers/acpi/numa.c index 0646b2955..1f63b4d71 100644 --- a/drivers/acpi/numa.c +++ b/drivers/acpi/numa.c @@ -51,6 +51,7 @@ acpi_table_print_srat_entry ( switch (header->type) { case ACPI_SRAT_PROCESSOR_AFFINITY: +#ifdef ACPI_DEBUG_OUTPUT { struct acpi_table_processor_affinity *p = (struct acpi_table_processor_affinity*) header; @@ -58,9 +59,11 @@ acpi_table_print_srat_entry ( p->apic_id, p->lsapic_eid, p->proximity_domain, p->flags.enabled?"enabled":"disabled")); } +#endif /* ACPI_DEBUG_OUTPUT */ break; case ACPI_SRAT_MEMORY_AFFINITY: +#ifdef ACPI_DEBUG_OUTPUT { struct acpi_table_memory_affinity *p = (struct acpi_table_memory_affinity*) header; @@ -70,6 +73,7 @@ acpi_table_print_srat_entry ( p->flags.enabled ? "enabled" : "disabled", p->flags.hot_pluggable ? " hot-pluggable" : "")); } +#endif /* ACPI_DEBUG_OUTPUT */ break; default: @@ -94,8 +98,6 @@ acpi_parse_slit (unsigned long phys_addr, unsigned long size) /* downcast just for %llu vs %lu for i386/ia64 */ localities = (u32) slit->localities; - printk(KERN_INFO PREFIX "SLIT localities %ux%u\n", localities, localities); - acpi_numa_slit_init(slit); return 0; @@ -103,7 +105,9 @@ acpi_parse_slit (unsigned long phys_addr, unsigned long size) static int __init -acpi_parse_processor_affinity (acpi_table_entry_header *header) +acpi_parse_processor_affinity ( + acpi_table_entry_header *header, + const unsigned long end) { struct acpi_table_processor_affinity *processor_affinity; @@ -121,7 +125,9 @@ acpi_parse_processor_affinity (acpi_table_entry_header *header) static int __init -acpi_parse_memory_affinity (acpi_table_entry_header *header) +acpi_parse_memory_affinity ( + acpi_table_entry_header *header, + const unsigned long end) { struct acpi_table_memory_affinity *memory_affinity; @@ -148,8 +154,6 @@ acpi_parse_srat (unsigned long phys_addr, unsigned long size) srat = (struct acpi_table_srat *) __va(phys_addr); - printk(KERN_INFO PREFIX "SRAT revision %d\n", srat->table_revision); - return 0; } diff --git a/drivers/acpi/parser/psopcode.c b/drivers/acpi/parser/psopcode.c index 198e2206b..30620375b 100644 --- a/drivers/acpi/parser/psopcode.c +++ b/drivers/acpi/parser/psopcode.c @@ -251,7 +251,7 @@ #define ARGI_CREATE_FIELD_OP ARGI_LIST4 (ARGI_BUFFER, ARGI_INTEGER, ARGI_INTEGER, ARGI_REFERENCE) #define ARGI_CREATE_QWORD_FIELD_OP ARGI_LIST3 (ARGI_BUFFER, ARGI_INTEGER, ARGI_REFERENCE) #define ARGI_CREATE_WORD_FIELD_OP ARGI_LIST3 (ARGI_BUFFER, ARGI_INTEGER, ARGI_REFERENCE) -#define ARGI_DATA_REGION_OP ARGI_LIST3 (ARGI_STRING, ARGI_STRING, ARGI_STRING) +#define ARGI_DATA_REGION_OP ARGI_LIST3 (ARGI_STRING, ARGI_STRING, ARGI_STRING) #define ARGI_DEBUG_OP ARG_NONE #define ARGI_DECREMENT_OP ARGI_LIST1 (ARGI_INTEGER_REF) #define ARGI_DEREF_OF_OP ARGI_LIST1 (ARGI_REF_OR_STRING) @@ -270,10 +270,10 @@ #define ARGI_INDEX_FIELD_OP ARGI_INVALID_OPCODE #define ARGI_INDEX_OP ARGI_LIST3 (ARGI_COMPLEXOBJ, ARGI_INTEGER, ARGI_TARGETREF) #define ARGI_LAND_OP ARGI_LIST2 (ARGI_INTEGER, ARGI_INTEGER) -#define ARGI_LEQUAL_OP ARGI_LIST2 (ARGI_INTEGER, ARGI_INTEGER) -#define ARGI_LGREATER_OP ARGI_LIST2 (ARGI_INTEGER, ARGI_INTEGER) +#define ARGI_LEQUAL_OP ARGI_LIST2 (ARGI_COMPUTEDATA,ARGI_COMPUTEDATA) +#define ARGI_LGREATER_OP ARGI_LIST2 (ARGI_COMPUTEDATA,ARGI_COMPUTEDATA) #define ARGI_LGREATEREQUAL_OP ARGI_INVALID_OPCODE -#define ARGI_LLESS_OP ARGI_LIST2 (ARGI_INTEGER, ARGI_INTEGER) +#define ARGI_LLESS_OP ARGI_LIST2 (ARGI_COMPUTEDATA,ARGI_COMPUTEDATA) #define ARGI_LLESSEQUAL_OP ARGI_INVALID_OPCODE #define ARGI_LNOT_OP ARGI_LIST1 (ARGI_INTEGER) #define ARGI_LNOTEQUAL_OP ARGI_INVALID_OPCODE diff --git a/drivers/acpi/parser/psxface.c b/drivers/acpi/parser/psxface.c index b7917cfdc..9281f6aec 100644 --- a/drivers/acpi/parser/psxface.c +++ b/drivers/acpi/parser/psxface.c @@ -57,7 +57,7 @@ * * FUNCTION: acpi_psx_execute * - * PARAMETERS: method_node - A method object containing both the AML + * PARAMETERS: Info->Node - A method object containing both the AML * address and length. * **Params - List of parameters to pass to method, * terminated by NULL. Params itself may be @@ -73,9 +73,7 @@ acpi_status acpi_psx_execute ( - struct acpi_namespace_node *method_node, - union acpi_operand_object **params, - union acpi_operand_object **return_obj_desc) + struct acpi_parameter_info *info) { acpi_status status; union acpi_operand_object *obj_desc; @@ -89,29 +87,30 @@ acpi_psx_execute ( /* Validate the Node and get the attached object */ - if (!method_node) { + if (!info || !info->node) { return_ACPI_STATUS (AE_NULL_ENTRY); } - obj_desc = acpi_ns_get_attached_object (method_node); + obj_desc = acpi_ns_get_attached_object (info->node); if (!obj_desc) { return_ACPI_STATUS (AE_NULL_OBJECT); } /* Init for new method, wait on concurrency semaphore */ - status = acpi_ds_begin_method_execution (method_node, obj_desc, NULL); + status = acpi_ds_begin_method_execution (info->node, obj_desc, NULL); if (ACPI_FAILURE (status)) { return_ACPI_STATUS (status); } - if (params) { + if ((info->parameter_type == ACPI_PARAM_ARGS) && + (info->parameters)) { /* * The caller "owns" the parameters, so give each one an extra * reference */ - for (i = 0; params[i]; i++) { - acpi_ut_add_reference (params[i]); + for (i = 0; info->parameters[i]; i++) { + acpi_ut_add_reference (info->parameters[i]); } } @@ -121,7 +120,7 @@ acpi_psx_execute ( */ ACPI_DEBUG_PRINT ((ACPI_DB_PARSE, "**** Begin Method Parse **** Entry=%p obj=%p\n", - method_node, obj_desc)); + info->node, obj_desc)); /* Create and init a Root Node */ @@ -147,8 +146,9 @@ acpi_psx_execute ( goto cleanup2; } - status = acpi_ds_init_aml_walk (walk_state, op, method_node, obj_desc->method.aml_start, - obj_desc->method.aml_length, NULL, NULL, 1); + status = acpi_ds_init_aml_walk (walk_state, op, info->node, + obj_desc->method.aml_start, + obj_desc->method.aml_length, NULL, 1); if (ACPI_FAILURE (status)) { goto cleanup3; } @@ -159,7 +159,6 @@ acpi_psx_execute ( acpi_ps_delete_parse_tree (op); if (ACPI_FAILURE (status)) { goto cleanup1; /* Walk state is already deleted */ - } /* @@ -167,7 +166,7 @@ acpi_psx_execute ( */ ACPI_DEBUG_PRINT ((ACPI_DB_PARSE, "**** Begin Method Execution **** Entry=%p obj=%p\n", - method_node, obj_desc)); + info->node, obj_desc)); /* Create and init a Root Node */ @@ -179,8 +178,8 @@ acpi_psx_execute ( /* Init new op with the method name and pointer back to the NS node */ - acpi_ps_set_name (op, method_node->name.integer); - op->common.node = method_node; + acpi_ps_set_name (op, info->node->name.integer); + op->common.node = info->node; /* Create and initialize a new walk state */ @@ -190,8 +189,9 @@ acpi_psx_execute ( goto cleanup2; } - status = acpi_ds_init_aml_walk (walk_state, op, method_node, obj_desc->method.aml_start, - obj_desc->method.aml_length, params, return_obj_desc, 3); + status = acpi_ds_init_aml_walk (walk_state, op, info->node, + obj_desc->method.aml_start, + obj_desc->method.aml_length, info, 3); if (ACPI_FAILURE (status)) { goto cleanup3; } @@ -210,13 +210,14 @@ cleanup2: acpi_ps_delete_parse_tree (op); cleanup1: - if (params) { + if ((info->parameter_type == ACPI_PARAM_ARGS) && + (info->parameters)) { /* Take away the extra reference that we gave the parameters above */ - for (i = 0; params[i]; i++) { + for (i = 0; info->parameters[i]; i++) { /* Ignore errors, just do them all */ - (void) acpi_ut_update_object_reference (params[i], REF_DECREMENT); + (void) acpi_ut_update_object_reference (info->parameters[i], REF_DECREMENT); } } @@ -228,10 +229,10 @@ cleanup1: * If the method has returned an object, signal this to the caller with * a control exception code */ - if (*return_obj_desc) { + if (info->return_object) { ACPI_DEBUG_PRINT ((ACPI_DB_PARSE, "Method returned obj_desc=%p\n", - *return_obj_desc)); - ACPI_DUMP_STACK_ENTRY (*return_obj_desc); + info->return_object)); + ACPI_DUMP_STACK_ENTRY (info->return_object); status = AE_CTRL_RETURN_VALUE; } diff --git a/drivers/acpi/resources/rsutils.c b/drivers/acpi/resources/rsutils.c index eaf77c635..01dea9b94 100644 --- a/drivers/acpi/resources/rsutils.c +++ b/drivers/acpi/resources/rsutils.c @@ -289,6 +289,7 @@ acpi_rs_set_srs_method_data ( acpi_handle handle, struct acpi_buffer *in_buffer) { + struct acpi_parameter_info info; union acpi_operand_object *params[2]; acpi_status status; struct acpi_buffer buffer; @@ -329,10 +330,14 @@ acpi_rs_set_srs_method_data ( params[0]->common.flags = AOPOBJ_DATA_VALID; params[1] = NULL; + info.node = handle; + info.parameters = params; + info.parameter_type = ACPI_PARAM_ARGS; + /* * Execute the method, no return value */ - status = acpi_ns_evaluate_relative (handle, "_SRS", params, NULL); + status = acpi_ns_evaluate_relative ("_SRS", &info); /* * Clean up and return the status from acpi_ns_evaluate_relative diff --git a/drivers/acpi/resources/rsxface.c b/drivers/acpi/resources/rsxface.c index 362f6252b..d02aa1df3 100644 --- a/drivers/acpi/resources/rsxface.c +++ b/drivers/acpi/resources/rsxface.c @@ -259,7 +259,8 @@ acpi_walk_resources ( /* Setup pointers */ resource = (struct acpi_resource *) buffer.pointer; - buffer_end = (struct acpi_resource *) ((u8 *) buffer.pointer + buffer.length); + buffer_end = ACPI_CAST_PTR (struct acpi_resource, + ((u8 *) buffer.pointer + buffer.length)); /* Walk the resource list */ diff --git a/drivers/acpi/sleep/Makefile b/drivers/acpi/sleep/Makefile index 975d03d59..bb52f85d0 100644 --- a/drivers/acpi/sleep/Makefile +++ b/drivers/acpi/sleep/Makefile @@ -1,5 +1,5 @@ obj-y := poweroff.o -obj-$(CONFIG_ACPI_SLEEP) += main.o +obj-$(CONFIG_ACPI_SLEEP) += main.o wakeup.o obj-$(CONFIG_ACPI_SLEEP_PROC_FS) += proc.o EXTRA_CFLAGS += $(ACPI_CFLAGS) diff --git a/drivers/acpi/sleep/proc.c b/drivers/acpi/sleep/proc.c index 7eb206096..666498e38 100644 --- a/drivers/acpi/sleep/proc.c +++ b/drivers/acpi/sleep/proc.c @@ -15,6 +15,7 @@ #define ACPI_SYSTEM_FILE_SLEEP "sleep" #define ACPI_SYSTEM_FILE_ALARM "alarm" +#define ACPI_SYSTEM_FILE_WAKEUP_DEVICE "wakeup" #define _COMPONENT ACPI_SYSTEM_COMPONENT ACPI_MODULE_NAME ("sleep") @@ -352,6 +353,84 @@ end: return_VALUE(result ? result : count); } +extern struct list_head acpi_wakeup_device_list; +extern spinlock_t acpi_device_lock; + +static int +acpi_system_wakeup_device_seq_show(struct seq_file *seq, void *offset) +{ + struct list_head * node, * next; + + seq_printf(seq, "Device Sleep state Status\n"); + + spin_lock(&acpi_device_lock); + list_for_each_safe(node, next, &acpi_wakeup_device_list) { + struct acpi_device * dev = container_of(node, struct acpi_device, wakeup_list); + + if (!dev->wakeup.flags.valid) + continue; + spin_unlock(&acpi_device_lock); + if (dev->wakeup.flags.run_wake) + seq_printf(seq, "%4s %4d %8s\n", + dev->pnp.bus_id, (u32) dev->wakeup.sleep_state, + dev->wakeup.state.enabled ? "*enabled" : "*disabled"); + else + seq_printf(seq, "%4s %4d %8s\n", + dev->pnp.bus_id, (u32) dev->wakeup.sleep_state, + dev->wakeup.state.enabled ? "enabled" : "disabled"); + spin_lock(&acpi_device_lock); + } + spin_unlock(&acpi_device_lock); + return 0; +} + +static ssize_t +acpi_system_write_wakeup_device ( + struct file *file, + const char __user *buffer, + size_t count, + loff_t *ppos) +{ + struct list_head * node, * next; + char strbuf[5]; + char str[5] = ""; + int len = count; + + if (len > 4) len = 4; + + if (copy_from_user(strbuf, buffer, len)) + return -EFAULT; + strbuf[len] = '\0'; + sscanf(strbuf, "%s", str); + + spin_lock(&acpi_device_lock); + list_for_each_safe(node, next, &acpi_wakeup_device_list) { + struct acpi_device * dev = container_of(node, struct acpi_device, wakeup_list); + if (!dev->wakeup.flags.valid) + continue; + + if (!strncmp(dev->pnp.bus_id, str, 4)) { + dev->wakeup.state.enabled = dev->wakeup.state.enabled ? 0:1; + break; + } + } + spin_unlock(&acpi_device_lock); + return count; +} + +static int +acpi_system_wakeup_device_open_fs(struct inode *inode, struct file *file) +{ + return single_open(file, acpi_system_wakeup_device_seq_show, PDE(inode)->data); +} + +static struct file_operations acpi_system_wakeup_device_fops = { + .open = acpi_system_wakeup_device_open_fs, + .read = seq_read, + .write = acpi_system_write_wakeup_device, + .llseek = seq_lseek, + .release = single_release, +}; static struct file_operations acpi_system_sleep_fops = { .open = acpi_system_sleep_open_fs, @@ -388,6 +467,13 @@ static int acpi_sleep_proc_init(void) S_IFREG|S_IRUGO|S_IWUSR, acpi_root_dir); if (entry) entry->proc_fops = &acpi_system_alarm_fops; + + /* 'wakeup device' [R/W]*/ + entry = create_proc_entry(ACPI_SYSTEM_FILE_WAKEUP_DEVICE, + S_IFREG|S_IRUGO|S_IWUSR, acpi_root_dir); + if (entry) + entry->proc_fops = &acpi_system_wakeup_device_fops; + return 0; } diff --git a/drivers/acpi/sleep/sleep.h b/drivers/acpi/sleep/sleep.h index ad38f4153..cfaf8f5b0 100644 --- a/drivers/acpi/sleep/sleep.h +++ b/drivers/acpi/sleep/sleep.h @@ -2,3 +2,6 @@ extern u8 sleep_states[]; extern int acpi_suspend (u32 state); +extern void acpi_enable_wakeup_device_prep(u8 sleep_state); +extern void acpi_enable_wakeup_device(u8 sleep_state); +extern void acpi_disable_wakeup_device(u8 sleep_state); diff --git a/drivers/acpi/sleep/wakeup.c b/drivers/acpi/sleep/wakeup.c new file mode 100644 index 000000000..9c004b948 --- /dev/null +++ b/drivers/acpi/sleep/wakeup.c @@ -0,0 +1,181 @@ +/* + * wakeup.c - support wakeup devices + */ + +#include +#include +#include +#include +#include +#include +#include "sleep.h" + +#define _COMPONENT ACPI_SYSTEM_COMPONENT +ACPI_MODULE_NAME ("wakeup_devices") + +/** + * acpi_enable_wakeup_device_prep - prepare wakeup devices + * @sleep_state: ACPI state + * Enable all wakup devices power if the devices' wakeup level + * is higher than requested sleep level + */ +extern struct list_head acpi_wakeup_device_list; +extern spinlock_t acpi_device_lock; + +void +acpi_enable_wakeup_device_prep( + u8 sleep_state) +{ + struct list_head * node, * next; + + ACPI_FUNCTION_TRACE("acpi_enable_wakeup_device_prep"); + + spin_lock(&acpi_device_lock); + list_for_each_safe(node, next, &acpi_wakeup_device_list) { + struct acpi_device * dev = container_of(node, + struct acpi_device, wakeup_list); + + if (!dev->wakeup.flags.valid || + !dev->wakeup.state.enabled || + (sleep_state > (u32) dev->wakeup.sleep_state)) + continue; + + spin_unlock(&acpi_device_lock); + acpi_enable_wakeup_device_power(dev); + spin_lock(&acpi_device_lock); + } + spin_unlock(&acpi_device_lock); +} + +/** + * acpi_enable_wakeup_device - enable wakeup devices + * @sleep_state: ACPI state + * Enable all wakup devices's GPE + */ +void +acpi_enable_wakeup_device( + u8 sleep_state) +{ + struct list_head * node, * next; + + /* + * Caution: this routine must be invoked when interrupt is disabled + * Refer ACPI2.0: P212 + */ + ACPI_FUNCTION_TRACE("acpi_enable_wakeup_device"); + spin_lock(&acpi_device_lock); + list_for_each_safe(node, next, &acpi_wakeup_device_list) { + struct acpi_device * dev = container_of(node, + struct acpi_device, wakeup_list); + + /* If users want to disable run-wake GPE, + * we only disable it for wake and leave it for runtime + */ + if (dev->wakeup.flags.run_wake && !dev->wakeup.state.enabled) { + spin_unlock(&acpi_device_lock); + acpi_set_gpe_type(dev->wakeup.gpe_device, + dev->wakeup.gpe_number, ACPI_GPE_TYPE_RUNTIME); + /* Re-enable it, since set_gpe_type will disable it */ + acpi_enable_gpe(dev->wakeup.gpe_device, + dev->wakeup.gpe_number, ACPI_ISR); + spin_lock(&acpi_device_lock); + continue; + } + + if (!dev->wakeup.flags.valid || + !dev->wakeup.state.enabled || + (sleep_state > (u32) dev->wakeup.sleep_state)) + continue; + + spin_unlock(&acpi_device_lock); + /* run-wake GPE has been enabled */ + if (!dev->wakeup.flags.run_wake) + acpi_enable_gpe(dev->wakeup.gpe_device, + dev->wakeup.gpe_number, ACPI_ISR); + dev->wakeup.state.active = 1; + spin_lock(&acpi_device_lock); + } + spin_unlock(&acpi_device_lock); +} + +/** + * acpi_disable_wakeup_device - disable devices' wakeup capability + * @sleep_state: ACPI state + * Disable all wakup devices's GPE and wakeup capability + */ +void +acpi_disable_wakeup_device ( + u8 sleep_state) +{ + struct list_head * node, * next; + + ACPI_FUNCTION_TRACE("acpi_disable_wakeup_device"); + + spin_lock(&acpi_device_lock); + list_for_each_safe(node, next, &acpi_wakeup_device_list) { + struct acpi_device * dev = container_of(node, + struct acpi_device, wakeup_list); + + if (dev->wakeup.flags.run_wake && !dev->wakeup.state.enabled) { + spin_unlock(&acpi_device_lock); + acpi_set_gpe_type(dev->wakeup.gpe_device, + dev->wakeup.gpe_number, ACPI_GPE_TYPE_WAKE_RUN); + /* Re-enable it, since set_gpe_type will disable it */ + acpi_enable_gpe(dev->wakeup.gpe_device, + dev->wakeup.gpe_number, ACPI_NOT_ISR); + spin_lock(&acpi_device_lock); + continue; + } + + if (!dev->wakeup.flags.valid || + !dev->wakeup.state.active || + (sleep_state > (u32) dev->wakeup.sleep_state)) + continue; + + spin_unlock(&acpi_device_lock); + acpi_disable_wakeup_device_power(dev); + /* Never disable run-wake GPE */ + if (!dev->wakeup.flags.run_wake) { + acpi_disable_gpe(dev->wakeup.gpe_device, + dev->wakeup.gpe_number, ACPI_NOT_ISR); + acpi_clear_gpe(dev->wakeup.gpe_device, + dev->wakeup.gpe_number, ACPI_NOT_ISR); + } + dev->wakeup.state.active = 0; + spin_lock(&acpi_device_lock); + } + spin_unlock(&acpi_device_lock); +} + +static int __init acpi_wakeup_device_init(void) +{ + struct list_head * node, * next; + + if (acpi_disabled) + return 0; + printk("ACPI wakeup devices: \n"); + + spin_lock(&acpi_device_lock); + list_for_each_safe(node, next, &acpi_wakeup_device_list) { + struct acpi_device * dev = container_of(node, + struct acpi_device, wakeup_list); + + /* In case user doesn't load button driver */ + if (dev->wakeup.flags.run_wake && !dev->wakeup.state.enabled) { + spin_unlock(&acpi_device_lock); + acpi_set_gpe_type(dev->wakeup.gpe_device, + dev->wakeup.gpe_number, ACPI_GPE_TYPE_WAKE_RUN); + acpi_enable_gpe(dev->wakeup.gpe_device, + dev->wakeup.gpe_number, ACPI_NOT_ISR); + dev->wakeup.state.enabled = 1; + spin_lock(&acpi_device_lock); + } + printk("%4s ", dev->pnp.bus_id); + } + spin_unlock(&acpi_device_lock); + printk("\n"); + + return 0; +} + +late_initcall(acpi_wakeup_device_init); diff --git a/drivers/acpi/tables/tbxfroot.c b/drivers/acpi/tables/tbxfroot.c index b7328345d..4a9669590 100644 --- a/drivers/acpi/tables/tbxfroot.c +++ b/drivers/acpi/tables/tbxfroot.c @@ -115,17 +115,14 @@ acpi_tb_find_table ( * Instance - the non zero instance of the table, allows * support for multiple tables of the same type * Flags - Physical/Virtual support - * ret_buffer - pointer to a structure containing a buffer to - * receive the table + * table_pointer - Where a buffer containing the table is + * returned * * RETURN: Status * - * DESCRIPTION: This function is called to get an ACPI table. The caller - * supplies an out_buffer large enough to contain the entire ACPI - * table. Upon completion - * the out_buffer->Length field will indicate the number of bytes - * copied into the out_buffer->buf_ptr buffer. This table will be - * a complete table including the header. + * DESCRIPTION: This function is called to get an ACPI table. A buffer is + * allocated for the table and returned in table_pointer. + * This table will be a complete table including the header. * ******************************************************************************/ @@ -136,12 +133,11 @@ acpi_get_firmware_table ( u32 flags, struct acpi_table_header **table_pointer) { - struct acpi_pointer rsdp_address; - struct acpi_pointer address; acpi_status status; - struct acpi_table_header header; - struct acpi_table_desc table_info; - struct acpi_table_desc rsdt_info; + struct acpi_pointer address; + struct acpi_table_header *header = NULL; + struct acpi_table_desc *table_info = NULL; + struct acpi_table_desc *rsdt_info; u32 table_count; u32 i; u32 j; @@ -152,45 +148,41 @@ acpi_get_firmware_table ( /* * Ensure that at least the table manager is initialized. We don't - * require that the entire ACPI subsystem is up for this interface - */ - - /* - * If we have a buffer, we must have a length too + * require that the entire ACPI subsystem is up for this interface. + * If we have a buffer, we must have a length too */ - if ((instance == 0) || - (!signature) || + if ((instance == 0) || + (!signature) || (!table_pointer)) { return_ACPI_STATUS (AE_BAD_PARAMETER); } - rsdt_info.pointer = NULL; + /* Ensure that we have a RSDP */ if (!acpi_gbl_RSDP) { /* Get the RSDP */ - status = acpi_os_get_root_pointer (flags, &rsdp_address); + status = acpi_os_get_root_pointer (flags, &address); if (ACPI_FAILURE (status)) { - ACPI_DEBUG_PRINT ((ACPI_DB_INFO, "RSDP not found\n")); + ACPI_DEBUG_PRINT ((ACPI_DB_INFO, "RSDP not found\n")); return_ACPI_STATUS (AE_NO_ACPI_TABLES); } /* Map and validate the RSDP */ if ((flags & ACPI_MEMORY_MODE) == ACPI_LOGICAL_ADDRESSING) { - status = acpi_os_map_memory (rsdp_address.pointer.physical, sizeof (struct rsdp_descriptor), + status = acpi_os_map_memory (address.pointer.physical, sizeof (struct rsdp_descriptor), (void *) &acpi_gbl_RSDP); if (ACPI_FAILURE (status)) { return_ACPI_STATUS (status); } } else { - acpi_gbl_RSDP = rsdp_address.pointer.logical; + acpi_gbl_RSDP = address.pointer.logical; } - /* - * The signature and checksum must both be correct - */ + /* The signature and checksum must both be correct */ + if (ACPI_STRNCMP ((char *) acpi_gbl_RSDP, RSDP_SIG, sizeof (RSDP_SIG)-1) != 0) { /* Nope, BAD Signature */ @@ -204,10 +196,9 @@ acpi_get_firmware_table ( } } - /* Get the RSDT and validate it */ + /* Get the RSDT address via the RSDP */ acpi_tb_get_rsdt_address (&address); - ACPI_DEBUG_PRINT ((ACPI_DB_INFO, "RSDP located at %p, RSDT physical=%8.8X%8.8X \n", acpi_gbl_RSDP, @@ -217,20 +208,40 @@ acpi_get_firmware_table ( address.pointer_type |= flags; - status = acpi_tb_get_table (&address, &rsdt_info); + /* Get and validate the RSDT */ + + rsdt_info = ACPI_MEM_CALLOCATE (sizeof (struct acpi_table_desc)); + if (!rsdt_info) { + return_ACPI_STATUS (AE_NO_MEMORY); + } + + status = acpi_tb_get_table (&address, rsdt_info); if (ACPI_FAILURE (status)) { - return_ACPI_STATUS (status); + goto cleanup; } - status = acpi_tb_validate_rsdt (rsdt_info.pointer); + status = acpi_tb_validate_rsdt (rsdt_info->pointer); if (ACPI_FAILURE (status)) { goto cleanup; } - /* Get the number of table pointers within the RSDT */ + /* Allocate a scratch table header and table descriptor */ + + header = ACPI_MEM_ALLOCATE (sizeof (struct acpi_table_header)); + if (!header) { + status = AE_NO_MEMORY; + goto cleanup; + } + + table_info = ACPI_MEM_ALLOCATE (sizeof (struct acpi_table_desc)); + if (!table_info) { + status = AE_NO_MEMORY; + goto cleanup; + } - table_count = acpi_tb_get_table_count (acpi_gbl_RSDP, rsdt_info.pointer); + /* Get the number of table pointers within the RSDT */ + table_count = acpi_tb_get_table_count (acpi_gbl_RSDP, rsdt_info->pointer); address.pointer_type = acpi_gbl_table_flags | flags; /* @@ -241,35 +252,36 @@ acpi_get_firmware_table ( /* Get the next table pointer, handle RSDT vs. XSDT */ if (acpi_gbl_RSDP->revision < 2) { - address.pointer.value = (ACPI_CAST_PTR (RSDT_DESCRIPTOR, rsdt_info.pointer))->table_offset_entry[i]; + address.pointer.value = (ACPI_CAST_PTR ( + RSDT_DESCRIPTOR, rsdt_info->pointer))->table_offset_entry[i]; } else { - address.pointer.value = - (ACPI_CAST_PTR (XSDT_DESCRIPTOR, rsdt_info.pointer))->table_offset_entry[i]; + address.pointer.value = (ACPI_CAST_PTR ( + XSDT_DESCRIPTOR, rsdt_info->pointer))->table_offset_entry[i]; } /* Get the table header */ - status = acpi_tb_get_table_header (&address, &header); + status = acpi_tb_get_table_header (&address, header); if (ACPI_FAILURE (status)) { goto cleanup; } /* Compare table signatures and table instance */ - if (!ACPI_STRNCMP (header.signature, signature, ACPI_NAME_SIZE)) { + if (!ACPI_STRNCMP (header->signature, signature, ACPI_NAME_SIZE)) { /* An instance of the table was found */ j++; if (j >= instance) { /* Found the correct instance, get the entire table */ - status = acpi_tb_get_table_body (&address, &header, &table_info); + status = acpi_tb_get_table_body (&address, header, table_info); if (ACPI_FAILURE (status)) { goto cleanup; } - *table_pointer = table_info.pointer; + *table_pointer = table_info->pointer; goto cleanup; } } @@ -281,7 +293,15 @@ acpi_get_firmware_table ( cleanup: - acpi_os_unmap_memory (rsdt_info.pointer, (acpi_size) rsdt_info.pointer->length); + acpi_os_unmap_memory (rsdt_info->pointer, (acpi_size) rsdt_info->pointer->length); + ACPI_MEM_FREE (rsdt_info); + + if (header) { + ACPI_MEM_FREE (header); + } + if (table_info) { + ACPI_MEM_FREE (table_info); + } return_ACPI_STATUS (status); } @@ -389,14 +409,17 @@ acpi_tb_scan_memory_for_rsdp ( * Flags - Current memory mode (logical vs. * physical addressing) * - * RETURN: Status + * RETURN: Status, RSDP physical address * * DESCRIPTION: search lower 1_mbyte of memory for the root system descriptor * pointer structure. If it is found, set *RSDP to point to it. * - * NOTE: The RSDp must be either in the first 1_k of the Extended - * BIOS Data Area or between E0000 and FFFFF (ACPI 1.0 section - * 5.2.2; assertion #421). + * NOTE1: The RSDp must be either in the first 1_k of the Extended + * BIOS Data Area or between E0000 and FFFFF (From ACPI Spec.) + * Only a 32-bit physical address is necessary. + * + * NOTE2: This function is always available, regardless of the + * initialization state of the rest of ACPI. * ******************************************************************************/ @@ -407,8 +430,8 @@ acpi_tb_find_rsdp ( { u8 *table_ptr; u8 *mem_rover; - u64 phys_addr; - acpi_status status = AE_OK; + u32 physical_address; + acpi_status status; ACPI_FUNCTION_TRACE ("tb_find_rsdp"); @@ -419,36 +442,57 @@ acpi_tb_find_rsdp ( */ if ((flags & ACPI_MEMORY_MODE) == ACPI_LOGICAL_ADDRESSING) { /* - * 1) Search EBDA (low memory) paragraphs + * 1a) Get the location of the EBDA */ - status = acpi_os_map_memory ((u64) ACPI_LO_RSDP_WINDOW_BASE, ACPI_LO_RSDP_WINDOW_SIZE, + status = acpi_os_map_memory ((acpi_physical_address) ACPI_EBDA_PTR_LOCATION, + ACPI_EBDA_PTR_LENGTH, (void *) &table_ptr); if (ACPI_FAILURE (status)) { - ACPI_DEBUG_PRINT ((ACPI_DB_ERROR, "Could not map memory at %X for length %X\n", - ACPI_LO_RSDP_WINDOW_BASE, ACPI_LO_RSDP_WINDOW_SIZE)); + ACPI_DEBUG_PRINT ((ACPI_DB_ERROR, "Could not map memory at %8.8X for length %X\n", + ACPI_EBDA_PTR_LOCATION, ACPI_EBDA_PTR_LENGTH)); return_ACPI_STATUS (status); } - mem_rover = acpi_tb_scan_memory_for_rsdp (table_ptr, ACPI_LO_RSDP_WINDOW_SIZE); - acpi_os_unmap_memory (table_ptr, ACPI_LO_RSDP_WINDOW_SIZE); + ACPI_MOVE_16_TO_32 (&physical_address, table_ptr); + physical_address <<= 4; /* Convert segment to physical address */ + acpi_os_unmap_memory (table_ptr, ACPI_EBDA_PTR_LENGTH); - if (mem_rover) { - /* Found it, return the physical address */ + /* EBDA present? */ - phys_addr = ACPI_LO_RSDP_WINDOW_BASE; - phys_addr += ACPI_PTR_DIFF (mem_rover,table_ptr); + if (physical_address > 0x400) { + /* + * 1b) Search EBDA paragraphs (EBDa is required to be a minimum of 1_k length) + */ + status = acpi_os_map_memory ((acpi_physical_address) physical_address, + ACPI_EBDA_WINDOW_SIZE, + (void *) &table_ptr); + if (ACPI_FAILURE (status)) { + ACPI_DEBUG_PRINT ((ACPI_DB_ERROR, "Could not map memory at %8.8X for length %X\n", + physical_address, ACPI_EBDA_WINDOW_SIZE)); + return_ACPI_STATUS (status); + } - table_info->physical_address = phys_addr; - return_ACPI_STATUS (AE_OK); + mem_rover = acpi_tb_scan_memory_for_rsdp (table_ptr, ACPI_EBDA_WINDOW_SIZE); + acpi_os_unmap_memory (table_ptr, ACPI_EBDA_WINDOW_SIZE); + + if (mem_rover) { + /* Found it, return the physical address */ + + physical_address += ACPI_PTR_DIFF (mem_rover, table_ptr); + + table_info->physical_address = (acpi_physical_address) physical_address; + return_ACPI_STATUS (AE_OK); + } } /* - * 2) Search upper memory: 16-byte boundaries in E0000h-F0000h + * 2) Search upper memory: 16-byte boundaries in E0000h-FFFFFh */ - status = acpi_os_map_memory ((u64) ACPI_HI_RSDP_WINDOW_BASE, ACPI_HI_RSDP_WINDOW_SIZE, + status = acpi_os_map_memory ((acpi_physical_address) ACPI_HI_RSDP_WINDOW_BASE, + ACPI_HI_RSDP_WINDOW_SIZE, (void *) &table_ptr); if (ACPI_FAILURE (status)) { - ACPI_DEBUG_PRINT ((ACPI_DB_ERROR, "Could not map memory at %X for length %X\n", + ACPI_DEBUG_PRINT ((ACPI_DB_ERROR, "Could not map memory at %8.8X for length %X\n", ACPI_HI_RSDP_WINDOW_BASE, ACPI_HI_RSDP_WINDOW_SIZE)); return_ACPI_STATUS (status); } @@ -459,10 +503,9 @@ acpi_tb_find_rsdp ( if (mem_rover) { /* Found it, return the physical address */ - phys_addr = ACPI_HI_RSDP_WINDOW_BASE; - phys_addr += ACPI_PTR_DIFF (mem_rover, table_ptr); + physical_address = ACPI_HI_RSDP_WINDOW_BASE + ACPI_PTR_DIFF (mem_rover, table_ptr); - table_info->physical_address = phys_addr; + table_info->physical_address = (acpi_physical_address) physical_address; return_ACPI_STATUS (AE_OK); } } @@ -472,19 +515,29 @@ acpi_tb_find_rsdp ( */ else { /* - * 1) Search EBDA (low memory) paragraphs + * 1a) Get the location of the EBDA */ - mem_rover = acpi_tb_scan_memory_for_rsdp (ACPI_PHYSADDR_TO_PTR (ACPI_LO_RSDP_WINDOW_BASE), - ACPI_LO_RSDP_WINDOW_SIZE); - if (mem_rover) { - /* Found it, return the physical address */ - - table_info->physical_address = ACPI_TO_INTEGER (mem_rover); - return_ACPI_STATUS (AE_OK); + ACPI_MOVE_16_TO_32 (&physical_address, ACPI_EBDA_PTR_LOCATION); + physical_address <<= 4; /* Convert segment to physical address */ + + /* EBDA present? */ + + if (physical_address > 0x400) { + /* + * 1b) Search EBDA paragraphs (EBDa is required to be a minimum of 1_k length) + */ + mem_rover = acpi_tb_scan_memory_for_rsdp (ACPI_PHYSADDR_TO_PTR (physical_address), + ACPI_EBDA_WINDOW_SIZE); + if (mem_rover) { + /* Found it, return the physical address */ + + table_info->physical_address = ACPI_TO_INTEGER (mem_rover); + return_ACPI_STATUS (AE_OK); + } } /* - * 2) Search upper memory: 16-byte boundaries in E0000h-F0000h + * 2) Search upper memory: 16-byte boundaries in E0000h-FFFFFh */ mem_rover = acpi_tb_scan_memory_for_rsdp (ACPI_PHYSADDR_TO_PTR (ACPI_HI_RSDP_WINDOW_BASE), ACPI_HI_RSDP_WINDOW_SIZE); diff --git a/drivers/acpi/utilities/utalloc.c b/drivers/acpi/utilities/utalloc.c index 2050d238d..58ec6733e 100644 --- a/drivers/acpi/utilities/utalloc.c +++ b/drivers/acpi/utilities/utalloc.c @@ -259,8 +259,8 @@ acpi_ut_validate_buffer ( * * FUNCTION: acpi_ut_initialize_buffer * - * PARAMETERS: required_length - Length needed - * Buffer - Buffer to be validated + * PARAMETERS: Buffer - Buffer to be validated + * required_length - Length needed * * RETURN: Status * @@ -603,7 +603,8 @@ acpi_ut_free_and_track ( * * FUNCTION: acpi_ut_find_allocation * - * PARAMETERS: Allocation - Address of allocated memory + * PARAMETERS: list_id - Memory list to search + * Allocation - Address of allocated memory * * RETURN: A list element if found; NULL otherwise. * @@ -646,7 +647,8 @@ acpi_ut_find_allocation ( * * FUNCTION: acpi_ut_track_allocation * - * PARAMETERS: Allocation - Address of allocated memory + * PARAMETERS: list_id - Memory list to search + * Allocation - Address of allocated memory * Size - Size of the allocation * alloc_type - MEM_MALLOC or MEM_CALLOC * Component - Component type of caller @@ -733,7 +735,8 @@ unlock_and_exit: * * FUNCTION: acpi_ut_remove_allocation * - * PARAMETERS: Allocation - Address of allocated memory + * PARAMETERS: list_id - Memory list to search + * Allocation - Address of allocated memory * Component - Component type of caller * Module - Source file name of caller * Line - Line number of caller diff --git a/drivers/acpi/utilities/uteval.c b/drivers/acpi/utilities/uteval.c index c3f5900b6..0aa1fde7e 100644 --- a/drivers/acpi/utilities/uteval.c +++ b/drivers/acpi/utilities/uteval.c @@ -133,7 +133,7 @@ acpi_ut_evaluate_object ( u32 expected_return_btypes, union acpi_operand_object **return_desc) { - union acpi_operand_object *obj_desc; + struct acpi_parameter_info info; acpi_status status; u32 return_btype; @@ -141,9 +141,13 @@ acpi_ut_evaluate_object ( ACPI_FUNCTION_TRACE ("ut_evaluate_object"); + info.node = prefix_node; + info.parameters = NULL; + info.parameter_type = ACPI_PARAM_ARGS; + /* Evaluate the object/method */ - status = acpi_ns_evaluate_relative (prefix_node, path, NULL, &obj_desc); + status = acpi_ns_evaluate_relative (path, &info); if (ACPI_FAILURE (status)) { if (status == AE_NOT_FOUND) { ACPI_DEBUG_PRINT ((ACPI_DB_EXEC, "[%4.4s.%s] was not found\n", @@ -159,7 +163,7 @@ acpi_ut_evaluate_object ( /* Did we get a return object? */ - if (!obj_desc) { + if (!info.return_object) { if (expected_return_btypes) { ACPI_REPORT_METHOD_ERROR ("No object was returned from", prefix_node, path, AE_NOT_EXIST); @@ -172,7 +176,7 @@ acpi_ut_evaluate_object ( /* Map the return object type to the bitmapped type */ - switch (ACPI_GET_OBJECT_TYPE (obj_desc)) { + switch (ACPI_GET_OBJECT_TYPE (info.return_object)) { case ACPI_TYPE_INTEGER: return_btype = ACPI_BTYPE_INTEGER; break; @@ -202,17 +206,17 @@ acpi_ut_evaluate_object ( ACPI_DEBUG_PRINT ((ACPI_DB_ERROR, "Type returned from %s was incorrect: %X\n", - path, ACPI_GET_OBJECT_TYPE (obj_desc))); + path, ACPI_GET_OBJECT_TYPE (info.return_object))); /* On error exit, we must delete the return object */ - acpi_ut_remove_reference (obj_desc); + acpi_ut_remove_reference (info.return_object); return_ACPI_STATUS (AE_TYPE); } /* Object type is OK, return it */ - *return_desc = obj_desc; + *return_desc = info.return_object; return_ACPI_STATUS (AE_OK); } diff --git a/drivers/acpi/utilities/utglobal.c b/drivers/acpi/utilities/utglobal.c index e1d19936e..f42848ab7 100644 --- a/drivers/acpi/utilities/utglobal.c +++ b/drivers/acpi/utilities/utglobal.c @@ -142,16 +142,11 @@ unknown: */ /* Debug switch - level and trace mask */ - -#ifdef ACPI_DEBUG_OUTPUT -u32 acpi_dbg_level = ACPI_DEBUG_DEFAULT; -#else -u32 acpi_dbg_level = ACPI_NORMAL_DEFAULT; -#endif +u32 acpi_dbg_level = 0; /* Debug switch - layer (component) mask */ -u32 acpi_dbg_layer = ACPI_COMPONENT_DEFAULT; +u32 acpi_dbg_layer = 0; u32 acpi_gbl_nesting_level = 0; @@ -171,27 +166,40 @@ u8 acpi_gbl_shutdown = TRUE; const u8 acpi_gbl_decode_to8bit [8] = {1,2,4,8,16,32,64,128}; -const char *acpi_gbl_sleep_state_names[ACPI_S_STATE_COUNT] = { - "\\_S0_", - "\\_S1_", - "\\_S2_", - "\\_S3_", - "\\_S4_", - "\\_S5_"}; - -const char *acpi_gbl_highest_dstate_names[4] = { - "_S1D", - "_S2D", - "_S3D", - "_S4D"}; +const char *acpi_gbl_sleep_state_names[ACPI_S_STATE_COUNT] = +{ + "\\_S0_", + "\\_S1_", + "\\_S2_", + "\\_S3_", + "\\_S4_", + "\\_S5_" +}; -/* Strings supported by the _OSI predefined (internal) method */ +const char *acpi_gbl_highest_dstate_names[4] = +{ + "_S1D", + "_S2D", + "_S3D", + "_S4D" +}; -const char *acpi_gbl_valid_osi_strings[ACPI_NUM_OSI_STRINGS] = { - "Linux", - "Windows 2000", - "Windows 2001", - "Windows 2001.1"}; +/* + * Strings supported by the _OSI predefined (internal) method. + * When adding strings, be sure to update ACPI_NUM_OSI_STRINGS. + */ +const char *acpi_gbl_valid_osi_strings[ACPI_NUM_OSI_STRINGS] = +{ + "Linux", + "Windows 2000", + "Windows 2001", + "Windows 2001.1", + "Windows 2001 SP0", + "Windows 2001 SP1", + "Windows 2001 SP2", + "Windows 2001 SP3", + "Windows 2001 SP4" +}; /****************************************************************************** @@ -213,7 +221,7 @@ const struct acpi_predefined_names acpi_gbl_pre_defined_names[] = {"_PR_", ACPI_TYPE_LOCAL_SCOPE, NULL}, {"_SB_", ACPI_TYPE_DEVICE, NULL}, {"_SI_", ACPI_TYPE_LOCAL_SCOPE, NULL}, - {"_TZ_", ACPI_TYPE_LOCAL_SCOPE, NULL}, + {"_TZ_", ACPI_TYPE_THERMAL, NULL}, {"_REV", ACPI_TYPE_INTEGER, "2"}, {"_OS_", ACPI_TYPE_STRING, ACPI_OS_NAME}, {"_GL_", ACPI_TYPE_MUTEX, "0"}, @@ -561,26 +569,37 @@ acpi_ut_get_node_name ( struct acpi_namespace_node *node = (struct acpi_namespace_node *) object; + /* Must return a string of exactly 4 characters == ACPI_NAME_SIZE */ + if (!object) { - return ("NULL NODE"); + return ("NULL"); } - if (object == ACPI_ROOT_OBJECT) + /* Check for Root node */ + + if ((object == ACPI_ROOT_OBJECT) || + (object == acpi_gbl_root_node)) { - node = acpi_gbl_root_node; + return ("\"\\\" "); } + /* Descriptor must be a namespace node */ + if (node->descriptor != ACPI_DESC_TYPE_NAMED) { - return ("****"); + return ("####"); } + /* Name must be a valid ACPI name */ + if (!acpi_ut_valid_acpi_name (* (u32 *) node->name.ascii)) { - return ("----"); + return ("????"); } + /* Return the name */ + return (node->name.ascii); } @@ -783,10 +802,6 @@ acpi_ut_init_globals ( ACPI_FUNCTION_TRACE ("ut_init_globals"); - /* Runtime configuration */ - - acpi_gbl_create_osi_method = TRUE; - acpi_gbl_all_methods_serialized = FALSE; /* Memory allocation and cache lists */ @@ -880,6 +895,7 @@ acpi_ut_init_globals ( /* Hardware oriented */ acpi_gbl_events_initialized = FALSE; + acpi_gbl_system_awake_and_running = TRUE; /* Namespace */ diff --git a/drivers/acpi/utilities/utxface.c b/drivers/acpi/utilities/utxface.c index 5cbd93eef..45c14f9ef 100644 --- a/drivers/acpi/utilities/utxface.c +++ b/drivers/acpi/utilities/utxface.c @@ -157,9 +157,8 @@ acpi_enable_subsystem ( } } - /* - * Enable ACPI mode - */ + /* Enable ACPI mode */ + if (!(flags & ACPI_NO_ACPI_ENABLE)) { ACPI_DEBUG_PRINT ((ACPI_DB_EXEC, "[Init] Going into ACPI mode\n")); @@ -173,7 +172,21 @@ acpi_enable_subsystem ( } /* - * Initialize ACPI Event handling + * Install the default op_region handlers. These are installed unless + * other handlers have already been installed via the + * install_address_space_handler interface. + */ + if (!(flags & ACPI_NO_ADDRESS_SPACE_INIT)) { + ACPI_DEBUG_PRINT ((ACPI_DB_EXEC, "[Init] Installing default address space handlers\n")); + + status = acpi_ev_install_region_handlers (); + if (ACPI_FAILURE (status)) { + return_ACPI_STATUS (status); + } + } + + /* + * Initialize ACPI Event handling (Fixed and General Purpose) * * NOTE: We must have the hardware AND events initialized before we can execute * ANY control methods SAFELY. Any control method can require ACPI hardware @@ -182,18 +195,18 @@ acpi_enable_subsystem ( if (!(flags & ACPI_NO_EVENT_INIT)) { ACPI_DEBUG_PRINT ((ACPI_DB_EXEC, "[Init] Initializing ACPI events\n")); - status = acpi_ev_initialize (); + status = acpi_ev_initialize_events (); if (ACPI_FAILURE (status)) { return_ACPI_STATUS (status); } } - /* Install the SCI handler, Global Lock handler, and GPE handlers */ + /* Install the SCI handler and Global Lock handler */ if (!(flags & ACPI_NO_HANDLER_INIT)) { - ACPI_DEBUG_PRINT ((ACPI_DB_EXEC, "[Init] Installing SCI/GL/GPE handlers\n")); + ACPI_DEBUG_PRINT ((ACPI_DB_EXEC, "[Init] Installing SCI/GL handlers\n")); - status = acpi_ev_handler_initialize (); + status = acpi_ev_install_xrupt_handlers (); if (ACPI_FAILURE (status)) { return_ACPI_STATUS (status); } @@ -226,18 +239,16 @@ acpi_initialize_objects ( /* - * Install the default op_region handlers. These are installed unless - * other handlers have already been installed via the - * install_address_space_handler interface. + * Run all _REG methods * - * NOTE: This will cause _REG methods to be run. Any objects accessed + * NOTE: Any objects accessed * by the _REG methods will be automatically initialized, even if they * contain executable AML (see call to acpi_ns_initialize_objects below). */ if (!(flags & ACPI_NO_ADDRESS_SPACE_INIT)) { - ACPI_DEBUG_PRINT ((ACPI_DB_EXEC, "[Init] Installing default address space handlers\n")); + ACPI_DEBUG_PRINT ((ACPI_DB_EXEC, "[Init] Executing _REG op_region methods\n")); - status = acpi_ev_init_address_spaces (); + status = acpi_ev_initialize_op_regions (); if (ACPI_FAILURE (status)) { return_ACPI_STATUS (status); } @@ -249,7 +260,7 @@ acpi_initialize_objects ( * objects: operation_regions, buffer_fields, Buffers, and Packages. */ if (!(flags & ACPI_NO_OBJECT_INIT)) { - ACPI_DEBUG_PRINT ((ACPI_DB_EXEC, "[Init] Initializing ACPI Objects\n")); + ACPI_DEBUG_PRINT ((ACPI_DB_EXEC, "[Init] Completing Initialization of ACPI Objects\n")); status = acpi_ns_initialize_objects (); if (ACPI_FAILURE (status)) { diff --git a/drivers/atm/Makefile b/drivers/atm/Makefile index bcd60fb8d..907c5933a 100644 --- a/drivers/atm/Makefile +++ b/drivers/atm/Makefile @@ -3,7 +3,7 @@ # fore_200e-objs := fore200e.o -host-progs := fore200e_mkfirm +hostprogs-y := fore200e_mkfirm # Files generated that shall be removed upon make clean clean-files := atmsar11.bin atmsar11.bin1 atmsar11.bin2 pca200e.bin \ diff --git a/drivers/atm/eni.c b/drivers/atm/eni.c index 8b137034d..81127d178 100644 --- a/drivers/atm/eni.c +++ b/drivers/atm/eni.c @@ -173,7 +173,7 @@ static void dump_mem(struct eni_dev *eni_dev) int i; for (i = 0; i < eni_dev->free_len; i++) - printk(KERN_DEBUG " %d: 0x%lx %d\n",i, + printk(KERN_DEBUG " %d: %p %d\n",i, eni_dev->free_list[i].start, 1 << eni_dev->free_list[i].order); } @@ -191,19 +191,19 @@ static void dump(struct atm_dev *dev) printk(KERN_NOTICE "TX buffers\n"); for (i = 0; i < NR_CHAN; i++) if (eni_dev->tx[i].send) - printk(KERN_NOTICE " TX %d @ 0x%lx: %ld\n",i, + printk(KERN_NOTICE " TX %d @ %p: %ld\n",i, eni_dev->tx[i].send,eni_dev->tx[i].words*4); printk(KERN_NOTICE "RX buffers\n"); for (i = 0; i < 1024; i++) if (eni_dev->rx_map[i] && ENI_VCC(eni_dev->rx_map[i])->rx) - printk(KERN_NOTICE " RX %d @ 0x%lx: %ld\n",i, + printk(KERN_NOTICE " RX %d @ %p: %ld\n",i, ENI_VCC(eni_dev->rx_map[i])->recv, ENI_VCC(eni_dev->rx_map[i])->words*4); printk(KERN_NOTICE "----\n"); } -static void eni_put_free(struct eni_dev *eni_dev,unsigned long start, +static void eni_put_free(struct eni_dev *eni_dev, void __iomem *start, unsigned long size) { struct eni_free *list; @@ -215,17 +215,17 @@ static void eni_put_free(struct eni_dev *eni_dev,unsigned long start, len = eni_dev->free_len; while (size) { if (len >= eni_dev->free_list_size) { - printk(KERN_CRIT "eni_put_free overflow (0x%lx,%ld)\n", + printk(KERN_CRIT "eni_put_free overflow (%p,%ld)\n", start,size); break; } - for (order = 0; !((start | size) & (1 << order)); order++); + for (order = 0; !(((unsigned long)start | size) & (1 << order)); order++); if (MID_MIN_BUF_SIZE > (1 << order)) { printk(KERN_CRIT "eni_put_free: order %d too small\n", order); break; } - list[len].start = start; + list[len].start = (void __iomem *) start; list[len].order = order; len++; start += 1 << order; @@ -236,10 +236,10 @@ static void eni_put_free(struct eni_dev *eni_dev,unsigned long start, } -static unsigned long eni_alloc_mem(struct eni_dev *eni_dev,unsigned long *size) +static void __iomem *eni_alloc_mem(struct eni_dev *eni_dev, unsigned long *size) { struct eni_free *list; - unsigned long start; + void __iomem *start; int len,i,order,best_order,index; list = eni_dev->free_list; @@ -273,7 +273,7 @@ static unsigned long eni_alloc_mem(struct eni_dev *eni_dev,unsigned long *size) } -static void eni_free_mem(struct eni_dev *eni_dev,unsigned long start, +static void eni_free_mem(struct eni_dev *eni_dev, void __iomem *start, unsigned long size) { struct eni_free *list; @@ -283,20 +283,20 @@ static void eni_free_mem(struct eni_dev *eni_dev,unsigned long start, list = eni_dev->free_list; len = eni_dev->free_len; for (order = -1; size; order++) size >>= 1; - DPRINTK("eni_free_mem: 0x%lx+0x%lx (order %d)\n",start,size,order); + DPRINTK("eni_free_mem: %p+0x%lx (order %d)\n",start,size,order); for (i = 0; i < len; i++) - if (list[i].start == (start^(1 << order)) && + if (((unsigned long) list[i].start) == ((unsigned long)start^(1 << order)) && list[i].order == order) { DPRINTK("match[%d]: 0x%lx/0x%lx(0x%x), %d/%d\n",i, list[i].start,start,1 << order,list[i].order,order); list[i] = list[--len]; - start &= ~(unsigned long) (1 << order); + start = (void __iomem *) ((unsigned long) start & ~(unsigned long) (1 << order)); order++; i = -1; continue; } if (len >= eni_dev->free_list_size) { - printk(KERN_ALERT "eni_free_mem overflow (0x%lx,%d)\n",start, + printk(KERN_ALERT "eni_free_mem overflow (%p,%d)\n",start, order); return; } @@ -333,7 +333,7 @@ static void rx_ident_err(struct atm_vcc *vcc) printk(KERN_ALERT " host descr 0x%lx, rx pos 0x%lx, descr value " "0x%x\n",eni_vcc->descr,eni_vcc->rx_pos, (unsigned) readl(eni_vcc->recv+eni_vcc->descr*4)); - printk(KERN_ALERT " last 0x%p, servicing %d\n",eni_vcc->last, + printk(KERN_ALERT " last %p, servicing %d\n",eni_vcc->last, eni_vcc->servicing); EVENT("---dump ends here---\n",0,0); printk(KERN_NOTICE "---recent events---\n"); @@ -617,7 +617,8 @@ static int rx_aal5(struct atm_vcc *vcc) static inline int rx_vcc(struct atm_vcc *vcc) { - unsigned long vci_dsc,tmp; + void __iomem *vci_dsc; + unsigned long tmp; struct eni_vcc *eni_vcc; eni_vcc = ENI_VCC(vcc); @@ -728,7 +729,7 @@ static void dequeue_rx(struct atm_dev *dev) struct eni_vcc *eni_vcc; struct atm_vcc *vcc; struct sk_buff *skb; - unsigned long vci_dsc; + void __iomem *vci_dsc; int first; eni_dev = ENI_DEV(dev); @@ -808,7 +809,7 @@ static int open_rx_first(struct atm_vcc *vcc) static int open_rx_second(struct atm_vcc *vcc) { - unsigned long here; + void __iomem *here; struct eni_dev *eni_dev; struct eni_vcc *eni_vcc; unsigned long size; @@ -840,7 +841,7 @@ static int open_rx_second(struct atm_vcc *vcc) static void close_rx(struct atm_vcc *vcc) { DECLARE_WAITQUEUE(wait,current); - unsigned long here; + void __iomem *here; struct eni_dev *eni_dev; struct eni_vcc *eni_vcc; @@ -1289,7 +1290,8 @@ static int reserve_or_set_tx(struct atm_vcc *vcc,struct atm_trafprm *txtp, struct eni_dev *eni_dev = ENI_DEV(vcc->dev); struct eni_vcc *eni_vcc = ENI_VCC(vcc); struct eni_tx *tx; - unsigned long size,mem; + unsigned long size; + void __iomem *mem; int rate,ubr,unlimited,new_tx; int pre,res,order; int error; @@ -1687,9 +1689,9 @@ static int __devinit get_esi_asic(struct atm_dev *dev) #undef GET_SEPROM -static int __devinit get_esi_fpga(struct atm_dev *dev,unsigned long base) +static int __devinit get_esi_fpga(struct atm_dev *dev, void __iomem *base) { - unsigned long mac_base; + void __iomem *mac_base; int i; mac_base = base+EPROM_SIZE-sizeof(struct midway_eprom); @@ -1703,7 +1705,8 @@ static int __devinit eni_do_init(struct atm_dev *dev) struct midway_eprom *eprom; struct eni_dev *eni_dev; struct pci_dev *pci_dev; - unsigned long real_base,base; + unsigned long real_base; + void __iomem *base; unsigned char revision; int error,i,last; @@ -1730,13 +1733,13 @@ static int __devinit eni_do_init(struct atm_dev *dev) } printk(KERN_NOTICE DEV_LABEL "(itf %d): rev.%d,base=0x%lx,irq=%d,", dev->number,revision,real_base,eni_dev->irq); - if (!(base = (unsigned long) ioremap_nocache(real_base,MAP_MAX_SIZE))) { + if (!(base = ioremap_nocache(real_base,MAP_MAX_SIZE))) { printk("\n"); printk(KERN_ERR DEV_LABEL "(itf %d): can't set up page " "mapping\n",dev->number); return error; } - eni_dev->base_diff = real_base-base; + eni_dev->base_diff = real_base - (unsigned long) base; /* id may not be present in ASIC Tonga boards - check this @@@ */ if (!eni_dev->asic) { eprom = (struct midway_eprom *) (base+EPROM_SIZE-sizeof(struct @@ -1790,7 +1793,9 @@ static int __devinit eni_do_init(struct atm_dev *dev) static int __devinit eni_start(struct atm_dev *dev) { struct eni_dev *eni_dev; - unsigned long buf,buffer_mem; + + void __iomem *buf; + unsigned long buffer_mem; int error; DPRINTK(">eni_start\n"); @@ -1828,7 +1833,7 @@ static int __devinit eni_start(struct atm_dev *dev) tasklet_init(&eni_dev->task,eni_tasklet,(unsigned long) dev); eni_dev->events = 0; /* initialize memory management */ - buffer_mem = eni_dev->mem-(buf-eni_dev->ram); + buffer_mem = eni_dev->mem - (buf - eni_dev->ram); eni_dev->free_list_size = buffer_mem/MID_MIN_BUF_SIZE/2; eni_dev->free_list = (struct eni_free *) kmalloc( sizeof(struct eni_free)*(eni_dev->free_list_size+1),GFP_KERNEL); @@ -1955,7 +1960,7 @@ static int eni_change_qos(struct atm_vcc *vcc,struct atm_qos *qos,int flgs) */ tasklet_disable(&eni_dev->task); skb_queue_walk(&eni_dev->tx_queue, skb) { - unsigned long dsc; + void __iomem *dsc; if (ATM_SKB(skb)->vcc != vcc) continue; dsc = tx->send+ENI_PRV_POS(skb)*4; @@ -2136,9 +2141,9 @@ static int eni_proc_read(struct atm_dev *dev,loff_t *pos,char *page) if (!tx->send) continue; if (!--left) { - return sprintf(page,"tx[%d]: 0x%06lx-0x%06lx " + return sprintf(page,"tx[%d]: 0x%ld-0x%ld " "(%6ld bytes), rsv %d cps, shp %d cps%s\n",i, - tx->send-eni_dev->ram, + (unsigned long) (tx->send - eni_dev->ram), tx->send-eni_dev->ram+tx->words*4-1,tx->words*4, tx->reserved,tx->shaping, tx == eni_dev->ubr ? " (UBR)" : ""); @@ -2162,9 +2167,9 @@ static int eni_proc_read(struct atm_dev *dev,loff_t *pos,char *page) if (--left) continue; length = sprintf(page,"vcc %4d: ",vcc->vci); if (eni_vcc->rx) { - length += sprintf(page+length,"0x%06lx-0x%06lx " + length += sprintf(page+length,"0x%ld-0x%ld " "(%6ld bytes)", - eni_vcc->recv-eni_dev->ram, + (unsigned long) (eni_vcc->recv - eni_dev->ram), eni_vcc->recv-eni_dev->ram+eni_vcc->words*4-1, eni_vcc->words*4); if (eni_vcc->tx) length += sprintf(page+length,", "); @@ -2183,8 +2188,8 @@ static int eni_proc_read(struct atm_dev *dev,loff_t *pos,char *page) unsigned long offset; if (--left) continue; - offset = eni_dev->ram+eni_dev->base_diff; - return sprintf(page,"free 0x%06lx-0x%06lx (%6d bytes)\n", + offset = (unsigned long) eni_dev->ram+eni_dev->base_diff; + return sprintf(page,"free %p-%p (%6d bytes)\n", fe->start-offset,fe->start-offset+(1 << fe->order)-1, 1 << fe->order); } diff --git a/drivers/atm/eni.h b/drivers/atm/eni.h index e7af66bd8..385090c2a 100644 --- a/drivers/atm/eni.h +++ b/drivers/atm/eni.h @@ -33,12 +33,12 @@ struct eni_free { - unsigned long start; /* counting in bytes */ + void __iomem *start; /* counting in bytes */ int order; }; struct eni_tx { - unsigned long send; /* base, 0 if unused */ + void __iomem *send; /* base, 0 if unused */ int prescaler; /* shaping prescaler */ int resolution; /* shaping divider */ unsigned long tx_pos; /* current TX write position */ @@ -51,7 +51,7 @@ struct eni_tx { struct eni_vcc { int (*rx)(struct atm_vcc *vcc); /* RX function, NULL if none */ - unsigned long recv; /* receive buffer */ + void __iomem *recv; /* receive buffer */ unsigned long words; /* its size in words */ unsigned long descr; /* next descriptor (RX) */ unsigned long rx_pos; /* current RX descriptor pos */ @@ -72,13 +72,13 @@ struct eni_dev { u32 events; /* pending events */ /*-------------------------------- base pointers into Midway address space */ - unsigned long phy; /* PHY interface chip registers */ - unsigned long reg; /* register base */ - unsigned long ram; /* RAM base */ - unsigned long vci; /* VCI table */ - unsigned long rx_dma; /* RX DMA queue */ - unsigned long tx_dma; /* TX DMA queue */ - unsigned long service; /* service list */ + void __iomem *phy; /* PHY interface chip registers */ + void __iomem *reg; /* register base */ + void __iomem *ram; /* RAM base */ + void __iomem *vci; /* VCI table */ + void __iomem *rx_dma; /* RX DMA queue */ + void __iomem *tx_dma; /* TX DMA queue */ + void __iomem *service; /* service list */ /*-------------------------------- TX part */ struct eni_tx tx[NR_CHAN]; /* TX channels */ struct eni_tx *ubr; /* UBR channel */ diff --git a/drivers/atm/firestream.h b/drivers/atm/firestream.h index 8ef266cc8..49e783e35 100644 --- a/drivers/atm/firestream.h +++ b/drivers/atm/firestream.h @@ -477,7 +477,7 @@ struct fs_dev { struct timer_list timer; unsigned long hw_base; /* mem base address */ - unsigned long base; /* Mapping of base address */ + void __iomem *base; /* Mapping of base address */ int channo; unsigned long channel_mask; diff --git a/drivers/atm/he.h b/drivers/atm/he.h index 0a4adaf4b..7075ef4aa 100644 --- a/drivers/atm/he.h +++ b/drivers/atm/he.h @@ -265,7 +265,7 @@ struct he_cs_stper struct he_dev { unsigned int number; unsigned int irq; - unsigned long membase; + void __iomem *membase; char prod_id[30]; char mac_addr[6]; diff --git a/drivers/atm/idt77252.h b/drivers/atm/idt77252.h index 5a087c4e9..cfdd15e7d 100644 --- a/drivers/atm/idt77252.h +++ b/drivers/atm/idt77252.h @@ -355,9 +355,9 @@ struct idt77252_dev struct pci_dev *pcidev; /* PCI handle (desriptor) */ struct atm_dev *atmdev; /* ATM device desriptor */ - unsigned long membase; /* SAR's memory base address */ + void __iomem *membase; /* SAR's memory base address */ unsigned long srambase; /* SAR's sram base address */ - unsigned long fbq[4]; /* FBQ fill addresses */ + void __iomem *fbq[4]; /* FBQ fill addresses */ struct semaphore mutex; spinlock_t cmd_lock; /* for r/w utility/sram */ diff --git a/drivers/atm/nicstar.c b/drivers/atm/nicstar.c index b63669c1f..3f18782bd 100644 --- a/drivers/atm/nicstar.c +++ b/drivers/atm/nicstar.c @@ -467,6 +467,7 @@ static int __devinit ns_init_card(int i, struct pci_dev *pcidev) u32 u32d[4]; u32 ns_cfg_rctsize; int bcount; + unsigned long membase; error = 0; @@ -494,8 +495,8 @@ static int __devinit ns_init_card(int i, struct pci_dev *pcidev) card->index = i; card->atmdev = NULL; card->pcidev = pcidev; - card->membase = pci_resource_start(pcidev, 1); - card->membase = (unsigned long) ioremap(card->membase, NS_IOREMAP_SIZE); + membase = pci_resource_start(pcidev, 1); + card->membase = ioremap(membase, NS_IOREMAP_SIZE); if (card->membase == 0) { printk("nicstar%d: can't ioremap() membase.\n",i); diff --git a/drivers/atm/nicstar.h b/drivers/atm/nicstar.h index f762abcf2..a7eaee736 100644 --- a/drivers/atm/nicstar.h +++ b/drivers/atm/nicstar.h @@ -763,7 +763,7 @@ typedef struct ns_dev { int index; /* Card ID to the device driver */ int sram_size; /* In k x 32bit words. 32 or 128 */ - unsigned long membase; /* Card's memory base address */ + void __iomem *membase; /* Card's memory base address */ unsigned long max_pcr; int rct_size; /* Number of entries */ int vpibits; diff --git a/drivers/atm/nicstarmac.c b/drivers/atm/nicstarmac.c index ef6b0c9f8..7a842ef71 100644 --- a/drivers/atm/nicstarmac.c +++ b/drivers/atm/nicstarmac.c @@ -162,7 +162,7 @@ nicstar_read_eprom_status( virt_addr_t base ) */ static u_int8_t -read_eprom_byte(u_int32_t base, u_int8_t offset) +read_eprom_byte(virt_addr_t base, u_int8_t offset) { u_int32_t val = 0; int i,j=0; diff --git a/drivers/atm/nicstarmac.h b/drivers/atm/nicstarmac.h index 0592daee4..80beec52f 100644 --- a/drivers/atm/nicstarmac.h +++ b/drivers/atm/nicstarmac.h @@ -7,7 +7,7 @@ ******************************************************************************/ -typedef unsigned int virt_addr_t; +typedef void __iomem *virt_addr_t; u_int32_t nicstar_read_eprom_status( virt_addr_t base ); void nicstar_init_eprom( virt_addr_t base ); diff --git a/drivers/block/DAC960.h b/drivers/block/DAC960.h index be8f8992a..d5e8e7190 100644 --- a/drivers/block/DAC960.h +++ b/drivers/block/DAC960.h @@ -2282,8 +2282,8 @@ DAC960_Command_T; typedef struct DAC960_Controller { - void *BaseAddress; - void *MemoryMappedAddress; + void __iomem *BaseAddress; + void __iomem *MemoryMappedAddress; DAC960_FirmwareType_T FirmwareType; DAC960_HardwareType_T HardwareType; DAC960_IO_Address_T IO_Address; @@ -2527,7 +2527,7 @@ DAC960_Controller_T; * dma_addr_t. */ static inline -void dma_addr_writeql(dma_addr_t addr, void *write_address) +void dma_addr_writeql(dma_addr_t addr, void __iomem *write_address) { union { u64 wq; @@ -2643,7 +2643,7 @@ DAC960_BA_ErrorStatusRegister_T; */ static inline -void DAC960_BA_HardwareMailboxNewCommand(void *ControllerBaseAddress) +void DAC960_BA_HardwareMailboxNewCommand(void __iomem *ControllerBaseAddress) { DAC960_BA_InboundDoorBellRegister_T InboundDoorBellRegister; InboundDoorBellRegister.All = 0; @@ -2653,7 +2653,7 @@ void DAC960_BA_HardwareMailboxNewCommand(void *ControllerBaseAddress) } static inline -void DAC960_BA_AcknowledgeHardwareMailboxStatus(void *ControllerBaseAddress) +void DAC960_BA_AcknowledgeHardwareMailboxStatus(void __iomem *ControllerBaseAddress) { DAC960_BA_InboundDoorBellRegister_T InboundDoorBellRegister; InboundDoorBellRegister.All = 0; @@ -2663,7 +2663,7 @@ void DAC960_BA_AcknowledgeHardwareMailboxStatus(void *ControllerBaseAddress) } static inline -void DAC960_BA_GenerateInterrupt(void *ControllerBaseAddress) +void DAC960_BA_GenerateInterrupt(void __iomem *ControllerBaseAddress) { DAC960_BA_InboundDoorBellRegister_T InboundDoorBellRegister; InboundDoorBellRegister.All = 0; @@ -2673,7 +2673,7 @@ void DAC960_BA_GenerateInterrupt(void *ControllerBaseAddress) } static inline -void DAC960_BA_ControllerReset(void *ControllerBaseAddress) +void DAC960_BA_ControllerReset(void __iomem *ControllerBaseAddress) { DAC960_BA_InboundDoorBellRegister_T InboundDoorBellRegister; InboundDoorBellRegister.All = 0; @@ -2683,7 +2683,7 @@ void DAC960_BA_ControllerReset(void *ControllerBaseAddress) } static inline -void DAC960_BA_MemoryMailboxNewCommand(void *ControllerBaseAddress) +void DAC960_BA_MemoryMailboxNewCommand(void __iomem *ControllerBaseAddress) { DAC960_BA_InboundDoorBellRegister_T InboundDoorBellRegister; InboundDoorBellRegister.All = 0; @@ -2693,7 +2693,7 @@ void DAC960_BA_MemoryMailboxNewCommand(void *ControllerBaseAddress) } static inline -boolean DAC960_BA_HardwareMailboxFullP(void *ControllerBaseAddress) +boolean DAC960_BA_HardwareMailboxFullP(void __iomem *ControllerBaseAddress) { DAC960_BA_InboundDoorBellRegister_T InboundDoorBellRegister; InboundDoorBellRegister.All = @@ -2702,7 +2702,7 @@ boolean DAC960_BA_HardwareMailboxFullP(void *ControllerBaseAddress) } static inline -boolean DAC960_BA_InitializationInProgressP(void *ControllerBaseAddress) +boolean DAC960_BA_InitializationInProgressP(void __iomem *ControllerBaseAddress) { DAC960_BA_InboundDoorBellRegister_T InboundDoorBellRegister; InboundDoorBellRegister.All = @@ -2711,7 +2711,7 @@ boolean DAC960_BA_InitializationInProgressP(void *ControllerBaseAddress) } static inline -void DAC960_BA_AcknowledgeHardwareMailboxInterrupt(void *ControllerBaseAddress) +void DAC960_BA_AcknowledgeHardwareMailboxInterrupt(void __iomem *ControllerBaseAddress) { DAC960_BA_OutboundDoorBellRegister_T OutboundDoorBellRegister; OutboundDoorBellRegister.All = 0; @@ -2721,7 +2721,7 @@ void DAC960_BA_AcknowledgeHardwareMailboxInterrupt(void *ControllerBaseAddress) } static inline -void DAC960_BA_AcknowledgeMemoryMailboxInterrupt(void *ControllerBaseAddress) +void DAC960_BA_AcknowledgeMemoryMailboxInterrupt(void __iomem *ControllerBaseAddress) { DAC960_BA_OutboundDoorBellRegister_T OutboundDoorBellRegister; OutboundDoorBellRegister.All = 0; @@ -2731,7 +2731,7 @@ void DAC960_BA_AcknowledgeMemoryMailboxInterrupt(void *ControllerBaseAddress) } static inline -void DAC960_BA_AcknowledgeInterrupt(void *ControllerBaseAddress) +void DAC960_BA_AcknowledgeInterrupt(void __iomem *ControllerBaseAddress) { DAC960_BA_OutboundDoorBellRegister_T OutboundDoorBellRegister; OutboundDoorBellRegister.All = 0; @@ -2742,7 +2742,7 @@ void DAC960_BA_AcknowledgeInterrupt(void *ControllerBaseAddress) } static inline -boolean DAC960_BA_HardwareMailboxStatusAvailableP(void *ControllerBaseAddress) +boolean DAC960_BA_HardwareMailboxStatusAvailableP(void __iomem *ControllerBaseAddress) { DAC960_BA_OutboundDoorBellRegister_T OutboundDoorBellRegister; OutboundDoorBellRegister.All = @@ -2751,7 +2751,7 @@ boolean DAC960_BA_HardwareMailboxStatusAvailableP(void *ControllerBaseAddress) } static inline -boolean DAC960_BA_MemoryMailboxStatusAvailableP(void *ControllerBaseAddress) +boolean DAC960_BA_MemoryMailboxStatusAvailableP(void __iomem *ControllerBaseAddress) { DAC960_BA_OutboundDoorBellRegister_T OutboundDoorBellRegister; OutboundDoorBellRegister.All = @@ -2760,7 +2760,7 @@ boolean DAC960_BA_MemoryMailboxStatusAvailableP(void *ControllerBaseAddress) } static inline -void DAC960_BA_EnableInterrupts(void *ControllerBaseAddress) +void DAC960_BA_EnableInterrupts(void __iomem *ControllerBaseAddress) { DAC960_BA_InterruptMaskRegister_T InterruptMaskRegister; InterruptMaskRegister.All = 0xFF; @@ -2771,7 +2771,7 @@ void DAC960_BA_EnableInterrupts(void *ControllerBaseAddress) } static inline -void DAC960_BA_DisableInterrupts(void *ControllerBaseAddress) +void DAC960_BA_DisableInterrupts(void __iomem *ControllerBaseAddress) { DAC960_BA_InterruptMaskRegister_T InterruptMaskRegister; InterruptMaskRegister.All = 0xFF; @@ -2782,7 +2782,7 @@ void DAC960_BA_DisableInterrupts(void *ControllerBaseAddress) } static inline -boolean DAC960_BA_InterruptsEnabledP(void *ControllerBaseAddress) +boolean DAC960_BA_InterruptsEnabledP(void __iomem *ControllerBaseAddress) { DAC960_BA_InterruptMaskRegister_T InterruptMaskRegister; InterruptMaskRegister.All = @@ -2805,7 +2805,7 @@ void DAC960_BA_WriteCommandMailbox(DAC960_V2_CommandMailbox_T static inline -void DAC960_BA_WriteHardwareMailbox(void *ControllerBaseAddress, +void DAC960_BA_WriteHardwareMailbox(void __iomem *ControllerBaseAddress, dma_addr_t CommandMailboxDMA) { dma_addr_writeql(CommandMailboxDMA, @@ -2814,19 +2814,19 @@ void DAC960_BA_WriteHardwareMailbox(void *ControllerBaseAddress, } static inline DAC960_V2_CommandIdentifier_T -DAC960_BA_ReadCommandIdentifier(void *ControllerBaseAddress) +DAC960_BA_ReadCommandIdentifier(void __iomem *ControllerBaseAddress) { return readw(ControllerBaseAddress + DAC960_BA_CommandStatusOffset); } static inline DAC960_V2_CommandStatus_T -DAC960_BA_ReadCommandStatus(void *ControllerBaseAddress) +DAC960_BA_ReadCommandStatus(void __iomem *ControllerBaseAddress) { return readw(ControllerBaseAddress + DAC960_BA_CommandStatusOffset + 2); } static inline boolean -DAC960_BA_ReadErrorStatus(void *ControllerBaseAddress, +DAC960_BA_ReadErrorStatus(void __iomem *ControllerBaseAddress, unsigned char *ErrorStatus, unsigned char *Parameter0, unsigned char *Parameter1) @@ -2948,7 +2948,7 @@ DAC960_LP_ErrorStatusRegister_T; */ static inline -void DAC960_LP_HardwareMailboxNewCommand(void *ControllerBaseAddress) +void DAC960_LP_HardwareMailboxNewCommand(void __iomem *ControllerBaseAddress) { DAC960_LP_InboundDoorBellRegister_T InboundDoorBellRegister; InboundDoorBellRegister.All = 0; @@ -2958,7 +2958,7 @@ void DAC960_LP_HardwareMailboxNewCommand(void *ControllerBaseAddress) } static inline -void DAC960_LP_AcknowledgeHardwareMailboxStatus(void *ControllerBaseAddress) +void DAC960_LP_AcknowledgeHardwareMailboxStatus(void __iomem *ControllerBaseAddress) { DAC960_LP_InboundDoorBellRegister_T InboundDoorBellRegister; InboundDoorBellRegister.All = 0; @@ -2968,7 +2968,7 @@ void DAC960_LP_AcknowledgeHardwareMailboxStatus(void *ControllerBaseAddress) } static inline -void DAC960_LP_GenerateInterrupt(void *ControllerBaseAddress) +void DAC960_LP_GenerateInterrupt(void __iomem *ControllerBaseAddress) { DAC960_LP_InboundDoorBellRegister_T InboundDoorBellRegister; InboundDoorBellRegister.All = 0; @@ -2978,7 +2978,7 @@ void DAC960_LP_GenerateInterrupt(void *ControllerBaseAddress) } static inline -void DAC960_LP_ControllerReset(void *ControllerBaseAddress) +void DAC960_LP_ControllerReset(void __iomem *ControllerBaseAddress) { DAC960_LP_InboundDoorBellRegister_T InboundDoorBellRegister; InboundDoorBellRegister.All = 0; @@ -2988,7 +2988,7 @@ void DAC960_LP_ControllerReset(void *ControllerBaseAddress) } static inline -void DAC960_LP_MemoryMailboxNewCommand(void *ControllerBaseAddress) +void DAC960_LP_MemoryMailboxNewCommand(void __iomem *ControllerBaseAddress) { DAC960_LP_InboundDoorBellRegister_T InboundDoorBellRegister; InboundDoorBellRegister.All = 0; @@ -2998,7 +2998,7 @@ void DAC960_LP_MemoryMailboxNewCommand(void *ControllerBaseAddress) } static inline -boolean DAC960_LP_HardwareMailboxFullP(void *ControllerBaseAddress) +boolean DAC960_LP_HardwareMailboxFullP(void __iomem *ControllerBaseAddress) { DAC960_LP_InboundDoorBellRegister_T InboundDoorBellRegister; InboundDoorBellRegister.All = @@ -3007,7 +3007,7 @@ boolean DAC960_LP_HardwareMailboxFullP(void *ControllerBaseAddress) } static inline -boolean DAC960_LP_InitializationInProgressP(void *ControllerBaseAddress) +boolean DAC960_LP_InitializationInProgressP(void __iomem *ControllerBaseAddress) { DAC960_LP_InboundDoorBellRegister_T InboundDoorBellRegister; InboundDoorBellRegister.All = @@ -3016,7 +3016,7 @@ boolean DAC960_LP_InitializationInProgressP(void *ControllerBaseAddress) } static inline -void DAC960_LP_AcknowledgeHardwareMailboxInterrupt(void *ControllerBaseAddress) +void DAC960_LP_AcknowledgeHardwareMailboxInterrupt(void __iomem *ControllerBaseAddress) { DAC960_LP_OutboundDoorBellRegister_T OutboundDoorBellRegister; OutboundDoorBellRegister.All = 0; @@ -3026,7 +3026,7 @@ void DAC960_LP_AcknowledgeHardwareMailboxInterrupt(void *ControllerBaseAddress) } static inline -void DAC960_LP_AcknowledgeMemoryMailboxInterrupt(void *ControllerBaseAddress) +void DAC960_LP_AcknowledgeMemoryMailboxInterrupt(void __iomem *ControllerBaseAddress) { DAC960_LP_OutboundDoorBellRegister_T OutboundDoorBellRegister; OutboundDoorBellRegister.All = 0; @@ -3036,7 +3036,7 @@ void DAC960_LP_AcknowledgeMemoryMailboxInterrupt(void *ControllerBaseAddress) } static inline -void DAC960_LP_AcknowledgeInterrupt(void *ControllerBaseAddress) +void DAC960_LP_AcknowledgeInterrupt(void __iomem *ControllerBaseAddress) { DAC960_LP_OutboundDoorBellRegister_T OutboundDoorBellRegister; OutboundDoorBellRegister.All = 0; @@ -3047,7 +3047,7 @@ void DAC960_LP_AcknowledgeInterrupt(void *ControllerBaseAddress) } static inline -boolean DAC960_LP_HardwareMailboxStatusAvailableP(void *ControllerBaseAddress) +boolean DAC960_LP_HardwareMailboxStatusAvailableP(void __iomem *ControllerBaseAddress) { DAC960_LP_OutboundDoorBellRegister_T OutboundDoorBellRegister; OutboundDoorBellRegister.All = @@ -3056,7 +3056,7 @@ boolean DAC960_LP_HardwareMailboxStatusAvailableP(void *ControllerBaseAddress) } static inline -boolean DAC960_LP_MemoryMailboxStatusAvailableP(void *ControllerBaseAddress) +boolean DAC960_LP_MemoryMailboxStatusAvailableP(void __iomem *ControllerBaseAddress) { DAC960_LP_OutboundDoorBellRegister_T OutboundDoorBellRegister; OutboundDoorBellRegister.All = @@ -3065,7 +3065,7 @@ boolean DAC960_LP_MemoryMailboxStatusAvailableP(void *ControllerBaseAddress) } static inline -void DAC960_LP_EnableInterrupts(void *ControllerBaseAddress) +void DAC960_LP_EnableInterrupts(void __iomem *ControllerBaseAddress) { DAC960_LP_InterruptMaskRegister_T InterruptMaskRegister; InterruptMaskRegister.All = 0xFF; @@ -3075,7 +3075,7 @@ void DAC960_LP_EnableInterrupts(void *ControllerBaseAddress) } static inline -void DAC960_LP_DisableInterrupts(void *ControllerBaseAddress) +void DAC960_LP_DisableInterrupts(void __iomem *ControllerBaseAddress) { DAC960_LP_InterruptMaskRegister_T InterruptMaskRegister; InterruptMaskRegister.All = 0xFF; @@ -3085,7 +3085,7 @@ void DAC960_LP_DisableInterrupts(void *ControllerBaseAddress) } static inline -boolean DAC960_LP_InterruptsEnabledP(void *ControllerBaseAddress) +boolean DAC960_LP_InterruptsEnabledP(void __iomem *ControllerBaseAddress) { DAC960_LP_InterruptMaskRegister_T InterruptMaskRegister; InterruptMaskRegister.All = @@ -3107,7 +3107,7 @@ void DAC960_LP_WriteCommandMailbox(DAC960_V2_CommandMailbox_T } static inline -void DAC960_LP_WriteHardwareMailbox(void *ControllerBaseAddress, +void DAC960_LP_WriteHardwareMailbox(void __iomem *ControllerBaseAddress, dma_addr_t CommandMailboxDMA) { dma_addr_writeql(CommandMailboxDMA, @@ -3116,19 +3116,19 @@ void DAC960_LP_WriteHardwareMailbox(void *ControllerBaseAddress, } static inline DAC960_V2_CommandIdentifier_T -DAC960_LP_ReadCommandIdentifier(void *ControllerBaseAddress) +DAC960_LP_ReadCommandIdentifier(void __iomem *ControllerBaseAddress) { return readw(ControllerBaseAddress + DAC960_LP_CommandStatusOffset); } static inline DAC960_V2_CommandStatus_T -DAC960_LP_ReadCommandStatus(void *ControllerBaseAddress) +DAC960_LP_ReadCommandStatus(void __iomem *ControllerBaseAddress) { return readw(ControllerBaseAddress + DAC960_LP_CommandStatusOffset + 2); } static inline boolean -DAC960_LP_ReadErrorStatus(void *ControllerBaseAddress, +DAC960_LP_ReadErrorStatus(void __iomem *ControllerBaseAddress, unsigned char *ErrorStatus, unsigned char *Parameter0, unsigned char *Parameter1) @@ -3262,7 +3262,7 @@ DAC960_LA_ErrorStatusRegister_T; */ static inline -void DAC960_LA_HardwareMailboxNewCommand(void *ControllerBaseAddress) +void DAC960_LA_HardwareMailboxNewCommand(void __iomem *ControllerBaseAddress) { DAC960_LA_InboundDoorBellRegister_T InboundDoorBellRegister; InboundDoorBellRegister.All = 0; @@ -3272,7 +3272,7 @@ void DAC960_LA_HardwareMailboxNewCommand(void *ControllerBaseAddress) } static inline -void DAC960_LA_AcknowledgeHardwareMailboxStatus(void *ControllerBaseAddress) +void DAC960_LA_AcknowledgeHardwareMailboxStatus(void __iomem *ControllerBaseAddress) { DAC960_LA_InboundDoorBellRegister_T InboundDoorBellRegister; InboundDoorBellRegister.All = 0; @@ -3282,7 +3282,7 @@ void DAC960_LA_AcknowledgeHardwareMailboxStatus(void *ControllerBaseAddress) } static inline -void DAC960_LA_GenerateInterrupt(void *ControllerBaseAddress) +void DAC960_LA_GenerateInterrupt(void __iomem *ControllerBaseAddress) { DAC960_LA_InboundDoorBellRegister_T InboundDoorBellRegister; InboundDoorBellRegister.All = 0; @@ -3292,7 +3292,7 @@ void DAC960_LA_GenerateInterrupt(void *ControllerBaseAddress) } static inline -void DAC960_LA_ControllerReset(void *ControllerBaseAddress) +void DAC960_LA_ControllerReset(void __iomem *ControllerBaseAddress) { DAC960_LA_InboundDoorBellRegister_T InboundDoorBellRegister; InboundDoorBellRegister.All = 0; @@ -3302,7 +3302,7 @@ void DAC960_LA_ControllerReset(void *ControllerBaseAddress) } static inline -void DAC960_LA_MemoryMailboxNewCommand(void *ControllerBaseAddress) +void DAC960_LA_MemoryMailboxNewCommand(void __iomem *ControllerBaseAddress) { DAC960_LA_InboundDoorBellRegister_T InboundDoorBellRegister; InboundDoorBellRegister.All = 0; @@ -3312,7 +3312,7 @@ void DAC960_LA_MemoryMailboxNewCommand(void *ControllerBaseAddress) } static inline -boolean DAC960_LA_HardwareMailboxFullP(void *ControllerBaseAddress) +boolean DAC960_LA_HardwareMailboxFullP(void __iomem *ControllerBaseAddress) { DAC960_LA_InboundDoorBellRegister_T InboundDoorBellRegister; InboundDoorBellRegister.All = @@ -3321,7 +3321,7 @@ boolean DAC960_LA_HardwareMailboxFullP(void *ControllerBaseAddress) } static inline -boolean DAC960_LA_InitializationInProgressP(void *ControllerBaseAddress) +boolean DAC960_LA_InitializationInProgressP(void __iomem *ControllerBaseAddress) { DAC960_LA_InboundDoorBellRegister_T InboundDoorBellRegister; InboundDoorBellRegister.All = @@ -3330,7 +3330,7 @@ boolean DAC960_LA_InitializationInProgressP(void *ControllerBaseAddress) } static inline -void DAC960_LA_AcknowledgeHardwareMailboxInterrupt(void *ControllerBaseAddress) +void DAC960_LA_AcknowledgeHardwareMailboxInterrupt(void __iomem *ControllerBaseAddress) { DAC960_LA_OutboundDoorBellRegister_T OutboundDoorBellRegister; OutboundDoorBellRegister.All = 0; @@ -3340,7 +3340,7 @@ void DAC960_LA_AcknowledgeHardwareMailboxInterrupt(void *ControllerBaseAddress) } static inline -void DAC960_LA_AcknowledgeMemoryMailboxInterrupt(void *ControllerBaseAddress) +void DAC960_LA_AcknowledgeMemoryMailboxInterrupt(void __iomem *ControllerBaseAddress) { DAC960_LA_OutboundDoorBellRegister_T OutboundDoorBellRegister; OutboundDoorBellRegister.All = 0; @@ -3350,7 +3350,7 @@ void DAC960_LA_AcknowledgeMemoryMailboxInterrupt(void *ControllerBaseAddress) } static inline -void DAC960_LA_AcknowledgeInterrupt(void *ControllerBaseAddress) +void DAC960_LA_AcknowledgeInterrupt(void __iomem *ControllerBaseAddress) { DAC960_LA_OutboundDoorBellRegister_T OutboundDoorBellRegister; OutboundDoorBellRegister.All = 0; @@ -3361,7 +3361,7 @@ void DAC960_LA_AcknowledgeInterrupt(void *ControllerBaseAddress) } static inline -boolean DAC960_LA_HardwareMailboxStatusAvailableP(void *ControllerBaseAddress) +boolean DAC960_LA_HardwareMailboxStatusAvailableP(void __iomem *ControllerBaseAddress) { DAC960_LA_OutboundDoorBellRegister_T OutboundDoorBellRegister; OutboundDoorBellRegister.All = @@ -3370,7 +3370,7 @@ boolean DAC960_LA_HardwareMailboxStatusAvailableP(void *ControllerBaseAddress) } static inline -boolean DAC960_LA_MemoryMailboxStatusAvailableP(void *ControllerBaseAddress) +boolean DAC960_LA_MemoryMailboxStatusAvailableP(void __iomem *ControllerBaseAddress) { DAC960_LA_OutboundDoorBellRegister_T OutboundDoorBellRegister; OutboundDoorBellRegister.All = @@ -3379,7 +3379,7 @@ boolean DAC960_LA_MemoryMailboxStatusAvailableP(void *ControllerBaseAddress) } static inline -void DAC960_LA_EnableInterrupts(void *ControllerBaseAddress) +void DAC960_LA_EnableInterrupts(void __iomem *ControllerBaseAddress) { DAC960_LA_InterruptMaskRegister_T InterruptMaskRegister; InterruptMaskRegister.All = 0xFF; @@ -3389,7 +3389,7 @@ void DAC960_LA_EnableInterrupts(void *ControllerBaseAddress) } static inline -void DAC960_LA_DisableInterrupts(void *ControllerBaseAddress) +void DAC960_LA_DisableInterrupts(void __iomem *ControllerBaseAddress) { DAC960_LA_InterruptMaskRegister_T InterruptMaskRegister; InterruptMaskRegister.All = 0xFF; @@ -3399,7 +3399,7 @@ void DAC960_LA_DisableInterrupts(void *ControllerBaseAddress) } static inline -boolean DAC960_LA_InterruptsEnabledP(void *ControllerBaseAddress) +boolean DAC960_LA_InterruptsEnabledP(void __iomem *ControllerBaseAddress) { DAC960_LA_InterruptMaskRegister_T InterruptMaskRegister; InterruptMaskRegister.All = @@ -3422,7 +3422,7 @@ void DAC960_LA_WriteCommandMailbox(DAC960_V1_CommandMailbox_T } static inline -void DAC960_LA_WriteHardwareMailbox(void *ControllerBaseAddress, +void DAC960_LA_WriteHardwareMailbox(void __iomem *ControllerBaseAddress, DAC960_V1_CommandMailbox_T *CommandMailbox) { writel(CommandMailbox->Words[0], @@ -3436,20 +3436,20 @@ void DAC960_LA_WriteHardwareMailbox(void *ControllerBaseAddress, } static inline DAC960_V1_CommandIdentifier_T -DAC960_LA_ReadStatusCommandIdentifier(void *ControllerBaseAddress) +DAC960_LA_ReadStatusCommandIdentifier(void __iomem *ControllerBaseAddress) { return readb(ControllerBaseAddress + DAC960_LA_StatusCommandIdentifierRegOffset); } static inline DAC960_V1_CommandStatus_T -DAC960_LA_ReadStatusRegister(void *ControllerBaseAddress) +DAC960_LA_ReadStatusRegister(void __iomem *ControllerBaseAddress) { return readw(ControllerBaseAddress + DAC960_LA_StatusRegisterOffset); } static inline boolean -DAC960_LA_ReadErrorStatus(void *ControllerBaseAddress, +DAC960_LA_ReadErrorStatus(void __iomem *ControllerBaseAddress, unsigned char *ErrorStatus, unsigned char *Parameter0, unsigned char *Parameter1) @@ -3583,7 +3583,7 @@ DAC960_PG_ErrorStatusRegister_T; */ static inline -void DAC960_PG_HardwareMailboxNewCommand(void *ControllerBaseAddress) +void DAC960_PG_HardwareMailboxNewCommand(void __iomem *ControllerBaseAddress) { DAC960_PG_InboundDoorBellRegister_T InboundDoorBellRegister; InboundDoorBellRegister.All = 0; @@ -3593,7 +3593,7 @@ void DAC960_PG_HardwareMailboxNewCommand(void *ControllerBaseAddress) } static inline -void DAC960_PG_AcknowledgeHardwareMailboxStatus(void *ControllerBaseAddress) +void DAC960_PG_AcknowledgeHardwareMailboxStatus(void __iomem *ControllerBaseAddress) { DAC960_PG_InboundDoorBellRegister_T InboundDoorBellRegister; InboundDoorBellRegister.All = 0; @@ -3603,7 +3603,7 @@ void DAC960_PG_AcknowledgeHardwareMailboxStatus(void *ControllerBaseAddress) } static inline -void DAC960_PG_GenerateInterrupt(void *ControllerBaseAddress) +void DAC960_PG_GenerateInterrupt(void __iomem *ControllerBaseAddress) { DAC960_PG_InboundDoorBellRegister_T InboundDoorBellRegister; InboundDoorBellRegister.All = 0; @@ -3613,7 +3613,7 @@ void DAC960_PG_GenerateInterrupt(void *ControllerBaseAddress) } static inline -void DAC960_PG_ControllerReset(void *ControllerBaseAddress) +void DAC960_PG_ControllerReset(void __iomem *ControllerBaseAddress) { DAC960_PG_InboundDoorBellRegister_T InboundDoorBellRegister; InboundDoorBellRegister.All = 0; @@ -3623,7 +3623,7 @@ void DAC960_PG_ControllerReset(void *ControllerBaseAddress) } static inline -void DAC960_PG_MemoryMailboxNewCommand(void *ControllerBaseAddress) +void DAC960_PG_MemoryMailboxNewCommand(void __iomem *ControllerBaseAddress) { DAC960_PG_InboundDoorBellRegister_T InboundDoorBellRegister; InboundDoorBellRegister.All = 0; @@ -3633,7 +3633,7 @@ void DAC960_PG_MemoryMailboxNewCommand(void *ControllerBaseAddress) } static inline -boolean DAC960_PG_HardwareMailboxFullP(void *ControllerBaseAddress) +boolean DAC960_PG_HardwareMailboxFullP(void __iomem *ControllerBaseAddress) { DAC960_PG_InboundDoorBellRegister_T InboundDoorBellRegister; InboundDoorBellRegister.All = @@ -3642,7 +3642,7 @@ boolean DAC960_PG_HardwareMailboxFullP(void *ControllerBaseAddress) } static inline -boolean DAC960_PG_InitializationInProgressP(void *ControllerBaseAddress) +boolean DAC960_PG_InitializationInProgressP(void __iomem *ControllerBaseAddress) { DAC960_PG_InboundDoorBellRegister_T InboundDoorBellRegister; InboundDoorBellRegister.All = @@ -3651,7 +3651,7 @@ boolean DAC960_PG_InitializationInProgressP(void *ControllerBaseAddress) } static inline -void DAC960_PG_AcknowledgeHardwareMailboxInterrupt(void *ControllerBaseAddress) +void DAC960_PG_AcknowledgeHardwareMailboxInterrupt(void __iomem *ControllerBaseAddress) { DAC960_PG_OutboundDoorBellRegister_T OutboundDoorBellRegister; OutboundDoorBellRegister.All = 0; @@ -3661,7 +3661,7 @@ void DAC960_PG_AcknowledgeHardwareMailboxInterrupt(void *ControllerBaseAddress) } static inline -void DAC960_PG_AcknowledgeMemoryMailboxInterrupt(void *ControllerBaseAddress) +void DAC960_PG_AcknowledgeMemoryMailboxInterrupt(void __iomem *ControllerBaseAddress) { DAC960_PG_OutboundDoorBellRegister_T OutboundDoorBellRegister; OutboundDoorBellRegister.All = 0; @@ -3671,7 +3671,7 @@ void DAC960_PG_AcknowledgeMemoryMailboxInterrupt(void *ControllerBaseAddress) } static inline -void DAC960_PG_AcknowledgeInterrupt(void *ControllerBaseAddress) +void DAC960_PG_AcknowledgeInterrupt(void __iomem *ControllerBaseAddress) { DAC960_PG_OutboundDoorBellRegister_T OutboundDoorBellRegister; OutboundDoorBellRegister.All = 0; @@ -3682,7 +3682,7 @@ void DAC960_PG_AcknowledgeInterrupt(void *ControllerBaseAddress) } static inline -boolean DAC960_PG_HardwareMailboxStatusAvailableP(void *ControllerBaseAddress) +boolean DAC960_PG_HardwareMailboxStatusAvailableP(void __iomem *ControllerBaseAddress) { DAC960_PG_OutboundDoorBellRegister_T OutboundDoorBellRegister; OutboundDoorBellRegister.All = @@ -3691,7 +3691,7 @@ boolean DAC960_PG_HardwareMailboxStatusAvailableP(void *ControllerBaseAddress) } static inline -boolean DAC960_PG_MemoryMailboxStatusAvailableP(void *ControllerBaseAddress) +boolean DAC960_PG_MemoryMailboxStatusAvailableP(void __iomem *ControllerBaseAddress) { DAC960_PG_OutboundDoorBellRegister_T OutboundDoorBellRegister; OutboundDoorBellRegister.All = @@ -3700,7 +3700,7 @@ boolean DAC960_PG_MemoryMailboxStatusAvailableP(void *ControllerBaseAddress) } static inline -void DAC960_PG_EnableInterrupts(void *ControllerBaseAddress) +void DAC960_PG_EnableInterrupts(void __iomem *ControllerBaseAddress) { DAC960_PG_InterruptMaskRegister_T InterruptMaskRegister; InterruptMaskRegister.All = 0; @@ -3712,7 +3712,7 @@ void DAC960_PG_EnableInterrupts(void *ControllerBaseAddress) } static inline -void DAC960_PG_DisableInterrupts(void *ControllerBaseAddress) +void DAC960_PG_DisableInterrupts(void __iomem *ControllerBaseAddress) { DAC960_PG_InterruptMaskRegister_T InterruptMaskRegister; InterruptMaskRegister.All = 0; @@ -3724,7 +3724,7 @@ void DAC960_PG_DisableInterrupts(void *ControllerBaseAddress) } static inline -boolean DAC960_PG_InterruptsEnabledP(void *ControllerBaseAddress) +boolean DAC960_PG_InterruptsEnabledP(void __iomem *ControllerBaseAddress) { DAC960_PG_InterruptMaskRegister_T InterruptMaskRegister; InterruptMaskRegister.All = @@ -3747,7 +3747,7 @@ void DAC960_PG_WriteCommandMailbox(DAC960_V1_CommandMailbox_T } static inline -void DAC960_PG_WriteHardwareMailbox(void *ControllerBaseAddress, +void DAC960_PG_WriteHardwareMailbox(void __iomem *ControllerBaseAddress, DAC960_V1_CommandMailbox_T *CommandMailbox) { writel(CommandMailbox->Words[0], @@ -3761,20 +3761,20 @@ void DAC960_PG_WriteHardwareMailbox(void *ControllerBaseAddress, } static inline DAC960_V1_CommandIdentifier_T -DAC960_PG_ReadStatusCommandIdentifier(void *ControllerBaseAddress) +DAC960_PG_ReadStatusCommandIdentifier(void __iomem *ControllerBaseAddress) { return readb(ControllerBaseAddress + DAC960_PG_StatusCommandIdentifierRegOffset); } static inline DAC960_V1_CommandStatus_T -DAC960_PG_ReadStatusRegister(void *ControllerBaseAddress) +DAC960_PG_ReadStatusRegister(void __iomem *ControllerBaseAddress) { return readw(ControllerBaseAddress + DAC960_PG_StatusRegisterOffset); } static inline boolean -DAC960_PG_ReadErrorStatus(void *ControllerBaseAddress, +DAC960_PG_ReadErrorStatus(void __iomem *ControllerBaseAddress, unsigned char *ErrorStatus, unsigned char *Parameter0, unsigned char *Parameter1) @@ -3903,7 +3903,7 @@ DAC960_PD_ErrorStatusRegister_T; */ static inline -void DAC960_PD_NewCommand(void *ControllerBaseAddress) +void DAC960_PD_NewCommand(void __iomem *ControllerBaseAddress) { DAC960_PD_InboundDoorBellRegister_T InboundDoorBellRegister; InboundDoorBellRegister.All = 0; @@ -3913,7 +3913,7 @@ void DAC960_PD_NewCommand(void *ControllerBaseAddress) } static inline -void DAC960_PD_AcknowledgeStatus(void *ControllerBaseAddress) +void DAC960_PD_AcknowledgeStatus(void __iomem *ControllerBaseAddress) { DAC960_PD_InboundDoorBellRegister_T InboundDoorBellRegister; InboundDoorBellRegister.All = 0; @@ -3923,7 +3923,7 @@ void DAC960_PD_AcknowledgeStatus(void *ControllerBaseAddress) } static inline -void DAC960_PD_GenerateInterrupt(void *ControllerBaseAddress) +void DAC960_PD_GenerateInterrupt(void __iomem *ControllerBaseAddress) { DAC960_PD_InboundDoorBellRegister_T InboundDoorBellRegister; InboundDoorBellRegister.All = 0; @@ -3933,7 +3933,7 @@ void DAC960_PD_GenerateInterrupt(void *ControllerBaseAddress) } static inline -void DAC960_PD_ControllerReset(void *ControllerBaseAddress) +void DAC960_PD_ControllerReset(void __iomem *ControllerBaseAddress) { DAC960_PD_InboundDoorBellRegister_T InboundDoorBellRegister; InboundDoorBellRegister.All = 0; @@ -3943,7 +3943,7 @@ void DAC960_PD_ControllerReset(void *ControllerBaseAddress) } static inline -boolean DAC960_PD_MailboxFullP(void *ControllerBaseAddress) +boolean DAC960_PD_MailboxFullP(void __iomem *ControllerBaseAddress) { DAC960_PD_InboundDoorBellRegister_T InboundDoorBellRegister; InboundDoorBellRegister.All = @@ -3952,7 +3952,7 @@ boolean DAC960_PD_MailboxFullP(void *ControllerBaseAddress) } static inline -boolean DAC960_PD_InitializationInProgressP(void *ControllerBaseAddress) +boolean DAC960_PD_InitializationInProgressP(void __iomem *ControllerBaseAddress) { DAC960_PD_InboundDoorBellRegister_T InboundDoorBellRegister; InboundDoorBellRegister.All = @@ -3961,7 +3961,7 @@ boolean DAC960_PD_InitializationInProgressP(void *ControllerBaseAddress) } static inline -void DAC960_PD_AcknowledgeInterrupt(void *ControllerBaseAddress) +void DAC960_PD_AcknowledgeInterrupt(void __iomem *ControllerBaseAddress) { DAC960_PD_OutboundDoorBellRegister_T OutboundDoorBellRegister; OutboundDoorBellRegister.All = 0; @@ -3971,7 +3971,7 @@ void DAC960_PD_AcknowledgeInterrupt(void *ControllerBaseAddress) } static inline -boolean DAC960_PD_StatusAvailableP(void *ControllerBaseAddress) +boolean DAC960_PD_StatusAvailableP(void __iomem *ControllerBaseAddress) { DAC960_PD_OutboundDoorBellRegister_T OutboundDoorBellRegister; OutboundDoorBellRegister.All = @@ -3980,7 +3980,7 @@ boolean DAC960_PD_StatusAvailableP(void *ControllerBaseAddress) } static inline -void DAC960_PD_EnableInterrupts(void *ControllerBaseAddress) +void DAC960_PD_EnableInterrupts(void __iomem *ControllerBaseAddress) { DAC960_PD_InterruptEnableRegister_T InterruptEnableRegister; InterruptEnableRegister.All = 0; @@ -3990,7 +3990,7 @@ void DAC960_PD_EnableInterrupts(void *ControllerBaseAddress) } static inline -void DAC960_PD_DisableInterrupts(void *ControllerBaseAddress) +void DAC960_PD_DisableInterrupts(void __iomem *ControllerBaseAddress) { DAC960_PD_InterruptEnableRegister_T InterruptEnableRegister; InterruptEnableRegister.All = 0; @@ -4000,7 +4000,7 @@ void DAC960_PD_DisableInterrupts(void *ControllerBaseAddress) } static inline -boolean DAC960_PD_InterruptsEnabledP(void *ControllerBaseAddress) +boolean DAC960_PD_InterruptsEnabledP(void __iomem *ControllerBaseAddress) { DAC960_PD_InterruptEnableRegister_T InterruptEnableRegister; InterruptEnableRegister.All = @@ -4009,7 +4009,7 @@ boolean DAC960_PD_InterruptsEnabledP(void *ControllerBaseAddress) } static inline -void DAC960_PD_WriteCommandMailbox(void *ControllerBaseAddress, +void DAC960_PD_WriteCommandMailbox(void __iomem *ControllerBaseAddress, DAC960_V1_CommandMailbox_T *CommandMailbox) { writel(CommandMailbox->Words[0], @@ -4023,20 +4023,20 @@ void DAC960_PD_WriteCommandMailbox(void *ControllerBaseAddress, } static inline DAC960_V1_CommandIdentifier_T -DAC960_PD_ReadStatusCommandIdentifier(void *ControllerBaseAddress) +DAC960_PD_ReadStatusCommandIdentifier(void __iomem *ControllerBaseAddress) { return readb(ControllerBaseAddress + DAC960_PD_StatusCommandIdentifierRegOffset); } static inline DAC960_V1_CommandStatus_T -DAC960_PD_ReadStatusRegister(void *ControllerBaseAddress) +DAC960_PD_ReadStatusRegister(void __iomem *ControllerBaseAddress) { return readw(ControllerBaseAddress + DAC960_PD_StatusRegisterOffset); } static inline boolean -DAC960_PD_ReadErrorStatus(void *ControllerBaseAddress, +DAC960_PD_ReadErrorStatus(void __iomem *ControllerBaseAddress, unsigned char *ErrorStatus, unsigned char *Parameter0, unsigned char *Parameter1) diff --git a/drivers/block/amiflop.c b/drivers/block/amiflop.c index 42c1e5663..62e3a7ed3 100644 --- a/drivers/block/amiflop.c +++ b/drivers/block/amiflop.c @@ -386,16 +386,6 @@ static void fd_motor_off(unsigned long drive) fd_select(drive); udelay (1); fd_deselect(drive); - -#ifdef MODULE -/* - this is the last interrupt for any drive access, happens after - release (from floppy_off). So we have to wait until now to decrease - the use count. -*/ - if (decusecount) - MOD_DEC_USE_COUNT; -#endif } static void floppy_off (unsigned int nr) @@ -1590,10 +1580,6 @@ static int floppy_open(struct inode *inode, struct file *filp) local_irq_save(flags); fd_ref[drive]++; fd_device[drive] = system; -#ifdef MODULE - if (unit[drive].motor == 0) - MOD_INC_USE_COUNT; -#endif local_irq_restore(flags); unit[drive].dtype=&data_types[system]; @@ -1839,6 +1825,7 @@ int init_module(void) return amiga_floppy_init(); } +#if 0 /* not safe to unload */ void cleanup_module(void) { int i; @@ -1860,3 +1847,4 @@ void cleanup_module(void) unregister_blkdev(FLOPPY_MAJOR, "fd"); } #endif +#endif diff --git a/drivers/block/cciss.h b/drivers/block/cciss.h index 825855cd4..8ab689c21 100644 --- a/drivers/block/cciss.h +++ b/drivers/block/cciss.h @@ -43,12 +43,12 @@ struct ctlr_info char firm_ver[4]; // Firmware version struct pci_dev *pdev; __u32 board_id; - unsigned long vaddr; + void __iomem *vaddr; unsigned long paddr; unsigned long io_mem_addr; unsigned long io_mem_length; - CfgTable_struct *cfgtable; - int intr; + CfgTable_struct __iomem *cfgtable; + unsigned int intr; int interrupts_enabled; int max_commands; int commands_outstanding; diff --git a/drivers/block/cfq-iosched-orig.c b/drivers/block/cfq-iosched-orig.c deleted file mode 100644 index 977d32ddd..000000000 --- a/drivers/block/cfq-iosched-orig.c +++ /dev/null @@ -1,706 +0,0 @@ -/* - * linux/drivers/block/cfq-iosched.c - * - * CFQ, or complete fairness queueing, disk scheduler. - * - * Based on ideas from a previously unfinished io - * scheduler (round robin per-process disk scheduling) and Andrea Arcangeli. - * - * Copyright (C) 2003 Jens Axboe - */ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -/* - * tunables - */ -static int cfq_quantum = 4; -static int cfq_queued = 8; - -#define CFQ_QHASH_SHIFT 6 -#define CFQ_QHASH_ENTRIES (1 << CFQ_QHASH_SHIFT) -#define list_entry_qhash(entry) list_entry((entry), struct cfq_queue, cfq_hash) - -#define CFQ_MHASH_SHIFT 8 -#define CFQ_MHASH_BLOCK(sec) ((sec) >> 3) -#define CFQ_MHASH_ENTRIES (1 << CFQ_MHASH_SHIFT) -#define CFQ_MHASH_FN(sec) (hash_long(CFQ_MHASH_BLOCK((sec)),CFQ_MHASH_SHIFT)) -#define ON_MHASH(crq) !list_empty(&(crq)->hash) -#define rq_hash_key(rq) ((rq)->sector + (rq)->nr_sectors) -#define list_entry_hash(ptr) list_entry((ptr), struct cfq_rq, hash) - -#define list_entry_cfqq(ptr) list_entry((ptr), struct cfq_queue, cfq_list) - -#define RQ_DATA(rq) ((struct cfq_rq *) (rq)->elevator_private) - -static kmem_cache_t *crq_pool; -static kmem_cache_t *cfq_pool; -static mempool_t *cfq_mpool; - -struct cfq_data { - struct list_head rr_list; - struct list_head *dispatch; - struct list_head *cfq_hash; - - struct list_head *crq_hash; - - unsigned int busy_queues; - unsigned int max_queued; - - mempool_t *crq_pool; -}; - -struct cfq_queue { - struct list_head cfq_hash; - struct list_head cfq_list; - struct rb_root sort_list; - int pid; - int queued[2]; -#if 0 - /* - * with a simple addition like this, we can do io priorities. almost. - * does need a split request free list, too. - */ - int io_prio -#endif -}; - -struct cfq_rq { - struct rb_node rb_node; - sector_t rb_key; - - struct request *request; - - struct cfq_queue *cfq_queue; - - struct list_head hash; -}; - -static void cfq_put_queue(struct cfq_data *cfqd, struct cfq_queue *cfqq); -static struct cfq_queue *cfq_find_cfq_hash(struct cfq_data *cfqd, int pid); -static void cfq_dispatch_sort(struct list_head *head, struct cfq_rq *crq); - -/* - * lots of deadline iosched dupes, can be abstracted later... - */ -static inline void __cfq_del_crq_hash(struct cfq_rq *crq) -{ - list_del_init(&crq->hash); -} - -static inline void cfq_del_crq_hash(struct cfq_rq *crq) -{ - if (ON_MHASH(crq)) - __cfq_del_crq_hash(crq); -} - -static void cfq_remove_merge_hints(request_queue_t *q, struct cfq_rq *crq) -{ - cfq_del_crq_hash(crq); - - if (q->last_merge == crq->request) - q->last_merge = NULL; -} - -static inline void cfq_add_crq_hash(struct cfq_data *cfqd, struct cfq_rq *crq) -{ - struct request *rq = crq->request; - - BUG_ON(ON_MHASH(crq)); - - list_add(&crq->hash, &cfqd->crq_hash[CFQ_MHASH_FN(rq_hash_key(rq))]); -} - -static struct request *cfq_find_rq_hash(struct cfq_data *cfqd, sector_t offset) -{ - struct list_head *hash_list = &cfqd->crq_hash[CFQ_MHASH_FN(offset)]; - struct list_head *entry, *next = hash_list->next; - - while ((entry = next) != hash_list) { - struct cfq_rq *crq = list_entry_hash(entry); - struct request *__rq = crq->request; - - next = entry->next; - - BUG_ON(!ON_MHASH(crq)); - - if (!rq_mergeable(__rq)) { - __cfq_del_crq_hash(crq); - continue; - } - - if (rq_hash_key(__rq) == offset) - return __rq; - } - - return NULL; -} - -/* - * rb tree support functions - */ -#define RB_NONE (2) -#define RB_EMPTY(node) ((node)->rb_node == NULL) -#define RB_CLEAR(node) ((node)->rb_color = RB_NONE) -#define RB_CLEAR_ROOT(root) ((root)->rb_node = NULL) -#define ON_RB(node) ((node)->rb_color != RB_NONE) -#define rb_entry_crq(node) rb_entry((node), struct cfq_rq, rb_node) -#define rq_rb_key(rq) (rq)->sector - -static inline void cfq_del_crq_rb(struct cfq_queue *cfqq, struct cfq_rq *crq) -{ - if (ON_RB(&crq->rb_node)) { - cfqq->queued[rq_data_dir(crq->request)]--; - rb_erase(&crq->rb_node, &cfqq->sort_list); - crq->cfq_queue = NULL; - } -} - -static struct cfq_rq * -__cfq_add_crq_rb(struct cfq_queue *cfqq, struct cfq_rq *crq) -{ - struct rb_node **p = &cfqq->sort_list.rb_node; - struct rb_node *parent = NULL; - struct cfq_rq *__crq; - - while (*p) { - parent = *p; - __crq = rb_entry_crq(parent); - - if (crq->rb_key < __crq->rb_key) - p = &(*p)->rb_left; - else if (crq->rb_key > __crq->rb_key) - p = &(*p)->rb_right; - else - return __crq; - } - - rb_link_node(&crq->rb_node, parent, p); - return 0; -} - -static void -cfq_add_crq_rb(struct cfq_data *cfqd, struct cfq_queue *cfqq,struct cfq_rq *crq) -{ - struct request *rq = crq->request; - struct cfq_rq *__alias; - - crq->rb_key = rq_rb_key(rq); - cfqq->queued[rq_data_dir(rq)]++; -retry: - __alias = __cfq_add_crq_rb(cfqq, crq); - if (!__alias) { - rb_insert_color(&crq->rb_node, &cfqq->sort_list); - crq->cfq_queue = cfqq; - return; - } - - cfq_del_crq_rb(cfqq, __alias); - cfq_dispatch_sort(cfqd->dispatch, __alias); - goto retry; -} - -static struct request * -cfq_find_rq_rb(struct cfq_data *cfqd, sector_t sector) -{ - struct cfq_queue *cfqq = cfq_find_cfq_hash(cfqd, current->tgid); - struct rb_node *n; - - if (!cfqq) - goto out; - - n = cfqq->sort_list.rb_node; - while (n) { - struct cfq_rq *crq = rb_entry_crq(n); - - if (sector < crq->rb_key) - n = n->rb_left; - else if (sector > crq->rb_key) - n = n->rb_right; - else - return crq->request; - } - -out: - return NULL; -} - -static void cfq_remove_request(request_queue_t *q, struct request *rq) -{ - struct cfq_data *cfqd = q->elevator.elevator_data; - struct cfq_rq *crq = RQ_DATA(rq); - - if (crq) { - struct cfq_queue *cfqq = crq->cfq_queue; - - cfq_remove_merge_hints(q, crq); - list_del_init(&rq->queuelist); - - if (cfqq) { - cfq_del_crq_rb(cfqq, crq); - - if (RB_EMPTY(&cfqq->sort_list)) - cfq_put_queue(cfqd, cfqq); - } - } -} - -static int -cfq_merge(request_queue_t *q, struct request **req, struct bio *bio) -{ - struct cfq_data *cfqd = q->elevator.elevator_data; - struct request *__rq; - int ret; - - ret = elv_try_last_merge(q, bio); - if (ret != ELEVATOR_NO_MERGE) { - __rq = q->last_merge; - goto out_insert; - } - - __rq = cfq_find_rq_hash(cfqd, bio->bi_sector); - if (__rq) { - BUG_ON(__rq->sector + __rq->nr_sectors != bio->bi_sector); - - if (elv_rq_merge_ok(__rq, bio)) { - ret = ELEVATOR_BACK_MERGE; - goto out; - } - } - - __rq = cfq_find_rq_rb(cfqd, bio->bi_sector + bio_sectors(bio)); - if (__rq) { - if (elv_rq_merge_ok(__rq, bio)) { - ret = ELEVATOR_FRONT_MERGE; - goto out; - } - } - - return ELEVATOR_NO_MERGE; -out: - q->last_merge = __rq; -out_insert: - *req = __rq; - return ret; -} - -static void cfq_merged_request(request_queue_t *q, struct request *req) -{ - struct cfq_data *cfqd = q->elevator.elevator_data; - struct cfq_rq *crq = RQ_DATA(req); - - cfq_del_crq_hash(crq); - cfq_add_crq_hash(cfqd, crq); - - if (ON_RB(&crq->rb_node) && (rq_rb_key(req) != crq->rb_key)) { - struct cfq_queue *cfqq = crq->cfq_queue; - - cfq_del_crq_rb(cfqq, crq); - cfq_add_crq_rb(cfqd, cfqq, crq); - } - - q->last_merge = req; -} - -static void -cfq_merged_requests(request_queue_t *q, struct request *req, - struct request *next) -{ - cfq_merged_request(q, req); - cfq_remove_request(q, next); -} - -static void cfq_dispatch_sort(struct list_head *head, struct cfq_rq *crq) -{ - struct list_head *entry = head; - struct request *__rq; - - if (!list_empty(head)) { - __rq = list_entry_rq(head->next); - - if (crq->request->sector < __rq->sector) { - entry = head->prev; - goto link; - } - } - - while ((entry = entry->prev) != head) { - __rq = list_entry_rq(entry); - - if (crq->request->sector <= __rq->sector) - break; - } - -link: - list_add_tail(&crq->request->queuelist, entry); -} - -static inline void -__cfq_dispatch_requests(request_queue_t *q, struct cfq_data *cfqd, - struct cfq_queue *cfqq) -{ - struct cfq_rq *crq = rb_entry_crq(rb_first(&cfqq->sort_list)); - - cfq_del_crq_rb(cfqq, crq); - cfq_remove_merge_hints(q, crq); - cfq_dispatch_sort(cfqd->dispatch, crq); -} - -static int cfq_dispatch_requests(request_queue_t *q, struct cfq_data *cfqd) -{ - struct cfq_queue *cfqq; - struct list_head *entry, *tmp; - int ret, queued, good_queues; - - if (list_empty(&cfqd->rr_list)) - return 0; - - queued = ret = 0; -restart: - good_queues = 0; - list_for_each_safe(entry, tmp, &cfqd->rr_list) { - cfqq = list_entry_cfqq(cfqd->rr_list.next); - - BUG_ON(RB_EMPTY(&cfqq->sort_list)); - - __cfq_dispatch_requests(q, cfqd, cfqq); - - if (RB_EMPTY(&cfqq->sort_list)) - cfq_put_queue(cfqd, cfqq); - else - good_queues++; - - queued++; - ret = 1; - } - - if ((queued < cfq_quantum) && good_queues) - goto restart; - - return ret; -} - -static struct request *cfq_next_request(request_queue_t *q) -{ - struct cfq_data *cfqd = q->elevator.elevator_data; - struct request *rq; - - if (!list_empty(cfqd->dispatch)) { - struct cfq_rq *crq; -dispatch: - rq = list_entry_rq(cfqd->dispatch->next); - - crq = RQ_DATA(rq); - if (crq) - cfq_remove_merge_hints(q, crq); - - return rq; - } - - if (cfq_dispatch_requests(q, cfqd)) - goto dispatch; - - return NULL; -} - -static inline struct cfq_queue * -__cfq_find_cfq_hash(struct cfq_data *cfqd, int pid, const int hashval) -{ - struct list_head *hash_list = &cfqd->cfq_hash[hashval]; - struct list_head *entry; - - list_for_each(entry, hash_list) { - struct cfq_queue *__cfqq = list_entry_qhash(entry); - - if (__cfqq->pid == pid) - return __cfqq; - } - - return NULL; -} - -static struct cfq_queue *cfq_find_cfq_hash(struct cfq_data *cfqd, int pid) -{ - const int hashval = hash_long(current->tgid, CFQ_QHASH_SHIFT); - - return __cfq_find_cfq_hash(cfqd, pid, hashval); -} - -static void cfq_put_queue(struct cfq_data *cfqd, struct cfq_queue *cfqq) -{ - cfqd->busy_queues--; - list_del(&cfqq->cfq_list); - list_del(&cfqq->cfq_hash); - mempool_free(cfqq, cfq_mpool); -} - -static struct cfq_queue *cfq_get_queue(struct cfq_data *cfqd, int pid) -{ - const int hashval = hash_long(current->tgid, CFQ_QHASH_SHIFT); - struct cfq_queue *cfqq = __cfq_find_cfq_hash(cfqd, pid, hashval); - - if (!cfqq) { - cfqq = mempool_alloc(cfq_mpool, GFP_NOIO); - - INIT_LIST_HEAD(&cfqq->cfq_hash); - INIT_LIST_HEAD(&cfqq->cfq_list); - RB_CLEAR_ROOT(&cfqq->sort_list); - - cfqq->pid = pid; - cfqq->queued[0] = cfqq->queued[1] = 0; - list_add(&cfqq->cfq_hash, &cfqd->cfq_hash[hashval]); - } - - return cfqq; -} - -static void cfq_enqueue(struct cfq_data *cfqd, struct cfq_rq *crq) -{ - struct cfq_queue *cfqq; - - cfqq = cfq_get_queue(cfqd, current->tgid); - - cfq_add_crq_rb(cfqd, cfqq, crq); - - if (list_empty(&cfqq->cfq_list)) { - list_add(&cfqq->cfq_list, &cfqd->rr_list); - cfqd->busy_queues++; - } -} - -static void -cfq_insert_request(request_queue_t *q, struct request *rq, int where) -{ - struct cfq_data *cfqd = q->elevator.elevator_data; - struct cfq_rq *crq = RQ_DATA(rq); - - switch (where) { - case ELEVATOR_INSERT_BACK: - while (cfq_dispatch_requests(q, cfqd)) - ; - list_add_tail(&rq->queuelist, cfqd->dispatch); - break; - case ELEVATOR_INSERT_FRONT: - list_add(&rq->queuelist, cfqd->dispatch); - break; - case ELEVATOR_INSERT_SORT: - BUG_ON(!blk_fs_request(rq)); - cfq_enqueue(cfqd, crq); - break; - default: - printk("%s: bad insert point %d\n", __FUNCTION__,where); - return; - } - - if (rq_mergeable(rq)) { - cfq_add_crq_hash(cfqd, crq); - - if (!q->last_merge) - q->last_merge = rq; - } -} - -static int cfq_queue_empty(request_queue_t *q) -{ - struct cfq_data *cfqd = q->elevator.elevator_data; - - if (list_empty(cfqd->dispatch) && list_empty(&cfqd->rr_list)) - return 1; - - return 0; -} - -static struct request * -cfq_former_request(request_queue_t *q, struct request *rq) -{ - struct cfq_rq *crq = RQ_DATA(rq); - struct rb_node *rbprev = rb_prev(&crq->rb_node); - - if (rbprev) - return rb_entry_crq(rbprev)->request; - - return NULL; -} - -static struct request * -cfq_latter_request(request_queue_t *q, struct request *rq) -{ - struct cfq_rq *crq = RQ_DATA(rq); - struct rb_node *rbnext = rb_next(&crq->rb_node); - - if (rbnext) - return rb_entry_crq(rbnext)->request; - - return NULL; -} - -static int cfq_may_queue(request_queue_t *q, int rw) -{ - struct cfq_data *cfqd = q->elevator.elevator_data; - struct cfq_queue *cfqq; - int ret = 1; - - if (!cfqd->busy_queues) - goto out; - - cfqq = cfq_find_cfq_hash(cfqd, current->tgid); - if (cfqq) { - int limit = (q->nr_requests - cfq_queued) / cfqd->busy_queues; - - if (limit < 3) - limit = 3; - else if (limit > cfqd->max_queued) - limit = cfqd->max_queued; - - if (cfqq->queued[rw] > limit) - ret = 0; - } -out: - return ret; -} - -static void cfq_put_request(request_queue_t *q, struct request *rq) -{ - struct cfq_data *cfqd = q->elevator.elevator_data; - struct cfq_rq *crq = RQ_DATA(rq); - - if (crq) { - BUG_ON(q->last_merge == rq); - BUG_ON(ON_MHASH(crq)); - - mempool_free(crq, cfqd->crq_pool); - rq->elevator_private = NULL; - } -} - -static int cfq_set_request(request_queue_t *q, struct request *rq, int gfp_mask) -{ - struct cfq_data *cfqd = q->elevator.elevator_data; - struct cfq_rq *crq = mempool_alloc(cfqd->crq_pool, gfp_mask); - - if (crq) { - RB_CLEAR(&crq->rb_node); - crq->request = rq; - crq->cfq_queue = NULL; - INIT_LIST_HEAD(&crq->hash); - rq->elevator_private = crq; - return 0; - } - - return 1; -} - -static void cfq_exit(request_queue_t *q, elevator_t *e) -{ - struct cfq_data *cfqd = e->elevator_data; - - e->elevator_data = NULL; - mempool_destroy(cfqd->crq_pool); - kfree(cfqd->crq_hash); - kfree(cfqd->cfq_hash); - kfree(cfqd); -} - -static int cfq_init(request_queue_t *q, elevator_t *e) -{ - struct cfq_data *cfqd; - int i; - - cfqd = kmalloc(sizeof(*cfqd), GFP_KERNEL); - if (!cfqd) - return -ENOMEM; - - memset(cfqd, 0, sizeof(*cfqd)); - INIT_LIST_HEAD(&cfqd->rr_list); - - cfqd->crq_hash = kmalloc(sizeof(struct list_head) * CFQ_MHASH_ENTRIES, GFP_KERNEL); - if (!cfqd->crq_hash) - goto out_crqhash; - - cfqd->cfq_hash = kmalloc(sizeof(struct list_head) * CFQ_QHASH_ENTRIES, GFP_KERNEL); - if (!cfqd->cfq_hash) - goto out_cfqhash; - - cfqd->crq_pool = mempool_create(BLKDEV_MIN_RQ, mempool_alloc_slab, mempool_free_slab, crq_pool); - if (!cfqd->crq_pool) - goto out_crqpool; - - for (i = 0; i < CFQ_MHASH_ENTRIES; i++) - INIT_LIST_HEAD(&cfqd->crq_hash[i]); - for (i = 0; i < CFQ_QHASH_ENTRIES; i++) - INIT_LIST_HEAD(&cfqd->cfq_hash[i]); - - cfqd->dispatch = &q->queue_head; - e->elevator_data = cfqd; - - /* - * just set it to some high value, we want anyone to be able to queue - * some requests. fairness is handled differently - */ - cfqd->max_queued = q->nr_requests; - q->nr_requests = 8192; - - return 0; -out_crqpool: - kfree(cfqd->cfq_hash); -out_cfqhash: - kfree(cfqd->crq_hash); -out_crqhash: - kfree(cfqd); - return -ENOMEM; -} - -static int __init cfq_slab_setup(void) -{ - crq_pool = kmem_cache_create("crq_pool", sizeof(struct cfq_rq), 0, 0, - NULL, NULL); - - if (!crq_pool) - panic("cfq_iosched: can't init crq pool\n"); - - cfq_pool = kmem_cache_create("cfq_pool", sizeof(struct cfq_queue), 0, 0, - NULL, NULL); - - if (!cfq_pool) - panic("cfq_iosched: can't init cfq pool\n"); - - cfq_mpool = mempool_create(64, mempool_alloc_slab, mempool_free_slab, cfq_pool); - - if (!cfq_mpool) - panic("cfq_iosched: can't init cfq mpool\n"); - - return 0; -} - -subsys_initcall(cfq_slab_setup); - -elevator_t iosched_cfq = { - .elevator_name = "cfq", - .elevator_merge_fn = cfq_merge, - .elevator_merged_fn = cfq_merged_request, - .elevator_merge_req_fn = cfq_merged_requests, - .elevator_next_req_fn = cfq_next_request, - .elevator_add_req_fn = cfq_insert_request, - .elevator_remove_req_fn = cfq_remove_request, - .elevator_queue_empty_fn = cfq_queue_empty, - .elevator_former_req_fn = cfq_former_request, - .elevator_latter_req_fn = cfq_latter_request, - .elevator_set_req_fn = cfq_set_request, - .elevator_put_req_fn = cfq_put_request, - .elevator_may_queue_fn = cfq_may_queue, - .elevator_init_fn = cfq_init, - .elevator_exit_fn = cfq_exit, -}; - -EXPORT_SYMBOL(iosched_cfq); diff --git a/drivers/block/cfq-iosched.c b/drivers/block/cfq-iosched.c index 7b45a805d..70d66c5c9 100644 --- a/drivers/block/cfq-iosched.c +++ b/drivers/block/cfq-iosched.c @@ -39,8 +39,6 @@ #error Cannot support this many io priority levels #endif -#define LIMIT_DEBUG 1 - /* * tunables */ @@ -52,6 +50,10 @@ static int cfq_queued = 4; static int cfq_grace_rt = HZ / 100 ?: 1; static int cfq_grace_idle = HZ / 10; +#define CFQ_EPOCH 1000000000 +#define CFQ_SECTORATE 1000 +#define CFQ_HMAX_PCT 80 + #define CFQ_QHASH_SHIFT 6 #define CFQ_QHASH_ENTRIES (1 << CFQ_QHASH_SHIFT) #define list_entry_qhash(entry) hlist_entry((entry), struct cfq_queue, cfq_hash) @@ -69,13 +71,6 @@ static int cfq_grace_idle = HZ / 10; #define cfq_account_io(crq) \ ((crq)->ioprio != IOPRIO_IDLE && (crq)->ioprio != IOPRIO_RT) -/* define to be 50 ms for now; make tunable later */ -#define CFQ_EPOCH 50000 -/* Needs to be made tunable right away, in MiB/s */ -#define CFQ_DISKBW 10 -/* Temporary global limit, as percent of available b/w, for each "class" */ -#define CFQ_TEMPLIM 10 - /* * defines how we distribute bandwidth (can be tgid, uid, etc) */ @@ -87,18 +82,22 @@ static int cfq_grace_idle = HZ / 10; */ #if defined(CONFIG_CKRM_RES_BLKIO) || defined(CONFIG_CKRM_RES_BLKIO_MODULE) -extern inline void *cki_hash_key(struct task_struct *tsk); -extern inline int cki_ioprio(struct task_struct *tsk); -#define cfq_hash_key(current) ((int)cki_hash_key((current))) -#define cfq_ioprio(current) (cki_ioprio((current))) +extern void *cki_hash_key(struct task_struct *tsk); +extern int cki_ioprio(struct task_struct *tsk); +extern void *cki_cfqpriv(struct task_struct *tsk); + +#define cfq_hash_key(tsk) ((int)cki_hash_key((tsk))) +#define cfq_ioprio(tsk) (cki_ioprio((tsk))) +#define cfq_cfqpriv(cfqd,tsk) (cki_cfqpriv((tsk))) #else -#define cfq_hash_key(current) ((current)->tgid) +#define cfq_hash_key(tsk) ((tsk)->tgid) +#define cfq_cfqpriv(cfqd,tsk) (&(((cfqd)->cid[(tsk)->ioprio]).cfqpriv)) /* * move to io_context */ -#define cfq_ioprio(current) ((current)->ioprio) +#define cfq_ioprio(tsk) ((tsk)->ioprio) #endif #define CFQ_WAIT_RT 0 @@ -125,16 +124,12 @@ struct io_prio_data { atomic_t cum_sectors_in,cum_sectors_out; atomic_t cum_queues_in,cum_queues_out; -#ifdef LIMIT_DEBUG - int nskip; - unsigned long navsec; - unsigned long csectorate; - unsigned long lsectorate; -#endif + cfqlim_t cfqpriv; /* data for enforcing limits */ struct list_head prio_list; int last_rq; int last_sectors; + }; /* @@ -179,8 +174,9 @@ struct cfq_data { unsigned int cfq_grace_rt; unsigned int cfq_grace_idle; - unsigned long cfq_epoch; /* duration for limit enforcement */ - unsigned long cfq_epochsectors; /* max sectors dispatchable/epoch */ + unsigned int cfq_epoch; + unsigned int cfq_hmax_pct; + unsigned int cfq_qsectorate; }; /* @@ -194,14 +190,34 @@ struct cfq_queue { int queued[2]; int ioprio; + /* limit related settings/stats obtained + either from io_prio_data or ckrm I/O class + */ + struct cfqlim *cfqpriv; + + u64 epstart; /* current epoch's starting timestamp (ns) */ + u64 epsector[2]; /* Total sectors dispatched in [0] previous + * and [1] current epoch + */ + unsigned long avsec; /* avg sectors dispatched/epoch */ - unsigned long long lastime; /* timestamp of last request served */ - unsigned long sectorate; /* limit for sectors served/epoch */ +// unsigned long long lastime; /* timestamp of last request served */ +// unsigned long sectorate; /* limit for sectors served/epoch */ int skipped; /* queue skipped at last dispatch ? */ + + /* Per queue timer to suspend/resume queue from processing */ + struct timer_list timer; + unsigned long wait_end; + unsigned long flags; + struct work_struct work; + + struct cfq_data *cfqd; }; + + /* - * per-request structure + * Per-request structure */ struct cfq_rq { struct cfq_queue *cfq_queue; @@ -516,69 +532,101 @@ link: list_add_tail(&crq->request->queuelist, entry); } -/* - * remove from io scheduler core and put on dispatch list for service - */ +struct cfq_queue *dcfqq; +u64 dtmp; + + + +/* Over how many ns is sectorate defined */ +#define NS4SCALE (100000000) + static inline int -__cfq_dispatch_requests(request_queue_t *q, struct cfq_data *cfqd, - struct cfq_queue *cfqq) +__cfq_check_limit(struct cfq_data *cfqd,struct cfq_queue *cfqq, int dontskip) { struct cfq_rq *crq; - unsigned long long ts, gap; - unsigned long newavsec; + unsigned long long ts, gap, epoch, tmp; + unsigned long newavsec, sectorate; crq = rb_entry_crq(rb_first(&cfqq->sort_list)); -#if 1 - /* Determine if queue should be skipped for being overshare */ ts = sched_clock(); - gap = ts - cfqq->lastime; -#ifdef LIMIT_DEBUG - cfqq->sectorate = (cfqd->cfq_epochsectors - * CFQ_TEMPLIM)/100; - -#endif - if ((gap >= cfqd->cfq_epoch) || (gap < 0)) { - cfqq->avsec = crq->nr_sectors ; - cfqq->lastime = ts; + gap = ts - cfqq->epstart; + epoch = cfqd->cfq_epoch; + + sectorate = atomic_read(&cfqq->cfqpriv->sectorate); +// sectorate = atomic_read(&(cfqd->cid[crq->ioprio].sectorate)); + + dcfqq = cfqq; + + if ((gap >= epoch) || (gap < 0)) { + + if (gap >= (epoch << 1)) { + cfqq->epsector[0] = 0; + cfqq->epstart = ts ; + } else { + cfqq->epsector[0] = cfqq->epsector[1]; + cfqq->epstart += epoch; + } + cfqq->epsector[1] = 0; + gap = ts - cfqq->epstart; + + tmp = (cfqq->epsector[0] + crq->nr_sectors) * NS4SCALE; + do_div(tmp,epoch+gap); + + cfqq->avsec = (unsigned long)tmp; + cfqq->skipped = 0; + cfqq->epsector[1] += crq->nr_sectors; + + cfqq->cfqpriv->navsec = cfqq->avsec; + cfqq->cfqpriv->sec[0] = cfqq->epsector[0]; + cfqq->cfqpriv->sec[1] = cfqq->epsector[1]; + cfqq->cfqpriv->timedout++; + /* + cfqd->cid[crq->ioprio].navsec = cfqq->avsec; + cfqd->cid[crq->ioprio].sec[0] = cfqq->epsector[0]; + cfqd->cid[crq->ioprio].sec[1] = cfqq->epsector[1]; + cfqd->cid[crq->ioprio].timedout++; + */ + return 0; } else { - u64 tmp; - /* Age old average and accumalate request to be served */ - -// tmp = (u64) (cfqq->avsec * gap) ; -// do_div(tmp, cfqd->cfq_epoch); - newavsec = (unsigned long)(cfqq->avsec >> 1) + crq->nr_sectors; -// if (crq->ioprio >= 0 && crq->ioprio <= 20) -// cfqd->cid[crq->ioprio].lsectorate = newavsec; -// atomic_set(&(cfqd->cid[crq->ioprio].lsectorate), -// newavsec); - - if ((newavsec < cfqq->sectorate) || cfqq->skipped) { + + tmp = (cfqq->epsector[0] + cfqq->epsector[1] + crq->nr_sectors) + * NS4SCALE; + do_div(tmp,epoch+gap); + + newavsec = (unsigned long)tmp; + if ((newavsec < sectorate) || dontskip) { cfqq->avsec = newavsec ; - cfqq->lastime = ts; cfqq->skipped = 0; + cfqq->epsector[1] += crq->nr_sectors; + cfqq->cfqpriv->navsec = cfqq->avsec; + cfqq->cfqpriv->sec[1] = cfqq->epsector[1]; + /* + cfqd->cid[crq->ioprio].navsec = cfqq->avsec; + cfqd->cid[crq->ioprio].sec[1] = cfqq->epsector[1]; + */ } else { - /* queue over share ; skip once */ cfqq->skipped = 1; -#ifdef LIMIT_DEBUG -// atomic_inc(&(cfqd->cid[crq->ioprio].nskip)); -// if (crq->ioprio >= 0 && crq->ioprio <= 20) -// cfqd->cid[crq->ioprio].nskip++; -#endif - return 0; + /* pause q's processing till avsec drops to + cfq_hmax_pct % of its value */ + tmp = (epoch+gap) * (100-cfqd->cfq_hmax_pct); + do_div(tmp,1000000*cfqd->cfq_hmax_pct); + cfqq->wait_end = jiffies+msecs_to_jiffies(tmp); } - } -#endif + } +} -#ifdef LIMIT_DEBUG -// if (crq->ioprio >= 0 && crq->ioprio <= 20) { -// cfqd->cid[crq->ioprio].navsec = cfqq->avsec; -// cfqd->cid[crq->ioprio].csectorate = cfqq->sectorate; -// } +/* + * remove from io scheduler core and put on dispatch list for service + */ +static inline int +__cfq_dispatch_requests(request_queue_t *q, struct cfq_data *cfqd, + struct cfq_queue *cfqq) +{ + struct cfq_rq *crq; + + crq = rb_entry_crq(rb_first(&cfqq->sort_list)); -// atomic_set(&(cfqd->cid[crq->ioprio].navsec),cfqq->avsec); -// atomic_set(&(cfqd->cid[crq->ioprio].csectorate),cfqq->sectorate); -#endif cfq_dispatch_sort(cfqd, cfqq, crq); /* @@ -593,44 +641,83 @@ cfq_dispatch_requests(request_queue_t *q, int prio, int max_rq, int max_sectors) { struct cfq_data *cfqd = q->elevator.elevator_data; struct list_head *plist = &cfqd->cid[prio].rr_list; + struct cfq_queue *cfqq; struct list_head *entry, *nxt; int q_rq, q_io; - int ret ; + int first_round,busy_queues,busy_unlimited; + /* * for each queue at this prio level, dispatch a request */ q_rq = q_io = 0; + first_round=1; + restart: + busy_unlimited = 0; + busy_queues = 0; list_for_each_safe(entry, nxt, plist) { - struct cfq_queue *cfqq = list_entry_cfqq(entry); + cfqq = list_entry_cfqq(entry); BUG_ON(RB_EMPTY(&cfqq->sort_list)); + busy_queues++; - ret = __cfq_dispatch_requests(q, cfqd, cfqq); - if (ret <= 0) { - continue; /* skip queue */ - /* can optimize more by moving q to end of plist ? */ + + if (first_round || busy_unlimited) + __cfq_check_limit(cfqd,cfqq,0); + else + __cfq_check_limit(cfqd,cfqq,1); + + if (cfqq->skipped) { + cfqq->cfqpriv->nskip++; + /* cfqd->cid[prio].nskip++; */ + busy_queues--; + if (time_before(jiffies, cfqq->wait_end)) { + list_del(&cfqq->cfq_list); + mod_timer(&cfqq->timer,cfqq->wait_end); + } + continue; } - q_io += ret ; - q_rq++ ; + busy_unlimited++; + + q_io += __cfq_dispatch_requests(q, cfqd, cfqq); + q_rq++; - if (RB_EMPTY(&cfqq->sort_list)) + if (RB_EMPTY(&cfqq->sort_list)) { + busy_unlimited--; + busy_queues--; cfq_put_queue(cfqd, cfqq); - /* - * if we hit the queue limit, put the string of serviced - * queues at the back of the pending list - */ + } + if (q_io >= max_sectors || q_rq >= max_rq) { +#if 0 struct list_head *prv = nxt->prev; if (prv != plist) { list_del(plist); list_add(plist, prv); } +#endif break; } } + if ((q_io < max_sectors) && (q_rq < max_rq) && + (busy_queues || first_round)) + { + first_round = 0; + goto restart; + } else { + /* + * if we hit the queue limit, put the string of serviced + * queues at the back of the pending list + */ + struct list_head *prv = nxt->prev; + if (prv != plist) { + list_del(plist); + list_add(plist, prv); + } + } + cfqd->cid[prio].last_rq = q_rq; cfqd->cid[prio].last_sectors = q_io; return q_rq; @@ -806,6 +893,29 @@ static void cfq_put_queue(struct cfq_data *cfqd, struct cfq_queue *cfqq) mempool_free(cfqq, cfq_mpool); } +static void cfq_pauseq_timer(unsigned long data) +{ + struct cfq_queue *cfqq = (struct cfq_queue *) data; + kblockd_schedule_work(&cfqq->work); +} + +static void cfq_pauseq_work(void *data) +{ + struct cfq_queue *cfqq = (struct cfq_queue *) data; + struct cfq_data *cfqd = cfqq->cfqd; + request_queue_t *q = cfqd->queue; + unsigned long flags; + + spin_lock_irqsave(q->queue_lock, flags); + list_add_tail(&cfqq->cfq_list,&cfqd->cid[cfqq->ioprio].rr_list); + cfqq->skipped = 0; + if (cfq_next_request(q)) + q->request_fn(q); + spin_unlock_irqrestore(q->queue_lock, flags); + + //del_timer(&cfqq->timer); +} + static struct cfq_queue *__cfq_get_queue(struct cfq_data *cfqd, int hashkey, int gfp_mask) { @@ -833,9 +943,22 @@ retry: INIT_LIST_HEAD(&cfqq->cfq_list); cfqq->hash_key = cfq_hash_key(current); cfqq->ioprio = cfq_ioprio(current); - cfqq->avsec = 0 ; - cfqq->lastime = sched_clock(); - cfqq->sectorate = (cfqd->cfq_epochsectors * CFQ_TEMPLIM)/100; + + cfqq->cfqpriv = cfq_cfqpriv(cfqd,current); + if (!cfqq->cfqpriv) + cfqq->cfqpriv = &((cfqd->cid[cfqq->ioprio]).cfqpriv); + + cfqq->epstart = sched_clock(); + /* epsector, avsec, skipped initialized to zero by memset */ + + init_timer(&cfqq->timer); + cfqq->timer.function = cfq_pauseq_timer; + cfqq->timer.data = (unsigned long) cfqq; + + INIT_WORK(&cfqq->work, cfq_pauseq_work, cfqq); + + cfqq->cfqd = cfqd ; + hlist_add_head(&cfqq->cfq_hash, &cfqd->cfq_hash[hashval]); } @@ -1132,6 +1255,8 @@ static void cfq_exit(request_queue_t *q, elevator_t *e) kfree(cfqd); } + + static void cfq_timer(unsigned long data) { struct cfq_data *cfqd = (struct cfq_data *) data; @@ -1182,12 +1307,12 @@ static int cfq_init(request_queue_t *q, elevator_t *e) atomic_set(&cid->cum_sectors_out,0); atomic_set(&cid->cum_queues_in,0); atomic_set(&cid->cum_queues_out,0); -#if 0 - atomic_set(&cid->nskip,0); - atomic_set(&cid->navsec,0); - atomic_set(&cid->csectorate,0); - atomic_set(&cid->lsectorate,0); -#endif + + + atomic_set(&((cid->cfqpriv).sectorate),CFQ_SECTORATE); + (cid->cfqpriv).nskip = 0; + (cid->cfqpriv).navsec = 0; + (cid->cfqpriv).timedout = 0; } cfqd->crq_hash = kmalloc(sizeof(struct hlist_head) * CFQ_MHASH_ENTRIES, @@ -1217,6 +1342,9 @@ static int cfq_init(request_queue_t *q, elevator_t *e) cfqd->cfq_idle_quantum_io = cfq_idle_quantum_io; cfqd->cfq_grace_rt = cfq_grace_rt; cfqd->cfq_grace_idle = cfq_grace_idle; + + cfqd->cfq_epoch = CFQ_EPOCH; + cfqd->cfq_hmax_pct = CFQ_HMAX_PCT; q->nr_requests <<= 2; @@ -1224,14 +1352,6 @@ static int cfq_init(request_queue_t *q, elevator_t *e) e->elevator_data = cfqd; cfqd->queue = q; - cfqd->cfq_epoch = CFQ_EPOCH; - if (q->hardsect_size) - cfqd->cfq_epochsectors = ((CFQ_DISKBW * 1000000)/ - q->hardsect_size)* (1000000 / CFQ_EPOCH); - else - cfqd->cfq_epochsectors = ((CFQ_DISKBW * 1000000)/512) - * (1000000 / CFQ_EPOCH) ; - return 0; out_crqpool: kfree(cfqd->cfq_hash); @@ -1302,6 +1422,8 @@ SHOW_FUNCTION(cfq_idle_quantum_io_show, cfqd->cfq_idle_quantum_io); SHOW_FUNCTION(cfq_queued_show, cfqd->cfq_queued); SHOW_FUNCTION(cfq_grace_rt_show, cfqd->cfq_grace_rt); SHOW_FUNCTION(cfq_grace_idle_show, cfqd->cfq_grace_idle); +SHOW_FUNCTION(cfq_epoch_show, cfqd->cfq_epoch); +SHOW_FUNCTION(cfq_hmax_pct_show, cfqd->cfq_hmax_pct); #undef SHOW_FUNCTION #define STORE_FUNCTION(__FUNC, __PTR, MIN, MAX) \ @@ -1321,63 +1443,38 @@ STORE_FUNCTION(cfq_idle_quantum_io_store, &cfqd->cfq_idle_quantum_io, 4, INT_MAX STORE_FUNCTION(cfq_queued_store, &cfqd->cfq_queued, 1, INT_MAX); STORE_FUNCTION(cfq_grace_rt_store, &cfqd->cfq_grace_rt, 0, INT_MAX); STORE_FUNCTION(cfq_grace_idle_store, &cfqd->cfq_grace_idle, 0, INT_MAX); +STORE_FUNCTION(cfq_epoch_store, &cfqd->cfq_epoch, 0, INT_MAX); +STORE_FUNCTION(cfq_hmax_pct_store, &cfqd->cfq_hmax_pct, 1, 100); #undef STORE_FUNCTION -static ssize_t cfq_epoch_show(struct cfq_data *cfqd, char *page) -{ - return sprintf(page, "%lu\n", cfqd->cfq_epoch); -} - -static ssize_t cfq_epoch_store(struct cfq_data *cfqd, const char *page, size_t count) -{ - char *p = (char *) page; - cfqd->cfq_epoch = simple_strtoul(p, &p, 10); - return count; -} - -static ssize_t cfq_epochsectors_show(struct cfq_data *cfqd, char *page) -{ - return sprintf(page, "%lu\n", cfqd->cfq_epochsectors); -} - -static ssize_t -cfq_epochsectors_store(struct cfq_data *cfqd, const char *page, size_t count) -{ - char *p = (char *) page; - cfqd->cfq_epochsectors = simple_strtoul(p, &p, 10); - return count; -} - /* Additional entries to get priority level data */ static ssize_t cfq_prio_show(struct cfq_data *cfqd, char *page, unsigned int priolvl) { - int r1,r2,s1,s2,q1,q2; + //int r1,r2,s1,s2,q1,q2; if (!(priolvl >= IOPRIO_IDLE && priolvl <= IOPRIO_RT)) return 0; + /* r1 = (int)atomic_read(&(cfqd->cid[priolvl].cum_rq_in)); r2 = (int)atomic_read(&(cfqd->cid[priolvl].cum_rq_out)); s1 = (int)atomic_read(&(cfqd->cid[priolvl].cum_sectors_in)); s2 = (int)atomic_read(&(cfqd->cid[priolvl].cum_sectors_out)); q1 = (int)atomic_read(&(cfqd->cid[priolvl].cum_queues_in)); q2 = (int)atomic_read(&(cfqd->cid[priolvl].cum_queues_out)); - - return sprintf(page,"skip %d avsec %lu rate %lu new %lu" - "rq (%d,%d) sec (%d,%d) q (%d,%d)\n", - cfqd->cid[priolvl].nskip, - cfqd->cid[priolvl].navsec, - cfqd->cid[priolvl].csectorate, - cfqd->cid[priolvl].lsectorate, -// atomic_read(&cfqd->cid[priolvl].nskip), -// atomic_read(&cfqd->cid[priolvl].navsec), -// atomic_read(&cfqd->cid[priolvl].csectorate), -// atomic_read(&cfqd->cid[priolvl].lsectorate), - r1,r2, - s1,s2, - q1,q2); + */ + + return sprintf(page,"skip %d timdout %d avsec %lu rate %ld " + " sec0 %lu sec1 %lu\n", + cfqd->cid[priolvl].cfqpriv.nskip, + cfqd->cid[priolvl].cfqpriv.timedout, + cfqd->cid[priolvl].cfqpriv.navsec, + atomic_read(&(cfqd->cid[priolvl].cfqpriv.sectorate)), + (unsigned long)cfqd->cid[priolvl].cfqpriv.sec[0], + (unsigned long)cfqd->cid[priolvl].cfqpriv.sec[1]); + } #define SHOW_PRIO_DATA(__PRIOLVL) \ @@ -1411,12 +1508,25 @@ SHOW_PRIO_DATA(20); static ssize_t cfq_prio_store(struct cfq_data *cfqd, const char *page, size_t count, int priolvl) { + + char *p = (char *) page; + int val; + + val = (int) simple_strtoul(p, &p, 10); + + atomic_set(&(cfqd->cid[priolvl].cfqpriv.sectorate),val); + cfqd->cid[priolvl].cfqpriv.nskip = 0; + cfqd->cid[priolvl].cfqpriv.navsec = 0; + cfqd->cid[priolvl].cfqpriv.timedout = 0; + +#if 0 atomic_set(&(cfqd->cid[priolvl].cum_rq_in),0); atomic_set(&(cfqd->cid[priolvl].cum_rq_out),0); atomic_set(&(cfqd->cid[priolvl].cum_sectors_in),0); atomic_set(&(cfqd->cid[priolvl].cum_sectors_out),0); atomic_set(&(cfqd->cid[priolvl].cum_queues_in),0); atomic_set(&(cfqd->cid[priolvl].cum_queues_out),0); +#endif return count; } @@ -1491,10 +1601,10 @@ static struct cfq_fs_entry cfq_epoch_entry = { .show = cfq_epoch_show, .store = cfq_epoch_store, }; -static struct cfq_fs_entry cfq_epochsectors_entry = { - .attr = {.name = "epochsectors", .mode = S_IRUGO | S_IWUSR }, - .show = cfq_epochsectors_show, - .store = cfq_epochsectors_store, +static struct cfq_fs_entry cfq_hmax_pct_entry = { + .attr = {.name = "hmaxpct", .mode = S_IRUGO | S_IWUSR }, + .show = cfq_hmax_pct_show, + .store = cfq_hmax_pct_store, }; #define P_0_STR "p0" @@ -1558,7 +1668,7 @@ static struct attribute *default_attrs[] = { &cfq_grace_rt_entry.attr, &cfq_grace_idle_entry.attr, &cfq_epoch_entry.attr, - &cfq_epochsectors_entry.attr, + &cfq_hmax_pct_entry.attr, &cfq_prio_0_entry.attr, &cfq_prio_1_entry.attr, &cfq_prio_2_entry.attr, diff --git a/drivers/block/ckrm-io.c b/drivers/block/ckrm-io.c index 7edfce727..89910268f 100644 --- a/drivers/block/ckrm-io.c +++ b/drivers/block/ckrm-io.c @@ -35,14 +35,11 @@ #include #include -/* Tie to cfq priorities */ -#define CKI_IOPRIO_NORM IOPRIO_NORM +/* sectorate == 512 byte sectors served in CFQ_EPOCH ns*/ -/* Divisor to get fraction of bandwidth represented by an IOPRIO value */ -/* FIXME: Will not work if IOPRIO_NR > 100 */ -#define CKI_IOPRIO_DIV (IOPRIO_NR-1) -/* Minimum ioprio value to be assigned to a class */ -#define CKI_IOPRIO_MIN 1 +/* CKI_ROOTSECTORATE needs to be made configurable from outside */ +#define CKI_ROOTSECTORATE 100000 +#define CKI_MINSECTORATE 100 #define CKI_IOUSAGE_UNIT 512 @@ -52,7 +49,12 @@ typedef struct ckrm_io_stats{ unsigned long blksz; /* size of bandwidth unit */ atomic_t blkrd; /* read units submitted to DD */ atomic_t blkwr; /* write units submitted to DD */ - + + int nskip; /* # times q skipped */ + unsigned long navsec; /* avg sectors serviced */ + int timedout; /* # times gap > epoch */ + u64 sec[2]; /* sectors serviced in + prev & curr epochs */ } cki_stats_t; /* per class I/O statistics */ /* Note @@ -75,8 +77,12 @@ typedef struct ckrm_io_class { * in local units. */ + cfqlim_t cfqpriv; /* Data common with cfq priolvl's */ + + int cnt_guarantee; /* Allocation as parent */ int cnt_unused; /* Allocation to default subclass */ + int cnt_limit; /* Statistics, for class and default subclass */ cki_stats_t stats; @@ -85,19 +91,16 @@ typedef struct ckrm_io_class { } cki_icls_t; - /* Internal functions */ static inline void cki_reset_stats(cki_stats_t *usg); static inline void init_icls_one(cki_icls_t *icls); -static inline int cki_div(int *a, int b, int c); -//static inline int cki_recalc(cki_icls_t *icls, int rel2abs); static void cki_recalc_propagate(cki_icls_t *res, cki_icls_t *parres); /* External functions e.g. interface to ioscheduler */ void *cki_tsk_icls (struct task_struct *tsk); int cki_tsk_ioprio (struct task_struct *tsk); -extern void cki_cfq_set(icls_tsk_t tskicls, icls_ioprio_t tskioprio); +extern void cki_cfq_set(icls_tsk_t tskicls, icls_ioprio_t tskioprio, icls_tsk_t tskcfqpriv); /* CKRM Resource Controller API functions */ static void * cki_alloc(struct ckrm_core_class *this, @@ -139,45 +142,27 @@ static inline void init_icls_stats(cki_icls_t *icls) static inline void init_icls_one(cki_icls_t *icls) { - // Assign zero as initial guarantee otherwise creations - // could fail due to inadequate share - - //icls->shares.my_guarantee = - // (CKI_IOPRIO_MIN * CKRM_SHARE_DFLT_TOTAL_GUARANTEE) / - // CKI_IOPRIO_DIV ; - icls->shares.my_guarantee = 0; - icls->shares.my_limit = CKRM_SHARE_DFLT_TOTAL_GUARANTEE; - icls->shares.total_guarantee = CKRM_SHARE_DFLT_TOTAL_GUARANTEE; - icls->shares.max_limit = CKRM_SHARE_DFLT_TOTAL_GUARANTEE; + /* Zero initial guarantee for scalable creation of + multiple classes */ - icls->shares.unused_guarantee = icls->shares.total_guarantee - - icls->shares.my_guarantee; - icls->shares.cur_max_limit = CKRM_SHARE_DFLT_TOTAL_GUARANTEE; - - - icls->cnt_guarantee = icls->cnt_unused = IOPRIO_IDLE; + /* Try out a new set */ + + icls->shares.my_guarantee = CKRM_SHARE_DONTCARE; + icls->shares.my_limit = CKRM_SHARE_DONTCARE; + icls->shares.total_guarantee = CKRM_SHARE_DFLT_TOTAL_GUARANTEE; + icls->shares.max_limit = CKRM_SHARE_DFLT_MAX_LIMIT; + icls->shares.unused_guarantee = icls->shares.total_guarantee; + icls->shares.cur_max_limit = 0; - //Same rationale icls->ioprio = CKI_IOPRIO_MIN; - //IOPRIO_IDLE equivalence to zero my_guarantee (set above) relies - //on former being zero. + icls->cnt_guarantee = CKRM_SHARE_DONTCARE; + icls->cnt_unused = CKRM_SHARE_DONTCARE; + icls->cnt_limit = CKRM_SHARE_DONTCARE; init_icls_stats(icls); } - -static inline int cki_div(int *a, int b, int c) -{ - u64 temp = (u64) b * c ; - do_div(temp,CKI_IOPRIO_DIV); - *a = (int) temp; - - return 0; -} - - -/* Recalculate absolute shares from relative (rel2abs=1) - * or vice versa (rel2abs=0) - * Caller should have a lock on icls +/* Recalculate absolute shares from relative + * Caller should hold a lock on icls */ static void cki_recalc_propagate(cki_icls_t *res, cki_icls_t *parres) @@ -186,17 +171,17 @@ static void cki_recalc_propagate(cki_icls_t *res, cki_icls_t *parres) ckrm_core_class_t *child = NULL; cki_icls_t *childres; int resid = cki_rcbs.resid; + u64 temp; if (parres) { struct ckrm_shares *par = &parres->shares; struct ckrm_shares *self = &res->shares; - if (parres->cnt_guarantee == CKRM_SHARE_DONTCARE) { res->cnt_guarantee = CKRM_SHARE_DONTCARE; } else if (par->total_guarantee) { - u64 temp = (u64) self->my_guarantee * + temp = (u64) self->my_guarantee * parres->cnt_guarantee; do_div(temp, par->total_guarantee); res->cnt_guarantee = (int) temp; @@ -204,16 +189,36 @@ static void cki_recalc_propagate(cki_icls_t *res, cki_icls_t *parres) res->cnt_guarantee = 0; } + + if (parres->cnt_limit == CKRM_SHARE_DONTCARE) { + res->cnt_limit = CKRM_SHARE_DONTCARE; + atomic_set(&res->cfqpriv.sectorate,CKI_MINSECTORATE); + } else { + if (par->max_limit) { + temp = (u64) self->my_limit * + parres->cnt_limit; + do_div(temp, par->max_limit); + res->cnt_limit = (int) temp; + } else { + res->cnt_limit = 0; + } + atomic_set(&res->cfqpriv.sectorate,res->cnt_limit); + } + if (res->cnt_guarantee == CKRM_SHARE_DONTCARE) { res->cnt_unused = CKRM_SHARE_DONTCARE; - } else if (self->total_guarantee) { - u64 temp = (u64) self->unused_guarantee * - res->cnt_guarantee; - do_div(temp, self->total_guarantee); - res->cnt_unused = (int) temp; } else { - res->cnt_unused = 0; + if (self->total_guarantee) { + temp = (u64) self->unused_guarantee * + res->cnt_guarantee; + do_div(temp, self->total_guarantee); + res->cnt_unused = (int) temp; + } else { + res->cnt_unused = 0; + } + } + } // propagate to children ckrm_lock_hier(res->core); @@ -228,50 +233,6 @@ static void cki_recalc_propagate(cki_icls_t *res, cki_icls_t *parres) ckrm_unlock_hier(res->core); } -#if 0 -static inline int cki_recalc(cki_icls_t *icls, int rel2abs) -{ - u64 temp; - - if (icls->parent == NULL) { - /* Root, as parent, always gets all */ - - temp = icls->shares.my_guarantee * (IOPRIO_NR-1); - do_div(temp, icls->shares.total_guarantee); - - icls->total = IOPRIO_NR-1; - icls->ioprio = temp ; - icls->unused = icls->total - icls->ioprio; -// icls->unused = (IOPRIO_NR-1)-icls->ioprio; - - } else { - cki_icls_t *parres; - int partot ; - - parres = ckrm_get_res_class(icls->parent, - cki_rcbs.resid, - cki_icls_t); - if (!parres) { - printk(KERN_ERR "cki_recalc: error getting " - "resclass from core \n"); - return -EINVAL; - } - - - temp = (icls->shares.my_guarantee * - parres->total); - do_div(temp, parres->shares.total_guarantee); - - icls->ioprio = temp; - icls->unused = 0; - - } - - return 0; - -} -#endif - void *cki_tsk_icls(struct task_struct *tsk) { return (void *) ckrm_get_res_class(class_core(tsk->taskclass), @@ -279,12 +240,19 @@ void *cki_tsk_icls(struct task_struct *tsk) } int cki_tsk_ioprio(struct task_struct *tsk) +{ + /* Don't use I/O priorities for now */ + return IOPRIO_NORM; +} + +void *cki_tsk_cfqpriv(struct task_struct *tsk) { cki_icls_t *icls = ckrm_get_res_class(class_core(tsk->taskclass), cki_rcbs.resid, cki_icls_t); - return icls->cnt_unused; + return (void *)&(icls->cfqpriv); } + static void *cki_alloc(struct ckrm_core_class *core, struct ckrm_core_class *parent) { @@ -301,43 +269,13 @@ static void *cki_alloc(struct ckrm_core_class *core, icls->parent = parent; icls->shares_lock = SPIN_LOCK_UNLOCKED; - if (parent == NULL) { - - /* Root class gets same as "normal" CFQ priorities to - * retain compatibility of behaviour in the absence of - * other classes - */ - - icls->cnt_guarantee = icls->cnt_unused = IOPRIO_NR-1; - - /* Default gets normal, not minimum */ - //icls->unused = IOPRIO_NORM; - //icls->unused = icls->guarantee-icls->myguarantee; - //icls->limit = icls->mylimit = IOPRIO_NR; - - /* Compute shares in abstract units */ - icls->shares.total_guarantee = CKRM_SHARE_DFLT_TOTAL_GUARANTEE; - - // my_guarantee for root is meaningless. Set to default - icls->shares.my_guarantee = CKRM_SHARE_DFLT_TOTAL_GUARANTEE; + init_icls_one(icls); - icls->shares.unused_guarantee = - CKRM_SHARE_DFLT_TOTAL_GUARANTEE; - - //temp = (u64) icls->cnt_unused * icls->shares.total_guarantee; - //do_div(temp, CKI_IOPRIO_DIV); - // temp now has root's default's share - //icls->shares.unused_guarantee = - // icls->shares.total_guarantee - temp; - - icls->shares.my_limit = CKRM_SHARE_DFLT_TOTAL_GUARANTEE; - icls->shares.max_limit = CKRM_SHARE_DFLT_TOTAL_GUARANTEE; - icls->shares.cur_max_limit = CKRM_SHARE_DFLT_TOTAL_GUARANTEE; - - } else { - init_icls_one(icls); - /* No propagation to parent needed if icls' - initial share is zero */ + if (parent == NULL) { + icls->cnt_guarantee = CKI_ROOTSECTORATE; + icls->cnt_unused = CKI_ROOTSECTORATE; + icls->cnt_limit = CKI_ROOTSECTORATE; + atomic_set(&(icls->cfqpriv.sectorate),icls->cnt_limit); } try_module_get(THIS_MODULE); return icls; @@ -345,7 +283,10 @@ static void *cki_alloc(struct ckrm_core_class *core, static void cki_free(void *res) { - cki_icls_t *icls = res, *parres; + cki_icls_t *icls = res, *parres, *childres; + ckrm_core_class_t *child = NULL; + int maxlimit, resid = cki_rcbs.resid; + if (!res) return; @@ -361,9 +302,7 @@ static void cki_free(void *res) * */ - parres = ckrm_get_res_class(icls->parent, - cki_rcbs.resid, - cki_icls_t); + parres = ckrm_get_res_class(icls->parent, resid, cki_icls_t); if (!parres) { printk(KERN_ERR "cki_free: error getting " "resclass from core \n"); @@ -372,8 +311,23 @@ static void cki_free(void *res) /* Update parent's shares */ spin_lock(&parres->shares_lock); + child_guarantee_changed(&parres->shares, icls->shares.my_guarantee, 0); parres->cnt_unused += icls->cnt_guarantee; + + // run thru parent's children and get the new max_limit of the parent + ckrm_lock_hier(parres->core); + maxlimit = 0; + while ((child = ckrm_get_next_child(parres->core, child)) != NULL) { + childres = ckrm_get_res_class(child, resid, cki_icls_t); + if (maxlimit < childres->shares.my_limit) { + maxlimit = childres->shares.my_limit; + } + } + ckrm_unlock_hier(parres->core); + if (parres->shares.cur_max_limit < maxlimit) { + parres->shares.cur_max_limit = maxlimit; + } spin_unlock(&parres->shares_lock); kfree(res); @@ -388,26 +342,15 @@ static int cki_setshare(void *res, struct ckrm_shares *new) struct ckrm_shares *cur, *par; int rc = -EINVAL, resid = cki_rcbs.resid; - if (!icls) { - printk(KERN_ERR "No class\n"); + if (!icls) return rc; - } cur = &icls->shares; - - /* limits not supported */ - if ((new->max_limit != CKRM_SHARE_UNCHANGED) - || (new->my_limit != CKRM_SHARE_UNCHANGED)) { - printk(KERN_ERR "limits not supported\n"); - return -EINVAL; - } - if (icls->parent) { parres = ckrm_get_res_class(icls->parent, resid, cki_icls_t); if (!parres) { - printk(KERN_ERR "cki_setshare: error getting " - "resclass from core \n"); + pr_debug("cki_setshare: invalid resclass\n"); return -EINVAL; } spin_lock(&parres->shares_lock); @@ -420,10 +363,8 @@ static int cki_setshare(void *res, struct ckrm_shares *new) } rc = set_shares(new, cur, par); - printk(KERN_ERR "rc from set_shares %d\n", rc); if ((!rc) && parres) { - if (parres->cnt_guarantee == CKRM_SHARE_DONTCARE) { parres->cnt_unused = CKRM_SHARE_DONTCARE; } else if (par->total_guarantee) { @@ -435,17 +376,6 @@ static int cki_setshare(void *res, struct ckrm_shares *new) parres->cnt_unused = 0; } cki_recalc_propagate(res, parres); - -#if 0 - int old = icls->ioprio; - - rc = cki_recalc(icls,0); - - if (!rc && parres) { - int raise_tot = icls->ioprio - old ; - parres->unused -= raise_tot ; - } -#endif } spin_unlock(&icls->shares_lock); if (icls->parent) { @@ -471,15 +401,15 @@ static int cki_getstats(void *res, struct seq_file *sfile) if (!icls) return -EINVAL; -/* - seq_printf(sfile, "%d my_read\n",atomic_read(&icls->mystats.blkrd)); - seq_printf(sfile, "%d my_write\n",atomic_read(&icls->mystats.blkwr)); - seq_printf(sfile, "%d total_read\n",atomic_read(&icls->stats.blkrd)); - seq_printf(sfile, "%d total_write\n",atomic_read(&icls->stats.blkwr)); -*/ - - seq_printf(sfile, "%d total ioprio\n",icls->cnt_guarantee); - seq_printf(sfile, "%d unused/default ioprio\n",icls->cnt_unused); + seq_printf(sfile, "abs limit %d\n",icls->cnt_limit); + seq_printf(sfile, "skip %d timdout %d avsec %lu rate %ld " + " sec0 %ld sec1 %ld\n", + icls->cfqpriv.nskip, + icls->cfqpriv.timedout, + icls->cfqpriv.navsec, + atomic_read(&(icls->cfqpriv.sectorate)), + (unsigned long)icls->cfqpriv.sec[0], + (unsigned long)icls->cfqpriv.sec[1]); return 0; } @@ -554,7 +484,7 @@ int __init cki_init(void) resid = ckrm_register_res_ctlr(clstype, &cki_rcbs); if (resid != -1) { cki_rcbs.classtype = clstype; - cki_cfq_set(cki_tsk_icls,cki_tsk_ioprio); + cki_cfq_set(cki_tsk_icls,cki_tsk_ioprio,cki_tsk_cfqpriv); } } @@ -566,7 +496,7 @@ void __exit cki_exit(void) ckrm_unregister_res_ctlr(&cki_rcbs); cki_rcbs.resid = -1; cki_rcbs.classtype = NULL; - cki_cfq_set(NULL,NULL); + cki_cfq_set(NULL,NULL,NULL); } module_init(cki_init) diff --git a/drivers/block/ckrm-iostub.c b/drivers/block/ckrm-iostub.c index c325d8e8d..f4012545b 100644 --- a/drivers/block/ckrm-iostub.c +++ b/drivers/block/ckrm-iostub.c @@ -25,13 +25,14 @@ static spinlock_t stub_lock = SPIN_LOCK_UNLOCKED; static icls_tsk_t tskiclstub; static icls_ioprio_t tskiopriostub; +static icls_tsk_t tskcfqprivstub; - -void cki_cfq_set(icls_tsk_t tskicls, icls_ioprio_t tskioprio) +void cki_cfq_set(icls_tsk_t tskicls, icls_ioprio_t tskioprio, icls_tsk_t tskcfqpriv) { spin_lock(&stub_lock); tskiclstub = tskicls; tskiopriostub = tskioprio; + tskcfqprivstub = tskcfqpriv; spin_unlock(&stub_lock); } @@ -59,6 +60,19 @@ int cki_ioprio(struct task_struct *tsk) return ret; } +void *cki_cfqpriv(struct task_struct *tsk) +{ + void *ret; + spin_lock(&stub_lock); + if (tskiclstub) + ret = (*tskcfqprivstub)(tsk); + else + ret = NULL; + spin_unlock(&stub_lock); + return ret; +} + EXPORT_SYMBOL(cki_cfq_set); EXPORT_SYMBOL(cki_hash_key); EXPORT_SYMBOL(cki_ioprio); +EXPORT_SYMBOL(cki_cfqpriv); diff --git a/drivers/block/cpqarray.h b/drivers/block/cpqarray.h index 112e39422..6a9ded054 100644 --- a/drivers/block/cpqarray.h +++ b/drivers/block/cpqarray.h @@ -90,7 +90,7 @@ struct ctlr_info { __u32 board_id; char *product_name; - void *vaddr; + void __iomem *vaddr; unsigned long paddr; unsigned long io_mem_addr; unsigned long io_mem_length; diff --git a/drivers/block/nbd.c b/drivers/block/nbd.c index b98b70b8f..75ad39dc6 100644 --- a/drivers/block/nbd.c +++ b/drivers/block/nbd.c @@ -128,23 +128,11 @@ static void nbd_end_request(struct request *req) { int uptodate = (req->errors == 0) ? 1 : 0; request_queue_t *q = req->q; - struct nbd_device *lo = req->rq_disk->private_data; unsigned long flags; dprintk(DBG_BLKDEV, "%s: request %p: %s\n", req->rq_disk->disk_name, req, uptodate? "done": "failed"); - spin_lock(&lo->queue_lock); - while (req->ref_count > 1) { /* still in send */ - spin_unlock(&lo->queue_lock); - printk(KERN_DEBUG "%s: request %p still in use (%d), waiting\n", - lo->disk->disk_name, req, req->ref_count); - set_current_state(TASK_UNINTERRUPTIBLE); - schedule_timeout(HZ); /* wait a second */ - spin_lock(&lo->queue_lock); - } - spin_unlock(&lo->queue_lock); - spin_lock_irqsave(q->queue_lock, flags); if (!end_that_request_first(req, uptodate, req->nr_sectors)) { end_that_request_last(req); @@ -228,7 +216,7 @@ static inline int sock_send_bvec(struct socket *sock, struct bio_vec *bvec, return result; } -void nbd_send_req(struct nbd_device *lo, struct request *req) +static int nbd_send_req(struct nbd_device *lo, struct request *req) { int result, i, flags; struct nbd_request request; @@ -288,11 +276,11 @@ void nbd_send_req(struct nbd_device *lo, struct request *req) } } up(&lo->tx_lock); - return; + return 0; error_out: up(&lo->tx_lock); - req->errors++; + return 1; } static struct request *nbd_find_request(struct nbd_device *lo, char *handle) @@ -477,26 +465,19 @@ static void do_nbd_request(request_queue_t * q) } list_add(&req->queuelist, &lo->queue_head); - req->ref_count++; /* make sure req does not get freed */ spin_unlock(&lo->queue_lock); - nbd_send_req(lo, req); - - if (req->errors) { + if (nbd_send_req(lo, req) != 0) { printk(KERN_ERR "%s: Request send failed\n", lo->disk->disk_name); - spin_lock(&lo->queue_lock); - list_del_init(&req->queuelist); - req->ref_count--; - spin_unlock(&lo->queue_lock); - nbd_end_request(req); - spin_lock_irq(q->queue_lock); - continue; + if (nbd_find_request(lo, (char *)&req) != NULL) { + /* we still own req */ + req->errors++; + nbd_end_request(req); + } else /* we're racing with nbd_clear_que */ + printk(KERN_DEBUG "nbd: can't find req\n"); } - spin_lock(&lo->queue_lock); - req->ref_count--; - spin_unlock(&lo->queue_lock); spin_lock_irq(q->queue_lock); continue; diff --git a/drivers/block/sx8.c b/drivers/block/sx8.c index 0a0234129..cc8a17251 100644 --- a/drivers/block/sx8.c +++ b/drivers/block/sx8.c @@ -242,7 +242,7 @@ struct carm_request { struct carm_host { unsigned long flags; - void *mmio; + void __iomem *mmio; void *shm; dma_addr_t shm_dma; @@ -283,13 +283,13 @@ struct carm_host { }; struct carm_response { - u32 ret_handle; - u32 status; + __le32 ret_handle; + __le32 status; } __attribute__((packed)); struct carm_msg_sg { - u32 start; - u32 len; + __le32 start; + __le32 len; } __attribute__((packed)); struct carm_msg_rw { @@ -297,10 +297,10 @@ struct carm_msg_rw { u8 id; u8 sg_count; u8 sg_type; - u32 handle; - u32 lba; - u16 lba_count; - u16 lba_high; + __le32 handle; + __le32 lba; + __le16 lba_count; + __le16 lba_high; struct carm_msg_sg sg[32]; } __attribute__((packed)); @@ -309,15 +309,15 @@ struct carm_msg_allocbuf { u8 subtype; u8 n_sg; u8 sg_type; - u32 handle; - u32 addr; - u32 len; - u32 evt_pool; - u32 n_evt; - u32 rbuf_pool; - u32 n_rbuf; - u32 msg_pool; - u32 n_msg; + __le32 handle; + __le32 addr; + __le32 len; + __le32 evt_pool; + __le32 n_evt; + __le32 rbuf_pool; + __le32 n_rbuf; + __le32 msg_pool; + __le32 n_msg; struct carm_msg_sg sg[8]; } __attribute__((packed)); @@ -326,8 +326,8 @@ struct carm_msg_ioctl { u8 subtype; u8 array_id; u8 reserved1; - u32 handle; - u32 data_addr; + __le32 handle; + __le32 data_addr; u32 reserved2; } __attribute__((packed)); @@ -335,48 +335,48 @@ struct carm_msg_sync_time { u8 type; u8 subtype; u16 reserved1; - u32 handle; + __le32 handle; u32 reserved2; - u32 timestamp; + __le32 timestamp; } __attribute__((packed)); struct carm_msg_get_fw_ver { u8 type; u8 subtype; u16 reserved1; - u32 handle; - u32 data_addr; + __le32 handle; + __le32 data_addr; u32 reserved2; } __attribute__((packed)); struct carm_fw_ver { - u32 version; + __le32 version; u8 features; u8 reserved1; u16 reserved2; } __attribute__((packed)); struct carm_array_info { - u32 size; + __le32 size; - u16 size_hi; - u16 stripe_size; + __le16 size_hi; + __le16 stripe_size; - u32 mode; + __le32 mode; - u16 stripe_blk_sz; - u16 reserved1; + __le16 stripe_blk_sz; + __le16 reserved1; - u16 cyl; - u16 head; + __le16 cyl; + __le16 head; - u16 sect; + __le16 sect; u8 array_id; u8 reserved2; char name[40]; - u32 array_status; + __le32 array_status; /* device list continues beyond this point? */ } __attribute__((packed)); @@ -451,7 +451,7 @@ static inline int carm_lookup_bucket(u32 msg_size) return -ENOENT; } -static void carm_init_buckets(void *mmio) +static void carm_init_buckets(void __iomem *mmio) { unsigned int i; @@ -474,7 +474,7 @@ static inline dma_addr_t carm_ref_msg_dma(struct carm_host *host, static int carm_send_msg(struct carm_host *host, struct carm_request *crq) { - void *mmio = host->mmio; + void __iomem *mmio = host->mmio; u32 msg = (u32) carm_ref_msg_dma(host, crq->tag); u32 cm_bucket = crq->msg_bucket; u32 tmp; @@ -965,7 +965,7 @@ static void carm_handle_array_info(struct carm_host *host, port = &host->port[cur_port]; lo = (u64) le32_to_cpu(desc->size); - hi = (u64) le32_to_cpu(desc->size_hi); + hi = (u64) le16_to_cpu(desc->size_hi); port->capacity = lo | (hi << 32); port->dev_geom_head = le16_to_cpu(desc->head); @@ -1060,7 +1060,7 @@ static inline void carm_handle_rw(struct carm_host *host, } static inline void carm_handle_resp(struct carm_host *host, - u32 ret_handle_le, u32 status) + __le32 ret_handle_le, u32 status) { u32 handle = le32_to_cpu(ret_handle_le); unsigned int msg_idx; @@ -1158,7 +1158,7 @@ err_out: static inline void carm_handle_responses(struct carm_host *host) { - void *mmio = host->mmio; + void __iomem *mmio = host->mmio; struct carm_response *resp = (struct carm_response *) host->shm; unsigned int work = 0; unsigned int idx = host->resp_idx % RMSG_Q_LEN; @@ -1176,7 +1176,7 @@ static inline void carm_handle_responses(struct carm_host *host) else if ((status & (1 << 31)) == 0) { VPRINTK("handling msg response on index %u\n", idx); carm_handle_resp(host, resp[idx].ret_handle, status); - resp[idx].status = 0xffffffff; + resp[idx].status = cpu_to_le32(0xffffffff); } /* asynchronous events the hardware throws our way */ @@ -1185,7 +1185,7 @@ static inline void carm_handle_responses(struct carm_host *host) u8 evt_type = *evt_type_ptr; printk(KERN_WARNING DRV_NAME "(%s): unhandled event type %d\n", pci_name(host->pdev), (int) evt_type); - resp[idx].status = 0xffffffff; + resp[idx].status = cpu_to_le32(0xffffffff); } idx = NEXT_RESP(idx); @@ -1199,7 +1199,7 @@ static inline void carm_handle_responses(struct carm_host *host) static irqreturn_t carm_interrupt(int irq, void *__host, struct pt_regs *regs) { struct carm_host *host = __host; - void *mmio; + void __iomem *mmio; u32 mask; int handled = 0; unsigned long flags; @@ -1364,7 +1364,7 @@ static void carm_fsm_task (void *_data) schedule_work(&host->fsm_task); } -static int carm_init_wait(void *mmio, u32 bits, unsigned int test_bit) +static int carm_init_wait(void __iomem *mmio, u32 bits, unsigned int test_bit) { unsigned int i; @@ -1390,19 +1390,19 @@ static int carm_init_wait(void *mmio, u32 bits, unsigned int test_bit) static void carm_init_responses(struct carm_host *host) { - void *mmio = host->mmio; + void __iomem *mmio = host->mmio; unsigned int i; struct carm_response *resp = (struct carm_response *) host->shm; for (i = 0; i < RMSG_Q_LEN; i++) - resp[i].status = 0xffffffff; + resp[i].status = cpu_to_le32(0xffffffff); writel(0, mmio + CARM_RESP_IDX); } static int carm_init_host(struct carm_host *host) { - void *mmio = host->mmio; + void __iomem *mmio = host->mmio; u32 tmp; u8 tmp8; int rc; @@ -1414,7 +1414,7 @@ static int carm_init_host(struct carm_host *host) tmp8 = readb(mmio + CARM_INITC); if (tmp8 & 0x01) { tmp8 &= ~0x01; - writeb(tmp8, CARM_INITC); + writeb(tmp8, mmio + CARM_INITC); readb(mmio + CARM_INITC); /* flush */ DPRINTK("snooze...\n"); @@ -1503,7 +1503,7 @@ static int carm_init_disks(struct carm_host *host) } port->disk = disk; - sprintf(disk->disk_name, DRV_NAME "%u_%u", host->id, i); + sprintf(disk->disk_name, DRV_NAME "/%u", (host->id * CARM_MAX_PORTS) + i); sprintf(disk->devfs_name, DRV_NAME "/%u_%u", host->id, i); disk->major = host->major; disk->first_minor = i * CARM_MINORS_PER_MAJOR; diff --git a/drivers/block/ub.c b/drivers/block/ub.c new file mode 100644 index 000000000..f605535d3 --- /dev/null +++ b/drivers/block/ub.c @@ -0,0 +1,2097 @@ +/* + * The low performance USB storage driver (ub). + * + * Copyright (c) 1999, 2000 Matthew Dharm (mdharm-usb@one-eyed-alien.net) + * Copyright (C) 2004 Pete Zaitcev (zaitcev@yahoo.com) + * + * This work is a part of Linux kernel, is derived from it, + * and is not licensed separately. See file COPYING for details. + * + * TODO (sorted by decreasing priority) + * -- ZIP does "ub: resid 18 len 0 act 0" and whole transport quits (toggles?) + * -- set readonly flag for CDs, set removable flag for CF readers + * -- do inquiry and verify we got a disk and not a tape (for LUN mismatch) + * -- support pphaneuf's SDDR-75 with two LUNs (also broken capacity...) + * -- special case some senses, e.g. 3a/0 -> no media present, reduce retries + * -- do something about spin-down devices, they are extremely dangerous + * (ZIP is one. Needs spin-up command as well.) + * -- verify the 13 conditions and do bulk resets + * -- normal pool of commands instead of cmdv[]? + * -- kill last_pipe and simply do two-state clearing on both pipes + * -- verify protocol (bulk) from USB descriptors (maybe...) + * -- highmem and sg + * -- move top_sense and work_bcs into separate allocations (if they survive) + * for cache purists and esoteric architectures. + * -- prune comments, they are too volumnous + * -- Exterminate P3 printks + * -- Resove XXX's + */ +#include +#include +#include +#include +#include +#include +#include + +#define DRV_NAME "ub" +#define DEVFS_NAME DRV_NAME + +#define UB_MAJOR 125 /* Stolen from Experimental range for a week - XXX */ + +/* + * Definitions which have to be scattered once we understand the layout better. + */ + +/* Transport (despite PR in the name) */ +#define US_PR_BULK 0x50 /* bulk only */ + +/* Protocol */ +#define US_SC_SCSI 0x06 /* Transparent */ + +/* + */ +#define UB_MINORS_PER_MAJOR 8 + +#define UB_MAX_CDB_SIZE 16 /* Corresponds to Bulk */ + +#define UB_SENSE_SIZE 18 + +/* + */ + +/* command block wrapper */ +struct bulk_cb_wrap { + u32 Signature; /* contains 'USBC' */ + u32 Tag; /* unique per command id */ + u32 DataTransferLength; /* size of data */ + u8 Flags; /* direction in bit 0 */ + u8 Lun; /* LUN normally 0 */ + u8 Length; /* of of the CDB */ + u8 CDB[UB_MAX_CDB_SIZE]; /* max command */ +}; + +#define US_BULK_CB_WRAP_LEN 31 +#define US_BULK_CB_SIGN 0x43425355 /*spells out USBC */ +#define US_BULK_FLAG_IN 1 +#define US_BULK_FLAG_OUT 0 + +/* command status wrapper */ +struct bulk_cs_wrap { + u32 Signature; /* should = 'USBS' */ + u32 Tag; /* same as original command */ + u32 Residue; /* amount not transferred */ + u8 Status; /* see below */ +}; + +#define US_BULK_CS_WRAP_LEN 13 +#define US_BULK_CS_SIGN 0x53425355 /* spells out 'USBS' */ +/* This is for Olympus Camedia digital cameras */ +#define US_BULK_CS_OLYMPUS_SIGN 0x55425355 /* spells out 'USBU' */ +#define US_BULK_STAT_OK 0 +#define US_BULK_STAT_FAIL 1 +#define US_BULK_STAT_PHASE 2 + +/* bulk-only class specific requests */ +#define US_BULK_RESET_REQUEST 0xff +#define US_BULK_GET_MAX_LUN 0xfe + +/* + */ +struct ub_dev; + +#define UB_MAX_REQ_SG 1 +#define UB_MAX_SECTORS 64 + +/* + * A second ought to be enough for a 32K transfer (UB_MAX_SECTORS) + * even if a webcam hogs the bus (famous last words). + * Some CDs need a second to spin up though. + * ZIP drive rejects commands when it's not spinning, + * so it does not need long timeouts either. + */ +#define UB_URB_TIMEOUT (HZ*2) +#define UB_CTRL_TIMEOUT (HZ/2) /* 500ms ought to be enough to clear a stall */ + +/* + * An instance of a SCSI command in transit. + */ +#define UB_DIR_NONE 0 +#define UB_DIR_READ 1 +#define UB_DIR_ILLEGAL2 2 +#define UB_DIR_WRITE 3 + +#define UB_DIR_CHAR(c) (((c)==UB_DIR_WRITE)? 'w': \ + (((c)==UB_DIR_READ)? 'r': 'n')) + +enum ub_scsi_cmd_state { + UB_CMDST_INIT, /* Initial state */ + UB_CMDST_CMD, /* Command submitted */ + UB_CMDST_DATA, /* Data phase */ + UB_CMDST_CLR2STS, /* Clearing before requesting status */ + UB_CMDST_STAT, /* Status phase */ + UB_CMDST_CLEAR, /* Clearing a stall (halt, actually) */ + UB_CMDST_SENSE, /* Sending Request Sense */ + UB_CMDST_DONE /* Final state */ +}; + +static char *ub_scsi_cmd_stname[] = { + ". ", + "Cmd", + "dat", + "c2s", + "sts", + "clr", + "Sen", + "fin" +}; + +struct ub_scsi_cmd { + unsigned char cdb[UB_MAX_CDB_SIZE]; + unsigned char cdb_len; + + unsigned char dir; /* 0 - none, 1 - read, 3 - write. */ + unsigned char trace_index; + enum ub_scsi_cmd_state state; + unsigned int tag; + struct ub_scsi_cmd *next; + + int error; /* Return code - valid upon done */ + int act_len; /* Return size */ + + int stat_count; /* Retries getting status. */ + + /* + * We do not support transfers from highmem pages + * because the underlying USB framework does not do what we need. + */ + char *data; /* Requested buffer */ + unsigned int len; /* Requested length */ + // struct scatterlist sgv[UB_MAX_REQ_SG]; + + void (*done)(struct ub_dev *, struct ub_scsi_cmd *); + void *back; +}; + +/* + */ +struct ub_capacity { + unsigned long nsec; /* Linux size - 512 byte sectors */ + unsigned int bsize; /* Linux hardsect_size */ + unsigned int bshift; /* Shift between 512 and hard sects */ +}; + +/* + * The SCSI command tracing structure. + */ + +#define SCMD_ST_HIST_SZ 8 +#define SCMD_TRACE_SZ 15 /* No more than 256 (trace_index) */ + +struct ub_scsi_cmd_trace { + int hcur; + unsigned int tag; + unsigned int req_size, act_size; + unsigned char op; + unsigned char dir; + unsigned char key, asc, ascq; + char st_hst[SCMD_ST_HIST_SZ]; +}; + +struct ub_scsi_trace { + int cur; + struct ub_scsi_cmd_trace vec[SCMD_TRACE_SZ]; +}; + +/* + * This is a direct take-off from linux/include/completion.h + * The difference is that I do not wait on this thing, just poll. + * When I want to wait (ub_probe), I just use the stock completion. + * + * Note that INIT_COMPLETION takes no lock. It is correct. But why + * in the bloody hell that thing takes struct instead of pointer to struct + * is quite beyond me. I just copied it from the stock completion. + */ +struct ub_completion { + unsigned int done; + spinlock_t lock; +}; + +static inline void ub_init_completion(struct ub_completion *x) +{ + x->done = 0; + spin_lock_init(&x->lock); +} + +#define UB_INIT_COMPLETION(x) ((x).done = 0) + +static void ub_complete(struct ub_completion *x) +{ + unsigned long flags; + + spin_lock_irqsave(&x->lock, flags); + x->done++; + spin_unlock_irqrestore(&x->lock, flags); +} + +static int ub_is_completed(struct ub_completion *x) +{ + unsigned long flags; + int ret; + + spin_lock_irqsave(&x->lock, flags); + ret = x->done; + spin_unlock_irqrestore(&x->lock, flags); + return ret; +} + +/* + */ +struct ub_scsi_cmd_queue { + int qlen, qmax; + struct ub_scsi_cmd *head, *tail; +}; + +/* + * The UB device instance. + */ +struct ub_dev { + spinlock_t lock; + int id; /* Number among ub's */ + atomic_t poison; /* The USB device is disconnected */ + int openc; /* protected by ub_lock! */ + /* kref is too implicit for our taste */ + unsigned int tagcnt; + int changed; /* Media was changed */ + int removable; + int readonly; + char name[8]; + struct usb_device *dev; + struct usb_interface *intf; + + struct ub_capacity capacity; + struct gendisk *disk; + + unsigned int send_bulk_pipe; /* cached pipe values */ + unsigned int recv_bulk_pipe; + unsigned int send_ctrl_pipe; + unsigned int recv_ctrl_pipe; + + struct tasklet_struct tasklet; + + /* XXX Use Ingo's mempool (once we have more than one) */ + int cmda[1]; + struct ub_scsi_cmd cmdv[1]; + + struct ub_scsi_cmd_queue cmd_queue; + struct ub_scsi_cmd top_rqs_cmd; /* REQUEST SENSE */ + unsigned char top_sense[UB_SENSE_SIZE]; + + struct ub_completion work_done; + struct urb work_urb; + struct timer_list work_timer; + int last_pipe; /* What might need clearing */ + struct bulk_cb_wrap work_bcb; + struct bulk_cs_wrap work_bcs; + struct usb_ctrlrequest work_cr; + + struct ub_scsi_trace tr; +}; + +/* + */ +static void ub_rw_cmd_done(struct ub_dev *sc, struct ub_scsi_cmd *cmd); +static void ub_end_rq(struct request *rq, int uptodate); +static int ub_submit_scsi(struct ub_dev *sc, struct ub_scsi_cmd *cmd); +static void ub_urb_complete(struct urb *urb, struct pt_regs *pt); +static void ub_scsi_action(unsigned long _dev); +static void ub_scsi_dispatch(struct ub_dev *sc); +static void ub_scsi_urb_compl(struct ub_dev *sc, struct ub_scsi_cmd *cmd); +static void ub_state_done(struct ub_dev *sc, struct ub_scsi_cmd *cmd, int rc); +static void ub_state_stat(struct ub_dev *sc, struct ub_scsi_cmd *cmd); +static void ub_state_sense(struct ub_dev *sc, struct ub_scsi_cmd *cmd); +static int ub_submit_clear_stall(struct ub_dev *sc, struct ub_scsi_cmd *cmd, + int stalled_pipe); +static void ub_top_sense_done(struct ub_dev *sc, struct ub_scsi_cmd *scmd); +static int ub_sync_tur(struct ub_dev *sc); +static int ub_sync_read_cap(struct ub_dev *sc, struct ub_capacity *ret); + +/* + */ +static struct usb_device_id ub_usb_ids[] = { + // { USB_DEVICE_VER(0x0781, 0x0002, 0x0009, 0x0009) }, /* SDDR-31 */ + { USB_INTERFACE_INFO(USB_CLASS_MASS_STORAGE, US_SC_SCSI, US_PR_BULK) }, + { } +}; + +MODULE_DEVICE_TABLE(usb, ub_usb_ids); + +/* + * Find me a way to identify "next free minor" for add_disk(), + * and the array disappears the next day. However, the number of + * hosts has something to do with the naming and /proc/partitions. + * This has to be thought out in detail before changing. + * If UB_MAX_HOST was 1000, we'd use a bitmap. Or a better data structure. + */ +#define UB_MAX_HOSTS 26 +static char ub_hostv[UB_MAX_HOSTS]; +static spinlock_t ub_lock = SPIN_LOCK_UNLOCKED; /* Locks globals and ->openc */ + +/* + * The SCSI command tracing procedures. + */ + +static void ub_cmdtr_new(struct ub_dev *sc, struct ub_scsi_cmd *cmd) +{ + int n; + struct ub_scsi_cmd_trace *t; + + if ((n = sc->tr.cur + 1) == SCMD_TRACE_SZ) n = 0; + t = &sc->tr.vec[n]; + + memset(t, 0, sizeof(struct ub_scsi_cmd_trace)); + t->tag = cmd->tag; + t->op = cmd->cdb[0]; + t->dir = cmd->dir; + t->req_size = cmd->len; + t->st_hst[0] = cmd->state; + + sc->tr.cur = n; + cmd->trace_index = n; +} + +static void ub_cmdtr_state(struct ub_dev *sc, struct ub_scsi_cmd *cmd) +{ + int n; + struct ub_scsi_cmd_trace *t; + + t = &sc->tr.vec[cmd->trace_index]; + if (t->tag == cmd->tag) { + if ((n = t->hcur + 1) == SCMD_ST_HIST_SZ) n = 0; + t->st_hst[n] = cmd->state; + t->hcur = n; + } +} + +static void ub_cmdtr_act_len(struct ub_dev *sc, struct ub_scsi_cmd *cmd) +{ + struct ub_scsi_cmd_trace *t; + + t = &sc->tr.vec[cmd->trace_index]; + if (t->tag == cmd->tag) + t->act_size = cmd->act_len; +} + +static void ub_cmdtr_sense(struct ub_dev *sc, struct ub_scsi_cmd *cmd, + unsigned char *sense) +{ + struct ub_scsi_cmd_trace *t; + + t = &sc->tr.vec[cmd->trace_index]; + if (t->tag == cmd->tag) { + t->key = sense[2] & 0x0F; + t->asc = sense[12]; + t->ascq = sense[13]; + } +} + +static ssize_t ub_diag_show(struct device *dev, char *page) +{ + struct usb_interface *intf; + struct ub_dev *sc; + int cnt; + unsigned long flags; + int nc, nh; + int i, j; + struct ub_scsi_cmd_trace *t; + + intf = to_usb_interface(dev); + sc = usb_get_intfdata(intf); + if (sc == NULL) + return 0; + + cnt = 0; + spin_lock_irqsave(&sc->lock, flags); + + cnt += sprintf(page + cnt, + "qlen %d qmax %d changed %d removable %d readonly %d\n", + sc->cmd_queue.qlen, sc->cmd_queue.qmax, + sc->changed, sc->removable, sc->readonly); + + if ((nc = sc->tr.cur + 1) == SCMD_TRACE_SZ) nc = 0; + for (j = 0; j < SCMD_TRACE_SZ; j++) { + t = &sc->tr.vec[nc]; + + cnt += sprintf(page + cnt, "%08x %02x", t->tag, t->op); + if (t->op == REQUEST_SENSE) { + cnt += sprintf(page + cnt, " [sense %x %02x %02x]", + t->key, t->asc, t->ascq); + } else { + cnt += sprintf(page + cnt, " %c", UB_DIR_CHAR(t->dir)); + cnt += sprintf(page + cnt, " [%5d %5d]", + t->req_size, t->act_size); + } + if ((nh = t->hcur + 1) == SCMD_ST_HIST_SZ) nh = 0; + for (i = 0; i < SCMD_ST_HIST_SZ; i++) { + cnt += sprintf(page + cnt, " %s", + ub_scsi_cmd_stname[(int)t->st_hst[nh]]); + if (++nh == SCMD_ST_HIST_SZ) nh = 0; + } + cnt += sprintf(page + cnt, "\n"); + + if (++nc == SCMD_TRACE_SZ) nc = 0; + } + + spin_unlock_irqrestore(&sc->lock, flags); + return cnt; +} + +static DEVICE_ATTR(diag, S_IRUGO, ub_diag_show, NULL); /* N.B. World readable */ + +/* + * The id allocator. + * + * This also stores the host for indexing by minor, which is somewhat dirty. + */ +static int ub_id_get(void) +{ + unsigned long flags; + int i; + + spin_lock_irqsave(&ub_lock, flags); + for (i = 0; i < UB_MAX_HOSTS; i++) { + if (ub_hostv[i] == 0) { + ub_hostv[i] = 1; + spin_unlock_irqrestore(&ub_lock, flags); + return i; + } + } + spin_unlock_irqrestore(&ub_lock, flags); + return -1; +} + +static void ub_id_put(int id) +{ + + if (id < 0 || id >= UB_MAX_HOSTS) { + printk(KERN_ERR DRV_NAME ": bad host ID %d\n", id); + return; + } + if (ub_hostv[id] == 0) { + printk(KERN_ERR DRV_NAME ": freeing free host ID %d\n", id); + return; + } + ub_hostv[id] = 0; +} + +/* + * Final cleanup and deallocation. + * This must be called with ub_lock taken. + */ +static void ub_cleanup(struct ub_dev *sc) +{ + ub_id_put(sc->id); + kfree(sc); +} + +/* + * The "command allocator". + */ +static struct ub_scsi_cmd *ub_get_cmd(struct ub_dev *sc) +{ + struct ub_scsi_cmd *ret; + + if (sc->cmda[0]) + return NULL; + ret = &sc->cmdv[0]; + sc->cmda[0] = 1; + return ret; +} + +static void ub_put_cmd(struct ub_dev *sc, struct ub_scsi_cmd *cmd) +{ + if (cmd != &sc->cmdv[0]) { + printk(KERN_WARNING "%s: releasing a foreign cmd %p\n", + sc->name, cmd); + return; + } + if (!sc->cmda[0]) { + printk(KERN_WARNING "%s: releasing a free cmd\n", sc->name); + return; + } + sc->cmda[0] = 0; +} + +/* + * The command queue. + */ +static void ub_cmdq_add(struct ub_dev *sc, struct ub_scsi_cmd *cmd) +{ + struct ub_scsi_cmd_queue *t = &sc->cmd_queue; + + if (t->qlen++ == 0) { + t->head = cmd; + t->tail = cmd; + } else { + t->tail->next = cmd; + t->tail = cmd; + } + + if (t->qlen > t->qmax) + t->qmax = t->qlen; +} + +static void ub_cmdq_insert(struct ub_dev *sc, struct ub_scsi_cmd *cmd) +{ + struct ub_scsi_cmd_queue *t = &sc->cmd_queue; + + if (t->qlen++ == 0) { + t->head = cmd; + t->tail = cmd; + } else { + cmd->next = t->head; + t->head = cmd; + } + + if (t->qlen > t->qmax) + t->qmax = t->qlen; +} + +static struct ub_scsi_cmd *ub_cmdq_pop(struct ub_dev *sc) +{ + struct ub_scsi_cmd_queue *t = &sc->cmd_queue; + struct ub_scsi_cmd *cmd; + + if (t->qlen == 0) + return NULL; + if (--t->qlen == 0) + t->tail = NULL; + cmd = t->head; + t->head = cmd->next; + cmd->next = NULL; + return cmd; +} + +#define ub_cmdq_peek(sc) ((sc)->cmd_queue.head) + +/* + * The request function is our main entry point + */ + +static inline int ub_bd_rq_fn_1(request_queue_t *q) +{ +#if 0 + int writing = 0, pci_dir, i, n_elem; + u32 tmp; + unsigned int msg_size; +#endif + struct ub_dev *sc = q->queuedata; + struct request *rq; +#if 0 /* We use rq->buffer for now */ + struct scatterlist *sg; + int n_elem; +#endif + struct ub_scsi_cmd *cmd; + int ub_dir; + unsigned int block, nblks; + int rc; + + if ((rq = elv_next_request(q)) == NULL) + return 1; + + if (atomic_read(&sc->poison) || sc->changed) { + blkdev_dequeue_request(rq); + ub_end_rq(rq, 0); + return 0; + } + + if ((cmd = ub_get_cmd(sc)) == NULL) { + blk_stop_queue(q); + return 1; + } + + blkdev_dequeue_request(rq); + + if (rq_data_dir(rq) == WRITE) + ub_dir = UB_DIR_WRITE; + else + ub_dir = UB_DIR_READ; + + /* + * get scatterlist from block layer + */ +#if 0 /* We use rq->buffer for now */ + sg = &cmd->sgv[0]; + n_elem = blk_rq_map_sg(q, rq, sg); + if (n_elem <= 0) { + ub_put_cmd(sc, cmd); + ub_end_rq(rq, 0); + blk_start_queue(q); + return 0; /* request with no s/g entries? */ + } + + if (n_elem != 1) { /* Paranoia */ + printk(KERN_WARNING "%s: request with %d segments\n", + sc->name, n_elem); + ub_put_cmd(sc, cmd); + ub_end_rq(rq, 0); + blk_start_queue(q); + return 0; + } +#endif + /* + * XXX Unfortunately, this check does not work. It is quite possible + * to get bogus non-null rq->buffer if you allow sg by mistake. + */ + if (rq->buffer == NULL) { + /* + * This must not happen if we set the queue right. + * The block level must create bounce buffers for us. + */ + static int do_print = 1; + if (do_print) { + printk(KERN_WARNING "%s: unmapped request\n", sc->name); + do_print = 0; + } + ub_put_cmd(sc, cmd); + ub_end_rq(rq, 0); + blk_start_queue(q); + return 0; + } + + /* + * build the command + */ + block = rq->sector; + nblks = rq->nr_sectors; + + memset(cmd, 0, sizeof(struct ub_scsi_cmd)); + cmd->cdb[0] = (ub_dir == UB_DIR_READ)? READ_10: WRITE_10; + /* 10-byte uses 4 bytes of LBA: 2147483648KB, 2097152MB, 2048GB */ + cmd->cdb[2] = block >> 24; + cmd->cdb[3] = block >> 16; + cmd->cdb[4] = block >> 8; + cmd->cdb[5] = block; + cmd->cdb[7] = nblks >> 8; + cmd->cdb[8] = nblks; + cmd->cdb_len = 10; + cmd->dir = ub_dir; + cmd->state = UB_CMDST_INIT; + cmd->data = rq->buffer; + cmd->len = nblks * 512; + cmd->done = ub_rw_cmd_done; + cmd->back = rq; + + cmd->tag = sc->tagcnt++; + if ((rc = ub_submit_scsi(sc, cmd)) != 0) { + ub_put_cmd(sc, cmd); + ub_end_rq(rq, 0); + blk_start_queue(q); + return 0; + } + + return 0; +} + +static void ub_bd_rq_fn(request_queue_t *q) +{ + do { } while (ub_bd_rq_fn_1(q) == 0); +} + +static void ub_rw_cmd_done(struct ub_dev *sc, struct ub_scsi_cmd *cmd) +{ + struct request *rq = cmd->back; + struct gendisk *disk = sc->disk; + request_queue_t *q = disk->queue; + int uptodate; + + if (cmd->error == 0) + uptodate = 1; + else + uptodate = 0; + + ub_put_cmd(sc, cmd); + ub_end_rq(rq, uptodate); + blk_start_queue(q); +} + +static void ub_end_rq(struct request *rq, int uptodate) +{ + int rc; + + rc = end_that_request_first(rq, uptodate, rq->hard_nr_sectors); + // assert(rc == 0); + end_that_request_last(rq); +} + +/* + * Submit a regular SCSI operation (not an auto-sense). + * + * The Iron Law of Good Submit Routine is: + * Zero return - callback is done, Nonzero return - callback is not done. + * No exceptions. + * + * Host is assumed locked. + * + * XXX We only support Bulk for the moment. + */ +static int ub_submit_scsi(struct ub_dev *sc, struct ub_scsi_cmd *cmd) +{ + + if (cmd->state != UB_CMDST_INIT || + (cmd->dir != UB_DIR_NONE && cmd->len == 0)) { + return -EINVAL; + } + + ub_cmdq_add(sc, cmd); + /* + * We can call ub_scsi_dispatch(sc) right away here, but it's a little + * safer to jump to a tasklet, in case upper layers do something silly. + */ + tasklet_schedule(&sc->tasklet); + return 0; +} + +/* + * Submit the first URB for the queued command. + * This function does not deal with queueing in any way. + */ +static int ub_scsi_cmd_start(struct ub_dev *sc, struct ub_scsi_cmd *cmd) +{ + struct bulk_cb_wrap *bcb; + int rc; + + bcb = &sc->work_bcb; + + /* set up the command wrapper */ + bcb->Signature = cpu_to_le32(US_BULK_CB_SIGN); + bcb->Tag = cmd->tag; /* Endianness is not important */ + bcb->DataTransferLength = cpu_to_le32(cmd->len); + bcb->Flags = (cmd->dir == UB_DIR_READ) ? 0x80 : 0; + bcb->Lun = 0; /* No multi-LUN yet */ + bcb->Length = cmd->cdb_len; + + /* copy the command payload */ + memcpy(bcb->CDB, cmd->cdb, UB_MAX_CDB_SIZE); + + UB_INIT_COMPLETION(sc->work_done); + + sc->last_pipe = sc->send_bulk_pipe; + usb_fill_bulk_urb(&sc->work_urb, sc->dev, sc->send_bulk_pipe, + bcb, US_BULK_CB_WRAP_LEN, ub_urb_complete, sc); + sc->work_urb.transfer_flags = URB_ASYNC_UNLINK; + + /* Fill what we shouldn't be filling, because usb-storage did so. */ + sc->work_urb.actual_length = 0; + sc->work_urb.error_count = 0; + sc->work_urb.status = 0; + + sc->work_timer.expires = jiffies + UB_URB_TIMEOUT; + add_timer(&sc->work_timer); + + if ((rc = usb_submit_urb(&sc->work_urb, GFP_ATOMIC)) != 0) { + /* XXX Clear stalls */ + printk("ub: cmd #%d start failed (%d)\n", cmd->tag, rc); /* P3 */ + del_timer(&sc->work_timer); + ub_complete(&sc->work_done); + return rc; + } + + cmd->state = UB_CMDST_CMD; + ub_cmdtr_state(sc, cmd); + return 0; +} + +/* + * Timeout handler. + */ +static void ub_urb_timeout(unsigned long arg) +{ + struct ub_dev *sc = (struct ub_dev *) arg; + unsigned long flags; + + spin_lock_irqsave(&sc->lock, flags); + usb_unlink_urb(&sc->work_urb); + spin_unlock_irqrestore(&sc->lock, flags); +} + +/* + * Completion routine for the work URB. + * + * This can be called directly from usb_submit_urb (while we have + * the sc->lock taken) and from an interrupt (while we do NOT have + * the sc->lock taken). Therefore, bounce this off to a tasklet. + */ +static void ub_urb_complete(struct urb *urb, struct pt_regs *pt) +{ + struct ub_dev *sc = urb->context; + + ub_complete(&sc->work_done); + tasklet_schedule(&sc->tasklet); +} + +static void ub_scsi_action(unsigned long _dev) +{ + struct ub_dev *sc = (struct ub_dev *) _dev; + unsigned long flags; + + spin_lock_irqsave(&sc->lock, flags); + ub_scsi_dispatch(sc); + spin_unlock_irqrestore(&sc->lock, flags); +} + +static void ub_scsi_dispatch(struct ub_dev *sc) +{ + struct ub_scsi_cmd *cmd; + int rc; + + while ((cmd = ub_cmdq_peek(sc)) != NULL) { + if (cmd->state == UB_CMDST_DONE) { + ub_cmdq_pop(sc); + (*cmd->done)(sc, cmd); + } else if (cmd->state == UB_CMDST_INIT) { + ub_cmdtr_new(sc, cmd); + if ((rc = ub_scsi_cmd_start(sc, cmd)) == 0) + break; + cmd->error = rc; + cmd->state = UB_CMDST_DONE; + ub_cmdtr_state(sc, cmd); + } else { + if (!ub_is_completed(&sc->work_done)) + break; + ub_scsi_urb_compl(sc, cmd); + } + } +} + +static void ub_scsi_urb_compl(struct ub_dev *sc, struct ub_scsi_cmd *cmd) +{ + struct urb *urb = &sc->work_urb; + struct bulk_cs_wrap *bcs; + int pipe; + int rc; + +/* P3 */ /** printk("ub: urb status %d pipe 0x%08x len %d act %d\n", + urb->status, urb->pipe, urb->transfer_buffer_length, urb->actual_length); **/ + + if (atomic_read(&sc->poison)) { + /* A little too simplistic, I feel... */ + goto Bad_End; + } + + if (cmd->state == UB_CMDST_CLEAR) { + if (urb->status == -EPIPE) { + /* + * STALL while clearning STALL. + * A STALL is illegal on a control pipe! + * XXX Might try to reset the device here and retry. + */ + printk(KERN_NOTICE "%s: " + "stall on control pipe for device %u\n", + sc->name, sc->dev->devnum); + goto Bad_End; + } + + /* + * We ignore the result for the halt clear. + */ + + /* reset the endpoint toggle */ + usb_settoggle(sc->dev, usb_pipeendpoint(sc->last_pipe), + usb_pipeout(sc->last_pipe), 0); + + ub_state_sense(sc, cmd); + + } else if (cmd->state == UB_CMDST_CLR2STS) { + if (urb->status == -EPIPE) { + /* + * STALL while clearning STALL. + * A STALL is illegal on a control pipe! + * XXX Might try to reset the device here and retry. + */ + printk(KERN_NOTICE "%s: " + "stall on control pipe for device %u\n", + sc->name, sc->dev->devnum); + goto Bad_End; + } + + /* + * We ignore the result for the halt clear. + */ + + /* reset the endpoint toggle */ + usb_settoggle(sc->dev, usb_pipeendpoint(sc->last_pipe), + usb_pipeout(sc->last_pipe), 0); + + ub_state_stat(sc, cmd); + + } else if (cmd->state == UB_CMDST_CMD) { + if (urb->status == -EPIPE) { + rc = ub_submit_clear_stall(sc, cmd, sc->last_pipe); + if (rc != 0) { + printk(KERN_NOTICE "%s: " + "unable to submit clear for device %u (%d)\n", + sc->name, sc->dev->devnum, rc); + /* + * This is typically ENOMEM or some other such shit. + * Retrying is pointless. Just do Bad End on it... + */ + goto Bad_End; + } + cmd->state = UB_CMDST_CLEAR; + ub_cmdtr_state(sc, cmd); + return; + } + if (urb->status != 0) + goto Bad_End; + if (urb->actual_length != US_BULK_CB_WRAP_LEN) { + /* XXX Must do reset here to unconfuse the device */ + goto Bad_End; + } + + if (cmd->dir == UB_DIR_NONE) { + ub_state_stat(sc, cmd); + return; + } + + UB_INIT_COMPLETION(sc->work_done); + + if (cmd->dir == UB_DIR_READ) + pipe = sc->recv_bulk_pipe; + else + pipe = sc->send_bulk_pipe; + sc->last_pipe = pipe; + usb_fill_bulk_urb(&sc->work_urb, sc->dev, pipe, + cmd->data, cmd->len, ub_urb_complete, sc); + sc->work_urb.transfer_flags = URB_ASYNC_UNLINK; + sc->work_urb.actual_length = 0; + sc->work_urb.error_count = 0; + sc->work_urb.status = 0; + + sc->work_timer.expires = jiffies + UB_URB_TIMEOUT; + add_timer(&sc->work_timer); + + if ((rc = usb_submit_urb(&sc->work_urb, GFP_ATOMIC)) != 0) { + /* XXX Clear stalls */ + printk("ub: data #%d submit failed (%d)\n", cmd->tag, rc); /* P3 */ + del_timer(&sc->work_timer); + ub_complete(&sc->work_done); + ub_state_done(sc, cmd, rc); + return; + } + + cmd->state = UB_CMDST_DATA; + ub_cmdtr_state(sc, cmd); + + } else if (cmd->state == UB_CMDST_DATA) { + if (urb->status == -EPIPE) { + rc = ub_submit_clear_stall(sc, cmd, sc->last_pipe); + if (rc != 0) { + printk(KERN_NOTICE "%s: " + "unable to submit clear for device %u (%d)\n", + sc->name, sc->dev->devnum, rc); + /* + * This is typically ENOMEM or some other such shit. + * Retrying is pointless. Just do Bad End on it... + */ + goto Bad_End; + } + cmd->state = UB_CMDST_CLR2STS; + ub_cmdtr_state(sc, cmd); + return; + } + if (urb->status == -EOVERFLOW) { + /* + * A babble? Failure, but we must transfer CSW now. + */ + cmd->error = -EOVERFLOW; /* A cheap trick... */ + } else { + if (urb->status != 0) + goto Bad_End; + } + + cmd->act_len = urb->actual_length; + ub_cmdtr_act_len(sc, cmd); + + ub_state_stat(sc, cmd); + + } else if (cmd->state == UB_CMDST_STAT) { + if (urb->status == -EPIPE) { + rc = ub_submit_clear_stall(sc, cmd, sc->last_pipe); + if (rc != 0) { + printk(KERN_NOTICE "%s: " + "unable to submit clear for device %u (%d)\n", + sc->name, sc->dev->devnum, rc); + /* + * This is typically ENOMEM or some other such shit. + * Retrying is pointless. Just do Bad End on it... + */ + goto Bad_End; + } + cmd->state = UB_CMDST_CLEAR; + ub_cmdtr_state(sc, cmd); + return; + } + if (urb->status != 0) + goto Bad_End; + + if (urb->actual_length == 0) { + /* + * Some broken devices add unnecessary zero-length + * packets to the end of their data transfers. + * Such packets show up as 0-length CSWs. If we + * encounter such a thing, try to read the CSW again. + */ + if (++cmd->stat_count >= 4) { + printk(KERN_NOTICE "%s: " + "unable to get CSW on device %u\n", + sc->name, sc->dev->devnum); + goto Bad_End; + } + + /* + * ub_state_stat only not dropping the count... + */ + UB_INIT_COMPLETION(sc->work_done); + + sc->last_pipe = sc->recv_bulk_pipe; + usb_fill_bulk_urb(&sc->work_urb, sc->dev, + sc->recv_bulk_pipe, &sc->work_bcs, + US_BULK_CS_WRAP_LEN, ub_urb_complete, sc); + sc->work_urb.transfer_flags = URB_ASYNC_UNLINK; + sc->work_urb.actual_length = 0; + sc->work_urb.error_count = 0; + sc->work_urb.status = 0; + + sc->work_timer.expires = jiffies + UB_URB_TIMEOUT; + add_timer(&sc->work_timer); + + rc = usb_submit_urb(&sc->work_urb, GFP_ATOMIC); + if (rc != 0) { + /* XXX Clear stalls */ + printk("%s: CSW #%d submit failed (%d)\n", + sc->name, cmd->tag, rc); /* P3 */ + del_timer(&sc->work_timer); + ub_complete(&sc->work_done); + ub_state_done(sc, cmd, rc); + return; + } + return; + } + + /* + * Check the returned Bulk protocol status. + */ + + bcs = &sc->work_bcs; + rc = le32_to_cpu(bcs->Residue); + if (rc != cmd->len - cmd->act_len) { + /* + * It is all right to transfer less, the caller has + * to check. But it's not all right if the device + * counts disagree with our counts. + */ + /* P3 */ printk("%s: resid %d len %d act %d\n", + sc->name, rc, cmd->len, cmd->act_len); + goto Bad_End; + } + + if (bcs->Signature != cpu_to_le32(US_BULK_CS_SIGN) && + bcs->Signature != cpu_to_le32(US_BULK_CS_OLYMPUS_SIGN)) { + /* XXX Rate-limit, even for P3 tagged */ + /* P3 */ printk("ub: signature 0x%x\n", bcs->Signature); + /* Windows ignores signatures, so do we. */ + } + + if (bcs->Tag != cmd->tag) { + /* P3 */ printk("%s: tag orig 0x%x reply 0x%x\n", + sc->name, cmd->tag, bcs->Tag); + goto Bad_End; + } + + switch (bcs->Status) { + case US_BULK_STAT_OK: + break; + case US_BULK_STAT_FAIL: + ub_state_sense(sc, cmd); + return; + case US_BULK_STAT_PHASE: + /* XXX We must reset the transport here */ + /* P3 */ printk("%s: status PHASE\n", sc->name); + goto Bad_End; + default: + printk(KERN_INFO "%s: unknown CSW status 0x%x\n", + sc->name, bcs->Status); + goto Bad_End; + } + + /* Not zeroing error to preserve a babble indicator */ + cmd->state = UB_CMDST_DONE; + ub_cmdtr_state(sc, cmd); + ub_cmdq_pop(sc); + (*cmd->done)(sc, cmd); + + } else if (cmd->state == UB_CMDST_SENSE) { + /* + * We do not look at sense, because even if there was no sense, + * we get into UB_CMDST_SENSE from a STALL or CSW FAIL only. + * We request sense because we want to clear CHECK CONDITION + * on devices with delusions of SCSI, and not because we + * are curious in any way about the sense itself. + */ + /* if ((cmd->top_sense[2] & 0x0F) == NO_SENSE) { foo } */ + + ub_state_done(sc, cmd, -EIO); + } else { + printk(KERN_WARNING "%s: " + "wrong command state %d on device %u\n", + sc->name, cmd->state, sc->dev->devnum); + goto Bad_End; + } + return; + +Bad_End: /* Little Excel is dead */ + ub_state_done(sc, cmd, -EIO); +} + +/* + * Factorization helper for the command state machine: + * Finish the command. + */ +static void ub_state_done(struct ub_dev *sc, struct ub_scsi_cmd *cmd, int rc) +{ + + cmd->error = rc; + cmd->state = UB_CMDST_DONE; + ub_cmdtr_state(sc, cmd); + ub_cmdq_pop(sc); + (*cmd->done)(sc, cmd); +} + +/* + * Factorization helper for the command state machine: + * Submit a CSW read and go to STAT state. + */ +static void ub_state_stat(struct ub_dev *sc, struct ub_scsi_cmd *cmd) +{ + int rc; + + UB_INIT_COMPLETION(sc->work_done); + + sc->last_pipe = sc->recv_bulk_pipe; + usb_fill_bulk_urb(&sc->work_urb, sc->dev, sc->recv_bulk_pipe, + &sc->work_bcs, US_BULK_CS_WRAP_LEN, ub_urb_complete, sc); + sc->work_urb.transfer_flags = URB_ASYNC_UNLINK; + sc->work_urb.actual_length = 0; + sc->work_urb.error_count = 0; + sc->work_urb.status = 0; + + sc->work_timer.expires = jiffies + UB_URB_TIMEOUT; + add_timer(&sc->work_timer); + + if ((rc = usb_submit_urb(&sc->work_urb, GFP_ATOMIC)) != 0) { + /* XXX Clear stalls */ + printk("ub: CSW #%d submit failed (%d)\n", cmd->tag, rc); /* P3 */ + del_timer(&sc->work_timer); + ub_complete(&sc->work_done); + ub_state_done(sc, cmd, rc); + return; + } + + cmd->stat_count = 0; + cmd->state = UB_CMDST_STAT; + ub_cmdtr_state(sc, cmd); +} + +/* + * Factorization helper for the command state machine: + * Submit a REQUEST SENSE and go to SENSE state. + */ +static void ub_state_sense(struct ub_dev *sc, struct ub_scsi_cmd *cmd) +{ + struct ub_scsi_cmd *scmd; + int rc; + + if (cmd->cdb[0] == REQUEST_SENSE) { + rc = -EPIPE; + goto error; + } + + memset(&sc->top_sense, 0, UB_SENSE_SIZE); + scmd = &sc->top_rqs_cmd; + scmd->cdb[0] = REQUEST_SENSE; + scmd->cdb_len = 6; + scmd->dir = UB_DIR_READ; + scmd->state = UB_CMDST_INIT; + scmd->data = sc->top_sense; + scmd->len = UB_SENSE_SIZE; + scmd->done = ub_top_sense_done; + scmd->back = cmd; + + scmd->tag = sc->tagcnt++; + + cmd->state = UB_CMDST_SENSE; + ub_cmdtr_state(sc, cmd); + + ub_cmdq_insert(sc, scmd); + return; + +error: + ub_state_done(sc, cmd, rc); +} + +/* + * A helper for the command's state machine: + * Submit a stall clear. + */ +static int ub_submit_clear_stall(struct ub_dev *sc, struct ub_scsi_cmd *cmd, + int stalled_pipe) +{ + int endp; + struct usb_ctrlrequest *cr; + int rc; + + endp = usb_pipeendpoint(stalled_pipe); + if (usb_pipein (stalled_pipe)) + endp |= USB_DIR_IN; + + cr = &sc->work_cr; + cr->bRequestType = USB_RECIP_ENDPOINT; + cr->bRequest = USB_REQ_CLEAR_FEATURE; + cr->wValue = cpu_to_le16(USB_ENDPOINT_HALT); + cr->wIndex = cpu_to_le16(endp); + cr->wLength = cpu_to_le16(0); + + UB_INIT_COMPLETION(sc->work_done); + + usb_fill_control_urb(&sc->work_urb, sc->dev, sc->send_ctrl_pipe, + (unsigned char*) cr, NULL, 0, ub_urb_complete, sc); + sc->work_urb.transfer_flags = URB_ASYNC_UNLINK; + sc->work_urb.actual_length = 0; + sc->work_urb.error_count = 0; + sc->work_urb.status = 0; + + sc->work_timer.expires = jiffies + UB_CTRL_TIMEOUT; + add_timer(&sc->work_timer); + + if ((rc = usb_submit_urb(&sc->work_urb, GFP_ATOMIC)) != 0) { + del_timer(&sc->work_timer); + ub_complete(&sc->work_done); + return rc; + } + return 0; +} + +/* + */ +static void ub_top_sense_done(struct ub_dev *sc, struct ub_scsi_cmd *scmd) +{ + unsigned char *sense = scmd->data; + struct ub_scsi_cmd *cmd; + + ub_cmdtr_sense(sc, scmd, sense); + + if ((cmd = ub_cmdq_peek(sc)) == NULL) { + printk(KERN_WARNING "%s: sense done while idle\n", sc->name); + return; + } + if (cmd != scmd->back) { + printk(KERN_WARNING "%s: " + "sense done for wrong command 0x%x on device %u\n", + sc->name, cmd->tag, sc->dev->devnum); + return; + } + if (cmd->state != UB_CMDST_SENSE) { + printk(KERN_WARNING "%s: " + "sense done with bad cmd state %d on device %u\n", + sc->name, cmd->state, sc->dev->devnum); + return; + } + + ub_scsi_urb_compl(sc, cmd); +} + +#if 0 +/* Determine what the maximum LUN supported is */ +int usb_stor_Bulk_max_lun(struct us_data *us) +{ + int result; + + /* issue the command */ + result = usb_stor_control_msg(us, us->recv_ctrl_pipe, + US_BULK_GET_MAX_LUN, + USB_DIR_IN | USB_TYPE_CLASS | + USB_RECIP_INTERFACE, + 0, us->ifnum, us->iobuf, 1, HZ); + + /* + * Some devices (i.e. Iomega Zip100) need this -- apparently + * the bulk pipes get STALLed when the GetMaxLUN request is + * processed. This is, in theory, harmless to all other devices + * (regardless of if they stall or not). + */ + if (result < 0) { + usb_stor_clear_halt(us, us->recv_bulk_pipe); + usb_stor_clear_halt(us, us->send_bulk_pipe); + } + + US_DEBUGP("GetMaxLUN command result is %d, data is %d\n", + result, us->iobuf[0]); + + /* if we have a successful request, return the result */ + if (result == 1) + return us->iobuf[0]; + + /* return the default -- no LUNs */ + return 0; +} +#endif + +/* + * This is called from a process context. + */ +static void ub_revalidate(struct ub_dev *sc) +{ + + sc->readonly = 0; /* XXX Query this from the device */ + + /* + * XXX sd.c sets capacity to zero in such case. However, it doesn't + * work for us. In case of zero capacity, block layer refuses to + * have the /dev/uba opened (why?) Set capacity to some random value. + */ + sc->capacity.nsec = 50; + sc->capacity.bsize = 512; + sc->capacity.bshift = 0; + + if (ub_sync_tur(sc) != 0) + return; /* Not ready */ + sc->changed = 0; + + if (ub_sync_read_cap(sc, &sc->capacity) != 0) { + /* + * The retry here means something is wrong, either with the + * device, with the transport, or with our code. + * We keep this because sd.c has retries for capacity. + */ + if (ub_sync_read_cap(sc, &sc->capacity) != 0) { + sc->capacity.nsec = 100; + sc->capacity.bsize = 512; + sc->capacity.bshift = 0; + } + } +} + +/* + * The open funcion. + * This is mostly needed to keep refcounting, but also to support + * media checks on removable media drives. + */ +static int ub_bd_open(struct inode *inode, struct file *filp) +{ + struct gendisk *disk = inode->i_bdev->bd_disk; + struct ub_dev *sc; + unsigned long flags; + int rc; + + if ((sc = disk->private_data) == NULL) + return -ENXIO; + spin_lock_irqsave(&ub_lock, flags); + if (atomic_read(&sc->poison)) { + spin_unlock_irqrestore(&ub_lock, flags); + return -ENXIO; + } + sc->openc++; + spin_unlock_irqrestore(&ub_lock, flags); + + if (sc->removable || sc->readonly) + check_disk_change(inode->i_bdev); + + /* XXX sd.c and floppy.c bail on open if media is not present. */ + + if (sc->readonly && (filp->f_mode & FMODE_WRITE)) { + rc = -EROFS; + goto err_open; + } + + return 0; + +err_open: + spin_lock_irqsave(&ub_lock, flags); + --sc->openc; + if (sc->openc == 0 && atomic_read(&sc->poison)) + ub_cleanup(sc); + spin_unlock_irqrestore(&ub_lock, flags); + return rc; +} + +/* + */ +static int ub_bd_release(struct inode *inode, struct file *filp) +{ + struct gendisk *disk = inode->i_bdev->bd_disk; + struct ub_dev *sc = disk->private_data; + unsigned long flags; + + spin_lock_irqsave(&ub_lock, flags); + --sc->openc; + if (sc->openc == 0 && atomic_read(&sc->poison)) + ub_cleanup(sc); + spin_unlock_irqrestore(&ub_lock, flags); + return 0; +} + +/* + * The ioctl interface. + */ +static int ub_bd_ioctl(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg) +{ +// void __user *usermem = (void *) arg; +// struct carm_port *port = ino->i_bdev->bd_disk->private_data; +// struct hd_geometry geom; + +#if 0 + switch (cmd) { + case HDIO_GETGEO: + if (usermem == NULL) // XXX Bizzare. Why? + return -EINVAL; + + geom.heads = (u8) port->dev_geom_head; + geom.sectors = (u8) port->dev_geom_sect; + geom.cylinders = port->dev_geom_cyl; + geom.start = get_start_sect(ino->i_bdev); + + if (copy_to_user(usermem, &geom, sizeof(geom))) + return -EFAULT; + return 0; + + default: ; + } +#endif + + return -ENOTTY; +} + +/* + * This is called once a new disk was seen by the block layer or by ub_probe(). + * The main onjective here is to discover the features of the media such as + * the capacity, read-only status, etc. USB storage generally does not + * need to be spun up, but if we needed it, this would be the place. + * + * This call can sleep. + * + * The return code is not used. + */ +static int ub_bd_revalidate(struct gendisk *disk) +{ + struct ub_dev *sc = disk->private_data; + + ub_revalidate(sc); + /* This is pretty much a long term P3 */ + printk(KERN_INFO "%s: device %u capacity nsec %ld bsize %u\n", + sc->name, sc->dev->devnum, sc->capacity.nsec, sc->capacity.bsize); + + set_capacity(disk, sc->capacity.nsec); + // set_disk_ro(sdkp->disk, sc->readonly); + return 0; +} + +/* + * The check is called by the block layer to verify if the media + * is still available. It is supposed to be harmless, lightweight and + * non-intrusive in case the media was not changed. + * + * This call can sleep. + * + * The return code is bool! + */ +static int ub_bd_media_changed(struct gendisk *disk) +{ + struct ub_dev *sc = disk->private_data; + + if (!sc->removable) + return 0; + + /* + * We clean checks always after every command, so this is not + * as dangerous as it looks. If the TEST_UNIT_READY fails here, + * the device is actually not ready with operator or software + * intervention required. One dangerous item might be a drive which + * spins itself down, and come the time to write dirty pages, this + * will fail, then block layer discards the data. Since we never + * spin drives up, such devices simply cannot be used with ub anyway. + */ + if (ub_sync_tur(sc) != 0) { + sc->changed = 1; + /* P3 */ printk("%s: made changed\n", sc->name); + return 1; + } + + /* The sd.c clears this before returning (one-shot flag). Why? */ + /* P3 */ printk("%s: %s changed\n", sc->name, + sc->changed? "is": "was not"); + return sc->changed; +} + +static struct block_device_operations ub_bd_fops = { + .owner = THIS_MODULE, + .open = ub_bd_open, + .release = ub_bd_release, + .ioctl = ub_bd_ioctl, + .media_changed = ub_bd_media_changed, + .revalidate_disk = ub_bd_revalidate, +}; + +/* + * Common ->done routine for commands executed synchronously. + */ +static void ub_probe_done(struct ub_dev *sc, struct ub_scsi_cmd *cmd) +{ + struct completion *cop = cmd->back; + complete(cop); +} + +/* + * Test if the device has a check condition on it, synchronously. + */ +static int ub_sync_tur(struct ub_dev *sc) +{ + struct ub_scsi_cmd *cmd; + enum { ALLOC_SIZE = sizeof(struct ub_scsi_cmd) }; + unsigned long flags; + struct completion compl; + int rc; + + init_completion(&compl); + + rc = -ENOMEM; + if ((cmd = kmalloc(ALLOC_SIZE, GFP_KERNEL)) == NULL) + goto err_alloc; + memset(cmd, 0, ALLOC_SIZE); + + cmd->cdb[0] = TEST_UNIT_READY; + cmd->cdb_len = 6; + cmd->dir = UB_DIR_NONE; + cmd->state = UB_CMDST_INIT; + cmd->done = ub_probe_done; + cmd->back = &compl; + + spin_lock_irqsave(&sc->lock, flags); + cmd->tag = sc->tagcnt++; + + rc = ub_submit_scsi(sc, cmd); + spin_unlock_irqrestore(&sc->lock, flags); + + if (rc != 0) { + printk("ub: testing ready: submit error (%d)\n", rc); /* P3 */ + goto err_submit; + } + + wait_for_completion(&compl); + + rc = cmd->error; + +err_submit: + kfree(cmd); +err_alloc: + return rc; +} + +/* + * Read the SCSI capacity synchronously (for probing). + */ +static int ub_sync_read_cap(struct ub_dev *sc, struct ub_capacity *ret) +{ + struct ub_scsi_cmd *cmd; + char *p; + enum { ALLOC_SIZE = sizeof(struct ub_scsi_cmd) + 8 }; + unsigned long flags; + unsigned int bsize, shift; + unsigned long nsec; + struct completion compl; + int rc; + + init_completion(&compl); + + rc = -ENOMEM; + if ((cmd = kmalloc(ALLOC_SIZE, GFP_KERNEL)) == NULL) + goto err_alloc; + memset(cmd, 0, ALLOC_SIZE); + p = (char *)cmd + sizeof(struct ub_scsi_cmd); + + cmd->cdb[0] = 0x25; + cmd->cdb_len = 10; + cmd->dir = UB_DIR_READ; + cmd->state = UB_CMDST_INIT; + cmd->data = p; + cmd->len = 8; + cmd->done = ub_probe_done; + cmd->back = &compl; + + spin_lock_irqsave(&sc->lock, flags); + cmd->tag = sc->tagcnt++; + + rc = ub_submit_scsi(sc, cmd); + spin_unlock_irqrestore(&sc->lock, flags); + + if (rc != 0) { + printk("ub: reading capacity: submit error (%d)\n", rc); /* P3 */ + goto err_submit; + } + + wait_for_completion(&compl); + + if (cmd->error != 0) { + printk("ub: reading capacity: error %d\n", cmd->error); /* P3 */ + rc = -EIO; + goto err_read; + } + if (cmd->act_len != 8) { + printk("ub: reading capacity: size %d\n", cmd->act_len); /* P3 */ + rc = -EIO; + goto err_read; + } + + /* sd.c special-cases sector size of 0 to mean 512. Needed? Safe? */ + nsec = be32_to_cpu(*(u32 *)p) + 1; + bsize = be32_to_cpu(*(u32 *)(p + 4)); + switch (bsize) { + case 512: shift = 0; break; + case 1024: shift = 1; break; + case 2048: shift = 2; break; + case 4096: shift = 3; break; + default: + printk("ub: Bad sector size %u\n", bsize); /* P3 */ + rc = -EDOM; + goto err_inv_bsize; + } + + ret->bsize = bsize; + ret->bshift = shift; + ret->nsec = nsec << shift; + rc = 0; + +err_inv_bsize: +err_read: +err_submit: + kfree(cmd); +err_alloc: + return rc; +} + +/* + */ +static void ub_probe_urb_complete(struct urb *urb, struct pt_regs *pt) +{ + struct completion *cop = urb->context; + complete(cop); +} + +static void ub_probe_timeout(unsigned long arg) +{ + struct completion *cop = (struct completion *) arg; + complete(cop); +} + +/* + * Clear initial stalls. + */ +static int ub_probe_clear_stall(struct ub_dev *sc, int stalled_pipe) +{ + int endp; + struct usb_ctrlrequest *cr; + struct completion compl; + struct timer_list timer; + int rc; + + init_completion(&compl); + + endp = usb_pipeendpoint(stalled_pipe); + if (usb_pipein (stalled_pipe)) + endp |= USB_DIR_IN; + + cr = &sc->work_cr; + cr->bRequestType = USB_RECIP_ENDPOINT; + cr->bRequest = USB_REQ_CLEAR_FEATURE; + cr->wValue = cpu_to_le16(USB_ENDPOINT_HALT); + cr->wIndex = cpu_to_le16(endp); + cr->wLength = cpu_to_le16(0); + + usb_fill_control_urb(&sc->work_urb, sc->dev, sc->send_ctrl_pipe, + (unsigned char*) cr, NULL, 0, ub_probe_urb_complete, &compl); + sc->work_urb.transfer_flags = 0; + sc->work_urb.actual_length = 0; + sc->work_urb.error_count = 0; + sc->work_urb.status = 0; + + init_timer(&timer); + timer.function = ub_probe_timeout; + timer.data = (unsigned long) &compl; + timer.expires = jiffies + UB_CTRL_TIMEOUT; + add_timer(&timer); + + if ((rc = usb_submit_urb(&sc->work_urb, GFP_KERNEL)) != 0) { + printk(KERN_WARNING + "%s: Unable to submit a probe clear (%d)\n", sc->name, rc); + del_timer_sync(&timer); + return rc; + } + + wait_for_completion(&compl); + + del_timer_sync(&timer); + /* + * Most of the time, URB was done and dev set to NULL, and so + * the unlink bounces out with ENODEV. We do not call usb_kill_urb + * because we still think about a backport to 2.4. + */ + usb_unlink_urb(&sc->work_urb); + + /* reset the endpoint toggle */ + usb_settoggle(sc->dev, endp, usb_pipeout(sc->last_pipe), 0); + + return 0; +} + +/* + * Get the pipe settings. + */ +static int ub_get_pipes(struct ub_dev *sc, struct usb_device *dev, + struct usb_interface *intf) +{ + struct usb_host_interface *altsetting = intf->cur_altsetting; + struct usb_endpoint_descriptor *ep_in = NULL; + struct usb_endpoint_descriptor *ep_out = NULL; + struct usb_endpoint_descriptor *ep; + int i; + + /* + * Find the endpoints we need. + * We are expecting a minimum of 2 endpoints - in and out (bulk). + * We will ignore any others. + */ + for (i = 0; i < altsetting->desc.bNumEndpoints; i++) { + ep = &altsetting->endpoint[i].desc; + + /* Is it a BULK endpoint? */ + if ((ep->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) + == USB_ENDPOINT_XFER_BULK) { + /* BULK in or out? */ + if (ep->bEndpointAddress & USB_DIR_IN) + ep_in = ep; + else + ep_out = ep; + } + } + + if (ep_in == NULL || ep_out == NULL) { + printk(KERN_NOTICE "%s: device %u failed endpoint check\n", + sc->name, sc->dev->devnum); + return -EIO; + } + + /* Calculate and store the pipe values */ + sc->send_ctrl_pipe = usb_sndctrlpipe(dev, 0); + sc->recv_ctrl_pipe = usb_rcvctrlpipe(dev, 0); + sc->send_bulk_pipe = usb_sndbulkpipe(dev, + ep_out->bEndpointAddress & USB_ENDPOINT_NUMBER_MASK); + sc->recv_bulk_pipe = usb_rcvbulkpipe(dev, + ep_in->bEndpointAddress & USB_ENDPOINT_NUMBER_MASK); + + return 0; +} + +/* + * Probing is done in the process context, which allows us to cheat + * and not to build a state machine for the discovery. + */ +static int ub_probe(struct usb_interface *intf, + const struct usb_device_id *dev_id) +{ + struct ub_dev *sc; + request_queue_t *q; + struct gendisk *disk; + int rc; + + rc = -ENOMEM; + if ((sc = kmalloc(sizeof(struct ub_dev), GFP_KERNEL)) == NULL) + goto err_core; + memset(sc, 0, sizeof(struct ub_dev)); + spin_lock_init(&sc->lock); + usb_init_urb(&sc->work_urb); + tasklet_init(&sc->tasklet, ub_scsi_action, (unsigned long)sc); + atomic_set(&sc->poison, 0); + + init_timer(&sc->work_timer); + sc->work_timer.data = (unsigned long) sc; + sc->work_timer.function = ub_urb_timeout; + + ub_init_completion(&sc->work_done); + sc->work_done.done = 1; /* A little yuk, but oh well... */ + + rc = -ENOSR; + if ((sc->id = ub_id_get()) == -1) + goto err_id; + snprintf(sc->name, 8, DRV_NAME "%c", sc->id + 'a'); + + sc->dev = interface_to_usbdev(intf); + sc->intf = intf; + // sc->ifnum = intf->cur_altsetting->desc.bInterfaceNumber; + + usb_set_intfdata(intf, sc); + usb_get_dev(sc->dev); + // usb_get_intf(sc->intf); /* Do we need this? */ + + /* XXX Verify that we can handle the device (from descriptors) */ + + ub_get_pipes(sc, sc->dev, intf); + + if (device_create_file(&sc->intf->dev, &dev_attr_diag) != 0) + goto err_diag; + + /* + * At this point, all USB initialization is done, do upper layer. + * We really hate halfway initialized structures, so from the + * invariants perspective, this ub_dev is fully constructed at + * this point. + */ + + /* + * This is needed to clear toggles. It is a problem only if we do + * `rmmod ub && modprobe ub` without disconnects, but we like that. + */ + ub_probe_clear_stall(sc, sc->recv_bulk_pipe); + ub_probe_clear_stall(sc, sc->send_bulk_pipe); + + /* + * The way this is used by the startup code is a little specific. + * A SCSI check causes a USB stall. Our common case code sees it + * and clears the check, after which the device is ready for use. + * But if a check was not present, any command other than + * TEST_UNIT_READY ends with a lockup (including REQUEST_SENSE). + * + * If we neglect to clear the SCSI check, the first real command fails + * (which is the capacity readout). We clear that and retry, but why + * causing spurious retries for no reason. + * + * Revalidation may start with its own TEST_UNIT_READY, but that one + * has to succeed, so we clear checks with an additional one here. + * In any case it's not our business how revaliadation is implemented. + */ + ub_sync_tur(sc); + + sc->removable = 1; /* XXX Query this from the device */ + + ub_revalidate(sc); + /* This is pretty much a long term P3 */ + printk(KERN_INFO "%s: device %u capacity nsec %ld bsize %u\n", + sc->name, sc->dev->devnum, sc->capacity.nsec, sc->capacity.bsize); + + /* + * Just one disk per sc currently, but maybe more. + */ + rc = -ENOMEM; + if ((disk = alloc_disk(UB_MINORS_PER_MAJOR)) == NULL) + goto err_diskalloc; + + sc->disk = disk; + sprintf(disk->disk_name, DRV_NAME "%c", sc->id + 'a'); + sprintf(disk->devfs_name, DEVFS_NAME "/%c", sc->id + 'a'); + disk->major = UB_MAJOR; + disk->first_minor = sc->id * UB_MINORS_PER_MAJOR; + disk->fops = &ub_bd_fops; + disk->private_data = sc; + disk->driverfs_dev = &intf->dev; + + rc = -ENOMEM; + if ((q = blk_init_queue(ub_bd_rq_fn, &sc->lock)) == NULL) + goto err_blkqinit; + + disk->queue = q; + + // blk_queue_bounce_limit(q, hba[i]->pdev->dma_mask); + blk_queue_max_hw_segments(q, UB_MAX_REQ_SG); + blk_queue_max_phys_segments(q, UB_MAX_REQ_SG); + // blk_queue_segment_boundary(q, CARM_SG_BOUNDARY); + blk_queue_max_sectors(q, UB_MAX_SECTORS); + // blk_queue_hardsect_size(q, xxxxx); + + /* + * This is a serious infraction, caused by a deficiency in the + * USB sg interface (usb_sg_wait()). We plan to remove this once + * we get mileage on the driver and can justify a change to USB API. + * See blk_queue_bounce_limit() to understand this part. + * + * XXX And I still need to be aware of the DMA mask in the HC. + */ + q->bounce_pfn = blk_max_low_pfn; + q->bounce_gfp = GFP_NOIO; + + q->queuedata = sc; + + set_capacity(disk, sc->capacity.nsec); + if (sc->removable) + disk->flags |= GENHD_FL_REMOVABLE; + + add_disk(disk); + + return 0; + +err_blkqinit: + put_disk(disk); +err_diskalloc: + device_remove_file(&sc->intf->dev, &dev_attr_diag); +err_diag: + usb_set_intfdata(intf, NULL); + // usb_put_intf(sc->intf); + usb_put_dev(sc->dev); + spin_lock_irq(&ub_lock); + ub_id_put(sc->id); + spin_unlock_irq(&ub_lock); +err_id: + kfree(sc); +err_core: + return rc; +} + +static void ub_disconnect(struct usb_interface *intf) +{ + struct ub_dev *sc = usb_get_intfdata(intf); + struct gendisk *disk = sc->disk; + request_queue_t *q = disk->queue; + unsigned long flags; + + /* + * Fence stall clearnings, operations triggered by unlinkings and so on. + * We do not attempt to unlink any URBs, because we do not trust the + * unlink paths in HC drivers. Also, we get -84 upon disconnect anyway. + */ + atomic_set(&sc->poison, 1); + + /* + * Blow away queued commands. + * + * Actually, this never works, because before we get here + * the HCD terminates outstanding URB(s). It causes our + * SCSI command queue to advance, commands fail to submit, + * and the whole queue drains. So, we just use this code to + * print warnings. + */ + spin_lock_irqsave(&sc->lock, flags); + { + struct ub_scsi_cmd *cmd; + int cnt = 0; + while ((cmd = ub_cmdq_pop(sc)) != NULL) { + cmd->error = -ENOTCONN; + cmd->state = UB_CMDST_DONE; + ub_cmdtr_state(sc, cmd); + ub_cmdq_pop(sc); + (*cmd->done)(sc, cmd); + cnt++; + } + if (cnt != 0) { + printk(KERN_WARNING "%s: " + "%d was queued after shutdown\n", sc->name, cnt); + } + } + spin_unlock_irqrestore(&sc->lock, flags); + + /* + * Unregister the upper layer, this waits for all commands to end. + */ + if (disk->flags & GENHD_FL_UP) + del_gendisk(disk); + if (q) + blk_cleanup_queue(q); + + /* + * If we zero disk->private_data BEFORE put_disk, we have to check + * for NULL all over the place in open, release, check_media and + * revalidate, because the block level semaphore is well inside the + * put_disk. But we cannot zero after the call, because *disk is gone. + * The sd.c is blatantly racy in this area. + */ + /* disk->private_data = NULL; */ + put_disk(disk); + sc->disk = NULL; + + /* + * We really expect blk_cleanup_queue() to wait, so no amount + * of paranoya is too much. + * + * Taking a lock on a structure which is about to be freed + * is very nonsensual. Here it is largely a way to do a debug freeze, + * and a bracket which shows where the nonsensual code segment ends. + * + * Testing for -EINPROGRESS is always a bug, so we are bending + * the rules a little. + */ + spin_lock_irqsave(&sc->lock, flags); + if (sc->work_urb.status == -EINPROGRESS) { /* janitors: ignore */ + printk(KERN_WARNING "%s: " + "URB is active after disconnect\n", sc->name); + } + spin_unlock_irqrestore(&sc->lock, flags); + + /* + * At this point there must be no commands coming from anyone + * and no URBs left in transit. + */ + + device_remove_file(&sc->intf->dev, &dev_attr_diag); + usb_set_intfdata(intf, NULL); + // usb_put_intf(sc->intf); + sc->intf = NULL; + usb_put_dev(sc->dev); + sc->dev = NULL; + + spin_lock_irqsave(&ub_lock, flags); + if (sc->openc == 0) + ub_cleanup(sc); + spin_unlock_irqrestore(&ub_lock, flags); +} + +struct usb_driver ub_driver = { + .owner = THIS_MODULE, + .name = "ub", + .probe = ub_probe, + .disconnect = ub_disconnect, + .id_table = ub_usb_ids, +}; + +static int __init ub_init(void) +{ + int rc; + + /* P3 */ printk("ub: sizeof ub_scsi_cmd %zu ub_dev %zu\n", + sizeof(struct ub_scsi_cmd), sizeof(struct ub_dev)); + + if ((rc = register_blkdev(UB_MAJOR, DRV_NAME)) != 0) + goto err_regblkdev; + devfs_mk_dir(DEVFS_NAME); + + if ((rc = usb_register(&ub_driver)) != 0) + goto err_register; + + return 0; + +err_register: + devfs_remove(DEVFS_NAME); + unregister_blkdev(UB_MAJOR, DRV_NAME); +err_regblkdev: + return rc; +} + +static void __exit ub_exit(void) +{ + usb_deregister(&ub_driver); + + devfs_remove(DEVFS_NAME); + unregister_blkdev(UB_MAJOR, DRV_NAME); +} + +module_init(ub_init); +module_exit(ub_exit); + +MODULE_LICENSE("GPL"); diff --git a/drivers/cdrom/cdu31a.c b/drivers/cdrom/cdu31a.c index cbe336429..09873fa1b 100644 --- a/drivers/cdrom/cdu31a.c +++ b/drivers/cdrom/cdu31a.c @@ -729,8 +729,7 @@ static void restart_on_error(void) res_reg[1]); } - current->state = TASK_INTERRUPTIBLE; - schedule_timeout(2 * HZ); + msleep(2000); sony_get_toc(); } @@ -960,8 +959,7 @@ retry_cd_operation: if (((result_buffer[0] & 0xf0) == 0x20) && (num_retries < MAX_CDU31A_RETRIES)) { num_retries++; - current->state = TASK_INTERRUPTIBLE; - schedule_timeout(HZ / 10); /* Wait .1 seconds on retries */ + msleep(100); goto retry_cd_operation; } diff --git a/drivers/cdrom/mcd.c b/drivers/cdrom/mcd.c index 4cb0fb491..b4f5e0295 100644 --- a/drivers/cdrom/mcd.c +++ b/drivers/cdrom/mcd.c @@ -1021,10 +1021,9 @@ static int mcd_open(struct cdrom_device_info *cdi, int purpose) st = statusCmd(); /* check drive status */ if (st == -1) goto err_out; /* drive doesn't respond */ - if ((st & MST_READY) == 0) { /* no disk? wait a sec... */ - current->state = TASK_INTERRUPTIBLE; - schedule_timeout(HZ); - } + if ((st & MST_READY) == 0) /* no disk? wait a sec... */ + msleep(1000); + } while (((st & MST_READY) == 0) && count++ < MCD_RETRY_ATTEMPTS); if (updateToc() < 0) diff --git a/drivers/char/.cvsignore b/drivers/char/.cvsignore new file mode 100644 index 000000000..83683a2d8 --- /dev/null +++ b/drivers/char/.cvsignore @@ -0,0 +1,2 @@ +consolemap_deftbl.c +defkeymap.c diff --git a/drivers/char/agp/agp.h b/drivers/char/agp/agp.h index 2128374e7..f83e3e342 100644 --- a/drivers/char/agp/agp.h +++ b/drivers/char/agp/agp.h @@ -123,7 +123,7 @@ struct agp_bridge_data { void *current_size; void *dev_private_data; struct pci_dev *dev; - u32 *gatt_table; + u32 __iomem *gatt_table; u32 *gatt_table_real; unsigned long scratch_page; unsigned long scratch_page_real; diff --git a/drivers/char/agp/hp-agp.c b/drivers/char/agp/hp-agp.c index 5fd2adc96..323a6b68d 100644 --- a/drivers/char/agp/hp-agp.c +++ b/drivers/char/agp/hp-agp.c @@ -59,8 +59,8 @@ static struct gatt_mask hp_zx1_masks[] = }; static struct _hp_private { - volatile u8 *ioc_regs; - volatile u8 *lba_regs; + volatile u8 __iomem *ioc_regs; + volatile u8 __iomem *lba_regs; int lba_cap_offset; u64 *io_pdir; // PDIR for entire IOVA u64 *gatt; // PDIR just for GART (subset of above) @@ -97,7 +97,7 @@ static int __init hp_zx1_ioc_shared(void) default: printk(KERN_ERR PFX "Invalid IOTLB page size " "configuration 0x%x\n", hp->io_tlb_ps); - hp->gatt = 0; + hp->gatt = NULL; hp->gatt_entries = 0; return -ENODEV; } @@ -115,7 +115,7 @@ static int __init hp_zx1_ioc_shared(void) if (hp->gatt[0] != HP_ZX1_SBA_IOMMU_COOKIE) { /* Normal case when no AGP device in system */ - hp->gatt = 0; + hp->gatt = NULL; hp->gatt_entries = 0; printk(KERN_ERR PFX "No reserved IO PDIR entry found; " "GART disabled\n"); @@ -183,7 +183,7 @@ hp_zx1_ioc_init (u64 hpa) } static int -hp_zx1_lba_find_capability (volatile u8 *hpa, int cap) +hp_zx1_lba_find_capability (volatile u8 __iomem *hpa, int cap) { u16 status; u8 pos, id; @@ -267,10 +267,10 @@ hp_zx1_cleanup (void) if (hp->ioc_regs) { if (hp->io_pdir_owner) OUTREG64(hp->ioc_regs, HP_ZX1_IBASE, 0); - iounmap((void *) hp->ioc_regs); + iounmap(hp->ioc_regs); } if (hp->lba_regs) - iounmap((void *) hp->lba_regs); + iounmap(hp->lba_regs); } static void @@ -294,7 +294,7 @@ hp_zx1_create_gatt_table (void) if (!hp->io_pdir) { printk(KERN_ERR PFX "Couldn't allocate contiguous " "memory for I/O PDIR\n"); - hp->gatt = 0; + hp->gatt = NULL; hp->gatt_entries = 0; return -ENOMEM; } diff --git a/drivers/char/amiserial.c b/drivers/char/amiserial.c index 3dc61d41b..f11cf4ea0 100644 --- a/drivers/char/amiserial.c +++ b/drivers/char/amiserial.c @@ -118,10 +118,6 @@ static struct serial_state rs_table[1]; #define NR_PORTS (sizeof(rs_table)/sizeof(struct serial_state)) -#ifndef MIN -#define MIN(a,b) ((a) < (b) ? (a) : (b)) -#endif - /* * tmp_buf is used as a temporary buffer by serial_write. We need to * lock it in case the copy_from_user blocks while swapping in a page, @@ -430,7 +426,7 @@ static _INLINE_ void check_modem_status(struct async_struct *info) if ((info->flags & ASYNC_CHECK_CD) && (dstatus & SER_DCD)) { #if (defined(SERIAL_DEBUG_OPEN) || defined(SERIAL_DEBUG_INTR)) - printk("ttyS%02d CD now %s...", info->line, + printk("ttyS%d CD now %s...", info->line, (!(status & SER_DCD)) ? "on" : "off"); #endif if (!(status & SER_DCD)) @@ -557,9 +553,7 @@ static void do_softint(unsigned long private_) return; if (test_and_clear_bit(RS_EVENT_WRITE_WAKEUP, &info->event)) { - if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && - tty->ldisc.write_wakeup) - (tty->ldisc.write_wakeup)(tty); + tty_wakeup(tty); wake_up_interruptible(&tty->write_wait); } } @@ -1023,9 +1017,7 @@ static void rs_flush_buffer(struct tty_struct *tty) info->xmit.head = info->xmit.tail = 0; local_irq_restore(flags); wake_up_interruptible(&tty->write_wait); - if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && - tty->ldisc.write_wakeup) - (tty->ldisc.write_wakeup)(tty); + tty_wakeup(tty); } /* @@ -1564,8 +1556,8 @@ static void rs_close(struct tty_struct *tty, struct file * filp) shutdown(info); if (tty->driver->flush_buffer) tty->driver->flush_buffer(tty); - if (tty->ldisc.flush_buffer) - tty->ldisc.flush_buffer(tty); + + tty_ldisc_flush(tty); tty->closing = 0; info->event = 0; info->tty = 0; @@ -1610,7 +1602,7 @@ static void rs_wait_until_sent(struct tty_struct *tty, int timeout) if (char_time == 0) char_time = 1; if (timeout) - char_time = MIN(char_time, timeout); + char_time = min_t(unsigned long, char_time, timeout); /* * If the transmitter hasn't cleared in twice the approximate * amount of time to send the entire FIFO, it probably won't @@ -2095,7 +2087,7 @@ static int __init rs_init(void) continue; */ - printk(KERN_INFO "ttyS%02d is the amiga builtin serial port\n", + printk(KERN_INFO "ttyS%d is the amiga builtin serial port\n", state->line); /* Hardware set up */ diff --git a/drivers/char/drm/Makefile b/drivers/char/drm/Makefile index f0acc5e48..d59102e2e 100644 --- a/drivers/char/drm/Makefile +++ b/drivers/char/drm/Makefile @@ -8,6 +8,7 @@ r128-objs := r128_drv.o r128_cce.o r128_state.o r128_irq.o mga-objs := mga_drv.o mga_dma.o mga_state.o mga_warp.o mga_irq.o i810-objs := i810_drv.o i810_dma.o i830-objs := i830_drv.o i830_dma.o i830_irq.o +i915-objs := i915_drv.o i915_dma.o i915_irq.o i915_mem.o radeon-objs := radeon_drv.o radeon_cp.o radeon_state.o radeon_mem.o radeon_irq.o ffb-objs := ffb_drv.o ffb_context.o sis-objs := sis_drv.o sis_ds.o sis_mm.o @@ -19,6 +20,7 @@ obj-$(CONFIG_DRM_RADEON)+= radeon.o obj-$(CONFIG_DRM_MGA) += mga.o obj-$(CONFIG_DRM_I810) += i810.o obj-$(CONFIG_DRM_I830) += i830.o +obj-$(CONFIG_DRM_I915) += i915.o obj-$(CONFIG_DRM_FFB) += ffb.o obj-$(CONFIG_DRM_SIS) += sis.o diff --git a/drivers/char/drm/drm_core.h b/drivers/char/drm/drm_core.h new file mode 100644 index 000000000..8ebab5a7e --- /dev/null +++ b/drivers/char/drm/drm_core.h @@ -0,0 +1,40 @@ +/* + * Copyright 2004 Jon Smirl + * + * 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 + * VIA, S3 GRAPHICS, AND/OR ITS SUPPLIERS 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 "drm_auth.h" +#include "drm_agpsupport.h" +#include "drm_bufs.h" +#include "drm_context.h" +#include "drm_dma.h" +#include "drm_irq.h" +#include "drm_drawable.h" +#include "drm_drv.h" +#include "drm_fops.h" +#include "drm_init.h" +#include "drm_ioctl.h" +#include "drm_lock.h" +#include "drm_memory.h" +#include "drm_proc.h" +#include "drm_vm.h" +#include "drm_stub.h" +#include "drm_scatter.h" diff --git a/drivers/char/drm/drm_irq.h b/drivers/char/drm/drm_irq.h index 04bb0ac11..d26c999fe 100644 --- a/drivers/char/drm/drm_irq.h +++ b/drivers/char/drm/drm_irq.h @@ -37,16 +37,6 @@ #include /* For task queue support */ -#ifndef __HAVE_SHARED_IRQ -#define __HAVE_SHARED_IRQ 0 -#endif - -#if __HAVE_SHARED_IRQ -#define DRM_IRQ_TYPE SA_SHIRQ -#else -#define DRM_IRQ_TYPE 0 -#endif - /** * Get interrupt from bus id. * @@ -68,6 +58,9 @@ int DRM(irq_by_busid)(struct inode *inode, struct file *filp, drm_irq_busid_t __user *argp = (void __user *)arg; drm_irq_busid_t p; + if (!drm_core_check_feature(dev, DRIVER_HAVE_IRQ)) + return -EINVAL; + if (copy_from_user(&p, argp, sizeof(p))) return -EFAULT; @@ -86,8 +79,6 @@ int DRM(irq_by_busid)(struct inode *inode, struct file *filp, return 0; } -#if __HAVE_IRQ - /** * Install IRQ handler. * @@ -101,7 +92,11 @@ int DRM(irq_by_busid)(struct inode *inode, struct file *filp, int DRM(irq_install)( drm_device_t *dev ) { int ret; - + unsigned long sh_flags=0; + + if (!drm_core_check_feature(dev, DRIVER_HAVE_IRQ)) + return -EINVAL; + if ( dev->irq == 0 ) return -EINVAL; @@ -122,32 +117,25 @@ int DRM(irq_install)( drm_device_t *dev ) DRM_DEBUG( "%s: irq=%d\n", __FUNCTION__, dev->irq ); -#if __HAVE_DMA - dev->dma->next_buffer = NULL; - dev->dma->next_queue = NULL; - dev->dma->this_buffer = NULL; -#endif - -#ifdef __HAVE_IRQ_BH - INIT_WORK(&dev->work, DRM(irq_immediate_bh), dev); -#endif - -#ifdef __HAVE_VBL_IRQ - init_waitqueue_head(&dev->vbl_queue); - - spin_lock_init( &dev->vbl_lock ); - - INIT_LIST_HEAD( &dev->vbl_sigs.head ); - - dev->vbl_pending = 0; -#endif + if (drm_core_check_feature(dev, DRIVER_IRQ_VBL)) { + init_waitqueue_head(&dev->vbl_queue); + + spin_lock_init( &dev->vbl_lock ); + + INIT_LIST_HEAD( &dev->vbl_sigs.head ); + + dev->vbl_pending = 0; + } /* Before installing handler */ - DRM(driver_irq_preinstall)(dev); + dev->fn_tbl.irq_preinstall(dev); /* Install handler */ - ret = request_irq( dev->irq, DRM(irq_handler), - DRM_IRQ_TYPE, dev->devname, dev ); + if (drm_core_check_feature(dev, DRIVER_IRQ_SHARED)) + sh_flags = SA_SHIRQ; + + ret = request_irq( dev->irq, dev->fn_tbl.irq_handler, + sh_flags, dev->devname, dev ); if ( ret < 0 ) { down( &dev->struct_sem ); dev->irq_enabled = 0; @@ -156,7 +144,7 @@ int DRM(irq_install)( drm_device_t *dev ) } /* After installing handler */ - DRM(driver_irq_postinstall)(dev); + dev->fn_tbl.irq_postinstall(dev); return 0; } @@ -172,6 +160,9 @@ int DRM(irq_uninstall)( drm_device_t *dev ) { int irq_enabled; + if (!drm_core_check_feature(dev, DRIVER_HAVE_IRQ)) + return -EINVAL; + down( &dev->struct_sem ); irq_enabled = dev->irq_enabled; dev->irq_enabled = 0; @@ -182,7 +173,7 @@ int DRM(irq_uninstall)( drm_device_t *dev ) DRM_DEBUG( "%s: irq=%d\n", __FUNCTION__, dev->irq ); - DRM(driver_irq_uninstall)( dev ); + dev->fn_tbl.irq_uninstall(dev); free_irq( dev->irq, dev ); @@ -206,25 +197,29 @@ int DRM(control)( struct inode *inode, struct file *filp, drm_file_t *priv = filp->private_data; drm_device_t *dev = priv->dev; drm_control_t ctl; + + /* if we haven't irq we fallback for compatibility reasons - this used to be a separate function in drm_dma.h */ if ( copy_from_user( &ctl, (drm_control_t __user *)arg, sizeof(ctl) ) ) return -EFAULT; switch ( ctl.func ) { case DRM_INST_HANDLER: + if (!drm_core_check_feature(dev, DRIVER_HAVE_IRQ)) + return 0; if (dev->if_version < DRM_IF_VERSION(1, 2) && ctl.irq != dev->irq) return -EINVAL; return DRM(irq_install)( dev ); case DRM_UNINST_HANDLER: + if (!drm_core_check_feature(dev, DRIVER_HAVE_IRQ)) + return 0; return DRM(irq_uninstall)( dev ); default: return -EINVAL; } } -#ifdef __HAVE_VBL_IRQ - /** * Wait for VBLANK. * @@ -254,6 +249,9 @@ int DRM(wait_vblank)( DRM_IOCTL_ARGS ) int ret = 0; unsigned int flags; + if (!drm_core_check_feature(dev, DRIVER_IRQ_VBL)) + return -EINVAL; + if (!dev->irq) return -EINVAL; @@ -318,7 +316,8 @@ int DRM(wait_vblank)( DRM_IOCTL_ARGS ) spin_unlock_irqrestore( &dev->vbl_lock, irqflags ); } else { - ret = DRM(vblank_wait)( dev, &vblwait.request.sequence ); + if (dev->fn_tbl.vblank_wait) + ret = dev->fn_tbl.vblank_wait( dev, &vblwait.request.sequence ); do_gettimeofday( &now ); vblwait.reply.tval_sec = now.tv_sec; @@ -366,6 +365,4 @@ void DRM(vbl_send_signals)( drm_device_t *dev ) spin_unlock_irqrestore( &dev->vbl_lock, flags ); } -#endif /* __HAVE_VBL_IRQ */ -#endif /* __HAVE_IRQ */ diff --git a/drivers/char/drm/drm_memory.h b/drivers/char/drm/drm_memory.h index 9adfe790d..c68e8d5eb 100644 --- a/drivers/char/drm/drm_memory.h +++ b/drivers/char/drm/drm_memory.h @@ -44,7 +44,7 @@ */ #define DEBUG_MEMORY 0 -#if __REALLY_HAVE_AGP +#if __OS_HAS_AGP #include @@ -130,46 +130,56 @@ drm_follow_page (void *vaddr) return pte_pfn(*ptep) << PAGE_SHIFT; } -#endif /* __REALLY_HAVE_AGP */ +#else /* __OS_HAS_AGP */ + +static inline drm_map_t *drm_lookup_map(unsigned long offset, unsigned long size, drm_device_t *dev) +{ + return NULL; +} + +static inline void *agp_remap(unsigned long offset, unsigned long size, drm_device_t *dev) +{ + return NULL; +} + +static inline unsigned long drm_follow_page (void *vaddr) +{ + return 0; +} + +#endif static inline void *drm_ioremap(unsigned long offset, unsigned long size, drm_device_t *dev) { -#if __REALLY_HAVE_AGP - if (dev->agp && dev->agp->cant_use_aperture) { + if (drm_core_has_AGP(dev) && dev->agp && dev->agp->cant_use_aperture) { drm_map_t *map = drm_lookup_map(offset, size, dev); if (map && map->type == _DRM_AGP) return agp_remap(offset, size, dev); } -#endif - return ioremap(offset, size); } static inline void *drm_ioremap_nocache(unsigned long offset, unsigned long size, drm_device_t *dev) { -#if __REALLY_HAVE_AGP - if (dev->agp && dev->agp->cant_use_aperture) { + if (drm_core_has_AGP(dev) && dev->agp && dev->agp->cant_use_aperture) { drm_map_t *map = drm_lookup_map(offset, size, dev); if (map && map->type == _DRM_AGP) return agp_remap(offset, size, dev); } -#endif - return ioremap_nocache(offset, size); } static inline void drm_ioremapfree(void *pt, unsigned long size, drm_device_t *dev) { -#if __REALLY_HAVE_AGP /* * This is a bit ugly. It would be much cleaner if the DRM API would use separate * routines for handling mappings in the AGP space. Hopefully this can be done in * a future revision of the interface... */ - if (dev->agp && dev->agp->cant_use_aperture + if (drm_core_has_AGP(dev) && dev->agp && dev->agp->cant_use_aperture && ((unsigned long) pt >= VMALLOC_START && (unsigned long) pt < VMALLOC_END)) { unsigned long offset; @@ -182,11 +192,11 @@ static inline void drm_ioremapfree(void *pt, unsigned long size, drm_device_t *d return; } } -#endif iounmap(pt); } + #if DEBUG_MEMORY #include "drm_memory_debug.h" #else @@ -331,7 +341,7 @@ void DRM(ioremapfree)(void *pt, unsigned long size, drm_device_t *dev) drm_ioremapfree(pt, size, dev); } -#if __REALLY_HAVE_AGP +#if __OS_HAS_AGP /** Wrapper around agp_allocate_memory() */ DRM_AGP_MEM *DRM(alloc_agp)(int pages, u32 type) { diff --git a/drivers/char/drm/drm_pciids.h b/drivers/char/drm/drm_pciids.h index 3b5f8d3a2..768c131d2 100644 --- a/drivers/char/drm/drm_pciids.h +++ b/drivers/char/drm/drm_pciids.h @@ -201,3 +201,11 @@ #define ffb_PCI_IDS \ {0, 0, 0} +#define i915_PCI_IDS \ + {0x8086, 0x3577, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, \ + {0x8086, 0x2562, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, \ + {0x8086, 0x3582, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, \ + {0x8086, 0x2572, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, \ + {0x8086, 0x2582, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, \ + {0, 0, 0} + diff --git a/drivers/char/drm/drm_scatter.h b/drivers/char/drm/drm_scatter.h index e5d829367..436a0170e 100644 --- a/drivers/char/drm/drm_scatter.h +++ b/drivers/char/drm/drm_scatter.h @@ -73,6 +73,9 @@ int DRM(sg_alloc)( struct inode *inode, struct file *filp, DRM_DEBUG( "%s\n", __FUNCTION__ ); + if (!drm_core_check_feature(dev, DRIVER_SG)) + return -EINVAL; + if ( dev->sg ) return -EINVAL; @@ -206,6 +209,9 @@ int DRM(sg_free)( struct inode *inode, struct file *filp, drm_scatter_gather_t request; drm_sg_mem_t *entry; + if (!drm_core_check_feature(dev, DRIVER_SG)) + return -EINVAL; + if ( copy_from_user( &request, (drm_scatter_gather_t __user *)arg, sizeof(request) ) ) diff --git a/drivers/char/drm/ffb_context.c b/drivers/char/drm/ffb_context.c index 6fae05c8b..f51812078 100644 --- a/drivers/char/drm/ffb_context.c +++ b/drivers/char/drm/ffb_context.c @@ -354,7 +354,7 @@ static void FFBWait(ffb_fbcPtr ffb) } while (--limit); } -int DRM(context_switch)(drm_device_t *dev, int old, int new) +int ffb_driver_context_switch(drm_device_t *dev, int old, int new) { ffb_dev_priv_t *fpriv = (ffb_dev_priv_t *) dev->dev_private; @@ -380,7 +380,7 @@ int DRM(context_switch)(drm_device_t *dev, int old, int new) return 0; } -int DRM(resctx)(struct inode *inode, struct file *filp, unsigned int cmd, +int ffb_driver_resctx(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg) { drm_ctx_res_t res; @@ -407,7 +407,7 @@ int DRM(resctx)(struct inode *inode, struct file *filp, unsigned int cmd, } -int DRM(addctx)(struct inode *inode, struct file *filp, unsigned int cmd, +int ffb_driver_addctx(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg) { drm_file_t *priv = filp->private_data; @@ -428,7 +428,7 @@ int DRM(addctx)(struct inode *inode, struct file *filp, unsigned int cmd, return 0; } -int DRM(modctx)(struct inode *inode, struct file *filp, unsigned int cmd, +int ffb_driver_modctx(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg) { drm_file_t *priv = filp->private_data; @@ -457,7 +457,7 @@ int DRM(modctx)(struct inode *inode, struct file *filp, unsigned int cmd, return 0; } -int DRM(getctx)(struct inode *inode, struct file *filp, unsigned int cmd, +int ffb_driver_getctx(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg) { drm_file_t *priv = filp->private_data; @@ -489,7 +489,7 @@ int DRM(getctx)(struct inode *inode, struct file *filp, unsigned int cmd, return 0; } -int DRM(switchctx)(struct inode *inode, struct file *filp, unsigned int cmd, +int ffb_driver_switchctx(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg) { drm_file_t *priv = filp->private_data; @@ -499,10 +499,10 @@ int DRM(switchctx)(struct inode *inode, struct file *filp, unsigned int cmd, if (copy_from_user(&ctx, (drm_ctx_t __user *)arg, sizeof(ctx))) return -EFAULT; DRM_DEBUG("%d\n", ctx.handle); - return DRM(context_switch)(dev, dev->last_context, ctx.handle); + return ffb_driver_context_switch(dev, dev->last_context, ctx.handle); } -int DRM(newctx)(struct inode *inode, struct file *filp, unsigned int cmd, +int ffb_driver_newctx(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg) { drm_ctx_t ctx; @@ -514,7 +514,7 @@ int DRM(newctx)(struct inode *inode, struct file *filp, unsigned int cmd, return 0; } -int DRM(rmctx)(struct inode *inode, struct file *filp, unsigned int cmd, +int ffb_driver_rmctx(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg) { drm_ctx_t ctx; @@ -537,3 +537,15 @@ int DRM(rmctx)(struct inode *inode, struct file *filp, unsigned int cmd, } return 0; } + +void ffb_set_context_ioctls(void) +{ + DRM(ioctls)[DRM_IOCTL_NR(DRM_IOCTL_ADD_CTX)].func = ffb_driver_addctx; + DRM(ioctls)[DRM_IOCTL_NR(DRM_IOCTL_RM_CTX)].func = ffb_driver_rmctx; + DRM(ioctls)[DRM_IOCTL_NR(DRM_IOCTL_MOD_CTX)].func = ffb_driver_modctx; + DRM(ioctls)[DRM_IOCTL_NR(DRM_IOCTL_GET_CTX)].func = ffb_driver_getctx; + DRM(ioctls)[DRM_IOCTL_NR(DRM_IOCTL_SWITCH_CTX)].func = ffb_driver_switchctx; + DRM(ioctls)[DRM_IOCTL_NR(DRM_IOCTL_NEW_CTX)].func = ffb_driver_newctx; + DRM(ioctls)[DRM_IOCTL_NR(DRM_IOCTL_RES_CTX)].func = ffb_driver_resctx; + +} diff --git a/drivers/char/drm/ffb_drv.h b/drivers/char/drm/ffb_drv.h index 094bbce09..3b3546715 100644 --- a/drivers/char/drm/ffb_drv.h +++ b/drivers/char/drm/ffb_drv.h @@ -274,3 +274,13 @@ typedef struct ffb_dev_priv { /* Context table. */ struct ffb_hw_context *hw_state[FFB_MAX_CTXS]; } ffb_dev_priv_t; + +extern struct file_operations DRM(fops); +extern unsigned long ffb_get_unmapped_area(struct file *filp, + unsigned long hint, + unsigned long len, + unsigned long pgoff, + unsigned long flags); +extern void ffb_set_context_ioctls(void); +extern drm_ioctl_desc_t DRM(ioctls)[]; + diff --git a/drivers/char/drm/gamma_context.h b/drivers/char/drm/gamma_context.h index d3c8c29c2..d11b507f8 100644 --- a/drivers/char/drm/gamma_context.h +++ b/drivers/char/drm/gamma_context.h @@ -42,7 +42,7 @@ the circular buffer), is based on Alessandro Rubini's LINUX DEVICE DRIVERS (Cambridge: O'Reilly, 1998), pages 111-113. */ -ssize_t DRM(read)(struct file *filp, char __user *buf, size_t count, loff_t *off) +ssize_t gamma_fops_read(struct file *filp, char __user *buf, size_t count, loff_t *off) { drm_file_t *priv = filp->private_data; drm_device_t *dev = priv->dev; @@ -128,7 +128,7 @@ int DRM(write_string)(drm_device_t *dev, const char *s) return 0; } -unsigned int DRM(poll)(struct file *filp, struct poll_table_struct *wait) +unsigned int gamma_fops_poll(struct file *filp, struct poll_table_struct *wait) { drm_file_t *priv = filp->private_data; drm_device_t *dev = priv->dev; diff --git a/drivers/char/drm/gamma_drv.h b/drivers/char/drm/gamma_drv.h index fc78a11a3..146fcc625 100644 --- a/drivers/char/drm/gamma_drv.h +++ b/drivers/char/drm/gamma_drv.h @@ -35,7 +35,6 @@ typedef struct drm_gamma_private { drm_gamma_sarea_t *sarea_priv; drm_map_t *sarea; - drm_map_t *buffers; drm_map_t *mmio0; drm_map_t *mmio1; drm_map_t *mmio2; @@ -91,6 +90,10 @@ extern int DRM(freelist_put)(drm_device_t *dev, drm_freelist_t *bl, drm_buf_t *buf); extern drm_buf_t *DRM(freelist_get)(drm_freelist_t *bl, int block); +/* externs for gamma changes to the ops */ +extern struct file_operations DRM(fops); +extern unsigned int gamma_fops_poll(struct file *filp, struct poll_table_struct *wait); +extern ssize_t gamma_fops_read(struct file *filp, char __user *buf, size_t count, loff_t *off); #define GLINT_DRI_BUF_COUNT 256 diff --git a/drivers/char/drm/i810_drv.c b/drivers/char/drm/i810_drv.c index 0bc793864..00ca55166 100644 --- a/drivers/char/drm/i810_drv.c +++ b/drivers/char/drm/i810_drv.c @@ -37,19 +37,4 @@ #include "i810_drm.h" #include "i810_drv.h" -#include "drm_agpsupport.h" -#include "drm_auth.h" -#include "drm_bufs.h" -#include "drm_context.h" -#include "drm_dma.h" -#include "drm_drawable.h" -#include "drm_drv.h" - -#include "drm_fops.h" -#include "drm_init.h" -#include "drm_ioctl.h" -#include "drm_lock.h" -#include "drm_memory.h" -#include "drm_proc.h" -#include "drm_vm.h" -#include "drm_stub.h" +#include "drm_core.h" diff --git a/drivers/char/drm/i810_drv.h b/drivers/char/drm/i810_drv.h index 736c20d76..67c5c88bf 100644 --- a/drivers/char/drm/i810_drv.h +++ b/drivers/char/drm/i810_drv.h @@ -53,7 +53,6 @@ typedef struct _drm_i810_ring_buffer{ typedef struct drm_i810_private { drm_map_t *sarea_map; - drm_map_t *buffer_map; drm_map_t *mmio_map; drm_i810_sarea_t *sarea_priv; diff --git a/drivers/char/drm/i830_drv.h b/drivers/char/drm/i830_drv.h index c6d805fc7..c3cab2d83 100644 --- a/drivers/char/drm/i830_drv.h +++ b/drivers/char/drm/i830_drv.h @@ -53,7 +53,6 @@ typedef struct _drm_i830_ring_buffer{ typedef struct drm_i830_private { drm_map_t *sarea_map; - drm_map_t *buffer_map; drm_map_t *mmio_map; drm_i830_sarea_t *sarea_priv; @@ -137,6 +136,10 @@ extern int i830_irq_wait( struct inode *inode, struct file *filp, extern int i830_wait_irq(drm_device_t *dev, int irq_nr); extern int i830_emit_irq(drm_device_t *dev); +extern irqreturn_t i830_driver_irq_handler( DRM_IRQ_ARGS ); +extern void i830_driver_irq_preinstall( drm_device_t *dev ); +extern void i830_driver_irq_postinstall( drm_device_t *dev ); +extern void i830_driver_irq_uninstall( drm_device_t *dev ); #define I830_BASE(reg) ((unsigned long) \ dev_priv->mmio_map->handle) diff --git a/drivers/char/drm/i915.h b/drivers/char/drm/i915.h new file mode 100644 index 000000000..d76d737b4 --- /dev/null +++ b/drivers/char/drm/i915.h @@ -0,0 +1,53 @@ +/* i915.h -- Intel I915 DRM template customization -*- linux-c -*- + */ +/************************************************************************** + * + * Copyright 2003 Tungsten Graphics, Inc., Cedar Park, Texas. + * All Rights Reserved. + * + **************************************************************************/ + +#ifndef __I915_H__ +#define __I915_H__ + +/* This remains constant for all DRM template files. + */ +#define DRM(x) i915_##x + +/* General customization: + */ + +#define DRIVER_AUTHOR "Tungsten Graphics, Inc." + +#define DRIVER_NAME "i915" +#define DRIVER_DESC "Intel Graphics" +#define DRIVER_DATE "20040405" + +/* Interface history: + * + * 1.1: Original. + */ +#define DRIVER_MAJOR 1 +#define DRIVER_MINOR 1 +#define DRIVER_PATCHLEVEL 0 + +#define DRIVER_IOCTLS \ + [DRM_IOCTL_NR(DRM_IOCTL_I915_INIT)] = { i915_dma_init, 1, 1 }, \ + [DRM_IOCTL_NR(DRM_IOCTL_I915_FLUSH)] = { i915_flush_ioctl, 1, 0 }, \ + [DRM_IOCTL_NR(DRM_IOCTL_I915_FLIP)] = { i915_flip_bufs, 1, 0 }, \ + [DRM_IOCTL_NR(DRM_IOCTL_I915_BATCHBUFFER)] = { i915_batchbuffer, 1, 0 }, \ + [DRM_IOCTL_NR(DRM_IOCTL_I915_IRQ_EMIT)] = { i915_irq_emit, 1, 0 }, \ + [DRM_IOCTL_NR(DRM_IOCTL_I915_IRQ_WAIT)] = { i915_irq_wait, 1, 0 }, \ + [DRM_IOCTL_NR(DRM_IOCTL_I915_GETPARAM)] = { i915_getparam, 1, 0 }, \ + [DRM_IOCTL_NR(DRM_IOCTL_I915_SETPARAM)] = { i915_setparam, 1, 1 }, \ + [DRM_IOCTL_NR(DRM_IOCTL_I915_ALLOC)] = { i915_mem_alloc, 1, 0 }, \ + [DRM_IOCTL_NR(DRM_IOCTL_I915_FREE)] = { i915_mem_free, 1, 0 }, \ + [DRM_IOCTL_NR(DRM_IOCTL_I915_INIT_HEAP)] = { i915_mem_init_heap, 1, 1 }, \ + [DRM_IOCTL_NR(DRM_IOCTL_I915_CMDBUFFER)] = { i915_cmdbuffer, 1, 0 } + +/* We use our own dma mechanisms, not the drm template code. However, + * the shared IRQ code is useful to us: + */ +#define __HAVE_PM 1 + +#endif diff --git a/drivers/char/drm/i915_dma.c b/drivers/char/drm/i915_dma.c new file mode 100644 index 000000000..e33853b5a --- /dev/null +++ b/drivers/char/drm/i915_dma.c @@ -0,0 +1,755 @@ +/* i915_dma.c -- DMA support for the I915 -*- linux-c -*- + */ +/************************************************************************** + * + * Copyright 2003 Tungsten Graphics, Inc., Cedar Park, Texas. + * All Rights Reserved. + * + **************************************************************************/ + +#include "i915.h" +#include "drmP.h" +#include "drm.h" +#include "i915_drm.h" +#include "i915_drv.h" + +static inline void i915_print_status_page(drm_device_t * dev) +{ + drm_i915_private_t *dev_priv = dev->dev_private; + u32 *temp = dev_priv->hw_status_page; + + if (!temp) { + DRM_DEBUG("no status page\n"); + return; + } + + DRM_DEBUG("hw_status: Interrupt Status : %x\n", temp[0]); + DRM_DEBUG("hw_status: LpRing Head ptr : %x\n", temp[1]); + DRM_DEBUG("hw_status: IRing Head ptr : %x\n", temp[2]); + DRM_DEBUG("hw_status: Reserved : %x\n", temp[3]); + DRM_DEBUG("hw_status: Driver Counter : %d\n", temp[5]); + +} + +/* Really want an OS-independent resettable timer. Would like to have + * this loop run for (eg) 3 sec, but have the timer reset every time + * the head pointer changes, so that EBUSY only happens if the ring + * actually stalls for (eg) 3 seconds. + */ +int i915_wait_ring(drm_device_t * dev, int n, const char *caller) +{ + drm_i915_private_t *dev_priv = dev->dev_private; + drm_i915_ring_buffer_t *ring = &(dev_priv->ring); + u32 last_head = I915_READ(LP_RING + RING_HEAD) & HEAD_ADDR; + int i; + + for (i = 0; i < 10000; i++) { + ring->head = I915_READ(LP_RING + RING_HEAD) & HEAD_ADDR; + ring->space = ring->head - (ring->tail + 8); + if (ring->space < 0) + ring->space += ring->Size; + if (ring->space >= n) + return 0; + + dev_priv->sarea_priv->perf_boxes |= I915_BOX_WAIT; + + if (ring->head != last_head) + i = 0; + + last_head = ring->head; + } + + return DRM_ERR(EBUSY); +} + +void i915_kernel_lost_context(drm_device_t * dev) +{ + drm_i915_private_t *dev_priv = dev->dev_private; + drm_i915_ring_buffer_t *ring = &(dev_priv->ring); + + ring->head = I915_READ(LP_RING + RING_HEAD) & HEAD_ADDR; + ring->tail = I915_READ(LP_RING + RING_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->perf_boxes |= I915_BOX_RING_EMPTY; +} + +int i915_dma_cleanup(drm_device_t * dev) +{ + /* Make sure interrupts are disabled here because the uninstall ioctl + * may not have been called from userspace and after dev_private + * is freed, it's too late. + */ + if (dev->irq) + DRM(irq_uninstall) (dev); + + if (dev->dev_private) { + drm_i915_private_t *dev_priv = + (drm_i915_private_t *) dev->dev_private; + + if (dev_priv->ring.virtual_start) { + drm_core_ioremapfree( &dev_priv->ring.map, dev); + } + + if (dev_priv->hw_status_page) { + pci_free_consistent(dev->pdev, PAGE_SIZE, + dev_priv->hw_status_page, + dev_priv->dma_status_page); + /* Need to rewrite hardware status page */ + I915_WRITE(0x02080, 0x1ffff000); + } + + DRM(free) (dev->dev_private, sizeof(drm_i915_private_t), + DRM_MEM_DRIVER); + + dev->dev_private = NULL; + } + + return 0; +} + +static int i915_initialize(drm_device_t * dev, + drm_i915_private_t * dev_priv, + drm_i915_init_t * init) +{ + memset(dev_priv, 0, sizeof(drm_i915_private_t)); + + DRM_GETSAREA(); + if (!dev_priv->sarea) { + DRM_ERROR("can not find sarea!\n"); + dev->dev_private = (void *)dev_priv; + i915_dma_cleanup(dev); + return DRM_ERR(EINVAL); + } + + dev_priv->mmio_map = drm_core_findmap(dev, init->mmio_offset); + if (!dev_priv->mmio_map) { + dev->dev_private = (void *)dev_priv; + i915_dma_cleanup(dev); + DRM_ERROR("can not find mmio map!\n"); + return DRM_ERR(EINVAL); + } + + dev_priv->sarea_priv = (drm_i915_sarea_t *) + ((u8 *) dev_priv->sarea->handle + init->sarea_priv_offset); + + dev_priv->ring.Start = init->ring_start; + dev_priv->ring.End = init->ring_end; + dev_priv->ring.Size = init->ring_size; + dev_priv->ring.tail_mask = dev_priv->ring.Size - 1; + + dev_priv->ring.map.offset = init->ring_start; + dev_priv->ring.map.size = init->ring_size; + dev_priv->ring.map.type = 0; + dev_priv->ring.map.flags = 0; + dev_priv->ring.map.mtrr = 0; + + drm_core_ioremap( &dev_priv->ring.map, dev ); + + if (dev_priv->ring.map.handle == NULL) { + dev->dev_private = (void *)dev_priv; + i915_dma_cleanup(dev); + DRM_ERROR("can not ioremap virtual address for" + " ring buffer\n"); + return DRM_ERR(ENOMEM); + } + + dev_priv->ring.virtual_start = dev_priv->ring.map.handle; + + 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; + + /* We are using separate values as placeholders for mechanisms for + * private backbuffer/depthbuffer usage. + */ + dev_priv->use_mi_batchbuffer_start = 0; + + /* Allow hardware batchbuffers unless told otherwise. + */ + dev_priv->allow_batchbuffer = 1; + + /* Program Hardware Status Page */ + dev_priv->hw_status_page = + pci_alloc_consistent(dev->pdev, PAGE_SIZE, + &dev_priv->dma_status_page); + + if (!dev_priv->hw_status_page) { + dev->dev_private = (void *)dev_priv; + i915_dma_cleanup(dev); + DRM_ERROR("Can not allocate hardware status page\n"); + return DRM_ERR(ENOMEM); + } + memset(dev_priv->hw_status_page, 0, PAGE_SIZE); + DRM_DEBUG("hw status page @ %p\n", dev_priv->hw_status_page); + + I915_WRITE(0x02080, dev_priv->dma_status_page); + DRM_DEBUG("Enabled hardware status page\n"); + + dev->dev_private = (void *)dev_priv; + + return 0; +} + +static int i915_resume(drm_device_t * dev) +{ + drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private; + + DRM_DEBUG("%s\n", __FUNCTION__); + + if (!dev_priv->sarea) { + DRM_ERROR("can not find sarea!\n"); + return DRM_ERR(EINVAL); + } + + if (!dev_priv->mmio_map) { + DRM_ERROR("can not find mmio map!\n"); + return DRM_ERR(EINVAL); + } + + if (dev_priv->ring.map.handle == NULL) { + DRM_ERROR("can not ioremap virtual address for" + " ring buffer\n"); + return DRM_ERR(ENOMEM); + } + + /* Program Hardware Status Page */ + if (!dev_priv->hw_status_page) { + DRM_ERROR("Can not find hardware status page\n"); + return DRM_ERR(EINVAL); + } + DRM_DEBUG("hw status page @ %p\n", dev_priv->hw_status_page); + + I915_WRITE(0x02080, dev_priv->dma_status_page); + DRM_DEBUG("Enabled hardware status page\n"); + + return 0; +} + +int i915_dma_init(DRM_IOCTL_ARGS) +{ + DRM_DEVICE; + drm_i915_private_t *dev_priv; + drm_i915_init_t init; + int retcode = 0; + + DRM_COPY_FROM_USER_IOCTL(init, (drm_i915_init_t __user *) data, + sizeof(init)); + + switch (init.func) { + case I915_INIT_DMA: + dev_priv = DRM(alloc) (sizeof(drm_i915_private_t), + DRM_MEM_DRIVER); + if (dev_priv == NULL) + return DRM_ERR(ENOMEM); + retcode = i915_initialize(dev, dev_priv, &init); + break; + case I915_CLEANUP_DMA: + retcode = i915_dma_cleanup(dev); + break; + case I915_RESUME_DMA: + retcode = i915_resume(dev); + break; + default: + retcode = -EINVAL; + break; + } + + return retcode; +} + +/* Implement basically the same security restrictions as hardware does + * for MI_BATCH_NON_SECURE. These can be made stricter at any time. + * + * Most of the calculations below involve calculating the size of a + * particular instruction. It's important to get the size right as + * that tells us where the next instruction to check is. Any illegal + * instruction detected will be given a size of zero, which is a + * signal to abort the rest of the buffer. + */ +static int do_validate_cmd(int cmd) +{ + switch (((cmd >> 29) & 0x7)) { + case 0x0: + switch ((cmd >> 23) & 0x3f) { + case 0x0: + return 1; /* MI_NOOP */ + case 0x4: + return 1; /* MI_FLUSH */ + default: + return 0; /* disallow everything else */ + } + break; + case 0x1: + return 0; /* reserved */ + case 0x2: + return (cmd & 0xff) + 2; /* 2d commands */ + case 0x3: + if (((cmd >> 24) & 0x1f) <= 0x18) + return 1; + + switch ((cmd >> 24) & 0x1f) { + case 0x1c: + return 1; + case 0x1d: + switch ((cmd>>16)&0xff) { + case 0x3: + return (cmd & 0x1f) + 2; + case 0x4: + return (cmd & 0xf) + 2; + default: + return (cmd & 0xffff) + 2; + } + case 0x1e: + if (cmd & (1 << 23)) + return (cmd & 0xffff) + 1; + else + return 1; + case 0x1f: + if ((cmd & (1 << 23)) == 0) /* inline vertices */ + return (cmd & 0x1ffff) + 2; + else if (cmd & (1 << 17)) /* indirect random */ + if ((cmd & 0xffff) == 0) + return 0; /* unknown length, too hard */ + else + return (((cmd & 0xffff) + 1) / 2) + 1; + else + return 2; /* indirect sequential */ + default: + return 0; + } + default: + return 0; + } + + return 0; +} + +static int validate_cmd(int cmd) +{ + int ret = do_validate_cmd(cmd); + +/* printk("validate_cmd( %x ): %d\n", cmd, ret); */ + + return ret; +} + +static int i915_emit_cmds(drm_device_t * dev, int __user * buffer, int dwords) +{ + drm_i915_private_t *dev_priv = dev->dev_private; + int i; + RING_LOCALS; + + for (i = 0; i < dwords;) { + int cmd, sz; + + if (DRM_COPY_FROM_USER_UNCHECKED(&cmd, &buffer[i], sizeof(cmd))) + return DRM_ERR(EINVAL); + +/* printk("%d/%d ", i, dwords); */ + + if ((sz = validate_cmd(cmd)) == 0 || i + sz > dwords) + return DRM_ERR(EINVAL); + + BEGIN_LP_RING(sz); + OUT_RING(cmd); + + while (++i, --sz) { + if (DRM_COPY_FROM_USER_UNCHECKED(&cmd, &buffer[i], + sizeof(cmd))) { + return DRM_ERR(EINVAL); + } + OUT_RING(cmd); + } + ADVANCE_LP_RING(); + } + + return 0; +} + +static int i915_emit_box(drm_device_t * dev, + drm_clip_rect_t __user * boxes, + int i, int DR1, int DR4) +{ + drm_i915_private_t *dev_priv = dev->dev_private; + drm_clip_rect_t box; + RING_LOCALS; + + if (DRM_COPY_FROM_USER_UNCHECKED(&box, &boxes[i], sizeof(box))) { + return EFAULT; + } + + if (box.y2 <= box.y1 || box.x2 <= box.x1 || box.y2 <= 0 || box.x2 <= 0) { + DRM_ERROR("Bad box %d,%d..%d,%d\n", + box.x1, box.y1, box.x2, box.y2); + return DRM_ERR(EINVAL); + } + + BEGIN_LP_RING(6); + OUT_RING(GFX_OP_DRAWRECT_INFO); + OUT_RING(DR1); + OUT_RING((box.x1 & 0xffff) | (box.y1 << 16)); + OUT_RING(((box.x2 - 1) & 0xffff) | ((box.y2 - 1) << 16)); + OUT_RING(DR4); + OUT_RING(0); + ADVANCE_LP_RING(); + + return 0; +} + +static int i915_dispatch_cmdbuffer(drm_device_t * dev, + drm_i915_cmdbuffer_t * cmd) +{ + int nbox = cmd->num_cliprects; + int i = 0, count, ret; + + if (cmd->sz & 0x3) { + DRM_ERROR("alignment"); + return DRM_ERR(EINVAL); + } + + i915_kernel_lost_context(dev); + + count = nbox ? nbox : 1; + + for (i = 0; i < count; i++) { + if (i < nbox) { + ret = i915_emit_box(dev, cmd->cliprects, i, + cmd->DR1, cmd->DR4); + if (ret) + return ret; + } + + ret = i915_emit_cmds(dev, (int __user *)cmd->buf, cmd->sz / 4); + if (ret) + return ret; + } + + return 0; +} + +static int i915_dispatch_batchbuffer(drm_device_t * dev, + drm_i915_batchbuffer_t * batch) +{ + drm_i915_private_t *dev_priv = dev->dev_private; + drm_clip_rect_t __user *boxes = batch->cliprects; + int nbox = batch->num_cliprects; + int i = 0, count; + RING_LOCALS; + + if ((batch->start | batch->used) & 0x7) { + DRM_ERROR("alignment"); + return DRM_ERR(EINVAL); + } + + i915_kernel_lost_context(dev); + + count = nbox ? nbox : 1; + + for (i = 0; i < count; i++) { + if (i < nbox) { + int ret = i915_emit_box(dev, boxes, i, + batch->DR1, batch->DR4); + if (ret) + return ret; + } + + if (dev_priv->use_mi_batchbuffer_start) { + BEGIN_LP_RING(2); + OUT_RING(MI_BATCH_BUFFER_START | (2 << 6)); + OUT_RING(batch->start | MI_BATCH_NON_SECURE); + ADVANCE_LP_RING(); + } else { + BEGIN_LP_RING(4); + OUT_RING(MI_BATCH_BUFFER); + OUT_RING(batch->start | MI_BATCH_NON_SECURE); + OUT_RING(batch->start + batch->used - 4); + OUT_RING(0); + ADVANCE_LP_RING(); + } + } + + dev_priv->sarea_priv->last_enqueue = dev_priv->counter++; + + BEGIN_LP_RING(4); + OUT_RING(CMD_STORE_DWORD_IDX); + OUT_RING(20); + OUT_RING(dev_priv->counter); + OUT_RING(0); + ADVANCE_LP_RING(); + + return 0; +} + +static int i915_dispatch_flip(drm_device_t * dev) +{ + drm_i915_private_t *dev_priv = dev->dev_private; + RING_LOCALS; + + DRM_DEBUG("%s: page=%d pfCurrentPage=%d\n", + __FUNCTION__, + dev_priv->current_page, + dev_priv->sarea_priv->pf_current_page); + + i915_kernel_lost_context(dev); + + BEGIN_LP_RING(2); + OUT_RING(INST_PARSER_CLIENT | INST_OP_FLUSH | INST_FLUSH_MAP_CACHE); + OUT_RING(0); + ADVANCE_LP_RING(); + + BEGIN_LP_RING(6); + OUT_RING(CMD_OP_DISPLAYBUFFER_INFO | ASYNC_FLIP); + OUT_RING(0); + if (dev_priv->current_page == 0) { + OUT_RING(dev_priv->back_offset); + dev_priv->current_page = 1; + } else { + OUT_RING(dev_priv->front_offset); + dev_priv->current_page = 0; + } + OUT_RING(0); + ADVANCE_LP_RING(); + + BEGIN_LP_RING(2); + OUT_RING(MI_WAIT_FOR_EVENT | MI_WAIT_FOR_PLANE_A_FLIP); + OUT_RING(0); + ADVANCE_LP_RING(); + + dev_priv->sarea_priv->last_enqueue = dev_priv->counter++; + + BEGIN_LP_RING(4); + OUT_RING(CMD_STORE_DWORD_IDX); + OUT_RING(20); + OUT_RING(dev_priv->counter); + OUT_RING(0); + ADVANCE_LP_RING(); + + dev_priv->sarea_priv->pf_current_page = dev_priv->current_page; + return 0; +} + +static int i915_quiescent(drm_device_t * dev) +{ + drm_i915_private_t *dev_priv = dev->dev_private; + + i915_kernel_lost_context(dev); + return i915_wait_ring(dev, dev_priv->ring.Size - 8, __FUNCTION__); +} + +int i915_flush_ioctl(DRM_IOCTL_ARGS) +{ + DRM_DEVICE; + + if (!_DRM_LOCK_IS_HELD(dev->lock.hw_lock->lock)) { + DRM_ERROR("i915_flush_ioctl called without lock held\n"); + return DRM_ERR(EINVAL); + } + + return i915_quiescent(dev); +} + +int i915_batchbuffer(DRM_IOCTL_ARGS) +{ + DRM_DEVICE; + drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private; + u32 *hw_status = dev_priv->hw_status_page; + drm_i915_sarea_t *sarea_priv = (drm_i915_sarea_t *) + dev_priv->sarea_priv; + drm_i915_batchbuffer_t batch; + int ret; + + if (!dev_priv->allow_batchbuffer) { + DRM_ERROR("Batchbuffer ioctl disabled\n"); + return DRM_ERR(EINVAL); + } + + DRM_COPY_FROM_USER_IOCTL(batch, (drm_i915_batchbuffer_t __user *) data, + sizeof(batch)); + + DRM_DEBUG("i915 batchbuffer, start %x used %d cliprects %d\n", + batch.start, batch.used, batch.num_cliprects); + + if (!_DRM_LOCK_IS_HELD(dev->lock.hw_lock->lock)) { + DRM_ERROR("i915_batchbuffer called without lock held\n"); + return DRM_ERR(EINVAL); + } + + if (batch.num_cliprects && DRM_VERIFYAREA_READ(batch.cliprects, + batch.num_cliprects * + sizeof(drm_clip_rect_t))) + return DRM_ERR(EFAULT); + + ret = i915_dispatch_batchbuffer(dev, &batch); + + sarea_priv->last_dispatch = (int)hw_status[5]; + return ret; +} + +int i915_cmdbuffer(DRM_IOCTL_ARGS) +{ + DRM_DEVICE; + drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private; + u32 *hw_status = dev_priv->hw_status_page; + drm_i915_sarea_t *sarea_priv = (drm_i915_sarea_t *) + dev_priv->sarea_priv; + drm_i915_cmdbuffer_t cmdbuf; + int ret; + + DRM_COPY_FROM_USER_IOCTL(cmdbuf, (drm_i915_cmdbuffer_t __user *) data, + sizeof(cmdbuf)); + + DRM_DEBUG("i915 cmdbuffer, buf %p sz %d cliprects %d\n", + cmdbuf.buf, cmdbuf.sz, cmdbuf.num_cliprects); + + if (!_DRM_LOCK_IS_HELD(dev->lock.hw_lock->lock)) { + DRM_ERROR("i915_cmdbuffer called without lock held\n"); + return DRM_ERR(EINVAL); + } + + if (cmdbuf.num_cliprects && + DRM_VERIFYAREA_READ(cmdbuf.cliprects, + cmdbuf.num_cliprects * + sizeof(drm_clip_rect_t))) { + DRM_ERROR("Fault accessing cliprects\n"); + return DRM_ERR(EFAULT); + } + + ret = i915_dispatch_cmdbuffer(dev, &cmdbuf); + if (ret) { + DRM_ERROR("i915_dispatch_cmdbuffer failed\n"); + return ret; + } + + sarea_priv->last_dispatch = (int)hw_status[5]; + return 0; +} + +int i915_do_cleanup_pageflip(drm_device_t * dev) +{ + drm_i915_private_t *dev_priv = dev->dev_private; + + DRM_DEBUG("%s\n", __FUNCTION__); + if (dev_priv->current_page != 0) + i915_dispatch_flip(dev); + + return 0; +} + +int i915_flip_bufs(DRM_IOCTL_ARGS) +{ + DRM_DEVICE; + + DRM_DEBUG("%s\n", __FUNCTION__); + if (!_DRM_LOCK_IS_HELD(dev->lock.hw_lock->lock)) { + DRM_ERROR("i915_flip_buf called without lock held\n"); + return DRM_ERR(EINVAL); + } + + return i915_dispatch_flip(dev); +} + +int i915_getparam(DRM_IOCTL_ARGS) +{ + DRM_DEVICE; + drm_i915_private_t *dev_priv = dev->dev_private; + drm_i915_getparam_t param; + int value; + + if (!dev_priv) { + DRM_ERROR("%s called with no initialization\n", __FUNCTION__); + return DRM_ERR(EINVAL); + } + + DRM_COPY_FROM_USER_IOCTL(param, (drm_i915_getparam_t __user *) data, + sizeof(param)); + + switch (param.param) { + case I915_PARAM_IRQ_ACTIVE: + value = dev->irq ? 1 : 0; + break; + case I915_PARAM_ALLOW_BATCHBUFFER: + value = dev_priv->allow_batchbuffer ? 1 : 0; + break; + default: + DRM_ERROR("Unkown parameter %d\n", param.param); + return DRM_ERR(EINVAL); + } + + if (DRM_COPY_TO_USER(param.value, &value, sizeof(int))) { + DRM_ERROR("DRM_COPY_TO_USER failed\n"); + return DRM_ERR(EFAULT); + } + + return 0; +} + +int i915_setparam(DRM_IOCTL_ARGS) +{ + DRM_DEVICE; + drm_i915_private_t *dev_priv = dev->dev_private; + drm_i915_setparam_t param; + + if (!dev_priv) { + DRM_ERROR("%s called with no initialization\n", __FUNCTION__); + return DRM_ERR(EINVAL); + } + + DRM_COPY_FROM_USER_IOCTL(param, (drm_i915_setparam_t __user *) data, + sizeof(param)); + + switch (param.param) { + case I915_SETPARAM_USE_MI_BATCHBUFFER_START: + dev_priv->use_mi_batchbuffer_start = param.value; + break; + case I915_SETPARAM_TEX_LRU_LOG_GRANULARITY: + dev_priv->tex_lru_log_granularity = param.value; + break; + case I915_SETPARAM_ALLOW_BATCHBUFFER: + dev_priv->allow_batchbuffer = param.value; + break; + default: + DRM_ERROR("unknown parameter %d\n", param.param); + return DRM_ERR(EINVAL); + } + + return 0; +} + +static void i915_driver_pretakedown(drm_device_t *dev) +{ + if ( dev->dev_private ) { + drm_i915_private_t *dev_priv = dev->dev_private; + i915_mem_takedown( &(dev_priv->agp_heap) ); + } + i915_dma_cleanup( dev ); +} + +static void i915_driver_prerelease(drm_device_t *dev, DRMFILE filp) +{ + if ( dev->dev_private ) { + drm_i915_private_t *dev_priv = dev->dev_private; + i915_mem_release( dev, filp, dev_priv->agp_heap ); + } +} + +void i915_driver_register_fns(drm_device_t *dev) +{ + dev->driver_features = DRIVER_USE_AGP | DRIVER_REQUIRE_AGP | DRIVER_USE_MTRR | DRIVER_HAVE_IRQ | DRIVER_IRQ_SHARED; + dev->fn_tbl.pretakedown = i915_driver_pretakedown; + dev->fn_tbl.prerelease = i915_driver_prerelease; + dev->fn_tbl.irq_preinstall = i915_driver_irq_preinstall; + dev->fn_tbl.irq_postinstall = i915_driver_irq_postinstall; + dev->fn_tbl.irq_uninstall = i915_driver_irq_uninstall; + dev->fn_tbl.irq_handler = i915_driver_irq_handler; + + dev->counters += 4; + dev->types[6] = _DRM_STAT_IRQ; + dev->types[7] = _DRM_STAT_PRIMARY; + dev->types[8] = _DRM_STAT_SECONDARY; + dev->types[9] = _DRM_STAT_DMA; +} diff --git a/drivers/char/drm/i915_drm.h b/drivers/char/drm/i915_drm.h new file mode 100644 index 000000000..24f4cd622 --- /dev/null +++ b/drivers/char/drm/i915_drm.h @@ -0,0 +1,154 @@ +#ifndef _I915_DRM_H_ +#define _I915_DRM_H_ + +/* Please note that modifications to all structs defined here are + * subject to backwards-compatibility constraints. + */ + +#include "drm.h" + +/* Each region is a minimum of 16k, and there are at most 255 of them. + */ +#define I915_NR_TEX_REGIONS 255 /* table size 2k - maximum due to use + * of chars for next/prev indices */ +#define I915_LOG_MIN_TEX_REGION_SIZE 14 + +typedef struct _drm_i915_init { + enum { + I915_INIT_DMA = 0x01, + I915_CLEANUP_DMA = 0x02, + I915_RESUME_DMA = 0x03 + } func; + unsigned int mmio_offset; + int sarea_priv_offset; + unsigned int ring_start; + unsigned int ring_end; + unsigned int ring_size; + unsigned int front_offset; + unsigned int back_offset; + unsigned int depth_offset; + unsigned int w; + unsigned int h; + unsigned int pitch; + unsigned int pitch_bits; + unsigned int back_pitch; + unsigned int depth_pitch; + unsigned int cpp; + unsigned int chipset; +} drm_i915_init_t; + +typedef struct _drm_i915_sarea { + drm_tex_region_t texList[I915_NR_TEX_REGIONS + 1]; + int last_upload; /* last time texture was uploaded */ + int last_enqueue; /* last time a buffer was enqueued */ + int last_dispatch; /* age of the most recently dispatched buffer */ + int ctxOwner; /* last context to upload state */ + int texAge; + int pf_enabled; /* is pageflipping allowed? */ + int pf_active; + int pf_current_page; /* which buffer is being displayed? */ + int perf_boxes; /* performance boxes to be displayed */ +} drm_i915_sarea_t; + +/* Flags for perf_boxes + */ +#define I915_BOX_RING_EMPTY 0x1 +#define I915_BOX_FLIP 0x2 +#define I915_BOX_WAIT 0x4 +#define I915_BOX_TEXTURE_LOAD 0x8 +#define I915_BOX_LOST_CONTEXT 0x10 + +/* I915 specific ioctls + * The device specific ioctl range is 0x40 to 0x79. + */ +#define DRM_IOCTL_I915_INIT DRM_IOW( 0x40, drm_i915_init_t) +#define DRM_IOCTL_I915_FLUSH DRM_IO ( 0x41) +#define DRM_IOCTL_I915_FLIP DRM_IO ( 0x42) +#define DRM_IOCTL_I915_BATCHBUFFER DRM_IOW( 0x43, drm_i915_batchbuffer_t) +#define DRM_IOCTL_I915_IRQ_EMIT DRM_IOWR(0x44, drm_i915_irq_emit_t) +#define DRM_IOCTL_I915_IRQ_WAIT DRM_IOW( 0x45, drm_i915_irq_wait_t) +#define DRM_IOCTL_I915_GETPARAM DRM_IOWR(0x46, drm_i915_getparam_t) +#define DRM_IOCTL_I915_SETPARAM DRM_IOW( 0x47, drm_i915_setparam_t) +#define DRM_IOCTL_I915_ALLOC DRM_IOWR(0x48, drm_i915_mem_alloc_t) +#define DRM_IOCTL_I915_FREE DRM_IOW( 0x49, drm_i915_mem_free_t) +#define DRM_IOCTL_I915_INIT_HEAP DRM_IOW( 0x4a, drm_i915_mem_init_heap_t) +#define DRM_IOCTL_I915_CMDBUFFER DRM_IOW( 0x4b, drm_i915_cmdbuffer_t) + +/* Allow drivers to submit batchbuffers directly to hardware, relying + * on the security mechanisms provided by hardware. + */ +typedef struct _drm_i915_batchbuffer { + int start; /* agp offset */ + int used; /* nr bytes in use */ + int DR1; /* hw flags for GFX_OP_DRAWRECT_INFO */ + int DR4; /* window origin for GFX_OP_DRAWRECT_INFO */ + int num_cliprects; /* mulitpass with multiple cliprects? */ + drm_clip_rect_t __user *cliprects; /* pointer to userspace cliprects */ +} drm_i915_batchbuffer_t; + +/* As above, but pass a pointer to userspace buffer which can be + * validated by the kernel prior to sending to hardware. + */ +typedef struct _drm_i915_cmdbuffer { + char __user *buf; /* pointer to userspace command buffer */ + int sz; /* nr bytes in buf */ + int DR1; /* hw flags for GFX_OP_DRAWRECT_INFO */ + int DR4; /* window origin for GFX_OP_DRAWRECT_INFO */ + int num_cliprects; /* mulitpass with multiple cliprects? */ + drm_clip_rect_t __user *cliprects; /* pointer to userspace cliprects */ +} drm_i915_cmdbuffer_t; + +/* Userspace can request & wait on irq's: + */ +typedef struct drm_i915_irq_emit { + int __user *irq_seq; +} drm_i915_irq_emit_t; + +typedef struct drm_i915_irq_wait { + int irq_seq; +} drm_i915_irq_wait_t; + +/* Ioctl to query kernel params: + */ +#define I915_PARAM_IRQ_ACTIVE 1 +#define I915_PARAM_ALLOW_BATCHBUFFER 2 + +typedef struct drm_i915_getparam { + int param; + int __user *value; +} drm_i915_getparam_t; + +/* Ioctl to set kernel params: + */ +#define I915_SETPARAM_USE_MI_BATCHBUFFER_START 1 +#define I915_SETPARAM_TEX_LRU_LOG_GRANULARITY 2 +#define I915_SETPARAM_ALLOW_BATCHBUFFER 3 + +typedef struct drm_i915_setparam { + int param; + int value; +} drm_i915_setparam_t; + +/* A memory manager for regions of shared memory: + */ +#define I915_MEM_REGION_AGP 1 + +typedef struct drm_i915_mem_alloc { + int region; + int alignment; + int size; + int __user *region_offset; /* offset from start of fb or agp */ +} drm_i915_mem_alloc_t; + +typedef struct drm_i915_mem_free { + int region; + int region_offset; +} drm_i915_mem_free_t; + +typedef struct drm_i915_mem_init_heap { + int region; + int size; + int start; +} drm_i915_mem_init_heap_t; + +#endif /* _I915_DRM_H_ */ diff --git a/drivers/char/drm/i915_drv.c b/drivers/char/drm/i915_drv.c new file mode 100644 index 000000000..becce4d70 --- /dev/null +++ b/drivers/char/drm/i915_drv.c @@ -0,0 +1,17 @@ +/* i915_drv.c -- i830,i845,i855,i865,i915 driver -*- linux-c -*- + */ + +/************************************************************************** + * + * Copyright 2003 Tungsten Graphics, Inc., Cedar Park, Texas. + * All Rights Reserved. + * + **************************************************************************/ + +#include "i915.h" +#include "drmP.h" +#include "drm.h" +#include "i915_drm.h" +#include "i915_drv.h" + +#include "drm_core.h" diff --git a/drivers/char/drm/i915_drv.h b/drivers/char/drm/i915_drv.h new file mode 100644 index 000000000..7564cd090 --- /dev/null +++ b/drivers/char/drm/i915_drv.h @@ -0,0 +1,219 @@ +/* i915_drv.h -- Private header for the I915 driver -*- linux-c -*- + */ +/************************************************************************** + * + * Copyright 2003 Tungsten Graphics, Inc., Cedar Park, Texas. + * All Rights Reserved. + * + **************************************************************************/ + +#ifndef _I915_DRV_H_ +#define _I915_DRV_H_ + +typedef struct _drm_i915_ring_buffer { + int tail_mask; + unsigned long Start; + unsigned long End; + unsigned long Size; + u8 *virtual_start; + int head; + int tail; + int space; + drm_local_map_t map; +} drm_i915_ring_buffer_t; + +struct mem_block { + struct mem_block *next; + struct mem_block *prev; + int start; + int size; + DRMFILE filp; /* 0: free, -1: heap, other: real files */ +}; + +typedef struct drm_i915_private { + drm_local_map_t *sarea; + drm_local_map_t *mmio_map; + + drm_i915_sarea_t *sarea_priv; + drm_i915_ring_buffer_t ring; + + void *hw_status_page; + unsigned long counter; + dma_addr_t dma_status_page; + + int back_offset; + int front_offset; + int current_page; + int page_flipping; + int use_mi_batchbuffer_start; + + wait_queue_head_t irq_queue; + atomic_t irq_received; + atomic_t irq_emitted; + + int tex_lru_log_granularity; + int allow_batchbuffer; + struct mem_block *agp_heap; +} drm_i915_private_t; + + /* i915_dma.c */ +extern int i915_dma_init(DRM_IOCTL_ARGS); +extern int i915_dma_cleanup(drm_device_t * dev); +extern int i915_flush_ioctl(DRM_IOCTL_ARGS); +extern int i915_batchbuffer(DRM_IOCTL_ARGS); +extern int i915_flip_bufs(DRM_IOCTL_ARGS); +extern int i915_getparam(DRM_IOCTL_ARGS); +extern int i915_setparam(DRM_IOCTL_ARGS); +extern int i915_cmdbuffer(DRM_IOCTL_ARGS); +extern void i915_kernel_lost_context(drm_device_t * dev); + +/* i915_irq.c */ +extern int i915_irq_emit(DRM_IOCTL_ARGS); +extern int i915_irq_wait(DRM_IOCTL_ARGS); +extern int i915_wait_irq(drm_device_t * dev, int irq_nr); +extern int i915_emit_irq(drm_device_t * dev); + +extern irqreturn_t i915_driver_irq_handler(DRM_IRQ_ARGS); +extern void i915_driver_irq_preinstall(drm_device_t *dev); +extern void i915_driver_irq_postinstall(drm_device_t *dev); +extern void i915_driver_irq_uninstall(drm_device_t *dev); + +/* i915_mem.c */ +extern int i915_mem_alloc(DRM_IOCTL_ARGS); +extern int i915_mem_free(DRM_IOCTL_ARGS); +extern int i915_mem_init_heap(DRM_IOCTL_ARGS); +extern void i915_mem_takedown(struct mem_block **heap); +extern void i915_mem_release(drm_device_t * dev, + DRMFILE filp, struct mem_block *heap); + +#define I915_READ(reg) DRM_READ32(dev_priv->mmio_map, reg) +#define I915_WRITE(reg,val) DRM_WRITE32(dev_priv->mmio_map, reg, val) +#define I915_READ16(reg) DRM_READ16(dev_priv->mmio_map, reg) +#define I915_WRITE16(reg,val) DRM_WRITE16(dev_priv->mmio_map, reg, val) + +#define I915_VERBOSE 0 + +#define RING_LOCALS unsigned int outring, ringmask, outcount; \ + volatile char *virt; + +#define BEGIN_LP_RING(n) do { \ + if (I915_VERBOSE) \ + DRM_DEBUG("BEGIN_LP_RING(%d) in %s\n", \ + n, __FUNCTION__); \ + if (dev_priv->ring.space < n*4) \ + i915_wait_ring(dev, n*4, __FUNCTION__); \ + outcount = 0; \ + outring = dev_priv->ring.tail; \ + ringmask = dev_priv->ring.tail_mask; \ + virt = dev_priv->ring.virtual_start; \ +} while (0) + +#define OUT_RING(n) do { \ + if (I915_VERBOSE) DRM_DEBUG(" OUT_RING %x\n", (int)(n)); \ + *(volatile unsigned int *)(virt + outring) = n; \ + outcount++; \ + outring += 4; \ + outring &= ringmask; \ +} while (0) + +#define ADVANCE_LP_RING() do { \ + if (I915_VERBOSE) DRM_DEBUG("ADVANCE_LP_RING %x\n", outring); \ + dev_priv->ring.tail = outring; \ + dev_priv->ring.space -= outcount * 4; \ + I915_WRITE(LP_RING + RING_TAIL, outring); \ +} while(0) + +extern int i915_wait_ring(drm_device_t * dev, int n, const char *caller); + +#define GFX_OP_USER_INTERRUPT ((0<<29)|(2<<23)) +#define GFX_OP_BREAKPOINT_INTERRUPT ((0<<29)|(1<<23)) +#define CMD_REPORT_HEAD (7<<23) +#define CMD_STORE_DWORD_IDX ((0x21<<23) | 0x1) +#define CMD_OP_BATCH_BUFFER ((0x0<<29)|(0x30<<23)|0x1) + +#define INST_PARSER_CLIENT 0x00000000 +#define INST_OP_FLUSH 0x02000000 +#define INST_FLUSH_MAP_CACHE 0x00000001 + +#define BB1_START_ADDR_MASK (~0x7) +#define BB1_PROTECTED (1<<0) +#define BB1_UNPROTECTED (0<<0) +#define BB2_END_ADDR_MASK (~0x7) + +#define I915REG_HWSTAM 0x02098 +#define I915REG_INT_IDENTITY_R 0x020a4 +#define I915REG_INT_MASK_R 0x020a8 +#define I915REG_INT_ENABLE_R 0x020a0 + +#define SRX_INDEX 0x3c4 +#define SRX_DATA 0x3c5 +#define SR01 1 +#define SR01_SCREEN_OFF (1<<5) + +#define PPCR 0x61204 +#define PPCR_ON (1<<0) + +#define ADPA 0x61100 +#define ADPA_DPMS_MASK (~(3<<10)) +#define ADPA_DPMS_ON (0<<10) +#define ADPA_DPMS_SUSPEND (1<<10) +#define ADPA_DPMS_STANDBY (2<<10) +#define ADPA_DPMS_OFF (3<<10) + +#define NOPID 0x2094 +#define LP_RING 0x2030 +#define HP_RING 0x2040 +#define RING_TAIL 0x00 +#define TAIL_ADDR 0x001FFFF8 +#define RING_HEAD 0x04 +#define HEAD_WRAP_COUNT 0xFFE00000 +#define HEAD_WRAP_ONE 0x00200000 +#define HEAD_ADDR 0x001FFFFC +#define RING_START 0x08 +#define START_ADDR 0x0xFFFFF000 +#define RING_LEN 0x0C +#define RING_NR_PAGES 0x001FF000 +#define RING_REPORT_MASK 0x00000006 +#define RING_REPORT_64K 0x00000002 +#define RING_REPORT_128K 0x00000004 +#define RING_NO_REPORT 0x00000000 +#define RING_VALID_MASK 0x00000001 +#define RING_VALID 0x00000001 +#define RING_INVALID 0x00000000 + +#define GFX_OP_SCISSOR ((0x3<<29)|(0x1c<<24)|(0x10<<19)) +#define SC_UPDATE_SCISSOR (0x1<<1) +#define SC_ENABLE_MASK (0x1<<0) +#define SC_ENABLE (0x1<<0) + +#define GFX_OP_SCISSOR_INFO ((0x3<<29)|(0x1d<<24)|(0x81<<16)|(0x1)) +#define SCI_YMIN_MASK (0xffff<<16) +#define SCI_XMIN_MASK (0xffff<<0) +#define SCI_YMAX_MASK (0xffff<<16) +#define SCI_XMAX_MASK (0xffff<<0) + +#define GFX_OP_SCISSOR_ENABLE ((0x3<<29)|(0x1c<<24)|(0x10<<19)) +#define GFX_OP_SCISSOR_RECT ((0x3<<29)|(0x1d<<24)|(0x81<<16)|1) +#define GFX_OP_COLOR_FACTOR ((0x3<<29)|(0x1d<<24)|(0x1<<16)|0x0) +#define GFX_OP_STIPPLE ((0x3<<29)|(0x1d<<24)|(0x83<<16)) +#define GFX_OP_MAP_INFO ((0x3<<29)|(0x1d<<24)|0x4) +#define GFX_OP_DESTBUFFER_VARS ((0x3<<29)|(0x1d<<24)|(0x85<<16)|0x0) +#define GFX_OP_DRAWRECT_INFO ((0x3<<29)|(0x1d<<24)|(0x80<<16)|(0x3)) + +#define MI_BATCH_BUFFER ((0x30<<23)|1) +#define MI_BATCH_BUFFER_START (0x31<<23) +#define MI_BATCH_BUFFER_END (0xA<<23) +#define MI_BATCH_NON_SECURE (1) + +#define MI_WAIT_FOR_EVENT ((0x3<<23)) +#define MI_WAIT_FOR_PLANE_A_FLIP (1<<2) +#define MI_WAIT_FOR_PLANE_A_SCANLINES (1<<1) + +#define MI_LOAD_SCAN_LINES_INCL ((0x12<<23)) + +#define CMD_OP_DISPLAYBUFFER_INFO ((0x0<<29)|(0x14<<23)|2) +#define ASYNC_FLIP (1<<22) + +#define CMD_OP_DESTBUFFER_INFO ((0x3<<29)|(0x1d<<24)|(0x8e<<16)|1) + +#endif diff --git a/drivers/char/drm/i915_irq.c b/drivers/char/drm/i915_irq.c new file mode 100644 index 000000000..de91aba58 --- /dev/null +++ b/drivers/char/drm/i915_irq.c @@ -0,0 +1,165 @@ +/* i915_dma.c -- DMA support for the I915 -*- linux-c -*- + */ +/************************************************************************** + * + * Copyright 2003 Tungsten Graphics, Inc., Cedar Park, Texas. + * All Rights Reserved. + * + **************************************************************************/ + +#include "i915.h" +#include "drmP.h" +#include "drm.h" +#include "i915_drm.h" +#include "i915_drv.h" + +#define USER_INT_FLAG 0x2 +#define MAX_NOPID ((u32)~0) +#define READ_BREADCRUMB(dev_priv) (((u32*)(dev_priv->hw_status_page))[5]) + +irqreturn_t i915_driver_irq_handler(DRM_IRQ_ARGS) +{ + drm_device_t *dev = (drm_device_t *) arg; + drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private; + u16 temp; + + temp = I915_READ16(I915REG_INT_IDENTITY_R); + temp &= USER_INT_FLAG; + + DRM_DEBUG("%s flag=%08x\n", __FUNCTION__, temp); + + if (temp == 0) + return IRQ_NONE; + + I915_WRITE16(I915REG_INT_IDENTITY_R, temp); + DRM_WAKEUP(&dev_priv->irq_queue); + + return IRQ_HANDLED; +} + +int i915_emit_irq(drm_device_t * dev) +{ + drm_i915_private_t *dev_priv = dev->dev_private; + u32 ret; + RING_LOCALS; + + i915_kernel_lost_context(dev); + + DRM_DEBUG("%s\n", __FUNCTION__); + + ret = dev_priv->counter; + + BEGIN_LP_RING(2); + OUT_RING(0); + OUT_RING(GFX_OP_USER_INTERRUPT); + ADVANCE_LP_RING(); + + return ret; +} + +int i915_wait_irq(drm_device_t * dev, int irq_nr) +{ + drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private; + int ret = 0; + + DRM_DEBUG("%s irq_nr=%d breadcrumb=%d\n", __FUNCTION__, irq_nr, + READ_BREADCRUMB(dev_priv)); + + if (READ_BREADCRUMB(dev_priv) >= irq_nr) + return 0; + + dev_priv->sarea_priv->perf_boxes |= I915_BOX_WAIT; + + DRM_WAIT_ON(ret, dev_priv->irq_queue, 3 * DRM_HZ, + READ_BREADCRUMB(dev_priv) >= irq_nr); + + if (ret == DRM_ERR(EBUSY)) { + DRM_ERROR("%s: EBUSY -- rec: %d emitted: %d\n", + __FUNCTION__, + READ_BREADCRUMB(dev_priv), (int)dev_priv->counter); + } + + dev_priv->sarea_priv->last_dispatch = READ_BREADCRUMB(dev_priv); + return ret; +} + +/* Needs the lock as it touches the ring. + */ +int i915_irq_emit(DRM_IOCTL_ARGS) +{ + DRM_DEVICE; + drm_i915_private_t *dev_priv = dev->dev_private; + drm_i915_irq_emit_t emit; + int result; + + if (!_DRM_LOCK_IS_HELD(dev->lock.hw_lock->lock)) { + DRM_ERROR("i915_irq_emit called without lock held\n"); + return DRM_ERR(EINVAL); + } + + if (!dev_priv) { + DRM_ERROR("%s called with no initialization\n", __FUNCTION__); + return DRM_ERR(EINVAL); + } + + DRM_COPY_FROM_USER_IOCTL(emit, (drm_i915_irq_emit_t __user *) data, + sizeof(emit)); + + result = i915_emit_irq(dev); + + if (DRM_COPY_TO_USER(emit.irq_seq, &result, sizeof(int))) { + DRM_ERROR("copy_to_user\n"); + return DRM_ERR(EFAULT); + } + + return 0; +} + +/* Doesn't need the hardware lock. + */ +int i915_irq_wait(DRM_IOCTL_ARGS) +{ + DRM_DEVICE; + drm_i915_private_t *dev_priv = dev->dev_private; + drm_i915_irq_wait_t irqwait; + + if (!dev_priv) { + DRM_ERROR("%s called with no initialization\n", __FUNCTION__); + return DRM_ERR(EINVAL); + } + + DRM_COPY_FROM_USER_IOCTL(irqwait, (drm_i915_irq_wait_t __user *) data, + sizeof(irqwait)); + + return i915_wait_irq(dev, irqwait.irq_seq); +} + +/* drm_dma.h hooks +*/ +void i915_driver_irq_preinstall(drm_device_t * dev) +{ + drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private; + + I915_WRITE16(I915REG_HWSTAM, 0xfffe); + I915_WRITE16(I915REG_INT_MASK_R, 0x0); + I915_WRITE16(I915REG_INT_ENABLE_R, 0x0); +} + +void i915_driver_irq_postinstall(drm_device_t * dev) +{ + drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private; + + I915_WRITE16(I915REG_INT_ENABLE_R, USER_INT_FLAG); + DRM_INIT_WAITQUEUE(&dev_priv->irq_queue); +} + +void i915_driver_irq_uninstall(drm_device_t * dev) +{ + drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private; + if (!dev_priv) + return; + + I915_WRITE16(I915REG_HWSTAM, 0xffff); + I915_WRITE16(I915REG_INT_MASK_R, 0xffff); + I915_WRITE16(I915REG_INT_ENABLE_R, 0x0); +} diff --git a/drivers/char/drm/i915_mem.c b/drivers/char/drm/i915_mem.c new file mode 100644 index 000000000..42c1e3535 --- /dev/null +++ b/drivers/char/drm/i915_mem.c @@ -0,0 +1,347 @@ +/* i915_mem.c -- Simple agp/fb memory manager for i915 -*- linux-c -*- + */ +/************************************************************************** + * + * Copyright 2003 Tungsten Graphics, Inc., Cedar Park, Texas. + * All Rights Reserved. + * + **************************************************************************/ + +#include "i915.h" +#include "drmP.h" +#include "drm.h" +#include "i915_drm.h" +#include "i915_drv.h" + +/* This memory manager is integrated into the global/local lru + * mechanisms used by the clients. Specifically, it operates by + * setting the 'in_use' fields of the global LRU to indicate whether + * this region is privately allocated to a client. + * + * This does require the client to actually respect that field. + * + * Currently no effort is made to allocate 'private' memory in any + * clever way - the LRU information isn't used to determine which + * block to allocate, and the ring is drained prior to allocations -- + * in other words allocation is expensive. + */ +static void mark_block(drm_device_t * 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; + drm_tex_region_t *list; + unsigned shift, nr; + unsigned start; + unsigned end; + unsigned i; + int age; + + shift = dev_priv->tex_lru_log_granularity; + nr = I915_NR_TEX_REGIONS; + + start = p->start >> shift; + end = (p->start + p->size - 1) >> shift; + + age = ++sarea_priv->texAge; + list = sarea_priv->texList; + + /* Mark the regions with the new flag and update their age. Move + * them to head of list to preserve LRU semantics. + */ + for (i = start; i <= end; i++) { + list[i].in_use = in_use; + list[i].age = age; + + /* remove_from_list(i) + */ + list[(unsigned)list[i].next].prev = list[i].prev; + list[(unsigned)list[i].prev].next = list[i].next; + + /* insert_at_head(list, i) + */ + list[i].prev = nr; + list[i].next = list[nr].next; + list[(unsigned)list[nr].next].prev = i; + list[nr].next = i; + } +} + +/* Very simple allocator for agp memory, working on a static range + * already mapped into each client's address space. + */ + +static struct mem_block *split_block(struct mem_block *p, int start, int size, + DRMFILE filp) +{ + /* Maybe cut off the start of an existing block */ + if (start > p->start) { + struct mem_block *newblock = DRM_MALLOC(sizeof(*newblock)); + if (!newblock) + goto out; + newblock->start = start; + newblock->size = p->size - (start - p->start); + newblock->filp = NULL; + newblock->next = p->next; + newblock->prev = p; + p->next->prev = newblock; + p->next = newblock; + p->size -= newblock->size; + p = newblock; + } + + /* Maybe cut off the end of an existing block */ + if (size < p->size) { + struct mem_block *newblock = DRM_MALLOC(sizeof(*newblock)); + if (!newblock) + goto out; + newblock->start = start + size; + newblock->size = p->size - size; + newblock->filp = NULL; + newblock->next = p->next; + newblock->prev = p; + p->next->prev = newblock; + p->next = newblock; + p->size = size; + } + + out: + /* Our block is in the middle */ + p->filp = filp; + return p; +} + +static struct mem_block *alloc_block(struct mem_block *heap, int size, + int align2, DRMFILE filp) +{ + struct mem_block *p; + int mask = (1 << align2) - 1; + + for (p = heap->next; p != heap; p = p->next) { + int start = (p->start + mask) & ~mask; + if (p->filp == NULL && start + size <= p->start + p->size) + return split_block(p, start, size, filp); + } + + return NULL; +} + +static struct mem_block *find_block(struct mem_block *heap, int start) +{ + struct mem_block *p; + + for (p = heap->next; p != heap; p = p->next) + if (p->start == start) + return p; + + return NULL; +} + +static void free_block(struct mem_block *p) +{ + p->filp = NULL; + + /* Assumes a single contiguous range. Needs a special filp in + * 'heap' to stop it being subsumed. + */ + if (p->next->filp == NULL) { + struct mem_block *q = p->next; + p->size += q->size; + p->next = q->next; + p->next->prev = p; + DRM_FREE(q, sizeof(*q)); + } + + if (p->prev->filp == NULL) { + struct mem_block *q = p->prev; + q->size += p->size; + q->next = p->next; + q->next->prev = q; + DRM_FREE(p, sizeof(*q)); + } +} + +/* Initialize. How to check for an uninitialized heap? + */ +static int init_heap(struct mem_block **heap, int start, int size) +{ + struct mem_block *blocks = DRM_MALLOC(sizeof(*blocks)); + + if (!blocks) + return -ENOMEM; + + *heap = DRM_MALLOC(sizeof(**heap)); + if (!*heap) { + DRM_FREE(blocks, sizeof(*blocks)); + return -ENOMEM; + } + + blocks->start = start; + blocks->size = size; + blocks->filp = NULL; + blocks->next = blocks->prev = *heap; + + memset(*heap, 0, sizeof(**heap)); + (*heap)->filp = (DRMFILE) - 1; + (*heap)->next = (*heap)->prev = blocks; + return 0; +} + +/* Free all blocks associated with the releasing file. + */ +void i915_mem_release(drm_device_t * dev, DRMFILE filp, struct mem_block *heap) +{ + struct mem_block *p; + + if (!heap || !heap->next) + return; + + for (p = heap->next; p != heap; p = p->next) { + if (p->filp == filp) { + p->filp = NULL; + mark_block(dev, p, 0); + } + } + + /* Assumes a single contiguous range. Needs a special filp in + * 'heap' to stop it being subsumed. + */ + for (p = heap->next; p != heap; p = p->next) { + while (p->filp == NULL && p->next->filp == NULL) { + struct mem_block *q = p->next; + p->size += q->size; + p->next = q->next; + p->next->prev = p; + DRM_FREE(q, sizeof(*q)); + } + } +} + +/* Shutdown. + */ +void i915_mem_takedown(struct mem_block **heap) +{ + struct mem_block *p; + + if (!*heap) + return; + + for (p = (*heap)->next; p != *heap;) { + struct mem_block *q = p; + p = p->next; + DRM_FREE(q, sizeof(*q)); + } + + DRM_FREE(*heap, sizeof(**heap)); + *heap = NULL; +} + +static struct mem_block **get_heap(drm_i915_private_t * dev_priv, int region) +{ + switch (region) { + case I915_MEM_REGION_AGP: + return &dev_priv->agp_heap; + default: + return NULL; + } +} + +/* IOCTL HANDLERS */ + +int i915_mem_alloc(DRM_IOCTL_ARGS) +{ + DRM_DEVICE; + drm_i915_private_t *dev_priv = dev->dev_private; + drm_i915_mem_alloc_t alloc; + struct mem_block *block, **heap; + + if (!dev_priv) { + DRM_ERROR("%s called with no initialization\n", __FUNCTION__); + return DRM_ERR(EINVAL); + } + + DRM_COPY_FROM_USER_IOCTL(alloc, (drm_i915_mem_alloc_t __user *) data, + sizeof(alloc)); + + heap = get_heap(dev_priv, alloc.region); + if (!heap || !*heap) + return DRM_ERR(EFAULT); + + /* Make things easier on ourselves: all allocations at least + * 4k aligned. + */ + if (alloc.alignment < 12) + alloc.alignment = 12; + + block = alloc_block(*heap, alloc.size, alloc.alignment, filp); + + if (!block) + return DRM_ERR(ENOMEM); + + mark_block(dev, block, 1); + + if (DRM_COPY_TO_USER(alloc.region_offset, &block->start, sizeof(int))) { + DRM_ERROR("copy_to_user\n"); + return DRM_ERR(EFAULT); + } + + return 0; +} + +int i915_mem_free(DRM_IOCTL_ARGS) +{ + DRM_DEVICE; + drm_i915_private_t *dev_priv = dev->dev_private; + drm_i915_mem_free_t memfree; + struct mem_block *block, **heap; + + if (!dev_priv) { + DRM_ERROR("%s called with no initialization\n", __FUNCTION__); + return DRM_ERR(EINVAL); + } + + DRM_COPY_FROM_USER_IOCTL(memfree, (drm_i915_mem_free_t __user *) data, + sizeof(memfree)); + + heap = get_heap(dev_priv, memfree.region); + if (!heap || !*heap) + return DRM_ERR(EFAULT); + + block = find_block(*heap, memfree.region_offset); + if (!block) + return DRM_ERR(EFAULT); + + if (block->filp != filp) + return DRM_ERR(EPERM); + + mark_block(dev, block, 0); + free_block(block); + return 0; +} + +int i915_mem_init_heap(DRM_IOCTL_ARGS) +{ + DRM_DEVICE; + drm_i915_private_t *dev_priv = dev->dev_private; + drm_i915_mem_init_heap_t initheap; + struct mem_block **heap; + + if (!dev_priv) { + DRM_ERROR("%s called with no initialization\n", __FUNCTION__); + return DRM_ERR(EINVAL); + } + + DRM_COPY_FROM_USER_IOCTL(initheap, + (drm_i915_mem_init_heap_t __user *) data, + sizeof(initheap)); + + heap = get_heap(dev_priv, initheap.region); + if (!heap) + return DRM_ERR(EFAULT); + + if (*heap) { + DRM_ERROR("heap already initialized?"); + return DRM_ERR(EFAULT); + } + + return init_heap(heap, initheap.start, initheap.size); +} diff --git a/drivers/char/drm/sis.h b/drivers/char/drm/sis.h index 7bdc99c0a..838e360df 100644 --- a/drivers/char/drm/sis.h +++ b/drivers/char/drm/sis.h @@ -41,10 +41,6 @@ /* General customization: */ -#define __HAVE_AGP 1 -#define __MUST_HAVE_AGP 0 -#define __HAVE_MTRR 1 -#define __HAVE_CTX_BITMAP 1 #define DRIVER_AUTHOR "SIS" #define DRIVER_NAME "sis" @@ -62,17 +58,4 @@ [DRM_IOCTL_NR(DRM_IOCTL_SIS_AGP_FREE)] = { sis_ioctl_agp_free, 1, 0 }, \ [DRM_IOCTL_NR(DRM_IOCTL_SIS_FB_INIT)] = { sis_fb_init, 1, 1 } -#define __HAVE_COUNTERS 5 - -/* Buffer customization: - */ -#define DRIVER_AGP_BUFFERS_MAP( dev ) \ - ((drm_sis_private_t *)((dev)->dev_private))->buffers - -extern int sis_init_context(int context); -extern int sis_final_context(int context); - -#define DRIVER_CTX_CTOR sis_init_context -#define DRIVER_CTX_DTOR sis_final_context - #endif diff --git a/drivers/char/drm/sis_drv.c b/drivers/char/drm/sis_drv.c index 3dd075d30..f05734374 100644 --- a/drivers/char/drm/sis_drv.c +++ b/drivers/char/drm/sis_drv.c @@ -31,18 +31,4 @@ #include "sis_drm.h" #include "sis_drv.h" -#include "drm_auth.h" -#include "drm_agpsupport.h" -#include "drm_bufs.h" -#include "drm_context.h" -#include "drm_dma.h" -#include "drm_drawable.h" -#include "drm_drv.h" -#include "drm_fops.h" -#include "drm_init.h" -#include "drm_ioctl.h" -#include "drm_lock.h" -#include "drm_memory.h" -#include "drm_proc.h" -#include "drm_vm.h" -#include "drm_stub.h" +#include "drm_core.h" diff --git a/drivers/char/drm/sis_drv.h b/drivers/char/drm/sis_drv.h index 40e7f463e..a87001819 100644 --- a/drivers/char/drm/sis_drv.h +++ b/drivers/char/drm/sis_drv.h @@ -31,8 +31,6 @@ #include "sis_ds.h" typedef struct drm_sis_private { - drm_map_t *buffers; - memHeap_t *AGPHeap; memHeap_t *FBHeap; } drm_sis_private_t; diff --git a/drivers/char/ds1620.c b/drivers/char/ds1620.c index e2c4ecac7..7def6ad51 100644 --- a/drivers/char/ds1620.c +++ b/drivers/char/ds1620.c @@ -373,8 +373,7 @@ static int __init ds1620_init(void) th_start.hi = 1; ds1620_write_state(&th_start); - set_current_state(TASK_INTERRUPTIBLE); - schedule_timeout(2*HZ); + msleep(2000); ds1620_write_state(&th); diff --git a/drivers/char/dsp56k.c b/drivers/char/dsp56k.c index b022aca3d..37d664901 100644 --- a/drivers/char/dsp56k.c +++ b/drivers/char/dsp56k.c @@ -58,12 +58,6 @@ #define DSP56K_TRANSMIT (dsp56k_host_interface.isr & DSP56K_ISR_TXDE) #define DSP56K_RECEIVE (dsp56k_host_interface.isr & DSP56K_ISR_RXDF) -#define wait_some(n) \ -{ \ - set_current_state(TASK_INTERRUPTIBLE); \ - schedule_timeout(n); \ -} - #define handshake(count, maxio, timeout, ENABLE, f) \ { \ long i, t, m; \ @@ -71,13 +65,13 @@ m = min_t(unsigned long, count, maxio); \ for (i = 0; i < m; i++) { \ for (t = 0; t < timeout && !ENABLE; t++) \ - wait_some(HZ/50); \ + msleep(20); \ if(!ENABLE) \ return -EIO; \ f; \ } \ count -= m; \ - if (m == maxio) wait_some(HZ/50); \ + if (m == maxio) msleep(20); \ } \ } @@ -85,7 +79,7 @@ { \ int t; \ for(t = 0; t < n && !DSP56K_TRANSMIT; t++) \ - wait_some(HZ/100); \ + msleep(10); \ if(!DSP56K_TRANSMIT) { \ return -EIO; \ } \ @@ -95,7 +89,7 @@ { \ int t; \ for(t = 0; t < n && !DSP56K_RECEIVE; t++) \ - wait_some(HZ/100); \ + msleep(10); \ if(!DSP56K_RECEIVE) { \ return -EIO; \ } \ diff --git a/drivers/char/ec3104_keyb.c b/drivers/char/ec3104_keyb.c index 933282df5..b3289d9d2 100644 --- a/drivers/char/ec3104_keyb.c +++ b/drivers/char/ec3104_keyb.c @@ -412,7 +412,7 @@ static void ec3104_keyb_clear_state(void) k->last_msr = 0; for (;;) { - schedule_timeout(HZ/10); + msleep(100); msr = ctrl_inb(EC3104_SER4_MSR); diff --git a/drivers/char/efirtc.c b/drivers/char/efirtc.c index 6d0b81298..1845739ee 100644 --- a/drivers/char/efirtc.c +++ b/drivers/char/efirtc.c @@ -155,7 +155,7 @@ efi_rtc_ioctl(struct inode *inode, struct file *file, unsigned int cmd, efi_time_t eft; efi_time_cap_t cap; struct rtc_time wtime; - struct rtc_wkalrm *ewp; + struct rtc_wkalrm __user *ewp; unsigned char enabled, pending; switch (cmd) { @@ -189,13 +189,15 @@ efi_rtc_ioctl(struct inode *inode, struct file *file, unsigned int cmd, convert_from_efi_time(&eft, &wtime); - return copy_to_user((void *)arg, &wtime, sizeof (struct rtc_time)) ? - EFAULT : 0; + return copy_to_user((void __user *)arg, &wtime, + sizeof (struct rtc_time)) ? - EFAULT : 0; case RTC_SET_TIME: if (!capable(CAP_SYS_TIME)) return -EACCES; - if (copy_from_user(&wtime, (struct rtc_time *)arg, sizeof(struct rtc_time)) ) + if (copy_from_user(&wtime, (struct rtc_time __user *)arg, + sizeof(struct rtc_time)) ) return -EFAULT; convert_to_efi_time(&wtime, &eft); @@ -212,19 +214,19 @@ efi_rtc_ioctl(struct inode *inode, struct file *file, unsigned int cmd, if (!capable(CAP_SYS_TIME)) return -EACCES; - ewp = (struct rtc_wkalrm *)arg; + ewp = (struct rtc_wkalrm __user *)arg; if ( get_user(enabled, &ewp->enabled) || copy_from_user(&wtime, &ewp->time, sizeof(struct rtc_time)) ) return -EFAULT; convert_to_efi_time(&wtime, &eft); - + spin_lock_irqsave(&efi_rtc_lock, flags); /* * XXX Fixme: * As of EFI 0.92 with the firmware I have on my - * machine this call does not seem to work quite + * machine this call does not seem to work quite * right */ status = efi.set_wakeup_time((efi_bool_t)enabled, &eft); @@ -243,14 +245,15 @@ efi_rtc_ioctl(struct inode *inode, struct file *file, unsigned int cmd, if (status != EFI_SUCCESS) return -EINVAL; - ewp = (struct rtc_wkalrm *)arg; + ewp = (struct rtc_wkalrm __user *)arg; if ( put_user(enabled, &ewp->enabled) || put_user(pending, &ewp->pending)) return -EFAULT; convert_from_efi_time(&eft, &wtime); - return copy_to_user((void *)&ewp->time, &wtime, sizeof(struct rtc_time)) ? -EFAULT : 0; + return copy_to_user(&ewp->time, &wtime, + sizeof(struct rtc_time)) ? -EFAULT : 0; } return -EINVAL; } diff --git a/drivers/char/epca.c b/drivers/char/epca.c index 854d16a39..0f13bef97 100644 --- a/drivers/char/epca.c +++ b/drivers/char/epca.c @@ -74,7 +74,6 @@ #define DIGIINFOMAJOR 35 /* For Digi specific ioctl */ -#define MIN(a,b) ((a) < (b) ? (a) : (b)) #define MAXCARDS 7 #define epcaassert(x, msg) if (!(x)) epca_error(__LINE__, msg) @@ -551,9 +550,7 @@ static void pc_close(struct tty_struct * tty, struct file * filp) if (tty->driver->flush_buffer) tty->driver->flush_buffer(tty); - if (tty->ldisc.flush_buffer) - tty->ldisc.flush_buffer(tty); - + tty_ldisc_flush(tty); shutdown(ch); tty->closing = 0; ch->event = 0; @@ -657,10 +654,7 @@ static void pc_hangup(struct tty_struct *tty) cli(); if (tty->driver->flush_buffer) tty->driver->flush_buffer(tty); - - if (tty->ldisc.flush_buffer) - tty->ldisc.flush_buffer(tty); - + tty_ldisc_flush(tty); shutdown(ch); ch->tty = NULL; @@ -826,7 +820,7 @@ static int pc_write(struct tty_struct * tty, int from_user, bytesAvailable will then take on this newly calculated value. ---------------------------------------------------------------------- */ - bytesAvailable = MIN(dataLen, bytesAvailable); + bytesAvailable = min(dataLen, bytesAvailable); /* First we read the data in from the file system into a temp buffer */ @@ -912,7 +906,7 @@ static int pc_write(struct tty_struct * tty, int from_user, space; reduce the amount of data to fit the space. ---------------------------------------------------------------------- */ - bytesAvailable = MIN(remain, bytesAvailable); + bytesAvailable = min(remain, bytesAvailable); txwinon(ch); while (bytesAvailable > 0) @@ -923,7 +917,7 @@ static int pc_write(struct tty_struct * tty, int from_user, data copy fills to the end of card buffer. ------------------------------------------------------------------- */ - dataLen = MIN(bytesAvailable, dataLen); + dataLen = min(bytesAvailable, dataLen); memcpy(ch->txptr + head, buf, dataLen); buf += dataLen; head += dataLen; @@ -1120,8 +1114,7 @@ static void pc_flush_buffer(struct tty_struct *tty) restore_flags(flags); wake_up_interruptible(&tty->write_wait); - if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && tty->ldisc.write_wakeup) - (tty->ldisc.write_wakeup)(tty); + tty_wakeup(tty); } /* End pc_flush_buffer */ @@ -2262,9 +2255,7 @@ static void doevent(int crd) { /* Begin if LOWWAIT */ ch->statusflags &= ~LOWWAIT; - if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && - tty->ldisc.write_wakeup) - (tty->ldisc.write_wakeup)(tty); + tty_wakeup(tty); wake_up_interruptible(&tty->write_wait); } /* End if LOWWAIT */ @@ -2281,9 +2272,7 @@ static void doevent(int crd) { /* Begin if EMPTYWAIT */ ch->statusflags &= ~EMPTYWAIT; - if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && - tty->ldisc.write_wakeup) - (tty->ldisc.write_wakeup)(tty); + tty_wakeup(tty); wake_up_interruptible(&tty->write_wait); @@ -3136,6 +3125,7 @@ static int pc_ioctl(struct tty_struct *tty, struct file * file, } else { + /* ldisc lock already held in ioctl */ if (tty->ldisc.flush_buffer) tty->ldisc.flush_buffer(tty); } @@ -3307,7 +3297,6 @@ static void do_softint(void *private_) } } /* End EPCA_MAGIC */ - MOD_DEC_USE_COUNT; } /* End do_softint */ /* ------------------------------------------------------------ diff --git a/drivers/char/ftape/lowlevel/ftape-calibr.c b/drivers/char/ftape/lowlevel/ftape-calibr.c index 34f0e69d3..956b2586e 100644 --- a/drivers/char/ftape/lowlevel/ftape-calibr.c +++ b/drivers/char/ftape/lowlevel/ftape-calibr.c @@ -31,7 +31,10 @@ #include #if defined(__alpha__) # include -#elif defined(__i386__) || defined(__x86_64__) +#elif defined(__x86_64__) +# include +# include +#elif defined(__i386__) # include #endif #include @@ -45,7 +48,7 @@ # error Ftape is not implemented for this architecture! #endif -#if defined(__alpha__) +#if defined(__alpha__) || defined(__x86_64__) static unsigned long ps_per_cycle = 0; #endif @@ -72,7 +75,18 @@ unsigned int ftape_timestamp(void) asm volatile ("rpcc %0" : "=r" (r)); return r; -#elif defined(__i386__) || defined(__x86_64__) +#elif defined(__x86_64__) + unsigned long r; + rdtscl(r); + return r; +#elif defined(__i386__) + +/* + * Note that there is some time between counter underflowing and jiffies + * increasing, so the code below won't always give correct output. + * -Vojtech + */ + unsigned long flags; __u16 lo; __u16 hi; @@ -89,9 +103,9 @@ unsigned int ftape_timestamp(void) static unsigned int short_ftape_timestamp(void) { -#if defined(__alpha__) +#if defined(__alpha__) || defined(__x86_64__) return ftape_timestamp(); -#elif defined(__i386__) || defined(__x86_64__) +#elif defined(__i386__) unsigned int count; unsigned long flags; @@ -106,9 +120,9 @@ static unsigned int short_ftape_timestamp(void) static unsigned int diff(unsigned int t0, unsigned int t1) { -#if defined(__alpha__) - return (t1 <= t0) ? t1 + (1UL << 32) - t0 : t1 - t0; -#elif defined(__i386__) || defined(__x86_64__) +#if defined(__alpha__) || defined(__x86_64__) + return (t1 - t0); +#elif defined(__i386__) /* * This is tricky: to work for both short and full ftape_timestamps * we'll have to discriminate between these. @@ -122,9 +136,9 @@ static unsigned int diff(unsigned int t0, unsigned int t1) static unsigned int usecs(unsigned int count) { -#if defined(__alpha__) +#if defined(__alpha__) || defined(__x86_64__) return (ps_per_cycle * count) / 1000000UL; -#elif defined(__i386__) || defined(__x86_64__) +#elif defined(__i386__) return (10000 * count) / ((CLOCK_TICK_RATE + 50) / 100); #endif } @@ -163,38 +177,13 @@ static void time_inb(void) static void init_clock(void) { -#if defined(__i386__) || defined(__x86_64__) - unsigned int t; - int i; TRACE_FUN(ft_t_any); - /* Haven't studied on why, but there sometimes is a problem - * with the tick timer readout. The two bytes get swapped. - * This hack solves that problem by doing one extra input. - */ - for (i = 0; i < 1000; ++i) { - t = short_ftape_timestamp(); - if (t > LATCH) { - inb_p(0x40); /* get in sync again */ - TRACE(ft_t_warn, "clock counter fixed"); - break; - } - } +#if defined(__x86_64__) + ps_per_cycle = 1000000000UL / cpu_khz; #elif defined(__alpha__) -#if CONFIG_FT_ALPHA_CLOCK == 0 -#error You must define and set CONFIG_FT_ALPHA_CLOCK in 'make config' ! -#endif extern struct hwrpb_struct *hwrpb; - TRACE_FUN(ft_t_any); - - if (hwrpb->cycle_freq != 0) { - ps_per_cycle = (1000*1000*1000*1000UL) / hwrpb->cycle_freq; - } else { - /* - * HELP: Linux 2.0.x doesn't set cycle_freq on my noname ! - */ - ps_per_cycle = (1000*1000*1000*1000UL) / CONFIG_FT_ALPHA_CLOCK; - } + ps_per_cycle = (1000*1000*1000*1000UL) / hwrpb->cycle_freq; #endif TRACE_EXIT; } @@ -213,7 +202,7 @@ void ftape_calibrate(char *name, unsigned int tc = 0; unsigned int count; unsigned int time; -#if defined(__i386__) || defined(__x86_64__) +#if defined(__i386__) unsigned int old_tc = 0; unsigned int old_count = 1; unsigned int old_time = 1; @@ -255,7 +244,7 @@ void ftape_calibrate(char *name, tc = (1000 * time) / (count - 1); TRACE(ft_t_any, "once:%3d us,%6d times:%6d us, TC:%5d ns", usecs(once), count - 1, usecs(multiple), tc); -#if defined(__alpha__) +#if defined(__alpha__) || defined(__x86_64__) /* * Increase the calibration count exponentially until the * calibration time exceeds 100 ms. @@ -263,7 +252,7 @@ void ftape_calibrate(char *name, if (time >= 100*1000) { break; } -#elif defined(__i386__) || defined(__x86_64__) +#elif defined(__i386__) /* * increase the count until the resulting time nears 2/HZ, * then the tc will drop sharply because we lose LATCH counts. diff --git a/drivers/char/generic_serial.c b/drivers/char/generic_serial.c index 1027eb7c6..45bc1be63 100644 --- a/drivers/char/generic_serial.c +++ b/drivers/char/generic_serial.c @@ -436,9 +436,7 @@ void gs_flush_buffer(struct tty_struct *tty) restore_flags(flags); wake_up_interruptible(&tty->write_wait); - if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && - tty->ldisc.write_wakeup) - (tty->ldisc.write_wakeup)(tty); + tty_wakeup(tty); func_exit (); } @@ -578,9 +576,7 @@ void gs_do_softint(void *private_) if (!tty) return; if (test_and_clear_bit(RS_EVENT_WRITE_WAKEUP, &port->event)) { - if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && - tty->ldisc.write_wakeup) - (tty->ldisc.write_wakeup)(tty); + tty_wakeup(tty); wake_up_interruptible(&tty->write_wait); } func_exit (); @@ -694,7 +690,7 @@ void gs_close(struct tty_struct * tty, struct file * filp) { unsigned long flags; struct gs_port *port; - + func_enter (); if (!tty) return; @@ -760,8 +756,8 @@ void gs_close(struct tty_struct * tty, struct file * filp) if (tty->driver->flush_buffer) tty->driver->flush_buffer(tty); - if (tty->ldisc.flush_buffer) - tty->ldisc.flush_buffer(tty); + + tty_ldisc_flush(tty); tty->closing = 0; port->event = 0; diff --git a/drivers/char/hpet.c b/drivers/char/hpet.c index 5e8c47244..75d8b7b6a 100644 --- a/drivers/char/hpet.c +++ b/drivers/char/hpet.c @@ -60,8 +60,8 @@ static spinlock_t hpet_task_lock = SPIN_LOCK_UNLOCKED; struct hpet_dev { struct hpets *hd_hpets; - struct hpet *hd_hpet; - struct hpet_timer *hd_timer; + struct hpet __iomem *hd_hpet; + struct hpet_timer __iomem *hd_timer; unsigned long hd_ireqfreq; unsigned long hd_irqdata; wait_queue_head_t hd_waitqueue; @@ -75,7 +75,7 @@ struct hpet_dev { struct hpets { struct hpets *hp_next; - struct hpet *hp_hpet; + struct hpet __iomem *hp_hpet; unsigned long hp_period; unsigned long hp_delta; unsigned int hp_ntimer; @@ -98,14 +98,14 @@ static struct hpets *hpets; #endif #ifndef readq -static unsigned long long __inline readq(void *addr) +static unsigned long long __inline readq(void __iomem *addr) { return readl(addr) | (((unsigned long long)readl(addr + 4)) << 32LL); } #endif #ifndef writeq -static void __inline writeq(unsigned long long v, void *addr) +static void __inline writeq(unsigned long long v, void __iomem *addr) { writel(v & 0xffffffff, addr); writel(v >> 32, addr + 4); @@ -300,7 +300,7 @@ static int hpet_fasync(int fd, struct file *file, int on) static int hpet_release(struct inode *inode, struct file *file) { struct hpet_dev *devp; - struct hpet_timer *timer; + struct hpet_timer __iomem *timer; int irq = 0; devp = file->private_data; @@ -352,8 +352,8 @@ hpet_ioctl(struct inode *inode, struct file *file, unsigned int cmd, static int hpet_ioctl_ieon(struct hpet_dev *devp) { - struct hpet_timer *timer; - struct hpet *hpet; + struct hpet_timer __iomem *timer; + struct hpet __iomem *hpet; struct hpets *hpetp; int irq; unsigned long g, v, t, m; @@ -435,8 +435,8 @@ static inline unsigned long hpet_time_div(unsigned long dis) static int hpet_ioctl_common(struct hpet_dev *devp, int cmd, unsigned long arg, int kernel) { - struct hpet_timer *timer; - struct hpet *hpet; + struct hpet_timer __iomem *timer; + struct hpet __iomem *hpet; struct hpets *hpetp; int err; unsigned long v; @@ -547,7 +547,7 @@ int hpet_register(struct hpet_task *tp, int periodic) { unsigned int i; u64 mask; - struct hpet_timer *timer; + struct hpet_timer __iomem *timer; struct hpet_dev *devp; struct hpets *hpetp; @@ -615,7 +615,7 @@ static inline int hpet_tpcheck(struct hpet_task *tp) int hpet_unregister(struct hpet_task *tp) { struct hpet_dev *devp; - struct hpet_timer *timer; + struct hpet_timer __iomem *timer; int err; if ((err = hpet_tpcheck(tp))) @@ -662,40 +662,10 @@ int hpet_control(struct hpet_task *tp, unsigned int cmd, unsigned long arg) #ifdef CONFIG_TIME_INTERPOLATION -static unsigned long hpet_offset, last_wall_hpet; -static long hpet_nsecs_per_cycle, hpet_cycles_per_sec; - -static unsigned long hpet_getoffset(void) -{ - return hpet_offset + (read_counter(&hpets->hp_hpet->hpet_mc) - - last_wall_hpet) * hpet_nsecs_per_cycle; -} - -static void hpet_update(long delta) -{ - unsigned long mc; - unsigned long offset; - - mc = read_counter(&hpets->hp_hpet->hpet_mc); - offset = hpet_offset + (mc - last_wall_hpet) * hpet_nsecs_per_cycle; - - if (delta < 0 || (unsigned long)delta < offset) - hpet_offset = offset - delta; - else - hpet_offset = 0; - last_wall_hpet = mc; -} - -static void hpet_reset(void) -{ - hpet_offset = 0; - last_wall_hpet = read_counter(&hpets->hp_hpet->hpet_mc); -} - static struct time_interpolator hpet_interpolator = { - .get_offset = hpet_getoffset, - .update = hpet_update, - .reset = hpet_reset + .source = TIME_SOURCE_MMIO64, + .shift = 10, + .mask = 0xffffffffffffffffLL }; #endif @@ -745,11 +715,11 @@ static struct ctl_table_header *sysctl_header; static unsigned long __init hpet_calibrate(struct hpets *hpetp) { - struct hpet_timer *timer = NULL; + struct hpet_timer __iomem *timer = NULL; unsigned long t, m, count, i, flags, start; struct hpet_dev *devp; int j; - struct hpet *hpet; + struct hpet __iomem *hpet; for (j = 0, devp = hpetp->hp_dev; j < hpetp->hp_ntimer; j++, devp++) if ((devp->hd_flags & HPET_OPEN) == 0) { @@ -787,8 +757,9 @@ int __init hpet_alloc(struct hpet_data *hdp) u32 i, ntimer; struct hpets *hpetp; size_t siz; - struct hpet *hpet; + struct hpet __iomem *hpet; static struct hpets *last __initdata = (struct hpets *)0; + unsigned long ns; /* * hpet_alloc can be called by platform dependent code. @@ -796,7 +767,7 @@ int __init hpet_alloc(struct hpet_data *hdp) * ACPI also reports hpet, then we catch it here. */ for (hpetp = hpets; hpetp; hpetp = hpetp->hp_next) - if (hpetp->hp_hpet == (struct hpet *)(hdp->hd_address)) + if (hpetp->hp_hpet == hdp->hd_address) return 0; siz = sizeof(struct hpets) + ((hdp->hd_nirqs - 1) * @@ -810,7 +781,7 @@ int __init hpet_alloc(struct hpet_data *hdp) memset(hpetp, 0, siz); hpetp->hp_which = hpet_nhpet++; - hpetp->hp_hpet = (struct hpet *)hdp->hd_address; + hpetp->hp_hpet = hdp->hd_address; hpetp->hp_ntimer = hdp->hd_nirqs; @@ -840,6 +811,18 @@ int __init hpet_alloc(struct hpet_data *hdp) hpetp->hp_period = (cap & HPET_COUNTER_CLK_PERIOD_MASK) >> HPET_COUNTER_CLK_PERIOD_SHIFT; + printk(KERN_INFO "hpet%d: at MMIO 0x%p, IRQ%s", + hpetp->hp_which, hpet, hpetp->hp_ntimer > 1 ? "s" : ""); + for (i = 0; i < hpetp->hp_ntimer; i++) + printk("%s %d", i > 0 ? "," : "", hdp->hd_irq[i]); + printk("\n"); + + ns = hpetp->hp_period; /* femptoseconds, 10^-15 */ + do_div(ns, 1000000); /* convert to nanoseconds, 10^-9 */ + printk(KERN_INFO "hpet%d: %ldns tick, %d %d-bit timers\n", + hpetp->hp_which, ns, hpetp->hp_ntimer, + cap & HPET_COUNTER_SIZE_MASK ? 64 : 32); + mcfg = readq(&hpet->hpet_config); if ((mcfg & HPET_ENABLE_CNF_MASK) == 0) { write_counter(0L, &hpet->hpet_mc); @@ -850,7 +833,7 @@ int __init hpet_alloc(struct hpet_data *hdp) for (i = 0, devp = hpetp->hp_dev; i < hpetp->hp_ntimer; i++, hpet_ntimer++, devp++) { unsigned long v; - struct hpet_timer *timer; + struct hpet_timer __iomem *timer; timer = &hpet->hpet_timers[devp - hpetp->hp_dev]; v = readq(&timer->hpet_config); @@ -891,11 +874,10 @@ static acpi_status __init hpet_resources(struct acpi_resource *res, void *data) unsigned long size; size = addr.max_address_range - addr.min_address_range + 1; - hdp->hd_address = - (unsigned long)ioremap(addr.min_address_range, size); + hdp->hd_address = ioremap(addr.min_address_range, size); for (hpetp = hpets; hpetp; hpetp = hpetp->hp_next) - if (hpetp->hp_hpet == (struct hpet *)(hdp->hd_address)) + if (hpetp->hp_hpet == hdp->hd_address) return -EBUSY; } else if (res->id == ACPI_RSTYPE_EXT_IRQ) { struct acpi_resource_ext_irq *irqp; @@ -944,9 +926,8 @@ static int __init hpet_acpi_remove(struct acpi_device *device, int type) return 0; } -static struct acpi_driver hpet_acpi_driver __initdata = { +static struct acpi_driver hpet_acpi_driver = { .name = "hpet", - .class = "", .ids = "PNP0103", .ops = { .add = hpet_acpi_add, @@ -971,11 +952,10 @@ static int __init hpet_init(void) struct hpet *hpet; hpet = hpets->hp_hpet; - hpet_cycles_per_sec = hpet_time_div(hpets->hp_period); - hpet_interpolator.frequency = hpet_cycles_per_sec; - hpet_interpolator.drift = hpet_cycles_per_sec * + hpet_interpolator.addr = &hpets->hp_hpet->hpet_mc; + hpet_interpolator.frequency = hpet_time_div(hpets->hp_period); + hpet_interpolator.drift = hpet_interpolator.frequency * HPET_DRIFT / 1000000; - hpet_nsecs_per_cycle = 1000000000 / hpet_cycles_per_sec; register_time_interpolator(&hpet_interpolator); } #endif diff --git a/drivers/char/hvc_console.c b/drivers/char/hvc_console.c index d4bf205fd..3c0af2cad 100644 --- a/drivers/char/hvc_console.c +++ b/drivers/char/hvc_console.c @@ -1,6 +1,11 @@ /* * Copyright (C) 2001 Anton Blanchard , IBM * Copyright (C) 2001 Paul Mackerras , IBM + * Copyright (C) 2004 Benjamin Herrenschmidt , IBM Corp. + * Copyright (C) 2004 IBM Corporation + * + * Additional Author(s): + * Ryan S. Arnold * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -17,38 +22,51 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ +#include +#include #include +#include +#include +#include +#include +#include #include -#include #include -#include #include #include #include #include -#include -#include #include -#include - -extern int hvc_count(int *); -extern int hvc_get_chars(int index, char *buf, int count); -extern int hvc_put_chars(int index, const char *buf, int count); +#include +#include +#include #define HVC_MAJOR 229 #define HVC_MINOR 0 -#define MAX_NR_HVC_CONSOLES 4 - #define TIMEOUT ((HZ + 99) / 100) +/* + * Wait this long per iteration while trying to push buffered data to the + * hypervisor before allowing the tty to complete a close operation. + */ +#define HVC_CLOSE_WAIT (HZ/100) /* 1/10 of a second */ + +/* + * The Linux TTY code does not support dynamic addition of tty derived devices + * so we need to know how many tty devices we might need when space is allocated + * for the tty device. Since this driver supports hotplug of vty adapters we + * need to make sure we have enough allocated. + */ +#define HVC_ALLOC_TTY_ADAPTERS 8 + static struct tty_driver *hvc_driver; -static int hvc_offset; #ifdef CONFIG_MAGIC_SYSRQ static int sysrq_pressed; #endif #define N_OUTBUF 16 +#define N_INBUF 16 #define __ALIGNED__ __attribute__((__aligned__(8))) @@ -60,59 +78,249 @@ struct hvc_struct { int do_wakeup; char outbuf[N_OUTBUF] __ALIGNED__; int n_outbuf; + uint32_t vtermno; + int irq_requested; + int irq; + struct list_head next; + struct kobject kobj; /* ref count & hvc_struct lifetime */ + struct vio_dev *vdev; }; -struct hvc_struct hvc_struct[MAX_NR_HVC_CONSOLES]; +/* dynamic list of hvc_struct instances */ +static struct list_head hvc_structs = LIST_HEAD_INIT(hvc_structs); + +/* + * Protect the list of hvc_struct instances from inserts and removals during + * list traversal. + */ +static spinlock_t hvc_structs_lock = SPIN_LOCK_UNLOCKED; + +/* + * Initial console vtermnos for console API usage prior to full console + * initialization. Any vty adapter outside this range will not have usable + * console interfaces but can still be used as a tty device. This has to be + * static because kmalloc will not work during early console init. + */ +static uint32_t vtermnos[MAX_NR_HVC_CONSOLES]; + +/* Used for accounting purposes */ +static int num_vterms = 0; + +static struct task_struct *hvc_task; + +/* + * This value is used to associate a tty->index value to a hvc_struct based + * upon order of exposure via hvc_probe(). + */ +static int hvc_count = -1; + +/* Picks up late kicks after list walk but before schedule() */ +static int hvc_kicked; + +/* Wake the sleeping khvcd */ +static void hvc_kick(void) +{ + hvc_kicked = 1; + wake_up_process(hvc_task); +} + +/* + * NOTE: This API isn't used if the console adapter doesn't support interrupts. + * In this case the console is poll driven. + */ +static irqreturn_t hvc_handle_interrupt(int irq, void *dev_instance, struct pt_regs *regs) +{ + hvc_kick(); + return IRQ_HANDLED; +} + +static void hvc_unthrottle(struct tty_struct *tty) +{ + hvc_kick(); +} + +/* + * Do not call this function with either the hvc_strucst_lock or the hvc_struct + * lock held. If successful, this function increments the kobject reference + * count against the target hvc_struct so it should be released when finished. + */ +struct hvc_struct *hvc_get_by_index(int index) +{ + struct hvc_struct *hp; + unsigned long flags; + + spin_lock(&hvc_structs_lock); + list_for_each_entry(hp, &hvc_structs, next) { + spin_lock_irqsave(&hp->lock, flags); + if (hp->index == index) { + kobject_get(&hp->kobj); + spin_unlock_irqrestore(&hp->lock, flags); + spin_unlock(&hvc_structs_lock); + return hp; + } + spin_unlock_irqrestore(&hp->lock, flags); + } + hp = NULL; + + spin_unlock(&hvc_structs_lock); + return hp; +} + +/* + * The TTY interface won't be used until after the vio layer has exposed the vty + * adapter to the kernel. + */ static int hvc_open(struct tty_struct *tty, struct file * filp) { - int line = tty->index; struct hvc_struct *hp; unsigned long flags; + int irq = NO_IRQ; + int rc = 0; + struct kobject *kobjp; - if (line < 0 || line >= MAX_NR_HVC_CONSOLES) + /* Auto increments kobject reference if found. */ + if (!(hp = hvc_get_by_index(tty->index))) { + printk(KERN_WARNING "hvc_console: tty open failed, no vty associated with tty.\n"); return -ENODEV; - hp = &hvc_struct[line]; + } - tty->driver_data = hp; spin_lock_irqsave(&hp->lock, flags); + /* Check and then increment for fast path open. */ + if (hp->count++ > 0) { + spin_unlock_irqrestore(&hp->lock, flags); + hvc_kick(); + return 0; + } /* else count == 0 */ + + tty->driver_data = hp; hp->tty = tty; - hp->count++; + /* Save for request_irq outside of spin_lock. */ + irq = hp->irq; + if (irq != NO_IRQ) + hp->irq_requested = 1; + + kobjp = &hp->kobj; + spin_unlock_irqrestore(&hp->lock, flags); + /* check error, fallback to non-irq */ + if (irq != NO_IRQ) + rc = request_irq(irq, hvc_handle_interrupt, SA_INTERRUPT, "hvc_console", hp); + + /* + * If the request_irq() fails and we return an error. The tty layer + * will call hvc_close() after a failed open but we don't want to clean + * up there so we'll clean up here and clear out the previously set + * tty fields and return the kobject reference. + */ + if (rc) { + spin_lock_irqsave(&hp->lock, flags); + hp->tty = NULL; + hp->irq_requested = 0; + spin_unlock_irqrestore(&hp->lock, flags); + tty->driver_data = NULL; + kobject_put(kobjp); + } + /* Force wakeup of the polling thread */ + hvc_kick(); - return 0; + return rc; } static void hvc_close(struct tty_struct *tty, struct file * filp) { - struct hvc_struct *hp = tty->driver_data; + struct hvc_struct *hp; + struct kobject *kobjp; + int irq = NO_IRQ; unsigned long flags; if (tty_hung_up_p(filp)) return; + + /* + * No driver_data means that this close was issued after a failed + * hvcs_open by the tty layer's release_dev() function and we can just + * exit cleanly because the kobject reference wasn't made. + */ + if (!tty->driver_data) + return; + + hp = tty->driver_data; spin_lock_irqsave(&hp->lock, flags); - if (--hp->count == 0) + + kobjp = &hp->kobj; + if (--hp->count == 0) { + if (hp->irq_requested) + irq = hp->irq; + hp->irq_requested = 0; + + /* We are done with the tty pointer now. */ hp->tty = NULL; - else if (hp->count < 0) - printk(KERN_ERR "hvc_close %lu: oops, count is %d\n", - hp - hvc_struct, hp->count); - spin_unlock_irqrestore(&hp->lock, flags); + spin_unlock_irqrestore(&hp->lock, flags); + + /* + * Chain calls chars_in_buffer() and returns immediately if + * there is no buffered data otherwise sleeps on a wait queue + * waking periodically to check chars_in_buffer(). + */ + tty_wait_until_sent(tty, HVC_CLOSE_WAIT); + + /* + * Since the line disc doesn't block writes during tty close + * operations we'll set driver_data to NULL and then make sure + * to check tty->driver_data for NULL in hvc_write(). + */ + tty->driver_data = NULL; + + if (irq != NO_IRQ) + free_irq(irq, hp); + + } else { + if (hp->count < 0) + printk(KERN_ERR "hvc_close %X: oops, count is %d\n", + hp->vtermno, hp->count); + spin_unlock_irqrestore(&hp->lock, flags); + } + + kobject_put(kobjp); } static void hvc_hangup(struct tty_struct *tty) { struct hvc_struct *hp = tty->driver_data; + unsigned long flags; + int irq = NO_IRQ; + int temp_open_count; + struct kobject *kobjp; + spin_lock_irqsave(&hp->lock, flags); + kobjp = &hp->kobj; + temp_open_count = hp->count; hp->count = 0; + hp->n_outbuf = 0; hp->tty = NULL; + if (hp->irq_requested) + /* Saved for use outside of spin_lock. */ + irq = hp->irq; + hp->irq_requested = 0; + spin_unlock_irqrestore(&hp->lock, flags); + if (irq != NO_IRQ) + free_irq(irq, hp); + while(temp_open_count) { + --temp_open_count; + kobject_put(kobjp); + } } -/* called with hp->lock held */ +/* + * Push buffered characters whether they were just recently buffered or waiting + * on a blocked hypervisor. Call this function with hp->lock held. + */ static void hvc_push(struct hvc_struct *hp) { int n; - n = hvc_put_chars(hp->index + hvc_offset, hp->outbuf, hp->n_outbuf); + n = hvc_put_chars(hp->vtermno, hp->outbuf, hp->n_outbuf); if (n <= 0) { if (n == 0) return; @@ -127,77 +335,126 @@ static void hvc_push(struct hvc_struct *hp) hp->do_wakeup = 1; } -static int hvc_write(struct tty_struct *tty, int from_user, - const unsigned char *buf, int count) +static inline int __hvc_write_user(struct hvc_struct *hp, + const unsigned char *buf, int count) { - struct hvc_struct *hp = tty->driver_data; char *tbuf, *p; int tbsize, rsize, written = 0; unsigned long flags; - if (from_user) { - tbsize = min(count, (int)PAGE_SIZE); - if (!(tbuf = kmalloc(tbsize, GFP_KERNEL))) - return -ENOMEM; - - while ((rsize = count - written) > 0) { - int wsize; - if (rsize > tbsize) - rsize = tbsize; - - p = tbuf; - rsize -= copy_from_user(p, buf, rsize); - if (!rsize) { - if (written == 0) - written = -EFAULT; - break; - } - buf += rsize; - written += rsize; - - spin_lock_irqsave(&hp->lock, flags); - for (wsize = N_OUTBUF - hp->n_outbuf; rsize && wsize; - wsize = N_OUTBUF - hp->n_outbuf) { - if (wsize > rsize) - wsize = rsize; - memcpy(hp->outbuf + hp->n_outbuf, p, wsize); - hp->n_outbuf += wsize; - hvc_push(hp); - rsize -= wsize; - p += wsize; - } - spin_unlock_irqrestore(&hp->lock, flags); - - if (rsize) - break; + tbsize = min(count, (int)PAGE_SIZE); + if (!(tbuf = kmalloc(tbsize, GFP_KERNEL))) + return -ENOMEM; - if (count < tbsize) - tbsize = count; + while ((rsize = count - written) > 0) { + int wsize; + if (rsize > tbsize) + rsize = tbsize; + + p = tbuf; + rsize -= copy_from_user(p, buf, rsize); + if (!rsize) { + if (written == 0) + written = -EFAULT; + break; } + buf += rsize; - kfree(tbuf); - } else { spin_lock_irqsave(&hp->lock, flags); - while (count > 0 && (rsize = N_OUTBUF - hp->n_outbuf) > 0) { - if (rsize > count) - rsize = count; - memcpy(hp->outbuf + hp->n_outbuf, buf, rsize); - count -= rsize; - buf += rsize; - hp->n_outbuf += rsize; - written += rsize; + + /* Push pending writes: make some room in buffer */ + if (hp->n_outbuf > 0) hvc_push(hp); + + for (wsize = N_OUTBUF - hp->n_outbuf; rsize && wsize; + wsize = N_OUTBUF - hp->n_outbuf) { + if (wsize > rsize) + wsize = rsize; + memcpy(hp->outbuf + hp->n_outbuf, p, wsize); + hp->n_outbuf += wsize; + hvc_push(hp); + rsize -= wsize; + p += wsize; + written += wsize; } spin_unlock_irqrestore(&hp->lock, flags); + + if (rsize) + break; + + if (count < tbsize) + tbsize = count; + } + + kfree(tbuf); + + return written; +} + +static inline int __hvc_write_kernel(struct hvc_struct *hp, + const unsigned char *buf, int count) +{ + unsigned long flags; + int rsize, written = 0; + + spin_lock_irqsave(&hp->lock, flags); + + /* Push pending writes */ + if (hp->n_outbuf > 0) + hvc_push(hp); + + while (count > 0 && (rsize = N_OUTBUF - hp->n_outbuf) > 0) { + if (rsize > count) + rsize = count; + memcpy(hp->outbuf + hp->n_outbuf, buf, rsize); + count -= rsize; + buf += rsize; + hp->n_outbuf += rsize; + written += rsize; + hvc_push(hp); } + spin_unlock_irqrestore(&hp->lock, flags); + + return written; +} +static int hvc_write(struct tty_struct *tty, int from_user, + const unsigned char *buf, int count) +{ + struct hvc_struct *hp = tty->driver_data; + int written; + + /* This write was probably executed during a tty close. */ + if (!hp) + return -EPIPE; + + if (from_user) + written = __hvc_write_user(hp, buf, count); + else + written = __hvc_write_kernel(hp, buf, count); + + /* + * Racy, but harmless, kick thread if there is still pending data. + * There really is nothing wrong with kicking the thread, even if there + * is no buffered data. + */ + if (hp->n_outbuf) + hvc_kick(); return written; } +/* + * This is actually a contract between the driver and the tty layer outlining + * how much write room the driver can guarentee will be sent OR BUFFERED. This + * driver MUST honor the return value. + */ static int hvc_write_room(struct tty_struct *tty) { struct hvc_struct *hp = tty->driver_data; + if (!hp) + return -1; + return N_OUTBUF - hp->n_outbuf; } @@ -205,57 +462,108 @@ static int hvc_chars_in_buffer(struct tty_struct *tty) { struct hvc_struct *hp = tty->driver_data; + if (!hp) + return -1; return hp->n_outbuf; } -static void hvc_poll(int index) +#define HVC_POLL_READ 0x00000001 +#define HVC_POLL_WRITE 0x00000002 +#define HVC_POLL_QUICK 0x00000004 + +static int hvc_poll(struct hvc_struct *hp) { - struct hvc_struct *hp = &hvc_struct[index]; struct tty_struct *tty; - int i, n; - char buf[16] __ALIGNED__; + int i, n, poll_mask = 0; + char buf[N_INBUF] __ALIGNED__; unsigned long flags; + int read_total = 0; spin_lock_irqsave(&hp->lock, flags); + /* Push pending writes */ if (hp->n_outbuf > 0) hvc_push(hp); + /* Reschedule us if still some write pending */ + if (hp->n_outbuf > 0) + poll_mask |= HVC_POLL_WRITE; + /* No tty attached, just skip */ tty = hp->tty; - if (tty) { - for (;;) { - if (TTY_FLIPBUF_SIZE - tty->flip.count < sizeof(buf)) - break; - n = hvc_get_chars(index + hvc_offset, buf, sizeof(buf)); - if (n <= 0) - break; - for (i = 0; i < n; ++i) { -#ifdef CONFIG_MAGIC_SYSRQ /* Handle the SysRq Hack */ - if (buf[i] == '\x0f') { /* ^O -- should support a sequence */ - sysrq_pressed = 1; - continue; - } else if (sysrq_pressed) { - handle_sysrq(buf[i], NULL, tty); - sysrq_pressed = 0; - continue; - } -#endif - tty_insert_flip_char(tty, buf[i], 0); + if (tty == NULL) + goto bail; + + /* Now check if we can get data (are we throttled ?) */ + if (test_bit(TTY_THROTTLED, &tty->flags)) + goto throttled; + + /* If we aren't interrupt driven and aren't throttled, we always + * request a reschedule + */ + if (hp->irq == NO_IRQ) + poll_mask |= HVC_POLL_READ; + + /* Read data if any */ + for (;;) { + int count = N_INBUF; + if (count > (TTY_FLIPBUF_SIZE - tty->flip.count)) + count = TTY_FLIPBUF_SIZE - tty->flip.count; + + /* If flip is full, just reschedule a later read */ + if (count == 0) { + poll_mask |= HVC_POLL_READ; + break; + } + + n = hvc_get_chars(hp->vtermno, buf, count); + if (n <= 0) { + /* Hangup the tty when disconnected from host */ + if (n == -EPIPE) { + spin_unlock_irqrestore(&hp->lock, flags); + tty_hangup(tty); + spin_lock_irqsave(&hp->lock, flags); } + break; } + for (i = 0; i < n; ++i) { +#ifdef CONFIG_MAGIC_SYSRQ + /* Handle the SysRq Hack */ + if (buf[i] == '\x0f') { /* ^O -- should support a sequence */ + sysrq_pressed = 1; + continue; + } else if (sysrq_pressed) { + handle_sysrq(buf[i], NULL, tty); + sysrq_pressed = 0; + continue; + } +#endif /* CONFIG_MAGIC_SYSRQ */ + tty_insert_flip_char(tty, buf[i], 0); + } + if (tty->flip.count) tty_schedule_flip(tty); - if (hp->do_wakeup) { - hp->do_wakeup = 0; - if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) - && tty->ldisc.write_wakeup) - (tty->ldisc.write_wakeup)(tty); - wake_up_interruptible(&tty->write_wait); + /* + * Account for the total amount read in one loop, and if above + * 64 bytes, we do a quick schedule loop to let the tty grok the + * data and eventually throttle us. + */ + read_total += n; + if (read_total >= 64) { + poll_mask |= HVC_POLL_QUICK; + break; } } - + throttled: + /* Wakeup write queue if necessary */ + if (hp->do_wakeup) { + hp->do_wakeup = 0; + tty_wakeup(tty); + } + bail: spin_unlock_irqrestore(&hp->lock, flags); + + return poll_mask; } #if defined(CONFIG_XMON) && defined(CONFIG_SMP) @@ -264,21 +572,47 @@ extern cpumask_t cpus_in_xmon; static const cpumask_t cpus_in_xmon = CPU_MASK_NONE; #endif - +/* + * This kthread is either polling or interrupt driven. This is determined by + * calling hvc_poll() who determines whether a console adapter support + * interrupts. + */ int khvcd(void *unused) { - int i; - - daemonize("khvcd"); + int poll_mask; + struct hvc_struct *hp; - for (;;) { + __set_current_state(TASK_RUNNING); + do { + poll_mask = 0; + hvc_kicked = 0; + wmb(); if (cpus_empty(cpus_in_xmon)) { - for (i = 0; i < MAX_NR_HVC_CONSOLES; ++i) - hvc_poll(i); + spin_lock(&hvc_structs_lock); + list_for_each_entry(hp, &hvc_structs, next) { + /*hp = list_entry(node, struct hvc_struct, * next); */ + poll_mask |= hvc_poll(hp); + } + spin_unlock(&hvc_structs_lock); + } else + poll_mask |= HVC_POLL_READ; + if (hvc_kicked) + continue; + if (poll_mask & HVC_POLL_QUICK) { + yield(); + continue; } set_current_state(TASK_INTERRUPTIBLE); - schedule_timeout(TIMEOUT); - } + if (!hvc_kicked) { + if (poll_mask == 0) + schedule(); + else + schedule_timeout(TIMEOUT); + } + __set_current_state(TASK_RUNNING); + } while (!kthread_should_stop()); + + return 0; } static struct tty_operations hvc_ops = { @@ -286,19 +620,124 @@ static struct tty_operations hvc_ops = { .close = hvc_close, .write = hvc_write, .hangup = hvc_hangup, + .unthrottle = hvc_unthrottle, .write_room = hvc_write_room, .chars_in_buffer = hvc_chars_in_buffer, }; -int __init hvc_init(void) +char hvc_driver_name[] = "hvc_console"; + +static struct vio_device_id hvc_driver_table[] __devinitdata= { + {"serial", "hvterm1"}, + { 0, } +}; +MODULE_DEVICE_TABLE(vio, hvc_driver_table); + +/* callback when the kboject ref count reaches zero. */ +static void destroy_hvc_struct(struct kobject *kobj) { - int num = hvc_count(&hvc_offset); - int i; + struct hvc_struct *hp = container_of(kobj, struct hvc_struct, kobj); + unsigned long flags; - if (num > MAX_NR_HVC_CONSOLES) - num = MAX_NR_HVC_CONSOLES; + spin_lock(&hvc_structs_lock); - hvc_driver = alloc_tty_driver(num); + spin_lock_irqsave(&hp->lock, flags); + list_del(&(hp->next)); + spin_unlock_irqrestore(&hp->lock, flags); + + spin_unlock(&hvc_structs_lock); + + kfree(hp); +} + +static struct kobj_type hvc_kobj_type = { + .release = destroy_hvc_struct, +}; + +static int __devinit hvc_probe( + struct vio_dev *dev, + const struct vio_device_id *id) +{ + struct hvc_struct *hp; + + /* probed with invalid parameters. */ + if (!dev || !id) + return -EPERM; + + hp = kmalloc(sizeof(*hp), GFP_KERNEL); + if (!hp) + return -ENOMEM; + + memset(hp, 0x00, sizeof(*hp)); + hp->vtermno = dev->unit_address; + hp->vdev = dev; + hp->vdev->dev.driver_data = hp; + hp->irq = dev->irq; + + kobject_init(&hp->kobj); + hp->kobj.ktype = &hvc_kobj_type; + + hp->lock = SPIN_LOCK_UNLOCKED; + spin_lock(&hvc_structs_lock); + hp->index = ++hvc_count; + list_add_tail(&(hp->next), &hvc_structs); + spin_unlock(&hvc_structs_lock); + + return 0; +} + +static int __devexit hvc_remove(struct vio_dev *dev) +{ + struct hvc_struct *hp = dev->dev.driver_data; + unsigned long flags; + struct kobject *kobjp; + struct tty_struct *tty; + + spin_lock_irqsave(&hp->lock, flags); + tty = hp->tty; + kobjp = &hp->kobj; + + if (hp->index < MAX_NR_HVC_CONSOLES) + vtermnos[hp->index] = -1; + + /* Don't whack hp->irq because tty_hangup() will need to free the irq. */ + + spin_unlock_irqrestore(&hp->lock, flags); + + /* + * We 'put' the instance that was grabbed when the kobject instance + * was intialized using kobject_init(). Let the last holder of this + * kobject cause it to be removed, which will probably be the tty_hangup + * below. + */ + kobject_put(kobjp); + + /* + * This function call will auto chain call hvc_hangup. The tty should + * always be valid at this time unless a simultaneous tty close already + * cleaned up the hvc_struct. + */ + if (tty) + tty_hangup(tty); + return 0; +} + +static struct vio_driver hvc_vio_driver = { + .name = hvc_driver_name, + .id_table = hvc_driver_table, + .probe = hvc_probe, + .remove = hvc_remove, +}; + +/* Driver initialization. Follow console initialization. This is where the TTY + * interfaces start to become available. */ +int __init hvc_init(void) +{ + int rc; + + /* We need more than num_vterms adapters due to hotplug additions. */ + hvc_driver = alloc_tty_driver(HVC_ALLOC_TTY_ADAPTERS); + /* hvc_driver = alloc_tty_driver(num_vterms); */ if (!hvc_driver) return -ENOMEM; @@ -312,31 +751,73 @@ int __init hvc_init(void) hvc_driver->init_termios = tty_std_termios; hvc_driver->flags = TTY_DRIVER_REAL_RAW; tty_set_operations(hvc_driver, &hvc_ops); - for (i = 0; i < num; i++) { - hvc_struct[i].lock = SPIN_LOCK_UNLOCKED; - hvc_struct[i].index = i; - } if (tty_register_driver(hvc_driver)) panic("Couldn't register hvc console driver\n"); - if (num > 0) - kernel_thread(khvcd, NULL, CLONE_KERNEL); + /* Always start the kthread because there can be hotplug vty adapters + * added later. */ + hvc_task = kthread_run(khvcd, NULL, "khvcd"); + if (IS_ERR(hvc_task)) { + panic("Couldn't create kthread for console.\n"); + put_tty_driver(hvc_driver); + return -EIO; + } + + /* Register as a vio device to receive callbacks */ + rc = vio_register_driver(&hvc_vio_driver); - return 0; + return rc; } +/* This isn't particularily necessary due to this being a console driver but it + * is nice to be thorough */ static void __exit hvc_exit(void) { + kthread_stop(hvc_task); + + vio_unregister_driver(&hvc_vio_driver); + tty_unregister_driver(hvc_driver); + /* return tty_struct instances allocated in hvc_init(). */ + put_tty_driver(hvc_driver); +} + +/* + * Console APIs, NOT TTY. These APIs are available immediately when + * hvc_console_setup() finds adapters. + */ + +/* + * hvc_instantiate() is an early console discovery method which locates consoles + * prior to the vio subsystem discovering them. Hotplugged vty adapters do NOT + * get an hvc_instantiate() callback since the appear after early console init. + */ +int hvc_instantiate(uint32_t vtermno, int index) +{ + if (index < 0 || index >= MAX_NR_HVC_CONSOLES) + return -1; + + if (vtermnos[index] != -1) + return -1; + + vtermnos[index] = vtermno; + return 0; } void hvc_console_print(struct console *co, const char *b, unsigned count) { char c[16] __ALIGNED__; - unsigned i, n; + unsigned i = 0, n = 0; int r, donecr = 0; - i = n = 0; + /* Console access attempt outside of acceptable console range. */ + if (co->index >= MAX_NR_HVC_CONSOLES) + return; + + /* This console adapter was removed so it is not useable. */ + if (vtermnos[co->index] < 0) + return; + while (count > 0 || i > 0) { if (count > 0 && i < sizeof(c)) { if (b[n] == '\n' && !donecr) { @@ -348,7 +829,7 @@ void hvc_console_print(struct console *co, const char *b, unsigned count) --count; } } else { - r = hvc_put_chars(co->index + hvc_offset, c, i); + r = hvc_put_chars(vtermnos[co->index], c, i); if (r < 0) { /* throw away chars on error */ i = 0; @@ -369,9 +850,6 @@ static struct tty_driver *hvc_console_device(struct console *c, int *index) static int __init hvc_console_setup(struct console *co, char *options) { - if (co->index < 0 || co->index >= MAX_NR_HVC_CONSOLES - || co->index >= hvc_count(&hvc_offset)) - return -1; return 0; } @@ -384,8 +862,14 @@ struct console hvc_con_driver = { .index = -1, }; +/* Early console initialization. Preceeds driver initialization. */ static int __init hvc_console_init(void) { + int i; + + for (i=0; i /* - * 1.0.0 -> 1.1.0 Added kernel_thread scheduling methodology to driver to - * replace wait_task constructs. + * 1.3.0 -> 1.3.1 In hvcs_open memset(..,0x00,..) instead of memset(..,0x3F,00). + * Removed braces around single statements following conditionals. Removed '= + * 0' after static int declarations since these default to zero. Removed + * list_for_each_safe() and replaced with list_for_each_entry() in + * hvcs_get_by_index(). The 'safe' version is un-needed now that the driver is + * using spinlocks. Changed spin_lock_irqsave() to spin_lock() when locking + * hvcs_structs_lock and hvcs_pi_lock since these are not touched in an int + * handler. Initialized hvcs_structs_lock and hvcs_pi_lock to + * SPIN_LOCK_UNLOCKED at declaration time rather than in hvcs_module_init(). + * Added spin_lock around list_del() in destroy_hvcs_struct() to protect the + * list traversals from a deletion. Removed '= NULL' from pointer declaration + * statements since they are initialized NULL by default. Removed wmb() + * instances from hvcs_try_write(). They probably aren't needed with locking in + * place. Added check and cleanup for hvcs_pi_buff = kmalloc() in + * hvcs_module_init(). Exposed hvcs_struct.index via a sysfs attribute so that + * the coupling between /dev/hvcs* and a vty-server can be automatically + * determined. Moved kobject_put() in hvcs_open outside of the + * spin_unlock_irqrestore(). * - * 1.1.0 -> 1.2.0 Moved pi_buff initialization out of arch code into driver code - * and added locking to share this buffer between hvcs_struct instances. This - * is because the page_size kmalloc can't be done with a spin_lock held. + * 1.3.1 -> 1.3.2 Changed method for determining hvcs_struct->index and had it + * align with how the tty layer always assigns the lowest index available. This + * change resulted in a list of ints that denotes which indexes are available. + * Device additions and removals use the new hvcs_get_index() and + * hvcs_return_index() helper functions. The list is created with + * hvsc_alloc_index_list() and it is destroyed with hvcs_free_index_list(). + * Without these fixes hotplug vty-server adapter support goes crazy with this + * driver if the user removes a vty-server adapter. Moved free_irq() outside of + * the hvcs_final_close() function in order to get it out of the spinlock. + * Rearranged hvcs_close(). Cleaned up some printks and did some housekeeping + * on the changelog. Removed local CLC_LENGTH and used HVCS_CLC_LENGTH from + * arch/ppc64/hvcserver.h. * - * Also added sysfs attribute to manually disconnect the vty-server from the vty - * due to stupid firmware behavior when opening the connection then sending data - * then then quickly closing the connection would cause data loss on the - * receiving side. This required some reordering of the termination code. - * - * Fixed the hangup scenario and fixed memory leaks on module_exit. - * - * 1.2.0 -> 1.3.0 Moved from manual kernel thread creation & execution to - * kthread construct which replaced in-kernel IPC for thread termination with - * kthread_stop and kthread_should_stop. Explicit wait_queue handling was - * removed because kthread handles this. Minor bug fix to postpone partner_info - * clearing on hvcs_close until adapter removal to preserve context data for - * printk on partner connection free. Added lock to protect hvcs_structs so - * that hvcs_struct instances aren't added or removed during list traversal. - * Cleaned up comment style, added spaces after commas, and broke function - * declaration lines to be under 80 columns. + * 1.3.2 -> 1.3.3 Replaced yield() in hvcs_close() with tty_wait_until_sent() to + * prevent possible lockup with realtime scheduling as similarily pointed out by + * akpm in hvc_console. Changed resulted in the removal of hvcs_final_close() + * to reorder cleanup operations and prevent discarding of pending data during + * an hvcs_close(). Removed spinlock protection of hvcs_struct data members in + * hvcs_write_room() and hvcs_chars_in_buffer() because they aren't needed. */ -#define HVCS_DRIVER_VERSION "1.3.0" + +#define HVCS_DRIVER_VERSION "1.3.3" MODULE_AUTHOR("Ryan S. Arnold "); MODULE_DESCRIPTION("IBM hvcs (Hypervisor Virtual Console Server) Driver"); MODULE_LICENSE("GPL"); MODULE_VERSION(HVCS_DRIVER_VERSION); +/* + * Wait this long per iteration while trying to push buffered data to the + * hypervisor before allowing the tty to complete a close operation. + */ +#define HVCS_CLOSE_WAIT (HZ/100) /* 1/10 of a second */ + /* * Since the Linux TTY code does not currently (2-04-2004) support dynamic * addition of tty derived devices and we shouldn't allocate thousands of @@ -138,9 +160,9 @@ MODULE_VERSION(HVCS_DRIVER_VERSION); /* * We let Linux assign us a major number and we start the minors at zero. There - * is no intuitive mapping between minor number and the target partition. The - * mapping of minor number is related to the order the vty-servers are exposed - * to this driver via the hvcs_probe function. + * is no intuitive mapping between minor number and the target vty-server + * adapter except that each new vty-server adapter is always assigned to the + * smallest minor number available. */ #define HVCS_MINOR_START 0 @@ -152,9 +174,6 @@ MODULE_VERSION(HVCS_DRIVER_VERSION); */ #define __ALIGNED__ __attribute__((__aligned__(8))) -/* Converged location code string length + 1 null terminator */ -#define CLC_LENGTH 80 - /* * How much data can firmware send with each hvc_put_chars()? Maybe this * should be moved into an architecture specific area. @@ -194,27 +213,41 @@ char hvcs_driver_string[] = "IBM hvcs (Hypervisor Virtual Console Server) Driver"; /* Status of partner info rescan triggered via sysfs. */ -static int hvcs_rescan_status = 0; +static int hvcs_rescan_status; static struct tty_driver *hvcs_tty_driver; /* - * This is used to associate a vty-server, as it is exposed to this driver, with - * a preallocated tty_struct.index. The dev node and hvcs index numbers are not - * re-used after device removal otherwise removing and adding a new one would - * link a /dev/hvcs* entry to a different vty-server than it did before the - * removal. Incidentally, a newly exposed vty-server will always map to an - * incrementally higher /dev/hvcs* entry than the last exposed vty-server. + * In order to be somewhat sane this driver always associates the hvcs_struct + * index element with the numerically equal tty->index. This means that a + * hotplugged vty-server adapter will always map to the lowest index valued + * device node. If vty-servers were hotplug removed from the system and then + * new ones added the new vty-server may have the largest slot number of all + * the vty-server adapters in the partition but it may have the lowest dev node + * index of all the adapters due to the hole left by the hotplug removed + * adapter. There are a set of functions provided to get the lowest index for + * a new device as well as return the index to the list. This list is allocated + * with a number of elements equal to the number of device nodes requested when + * the module was inserted. + */ +static int *hvcs_index_list; + +/* + * How large is the list? This is kept for traversal since the list is + * dynamically created. */ -static int hvcs_struct_count = -1; +static int hvcs_index_count; /* * Used by the khvcsd to pick up I/O operations when the kernel_thread is * already awake but potentially shifted to TASK_INTERRUPTIBLE state. */ -static int hvcs_kicked = 0; +static int hvcs_kicked; -/* Used the the kthread construct for task operations */ +/* + * Use by the kthread construct for task operations like waking the sleeping + * thread and stopping the kthread. + */ static struct task_struct *hvcs_task; /* @@ -223,7 +256,8 @@ static struct task_struct *hvcs_task; */ static unsigned long *hvcs_pi_buff; -static spinlock_t hvcs_pi_lock; +/* Only allow one hvcs_struct to use the hvcs_pi_buff at a time. */ +static spinlock_t hvcs_pi_lock = SPIN_LOCK_UNLOCKED; /* One vty-server per hvcs_struct */ struct hvcs_struct { @@ -263,9 +297,9 @@ struct hvcs_struct { */ struct kobject kobj; /* ref count & hvcs_struct lifetime */ int connected; /* is the vty-server currently connected to a vty? */ - unsigned int p_unit_address; /* partner unit address */ - unsigned int p_partition_ID; /* partner partition ID */ - char p_location_code[CLC_LENGTH]; + uint32_t p_unit_address; /* partner unit address */ + uint32_t p_partition_ID; /* partner partition ID */ + char p_location_code[HVCS_CLC_LENGTH + 1]; /* CLC + Null Term */ struct list_head next; /* list management */ struct vio_dev *vdev; }; @@ -274,7 +308,7 @@ struct hvcs_struct { #define from_kobj(kobj) container_of(kobj, struct hvcs_struct, kobj) static struct list_head hvcs_structs = LIST_HEAD_INIT(hvcs_structs); -static spinlock_t hvcs_structs_lock; +static spinlock_t hvcs_structs_lock = SPIN_LOCK_UNLOCKED; static void hvcs_unthrottle(struct tty_struct *tty); static void hvcs_throttle(struct tty_struct *tty); @@ -297,7 +331,6 @@ static void hvcs_partner_free(struct hvcs_struct *hvcsd); static int hvcs_enable_device(struct hvcs_struct *hvcsd, uint32_t unit_address, unsigned int irq, struct vio_dev *dev); -static void hvcs_final_close(struct hvcs_struct *hvcsd); static void destroy_hvcs_struct(struct kobject *kobj); static int hvcs_open(struct tty_struct *tty, struct file *filp); @@ -357,12 +390,11 @@ static irqreturn_t hvcs_handle_interrupt(int irq, void *dev_instance, struct pt_regs *regs) { struct hvcs_struct *hvcsd = dev_instance; - unsigned long flags; - spin_lock_irqsave(&hvcsd->lock, flags); + spin_lock(&hvcsd->lock); vio_disable_interrupts(hvcsd->vdev); hvcsd->todo_mask |= HVCS_SCHED_READ; - spin_unlock_irqrestore(&hvcsd->lock, flags); + spin_unlock(&hvcsd->lock); hvcs_kick(); return IRQ_HANDLED; @@ -371,7 +403,7 @@ static irqreturn_t hvcs_handle_interrupt(int irq, void *dev_instance, /* This function must be called with the hvcsd->lock held */ static void hvcs_try_write(struct hvcs_struct *hvcsd) { - unsigned int unit_address = hvcsd->vdev->unit_address; + uint32_t unit_address = hvcsd->vdev->unit_address; struct tty_struct *tty = hvcsd->tty; int sent; @@ -382,9 +414,9 @@ static void hvcs_try_write(struct hvcs_struct *hvcsd) hvcsd->chars_in_buffer ); if (sent > 0) { hvcsd->chars_in_buffer = 0; - wmb(); + /* wmb(); */ hvcsd->todo_mask &= ~(HVCS_TRY_WRITE); - wmb(); + /* wmb(); */ /* * We are still obligated to deliver the data to the @@ -393,10 +425,7 @@ static void hvcs_try_write(struct hvcs_struct *hvcsd) * a non-existent tty. */ if (tty) { - if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) - && tty->ldisc.write_wakeup) - (tty->ldisc.write_wakeup) (tty); - wake_up_interruptible(&tty->write_wait); + tty_wakeup(tty); } } } @@ -404,11 +433,11 @@ static void hvcs_try_write(struct hvcs_struct *hvcsd) static int hvcs_io(struct hvcs_struct *hvcsd) { - unsigned int unit_address; + uint32_t unit_address; struct tty_struct *tty; char buf[HVCS_BUFF_LEN] __ALIGNED__; unsigned long flags; - int got; + int got = 0; int i; spin_lock_irqsave(&hvcsd->lock, flags); @@ -461,11 +490,8 @@ static int hvcs_io(struct hvcs_struct *hvcsd) static int khvcsd(void *unused) { - struct hvcs_struct *hvcsd = NULL; - struct list_head *element; - struct list_head *safe_temp; + struct hvcs_struct *hvcsd; int hvcs_todo_mask; - unsigned long structs_flags; __set_current_state(TASK_RUNNING); @@ -474,12 +500,11 @@ static int khvcsd(void *unused) hvcs_kicked = 0; wmb(); - spin_lock_irqsave(&hvcs_structs_lock, structs_flags); - list_for_each_safe(element, safe_temp, &hvcs_structs) { - hvcsd = list_entry(element, struct hvcs_struct, next); - hvcs_todo_mask |= hvcs_io(hvcsd); + spin_lock(&hvcs_structs_lock); + list_for_each_entry(hvcsd, &hvcs_structs, next) { + hvcs_todo_mask |= hvcs_io(hvcsd); } - spin_unlock_irqrestore(&hvcs_structs_lock, structs_flags); + spin_unlock(&hvcs_structs_lock); /* * If any of the hvcs adapters want to try a write or quick read @@ -506,6 +531,19 @@ static struct vio_device_id hvcs_driver_table[] __devinitdata= { }; MODULE_DEVICE_TABLE(vio, hvcs_driver_table); +static void hvcs_return_index(int index) +{ + /* Paranoia check */ + if (!hvcs_index_list) + return; + if (index < 0 || index >= hvcs_index_count) + return; + if (hvcs_index_list[index] == -1) + return; + else + hvcs_index_list[index] = -1; +} + /* callback when the kboject ref count reaches zero */ static void destroy_hvcs_struct(struct kobject *kobj) { @@ -513,6 +551,7 @@ static void destroy_hvcs_struct(struct kobject *kobj) struct vio_dev *vdev; unsigned long flags; + spin_lock(&hvcs_structs_lock); spin_lock_irqsave(&hvcsd->lock, flags); /* the list_del poisons the pointers */ @@ -524,7 +563,7 @@ static void destroy_hvcs_struct(struct kobject *kobj) " partner vty@%X:%d connection.\n", hvcsd->vdev->unit_address, hvcsd->p_unit_address, - (unsigned int)hvcsd->p_partition_ID); + (uint32_t)hvcsd->p_partition_ID); } printk(KERN_INFO "HVCS: Destroyed hvcs_struct for vty-server@%X.\n", hvcsd->vdev->unit_address); @@ -534,55 +573,60 @@ static void destroy_hvcs_struct(struct kobject *kobj) hvcsd->p_unit_address = 0; hvcsd->p_partition_ID = 0; - memset(&hvcsd->p_location_code[0], 0x00, CLC_LENGTH); + hvcs_return_index(hvcsd->index); + memset(&hvcsd->p_location_code[0], 0x00, HVCS_CLC_LENGTH + 1); spin_unlock_irqrestore(&hvcsd->lock, flags); + spin_unlock(&hvcs_structs_lock); hvcs_remove_device_attrs(vdev); kfree(hvcsd); } -/* This function must be called with hvcsd->lock held. */ -static void hvcs_final_close(struct hvcs_struct *hvcsd) -{ - vio_disable_interrupts(hvcsd->vdev); - free_irq(hvcsd->vdev->irq, hvcsd); - - hvcsd->todo_mask = 0; - - /* These two may be redundant if the operation was a close. */ - if (hvcsd->tty) { - hvcsd->tty->driver_data = NULL; - hvcsd->tty = NULL; - } - - hvcsd->open_count = 0; - - memset(&hvcsd->buffer[0], 0x00, HVCS_BUFF_LEN); - hvcsd->chars_in_buffer = 0; -} - static struct kobj_type hvcs_kobj_type = { .release = destroy_hvcs_struct, }; +static int hvcs_get_index(void) +{ + int i; + /* Paranoia check */ + if (!hvcs_index_list) { + printk(KERN_ERR "HVCS: hvcs_index_list NOT valid!.\n"); + return -EFAULT; + } + /* Find the numerically lowest first free index. */ + for(i = 0; i < hvcs_index_count; i++) { + if (hvcs_index_list[i] == -1) { + hvcs_index_list[i] = 0; + return i; + } + } + return -1; +} + static int __devinit hvcs_probe( struct vio_dev *dev, const struct vio_device_id *id) { struct hvcs_struct *hvcsd; - unsigned long structs_flags; + int index; if (!dev || !id) { printk(KERN_ERR "HVCS: probed with invalid parameter.\n"); return -EPERM; } + /* early to avoid cleanup on failure */ + index = hvcs_get_index(); + if (index < 0) { + return -EFAULT; + } + hvcsd = kmalloc(sizeof(*hvcsd), GFP_KERNEL); - if (!hvcsd) { + if (!hvcsd) return -ENODEV; - } /* hvcsd->tty is zeroed out with the memset */ memset(hvcsd, 0x00, sizeof(*hvcsd)); @@ -596,7 +640,9 @@ static int __devinit hvcs_probe( hvcsd->vdev = dev; dev->dev.driver_data = hvcsd; - hvcsd->index = ++hvcs_struct_count; + hvcsd->index = index; + + /* hvcsd->index = ++hvcs_struct_count; */ hvcsd->chars_in_buffer = 0; hvcsd->todo_mask = 0; hvcsd->connected = 0; @@ -617,15 +663,15 @@ static int __devinit hvcs_probe( * will get -ENODEV. */ - spin_lock_irqsave(&hvcs_structs_lock, structs_flags); + spin_lock(&hvcs_structs_lock); list_add_tail(&(hvcsd->next), &hvcs_structs); - spin_unlock_irqrestore(&hvcs_structs_lock, structs_flags); + spin_unlock(&hvcs_structs_lock); hvcs_create_device_attrs(hvcsd); - printk(KERN_INFO "HVCS: Added vty-server@%X.\n", dev->unit_address); + printk(KERN_INFO "HVCS: vty-server@%X added to the vio bus.\n", dev->unit_address); /* * DON'T enable interrupts here because there is no user to receive the @@ -688,8 +734,8 @@ static void hvcs_set_pi(struct hvcs_partner_info *pi, struct hvcs_struct *hvcsd) hvcsd->p_unit_address = pi->unit_address; hvcsd->p_partition_ID = pi->partition_ID; clclength = strlen(&pi->location_code[0]); - if (clclength > CLC_LENGTH - 1) - clclength = CLC_LENGTH - 1; + if (clclength > HVCS_CLC_LENGTH) + clclength = HVCS_CLC_LENGTH; /* copy the null-term char too */ strncpy(&hvcsd->p_location_code[0], @@ -711,20 +757,18 @@ static void hvcs_set_pi(struct hvcs_partner_info *pi, struct hvcs_struct *hvcsd) */ static int hvcs_get_pi(struct hvcs_struct *hvcsd) { - /* struct hvcs_partner_info *head_pi = NULL; */ - struct hvcs_partner_info *pi = NULL; - unsigned int unit_address = hvcsd->vdev->unit_address; + struct hvcs_partner_info *pi; + uint32_t unit_address = hvcsd->vdev->unit_address; struct list_head head; - unsigned long flags; int retval; - spin_lock_irqsave(&hvcs_pi_lock, flags); + spin_lock(&hvcs_pi_lock); if (!hvcs_pi_buff) { - spin_unlock_irqrestore(&hvcs_pi_lock, flags); + spin_unlock(&hvcs_pi_lock); return -EFAULT; } retval = hvcs_get_partner_info(unit_address, &head, hvcs_pi_buff); - spin_unlock_irqrestore(&hvcs_pi_lock, flags); + spin_unlock(&hvcs_pi_lock); if (retval) { printk(KERN_ERR "HVCS: Failed to fetch partner" " info for vty-server@%x.\n", unit_address); @@ -748,11 +792,10 @@ static int hvcs_get_pi(struct hvcs_struct *hvcsd) */ static int hvcs_rescan_devices_list(void) { - struct hvcs_struct *hvcsd = NULL; + struct hvcs_struct *hvcsd; unsigned long flags; - unsigned long structs_flags; - spin_lock_irqsave(&hvcs_structs_lock, structs_flags); + spin_lock(&hvcs_structs_lock); list_for_each_entry(hvcsd, &hvcs_structs, next) { spin_lock_irqsave(&hvcsd->lock, flags); @@ -760,7 +803,7 @@ static int hvcs_rescan_devices_list(void) spin_unlock_irqrestore(&hvcsd->lock, flags); } - spin_unlock_irqrestore(&hvcs_structs_lock, structs_flags); + spin_unlock(&hvcs_structs_lock); return 0; } @@ -848,13 +891,14 @@ static int hvcs_enable_device(struct hvcs_struct *hvcsd, uint32_t unit_address, unsigned int irq, struct vio_dev *vdev) { unsigned long flags; + int rc; /* * It is possible that the vty-server was removed between the time that * the conn was registered and now. */ - if (!request_irq(irq, &hvcs_handle_interrupt, - SA_INTERRUPT, "ibmhvcs", hvcsd)) { + if (!(rc = request_irq(irq, &hvcs_handle_interrupt, + SA_INTERRUPT, "ibmhvcs", hvcsd))) { /* * It is possible the vty-server was removed after the irq was * requested but before we have time to enable interrupts. @@ -874,7 +918,7 @@ static int hvcs_enable_device(struct hvcs_struct *hvcsd, uint32_t unit_address, hvcs_partner_free(hvcsd); spin_unlock_irqrestore(&hvcsd->lock, flags); - return -ENODEV; + return rc; } @@ -888,22 +932,17 @@ static int hvcs_enable_device(struct hvcs_struct *hvcsd, uint32_t unit_address, struct hvcs_struct *hvcs_get_by_index(int index) { struct hvcs_struct *hvcsd = NULL; - struct list_head *element; - struct list_head *safe_temp; unsigned long flags; - unsigned long structs_flags; - spin_lock_irqsave(&hvcs_structs_lock, structs_flags); + spin_lock(&hvcs_structs_lock); /* We can immediately discard OOB requests */ if (index >= 0 && index < HVCS_MAX_SERVER_ADAPTERS) { - list_for_each_safe(element, safe_temp, &hvcs_structs) { - hvcsd = list_entry(element, struct hvcs_struct, next); + list_for_each_entry(hvcsd, &hvcs_structs, next) { spin_lock_irqsave(&hvcsd->lock, flags); if (hvcsd->index == index) { kobject_get(&hvcsd->kobj); spin_unlock_irqrestore(&hvcsd->lock, flags); - spin_unlock_irqrestore(&hvcs_structs_lock, - structs_flags); + spin_unlock(&hvcs_structs_lock); return hvcsd; } spin_unlock_irqrestore(&hvcsd->lock, flags); @@ -911,7 +950,7 @@ struct hvcs_struct *hvcs_get_by_index(int index) hvcsd = NULL; } - spin_unlock_irqrestore(&hvcs_structs_lock, structs_flags); + spin_unlock(&hvcs_structs_lock); return hvcsd; } @@ -921,12 +960,13 @@ struct hvcs_struct *hvcs_get_by_index(int index) */ static int hvcs_open(struct tty_struct *tty, struct file *filp) { - struct hvcs_struct *hvcsd = NULL; - int retval = 0; + struct hvcs_struct *hvcsd; + int rc, retval = 0; unsigned long flags; unsigned int irq; struct vio_dev *vdev; unsigned long unit_address; + struct kobject *kobjp; if (tty->driver_data) goto fast_open; @@ -936,7 +976,8 @@ static int hvcs_open(struct tty_struct *tty, struct file *filp) * This function increments the kobject index. */ if (!(hvcsd = hvcs_get_by_index(tty->index))) { - printk(KERN_WARNING "HVCS: open failed, no index.\n"); + printk(KERN_WARNING "HVCS: open failed, no device associated" + " with tty->index %d.\n", tty->index); return -ENODEV; } @@ -959,7 +1000,7 @@ static int hvcs_open(struct tty_struct *tty, struct file *filp) */ tty->low_latency = 1; - memset(&hvcsd->buffer[0], 0x3F, HVCS_BUFF_LEN); + memset(&hvcsd->buffer[0], 0x00, HVCS_BUFF_LEN); /* * Save these in the spinlock for the enable operations that need them @@ -974,12 +1015,12 @@ static int hvcs_open(struct tty_struct *tty, struct file *filp) /* * This must be done outside of the spinlock because it requests irqs - * and will grab the spinlcok and free the connection if it fails. + * and will grab the spinlock and free the connection if it fails. */ - if ((hvcs_enable_device(hvcsd, unit_address, irq, vdev))) { + if (((rc = hvcs_enable_device(hvcsd, unit_address, irq, vdev)))) { kobject_put(&hvcsd->kobj); printk(KERN_WARNING "HVCS: enable device failed.\n"); - return -ENODEV; + return rc; } goto open_success; @@ -1002,16 +1043,17 @@ fast_open: open_success: hvcs_kick(); - printk(KERN_INFO "HVCS: vty-server@%X opened.\n", + printk(KERN_INFO "HVCS: vty-server@%X connection opened.\n", hvcsd->vdev->unit_address ); return 0; error_release: + kobjp = &hvcsd->kobj; spin_unlock_irqrestore(&hvcsd->lock, flags); kobject_put(&hvcsd->kobj); - printk(KERN_WARNING "HVCS: HVCS partner connect failed.\n"); + printk(KERN_WARNING "HVCS: partner connect failed.\n"); return retval; } @@ -1020,6 +1062,7 @@ static void hvcs_close(struct tty_struct *tty, struct file *filp) struct hvcs_struct *hvcsd; unsigned long flags; struct kobject *kobjp; + int irq = NO_IRQ; /* * Is someone trying to close the file associated with this device after @@ -1039,14 +1082,10 @@ static void hvcs_close(struct tty_struct *tty, struct file *filp) hvcsd = tty->driver_data; spin_lock_irqsave(&hvcsd->lock, flags); + kobjp = &hvcsd->kobj; if (--hvcsd->open_count == 0) { - /* - * This line is important because it tells hvcs_open that this - * device needs to be re-configured the next time hvcs_open is - * called. - */ - hvcsd->tty->driver_data = NULL; + vio_disable_interrupts(hvcsd->vdev); /* * NULL this early so that the kernel_thread doesn't try to @@ -1055,34 +1094,28 @@ static void hvcs_close(struct tty_struct *tty, struct file *filp) */ hvcsd->tty = NULL; - /* - * Block the close until all the buffered data has been - * delivered. - */ - while(hvcsd->chars_in_buffer) { - spin_unlock_irqrestore(&hvcsd->lock, flags); + irq = hvcsd->vdev->irq; + spin_unlock_irqrestore(&hvcsd->lock, flags); - /* - * Give the kernel thread the hvcs_struct so that it can - * try to deliver the remaining data but block the close - * operation by spinning in this function so that other - * tty operations have to wait. - */ - yield(); - spin_lock_irqsave(&hvcsd->lock, flags); - } + tty_wait_until_sent(tty, HVCS_CLOSE_WAIT); - hvcs_final_close(hvcsd); + /* + * This line is important because it tells hvcs_open that this + * device needs to be re-configured the next time hvcs_open is + * called. + */ + tty->driver_data = NULL; + free_irq(irq, hvcsd); + kobject_put(kobjp); + return; } else if (hvcsd->open_count < 0) { printk(KERN_ERR "HVCS: vty-server@%X open_count: %d" " is missmanaged.\n", - hvcsd->vdev->unit_address, hvcsd->open_count); + hvcsd->vdev->unit_address, hvcsd->open_count); } - kobjp = &hvcsd->kobj; spin_unlock_irqrestore(&hvcsd->lock, flags); - kobject_put(kobjp); } @@ -1092,6 +1125,7 @@ static void hvcs_hangup(struct tty_struct * tty) unsigned long flags; int temp_open_count; struct kobject *kobjp; + int irq = NO_IRQ; spin_lock_irqsave(&hvcsd->lock, flags); /* Preserve this so that we know how many kobject refs to put */ @@ -1101,15 +1135,31 @@ static void hvcs_hangup(struct tty_struct * tty) * Don't kobject put inside the spinlock because the destruction * callback may use the spinlock and it may get called before the * spinlock has been released. Get a pointer to the kobject and - * kobject_put on that instead. + * kobject_put on that after releasing the spinlock. */ kobjp = &hvcsd->kobj; - /* Calling this will drop any buffered data on the floor. */ - hvcs_final_close(hvcsd); + vio_disable_interrupts(hvcsd->vdev); + + hvcsd->todo_mask = 0; + + /* I don't think the tty needs the hvcs_struct pointer after a hangup */ + hvcsd->tty->driver_data = NULL; + hvcsd->tty = NULL; + + hvcsd->open_count = 0; + + /* This will drop any buffered data on the floor which is OK in a hangup + * scenario. */ + memset(&hvcsd->buffer[0], 0x00, HVCS_BUFF_LEN); + hvcsd->chars_in_buffer = 0; + + irq = hvcsd->vdev->irq; spin_unlock_irqrestore(&hvcsd->lock, flags); + free_irq(irq, hvcsd); + /* * We need to kobject_put() for every open_count we have since the * tty_hangup() function doesn't invoke a close per open connection on a @@ -1259,28 +1309,18 @@ static int hvcs_write(struct tty_struct *tty, int from_user, static int hvcs_write_room(struct tty_struct *tty) { struct hvcs_struct *hvcsd = tty->driver_data; - unsigned long flags; - int retval; if (!hvcsd || hvcsd->open_count <= 0) return 0; - spin_lock_irqsave(&hvcsd->lock, flags); - retval = HVCS_BUFF_LEN - hvcsd->chars_in_buffer; - spin_unlock_irqrestore(&hvcsd->lock, flags); - return retval; + return HVCS_BUFF_LEN - hvcsd->chars_in_buffer; } static int hvcs_chars_in_buffer(struct tty_struct *tty) { struct hvcs_struct *hvcsd = tty->driver_data; - unsigned long flags; - int retval; - spin_lock_irqsave(&hvcsd->lock, flags); - retval = hvcsd->chars_in_buffer; - spin_unlock_irqrestore(&hvcsd->lock, flags); - return retval; + return hvcsd->chars_in_buffer; } static struct tty_operations hvcs_ops = { @@ -1294,6 +1334,28 @@ static struct tty_operations hvcs_ops = { .throttle = hvcs_throttle, }; +static int hvcs_alloc_index_list(int n) +{ + int i; + hvcs_index_list = kmalloc(n * sizeof(hvcs_index_count),GFP_KERNEL); + if (!hvcs_index_list) + return -ENOMEM; + hvcs_index_count = n; + for(i = 0; i < hvcs_index_count; i++) + hvcs_index_list[i] = -1; + return 0; +} + +static void hvcs_free_index_list(void) +{ + /* Paranoia check to be thorough. */ + if (hvcs_index_list) { + kfree(hvcs_index_list); + hvcs_index_list = NULL; + hvcs_index_count = 0; + } +} + static int __init hvcs_module_init(void) { int rc; @@ -1312,6 +1374,9 @@ static int __init hvcs_module_init(void) if (!hvcs_tty_driver) return -ENOMEM; + if (hvcs_alloc_index_list(num_ttys_to_alloc)) + return -ENOMEM; + hvcs_tty_driver->owner = THIS_MODULE; hvcs_tty_driver->driver_name = hvcs_driver_name; @@ -1342,19 +1407,25 @@ static int __init hvcs_module_init(void) if (tty_register_driver(hvcs_tty_driver)) { printk(KERN_ERR "HVCS: registration " " as a tty driver failed.\n"); + hvcs_free_index_list(); put_tty_driver(hvcs_tty_driver); - return rc; + return -EIO; } - hvcs_structs_lock = SPIN_LOCK_UNLOCKED; - - hvcs_pi_lock = SPIN_LOCK_UNLOCKED; hvcs_pi_buff = kmalloc(PAGE_SIZE, GFP_KERNEL); + if (!hvcs_pi_buff) { + tty_unregister_driver(hvcs_tty_driver); + hvcs_free_index_list(); + put_tty_driver(hvcs_tty_driver); + return -ENOMEM; + } hvcs_task = kthread_run(khvcsd, NULL, "khvcsd"); if (IS_ERR(hvcs_task)) { - printk("khvcsd creation failed. Driver not loaded.\n"); + printk(KERN_ERR "HVCS: khvcsd creation failed. Driver not loaded.\n"); kfree(hvcs_pi_buff); + tty_unregister_driver(hvcs_tty_driver); + hvcs_free_index_list(); put_tty_driver(hvcs_tty_driver); return -EIO; } @@ -1374,8 +1445,6 @@ static int __init hvcs_module_init(void) static void __exit hvcs_module_exit(void) { - unsigned long flags; - /* * This driver receives hvcs_remove callbacks for each device upon * module removal. @@ -1387,10 +1456,10 @@ static void __exit hvcs_module_exit(void) */ kthread_stop(hvcs_task); - spin_lock_irqsave(&hvcs_pi_lock, flags); + spin_lock(&hvcs_pi_lock); kfree(hvcs_pi_buff); hvcs_pi_buff = NULL; - spin_unlock_irqrestore(&hvcs_pi_lock, flags); + spin_unlock(&hvcs_pi_lock); hvcs_remove_driver_attrs(); @@ -1398,6 +1467,8 @@ static void __exit hvcs_module_exit(void) tty_unregister_driver(hvcs_tty_driver); + hvcs_free_index_list(); + put_tty_driver(hvcs_tty_driver); printk(KERN_INFO "HVCS: driver module removed.\n"); @@ -1499,7 +1570,7 @@ static ssize_t hvcs_vterm_state_store(struct device *dev, const char *buf, " partner vty@%X:%d connection.\n", hvcsd->vdev->unit_address, hvcsd->p_unit_address, - (unsigned int)hvcsd->p_partition_ID); + (uint32_t)hvcsd->p_partition_ID); spin_unlock_irqrestore(&hvcsd->lock, flags); return count; @@ -1520,11 +1591,27 @@ static ssize_t hvcs_vterm_state_show(struct device *dev, char *buf) static DEVICE_ATTR(vterm_state, S_IRUGO | S_IWUSR, hvcs_vterm_state_show, hvcs_vterm_state_store); +static ssize_t hvcs_index_show(struct device *dev, char *buf) +{ + struct vio_dev *viod = to_vio_dev(dev); + struct hvcs_struct *hvcsd = from_vio_dev(viod); + unsigned long flags; + int retval; + + spin_lock_irqsave(&hvcsd->lock, flags); + retval = sprintf(buf, "%d\n", hvcsd->index); + spin_unlock_irqrestore(&hvcsd->lock, flags); + return retval; +} + +static DEVICE_ATTR(index, S_IRUGO, hvcs_index_show, NULL); + static struct attribute *hvcs_attrs[] = { &dev_attr_partner_vtys.attr, &dev_attr_partner_clcs.attr, &dev_attr_current_vty.attr, &dev_attr_vterm_state.attr, + &dev_attr_index.attr, NULL, }; diff --git a/drivers/char/hvsi.c b/drivers/char/hvsi.c new file mode 100644 index 000000000..595079c07 --- /dev/null +++ b/drivers/char/hvsi.c @@ -0,0 +1,1319 @@ +/* + * Copyright (C) 2004 Hollis Blanchard , IBM + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the 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 + */ + +/* Host Virtual Serial Interface (HVSI) is a protocol between the hosted OS + * and the service processor on IBM pSeries servers. On these servers, there + * are no serial ports under the OS's control, and sometimes there is no other + * console available either. However, the service processor has two standard + * serial ports, so this over-complicated protocol allows the OS to control + * those ports by proxy. + * + * Besides data, the procotol supports the reading/writing of the serial + * port's DTR line, and the reading of the CD line. This is to allow the OS to + * control a modem attached to the service processor's serial port. Note that + * the OS cannot change the speed of the port through this protocol. + */ + +/* TODO: + * test FSP reset + * add udbg support for xmon/kdb + */ + +#undef DEBUG + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define HVSI_MAJOR 229 +#define HVSI_MINOR 128 +#define MAX_NR_HVSI_CONSOLES 4 + +#define HVSI_TIMEOUT (5*HZ) +#define HVSI_VERSION 1 +#define HVSI_MAX_PACKET 256 +#define HVSI_MAX_READ 16 +#define HVSI_MAX_OUTGOING_DATA 12 +#define N_OUTBUF 12 + +/* + * we pass data via two 8-byte registers, so we would like our char arrays + * properly aligned for those loads. + */ +#define __ALIGNED__ __attribute__((__aligned__(sizeof(long)))) + +struct hvsi_struct { + struct work_struct writer; + wait_queue_head_t emptyq; /* woken when outbuf is emptied */ + wait_queue_head_t stateq; /* woken when HVSI state changes */ + spinlock_t lock; + int index; + struct tty_struct *tty; + unsigned 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. */ + uint8_t inbuf[HVSI_MAX_PACKET + HVSI_MAX_READ]; + uint8_t *inbuf_end; + int n_throttle; + int n_outbuf; + uint32_t vtermno; + uint32_t virq; + atomic_t seqno; /* HVSI packet sequence number */ + uint16_t mctrl; + uint8_t state; /* HVSI protocol state */ + uint8_t flags; +#ifdef CONFIG_MAGIC_SYSRQ + uint8_t sysrq; +#endif /* CONFIG_MAGIC_SYSRQ */ +}; +static struct hvsi_struct hvsi_ports[MAX_NR_HVSI_CONSOLES]; + +static struct tty_driver *hvsi_driver; +static int hvsi_count; +static int (*hvsi_wait)(struct hvsi_struct *hp, int state); + +enum HVSI_PROTOCOL_STATE { + HVSI_CLOSED, + HVSI_WAIT_FOR_VER_RESPONSE, + HVSI_WAIT_FOR_VER_QUERY, + HVSI_OPEN, + HVSI_WAIT_FOR_MCTRL_RESPONSE, +}; +#define HVSI_CONSOLE 0x1 + +#define VS_DATA_PACKET_HEADER 0xff +#define VS_CONTROL_PACKET_HEADER 0xfe +#define VS_QUERY_PACKET_HEADER 0xfd +#define VS_QUERY_RESPONSE_PACKET_HEADER 0xfc + +/* control verbs */ +#define VSV_SET_MODEM_CTL 1 /* to service processor only */ +#define VSV_MODEM_CTL_UPDATE 2 /* from service processor only */ +#define VSV_CLOSE_PROTOCOL 3 + +/* query verbs */ +#define VSV_SEND_VERSION_NUMBER 1 +#define VSV_SEND_MODEM_CTL_STATUS 2 + +/* yes, these masks are not consecutive. */ +#define HVSI_TSDTR 0x01 +#define HVSI_TSCD 0x20 + +struct hvsi_header { + uint8_t type; + uint8_t len; + uint16_t seqno; +} __attribute__((packed)); + +struct hvsi_data { + uint8_t type; + uint8_t len; + uint16_t seqno; + uint8_t data[HVSI_MAX_OUTGOING_DATA]; +} __attribute__((packed)); + +struct hvsi_control { + uint8_t type; + uint8_t len; + uint16_t seqno; + uint16_t verb; + /* optional depending on verb: */ + uint32_t word; + uint32_t mask; +} __attribute__((packed)); + +struct hvsi_query { + uint8_t type; + uint8_t len; + uint16_t seqno; + uint16_t verb; +} __attribute__((packed)); + +struct hvsi_query_response { + uint8_t type; + uint8_t len; + uint16_t seqno; + uint16_t verb; + uint16_t query_seqno; + union { + uint8_t version; + uint32_t mctrl_word; + } u; +} __attribute__((packed)); + +static inline int is_open(struct hvsi_struct *hp) +{ + /* if we're waiting for an mctrl then we're already open */ + return (hp->state == HVSI_OPEN) + || (hp->state == HVSI_WAIT_FOR_MCTRL_RESPONSE); +} + +static inline void print_state(struct hvsi_struct *hp) +{ +#ifdef DEBUG + static const char *state_names[] = { + "HVSI_CLOSED", + "HVSI_WAIT_FOR_VER_RESPONSE", + "HVSI_WAIT_FOR_VER_QUERY", + "HVSI_OPEN", + "HVSI_WAIT_FOR_MCTRL_RESPONSE", + }; + const char *name = state_names[hp->state]; + + if (hp->state > (sizeof(state_names)/sizeof(char*))) + name = "UNKNOWN"; + + pr_debug("hvsi%i: state = %s\n", hp->index, name); +#endif /* DEBUG */ +} + +static inline void __set_state(struct hvsi_struct *hp, int state) +{ + hp->state = state; + print_state(hp); + wake_up_all(&hp->stateq); +} + +static inline void set_state(struct hvsi_struct *hp, int state) +{ + unsigned long flags; + + spin_lock_irqsave(&hp->lock, flags); + __set_state(hp, state); + spin_unlock_irqrestore(&hp->lock, flags); +} + +static inline int len_packet(const uint8_t *packet) +{ + return (int)((struct hvsi_header *)packet)->len; +} + +static inline int is_header(const uint8_t *packet) +{ + struct hvsi_header *header = (struct hvsi_header *)packet; + return header->type >= VS_QUERY_RESPONSE_PACKET_HEADER; +} + +static inline int got_packet(const struct hvsi_struct *hp, uint8_t *packet) +{ + if (hp->inbuf_end < packet + sizeof(struct hvsi_header)) + return 0; /* don't even have the packet header */ + + if (hp->inbuf_end < (packet + len_packet(packet))) + return 0; /* don't have the rest of the packet */ + + return 1; +} + +/* shift remaining bytes in packetbuf down */ +static void compact_inbuf(struct hvsi_struct *hp, uint8_t *read_to) +{ + int remaining = (int)(hp->inbuf_end - read_to); + + pr_debug("%s: %i chars remain\n", __FUNCTION__, remaining); + + if (read_to != hp->inbuf) + memmove(hp->inbuf, read_to, remaining); + + hp->inbuf_end = hp->inbuf + remaining; +} + +#ifdef DEBUG +#define dbg_dump_packet(packet) dump_packet(packet) +#define dbg_dump_hex(data, len) dump_hex(data, len) +#else +#define dbg_dump_packet(packet) do { } while (0) +#define dbg_dump_hex(data, len) do { } while (0) +#endif + +static void dump_hex(const uint8_t *data, int len) +{ + int i; + + printk(" "); + for (i=0; i < len; i++) + printk("%.2x", data[i]); + + printk("\n "); + for (i=0; i < len; i++) { + if (isprint(data[i])) + printk("%c", data[i]); + else + printk("."); + } + printk("\n"); +} + +static void dump_packet(uint8_t *packet) +{ + struct hvsi_header *header = (struct hvsi_header *)packet; + + printk("type 0x%x, len %i, seqno %i:\n", header->type, header->len, + header->seqno); + + dump_hex(packet, header->len); +} + +/* can't use hvc_get_chars because that strips CRs */ +static int hvsi_read(struct hvsi_struct *hp, char *buf, int count) +{ + unsigned long got; + + if (plpar_hcall(H_GET_TERM_CHAR, hp->vtermno, 0, 0, 0, &got, + (unsigned long *)buf, (unsigned long *)buf+1) == H_Success) + return got; + return 0; +} + +/* + * we can't call tty_hangup() directly here because we need to call that + * outside of our lock + */ +static struct tty_struct *hvsi_recv_control(struct hvsi_struct *hp, + uint8_t *packet) +{ + struct tty_struct *to_hangup = NULL; + struct hvsi_control *header = (struct hvsi_control *)packet; + + switch (header->verb) { + case VSV_MODEM_CTL_UPDATE: + if ((header->word & HVSI_TSCD) == 0) { + /* CD went away; no more connection */ + pr_debug("hvsi%i: CD dropped\n", hp->index); + hp->mctrl &= TIOCM_CD; + if (!(hp->tty->flags & CLOCAL)) + to_hangup = hp->tty; + } + break; + case VSV_CLOSE_PROTOCOL: + printk(KERN_DEBUG + "hvsi%i: service processor closed connection!\n", hp->index); + __set_state(hp, HVSI_CLOSED); + to_hangup = hp->tty; + hp->tty = NULL; + break; + default: + printk(KERN_WARNING "hvsi%i: unknown HVSI control packet: ", + hp->index); + dump_packet(packet); + break; + } + + return to_hangup; +} + +static void hvsi_recv_response(struct hvsi_struct *hp, uint8_t *packet) +{ + struct hvsi_query_response *resp = (struct hvsi_query_response *)packet; + + switch (hp->state) { + case HVSI_WAIT_FOR_VER_RESPONSE: + __set_state(hp, HVSI_WAIT_FOR_VER_QUERY); + break; + case HVSI_WAIT_FOR_MCTRL_RESPONSE: + hp->mctrl = 0; + if (resp->u.mctrl_word & HVSI_TSDTR) + hp->mctrl |= TIOCM_DTR; + if (resp->u.mctrl_word & HVSI_TSCD) + hp->mctrl |= TIOCM_CD; + __set_state(hp, HVSI_OPEN); + break; + default: + printk(KERN_ERR "hvsi%i: unexpected query response: ", hp->index); + dump_packet(packet); + break; + } +} + +/* respond to service processor's version query */ +static int hvsi_version_respond(struct hvsi_struct *hp, uint16_t query_seqno) +{ + struct hvsi_query_response packet __ALIGNED__; + int wrote; + + packet.type = VS_QUERY_RESPONSE_PACKET_HEADER; + packet.len = sizeof(struct hvsi_query_response); + packet.seqno = atomic_inc_return(&hp->seqno); + packet.verb = VSV_SEND_VERSION_NUMBER; + packet.u.version = HVSI_VERSION; + packet.query_seqno = query_seqno+1; + + pr_debug("%s: sending %i bytes\n", __FUNCTION__, packet.len); + dbg_dump_hex((uint8_t*)&packet, packet.len); + + wrote = hvc_put_chars(hp->vtermno, (char *)&packet, packet.len); + if (wrote != packet.len) { + printk(KERN_ERR "hvsi%i: couldn't send query response!\n", + hp->index); + return -EIO; + } + + return 0; +} + +static void hvsi_recv_query(struct hvsi_struct *hp, uint8_t *packet) +{ + struct hvsi_query *query = (struct hvsi_query *)packet; + + switch (hp->state) { + case HVSI_WAIT_FOR_VER_QUERY: + __set_state(hp, HVSI_OPEN); + hvsi_version_respond(hp, query->seqno); + break; + default: + printk(KERN_ERR "hvsi%i: unexpected query: ", hp->index); + dump_packet(packet); + break; + } +} + +static void hvsi_insert_chars(struct hvsi_struct *hp, const char *buf, int len) +{ + int i; + + for (i=0; i < len; i++) { + char c = buf[i]; +#ifdef CONFIG_MAGIC_SYSRQ + if (c == '\0') { + hp->sysrq = 1; + continue; + } else if (hp->sysrq) { + handle_sysrq(c, NULL, hp->tty); + hp->sysrq = 0; + continue; + } +#endif /* CONFIG_MAGIC_SYSRQ */ + tty_insert_flip_char(hp->tty, c, 0); + } +} + +/* + * We could get 252 bytes of data at once here. But the tty layer only + * throttles us at TTY_THRESHOLD_THROTTLE (128) bytes, so we could overflow + * it. Accordingly we won't send more than 128 bytes at a time to the flip + * buffer, which will give the tty buffer a chance to throttle us. Should the + * value of TTY_THRESHOLD_THROTTLE change in n_tty.c, this code should be + * revisited. + */ +#define TTY_THRESHOLD_THROTTLE 128 +static struct tty_struct *hvsi_recv_data(struct hvsi_struct *hp, + const uint8_t *packet) +{ + const struct hvsi_header *header = (const struct hvsi_header *)packet; + const uint8_t *data = packet + sizeof(struct hvsi_header); + int datalen = header->len - sizeof(struct hvsi_header); + int overflow = datalen - TTY_THRESHOLD_THROTTLE; + + pr_debug("queueing %i chars '%.*s'\n", datalen, datalen, data); + + if (datalen == 0) + return NULL; + + if (overflow > 0) { + pr_debug("%s: got >TTY_THRESHOLD_THROTTLE bytes\n", __FUNCTION__); + datalen = TTY_THRESHOLD_THROTTLE; + } + + hvsi_insert_chars(hp, data, datalen); + + if (overflow > 0) { + /* + * we still have more data to deliver, so we need to save off the + * overflow and send it later + */ + pr_debug("%s: deferring overflow\n", __FUNCTION__); + memcpy(hp->throttle_buf, data + TTY_THRESHOLD_THROTTLE, overflow); + hp->n_throttle = overflow; + } + + return hp->tty; +} + +/* + * Returns true/false indicating data successfully read from hypervisor. + * Used both to get packets for tty connections and to advance the state + * machine during console handshaking (in which case tty = NULL and we ignore + * incoming data). + */ +static int hvsi_load_chunk(struct hvsi_struct *hp, struct tty_struct **flip, + struct tty_struct **hangup) +{ + uint8_t *packet = hp->inbuf; + int chunklen; + + *flip = NULL; + *hangup = NULL; + + chunklen = hvsi_read(hp, hp->inbuf_end, HVSI_MAX_READ); + if (chunklen == 0) + return 0; + + pr_debug("%s: got %i bytes\n", __FUNCTION__, chunklen); + dbg_dump_hex(hp->inbuf_end, chunklen); + + hp->inbuf_end += chunklen; + + /* handle all completed packets */ + while ((packet < hp->inbuf_end) && got_packet(hp, packet)) { + struct hvsi_header *header = (struct hvsi_header *)packet; + + if (!is_header(packet)) { + printk(KERN_ERR "hvsi%i: got malformed packet\n", hp->index); + /* skip bytes until we find a header or run out of data */ + while ((packet < hp->inbuf_end) && (!is_header(packet))) + packet++; + continue; + } + + pr_debug("%s: handling %i-byte packet\n", __FUNCTION__, + len_packet(packet)); + dbg_dump_packet(packet); + + switch (header->type) { + case VS_DATA_PACKET_HEADER: + if (!is_open(hp)) + break; + if (hp->tty == NULL) + break; /* no tty buffer to put data in */ + *flip = hvsi_recv_data(hp, packet); + break; + case VS_CONTROL_PACKET_HEADER: + *hangup = hvsi_recv_control(hp, packet); + break; + case VS_QUERY_RESPONSE_PACKET_HEADER: + hvsi_recv_response(hp, packet); + break; + case VS_QUERY_PACKET_HEADER: + hvsi_recv_query(hp, packet); + break; + default: + printk(KERN_ERR "hvsi%i: unknown HVSI packet type 0x%x\n", + hp->index, header->type); + dump_packet(packet); + break; + } + + packet += len_packet(packet); + + if (*hangup) { + pr_debug("%s: hangup\n", __FUNCTION__); + /* + * we need to send the hangup now before receiving any more data. + * If we get "data, hangup, data", we can't deliver the second + * data before the hangup. + */ + break; + } + } + + compact_inbuf(hp, packet); + + return 1; +} + +static void hvsi_send_overflow(struct hvsi_struct *hp) +{ + pr_debug("%s: delivering %i bytes overflow\n", __FUNCTION__, + hp->n_throttle); + + hvsi_insert_chars(hp, hp->throttle_buf, hp->n_throttle); + hp->n_throttle = 0; +} + +/* + * must get all pending data because we only get an irq on empty->non-empty + * transition + */ +static irqreturn_t hvsi_interrupt(int irq, void *arg, struct pt_regs *regs) +{ + struct hvsi_struct *hp = (struct hvsi_struct *)arg; + struct tty_struct *flip; + struct tty_struct *hangup; + unsigned long flags; + irqreturn_t handled = IRQ_NONE; + int again = 1; + + pr_debug("%s\n", __FUNCTION__); + + while (again) { + spin_lock_irqsave(&hp->lock, flags); + again = hvsi_load_chunk(hp, &flip, &hangup); + handled = IRQ_HANDLED; + spin_unlock_irqrestore(&hp->lock, flags); + + /* + * we have to call tty_flip_buffer_push() and tty_hangup() outside our + * spinlock. But we also have to keep going until we've read all the + * available data. + */ + + if (flip) { + /* there was data put in the tty flip buffer */ + tty_flip_buffer_push(flip); + flip = NULL; + } + + if (hangup) { + tty_hangup(hangup); + } + } + + spin_lock_irqsave(&hp->lock, flags); + if (hp->tty && hp->n_throttle + && (!test_bit(TTY_THROTTLED, &hp->tty->flags))) { + /* we weren't hung up and we weren't throttled, so we can deliver the + * rest now */ + flip = hp->tty; + hvsi_send_overflow(hp); + } + spin_unlock_irqrestore(&hp->lock, flags); + + if (flip) { + tty_flip_buffer_push(flip); + } + + return handled; +} + +/* for boot console, before the irq handler is running */ +static int __init poll_for_state(struct hvsi_struct *hp, int state) +{ + unsigned long end_jiffies = jiffies + HVSI_TIMEOUT; + + for (;;) { + hvsi_interrupt(hp->virq, (void *)hp, NULL); /* get pending data */ + + if (hp->state == state) + return 0; + + mdelay(5); + if (time_after(jiffies, end_jiffies)) + return -EIO; + } +} + +/* wait for irq handler to change our state */ +static int wait_for_state(struct hvsi_struct *hp, int state) +{ + unsigned long end_jiffies = jiffies + HVSI_TIMEOUT; + unsigned long timeout; + int ret = 0; + + DECLARE_WAITQUEUE(myself, current); + set_current_state(TASK_INTERRUPTIBLE); + add_wait_queue(&hp->stateq, &myself); + + for (;;) { + set_current_state(TASK_INTERRUPTIBLE); + if (hp->state == state) + break; + timeout = end_jiffies - jiffies; + if (time_after(jiffies, end_jiffies)) { + ret = -EIO; + break; + } + schedule_timeout(timeout); + } + remove_wait_queue(&hp->stateq, &myself); + set_current_state(TASK_RUNNING); + + return ret; +} + +static int hvsi_query(struct hvsi_struct *hp, uint16_t verb) +{ + struct hvsi_query packet __ALIGNED__; + int wrote; + + packet.type = VS_QUERY_PACKET_HEADER; + packet.len = sizeof(struct hvsi_query); + packet.seqno = atomic_inc_return(&hp->seqno); + packet.verb = verb; + + pr_debug("%s: sending %i bytes\n", __FUNCTION__, packet.len); + dbg_dump_hex((uint8_t*)&packet, packet.len); + + wrote = hvc_put_chars(hp->vtermno, (char *)&packet, packet.len); + if (wrote != packet.len) { + printk(KERN_ERR "hvsi%i: couldn't send query (%i)!\n", hp->index, + wrote); + return -EIO; + } + + return 0; +} + +static int hvsi_get_mctrl(struct hvsi_struct *hp) +{ + int ret; + + set_state(hp, HVSI_WAIT_FOR_MCTRL_RESPONSE); + hvsi_query(hp, VSV_SEND_MODEM_CTL_STATUS); + + ret = hvsi_wait(hp, HVSI_OPEN); + if (ret < 0) { + printk(KERN_ERR "hvsi%i: didn't get modem flags\n", hp->index); + set_state(hp, HVSI_OPEN); + return ret; + } + + pr_debug("%s: mctrl 0x%x\n", __FUNCTION__, hp->mctrl); + + return 0; +} + +/* note that we can only set DTR */ +static int hvsi_set_mctrl(struct hvsi_struct *hp, uint16_t mctrl) +{ + struct hvsi_control packet __ALIGNED__; + int wrote; + + packet.type = VS_CONTROL_PACKET_HEADER, + packet.seqno = atomic_inc_return(&hp->seqno); + packet.len = sizeof(struct hvsi_control); + packet.verb = VSV_SET_MODEM_CTL; + packet.mask = HVSI_TSDTR; + + if (mctrl & TIOCM_DTR) + packet.word = HVSI_TSDTR; + + pr_debug("%s: sending %i bytes\n", __FUNCTION__, packet.len); + dbg_dump_hex((uint8_t*)&packet, packet.len); + + wrote = hvc_put_chars(hp->vtermno, (char *)&packet, packet.len); + if (wrote != packet.len) { + printk(KERN_ERR "hvsi%i: couldn't set DTR!\n", hp->index); + return -EIO; + } + + return 0; +} + +static void hvsi_drain_input(struct hvsi_struct *hp) +{ + uint8_t buf[HVSI_MAX_READ] __ALIGNED__; + unsigned long end_jiffies = jiffies + HVSI_TIMEOUT; + + while (time_before(end_jiffies, jiffies)) + if (0 == hvsi_read(hp, buf, HVSI_MAX_READ)) + break; +} + +static int hvsi_handshake(struct hvsi_struct *hp) +{ + int ret; + + /* + * We could have a CLOSE or other data waiting for us before we even try + * to open; try to throw it all away so we don't get confused. (CLOSE + * is the first message sent up the pipe when the FSP comes online. We + * need to distinguish between "it came up a while ago and we're the first + * user" and "it was just reset before it saw our handshake packet".) + */ + hvsi_drain_input(hp); + + set_state(hp, HVSI_WAIT_FOR_VER_RESPONSE); + ret = hvsi_query(hp, VSV_SEND_VERSION_NUMBER); + if (ret < 0) { + printk(KERN_ERR "hvsi%i: couldn't send version query\n", hp->index); + return ret; + } + + ret = hvsi_wait(hp, HVSI_OPEN); + if (ret < 0) + return ret; + + return 0; +} + +static int hvsi_put_chars(struct hvsi_struct *hp, const char *buf, int count) +{ + struct hvsi_data packet __ALIGNED__; + int ret; + + BUG_ON(count > HVSI_MAX_OUTGOING_DATA); + + packet.type = VS_DATA_PACKET_HEADER; + packet.seqno = atomic_inc_return(&hp->seqno); + packet.len = count + sizeof(struct hvsi_header); + memcpy(&packet.data, buf, count); + + ret = hvc_put_chars(hp->vtermno, (char *)&packet, packet.len); + if (ret == packet.len) { + /* return the number of chars written, not the packet length */ + return count; + } + return ret; /* return any errors */ +} + +static void hvsi_close_protocol(struct hvsi_struct *hp) +{ + struct hvsi_control packet __ALIGNED__; + + packet.type = VS_CONTROL_PACKET_HEADER; + packet.seqno = atomic_inc_return(&hp->seqno); + packet.len = 6; + packet.verb = VSV_CLOSE_PROTOCOL; + + pr_debug("%s: sending %i bytes\n", __FUNCTION__, packet.len); + dbg_dump_hex((uint8_t*)&packet, packet.len); + + hvc_put_chars(hp->vtermno, (char *)&packet, packet.len); +} + +static int hvsi_open(struct tty_struct *tty, struct file *filp) +{ + struct hvsi_struct *hp; + unsigned long flags; + int line = tty->index; + int ret; + + pr_debug("%s\n", __FUNCTION__); + + if (line < 0 || line >= hvsi_count) + return -ENODEV; + hp = &hvsi_ports[line]; + + tty->driver_data = hp; + tty->low_latency = 1; /* avoid throttle/tty_flip_buffer_push race */ + + spin_lock_irqsave(&hp->lock, flags); + hp->tty = tty; + hp->count++; + atomic_set(&hp->seqno, 0); + h_vio_signal(hp->vtermno, VIO_IRQ_ENABLE); + spin_unlock_irqrestore(&hp->lock, flags); + + if (hp->flags & HVSI_CONSOLE) + return 0; /* this has already been handshaked as the console */ + + ret = hvsi_handshake(hp); + if (ret < 0) { + printk(KERN_ERR "%s: HVSI handshaking failed\n", tty->name); + return ret; + } + + ret = hvsi_get_mctrl(hp); + if (ret < 0) { + printk(KERN_ERR "%s: couldn't get initial modem flags\n", tty->name); + return ret; + } + + ret = hvsi_set_mctrl(hp, hp->mctrl | TIOCM_DTR); + if (ret < 0) { + printk(KERN_ERR "%s: couldn't set DTR\n", tty->name); + return ret; + } + + return 0; +} + +/* wait for hvsi_write_worker to empty hp->outbuf */ +static void hvsi_flush_output(struct hvsi_struct *hp) +{ + unsigned long end_jiffies = jiffies + HVSI_TIMEOUT; + unsigned long timeout; + + DECLARE_WAITQUEUE(myself, current); + set_current_state(TASK_UNINTERRUPTIBLE); + add_wait_queue(&hp->emptyq, &myself); + + for (;;) { + set_current_state(TASK_UNINTERRUPTIBLE); + if (hp->n_outbuf <= 0) + break; + timeout = end_jiffies - jiffies; + if (time_after(jiffies, end_jiffies)) + break; + schedule_timeout(timeout); + } + remove_wait_queue(&hp->emptyq, &myself); + set_current_state(TASK_RUNNING); + + /* 'writer' could still be pending if it didn't see n_outbuf = 0 yet */ + cancel_delayed_work(&hp->writer); + flush_scheduled_work(); + + /* + * it's also possible that our timeout expired and hvsi_write_worker + * didn't manage to push outbuf. poof. + */ + hp->n_outbuf = 0; +} + +static void hvsi_close(struct tty_struct *tty, struct file *filp) +{ + struct hvsi_struct *hp = tty->driver_data; + unsigned long flags; + + pr_debug("%s\n", __FUNCTION__); + + if (tty_hung_up_p(filp)) + return; + + spin_lock_irqsave(&hp->lock, flags); + + if (--hp->count == 0) { + hp->tty = NULL; + hp->inbuf_end = hp->inbuf; /* discard remaining partial packets */ + + /* only close down connection if it is not the console */ + if (!(hp->flags & HVSI_CONSOLE)) { + h_vio_signal(hp->vtermno, VIO_IRQ_DISABLE); /* no more irqs */ + __set_state(hp, HVSI_CLOSED); + /* + * any data delivered to the tty layer after this will be + * discarded (except for XON/XOFF) + */ + tty->closing = 1; + + spin_unlock_irqrestore(&hp->lock, flags); + + /* let any existing irq handlers finish. no more will start. */ + synchronize_irq(hp->virq); + + /* hvsi_write_worker will re-schedule until outbuf is empty. */ + hvsi_flush_output(hp); + + /* tell FSP to stop sending data */ + hvsi_close_protocol(hp); + + /* + * drain anything FSP is still in the middle of sending, and let + * hvsi_handshake drain the rest on the next open. + */ + hvsi_drain_input(hp); + + spin_lock_irqsave(&hp->lock, flags); + } + } else if (hp->count < 0) + printk(KERN_ERR "hvsi_close %lu: oops, count is %d\n", + hp - hvsi_ports, hp->count); + + spin_unlock_irqrestore(&hp->lock, flags); +} + +static void hvsi_hangup(struct tty_struct *tty) +{ + struct hvsi_struct *hp = tty->driver_data; + + pr_debug("%s\n", __FUNCTION__); + + hp->count = 0; + hp->tty = NULL; +} + +/* called with hp->lock held */ +static void hvsi_push(struct hvsi_struct *hp) +{ + int n; + + if (hp->n_outbuf <= 0) + return; + + n = hvsi_put_chars(hp, hp->outbuf, hp->n_outbuf); + if (n != 0) { + /* + * either all data was sent or there was an error, and we throw away + * data on error. + */ + hp->n_outbuf = 0; + } +} + +/* hvsi_write_worker will keep rescheduling itself until outbuf is empty */ +static void hvsi_write_worker(void *arg) +{ + struct hvsi_struct *hp = (struct hvsi_struct *)arg; + unsigned long flags; +#ifdef DEBUG + static long start_j = 0; + + if (start_j == 0) + start_j = jiffies; +#endif /* DEBUG */ + + spin_lock_irqsave(&hp->lock, flags); + + hvsi_push(hp); + if (hp->n_outbuf > 0) + schedule_delayed_work(&hp->writer, 10); + else { +#ifdef DEBUG + pr_debug("%s: outbuf emptied after %li jiffies\n", __FUNCTION__, + jiffies - start_j); + start_j = 0; +#endif /* DEBUG */ + wake_up_all(&hp->emptyq); + if (test_bit(TTY_DO_WRITE_WAKEUP, &hp->tty->flags) + && hp->tty->ldisc.write_wakeup) + hp->tty->ldisc.write_wakeup(hp->tty); + wake_up_interruptible(&hp->tty->write_wait); + } + + spin_unlock_irqrestore(&hp->lock, flags); +} + +static int hvsi_write_room(struct tty_struct *tty) +{ + struct hvsi_struct *hp = (struct hvsi_struct *)tty->driver_data; + + return N_OUTBUF - hp->n_outbuf; +} + +static int hvsi_chars_in_buffer(struct tty_struct *tty) +{ + struct hvsi_struct *hp = (struct hvsi_struct *)tty->driver_data; + + return hp->n_outbuf; +} + +static int hvsi_write(struct tty_struct *tty, int from_user, + const unsigned char *buf, int count) +{ + struct hvsi_struct *hp = tty->driver_data; + const char *source = buf; + char *kbuf = NULL; + unsigned long flags; + int total = 0; + int origcount = count; + + if (from_user) { + kbuf = kmalloc(count, GFP_KERNEL); + if (kbuf == NULL) + return -ENOMEM; + if (copy_from_user(kbuf, buf, count)) { + kfree(kbuf); + return -EFAULT; + } + source = kbuf; + } + + spin_lock_irqsave(&hp->lock, flags); + + if (!is_open(hp)) { + /* we're either closing or not yet open; don't accept data */ + pr_debug("%s: not open\n", __FUNCTION__); + goto out; + } + + /* + * when the hypervisor buffer (16K) fills, data will stay in hp->outbuf + * and hvsi_write_worker will be scheduled. subsequent hvsi_write() calls + * will see there is no room in outbuf and return. + */ + while ((count > 0) && (hvsi_write_room(hp->tty) > 0)) { + int chunksize = min(count, hvsi_write_room(hp->tty)); + + BUG_ON(hp->n_outbuf < 0); + memcpy(hp->outbuf + hp->n_outbuf, source, chunksize); + hp->n_outbuf += chunksize; + + total += chunksize; + source += chunksize; + count -= chunksize; + hvsi_push(hp); + } + + if (hp->n_outbuf > 0) { + /* + * we weren't able to write it all to the hypervisor. + * schedule another push attempt. + */ + schedule_delayed_work(&hp->writer, 10); + } + +out: + spin_unlock_irqrestore(&hp->lock, flags); + + if (from_user) + kfree(kbuf); + + if (total != origcount) + pr_debug("%s: wanted %i, only wrote %i\n", __FUNCTION__, origcount, + total); + + return total; +} + +/* + * I have never seen throttle or unthrottle called, so this little throttle + * buffering scheme may or may not work. + */ +static void hvsi_throttle(struct tty_struct *tty) +{ + struct hvsi_struct *hp = (struct hvsi_struct *)tty->driver_data; + + pr_debug("%s\n", __FUNCTION__); + + h_vio_signal(hp->vtermno, VIO_IRQ_DISABLE); +} + +static void hvsi_unthrottle(struct tty_struct *tty) +{ + struct hvsi_struct *hp = (struct hvsi_struct *)tty->driver_data; + unsigned long flags; + int shouldflip = 0; + + pr_debug("%s\n", __FUNCTION__); + + spin_lock_irqsave(&hp->lock, flags); + if (hp->n_throttle) { + hvsi_send_overflow(hp); + shouldflip = 1; + } + spin_unlock_irqrestore(&hp->lock, flags); + + if (shouldflip) + tty_flip_buffer_push(hp->tty); + + h_vio_signal(hp->vtermno, VIO_IRQ_ENABLE); +} + +static int hvsi_tiocmget(struct tty_struct *tty, struct file *file) +{ + struct hvsi_struct *hp = (struct hvsi_struct *)tty->driver_data; + + hvsi_get_mctrl(hp); + return hp->mctrl; +} + +static int hvsi_tiocmset(struct tty_struct *tty, struct file *file, + unsigned int set, unsigned int clear) +{ + struct hvsi_struct *hp = (struct hvsi_struct *)tty->driver_data; + unsigned long flags; + uint16_t new_mctrl; + + /* we can only alter DTR */ + clear &= TIOCM_DTR; + set &= TIOCM_DTR; + + spin_lock_irqsave(&hp->lock, flags); + + new_mctrl = (hp->mctrl & ~clear) | set; + + if (hp->mctrl != new_mctrl) { + hvsi_set_mctrl(hp, new_mctrl); + hp->mctrl = new_mctrl; + } + spin_unlock_irqrestore(&hp->lock, flags); + + return 0; +} + + +static struct tty_operations hvsi_ops = { + .open = hvsi_open, + .close = hvsi_close, + .write = hvsi_write, + .hangup = hvsi_hangup, + .write_room = hvsi_write_room, + .chars_in_buffer = hvsi_chars_in_buffer, + .throttle = hvsi_throttle, + .unthrottle = hvsi_unthrottle, + .tiocmget = hvsi_tiocmget, + .tiocmset = hvsi_tiocmset, +}; + +static int __init hvsi_init(void) +{ + int i; + + hvsi_driver = alloc_tty_driver(hvsi_count); + if (!hvsi_driver) + return -ENOMEM; + + hvsi_driver->owner = THIS_MODULE; + hvsi_driver->devfs_name = "hvsi/"; + hvsi_driver->driver_name = "hvsi"; + hvsi_driver->name = "hvsi"; + hvsi_driver->major = HVSI_MAJOR; + hvsi_driver->minor_start = HVSI_MINOR; + hvsi_driver->type = TTY_DRIVER_TYPE_SYSTEM; + hvsi_driver->init_termios = tty_std_termios; + hvsi_driver->init_termios.c_cflag = B9600 | CS8 | CREAD | HUPCL; + hvsi_driver->flags = TTY_DRIVER_REAL_RAW; + tty_set_operations(hvsi_driver, &hvsi_ops); + + for (i=0; i < hvsi_count; i++) { + struct hvsi_struct *hp = &hvsi_ports[i]; + int ret = 1; + + ret = request_irq(hp->virq, hvsi_interrupt, SA_INTERRUPT, "hvsi", hp); + if (ret) + printk(KERN_ERR "HVSI: couldn't reserve irq 0x%x (error %i)\n", + hp->virq, ret); + } + hvsi_wait = wait_for_state; /* irqs active now */ + + if (tty_register_driver(hvsi_driver)) + panic("Couldn't register hvsi console driver\n"); + + printk(KERN_INFO "HVSI: registered %i devices\n", hvsi_count); + + return 0; +} +device_initcall(hvsi_init); + +/***** console (not tty) code: *****/ + +static void hvsi_console_print(struct console *console, const char *buf, + unsigned int count) +{ + struct hvsi_struct *hp = &hvsi_ports[console->index]; + char c[HVSI_MAX_OUTGOING_DATA] __ALIGNED__; + unsigned int i = 0, n = 0; + int ret, donecr = 0; + + mb(); + if (!is_open(hp)) + return; + + /* + * ugh, we have to translate LF -> CRLF ourselves, in place. + * copied from hvc_console.c: + */ + while (count > 0 || i > 0) { + if (count > 0 && i < sizeof(c)) { + if (buf[n] == '\n' && !donecr) { + c[i++] = '\r'; + donecr = 1; + } else { + c[i++] = buf[n++]; + donecr = 0; + --count; + } + } else { + ret = hvsi_put_chars(hp, c, i); + if (ret < 0) + i = 0; + i -= ret; + } + } +} + +static struct tty_driver *hvsi_console_device(struct console *console, + int *index) +{ + *index = console->index; + return hvsi_driver; +} + +static int __init hvsi_console_setup(struct console *console, char *options) +{ + struct hvsi_struct *hp = &hvsi_ports[console->index]; + int ret; + + if (console->index < 0 || console->index >= hvsi_count) + return -1; + + /* give the FSP a chance to change the baud rate when we re-open */ + hvsi_close_protocol(hp); + + ret = hvsi_handshake(hp); + if (ret < 0) + return ret; + + ret = hvsi_get_mctrl(hp); + if (ret < 0) + return ret; + + ret = hvsi_set_mctrl(hp, hp->mctrl | TIOCM_DTR); + if (ret < 0) + return ret; + + hp->flags |= HVSI_CONSOLE; + + return 0; +} + +static struct console hvsi_con_driver = { + .name = "hvsi", + .write = hvsi_console_print, + .device = hvsi_console_device, + .setup = hvsi_console_setup, + .flags = CON_PRINTBUFFER, + .index = -1, +}; + +static int __init hvsi_console_init(void) +{ + struct device_node *vty; + + hvsi_wait = poll_for_state; /* no irqs yet; must poll */ + + /* search device tree for vty nodes */ + for (vty = of_find_compatible_node(NULL, "serial", "hvterm-protocol"); + vty != NULL; + vty = of_find_compatible_node(vty, "serial", "hvterm-protocol")) { + struct hvsi_struct *hp; + uint32_t *vtermno; + uint32_t *irq; + + vtermno = (uint32_t *)get_property(vty, "reg", NULL); + irq = (uint32_t *)get_property(vty, "interrupts", NULL); + if (!vtermno || !irq) + continue; + + if (hvsi_count >= MAX_NR_HVSI_CONSOLES) { + of_node_put(vty); + break; + } + + hp = &hvsi_ports[hvsi_count]; + INIT_WORK(&hp->writer, hvsi_write_worker, hp); + init_waitqueue_head(&hp->emptyq); + init_waitqueue_head(&hp->stateq); + hp->lock = SPIN_LOCK_UNLOCKED; + hp->index = hvsi_count; + hp->inbuf_end = hp->inbuf; + hp->state = HVSI_CLOSED; + hp->vtermno = *vtermno; + hp->virq = virt_irq_create_mapping(irq[0]); + if (hp->virq == NO_IRQ) { + printk(KERN_ERR "%s: couldn't create irq mapping for 0x%x\n", + __FUNCTION__, hp->virq); + continue; + } else + hp->virq = irq_offset_up(hp->virq); + + hvsi_count++; + } + + if (hvsi_count) + register_console(&hvsi_con_driver); + return 0; +} +console_initcall(hvsi_console_init); diff --git a/drivers/char/ip2/ip2types.h b/drivers/char/ip2/ip2types.h index 8d2b37999..9d67b260b 100644 --- a/drivers/char/ip2/ip2types.h +++ b/drivers/char/ip2/ip2types.h @@ -49,6 +49,9 @@ typedef struct short irq[IP2_MAX_BOARDS]; unsigned short addr[IP2_MAX_BOARDS]; int type[IP2_MAX_BOARDS]; +#ifdef CONFIG_PCI + struct pci_dev *pci_dev[IP2_MAX_BOARDS]; +#endif } ip2config_t; #endif diff --git a/drivers/char/ipmi/Kconfig b/drivers/char/ipmi/Kconfig index b632538ff..655a17219 100644 --- a/drivers/char/ipmi/Kconfig +++ b/drivers/char/ipmi/Kconfig @@ -57,4 +57,11 @@ config IPMI_WATCHDOG help This enables the IPMI watchdog timer. +config IPMI_POWEROFF + tristate 'IPMI Poweroff' + depends on IPMI_HANDLER + help + This enables a function to power off the system with IPMI if + the IPMI management controller is capable of this. + endmenu diff --git a/drivers/char/ipmi/Makefile b/drivers/char/ipmi/Makefile index b7d823072..553f0a408 100644 --- a/drivers/char/ipmi/Makefile +++ b/drivers/char/ipmi/Makefile @@ -8,6 +8,7 @@ obj-$(CONFIG_IPMI_HANDLER) += ipmi_msghandler.o obj-$(CONFIG_IPMI_DEVICE_INTERFACE) += ipmi_devintf.o obj-$(CONFIG_IPMI_SI) += ipmi_si.o obj-$(CONFIG_IPMI_WATCHDOG) += ipmi_watchdog.o +obj-$(CONFIG_IPMI_POWEROFF) += ipmi_poweroff.o ipmi_si.o: $(ipmi_si-objs) $(LD) -r -o $@ $(ipmi_si-objs) diff --git a/drivers/char/ipmi/ipmi_poweroff.c b/drivers/char/ipmi/ipmi_poweroff.c new file mode 100644 index 000000000..ade872962 --- /dev/null +++ b/drivers/char/ipmi/ipmi_poweroff.c @@ -0,0 +1,543 @@ +/* + * ipmi_poweroff.c + * + * MontaVista IPMI Poweroff extension to sys_reboot + * + * Author: MontaVista Software, Inc. + * Steven Dake + * Corey Minyard + * source@mvista.com + * + * Copyright 2002,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 SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR + * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE + * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 675 Mass Ave, Cambridge, MA 02139, USA. + */ +#include +#include +#include +#include +#include +#include + +#define PFX "IPMI poweroff: " +#define IPMI_POWEROFF_VERSION "v33" + +/* Where to we insert our poweroff function? */ +extern void (*pm_power_off)(void); + +/* Stuff from the get device id command. */ +unsigned int mfg_id; +unsigned int prod_id; +unsigned char capabilities; + +/* We use our own messages for this operation, we don't let the system + allocate them, since we may be in a panic situation. The whole + thing is single-threaded, anyway, so multiple messages are not + required. */ +static void dummy_smi_free(struct ipmi_smi_msg *msg) +{ +} +static void dummy_recv_free(struct ipmi_recv_msg *msg) +{ +} +static struct ipmi_smi_msg halt_smi_msg = +{ + .done = dummy_smi_free +}; +static struct ipmi_recv_msg halt_recv_msg = +{ + .done = dummy_recv_free +}; + + +/* + * Code to send a message and wait for the reponse. + */ + +static void receive_handler(struct ipmi_recv_msg *recv_msg, void *handler_data) +{ + struct semaphore *sem = recv_msg->user_msg_data; + + if (sem) + up(sem); +} + +static struct ipmi_user_hndl ipmi_poweroff_handler = +{ + .ipmi_recv_hndl = receive_handler +}; + + +static int ipmi_request_wait_for_response(ipmi_user_t user, + struct ipmi_addr *addr, + struct kernel_ipmi_msg *send_msg) +{ + int rv; + struct semaphore sem; + + sema_init (&sem, 0); + + rv = ipmi_request_supply_msgs(user, addr, 0, send_msg, &sem, + &halt_smi_msg, &halt_recv_msg, 0); + if (rv) + return rv; + + down (&sem); + + return halt_recv_msg.msg.data[0]; +} + +/* We are in run-to-completion mode, no semaphore is desired. */ +static int ipmi_request_in_rc_mode(ipmi_user_t user, + struct ipmi_addr *addr, + struct kernel_ipmi_msg *send_msg) +{ + int rv; + + rv = ipmi_request_supply_msgs(user, addr, 0, send_msg, NULL, + &halt_smi_msg, &halt_recv_msg, 0); + if (rv) + return rv; + + return halt_recv_msg.msg.data[0]; +} + +/* + * ATCA Support + */ + +#define IPMI_NETFN_ATCA 0x2c +#define IPMI_ATCA_SET_POWER_CMD 0x11 +#define IPMI_ATCA_GET_ADDR_INFO_CMD 0x01 +#define IPMI_PICMG_ID 0 + +static int ipmi_atca_detect (ipmi_user_t user) +{ + struct ipmi_system_interface_addr smi_addr; + struct kernel_ipmi_msg send_msg; + int rv; + unsigned char data[1]; + + /* + * Configure IPMI address for local access + */ + smi_addr.addr_type = IPMI_SYSTEM_INTERFACE_ADDR_TYPE; + smi_addr.channel = IPMI_BMC_CHANNEL; + smi_addr.lun = 0; + + /* + * Use get address info to check and see if we are ATCA + */ + send_msg.netfn = IPMI_NETFN_ATCA; + send_msg.cmd = IPMI_ATCA_GET_ADDR_INFO_CMD; + data[0] = IPMI_PICMG_ID; + send_msg.data = data; + send_msg.data_len = sizeof(data); + rv = ipmi_request_wait_for_response(user, + (struct ipmi_addr *) &smi_addr, + &send_msg); + return !rv; +} + +static void ipmi_poweroff_atca (ipmi_user_t user) +{ + struct ipmi_system_interface_addr smi_addr; + struct kernel_ipmi_msg send_msg; + int rv; + unsigned char data[4]; + + /* + * Configure IPMI address for local access + */ + smi_addr.addr_type = IPMI_SYSTEM_INTERFACE_ADDR_TYPE; + smi_addr.channel = IPMI_BMC_CHANNEL; + smi_addr.lun = 0; + + printk(KERN_INFO PFX "Powering down via ATCA power command\n"); + + /* + * Power down + */ + send_msg.netfn = IPMI_NETFN_ATCA; + send_msg.cmd = IPMI_ATCA_SET_POWER_CMD; + data[0] = IPMI_PICMG_ID; + data[1] = 0; /* FRU id */ + data[2] = 0; /* Power Level */ + data[3] = 0; /* Don't change saved presets */ + send_msg.data = data; + send_msg.data_len = sizeof (data); + rv = ipmi_request_in_rc_mode(user, + (struct ipmi_addr *) &smi_addr, + &send_msg); + if (rv) { + printk(KERN_ERR PFX "Unable to send ATCA powerdown message," + " IPMI error 0x%x\n", rv); + goto out; + } + + out: + return; +} + +/* + * CPI1 Support + */ + +#define IPMI_NETFN_OEM_1 0xf8 +#define OEM_GRP_CMD_SET_RESET_STATE 0x84 +#define OEM_GRP_CMD_SET_POWER_STATE 0x82 +#define IPMI_NETFN_OEM_8 0xf8 +#define OEM_GRP_CMD_REQUEST_HOTSWAP_CTRL 0x80 +#define OEM_GRP_CMD_GET_SLOT_GA 0xa3 +#define IPMI_NETFN_SENSOR_EVT 0x10 +#define IPMI_CMD_GET_EVENT_RECEIVER 0x01 + +#define IPMI_CPI1_PRODUCT_ID 0x000157 +#define IPMI_CPI1_MANUFACTURER_ID 0x0108 + +static int ipmi_cpi1_detect (ipmi_user_t user) +{ + return ((mfg_id == IPMI_CPI1_MANUFACTURER_ID) + && (prod_id == IPMI_CPI1_PRODUCT_ID)); +} + +static void ipmi_poweroff_cpi1 (ipmi_user_t user) +{ + struct ipmi_system_interface_addr smi_addr; + struct ipmi_ipmb_addr ipmb_addr; + struct kernel_ipmi_msg send_msg; + int rv; + unsigned char data[1]; + int slot; + unsigned char hotswap_ipmb; + unsigned char aer_addr; + unsigned char aer_lun; + + /* + * Configure IPMI address for local access + */ + smi_addr.addr_type = IPMI_SYSTEM_INTERFACE_ADDR_TYPE; + smi_addr.channel = IPMI_BMC_CHANNEL; + smi_addr.lun = 0; + + printk(KERN_INFO PFX "Powering down via CPI1 power command\n"); + + /* + * Get IPMI ipmb address + */ + send_msg.netfn = IPMI_NETFN_OEM_8 >> 2; + send_msg.cmd = OEM_GRP_CMD_GET_SLOT_GA; + send_msg.data = NULL; + send_msg.data_len = 0; + rv = ipmi_request_in_rc_mode(user, + (struct ipmi_addr *) &smi_addr, + &send_msg); + if (rv) + goto out; + slot = halt_recv_msg.msg.data[1]; + hotswap_ipmb = (slot > 9) ? (0xb0 + 2 * slot) : (0xae + 2 * slot); + + /* + * Get active event receiver + */ + send_msg.netfn = IPMI_NETFN_SENSOR_EVT >> 2; + send_msg.cmd = IPMI_CMD_GET_EVENT_RECEIVER; + send_msg.data = NULL; + send_msg.data_len = 0; + rv = ipmi_request_in_rc_mode(user, + (struct ipmi_addr *) &smi_addr, + &send_msg); + if (rv) + goto out; + aer_addr = halt_recv_msg.msg.data[1]; + aer_lun = halt_recv_msg.msg.data[2]; + + /* + * Setup IPMB address target instead of local target + */ + ipmb_addr.addr_type = IPMI_IPMB_ADDR_TYPE; + ipmb_addr.channel = 0; + ipmb_addr.slave_addr = aer_addr; + ipmb_addr.lun = aer_lun; + + /* + * Send request hotswap control to remove blade from dpv + */ + send_msg.netfn = IPMI_NETFN_OEM_8 >> 2; + send_msg.cmd = OEM_GRP_CMD_REQUEST_HOTSWAP_CTRL; + send_msg.data = &hotswap_ipmb; + send_msg.data_len = 1; + ipmi_request_in_rc_mode(user, + (struct ipmi_addr *) &ipmb_addr, + &send_msg); + + /* + * Set reset asserted + */ + send_msg.netfn = IPMI_NETFN_OEM_1 >> 2; + send_msg.cmd = OEM_GRP_CMD_SET_RESET_STATE; + send_msg.data = data; + data[0] = 1; /* Reset asserted state */ + send_msg.data_len = 1; + rv = ipmi_request_in_rc_mode(user, + (struct ipmi_addr *) &smi_addr, + &send_msg); + if (rv) + goto out; + + /* + * Power down + */ + send_msg.netfn = IPMI_NETFN_OEM_1 >> 2; + send_msg.cmd = OEM_GRP_CMD_SET_POWER_STATE; + send_msg.data = data; + data[0] = 1; /* Power down state */ + send_msg.data_len = 1; + rv = ipmi_request_in_rc_mode(user, + (struct ipmi_addr *) &smi_addr, + &send_msg); + if (rv) + goto out; + + out: + return; +} + +/* + * Standard chassis support + */ + +#define IPMI_NETFN_CHASSIS_REQUEST 0 +#define IPMI_CHASSIS_CONTROL_CMD 0x02 + +static int ipmi_chassis_detect (ipmi_user_t user) +{ + /* Chassis support, use it. */ + return (capabilities & 0x80); +} + +static void ipmi_poweroff_chassis (ipmi_user_t user) +{ + struct ipmi_system_interface_addr smi_addr; + struct kernel_ipmi_msg send_msg; + int rv; + unsigned char data[1]; + + /* + * Configure IPMI address for local access + */ + smi_addr.addr_type = IPMI_SYSTEM_INTERFACE_ADDR_TYPE; + smi_addr.channel = IPMI_BMC_CHANNEL; + smi_addr.lun = 0; + + printk(KERN_INFO PFX "Powering down via IPMI chassis control command\n"); + + /* + * Power down + */ + send_msg.netfn = IPMI_NETFN_CHASSIS_REQUEST; + send_msg.cmd = IPMI_CHASSIS_CONTROL_CMD; + data[0] = 0; /* Power down */ + send_msg.data = data; + send_msg.data_len = sizeof(data); + rv = ipmi_request_in_rc_mode(user, + (struct ipmi_addr *) &smi_addr, + &send_msg); + if (rv) { + printk(KERN_ERR PFX "Unable to send chassis powerdown message," + " IPMI error 0x%x\n", rv); + goto out; + } + + out: + return; +} + + +/* Table of possible power off functions. */ +struct poweroff_function { + char *platform_type; + int (*detect)(ipmi_user_t user); + void (*poweroff_func)(ipmi_user_t user); +}; + +static struct poweroff_function poweroff_functions[] = { + { "ATCA", ipmi_atca_detect, ipmi_poweroff_atca }, + { "CPI1", ipmi_cpi1_detect, ipmi_poweroff_cpi1 }, + /* Chassis should generally be last, other things should override + it. */ + { "chassis", ipmi_chassis_detect, ipmi_poweroff_chassis }, +}; +#define NUM_PO_FUNCS (sizeof(poweroff_functions) \ + / sizeof(struct poweroff_function)) + + +/* Our local state. */ +static int ready = 0; +static ipmi_user_t ipmi_user; +static void (*specific_poweroff_func)(ipmi_user_t user) = NULL; + +/* Holds the old poweroff function so we can restore it on removal. */ +static void (*old_poweroff_func)(void); + + +/* Called on a powerdown request. */ +static void ipmi_poweroff_function (void) +{ + if (!ready) + return; + + /* Use run-to-completion mode, since interrupts may be off. */ + ipmi_user_set_run_to_completion(ipmi_user, 1); + specific_poweroff_func(ipmi_user); + ipmi_user_set_run_to_completion(ipmi_user, 0); +} + +/* Wait for an IPMI interface to be installed, the first one installed + will be grabbed by this code and used to perform the powerdown. */ +static void ipmi_po_new_smi(int if_num) +{ + struct ipmi_system_interface_addr smi_addr; + struct kernel_ipmi_msg send_msg; + int rv; + int i; + + if (ready) + return; + + rv = ipmi_create_user(if_num, &ipmi_poweroff_handler, NULL, &ipmi_user); + if (rv) { + printk(KERN_ERR PFX "could not create IPMI user, error %d\n", + rv); + return; + } + + /* + * Do a get device ide and store some results, since this is + * used by several functions. + */ + smi_addr.addr_type = IPMI_SYSTEM_INTERFACE_ADDR_TYPE; + smi_addr.channel = IPMI_BMC_CHANNEL; + smi_addr.lun = 0; + + send_msg.netfn = IPMI_NETFN_APP_REQUEST; + send_msg.cmd = IPMI_GET_DEVICE_ID_CMD; + send_msg.data = NULL; + send_msg.data_len = 0; + rv = ipmi_request_wait_for_response(ipmi_user, + (struct ipmi_addr *) &smi_addr, + &send_msg); + if (rv) { + printk(KERN_ERR PFX "Unable to send IPMI get device id info," + " IPMI error 0x%x\n", rv); + goto out_err; + } + + if (halt_recv_msg.msg.data_len < 12) { + printk(KERN_ERR PFX "(chassis) IPMI get device id info too," + " short, was %d bytes, needed %d bytes\n", + halt_recv_msg.msg.data_len, 12); + goto out_err; + } + + mfg_id = (halt_recv_msg.msg.data[7] + | (halt_recv_msg.msg.data[8] << 8) + | (halt_recv_msg.msg.data[9] << 16)); + prod_id = (halt_recv_msg.msg.data[10] + | (halt_recv_msg.msg.data[11] << 8)); + capabilities = halt_recv_msg.msg.data[6]; + + + /* Scan for a poweroff method */ + for (i=0; i #include #include +#include #include #include @@ -389,7 +390,7 @@ static void isicom_tx(unsigned long _data) tty = port->tty; save_flags(flags); cli(); - txcount = MIN(TX_SIZE, port->xmit_cnt); + txcount = min_t(short, TX_SIZE, port->xmit_cnt); if ((txcount <= 0) || tty->stopped || tty->hw_stopped) { restore_flags(flags); continue; @@ -421,7 +422,7 @@ static void isicom_tx(unsigned long _data) residue = NO; wrd = 0; while (1) { - cnt = MIN(txcount, (SERIAL_XMIT_SIZE - port->xmit_tail)); + cnt = min_t(int, txcount, (SERIAL_XMIT_SIZE - port->xmit_tail)); if (residue == YES) { residue = NO; if (cnt > 0) { @@ -484,10 +485,8 @@ static void isicom_bottomhalf(void * data) if (!tty) return; - - if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && - tty->ldisc.write_wakeup) - (tty->ldisc.write_wakeup)(tty); + + tty_wakeup(tty); wake_up_interruptible(&tty->write_wait); } @@ -650,7 +649,7 @@ static irqreturn_t isicom_interrupt(int irq, void *dev_id, } } else { /* Data Packet */ - count = MIN(byte_count, (TTY_FLIPBUF_SIZE - tty->flip.count)); + count = min_t(unsigned short, byte_count, (TTY_FLIPBUF_SIZE - tty->flip.count)); #ifdef ISICOM_DEBUG printk(KERN_DEBUG "ISICOM: Intr: Can rx %d of %d bytes.\n", count, byte_count); @@ -1119,8 +1118,8 @@ static void isicom_close(struct tty_struct * tty, struct file * filp) isicom_shutdown_port(port); if (tty->driver->flush_buffer) tty->driver->flush_buffer(tty); - if (tty->ldisc.flush_buffer) - tty->ldisc.flush_buffer(tty); + + tty_ldisc_flush(tty); tty->closing = 0; port->tty = NULL; if (port->blocked_open) { @@ -1163,8 +1162,8 @@ static int isicom_write(struct tty_struct * tty, int from_user, save_flags(flags); while(1) { cli(); - cnt = MIN(count, MIN(SERIAL_XMIT_SIZE - port->xmit_cnt - 1, - SERIAL_XMIT_SIZE - port->xmit_head)); + cnt = min_t(int, count, min(SERIAL_XMIT_SIZE - port->xmit_cnt - 1, + SERIAL_XMIT_SIZE - port->xmit_head)); if (cnt <= 0) break; @@ -1180,8 +1179,8 @@ static int isicom_write(struct tty_struct * tty, int from_user, return -EFAULT; } cli(); - cnt = MIN(cnt, MIN(SERIAL_XMIT_SIZE - port->xmit_cnt - 1, - SERIAL_XMIT_SIZE - port->xmit_head)); + cnt = min_t(int, cnt, min(SERIAL_XMIT_SIZE - port->xmit_cnt - 1, + SERIAL_XMIT_SIZE - port->xmit_head)); memcpy(port->xmit_buf + port->xmit_head, tmp_buf, cnt); } else @@ -1563,9 +1562,7 @@ static void isicom_flush_buffer(struct tty_struct * tty) restore_flags(flags); wake_up_interruptible(&tty->write_wait); - if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && - tty->ldisc.write_wakeup) - (tty->ldisc.write_wakeup)(tty); + tty_wakeup(tty); } @@ -1906,8 +1903,7 @@ int init_module(void) void cleanup_module(void) { re_schedule = 0; - set_current_state(TASK_INTERRUPTIBLE); - schedule_timeout(HZ); + msleep(1000); #ifdef ISICOM_DEBUG printk("ISICOM: isicom_tx tx_count = %ld.\n", tx_count); diff --git a/drivers/char/mmtimer.c b/drivers/char/mmtimer.c new file mode 100644 index 000000000..20aa170db --- /dev/null +++ b/drivers/char/mmtimer.c @@ -0,0 +1,216 @@ +/* + * Intel Multimedia Timer device implementation for SGI SN platforms. + * + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file "COPYING" in the main directory of this archive + * for more details. + * + * Copyright (c) 2001-2004 Silicon Graphics, Inc. All rights reserved. + * + * This driver exports an API that should be supportable by any HPET or IA-PC + * multimedia timer. The code below is currently specific to the SGI Altix + * SHub RTC, however. + * + * 11/01/01 - jbarnes - initial revision + * 9/10/04 - Christoph Lameter - remove interrupt support for kernel inclusion + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +MODULE_AUTHOR("Jesse Barnes "); +MODULE_DESCRIPTION("Multimedia timer support"); +MODULE_LICENSE("GPL"); + +/* name of the device, usually in /dev */ +#define MMTIMER_NAME "mmtimer" +#define MMTIMER_DESC "IA-PC Multimedia Timer" +#define MMTIMER_VERSION "1.0" + +#define RTC_BITS 55 /* 55 bits for this implementation */ + +static int mmtimer_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg); +static int mmtimer_mmap(struct file *file, struct vm_area_struct *vma); + +/* + * Period in femtoseconds (10^-15 s) + */ +static unsigned long mmtimer_femtoperiod = 0; + +static struct file_operations mmtimer_fops = { + .owner = THIS_MODULE, + .mmap = mmtimer_mmap, + .ioctl = mmtimer_ioctl, +}; + +/** + * mmtimer_ioctl - ioctl interface for /dev/mmtimer + * @inode: inode of the device + * @file: file structure for the device + * @cmd: command to execute + * @arg: optional argument to command + * + * Executes the command specified by @cmd. Returns 0 for success, < 0 for + * failure. + * + * Valid commands: + * + * %MMTIMER_GETOFFSET - Should return the offset (relative to the start + * of the page where the registers are mapped) for the counter in question. + * + * %MMTIMER_GETRES - Returns the resolution of the clock in femto (10^-15) + * seconds + * + * %MMTIMER_GETFREQ - Copies the frequency of the clock in Hz to the address + * specified by @arg + * + * %MMTIMER_GETBITS - Returns the number of bits in the clock's counter + * + * %MMTIMER_MMAPAVAIL - Returns 1 if the registers can be mmap'd into userspace + * + * %MMTIMER_GETCOUNTER - Gets the current value in the counter and places it + * in the address specified by @arg. + */ +static int mmtimer_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg) +{ + int ret = 0; + + switch (cmd) { + case MMTIMER_GETOFFSET: /* offset of the counter */ + /* + * SN RTC registers are on their own 64k page + */ + if(PAGE_SIZE <= (1 << 16)) + ret = (((long)RTC_COUNTER_ADDR) & (PAGE_SIZE-1)) / 8; + else + ret = -ENOSYS; + break; + + case MMTIMER_GETRES: /* resolution of the clock in 10^-15 s */ + if(copy_to_user((unsigned long *)arg, &mmtimer_femtoperiod, + sizeof(unsigned long))) + return -EFAULT; + break; + + case MMTIMER_GETFREQ: /* frequency in Hz */ + if(copy_to_user((unsigned long *)arg, + &sn_rtc_cycles_per_second, + sizeof(unsigned long))) + return -EFAULT; + ret = 0; + break; + + case MMTIMER_GETBITS: /* number of bits in the clock */ + ret = RTC_BITS; + break; + + case MMTIMER_MMAPAVAIL: /* can we mmap the clock into userspace? */ + ret = (PAGE_SIZE <= (1 << 16)) ? 1 : 0; + break; + + case MMTIMER_GETCOUNTER: + if(copy_to_user((unsigned long *)arg, RTC_COUNTER_ADDR, + sizeof(unsigned long))) + return -EFAULT; + break; + default: + ret = -ENOSYS; + break; + } + + return ret; +} + +/** + * mmtimer_mmap - maps the clock's registers into userspace + * @file: file structure for the device + * @vma: VMA to map the registers into + * + * Calls remap_page_range() to map the clock's registers into + * the calling process' address space. + */ +static int mmtimer_mmap(struct file *file, struct vm_area_struct *vma) +{ + unsigned long mmtimer_addr; + + if (vma->vm_end - vma->vm_start != PAGE_SIZE) + return -EINVAL; + + if (vma->vm_flags & VM_WRITE) + return -EPERM; + + if (PAGE_SIZE > (1 << 16)) + return -ENOSYS; + + vma->vm_flags |= (VM_IO | VM_SHM | VM_LOCKED ); + vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot); + + mmtimer_addr = __pa(RTC_COUNTER_ADDR); + mmtimer_addr &= ~(PAGE_SIZE - 1); + mmtimer_addr &= 0xfffffffffffffffUL; + + if (remap_page_range(vma, vma->vm_start, mmtimer_addr, PAGE_SIZE, + vma->vm_page_prot)) { + printk(KERN_ERR "remap_page_range failed in mmtimer.c\n"); + return -EAGAIN; + } + + return 0; +} + +static struct miscdevice mmtimer_miscdev = { + SGI_MMTIMER, + MMTIMER_NAME, + &mmtimer_fops +}; + +/** + * mmtimer_init - device initialization routine + * + * Does initial setup for the mmtimer device. + */ +static int __init mmtimer_init(void) +{ + if (!ia64_platform_is("sn2")) + return -1; + + /* + * Sanity check the cycles/sec variable + */ + if (sn_rtc_cycles_per_second < 100000) { + printk(KERN_ERR "%s: unable to determine clock frequency\n", + MMTIMER_NAME); + return -1; + } + + mmtimer_femtoperiod = ((unsigned long)1E15 + sn_rtc_cycles_per_second / + 2) / sn_rtc_cycles_per_second; + + strcpy(mmtimer_miscdev.devfs_name, MMTIMER_NAME); + if (misc_register(&mmtimer_miscdev)) { + printk(KERN_ERR "%s: failed to register device\n", + MMTIMER_NAME); + return -1; + } + + printk(KERN_INFO "%s: v%s, %ld MHz\n", MMTIMER_DESC, MMTIMER_VERSION, + sn_rtc_cycles_per_second/(unsigned long)1E6); + + return 0; +} + +module_init(mmtimer_init); + diff --git a/drivers/char/moxa.c b/drivers/char/moxa.c index 0e75a0dc9..d45d381d4 100644 --- a/drivers/char/moxa.c +++ b/drivers/char/moxa.c @@ -618,8 +618,8 @@ static void moxa_close(struct tty_struct *tty, struct file *filp) if (tty->driver->flush_buffer) tty->driver->flush_buffer(tty); - if (tty->ldisc.flush_buffer) - tty->ldisc.flush_buffer(tty); + tty_ldisc_flush(tty); + tty->closing = 0; ch->event = 0; ch->tty = NULL; @@ -693,9 +693,7 @@ static void moxa_flush_buffer(struct tty_struct *tty) if (ch == NULL) return; MoxaPortFlushData(ch->port, 1); - if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && - tty->ldisc.write_wakeup) - (tty->ldisc.write_wakeup) (tty); + tty_wakeup(tty); wake_up_interruptible(&tty->write_wait); } @@ -954,9 +952,7 @@ static void moxa_poll(unsigned long ignored) if (MoxaPortTxQueue(ch->port) <= WAKEUP_CHARS) { if (!tp->stopped) { ch->statusflags &= ~LOWWAIT; - if ((tp->flags & (1 << TTY_DO_WRITE_WAKEUP)) && - tp->ldisc.write_wakeup) - (tp->ldisc.write_wakeup) (tp); + tty_wakeup(tp); wake_up_interruptible(&tp->write_wait); } } @@ -1123,9 +1119,7 @@ static void check_xmit_empty(unsigned long data) if (ch->tty && (ch->statusflags & EMPTYWAIT)) { if (MoxaPortTxQueue(ch->port) == 0) { ch->statusflags &= ~EMPTYWAIT; - if ((ch->tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && - ch->tty->ldisc.write_wakeup) - (ch->tty->ldisc.write_wakeup) (ch->tty); + tty_wakeup(ch->tty); wake_up_interruptible(&ch->tty->write_wait); return; } diff --git a/drivers/char/nwflash.c b/drivers/char/nwflash.c index e75381ec1..f344ed315 100644 --- a/drivers/char/nwflash.c +++ b/drivers/char/nwflash.c @@ -60,15 +60,6 @@ static DECLARE_MUTEX(nwflash_sem); extern spinlock_t gpio_lock; -/* - * the delay routine - it is often required to let the flash "breeze"... - */ -void flash_wait(int timeout) -{ - current->state = TASK_INTERRUPTIBLE; - schedule_timeout(timeout); -} - static int get_flash_id(void) { volatile unsigned int c1, c2; @@ -401,7 +392,7 @@ static int erase_block(int nBlock) /* * wait 10 ms */ - flash_wait(HZ / 100); + msleep(10); /* * wait while erasing in process (up to 10 sec) @@ -409,7 +400,7 @@ static int erase_block(int nBlock) timeout = jiffies + 10 * HZ; c1 = 0; while (!(c1 & 0x80) && time_before(jiffies, timeout)) { - flash_wait(HZ / 100); + msleep(10); /* * read any address */ @@ -440,7 +431,7 @@ static int erase_block(int nBlock) /* * just to make sure - verify if erased OK... */ - flash_wait(HZ / 100); + msleep(10); pWritePtr = (unsigned char *) ((unsigned int) (FLASH_BASE + (nBlock << 16))); @@ -587,7 +578,7 @@ static int write_block(unsigned long p, const char __user *buf, int count) /* * wait couple ms */ - flash_wait(HZ / 100); + msleep(10); /* * red LED == write */ @@ -612,7 +603,7 @@ static int write_block(unsigned long p, const char __user *buf, int count) leds_event(led_amber_off); leds_event(led_green_on); - flash_wait(HZ / 100); + msleep(10); pWritePtr = (unsigned char *) ((unsigned int) (FLASH_BASE + p)); diff --git a/drivers/char/pcxx.c b/drivers/char/pcxx.c index e804fdc73..061237bb0 100644 --- a/drivers/char/pcxx.c +++ b/drivers/char/pcxx.c @@ -130,7 +130,6 @@ static struct channel *digi_channels; int pcxx_ncook=sizeof(pcxx_cook); int pcxx_nbios=sizeof(pcxx_bios); -#define MIN(a,b) ((a) < (b) ? (a) : (b)) #define pcxxassert(x, msg) if(!(x)) pcxx_error(__LINE__, msg) #define FEPTIMEOUT 200000 @@ -206,7 +205,7 @@ static void __exit pcxe_cleanup(void) { unsigned long flags; - int e1, e2; + int e1; printk(KERN_NOTICE "Unloading PC/Xx version %s\n", VERSION); @@ -223,12 +222,6 @@ static void __exit pcxe_cleanup(void) restore_flags(flags); } -/* - * pcxe_init() is our init_module(): - */ -module_init(pcxe_init); -module_cleanup(pcxe_cleanup); - static inline struct channel *chan(register struct tty_struct *tty) { if (tty) { @@ -538,28 +531,11 @@ static void pcxe_close(struct tty_struct * tty, struct file * filp) if(tty->driver->flush_buffer) tty->driver->flush_buffer(tty); - if(tty->ldisc.flush_buffer) - tty->ldisc.flush_buffer(tty); + tty_ldisc_flush(tty); shutdown(info); tty->closing = 0; info->event = 0; info->tty = NULL; -#ifndef MODULE -/* ldiscs[] is not available in a MODULE -** worth noting that while I'm not sure what this hunk of code is supposed -** to do, it is not present in the serial.c driver. Hmmm. If you know, -** please send me a note. brian@ilinx.com -** Don't know either what this is supposed to do christoph@lameter.com. -*/ - if(tty->ldisc.num != ldiscs[N_TTY].num) { - if(tty->ldisc.close) - (tty->ldisc.close)(tty); - tty->ldisc = ldiscs[N_TTY]; - tty->termios->c_line = N_TTY; - if(tty->ldisc.open) - (tty->ldisc.open)(tty); - } -#endif if(info->blocked_open) { if(info->close_delay) { current->state = TASK_INTERRUPTIBLE; @@ -626,7 +602,7 @@ static int pcxe_write(struct tty_struct * tty, int from_user, const unsigned cha tail &= (size - 1); stlen = (head >= tail) ? (size - (head - tail) - 1) : (tail - head - 1); - count = MIN(stlen, count); + count = min(stlen, count); memoff(ch); restore_flags(flags); @@ -658,11 +634,11 @@ static int pcxe_write(struct tty_struct * tty, int from_user, const unsigned cha remain = tail - head - 1; stlen = remain; } - count = MIN(remain, count); + count = min(remain, count); txwinon(ch); while (count > 0) { - stlen = MIN(count, stlen); + stlen = min(count, stlen); memcpy(ch->txptr + head, buf, stlen); buf += stlen; count -= stlen; @@ -800,9 +776,7 @@ static void pcxe_flush_buffer(struct tty_struct *tty) memoff(ch); restore_flags(flags); - wake_up_interruptible(&tty->write_wait); - if((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && tty->ldisc.write_wakeup) - (tty->ldisc.write_wakeup)(tty); + tty_wakeup(tty); } static void pcxe_flush_chars(struct tty_struct *tty) @@ -1013,9 +987,6 @@ void __init pcxx_setup(char *str, int *ints) } #endif -module_init(pcxe_init) -module_exit(pcxe_exit) - static struct tty_operations pcxe_ops = { .open = pcxe_open, .close = pcxe_close, @@ -1561,6 +1532,8 @@ cleanup_boards: return ret; } +module_init(pcxe_init) +module_exit(pcxe_cleanup) static void pcxxpoll(unsigned long dummy) { @@ -1675,10 +1648,7 @@ static void doevent(int crd) if (event & LOWTX_IND) { if (ch->statusflags & LOWWAIT) { ch->statusflags &= ~LOWWAIT; - if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && - tty->ldisc.write_wakeup) - (tty->ldisc.write_wakeup)(tty); - wake_up_interruptible(&tty->write_wait); + tty_wakeup(tty); } } @@ -1686,10 +1656,7 @@ static void doevent(int crd) ch->statusflags &= ~TXBUSY; if (ch->statusflags & EMPTYWAIT) { ch->statusflags &= ~EMPTYWAIT; - if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && - tty->ldisc.write_wakeup) - (tty->ldisc.write_wakeup)(tty); - wake_up_interruptible(&tty->write_wait); + tty_wakeup(tty); } } } @@ -1995,6 +1962,7 @@ static int pcxe_tiocmget(struct tty_struct *tty, struct file *file) volatile struct board_chan *bc; unsigned long flags; int mflag = 0; + int mstat; if(ch) bc = ch->brdchan; @@ -2069,6 +2037,7 @@ static int pcxe_tiocmset(struct tty_struct *tty, struct file *file, pcxxparam(tty,ch); memoff(ch); restore_flags(flags); + return 0; } @@ -2165,8 +2134,7 @@ static int pcxe_ioctl(struct tty_struct *tty, struct file * file, tty_wait_until_sent(tty, 0); } else { - if(tty->ldisc.flush_buffer) - tty->ldisc.flush_buffer(tty); + tty_ldisc_flush(tty); } /* Fall Thru */ diff --git a/drivers/char/rio/linux_compat.h b/drivers/char/rio/linux_compat.h index 26063cb6a..d53843abe 100644 --- a/drivers/char/rio/linux_compat.h +++ b/drivers/char/rio/linux_compat.h @@ -16,7 +16,7 @@ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ -#include +#include #define disable(oldspl) save_flags (oldspl) diff --git a/drivers/char/rio/rio_linux.c b/drivers/char/rio/rio_linux.c index a11fafbab..0cc02371a 100644 --- a/drivers/char/rio/rio_linux.c +++ b/drivers/char/rio/rio_linux.c @@ -1138,8 +1138,8 @@ static int __init rio_init(void) hp->Ivec = pdev->irq; if (((1 << hp->Ivec) & rio_irqmask) == 0) hp->Ivec = 0; - hp->CardP = (struct DpRam *) hp->Caddr = ioremap(p->RIOHosts[p->RIONumHosts].PaddrP, RIO_WINDOW_LEN); + hp->CardP = (struct DpRam *) hp->Caddr; hp->Type = RIO_PCI; hp->Copy = rio_pcicopy; hp->Mode = RIO_PCI_BOOT_FROM_RAM; @@ -1196,8 +1196,8 @@ static int __init rio_init(void) if (((1 << hp->Ivec) & rio_irqmask) == 0) hp->Ivec = 0; hp->Ivec |= 0x8000; /* Mark as non-sharable */ - hp->CardP = (struct DpRam *) hp->Caddr = ioremap(p->RIOHosts[p->RIONumHosts].PaddrP, RIO_WINDOW_LEN); + hp->CardP = (struct DpRam *) hp->Caddr; hp->Type = RIO_PCI; hp->Copy = rio_pcicopy; hp->Mode = RIO_PCI_BOOT_FROM_RAM; @@ -1242,8 +1242,8 @@ static int __init rio_init(void) hp->PaddrP = rio_probe_addrs[i]; /* There was something about the IRQs of these cards. 'Forget what.--REW */ hp->Ivec = 0; - hp->CardP = (struct DpRam *) hp->Caddr = ioremap(p->RIOHosts[p->RIONumHosts].PaddrP, RIO_WINDOW_LEN); + hp->CardP = (struct DpRam *) hp->Caddr; hp->Type = RIO_AT; hp->Copy = rio_pcicopy; /* AT card PCI???? - PVDL * -- YES! this is now a normal copy. Only the diff --git a/drivers/char/riscom8.c b/drivers/char/riscom8.c index 69108ceea..a35ea03a3 100644 --- a/drivers/char/riscom8.c +++ b/drivers/char/riscom8.c @@ -75,10 +75,6 @@ ASYNC_SPD_HI | ASYNC_SPEED_VHI | ASYNC_SESSION_LOCKOUT | \ ASYNC_PGRP_LOCKOUT | ASYNC_CALLOUT_NOHUP) -#ifndef MIN -#define MIN(a,b) ((a) < (b) ? (a) : (b)) -#endif - #define RS_EVENT_WRITE_WAKEUP 0 static struct riscom_board * IRQ_to_board[16]; @@ -107,7 +103,7 @@ static struct riscom_board rc_board[RC_NBOARD] = { }; static struct riscom_port rc_port[RC_NBOARD * RC_NPORT]; - + /* RISCom/8 I/O ports addresses (without address translation) */ static unsigned short rc_ioport[] = { #if 1 @@ -483,7 +479,7 @@ static inline void rc_transmit(struct riscom_board const * bp) rc_out(bp, CD180_TDR, CD180_C_SBRK); port->COR2 &= ~COR2_ETC; } - count = MIN(port->break_length, 0xff); + count = min_t(int, port->break_length, 0xff); rc_out(bp, CD180_TDR, CD180_C_ESC); rc_out(bp, CD180_TDR, CD180_C_DELAY); rc_out(bp, CD180_TDR, count); @@ -1127,8 +1123,8 @@ static void rc_close(struct tty_struct * tty, struct file * filp) rc_shutdown_port(bp, port); if (tty->driver->flush_buffer) tty->driver->flush_buffer(tty); - if (tty->ldisc.flush_buffer) - tty->ldisc.flush_buffer(tty); + tty_ldisc_flush(tty); + tty->closing = 0; port->event = 0; port->tty = NULL; @@ -1165,8 +1161,8 @@ static int rc_write(struct tty_struct * tty, int from_user, down(&tmp_buf_sem); while (1) { cli(); - c = MIN(count, MIN(SERIAL_XMIT_SIZE - port->xmit_cnt - 1, - SERIAL_XMIT_SIZE - port->xmit_head)); + c = min_t(int, count, min(SERIAL_XMIT_SIZE - port->xmit_cnt - 1, + SERIAL_XMIT_SIZE - port->xmit_head)); if (c <= 0) break; @@ -1178,8 +1174,8 @@ static int rc_write(struct tty_struct * tty, int from_user, } cli(); - c = MIN(c, MIN(SERIAL_XMIT_SIZE - port->xmit_cnt - 1, - SERIAL_XMIT_SIZE - port->xmit_head)); + c = min_t(int, c, min(SERIAL_XMIT_SIZE - port->xmit_cnt - 1, + SERIAL_XMIT_SIZE - port->xmit_head)); memcpy(port->xmit_buf + port->xmit_head, tmp_buf, c); port->xmit_head = (port->xmit_head + c) & (SERIAL_XMIT_SIZE-1); port->xmit_cnt += c; @@ -1193,8 +1189,8 @@ static int rc_write(struct tty_struct * tty, int from_user, } else { while (1) { cli(); - c = MIN(count, MIN(SERIAL_XMIT_SIZE - port->xmit_cnt - 1, - SERIAL_XMIT_SIZE - port->xmit_head)); + c = min_t(int, count, min(SERIAL_XMIT_SIZE - port->xmit_cnt - 1, + SERIAL_XMIT_SIZE - port->xmit_head)); if (c <= 0) { restore_flags(flags); break; @@ -1301,9 +1297,7 @@ static void rc_flush_buffer(struct tty_struct *tty) restore_flags(flags); wake_up_interruptible(&tty->write_wait); - if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && - tty->ldisc.write_wakeup) - (tty->ldisc.write_wakeup)(tty); + tty_wakeup(tty); } static int rc_tiocmget(struct tty_struct *tty, struct file *file) @@ -1644,9 +1638,7 @@ static void do_softint(void *private_) return; if (test_and_clear_bit(RS_EVENT_WRITE_WAKEUP, &port->event)) { - if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && - tty->ldisc.write_wakeup) - (tty->ldisc.write_wakeup)(tty); + tty_wakeup(tty); wake_up_interruptible(&tty->write_wait); } } diff --git a/drivers/char/rocket_int.h b/drivers/char/rocket_int.h index 9380ff53f..802687290 100644 --- a/drivers/char/rocket_int.h +++ b/drivers/char/rocket_int.h @@ -1241,10 +1241,6 @@ struct r_port { #define TTY_ROCKET_MAJOR 46 #define CUA_ROCKET_MAJOR 47 -#ifndef MIN -#define MIN(a,b) ((a) < (b) ? (a) : (b)) -#endif - #ifdef PCI_VENDOR_ID_RP #undef PCI_VENDOR_ID_RP #undef PCI_DEVICE_ID_RP8OCTA diff --git a/drivers/char/ser_a2232.c b/drivers/char/ser_a2232.c index f0b966876..6b4e9d155 100644 --- a/drivers/char/ser_a2232.c +++ b/drivers/char/ser_a2232.c @@ -599,10 +599,7 @@ int ch, err, n, p; /* WakeUp if output buffer runs low */ if ((port->gs.xmit_cnt <= port->gs.wakeup_chars) && port->gs.tty) { - if ((port->gs.tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && port->gs.tty->ldisc.write_wakeup){ - (port->gs.tty->ldisc.write_wakeup)(port->gs.tty); - } - wake_up_interruptible(&port->gs.tty->write_wait); + tty_wakeup(port->gs.tty); } } // if the port is used } // for every port on the board diff --git a/drivers/char/serial167.c b/drivers/char/serial167.c index 1b51eecfb..885621edb 100644 --- a/drivers/char/serial167.c +++ b/drivers/char/serial167.c @@ -83,10 +83,6 @@ #undef CYCLOM_16Y_HACK #define CYCLOM_ENABLE_MONITORING -#ifndef MIN -#define MIN(a,b) ((a) < (b) ? (a) : (b)) -#endif - #define WAKEUP_CHARS 256 #define STD_COM_FLAGS (0) @@ -760,11 +756,7 @@ do_softint(void *private_) wake_up_interruptible(&info->open_wait); } if (test_and_clear_bit(Cy_EVENT_WRITE_WAKEUP, &info->event)) { - if((tty->flags & (1<< TTY_DO_WRITE_WAKEUP)) - && tty->ldisc.write_wakeup){ - (tty->ldisc.write_wakeup)(tty); - } - wake_up_interruptible(&tty->write_wait); + tty_wakeup(tty); } } /* do_softint */ @@ -1238,8 +1230,8 @@ cy_write(struct tty_struct * tty, int from_user, if (from_user) { down(&tmp_buf_sem); while (1) { - c = MIN(count, MIN(SERIAL_XMIT_SIZE - info->xmit_cnt - 1, - SERIAL_XMIT_SIZE - info->xmit_head)); + c = min_t(int, count, min(SERIAL_XMIT_SIZE - info->xmit_cnt - 1, + SERIAL_XMIT_SIZE - info->xmit_head)); if (c <= 0) break; @@ -1251,8 +1243,8 @@ cy_write(struct tty_struct * tty, int from_user, } local_irq_save(flags); - c = MIN(c, MIN(SERIAL_XMIT_SIZE - info->xmit_cnt - 1, - SERIAL_XMIT_SIZE - info->xmit_head)); + c = min_t(int, c, min(SERIAL_XMIT_SIZE - info->xmit_cnt - 1, + SERIAL_XMIT_SIZE - info->xmit_head)); memcpy(info->xmit_buf + info->xmit_head, tmp_buf, c); info->xmit_head = (info->xmit_head + c) & (SERIAL_XMIT_SIZE-1); info->xmit_cnt += c; @@ -1266,8 +1258,8 @@ cy_write(struct tty_struct * tty, int from_user, } else { while (1) { local_irq_save(flags); - c = MIN(count, MIN(SERIAL_XMIT_SIZE - info->xmit_cnt - 1, - SERIAL_XMIT_SIZE - info->xmit_head)); + c = min_t(int, count, min(SERIAL_XMIT_SIZE - info->xmit_cnt - 1, + SERIAL_XMIT_SIZE - info->xmit_head)); if (c <= 0) { local_irq_restore(flags); break; @@ -1343,10 +1335,7 @@ cy_flush_buffer(struct tty_struct *tty) local_irq_save(flags); info->xmit_cnt = info->xmit_head = info->xmit_tail = 0; local_irq_restore(flags); - wake_up_interruptible(&tty->write_wait); - if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) - && tty->ldisc.write_wakeup) - (tty->ldisc.write_wakeup)(tty); + tty_wakeup(tty); } /* cy_flush_buffer */ @@ -1846,18 +1835,9 @@ cy_close(struct tty_struct * tty, struct file * filp) shutdown(info); if (tty->driver->flush_buffer) tty->driver->flush_buffer(tty); - if (tty->ldisc.flush_buffer) - tty->ldisc.flush_buffer(tty); + tty_ldisc_flush(tty); info->event = 0; info->tty = 0; - if (tty->ldisc.num != ldiscs[N_TTY].num) { - if (tty->ldisc.close) - (tty->ldisc.close)(tty); - tty->ldisc = ldiscs[N_TTY]; - tty->termios->c_line = N_TTY; - if (tty->ldisc.open) - (tty->ldisc.open)(tty); - } if (info->blocked_open) { if (info->close_delay) { current->state = TASK_INTERRUPTIBLE; @@ -2379,7 +2359,7 @@ scrn[1] = '\0'; | CyPARITY| CyFRAME| CyOVERRUN; /* info->timeout */ - printk("ttyS%1d ", info->line); + printk("ttyS%d ", info->line); port_num++;info++; if(!(port_num & 7)){ printk("\n "); diff --git a/drivers/char/serial_tx3912.c b/drivers/char/serial_tx3912.c index 39717ed68..50ed6dfec 100644 --- a/drivers/char/serial_tx3912.c +++ b/drivers/char/serial_tx3912.c @@ -191,12 +191,9 @@ static inline void transmit_char_pio(struct rs_port *port) } if (port->gs.xmit_cnt <= port->gs.wakeup_chars) { - if ((port->gs.tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && - port->gs.tty->ldisc.write_wakeup) - (port->gs.tty->ldisc.write_wakeup)(port->gs.tty); + tty_wakeup(port->gs.tty); rs_dprintk (TX3912_UART_DEBUG_TRANSMIT, "Waking up.... ldisc (%d)....\n", port->gs.wakeup_chars); - wake_up_interruptible(&port->gs.tty->write_wait); } } diff --git a/drivers/char/snsc.c b/drivers/char/snsc.c new file mode 100644 index 000000000..25917e08e --- /dev/null +++ b/drivers/char/snsc.c @@ -0,0 +1,456 @@ +/* + * SN Platform system controller communication support + * + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file "COPYING" in the main directory of this archive + * for more details. + * + * Copyright (C) 2004 Silicon Graphics, Inc. All rights reserved. + */ + +/* + * System controller communication driver + * + * This driver allows a user process to communicate with the system + * controller (a.k.a. "IRouter") network in an SGI SN system. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include "snsc.h" + +#define SYSCTL_BASENAME "snsc" + +#define SCDRV_BUFSZ 2048 +#define SCDRV_TIMEOUT 1000 + +static irqreturn_t +scdrv_interrupt(int irq, void *subch_data, struct pt_regs *regs) +{ + struct subch_data_s *sd = subch_data; + unsigned long flags; + int status; + + spin_lock_irqsave(&sd->sd_rlock, flags); + spin_lock(&sd->sd_wlock); + status = ia64_sn_irtr_intr(sd->sd_nasid, sd->sd_subch); + + if (status > 0) { + if (status & SAL_IROUTER_INTR_RECV) { + wake_up(&sd->sd_rq); + } + if (status & SAL_IROUTER_INTR_XMIT) { + ia64_sn_irtr_intr_disable + (sd->sd_nasid, sd->sd_subch, + SAL_IROUTER_INTR_XMIT); + wake_up(&sd->sd_wq); + } + } + spin_unlock(&sd->sd_wlock); + spin_unlock_irqrestore(&sd->sd_rlock, flags); + return IRQ_HANDLED; +} + +/* + * scdrv_open + * + * Reserve a subchannel for system controller communication. + */ + +static int +scdrv_open(struct inode *inode, struct file *file) +{ + struct sysctl_data_s *scd; + struct subch_data_s *sd; + int rv; + + /* look up device info for this device file */ + scd = container_of(inode->i_cdev, struct sysctl_data_s, scd_cdev); + + /* allocate memory for subchannel data */ + sd = kmalloc(sizeof (struct subch_data_s), GFP_KERNEL); + if (sd == NULL) { + printk("%s: couldn't allocate subchannel data\n", + __FUNCTION__); + return -ENOMEM; + } + + /* initialize subch_data_s fields */ + memset(sd, 0, sizeof (struct subch_data_s)); + sd->sd_nasid = scd->scd_nasid; + sd->sd_subch = ia64_sn_irtr_open(scd->scd_nasid); + + if (sd->sd_subch < 0) { + kfree(sd); + printk("%s: couldn't allocate subchannel\n", __FUNCTION__); + return -EBUSY; + } + + spin_lock_init(&sd->sd_rlock); + spin_lock_init(&sd->sd_wlock); + init_waitqueue_head(&sd->sd_rq); + init_waitqueue_head(&sd->sd_wq); + sema_init(&sd->sd_rbs, 1); + sema_init(&sd->sd_wbs, 1); + + file->private_data = sd; + + /* hook this subchannel up to the system controller interrupt */ + rv = request_irq(SGI_UART_VECTOR, scdrv_interrupt, + SA_SHIRQ | SA_INTERRUPT, + SYSCTL_BASENAME, sd); + if (rv) { + ia64_sn_irtr_close(sd->sd_nasid, sd->sd_subch); + kfree(sd); + printk("%s: irq request failed (%d)\n", __FUNCTION__, rv); + return -EBUSY; + } + + return 0; +} + +/* + * scdrv_release + * + * Release a previously-reserved subchannel. + */ + +static int +scdrv_release(struct inode *inode, struct file *file) +{ + struct subch_data_s *sd = (struct subch_data_s *) file->private_data; + int rv; + + /* free the interrupt */ + free_irq(SGI_UART_VECTOR, sd); + + /* ask SAL to close the subchannel */ + rv = ia64_sn_irtr_close(sd->sd_nasid, sd->sd_subch); + + kfree(sd); + return rv; +} + +/* + * scdrv_read + * + * Called to read bytes from the open IRouter pipe. + * + */ + +static inline int +read_status_check(struct subch_data_s *sd, int *len) +{ + return ia64_sn_irtr_recv(sd->sd_nasid, sd->sd_subch, sd->sd_rb, len); +} + +static ssize_t +scdrv_read(struct file *file, char __user *buf, size_t count, loff_t *f_pos) +{ + int status; + int len; + unsigned long flags; + struct subch_data_s *sd = (struct subch_data_s *) file->private_data; + + /* try to get control of the read buffer */ + if (down_trylock(&sd->sd_rbs)) { + /* somebody else has it now; + * if we're non-blocking, then exit... + */ + if (file->f_flags & O_NONBLOCK) { + return -EAGAIN; + } + /* ...or if we want to block, then do so here */ + if (down_interruptible(&sd->sd_rbs)) { + /* something went wrong with wait */ + return -ERESTARTSYS; + } + } + + /* anything to read? */ + len = CHUNKSIZE; + spin_lock_irqsave(&sd->sd_rlock, flags); + status = read_status_check(sd, &len); + + /* if not, and we're blocking I/O, loop */ + while (status < 0) { + DECLARE_WAITQUEUE(wait, current); + + if (file->f_flags & O_NONBLOCK) { + spin_unlock_irqrestore(&sd->sd_rlock, flags); + up(&sd->sd_rbs); + return -EAGAIN; + } + + len = CHUNKSIZE; + add_wait_queue(&sd->sd_rq, &wait); + set_current_state(TASK_INTERRUPTIBLE); + spin_unlock_irqrestore(&sd->sd_rlock, flags); + + schedule_timeout(SCDRV_TIMEOUT); + + remove_wait_queue(&sd->sd_rq, &wait); + if (signal_pending(current)) { + /* wait was interrupted */ + up(&sd->sd_rbs); + return -ERESTARTSYS; + } + + spin_lock_irqsave(&sd->sd_rlock, flags); + status = read_status_check(sd, &len); + } + spin_unlock_irqrestore(&sd->sd_rlock, flags); + + if (len > 0) { + /* we read something in the last read_status_check(); copy + * it out to user space + */ + if (count < len) { + pr_debug("%s: only accepting %d of %d bytes\n", + __FUNCTION__, (int) count, len); + } + len = min((int) count, len); + if (copy_to_user(buf, sd->sd_rb, len)) + len = -EFAULT; + } + + /* release the read buffer and wake anyone who might be + * waiting for it + */ + up(&sd->sd_rbs); + + /* return the number of characters read in */ + return len; +} + +/* + * scdrv_write + * + * Writes a chunk of an IRouter packet (or other system controller data) + * to the system controller. + * + */ +static inline int +write_status_check(struct subch_data_s *sd, int count) +{ + return ia64_sn_irtr_send(sd->sd_nasid, sd->sd_subch, sd->sd_wb, count); +} + +static ssize_t +scdrv_write(struct file *file, const char __user *buf, + size_t count, loff_t *f_pos) +{ + unsigned long flags; + int status; + struct subch_data_s *sd = (struct subch_data_s *) file->private_data; + + /* try to get control of the write buffer */ + if (down_trylock(&sd->sd_wbs)) { + /* somebody else has it now; + * if we're non-blocking, then exit... + */ + if (file->f_flags & O_NONBLOCK) { + return -EAGAIN; + } + /* ...or if we want to block, then do so here */ + if (down_interruptible(&sd->sd_wbs)) { + /* something went wrong with wait */ + return -ERESTARTSYS; + } + } + + count = min((int) count, CHUNKSIZE); + if (copy_from_user(sd->sd_wb, buf, count)) { + up(&sd->sd_wbs); + return -EFAULT; + } + + /* try to send the buffer */ + spin_lock_irqsave(&sd->sd_wlock, flags); + status = write_status_check(sd, count); + + /* if we failed, and we want to block, then loop */ + while (status <= 0) { + DECLARE_WAITQUEUE(wait, current); + + if (file->f_flags & O_NONBLOCK) { + spin_unlock(&sd->sd_wlock); + up(&sd->sd_wbs); + return -EAGAIN; + } + + add_wait_queue(&sd->sd_wq, &wait); + set_current_state(TASK_INTERRUPTIBLE); + spin_unlock_irqrestore(&sd->sd_wlock, flags); + + schedule_timeout(SCDRV_TIMEOUT); + + remove_wait_queue(&sd->sd_wq, &wait); + if (signal_pending(current)) { + /* wait was interrupted */ + up(&sd->sd_wbs); + return -ERESTARTSYS; + } + + spin_lock_irqsave(&sd->sd_wlock, flags); + status = write_status_check(sd, count); + } + spin_unlock_irqrestore(&sd->sd_wlock, flags); + + /* release the write buffer and wake anyone who's waiting for it */ + up(&sd->sd_wbs); + + /* return the number of characters accepted (should be the complete + * "chunk" as requested) + */ + if ((status >= 0) && (status < count)) { + pr_debug("Didn't accept the full chunk; %d of %d\n", + status, (int) count); + } + return status; +} + +static unsigned int +scdrv_poll(struct file *file, struct poll_table_struct *wait) +{ + unsigned int mask = 0; + int status = 0; + struct subch_data_s *sd = (struct subch_data_s *) file->private_data; + unsigned long flags; + + poll_wait(file, &sd->sd_rq, wait); + poll_wait(file, &sd->sd_wq, wait); + + spin_lock_irqsave(&sd->sd_rlock, flags); + spin_lock(&sd->sd_wlock); + status = ia64_sn_irtr_intr(sd->sd_nasid, sd->sd_subch); + spin_unlock(&sd->sd_wlock); + spin_unlock_irqrestore(&sd->sd_rlock, flags); + + if (status > 0) { + if (status & SAL_IROUTER_INTR_RECV) { + mask |= POLLIN | POLLRDNORM; + } + if (status & SAL_IROUTER_INTR_XMIT) { + mask |= POLLOUT | POLLWRNORM; + } + } + + return mask; +} + +static struct file_operations scdrv_fops = { + .owner = THIS_MODULE, + .read = scdrv_read, + .write = scdrv_write, + .poll = scdrv_poll, + .open = scdrv_open, + .release = scdrv_release, +}; + +/* + * scdrv_init + * + * Called at boot time to initialize the system controller communication + * facility. + */ +int __init +scdrv_init(void) +{ + geoid_t geoid; + cmoduleid_t cmod; + int i; + char devname[32]; + char *devnamep; + module_t *m; + struct sysctl_data_s *scd; + void *salbuf; + struct class_simple *snsc_class; + dev_t first_dev, dev; + + if (alloc_chrdev_region(&first_dev, 0, (MAX_SLABS*nummodules), + SYSCTL_BASENAME) < 0) { + printk("%s: failed to register SN system controller device\n", + __FUNCTION__); + return -ENODEV; + } + snsc_class = class_simple_create(THIS_MODULE, SYSCTL_BASENAME); + + for (cmod = 0; cmod < nummodules; cmod++) { + m = sn_modules[cmod]; + for (i = 0; i <= MAX_SLABS; i++) { + + if (m->nodes[i] == -1) { + /* node is not alive in module */ + continue; + } + + geoid = m->geoid[i]; + devnamep = devname; + format_module_id(devnamep, geo_module(geoid), + MODULE_FORMAT_BRIEF); + devnamep = devname + strlen(devname); + sprintf(devnamep, "#%d", geo_slab(geoid)); + + /* allocate sysctl device data */ + scd = kmalloc(sizeof (struct sysctl_data_s), + GFP_KERNEL); + if (!scd) { + printk("%s: failed to allocate device info" + "for %s/%s\n", __FUNCTION__, + SYSCTL_BASENAME, devname); + continue; + } + memset(scd, 0, sizeof (struct sysctl_data_s)); + + /* initialize sysctl device data fields */ + scd->scd_nasid = cnodeid_to_nasid(m->nodes[i]); + if (!(salbuf = kmalloc(SCDRV_BUFSZ, GFP_KERNEL))) { + printk("%s: failed to allocate driver buffer" + "(%s%s)\n", __FUNCTION__, + SYSCTL_BASENAME, devname); + kfree(scd); + continue; + } + + if (ia64_sn_irtr_init(scd->scd_nasid, salbuf, + SCDRV_BUFSZ) < 0) { + printk + ("%s: failed to initialize SAL for" + " system controller communication" + " (%s/%s): outdated PROM?\n", + __FUNCTION__, SYSCTL_BASENAME, devname); + kfree(scd); + kfree(salbuf); + continue; + } + + dev = first_dev + m->nodes[i]; + cdev_init(&scd->scd_cdev, &scdrv_fops); + if (cdev_add(&scd->scd_cdev, dev, 1)) { + printk("%s: failed to register system" + " controller device (%s%s)\n", + __FUNCTION__, SYSCTL_BASENAME, devname); + kfree(scd); + kfree(salbuf); + continue; + } + + class_simple_device_add(snsc_class, dev, NULL, + "%s", devname); + + ia64_sn_irtr_intr_enable(scd->scd_nasid, + 0 /*ignored */ , + SAL_IROUTER_INTR_RECV); + } + } + return 0; +} + +module_init(scdrv_init); diff --git a/drivers/char/snsc.h b/drivers/char/snsc.h new file mode 100644 index 000000000..c22c6c55e --- /dev/null +++ b/drivers/char/snsc.h @@ -0,0 +1,50 @@ +/* + * SN Platform system controller communication support + * + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file "COPYING" in the main directory of this archive + * for more details. + * + * Copyright (C) 2004 Silicon Graphics, Inc. All rights reserved. + */ + +/* + * This file contains macros and data types for communication with the + * system controllers in SGI SN systems. + */ + +#ifndef _SN_SYSCTL_H_ +#define _SN_SYSCTL_H_ + +#include +#include +#include +#include +#include +#include +#include +#include + +#define CHUNKSIZE 127 + +/* This structure is used to track an open subchannel. */ +struct subch_data_s { + nasid_t sd_nasid; /* node on which the subchannel was opened */ + int sd_subch; /* subchannel number */ + spinlock_t sd_rlock; /* monitor lock for rsv */ + spinlock_t sd_wlock; /* monitor lock for wsv */ + wait_queue_head_t sd_rq; /* wait queue for readers */ + wait_queue_head_t sd_wq; /* wait queue for writers */ + struct semaphore sd_rbs; /* semaphore for read buffer */ + struct semaphore sd_wbs; /* semaphore for write buffer */ + + char sd_rb[CHUNKSIZE]; /* read buffer */ + char sd_wb[CHUNKSIZE]; /* write buffer */ +}; + +struct sysctl_data_s { + struct cdev scd_cdev; /* Character device info */ + nasid_t scd_nasid; /* Node on which subchannels are opened. */ +}; + +#endif /* _SN_SYSCTL_H_ */ diff --git a/drivers/char/specialix.c b/drivers/char/specialix.c index 0d3763dc6..bf82f06d4 100644 --- a/drivers/char/specialix.c +++ b/drivers/char/specialix.c @@ -135,10 +135,6 @@ ASYNC_SPD_HI | ASYNC_SPEED_VHI | ASYNC_SESSION_LOCKOUT | \ ASYNC_PGRP_LOCKOUT | ASYNC_CALLOUT_NOHUP) -#ifndef MIN -#define MIN(a,b) ((a) < (b) ? (a) : (b)) -#endif - #undef RS_EVENT_WRITE_WAKEUP #define RS_EVENT_WRITE_WAKEUP 0 @@ -159,7 +155,7 @@ static struct specialix_board sx_board[SX_NBOARD] = { }; static struct specialix_port sx_port[SX_NBOARD * SX_NPORT]; - + #ifdef SPECIALIX_TIMER static struct timer_list missed_irq_timer; @@ -715,7 +711,7 @@ static inline void sx_transmit(struct specialix_board * bp) sx_out(bp, CD186x_TDR, CD186x_C_SBRK); port->COR2 &= ~COR2_ETC; } - count = MIN(port->break_length, 0xff); + count = min_t(int, port->break_length, 0xff); sx_out(bp, CD186x_TDR, CD186x_C_ESC); sx_out(bp, CD186x_TDR, CD186x_C_DELAY); sx_out(bp, CD186x_TDR, count); @@ -1468,8 +1464,7 @@ static void sx_close(struct tty_struct * tty, struct file * filp) sx_shutdown_port(bp, port); if (tty->driver->flush_buffer) tty->driver->flush_buffer(tty); - if (tty->ldisc.flush_buffer) - tty->ldisc.flush_buffer(tty); + tty_ldisc_flush(tty); tty->closing = 0; port->event = 0; port->tty = NULL; @@ -1506,7 +1501,7 @@ static int sx_write(struct tty_struct * tty, int from_user, if (from_user) { down(&tmp_buf_sem); while (1) { - c = MIN(count, MIN(SERIAL_XMIT_SIZE - port->xmit_cnt - 1, + c = min_t(int, count, min(SERIAL_XMIT_SIZE - port->xmit_cnt - 1, SERIAL_XMIT_SIZE - port->xmit_head)); if (c <= 0) break; @@ -1519,7 +1514,7 @@ static int sx_write(struct tty_struct * tty, int from_user, } cli(); - c = MIN(c, MIN(SERIAL_XMIT_SIZE - port->xmit_cnt - 1, + c = min_t(int, c, min(SERIAL_XMIT_SIZE - port->xmit_cnt - 1, SERIAL_XMIT_SIZE - port->xmit_head)); memcpy(port->xmit_buf + port->xmit_head, tmp_buf, c); port->xmit_head = (port->xmit_head + c) & (SERIAL_XMIT_SIZE-1); @@ -1534,7 +1529,7 @@ static int sx_write(struct tty_struct * tty, int from_user, } else { while (1) { cli(); - c = MIN(count, MIN(SERIAL_XMIT_SIZE - port->xmit_cnt - 1, + c = min_t(int, count, min(SERIAL_XMIT_SIZE - port->xmit_cnt - 1, SERIAL_XMIT_SIZE - port->xmit_head)); if (c <= 0) { restore_flags(flags); @@ -1646,10 +1641,8 @@ static void sx_flush_buffer(struct tty_struct *tty) port->xmit_cnt = port->xmit_head = port->xmit_tail = 0; restore_flags(flags); + tty_wakeup(tty); wake_up_interruptible(&tty->write_wait); - if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && - tty->ldisc.write_wakeup) - (tty->ldisc.write_wakeup)(tty); } @@ -2052,12 +2045,8 @@ static void do_softint(void *private_) if(!(tty = port->tty)) return; - if (test_and_clear_bit(RS_EVENT_WRITE_WAKEUP, &port->event)) { - if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && - tty->ldisc.write_wakeup) - (tty->ldisc.write_wakeup)(tty); - wake_up_interruptible(&tty->write_wait); - } + if (test_and_clear_bit(RS_EVENT_WRITE_WAKEUP, &port->event)) + tty_wakeup(tty); } static struct tty_operations sx_ops = { diff --git a/drivers/char/sx.c b/drivers/char/sx.c index 1ca3d2543..a990eb7aa 100644 --- a/drivers/char/sx.c +++ b/drivers/char/sx.c @@ -1046,12 +1046,9 @@ static void sx_transmit_chars (struct sx_port *port) } if ((port->gs.xmit_cnt <= port->gs.wakeup_chars) && port->gs.tty) { - if ((port->gs.tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && - port->gs.tty->ldisc.write_wakeup) - (port->gs.tty->ldisc.write_wakeup)(port->gs.tty); + tty_wakeup(port->gs.tty); sx_dprintk (SX_DEBUG_TRANSMIT, "Waking up.... ldisc (%d)....\n", port->gs.wakeup_chars); - wake_up_interruptible(&port->gs.tty->write_wait); } clear_bit (SX_PORT_TRANSMIT_LOCK, &port->locks); diff --git a/drivers/char/synclink.c b/drivers/char/synclink.c index 78d4efe6f..0a1cc1d36 100644 --- a/drivers/char/synclink.c +++ b/drivers/char/synclink.c @@ -1,7 +1,7 @@ /* * linux/drivers/char/synclink.c * - * $Id: synclink.c,v 4.24 2004/06/03 14:50:09 paulkf Exp $ + * $Id: synclink.c,v 4.28 2004/08/11 19:30:01 paulkf Exp $ * * Device driver for Microgate SyncLink ISA and PCI * high speed multiprotocol serial adapters. @@ -100,13 +100,10 @@ #include #include #include +#include -#ifdef CONFIG_SYNCLINK_SYNCPPP_MODULE -#define CONFIG_SYNCLINK_SYNCPPP 1 -#endif - -#ifdef CONFIG_SYNCLINK_SYNCPPP -#include +#ifdef CONFIG_HDLC_MODULE +#define CONFIG_HDLC 1 #endif #define GET_USER(error,value,addr) error = get_user(value,addr) @@ -187,7 +184,6 @@ struct tx_holding_buffer { */ struct mgsl_struct { - void *if_ptr; /* General purpose pointer (used by SPPP) */ int magic; int flags; int count; /* count of opens */ @@ -318,15 +314,13 @@ struct mgsl_struct { struct _input_signal_events input_signal_events; - /* SPPP/Cisco HDLC device parts */ + /* generic HDLC device parts */ int netcount; int dosyncppp; spinlock_t netlock; -#ifdef CONFIG_SYNCLINK_SYNCPPP - struct ppp_device pppdev; - char netname[10]; + +#ifdef CONFIG_HDLC struct net_device *netdev; - struct net_device_stats netstats; #endif }; @@ -734,18 +728,12 @@ int usc_loopmode_send_active( struct mgsl_struct * info ); int mgsl_ioctl_common(struct mgsl_struct *info, unsigned int cmd, unsigned long arg); -#ifdef CONFIG_SYNCLINK_SYNCPPP -/* SPPP/HDLC stuff */ -static void mgsl_sppp_init(struct mgsl_struct *info); -static void mgsl_sppp_delete(struct mgsl_struct *info); -int mgsl_sppp_open(struct net_device *d); -int mgsl_sppp_close(struct net_device *d); -void mgsl_sppp_tx_timeout(struct net_device *d); -int mgsl_sppp_tx(struct sk_buff *skb, struct net_device *d); -void mgsl_sppp_rx_done(struct mgsl_struct *info, char *buf, int size); -void mgsl_sppp_tx_done(struct mgsl_struct *info); -int mgsl_sppp_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd); -struct net_device_stats *mgsl_net_stats(struct net_device *dev); +#ifdef CONFIG_HDLC +#define dev_to_port(D) (dev_to_hdlc(D)->priv) +static void hdlcdev_tx_done(struct mgsl_struct *info); +static void hdlcdev_rx(struct mgsl_struct *info, char *buf, int size); +static int hdlcdev_init(struct mgsl_struct *info); +static void hdlcdev_exit(struct mgsl_struct *info); #endif /* @@ -863,8 +851,6 @@ static int mgsl_rxenable(struct mgsl_struct * info, int enable); static int mgsl_wait_event(struct mgsl_struct * info, int __user *mask); static int mgsl_loopmode_send_done( struct mgsl_struct * info ); -#define jiffies_from_ms(a) ((((a) * HZ)/1000)+1) - /* set non-zero on successful registration with PCI subsystem */ static int pci_registered; @@ -911,7 +897,7 @@ MODULE_PARM(txdmabufs,"1-" __MODULE_STRING(MAX_TOTAL_DEVICES) "i"); MODULE_PARM(txholdbufs,"1-" __MODULE_STRING(MAX_TOTAL_DEVICES) "i"); static char *driver_name = "SyncLink serial driver"; -static char *driver_version = "$Revision: 4.24 $"; +static char *driver_version = "$Revision: 4.28 $"; static int synclink_init_one (struct pci_dev *dev, const struct pci_device_id *ent); @@ -942,10 +928,6 @@ static struct tty_driver *serial_driver; static void mgsl_change_params(struct mgsl_struct *info); static void mgsl_wait_until_sent(struct tty_struct *tty, int timeout); -#ifndef MIN -#define MIN(a,b) ((a) < (b) ? (a) : (b)) -#endif - /* * 1st function defined in .text section. Calling this function in * init_module() followed by a breakpoint allows a remote debugger @@ -993,6 +975,29 @@ static inline int mgsl_paranoia_check(struct mgsl_struct *info, return 0; } +/** + * line discipline callback wrappers + * + * The wrappers maintain line discipline references + * while calling into the line discipline. + * + * ldisc_receive_buf - pass receive data to line discipline + */ + +static void ldisc_receive_buf(struct tty_struct *tty, + const __u8 *data, char *flags, int count) +{ + struct tty_ldisc *ld; + if (!tty) + return; + ld = tty_ldisc_ref(tty); + if (ld) { + if (ld->receive_buf) + ld->receive_buf(tty, data, flags, count); + tty_ldisc_deref(ld); + } +} + /* mgsl_stop() throttle (stop) transmitter * * Arguments: tty pointer to tty info structure @@ -1153,13 +1158,7 @@ void mgsl_bh_transmit(struct mgsl_struct *info) __FILE__,__LINE__,info->device_name); if (tty) { - if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && - tty->ldisc.write_wakeup) { - if ( debug_level >= DEBUG_LEVEL_BH ) - printk( "%s(%d):calling ldisc.write_wakeup on %s\n", - __FILE__,__LINE__,info->device_name); - (tty->ldisc.write_wakeup)(tty); - } + tty_wakeup(tty); wake_up_interruptible(&tty->write_wait); } @@ -1289,9 +1288,9 @@ void mgsl_isr_transmit_status( struct mgsl_struct *info ) info->drop_rts_on_tx_done = 0; } -#ifdef CONFIG_SYNCLINK_SYNCPPP +#ifdef CONFIG_HDLC if (info->netcount) - mgsl_sppp_tx_done(info); + hdlcdev_tx_done(info); else #endif { @@ -1352,12 +1351,12 @@ void mgsl_isr_io_pin( struct mgsl_struct *info ) icount->dcd++; if (status & MISCSTATUS_DCD) { info->input_signal_events.dcd_up++; -#ifdef CONFIG_SYNCLINK_SYNCPPP - if (info->netcount) - sppp_reopen(info->netdev); -#endif } else info->input_signal_events.dcd_down++; +#ifdef CONFIG_HDLC + if (info->netcount) + hdlc_set_carrier(status & MISCSTATUS_DCD, info->netdev); +#endif } if (status & MISCSTATUS_CTS_LATCHED) { @@ -2258,8 +2257,8 @@ static int mgsl_write(struct tty_struct * tty, int from_user, if (from_user) { down(&tmp_buf_sem); while (1) { - c = MIN(count, - MIN(SERIAL_XMIT_SIZE - info->xmit_cnt - 1, + c = min_t(int, count, + min(SERIAL_XMIT_SIZE - info->xmit_cnt - 1, SERIAL_XMIT_SIZE - info->xmit_head)); if (c <= 0) break; @@ -2272,7 +2271,7 @@ static int mgsl_write(struct tty_struct * tty, int from_user, break; } spin_lock_irqsave(&info->irq_spinlock,flags); - c = MIN(c, MIN(SERIAL_XMIT_SIZE - info->xmit_cnt - 1, + c = min_t(int, c, min(SERIAL_XMIT_SIZE - info->xmit_cnt - 1, SERIAL_XMIT_SIZE - info->xmit_head)); memcpy(info->xmit_buf + info->xmit_head, tmp_buf, c); info->xmit_head = ((info->xmit_head + c) & @@ -2287,8 +2286,8 @@ static int mgsl_write(struct tty_struct * tty, int from_user, } else { while (1) { spin_lock_irqsave(&info->irq_spinlock,flags); - c = MIN(count, - MIN(SERIAL_XMIT_SIZE - info->xmit_cnt - 1, + c = min_t(int, count, + min(SERIAL_XMIT_SIZE - info->xmit_cnt - 1, SERIAL_XMIT_SIZE - info->xmit_head)); if (c <= 0) { spin_unlock_irqrestore(&info->irq_spinlock,flags); @@ -2415,11 +2414,8 @@ static void mgsl_flush_buffer(struct tty_struct *tty) spin_unlock_irqrestore(&info->irq_spinlock,flags); wake_up_interruptible(&tty->write_wait); - if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && - tty->ldisc.write_wakeup) - (tty->ldisc.write_wakeup)(tty); - -} /* end of mgsl_flush_buffer() */ + tty_wakeup(tty); +} /* mgsl_send_xchar() * @@ -3253,9 +3249,8 @@ static void mgsl_close(struct tty_struct *tty, struct file * filp) if (tty->driver->flush_buffer) tty->driver->flush_buffer(tty); - - if (tty->ldisc.flush_buffer) - tty->ldisc.flush_buffer(tty); + + tty_ldisc_flush(tty); shutdown(info); @@ -3326,7 +3321,7 @@ static void mgsl_wait_until_sent(struct tty_struct *tty, int timeout) char_time = 1; if (timeout) - char_time = MIN(char_time, timeout); + char_time = min_t(unsigned long, char_time, timeout); if ( info->params.mode == MGSL_MODE_HDLC || info->params.mode == MGSL_MODE_RAW ) { @@ -3592,7 +3587,7 @@ static int mgsl_open(struct tty_struct *tty, struct file * filp) cleanup: if (retval) { if (tty->count == 1) - info->tty = NULL;/* tty layer will release tty struct */ + info->tty = NULL; /* tty layer will release tty struct */ if(info->count) info->count--; } @@ -4187,7 +4182,7 @@ int load_next_tx_holding_buffer(struct mgsl_struct *info) info->get_tx_holding_index=0; /* restart transmit timer */ - mod_timer(&info->tx_timer, jiffies + jiffies_from_ms(5000)); + mod_timer(&info->tx_timer, jiffies + msecs_to_jiffies(5000)); ret = 1; } @@ -4415,12 +4410,10 @@ void mgsl_add_device( struct mgsl_struct *info ) info->max_frame_size ); } -#ifdef CONFIG_SYNCLINK_SYNCPPP -#ifdef MODULE - if (info->dosyncppp) -#endif - mgsl_sppp_init(info); +#ifdef CONFIG_HDLC + hdlcdev_init(info); #endif + } /* end of mgsl_add_device() */ /* mgsl_allocate_device() @@ -4575,9 +4568,8 @@ static void synclink_cleanup(void) info = mgsl_device_list; while(info) { -#ifdef CONFIG_SYNCLINK_SYNCPPP - if (info->dosyncppp) - mgsl_sppp_delete(info); +#ifdef CONFIG_HDLC + hdlcdev_exit(info); #endif mgsl_release_resources(info); tmp = info; @@ -5819,7 +5811,7 @@ void usc_start_transmitter( struct mgsl_struct *info ) usc_TCmd( info, TCmd_SendFrame ); - info->tx_timer.expires = jiffies + jiffies_from_ms(5000); + info->tx_timer.expires = jiffies + msecs_to_jiffies(5000); add_timer(&info->tx_timer); } info->tx_active = 1; @@ -6750,9 +6742,12 @@ int mgsl_get_rx_frame(struct mgsl_struct *info) return_frame = 1; } framesize = 0; -#ifdef CONFIG_SYNCLINK_SYNCPPP - info->netstats.rx_errors++; - info->netstats.rx_frame_errors++; +#ifdef CONFIG_HDLC + { + struct net_device_stats *stats = hdlc_stats(info->netdev); + stats->rx_errors++; + stats->rx_frame_errors++; + } #endif } else return_frame = 1; @@ -6779,7 +6774,7 @@ int mgsl_get_rx_frame(struct mgsl_struct *info) if ( debug_level >= DEBUG_LEVEL_DATA ) mgsl_trace_block(info,info->rx_buffer_list[StartIndex].virt_addr, - MIN(framesize,DMABUFFERSIZE),0); + min_t(int, framesize, DMABUFFERSIZE),0); if (framesize) { if ( ( (info->params.crc_type & HDLC_CRC_RETURN_EX) && @@ -6823,18 +6818,12 @@ int mgsl_get_rx_frame(struct mgsl_struct *info) *ptmp); } -#ifdef CONFIG_SYNCLINK_SYNCPPP - if (info->netcount) { - /* pass frame to syncppp device */ - mgsl_sppp_rx_done(info,info->intermediate_rxbuffer,framesize); - } +#ifdef CONFIG_HDLC + if (info->netcount) + hdlcdev_rx(info,info->intermediate_rxbuffer,framesize); else #endif - { - /* Call the line discipline receive callback directly. */ - if ( tty && tty->ldisc.receive_buf ) - tty->ldisc.receive_buf(tty, info->intermediate_rxbuffer, info->flag_buf, framesize); - } + ldisc_receive_buf(tty, info->intermediate_rxbuffer, info->flag_buf, framesize); } } /* Free the buffers used by this frame. */ @@ -6996,7 +6985,7 @@ int mgsl_get_raw_rx_frame(struct mgsl_struct *info) if ( debug_level >= DEBUG_LEVEL_DATA ) mgsl_trace_block(info,info->rx_buffer_list[CurrentIndex].virt_addr, - MIN(framesize,DMABUFFERSIZE),0); + min_t(int, framesize, DMABUFFERSIZE),0); if (framesize) { /* copy dma buffer(s) to contiguous intermediate buffer */ @@ -7006,9 +6995,7 @@ int mgsl_get_raw_rx_frame(struct mgsl_struct *info) memcpy( info->intermediate_rxbuffer, pBufEntry->virt_addr, framesize); info->icount.rxok++; - /* Call the line discipline receive callback directly. */ - if ( tty && tty->ldisc.receive_buf ) - tty->ldisc.receive_buf(tty, info->intermediate_rxbuffer, info->flag_buf, framesize); + ldisc_receive_buf(tty, info->intermediate_rxbuffer, info->flag_buf, framesize); } /* Free the buffers used by this frame. */ @@ -7056,7 +7043,7 @@ void mgsl_load_tx_dma_buffer(struct mgsl_struct *info, const char *Buffer, DMABUFFERENTRY *pBufEntry; if ( debug_level >= DEBUG_LEVEL_DATA ) - mgsl_trace_block(info,Buffer, MIN(BufferSize,DMABUFFERSIZE), 1); + mgsl_trace_block(info,Buffer, min_t(int, BufferSize, DMABUFFERSIZE), 1); if (info->params.flags & HDLC_FLAG_HDLC_LOOPMODE) { /* set CMR:13 to start transmit when @@ -7214,7 +7201,7 @@ BOOLEAN mgsl_irq_test( struct mgsl_struct *info ) EndTime=100; while( EndTime-- && !info->irq_occurred ) { set_current_state(TASK_INTERRUPTIBLE); - schedule_timeout(jiffies_from_ms(10)); + schedule_timeout(msecs_to_jiffies(10)); } spin_lock_irqsave(&info->irq_spinlock,flags); @@ -7353,7 +7340,7 @@ BOOLEAN mgsl_dma_test( struct mgsl_struct *info ) /*************************************************************/ /* Wait 100ms for interrupt. */ - EndTime = jiffies + jiffies_from_ms(100); + EndTime = jiffies + msecs_to_jiffies(100); for(;;) { if (time_after(jiffies, EndTime)) { @@ -7409,7 +7396,7 @@ BOOLEAN mgsl_dma_test( struct mgsl_struct *info ) /**********************************/ /* Wait 100ms */ - EndTime = jiffies + jiffies_from_ms(100); + EndTime = jiffies + msecs_to_jiffies(100); for(;;) { if (time_after(jiffies, EndTime)) { @@ -7451,7 +7438,7 @@ BOOLEAN mgsl_dma_test( struct mgsl_struct *info ) /******************************/ /* Wait 100ms */ - EndTime = jiffies + jiffies_from_ms(100); + EndTime = jiffies + msecs_to_jiffies(100); /* While timer not expired wait for transmit complete */ @@ -7482,7 +7469,7 @@ BOOLEAN mgsl_dma_test( struct mgsl_struct *info ) /* WAIT FOR RECEIVE COMPLETE */ /* Wait 100ms */ - EndTime = jiffies + jiffies_from_ms(100); + EndTime = jiffies + msecs_to_jiffies(100); /* Wait for 16C32 to write receive status to buffer entry. */ status=info->rx_buffer_list[0].status; @@ -7736,9 +7723,9 @@ void mgsl_tx_timeout(unsigned long context) spin_unlock_irqrestore(&info->irq_spinlock,flags); -#ifdef CONFIG_SYNCLINK_SYNCPPP +#ifdef CONFIG_HDLC if (info->netcount) - mgsl_sppp_tx_done(info); + hdlcdev_tx_done(info); else #endif mgsl_bh_transmit(info); @@ -7819,79 +7806,125 @@ int usc_loopmode_send_active( struct mgsl_struct * info ) return usc_InReg( info, CCSR ) & BIT6 ? 1 : 0 ; } -#ifdef CONFIG_SYNCLINK_SYNCPPP -/* syncppp net device routines - */ -static void mgsl_setup(struct net_device *dev) -{ - dev->open = mgsl_sppp_open; - dev->stop = mgsl_sppp_close; - dev->hard_start_xmit = mgsl_sppp_tx; - dev->do_ioctl = mgsl_sppp_ioctl; - dev->get_stats = mgsl_net_stats; - dev->tx_timeout = mgsl_sppp_tx_timeout; - dev->watchdog_timeo = 10*HZ; -} +#ifdef CONFIG_HDLC -static void mgsl_sppp_init(struct mgsl_struct *info) +/** + * called by generic HDLC layer when protocol selected (PPP, frame relay, etc.) + * set encoding and frame check sequence (FCS) options + * + * dev pointer to network device structure + * encoding serial encoding setting + * parity FCS setting + * + * returns 0 if success, otherwise error code + */ +static int hdlcdev_attach(struct net_device *dev, unsigned short encoding, + unsigned short parity) { - struct net_device *d; + struct mgsl_struct *info = dev_to_port(dev); + unsigned char new_encoding; + unsigned short new_crctype; - sprintf(info->netname,"mgsl%d",info->line); + /* return error if TTY interface open */ + if (info->count) + return -EBUSY; - d = alloc_netdev(0, info->netname, mgsl_setup); - if (!d) { - printk(KERN_WARNING "%s: alloc_netdev failed.\n", - info->netname); - return; + switch (encoding) + { + case ENCODING_NRZ: new_encoding = HDLC_ENCODING_NRZ; break; + case ENCODING_NRZI: new_encoding = HDLC_ENCODING_NRZI_SPACE; break; + case ENCODING_FM_MARK: new_encoding = HDLC_ENCODING_BIPHASE_MARK; break; + case ENCODING_FM_SPACE: new_encoding = HDLC_ENCODING_BIPHASE_SPACE; break; + case ENCODING_MANCHESTER: new_encoding = HDLC_ENCODING_BIPHASE_LEVEL; break; + default: return -EINVAL; } - info->if_ptr = &info->pppdev; - info->netdev = info->pppdev.dev = d; - - d->base_addr = info->io_base; - d->irq = info->irq_level; - d->dma = info->dma_level; - d->priv = info; + switch (parity) + { + case PARITY_NONE: new_crctype = HDLC_CRC_NONE; break; + case PARITY_CRC16_PR1_CCITT: new_crctype = HDLC_CRC_16_CCITT; break; + case PARITY_CRC32_PR1_CCITT: new_crctype = HDLC_CRC_32_CCITT; break; + default: return -EINVAL; + } - sppp_attach(&info->pppdev); - mgsl_setup(d); + info->params.encoding = new_encoding; + info->params.crc_type = new_crctype;; - if (register_netdev(d)) { - printk(KERN_WARNING "%s: register_netdev failed.\n", d->name); - sppp_detach(info->netdev); - info->netdev = NULL; - free_netdev(d); - return; - } + /* if network interface up, reprogram hardware */ + if (info->netcount) + mgsl_program_hw(info); - if (debug_level >= DEBUG_LEVEL_INFO) - printk("mgsl_sppp_init()\n"); + return 0; } -void mgsl_sppp_delete(struct mgsl_struct *info) +/** + * called by generic HDLC layer to send frame + * + * skb socket buffer containing HDLC frame + * dev pointer to network device structure + * + * returns 0 if success, otherwise error code + */ +static int hdlcdev_xmit(struct sk_buff *skb, struct net_device *dev) { + struct mgsl_struct *info = dev_to_port(dev); + struct net_device_stats *stats = hdlc_stats(dev); + unsigned long flags; + if (debug_level >= DEBUG_LEVEL_INFO) - printk("mgsl_sppp_delete(%s)\n",info->netname); - unregister_netdev(info->netdev); - sppp_detach(info->netdev); - free_netdev(info->netdev); - info->netdev = NULL; - info->pppdev.dev = NULL; + printk(KERN_INFO "%s:hdlc_xmit(%s)\n",__FILE__,dev->name); + + /* stop sending until this frame completes */ + netif_stop_queue(dev); + + /* copy data to device buffers */ + info->xmit_cnt = skb->len; + mgsl_load_tx_dma_buffer(info, skb->data, skb->len); + + /* update network statistics */ + stats->tx_packets++; + stats->tx_bytes += skb->len; + + /* done with socket buffer, so free it */ + dev_kfree_skb(skb); + + /* save start time for transmit timeout detection */ + dev->trans_start = jiffies; + + /* start hardware transmitter if necessary */ + spin_lock_irqsave(&info->irq_spinlock,flags); + if (!info->tx_active) + usc_start_transmitter(info); + spin_unlock_irqrestore(&info->irq_spinlock,flags); + + return 0; } -int mgsl_sppp_open(struct net_device *d) +/** + * called by network layer when interface enabled + * claim resources and initialize hardware + * + * dev pointer to network device structure + * + * returns 0 if success, otherwise error code + */ +static int hdlcdev_open(struct net_device *dev) { - struct mgsl_struct *info = d->priv; - int err; + struct mgsl_struct *info = dev_to_port(dev); + int rc; unsigned long flags; if (debug_level >= DEBUG_LEVEL_INFO) - printk("mgsl_sppp_open(%s)\n",info->netname); + printk("%s:hdlcdev_open(%s)\n",__FILE__,dev->name); + /* generic HDLC layer open processing */ + if ((rc = hdlc_open(dev))) + return rc; + + /* arbitrate between network and tty opens */ spin_lock_irqsave(&info->netlock, flags); if (info->count != 0 || info->netcount != 0) { - printk(KERN_WARNING "%s: sppp_open returning busy\n", info->netname); + printk(KERN_WARNING "%s: hdlc_open returning busy\n", dev->name); spin_unlock_irqrestore(&info->netlock, flags); return -EBUSY; } @@ -7899,141 +7932,301 @@ int mgsl_sppp_open(struct net_device *d) spin_unlock_irqrestore(&info->netlock, flags); /* claim resources and init adapter */ - if ((err = startup(info)) != 0) - goto open_fail; - - /* allow syncppp module to do open processing */ - if ((err = sppp_open(d)) != 0) { - shutdown(info); - goto open_fail; + if ((rc = startup(info)) != 0) { + spin_lock_irqsave(&info->netlock, flags); + info->netcount=0; + spin_unlock_irqrestore(&info->netlock, flags); + return rc; } + /* assert DTR and RTS, apply hardware settings */ info->serial_signals |= SerialSignal_RTS + SerialSignal_DTR; mgsl_program_hw(info); - d->trans_start = jiffies; - netif_start_queue(d); - return 0; + /* enable network layer transmit */ + dev->trans_start = jiffies; + netif_start_queue(dev); -open_fail: - spin_lock_irqsave(&info->netlock, flags); - info->netcount=0; - spin_unlock_irqrestore(&info->netlock, flags); - return err; + /* inform generic HDLC layer of current DCD status */ + spin_lock_irqsave(&info->irq_spinlock, flags); + usc_get_serial_signals(info); + spin_unlock_irqrestore(&info->irq_spinlock, flags); + hdlc_set_carrier(info->serial_signals & SerialSignal_DCD, dev); + + return 0; } -void mgsl_sppp_tx_timeout(struct net_device *dev) +/** + * called by network layer when interface is disabled + * shutdown hardware and release resources + * + * dev pointer to network device structure + * + * returns 0 if success, otherwise error code + */ +static int hdlcdev_close(struct net_device *dev) { - struct mgsl_struct *info = dev->priv; + struct mgsl_struct *info = dev_to_port(dev); unsigned long flags; if (debug_level >= DEBUG_LEVEL_INFO) - printk("mgsl_sppp_tx_timeout(%s)\n",info->netname); + printk("%s:hdlcdev_close(%s)\n",__FILE__,dev->name); - info->netstats.tx_errors++; - info->netstats.tx_aborted_errors++; + netif_stop_queue(dev); - spin_lock_irqsave(&info->irq_spinlock,flags); - usc_stop_transmitter(info); - spin_unlock_irqrestore(&info->irq_spinlock,flags); + /* shutdown adapter and release resources */ + shutdown(info); - netif_wake_queue(dev); + hdlc_close(dev); + + spin_lock_irqsave(&info->netlock, flags); + info->netcount=0; + spin_unlock_irqrestore(&info->netlock, flags); + + return 0; } -int mgsl_sppp_tx(struct sk_buff *skb, struct net_device *dev) +/** + * called by network layer to process IOCTL call to network device + * + * dev pointer to network device structure + * ifr pointer to network interface request structure + * cmd IOCTL command code + * + * returns 0 if success, otherwise error code + */ +static int hdlcdev_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) { - struct mgsl_struct *info = dev->priv; - unsigned long flags; + 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 mgsl_struct *info = dev_to_port(dev); + unsigned int flags; if (debug_level >= DEBUG_LEVEL_INFO) - printk("mgsl_sppp_tx(%s)\n",info->netname); + printk("%s:hdlcdev_ioctl(%s)\n",__FILE__,dev->name); - netif_stop_queue(dev); + /* return error if TTY interface open */ + if (info->count) + return -EBUSY; - info->xmit_cnt = skb->len; - mgsl_load_tx_dma_buffer(info, skb->data, skb->len); - info->netstats.tx_packets++; - info->netstats.tx_bytes += skb->len; - dev_kfree_skb(skb); + if (cmd != SIOCWANDEV) + return hdlc_ioctl(dev, ifr, cmd); - dev->trans_start = jiffies; + switch(ifr->ifr_settings.type) { + case IF_GET_IFACE: /* return current sync_serial_settings */ - spin_lock_irqsave(&info->irq_spinlock,flags); - if (!info->tx_active) - usc_start_transmitter(info); - spin_unlock_irqrestore(&info->irq_spinlock,flags); + ifr->ifr_settings.type = IF_IFACE_SYNC_SERIAL; + if (ifr->ifr_settings.size < size) { + ifr->ifr_settings.size = size; /* data size wanted */ + return -ENOBUFS; + } - return 0; + flags = info->params.flags & (HDLC_FLAG_RXC_RXCPIN | HDLC_FLAG_RXC_DPLL | + HDLC_FLAG_RXC_BRG | HDLC_FLAG_RXC_TXCPIN | + HDLC_FLAG_TXC_TXCPIN | HDLC_FLAG_TXC_DPLL | + HDLC_FLAG_TXC_BRG | HDLC_FLAG_TXC_RXCPIN); + + switch (flags){ + case (HDLC_FLAG_RXC_RXCPIN | HDLC_FLAG_TXC_TXCPIN): new_line.clock_type = CLOCK_EXT; break; + case (HDLC_FLAG_RXC_BRG | HDLC_FLAG_TXC_BRG): new_line.clock_type = CLOCK_INT; break; + case (HDLC_FLAG_RXC_RXCPIN | HDLC_FLAG_TXC_BRG): new_line.clock_type = CLOCK_TXINT; break; + case (HDLC_FLAG_RXC_RXCPIN | HDLC_FLAG_TXC_RXCPIN): new_line.clock_type = CLOCK_TXFROMRX; break; + default: new_line.clock_type = CLOCK_DEFAULT; + } + + new_line.clock_rate = info->params.clock_speed; + new_line.loopback = info->params.loopback ? 1:0; + + if (copy_to_user(line, &new_line, size)) + return -EFAULT; + return 0; + + case IF_IFACE_SYNC_SERIAL: /* set sync_serial_settings */ + + if(!capable(CAP_NET_ADMIN)) + return -EPERM; + if (copy_from_user(&new_line, line, size)) + return -EFAULT; + + switch (new_line.clock_type) + { + case CLOCK_EXT: flags = HDLC_FLAG_RXC_RXCPIN | HDLC_FLAG_TXC_TXCPIN; break; + case CLOCK_TXFROMRX: flags = HDLC_FLAG_RXC_RXCPIN | HDLC_FLAG_TXC_RXCPIN; break; + case CLOCK_INT: flags = HDLC_FLAG_RXC_BRG | HDLC_FLAG_TXC_BRG; break; + case CLOCK_TXINT: flags = HDLC_FLAG_RXC_RXCPIN | HDLC_FLAG_TXC_BRG; break; + case CLOCK_DEFAULT: flags = info->params.flags & + (HDLC_FLAG_RXC_RXCPIN | HDLC_FLAG_RXC_DPLL | + HDLC_FLAG_RXC_BRG | HDLC_FLAG_RXC_TXCPIN | + HDLC_FLAG_TXC_TXCPIN | HDLC_FLAG_TXC_DPLL | + HDLC_FLAG_TXC_BRG | HDLC_FLAG_TXC_RXCPIN); break; + default: return -EINVAL; + } + + if (new_line.loopback != 0 && new_line.loopback != 1) + return -EINVAL; + + info->params.flags &= ~(HDLC_FLAG_RXC_RXCPIN | HDLC_FLAG_RXC_DPLL | + HDLC_FLAG_RXC_BRG | HDLC_FLAG_RXC_TXCPIN | + HDLC_FLAG_TXC_TXCPIN | HDLC_FLAG_TXC_DPLL | + HDLC_FLAG_TXC_BRG | HDLC_FLAG_TXC_RXCPIN); + info->params.flags |= flags; + + info->params.loopback = new_line.loopback; + + if (flags & (HDLC_FLAG_RXC_BRG | HDLC_FLAG_TXC_BRG)) + info->params.clock_speed = new_line.clock_rate; + else + info->params.clock_speed = 0; + + /* if network interface up, reprogram hardware */ + if (info->netcount) + mgsl_program_hw(info); + return 0; + + default: + return hdlc_ioctl(dev, ifr, cmd); + } } -int mgsl_sppp_close(struct net_device *d) +/** + * called by network layer when transmit timeout is detected + * + * dev pointer to network device structure + */ +static void hdlcdev_tx_timeout(struct net_device *dev) { - struct mgsl_struct *info = d->priv; + struct mgsl_struct *info = dev_to_port(dev); + struct net_device_stats *stats = hdlc_stats(dev); unsigned long flags; if (debug_level >= DEBUG_LEVEL_INFO) - printk("mgsl_sppp_close(%s)\n",info->netname); + printk("hdlcdev_tx_timeout(%s)\n",dev->name); - /* shutdown adapter and release resources */ - shutdown(info); + stats->tx_errors++; + stats->tx_aborted_errors++; - /* allow syncppp to do close processing */ - sppp_close(d); - netif_stop_queue(d); + spin_lock_irqsave(&info->irq_spinlock,flags); + usc_stop_transmitter(info); + spin_unlock_irqrestore(&info->irq_spinlock,flags); - spin_lock_irqsave(&info->netlock, flags); - info->netcount=0; - spin_unlock_irqrestore(&info->netlock, flags); - return 0; + netif_wake_queue(dev); +} + +/** + * called by device driver when transmit completes + * reenable network layer transmit if stopped + * + * info pointer to device instance information + */ +static void hdlcdev_tx_done(struct mgsl_struct *info) +{ + if (netif_queue_stopped(info->netdev)) + netif_wake_queue(info->netdev); } -void mgsl_sppp_rx_done(struct mgsl_struct *info, char *buf, int size) +/** + * called by device driver when frame received + * pass frame to network layer + * + * info pointer to device instance information + * buf pointer to buffer contianing frame data + * size count of data bytes in buf + */ +static void hdlcdev_rx(struct mgsl_struct *info, char *buf, int size) { struct sk_buff *skb = dev_alloc_skb(size); + struct net_device *dev = info->netdev; + struct net_device_stats *stats = hdlc_stats(dev); + if (debug_level >= DEBUG_LEVEL_INFO) - printk("mgsl_sppp_rx_done(%s)\n",info->netname); + printk("hdlcdev_rx(%s)\n",dev->name); + if (skb == NULL) { - printk(KERN_NOTICE "%s: can't alloc skb, dropping packet\n", - info->netname); - info->netstats.rx_dropped++; + printk(KERN_NOTICE "%s: can't alloc skb, dropping packet\n", dev->name); + stats->rx_dropped++; return; } memcpy(skb_put(skb, size),buf,size); - skb->protocol = htons(ETH_P_WAN_PPP); - skb->dev = info->netdev; - skb->mac.raw = skb->data; - info->netstats.rx_packets++; - info->netstats.rx_bytes += size; + skb->dev = info->netdev; + skb->mac.raw = skb->data; + skb->protocol = hdlc_type_trans(skb, skb->dev); + + stats->rx_packets++; + stats->rx_bytes += size; + netif_rx(skb); - info->netdev->trans_start = jiffies; -} -void mgsl_sppp_tx_done(struct mgsl_struct *info) -{ - if (netif_queue_stopped(info->netdev)) - netif_wake_queue(info->netdev); + info->netdev->last_rx = jiffies; } -struct net_device_stats *mgsl_net_stats(struct net_device *dev) +/** + * called by device driver when adding device instance + * do generic HDLC initialization + * + * info pointer to device instance information + * + * returns 0 if success, otherwise error code + */ +static int hdlcdev_init(struct mgsl_struct *info) { - struct mgsl_struct *info = dev->priv; - if (debug_level >= DEBUG_LEVEL_INFO) - printk("mgsl_net_stats(%s)\n",info->netname); - return &info->netstats; + int rc; + struct net_device *dev; + hdlc_device *hdlc; + + /* allocate and initialize network and HDLC layer objects */ + + if (!(dev = alloc_hdlcdev(info))) { + printk(KERN_ERR "%s:hdlc device allocation failure\n",__FILE__); + return -ENOMEM; + } + + /* for network layer reporting purposes only */ + dev->base_addr = info->io_base; + dev->irq = info->irq_level; + dev->dma = info->dma_level; + + /* network layer callbacks and settings */ + dev->do_ioctl = hdlcdev_ioctl; + dev->open = hdlcdev_open; + dev->stop = hdlcdev_close; + dev->tx_timeout = hdlcdev_tx_timeout; + dev->watchdog_timeo = 10*HZ; + dev->tx_queue_len = 50; + + /* generic HDLC layer callbacks and settings */ + hdlc = dev_to_hdlc(dev); + hdlc->attach = hdlcdev_attach; + hdlc->xmit = hdlcdev_xmit; + + /* register objects with HDLC layer */ + if ((rc = register_hdlc_device(dev))) { + printk(KERN_WARNING "%s:unable to register hdlc device\n",__FILE__); + free_netdev(dev); + return rc; + } + + info->netdev = dev; + return 0; } -int mgsl_sppp_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) +/** + * called by device driver when removing device instance + * do generic HDLC cleanup + * + * info pointer to device instance information + */ +static void hdlcdev_exit(struct mgsl_struct *info) { - struct mgsl_struct *info = dev->priv; - if (debug_level >= DEBUG_LEVEL_INFO) - printk("%s(%d):mgsl_ioctl %s cmd=%08X\n", __FILE__,__LINE__, - info->netname, cmd ); - return sppp_do_ioctl(dev, ifr, cmd); + unregister_hdlc_device(info->netdev); + free_netdev(info->netdev); + info->netdev = NULL; } -#endif /* ifdef CONFIG_SYNCLINK_SYNCPPP */ +#endif /* CONFIG_HDLC */ + static int __devinit synclink_init_one (struct pci_dev *dev, const struct pci_device_id *ent) diff --git a/drivers/char/viocons.c b/drivers/char/viocons.c index df9d4de9b..dcfcef4be 100644 --- a/drivers/char/viocons.c +++ b/drivers/char/viocons.c @@ -422,10 +422,7 @@ static void send_buffers(struct port_info *pi) pi->overflowMessage = 0; if (pi->tty) { - if ((pi->tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && - (pi->tty->ldisc.write_wakeup)) - (pi->tty->ldisc.write_wakeup)(pi->tty); - wake_up_interruptible(&pi->tty->write_wait); + tty_wakeup(pi->tty); } } diff --git a/drivers/char/vme_scc.c b/drivers/char/vme_scc.c index 6f7833417..19ba83635 100644 --- a/drivers/char/vme_scc.c +++ b/drivers/char/vme_scc.c @@ -544,12 +544,8 @@ static irqreturn_t scc_tx_int(int irq, void *data, struct pt_regs *fp) SCCwrite(COMMAND_REG, CR_TX_PENDING_RESET); /* disable tx_int on next tx underrun? */ port->gs.flags &= ~GS_TX_INTEN; } - if (port->gs.tty && port->gs.xmit_cnt <= port->gs.wakeup_chars) { - if ((port->gs.tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && - port->gs.tty->ldisc.write_wakeup) - (port->gs.tty->ldisc.write_wakeup)(port->gs.tty); - wake_up_interruptible(&port->gs.tty->write_wait); - } + if (port->gs.tty && port->gs.xmit_cnt <= port->gs.wakeup_chars) + tty_wakeup(port->gs.tty); SCCwrite_NB(COMMAND_REG, CR_HIGHEST_IUS_RESET); return IRQ_HANDLED; diff --git a/drivers/char/watchdog/ixp4xx_wdt.c b/drivers/char/watchdog/ixp4xx_wdt.c index b8e3d91d3..a6781dede 100644 --- a/drivers/char/watchdog/ixp4xx_wdt.c +++ b/drivers/char/watchdog/ixp4xx_wdt.c @@ -170,6 +170,7 @@ ixp4xx_wdt_release(struct inode *inode, struct file *file) static struct file_operations ixp4xx_wdt_fops = { .owner = THIS_MODULE, + .llseek = no_llseek, .write = ixp4xx_wdt_write, .ioctl = ixp4xx_wdt_ioctl, .open = ixp4xx_wdt_open, diff --git a/drivers/char/watchdog/mpc8xx_wdt.c b/drivers/char/watchdog/mpc8xx_wdt.c new file mode 100644 index 000000000..56d62ba7c --- /dev/null +++ b/drivers/char/watchdog/mpc8xx_wdt.c @@ -0,0 +1,164 @@ +/* + * mpc8xx_wdt.c - MPC8xx watchdog userspace interface + * + * Author: Florian Schirmer + * + * 2002 (c) Florian Schirmer 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static unsigned long wdt_opened; +static int wdt_status; + +static void mpc8xx_wdt_handler_disable(void) +{ + volatile immap_t *imap = (volatile immap_t *)IMAP_ADDR; + + imap->im_sit.sit_piscr &= ~(PISCR_PIE | PISCR_PTE); + + printk(KERN_NOTICE "mpc8xx_wdt: keep-alive handler deactivated\n"); +} + +static void mpc8xx_wdt_handler_enable(void) +{ + volatile immap_t *imap = (volatile immap_t *)IMAP_ADDR; + + imap->im_sit.sit_piscr |= PISCR_PIE | PISCR_PTE; + + printk(KERN_NOTICE "mpc8xx_wdt: keep-alive handler activated\n"); +} + +static int mpc8xx_wdt_open(struct inode *inode, struct file *file) +{ + if (test_and_set_bit(0, &wdt_opened)) + return -EBUSY; + + m8xx_wdt_reset(); + mpc8xx_wdt_handler_disable(); + + return 0; +} + +static int mpc8xx_wdt_release(struct inode *inode, struct file *file) +{ + m8xx_wdt_reset(); + +#if !defined(CONFIG_WATCHDOG_NOWAYOUT) + mpc8xx_wdt_handler_enable(); +#endif + + clear_bit(0, &wdt_opened); + + return 0; +} + +static ssize_t mpc8xx_wdt_write(struct file *file, const char *data, size_t len, + loff_t * ppos) +{ + if (ppos != &file->f_pos) + return -ESPIPE; + + if (len) + m8xx_wdt_reset(); + + return len; +} + +static int mpc8xx_wdt_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg) +{ + int timeout; + static struct watchdog_info info = { + .options = WDIOF_KEEPALIVEPING, + .firmware_version = 0, + .identity = "MPC8xx watchdog", + }; + + switch (cmd) { + case WDIOC_GETSUPPORT: + if (copy_to_user((void *)arg, &info, sizeof(info))) + return -EFAULT; + break; + + case WDIOC_GETSTATUS: + case WDIOC_GETBOOTSTATUS: + if (put_user(wdt_status, (int *)arg)) + return -EFAULT; + wdt_status &= ~WDIOF_KEEPALIVEPING; + break; + + case WDIOC_GETTEMP: + return -EOPNOTSUPP; + + case WDIOC_SETOPTIONS: + return -EOPNOTSUPP; + + case WDIOC_KEEPALIVE: + m8xx_wdt_reset(); + wdt_status |= WDIOF_KEEPALIVEPING; + break; + + case WDIOC_SETTIMEOUT: + return -EOPNOTSUPP; + + case WDIOC_GETTIMEOUT: + timeout = m8xx_wdt_get_timeout(); + if (put_user(timeout, (int *)arg)) + return -EFAULT; + break; + + default: + return -ENOIOCTLCMD; + } + + return 0; +} + +static struct file_operations mpc8xx_wdt_fops = { + .owner = THIS_MODULE, + .llseek = no_llseek, + .write = mpc8xx_wdt_write, + .ioctl = mpc8xx_wdt_ioctl, + .open = mpc8xx_wdt_open, + .release = mpc8xx_wdt_release, +}; + +static struct miscdevice mpc8xx_wdt_miscdev = { + .minor = WATCHDOG_MINOR, + .name = "watchdog", + .fops = &mpc8xx_wdt_fops, +}; + +static int __init mpc8xx_wdt_init(void) +{ + return misc_register(&mpc8xx_wdt_miscdev); +} + +static void __exit mpc8xx_wdt_exit(void) +{ + misc_deregister(&mpc8xx_wdt_miscdev); + + m8xx_wdt_reset(); + mpc8xx_wdt_handler_enable(); +} + +module_init(mpc8xx_wdt_init); +module_exit(mpc8xx_wdt_exit); + +MODULE_AUTHOR("Florian Schirmer "); +MODULE_DESCRIPTION("MPC8xx watchdog driver"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR); diff --git a/drivers/char/watchdog/pcwd.c b/drivers/char/watchdog/pcwd.c index 88bdbda92..592dca108 100644 --- a/drivers/char/watchdog/pcwd.c +++ b/drivers/char/watchdog/pcwd.c @@ -859,8 +859,7 @@ static int __init pcwd_checkcard(int base_addr) /* Not an 'ff' from a floating bus, so must be a card! */ for (i = 0; i < 4; ++i) { - set_current_state(TASK_INTERRUPTIBLE); - schedule_timeout(HZ / 2); + msleep(500); last_port0 = port0; last_port1 = port1; diff --git a/drivers/char/watchdog/wdt285.c b/drivers/char/watchdog/wdt285.c index d3fced33d..52825a1f1 100644 --- a/drivers/char/watchdog/wdt285.c +++ b/drivers/char/watchdog/wdt285.c @@ -180,6 +180,7 @@ watchdog_ioctl(struct inode *inode, struct file *file, unsigned int cmd, static struct file_operations watchdog_fops = { .owner = THIS_MODULE, + .llseek = no_llseek, .write = watchdog_write, .ioctl = watchdog_ioctl, .open = watchdog_open, diff --git a/drivers/char/watchdog/wdt977.c b/drivers/char/watchdog/wdt977.c index 388c548c2..072e9b214 100644 --- a/drivers/char/watchdog/wdt977.c +++ b/drivers/char/watchdog/wdt977.c @@ -392,6 +392,7 @@ static int wdt977_notify_sys(struct notifier_block *this, unsigned long code, static struct file_operations wdt977_fops= { .owner = THIS_MODULE, + .llseek = no_llseek, .write = wdt977_write, .ioctl = wdt977_ioctl, .open = wdt977_open, diff --git a/drivers/cpufreq/Kconfig b/drivers/cpufreq/Kconfig index 331b5156d..889f243fb 100644 --- a/drivers/cpufreq/Kconfig +++ b/drivers/cpufreq/Kconfig @@ -69,6 +69,21 @@ config CPU_FREQ_GOV_USERSPACE If in doubt, say Y. +config CPU_FREQ_GOV_ONDEMAND + tristate "'ondemand' cpufreq policy governor" + depends on CPU_FREQ + help + 'ondemand' - This driver adds a dynamic cpufreq policy governor. + The governor does a periodic polling and + changes frequency based on the CPU utilization. + The support for this governor depends on CPU capability to + do fast frequency switching (i.e, very low latency frequency + transitions). + + For details, take a look at linux/Documentation/cpu-freq. + + If in doubt, say N. + config CPU_FREQ_24_API bool "/proc/sys/cpu/ interface (2.4. / OLD)" depends on CPU_FREQ && SYSCTL && CPU_FREQ_GOV_USERSPACE diff --git a/drivers/cpufreq/Makefile b/drivers/cpufreq/Makefile index 1eebf3c6e..50291abb4 100644 --- a/drivers/cpufreq/Makefile +++ b/drivers/cpufreq/Makefile @@ -5,6 +5,7 @@ obj-$(CONFIG_CPU_FREQ) += cpufreq.o obj-$(CONFIG_CPU_FREQ_GOV_PERFORMANCE) += cpufreq_performance.o obj-$(CONFIG_CPU_FREQ_GOV_POWERSAVE) += cpufreq_powersave.o obj-$(CONFIG_CPU_FREQ_GOV_USERSPACE) += cpufreq_userspace.o +obj-$(CONFIG_CPU_FREQ_GOV_ONDEMAND) += cpufreq_ondemand.o # CPUfreq cross-arch helpers obj-$(CONFIG_CPU_FREQ_TABLE) += freq_table.o diff --git a/drivers/cpufreq/cpufreq_ondemand.c b/drivers/cpufreq/cpufreq_ondemand.c new file mode 100644 index 000000000..a9320ae41 --- /dev/null +++ b/drivers/cpufreq/cpufreq_ondemand.c @@ -0,0 +1,437 @@ +/* + * drivers/cpufreq/cpufreq_ondemand.c + * + * Copyright (C) 2001 Russell King + * (C) 2003 Venkatesh Pallipadi . + * Jun Nakajima + * + * This program is free software; you can 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * dbs is used in this file as a shortform for demandbased switching + * It helps to keep variable names smaller, simpler + */ + +#define DEF_FREQUENCY_UP_THRESHOLD (80) +#define MIN_FREQUENCY_UP_THRESHOLD (0) +#define MAX_FREQUENCY_UP_THRESHOLD (100) + +#define DEF_FREQUENCY_DOWN_THRESHOLD (20) +#define MIN_FREQUENCY_DOWN_THRESHOLD (0) +#define MAX_FREQUENCY_DOWN_THRESHOLD (100) + +/* + * The polling frequency of this governor depends on the capability of + * the processor. Default polling frequency is 1000 times the transition + * latency of the processor. The governor will work on any processor with + * transition latency <= 10mS, using appropriate sampling + * rate. + * For CPUs with transition latency > 10mS (mostly drivers with CPUFREQ_ETERNAL) + * this governor will not work. + * All times here are in uS. + */ +static unsigned int def_sampling_rate; +#define MIN_SAMPLING_RATE (def_sampling_rate / 2) +#define MAX_SAMPLING_RATE (500 * def_sampling_rate) +#define DEF_SAMPLING_RATE_LATENCY_MULTIPLIER (1000) +#define DEF_SAMPLING_DOWN_FACTOR (10) +#define TRANSITION_LATENCY_LIMIT (10 * 1000) +#define sampling_rate_in_HZ(x) (((x * HZ) < (1000 * 1000))?1:((x * HZ) / (1000 * 1000))) + +static void do_dbs_timer(void *data); + +struct cpu_dbs_info_s { + struct cpufreq_policy *cur_policy; + unsigned int prev_cpu_idle_up; + unsigned int prev_cpu_idle_down; + unsigned int enable; +}; +static DEFINE_PER_CPU(struct cpu_dbs_info_s, cpu_dbs_info); + +static unsigned int dbs_enable; /* number of CPUs using this policy */ + +static DECLARE_MUTEX (dbs_sem); +static DECLARE_WORK (dbs_work, do_dbs_timer, NULL); + +struct dbs_tuners { + unsigned int sampling_rate; + unsigned int sampling_down_factor; + unsigned int up_threshold; + unsigned int down_threshold; +}; + +struct dbs_tuners dbs_tuners_ins = { + .up_threshold = DEF_FREQUENCY_UP_THRESHOLD, + .down_threshold = DEF_FREQUENCY_DOWN_THRESHOLD, + .sampling_down_factor = DEF_SAMPLING_DOWN_FACTOR, +}; + +/************************** sysfs interface ************************/ +static ssize_t show_sampling_rate_max(struct cpufreq_policy *policy, char *buf) +{ + return sprintf (buf, "%u\n", MAX_SAMPLING_RATE); +} + +static ssize_t show_sampling_rate_min(struct cpufreq_policy *policy, char *buf) +{ + return sprintf (buf, "%u\n", MIN_SAMPLING_RATE); +} + +#define define_one_ro(_name) \ +static struct freq_attr _name = { \ + .attr = { .name = __stringify(_name), .mode = 0444 }, \ + .show = show_##_name, \ +} + +define_one_ro(sampling_rate_max); +define_one_ro(sampling_rate_min); + +/* cpufreq_ondemand Governor Tunables */ +#define show_one(file_name, object) \ +static ssize_t show_##file_name \ +(struct cpufreq_policy *unused, char *buf) \ +{ \ + return sprintf(buf, "%u\n", dbs_tuners_ins.object); \ +} +show_one(sampling_rate, sampling_rate); +show_one(sampling_down_factor, sampling_down_factor); +show_one(up_threshold, up_threshold); +show_one(down_threshold, down_threshold); + +static ssize_t store_sampling_down_factor(struct cpufreq_policy *unused, + const char *buf, size_t count) +{ + unsigned int input; + int ret; + ret = sscanf (buf, "%u", &input); + down(&dbs_sem); + if (ret != 1 ) + goto out; + + dbs_tuners_ins.sampling_down_factor = input; +out: + up(&dbs_sem); + return count; +} + +static ssize_t store_sampling_rate(struct cpufreq_policy *unused, + const char *buf, size_t count) +{ + unsigned int input; + int ret; + ret = sscanf (buf, "%u", &input); + down(&dbs_sem); + if (ret != 1 || input > MAX_SAMPLING_RATE || input < MIN_SAMPLING_RATE) + goto out; + + dbs_tuners_ins.sampling_rate = input; +out: + up(&dbs_sem); + return count; +} + +static ssize_t store_up_threshold(struct cpufreq_policy *unused, + const char *buf, size_t count) +{ + unsigned int input; + int ret; + ret = sscanf (buf, "%u", &input); + down(&dbs_sem); + if (ret != 1 || input > MAX_FREQUENCY_UP_THRESHOLD || + input < MIN_FREQUENCY_UP_THRESHOLD || + input <= dbs_tuners_ins.down_threshold) + goto out; + + dbs_tuners_ins.up_threshold = input; +out: + up(&dbs_sem); + return count; +} + +static ssize_t store_down_threshold(struct cpufreq_policy *unused, + const char *buf, size_t count) +{ + unsigned int input; + int ret; + ret = sscanf (buf, "%u", &input); + down(&dbs_sem); + if (ret != 1 || input > MAX_FREQUENCY_DOWN_THRESHOLD || + input < MIN_FREQUENCY_DOWN_THRESHOLD || + input >= dbs_tuners_ins.up_threshold) + goto out; + + dbs_tuners_ins.down_threshold = input; +out: + up(&dbs_sem); + return count; +} + +#define define_one_rw(_name) \ +static struct freq_attr _name = { \ + .attr = { .name = __stringify(_name), .mode = 0644 }, \ + .show = show_##_name, \ + .store = store_##_name, \ +} + +define_one_rw(sampling_rate); +define_one_rw(sampling_down_factor); +define_one_rw(up_threshold); +define_one_rw(down_threshold); + +static struct attribute * dbs_attributes[] = { + &sampling_rate_max.attr, + &sampling_rate_min.attr, + &sampling_rate.attr, + &sampling_down_factor.attr, + &up_threshold.attr, + &down_threshold.attr, + NULL +}; + +static struct attribute_group dbs_attr_group = { + .attrs = dbs_attributes, + .name = "ondemand", +}; + +/************************** sysfs end ************************/ + +static void dbs_check_cpu(int cpu) +{ + unsigned int idle_ticks, up_idle_ticks, down_idle_ticks; + unsigned int total_idle_ticks; + unsigned int freq_down_step; + unsigned int freq_down_sampling_rate; + static int down_skip[NR_CPUS]; + struct cpu_dbs_info_s *this_dbs_info; + + this_dbs_info = &per_cpu(cpu_dbs_info, cpu); + if (!this_dbs_info->enable) + return; + + /* + * The default safe range is 20% to 80% + * Every sampling_rate, we check + * - If current idle time is less than 20%, then we try to + * increase frequency + * Every sampling_rate*sampling_down_factor, we check + * - If current idle time is more than 80%, then we try to + * decrease frequency + * + * Any frequency increase takes it to the maximum frequency. + * Frequency reduction happens at minimum steps of + * 5% of max_frequency + */ + /* Check for frequency increase */ + total_idle_ticks = kstat_cpu(cpu).cpustat.idle + + kstat_cpu(cpu).cpustat.iowait; + idle_ticks = total_idle_ticks - + this_dbs_info->prev_cpu_idle_up; + this_dbs_info->prev_cpu_idle_up = total_idle_ticks; + + /* Scale idle ticks by 100 and compare with up and down ticks */ + idle_ticks *= 100; + up_idle_ticks = (100 - dbs_tuners_ins.up_threshold) * + sampling_rate_in_HZ(dbs_tuners_ins.sampling_rate); + + if (idle_ticks < up_idle_ticks) { + __cpufreq_driver_target(this_dbs_info->cur_policy, + this_dbs_info->cur_policy->max, + CPUFREQ_RELATION_H); + down_skip[cpu] = 0; + this_dbs_info->prev_cpu_idle_down = total_idle_ticks; + return; + } + + /* Check for frequency decrease */ + down_skip[cpu]++; + if (down_skip[cpu] < dbs_tuners_ins.sampling_down_factor) + return; + + idle_ticks = total_idle_ticks - + this_dbs_info->prev_cpu_idle_down; + /* Scale idle ticks by 100 and compare with up and down ticks */ + idle_ticks *= 100; + down_skip[cpu] = 0; + this_dbs_info->prev_cpu_idle_down = total_idle_ticks; + + freq_down_sampling_rate = dbs_tuners_ins.sampling_rate * + dbs_tuners_ins.sampling_down_factor; + down_idle_ticks = (100 - dbs_tuners_ins.down_threshold) * + sampling_rate_in_HZ(freq_down_sampling_rate); + + if (idle_ticks > down_idle_ticks ) { + freq_down_step = (5 * this_dbs_info->cur_policy->max) / 100; + + /* max freq cannot be less than 100. But who knows.... */ + if (unlikely(freq_down_step == 0)) + freq_down_step = 5; + + __cpufreq_driver_target(this_dbs_info->cur_policy, + this_dbs_info->cur_policy->cur - freq_down_step, + CPUFREQ_RELATION_H); + return; + } +} + +static void do_dbs_timer(void *data) +{ + int i; + down(&dbs_sem); + for (i = 0; i < NR_CPUS; i++) + if (cpu_online(i)) + dbs_check_cpu(i); + schedule_delayed_work(&dbs_work, + sampling_rate_in_HZ(dbs_tuners_ins.sampling_rate)); + up(&dbs_sem); +} + +static inline void dbs_timer_init(void) +{ + INIT_WORK(&dbs_work, do_dbs_timer, NULL); + schedule_work(&dbs_work); + return; +} + +static inline void dbs_timer_exit(void) +{ + cancel_delayed_work(&dbs_work); + return; +} + +static int cpufreq_governor_dbs(struct cpufreq_policy *policy, + unsigned int event) +{ + unsigned int cpu = policy->cpu; + struct cpu_dbs_info_s *this_dbs_info; + + this_dbs_info = &per_cpu(cpu_dbs_info, cpu); + + switch (event) { + case CPUFREQ_GOV_START: + if ((!cpu_online(cpu)) || + (!policy->cur)) + return -EINVAL; + + if (policy->cpuinfo.transition_latency > + (TRANSITION_LATENCY_LIMIT * 1000)) + return -EINVAL; + if (this_dbs_info->enable) /* Already enabled */ + break; + + down(&dbs_sem); + this_dbs_info->cur_policy = policy; + + this_dbs_info->prev_cpu_idle_up = + kstat_cpu(cpu).cpustat.idle + + kstat_cpu(cpu).cpustat.iowait; + this_dbs_info->prev_cpu_idle_down = + kstat_cpu(cpu).cpustat.idle + + kstat_cpu(cpu).cpustat.iowait; + this_dbs_info->enable = 1; + sysfs_create_group(&policy->kobj, &dbs_attr_group); + dbs_enable++; + /* + * Start the timerschedule work, when this governor + * is used for first time + */ + if (dbs_enable == 1) { + unsigned int latency; + /* policy latency is in nS. Convert it to uS first */ + + latency = policy->cpuinfo.transition_latency; + if (latency < 1000) + latency = 1000; + + def_sampling_rate = (latency / 1000) * + DEF_SAMPLING_RATE_LATENCY_MULTIPLIER; + dbs_tuners_ins.sampling_rate = def_sampling_rate; + + dbs_timer_init(); + } + + up(&dbs_sem); + break; + + case CPUFREQ_GOV_STOP: + down(&dbs_sem); + this_dbs_info->enable = 0; + sysfs_remove_group(&policy->kobj, &dbs_attr_group); + dbs_enable--; + /* + * Stop the timerschedule work, when this governor + * is used for first time + */ + if (dbs_enable == 0) + dbs_timer_exit(); + + up(&dbs_sem); + + break; + + case CPUFREQ_GOV_LIMITS: + down(&dbs_sem); + if (policy->max < this_dbs_info->cur_policy->cur) + __cpufreq_driver_target( + this_dbs_info->cur_policy, + policy->max, CPUFREQ_RELATION_H); + else if (policy->min > this_dbs_info->cur_policy->cur) + __cpufreq_driver_target( + this_dbs_info->cur_policy, + policy->min, CPUFREQ_RELATION_L); + up(&dbs_sem); + break; + } + return 0; +} + +struct cpufreq_governor cpufreq_gov_dbs = { + .name = "ondemand", + .governor = cpufreq_governor_dbs, + .owner = THIS_MODULE, +}; +EXPORT_SYMBOL(cpufreq_gov_dbs); + +static int __init cpufreq_gov_dbs_init(void) +{ + return cpufreq_register_governor(&cpufreq_gov_dbs); +} + +static void __exit cpufreq_gov_dbs_exit(void) +{ + /* Make sure that the scheduled work is indeed not running */ + flush_scheduled_work(); + + cpufreq_unregister_governor(&cpufreq_gov_dbs); +} + + +MODULE_AUTHOR ("Venkatesh Pallipadi "); +MODULE_DESCRIPTION ("'cpufreq_ondemand' - A dynamic cpufreq governor for " + "Low Latency Frequency Transition capable processors"); +MODULE_LICENSE ("GPL"); + +module_init(cpufreq_gov_dbs_init); +module_exit(cpufreq_gov_dbs_exit); diff --git a/drivers/cpufreq/proc_intf.c b/drivers/cpufreq/proc_intf.c index a0eaf6f1d..673977899 100644 --- a/drivers/cpufreq/proc_intf.c +++ b/drivers/cpufreq/proc_intf.c @@ -12,9 +12,12 @@ #include #include +#warning This module will be removed from the 2.6. kernel series soon after 2005-01-01 #define CPUFREQ_ALL_CPUS ((NR_CPUS)) +static unsigned int warning_print = 0; + /** * cpufreq_parse_policy - parse a policy string * @input_string: the string to parse. @@ -110,6 +113,13 @@ static int cpufreq_proc_read ( if (off != 0) goto end; + if (!warning_print) { + warning_print++; + printk(KERN_INFO "Access to /proc/cpufreq is deprecated and " + "will be removed from (new) 2.6. kernels soon " + "after 2005-01-01\n"); + } + p += sprintf(p, " minimum CPU frequency - maximum CPU frequency - policy\n"); for (i=0;iimask = (i); \ * That's why here are the following inline functions... */ -typedef unsigned long xram_p; +typedef void __iomem *xram_p; /* Get 32bit number from XRAM */ -static inline u32 xram_get_32 (xram_p x) +static inline u32 xram_get_32(xram_p x) { return ((sbus_readw(x + 0x00UL) << 16) | (sbus_readw(x + 0x02UL))); } /* Like the above, but when we don't care about the high 16 bits */ -static inline u32 xram_get_32low (xram_p x) +static inline u32 xram_get_32low(xram_p x) { return (u32) sbus_readw(x + 0x02UL); } -static inline u16 xram_get_16 (xram_p x) +static inline u16 xram_get_16(xram_p x) { return sbus_readw(x); } -static inline u8 xram_get_8 (xram_p x) +static inline u8 xram_get_8(xram_p x) { - if (x & (xram_p)0x1) { + if ((unsigned long)x & 0x1UL) { x = x - 1; return (u8) sbus_readw(x); } else { @@ -111,7 +111,7 @@ static inline u8 xram_get_8 (xram_p x) } } -static inline void xram_copy_from (void *p, xram_p x, int len) +static inline void xram_copy_from(void *p, xram_p x, int len) { for (len >>= 2; len > 0; len--, x += sizeof(u32)) { u32 val, *p32 = p; @@ -123,7 +123,7 @@ static inline void xram_copy_from (void *p, xram_p x, int len) } } -static inline void xram_copy_to (xram_p x, void *p, int len) +static inline void xram_copy_to(xram_p x, void *p, int len) { for (len >>= 2; len > 0; len--, x += sizeof(u32)) { u32 tmp, *p32 = p; @@ -135,7 +135,7 @@ static inline void xram_copy_to (xram_p x, void *p, int len) } } -static inline void xram_bzero (xram_p x, int len) +static inline void xram_bzero(xram_p x, int len) { for (len >>= 1; len > 0; len--, x += sizeof(u16)) sbus_writew(0, x); @@ -274,7 +274,7 @@ struct soc { soc_cq req[2]; /* Request CQs */ soc_cq rsp[2]; /* Response CQs */ int soc_no; - unsigned long regs; + void __iomem *regs; xram_p xram; fc_wwn wwn; u32 imask; /* Our copy of regs->imask */ diff --git a/drivers/fc4/socal.c b/drivers/fc4/socal.c index e158d7d26..5ee4dda53 100644 --- a/drivers/fc4/socal.c +++ b/drivers/fc4/socal.c @@ -60,7 +60,7 @@ static char *version = #define for_each_socal(s) for (s = socals; s; s = s->next) struct socal *socals = NULL; -static void socal_copy_from_xram(void *d, unsigned long xram, long size) +static void socal_copy_from_xram(void *d, void __iomem *xram, long size) { u32 *dp = (u32 *) d; while (size) { @@ -70,7 +70,7 @@ static void socal_copy_from_xram(void *d, unsigned long xram, long size) } } -static void socal_copy_to_xram(unsigned long xram, void *s, long size) +static void socal_copy_to_xram(void __iomem *xram, void *s, long size) { u32 *sp = (u32 *) s; while (size) { diff --git a/drivers/fc4/socal.h b/drivers/fc4/socal.h index a853fad92..2ff386b9f 100644 --- a/drivers/fc4/socal.h +++ b/drivers/fc4/socal.h @@ -295,9 +295,9 @@ struct socal { socal_cq req[4]; /* Request CQs */ socal_cq rsp[4]; /* Response CQs */ int socal_no; - unsigned long regs; - unsigned long xram; - unsigned long eeprom; + void __iomem *regs; + void __iomem *xram; + void __iomem *eeprom; fc_wwn wwn; u32 imask; /* Our copy of regs->imask */ u32 cfg; /* Our copy of regs->cfg */ diff --git a/drivers/i2c/Makefile b/drivers/i2c/Makefile index 2b48079f5..cd170395a 100644 --- a/drivers/i2c/Makefile +++ b/drivers/i2c/Makefile @@ -7,6 +7,9 @@ obj-$(CONFIG_I2C_CHARDEV) += i2c-dev.o obj-$(CONFIG_I2C_SENSOR) += i2c-sensor.o obj-y += busses/ chips/ algos/ +i2c-sensor-objs := i2c-sensor-detect.o i2c-sensor-vid.o + + ifeq ($(CONFIG_I2C_DEBUG_CORE),y) EXTRA_CFLAGS += -DDEBUG endif diff --git a/drivers/i2c/algos/Kconfig b/drivers/i2c/algos/Kconfig index 88982cd31..cf9832bdc 100644 --- a/drivers/i2c/algos/Kconfig +++ b/drivers/i2c/algos/Kconfig @@ -27,6 +27,17 @@ config I2C_ALGOPCF This support is also available as a module. If so, the module will be called i2c-algo-pcf. +config I2C_ALGOPCA + tristate "I2C PCA 9564 interfaces" + depends on I2C + help + This allows you to use a range of I2C adapters called PCA adapters. + Say Y if you own an I2C adapter belonging to this class and then say + Y to the specific driver for you adapter below. + + This support is also available as a module. If so, the module + will be called i2c-algo-pca. + config I2C_ALGOITE tristate "ITE I2C Algorithm" depends on MIPS_ITE8172 && I2C diff --git a/drivers/i2c/algos/Makefile b/drivers/i2c/algos/Makefile index df88f3f01..3545644be 100644 --- a/drivers/i2c/algos/Makefile +++ b/drivers/i2c/algos/Makefile @@ -4,6 +4,7 @@ obj-$(CONFIG_I2C_ALGOBIT) += i2c-algo-bit.o obj-$(CONFIG_I2C_ALGOPCF) += i2c-algo-pcf.o +obj-$(CONFIG_I2C_ALGOPCA) += i2c-algo-pca.o obj-$(CONFIG_I2C_ALGOITE) += i2c-algo-ite.o ifeq ($(CONFIG_I2C_DEBUG_ALGO),y) diff --git a/drivers/i2c/algos/i2c-algo-ite.c b/drivers/i2c/algos/i2c-algo-ite.c index acc7ab6a9..7405aac67 100644 --- a/drivers/i2c/algos/i2c-algo-ite.c +++ b/drivers/i2c/algos/i2c-algo-ite.c @@ -52,21 +52,15 @@ #define PM_IBSR IT8172_PCI_IO_BASE + IT_PM_DSR + 0x04 #define GPIO_CCR IT8172_PCI_IO_BASE + IT_GPCCR -/* ----- global defines ----------------------------------------------- */ -#define DEB(x) if (i2c_debug>=1) x #define DEB2(x) if (i2c_debug>=2) x #define DEB3(x) if (i2c_debug>=3) x /* print several statistical values*/ -#define DEBPROTO(x) if (i2c_debug>=9) x; - /* debug the protocol by showing transferred bits */ #define DEF_TIMEOUT 16 -/* ----- global variables --------------------------------------------- */ - /* module parameters: */ -static int i2c_debug=1; -static int iic_test=0; /* see if the line-setting functions work */ +static int i2c_debug; +static int iic_test; /* see if the line-setting functions work */ /* --- setting states on the bus with the right timing: --------------- */ @@ -804,8 +798,8 @@ MODULE_AUTHOR("MontaVista Software "); MODULE_DESCRIPTION("ITE iic algorithm"); MODULE_LICENSE("GPL"); -MODULE_PARM(iic_test, "i"); -MODULE_PARM(i2c_debug,"i"); +module_param(iic_test, bool, 0); +module_param(i2c_debug, int, S_IRUGO | S_IWUSR); MODULE_PARM_DESC(iic_test, "Test if the I2C bus is available"); MODULE_PARM_DESC(i2c_debug, diff --git a/drivers/i2c/algos/i2c-algo-pca.c b/drivers/i2c/algos/i2c-algo-pca.c new file mode 100644 index 000000000..38092b7a7 --- /dev/null +++ b/drivers/i2c/algos/i2c-algo-pca.c @@ -0,0 +1,395 @@ +/* + * i2c-algo-pca.c i2c driver algorithms for PCA9564 adapters + * Copyright (C) 2004 Arcom Control Systems + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include "i2c-algo-pca.h" + +#define DRIVER "i2c-algo-pca" + +#define DEB1(fmt, args...) do { if (i2c_debug>=1) printk(fmt, ## args); } while(0) +#define DEB2(fmt, args...) do { if (i2c_debug>=2) printk(fmt, ## args); } while(0) +#define DEB3(fmt, args...) do { if (i2c_debug>=3) printk(fmt, ## args); } while(0) + +static int i2c_debug=0; + +#define pca_outw(adap, reg, val) adap->write_byte(adap, reg, val) +#define pca_inw(adap, reg) adap->read_byte(adap, reg) + +#define pca_status(adap) pca_inw(adap, I2C_PCA_STA) +#define pca_clock(adap) adap->get_clock(adap) +#define pca_own(adap) adap->get_own(adap) +#define pca_set_con(adap, val) pca_outw(adap, I2C_PCA_CON, val) +#define pca_get_con(adap) pca_inw(adap, I2C_PCA_CON) +#define pca_wait(adap) adap->wait_for_interrupt(adap) + +/* + * Generate a start condition on the i2c bus. + * + * returns after the start condition has occured + */ +static void pca_start(struct i2c_algo_pca_data *adap) +{ + int sta = pca_get_con(adap); + DEB2("=== START\n"); + sta |= I2C_PCA_CON_STA; + sta &= ~(I2C_PCA_CON_STO|I2C_PCA_CON_SI); + pca_set_con(adap, sta); + pca_wait(adap); +} + +/* + * Generate a repeated start condition on the i2c bus + * + * return after the repeated start condition has occured + */ +static void pca_repeated_start(struct i2c_algo_pca_data *adap) +{ + int sta = pca_get_con(adap); + DEB2("=== REPEATED START\n"); + sta |= I2C_PCA_CON_STA; + sta &= ~(I2C_PCA_CON_STO|I2C_PCA_CON_SI); + pca_set_con(adap, sta); + pca_wait(adap); +} + +/* + * Generate a stop condition on the i2c bus + * + * returns after the stop condition has been generated + * + * STOPs do not generate an interrupt or set the SI flag, since the + * part returns the the idle state (0xf8). Hence we don't need to + * pca_wait here. + */ +static void pca_stop(struct i2c_algo_pca_data *adap) +{ + int sta = pca_get_con(adap); + DEB2("=== STOP\n"); + sta |= I2C_PCA_CON_STO; + sta &= ~(I2C_PCA_CON_STA|I2C_PCA_CON_SI); + pca_set_con(adap, sta); +} + +/* + * Send the slave address and R/W bit + * + * returns after the address has been sent + */ +static void pca_address(struct i2c_algo_pca_data *adap, + struct i2c_msg *msg) +{ + int sta = pca_get_con(adap); + int addr; + + addr = ( (0x7f & msg->addr) << 1 ); + if (msg->flags & I2C_M_RD ) + addr |= 1; + DEB2("=== SLAVE ADDRESS %#04x+%c=%#04x\n", + msg->addr, msg->flags & I2C_M_RD ? 'R' : 'W', addr); + + pca_outw(adap, I2C_PCA_DAT, addr); + + sta &= ~(I2C_PCA_CON_STO|I2C_PCA_CON_STA|I2C_PCA_CON_SI); + pca_set_con(adap, sta); + + pca_wait(adap); +} + +/* + * Transmit a byte. + * + * Returns after the byte has been transmitted + */ +static void pca_tx_byte(struct i2c_algo_pca_data *adap, + __u8 b) +{ + int sta = pca_get_con(adap); + DEB2("=== WRITE %#04x\n", b); + pca_outw(adap, I2C_PCA_DAT, b); + + sta &= ~(I2C_PCA_CON_STO|I2C_PCA_CON_STA|I2C_PCA_CON_SI); + pca_set_con(adap, sta); + + pca_wait(adap); +} + +/* + * Receive a byte + * + * returns immediately. + */ +static void pca_rx_byte(struct i2c_algo_pca_data *adap, + __u8 *b, int ack) +{ + *b = pca_inw(adap, I2C_PCA_DAT); + DEB2("=== READ %#04x %s\n", *b, ack ? "ACK" : "NACK"); +} + +/* + * Setup ACK or NACK for next received byte and wait for it to arrive. + * + * Returns after next byte has arrived. + */ +static void pca_rx_ack(struct i2c_algo_pca_data *adap, + int ack) +{ + int sta = pca_get_con(adap); + + sta &= ~(I2C_PCA_CON_STO|I2C_PCA_CON_STA|I2C_PCA_CON_SI|I2C_PCA_CON_AA); + + if ( ack ) + sta |= I2C_PCA_CON_AA; + + pca_set_con(adap, sta); + pca_wait(adap); +} + +/* + * Reset the i2c bus / SIO + */ +static void pca_reset(struct i2c_algo_pca_data *adap) +{ + /* apparently only an external reset will do it. not a lot can be done */ + printk(KERN_ERR DRIVER ": Haven't figured out how to do a reset yet\n"); +} + +static int pca_xfer(struct i2c_adapter *i2c_adap, + struct i2c_msg msgs[], + int num) +{ + struct i2c_algo_pca_data *adap = i2c_adap->algo_data; + struct i2c_msg *msg = NULL; + int curmsg; + int numbytes = 0; + int state; + + state = pca_status(adap); + if ( state != 0xF8 ) { + printk(KERN_ERR DRIVER ": bus is not idle. status is %#04x\n", state ); + /* FIXME: what to do. Force stop ? */ + return -EREMOTEIO; + } + + DEB1("{{{ XFER %d messages\n", num); + + if (i2c_debug>=2) { + for (curmsg = 0; curmsg < num; curmsg++) { + int addr, i; + msg = &msgs[curmsg]; + + addr = (0x7f & msg->addr) ; + + if (msg->flags & I2C_M_RD ) + printk(KERN_INFO " [%02d] RD %d bytes from %#02x [%#02x, ...]\n", + curmsg, msg->len, addr, (addr<<1) | 1); + else { + printk(KERN_INFO " [%02d] WR %d bytes to %#02x [%#02x%s", + curmsg, msg->len, addr, addr<<1, + msg->len == 0 ? "" : ", "); + for(i=0; i < msg->len; i++) + printk("%#04x%s", msg->buf[i], i == msg->len - 1 ? "" : ", "); + printk("]\n"); + } + } + } + + curmsg = 0; + while (curmsg < num) { + state = pca_status(adap); + + DEB3("STATE is 0x%02x\n", state); + msg = &msgs[curmsg]; + + switch (state) { + case 0xf8: /* On reset or stop the bus is idle */ + pca_start(adap); + break; + + case 0x08: /* A START condition has been transmitted */ + case 0x10: /* A repeated start condition has been transmitted */ + pca_address(adap, msg); + break; + + case 0x18: /* SLA+W has been transmitted; ACK has been received */ + case 0x28: /* Data byte in I2CDAT has been transmitted; ACK has been received */ + if (numbytes < msg->len) { + pca_tx_byte(adap, msg->buf[numbytes]); + numbytes++; + break; + } + curmsg++; numbytes = 0; + if (curmsg == num) + pca_stop(adap); + else + pca_repeated_start(adap); + break; + + case 0x20: /* SLA+W has been transmitted; NOT ACK has been received */ + DEB2("NOT ACK recieved after SLA+W\n"); + pca_stop(adap); + return -EREMOTEIO; + + case 0x40: /* SLA+R has been transmitted; ACK has been received */ + pca_rx_ack(adap, msg->len > 1); + break; + + case 0x50: /* Data bytes has been received; ACK has been returned */ + if (numbytes < msg->len) { + pca_rx_byte(adap, &msg->buf[numbytes], 1); + numbytes++; + pca_rx_ack(adap, numbytes < msg->len - 1); + break; + } + curmsg++; numbytes = 0; + if (curmsg == num) + pca_stop(adap); + else + pca_repeated_start(adap); + break; + + case 0x48: /* SLA+R has been transmitted; NOT ACK has been received */ + DEB2("NOT ACK received after SLA+R\n"); + pca_stop(adap); + return -EREMOTEIO; + + case 0x30: /* Data byte in I2CDAT has been transmitted; NOT ACK has been received */ + DEB2("NOT ACK recieved after data byte\n"); + return -EREMOTEIO; + + case 0x38: /* Arbitration lost during SLA+W, SLA+R or data bytes */ + DEB2("Arbitration lost\n"); + return -EREMOTEIO; + + case 0x58: /* Data byte has been received; NOT ACK has been returned */ + if ( numbytes == msg->len - 1 ) { + pca_rx_byte(adap, &msg->buf[numbytes], 0); + curmsg++; numbytes = 0; + if (curmsg == num) + pca_stop(adap); + else + pca_repeated_start(adap); + } else { + DEB2("NOT ACK sent after data byte received. " + "Not final byte. numbytes %d. len %d\n", + numbytes, msg->len); + pca_stop(adap); + return -EREMOTEIO; + } + break; + case 0x70: /* Bus error - SDA stuck low */ + DEB2("BUS ERROR - SDA Stuck low\n"); + pca_reset(adap); + return -EREMOTEIO; + case 0x90: /* Bus error - SCL stuck low */ + DEB2("BUS ERROR - SCL Stuck low\n"); + pca_reset(adap); + return -EREMOTEIO; + case 0x00: /* Bus error during master or slave mode due to illegal START or STOP condition */ + DEB2("BUS ERROR - Illegal START or STOP\n"); + pca_reset(adap); + return -EREMOTEIO; + default: + printk(KERN_ERR DRIVER ": unhandled SIO state 0x%02x\n", state); + break; + } + + } + + DEB1(KERN_CRIT "}}} transfered %d messages. " + "status is %#04x. control is %#04x\n", + num, pca_status(adap), + pca_get_con(adap)); + return curmsg; +} + +static u32 pca_func(struct i2c_adapter *adap) +{ + return I2C_FUNC_SMBUS_EMUL; +} + +static int pca_init(struct i2c_algo_pca_data *adap) +{ + static int freqs[] = {330,288,217,146,88,59,44,36}; + int own, clock; + + own = pca_own(adap); + clock = pca_clock(adap); + DEB1(KERN_INFO DRIVER ": own address is %#04x\n", own); + DEB1(KERN_INFO DRIVER ": clock freqeuncy is %dkHz\n", freqs[clock]); + + pca_outw(adap, I2C_PCA_ADR, own << 1); + + pca_set_con(adap, I2C_PCA_CON_ENSIO | clock); + udelay(500); /* 500 µs for oscilator to stabilise */ + + return 0; +} + +static struct i2c_algorithm pca_algo = { + .name = "PCA9564 algorithm", + .id = I2C_ALGO_PCA, + .master_xfer = pca_xfer, + .functionality = pca_func, +}; + +/* + * registering functions to load algorithms at runtime + */ +int i2c_pca_add_bus(struct i2c_adapter *adap) +{ + struct i2c_algo_pca_data *pca_adap = adap->algo_data; + int rval; + + /* register new adapter to i2c module... */ + + adap->id |= pca_algo.id; + adap->algo = &pca_algo; + + adap->timeout = 100; /* default values, should */ + adap->retries = 3; /* be replaced by defines */ + + rval = pca_init(pca_adap); + + if (!rval) + i2c_add_adapter(adap); + + return rval; +} + +int i2c_pca_del_bus(struct i2c_adapter *adap) +{ + return i2c_del_adapter(adap); +} + +EXPORT_SYMBOL(i2c_pca_add_bus); +EXPORT_SYMBOL(i2c_pca_del_bus); + +MODULE_AUTHOR("Ian Campbell "); +MODULE_DESCRIPTION("I2C-Bus PCA9564 algorithm"); +MODULE_LICENSE("GPL"); + +module_param(i2c_debug, int, 0); diff --git a/drivers/i2c/algos/i2c-algo-pca.h b/drivers/i2c/algos/i2c-algo-pca.h new file mode 100644 index 000000000..2fee07e05 --- /dev/null +++ b/drivers/i2c/algos/i2c-algo-pca.h @@ -0,0 +1,26 @@ +#ifndef I2C_PCA9564_H +#define I2C_PCA9564_H 1 + +#define I2C_PCA_STA 0x00 /* STATUS Read Only */ +#define I2C_PCA_TO 0x00 /* TIMEOUT Write Only */ +#define I2C_PCA_DAT 0x01 /* DATA Read/Write */ +#define I2C_PCA_ADR 0x02 /* OWN ADR Read/Write */ +#define I2C_PCA_CON 0x03 /* CONTROL Read/Write */ + +#define I2C_PCA_CON_AA 0x80 /* Assert Acknowledge */ +#define I2C_PCA_CON_ENSIO 0x40 /* Enable */ +#define I2C_PCA_CON_STA 0x20 /* Start */ +#define I2C_PCA_CON_STO 0x10 /* Stop */ +#define I2C_PCA_CON_SI 0x08 /* Serial Interrupt */ +#define I2C_PCA_CON_CR 0x07 /* Clock Rate (MASK) */ + +#define I2C_PCA_CON_330kHz 0x00 +#define I2C_PCA_CON_288kHz 0x01 +#define I2C_PCA_CON_217kHz 0x02 +#define I2C_PCA_CON_146kHz 0x03 +#define I2C_PCA_CON_88kHz 0x04 +#define I2C_PCA_CON_59kHz 0x05 +#define I2C_PCA_CON_44kHz 0x06 +#define I2C_PCA_CON_36kHz 0x07 + +#endif /* I2C_PCA9564_H */ diff --git a/drivers/i2c/algos/i2c-algo-pcf.c b/drivers/i2c/algos/i2c-algo-pcf.c index b53f65a12..6dc807abc 100644 --- a/drivers/i2c/algos/i2c-algo-pcf.c +++ b/drivers/i2c/algos/i2c-algo-pcf.c @@ -38,8 +38,6 @@ #include "i2c-algo-pcf.h" -/* ----- global defines ----------------------------------------------- */ -#define DEB(x) if (i2c_debug>=1) x #define DEB2(x) if (i2c_debug>=2) x #define DEB3(x) if (i2c_debug>=3) x /* print several statistical values*/ #define DEBPROTO(x) if (i2c_debug>=9) x; @@ -48,7 +46,7 @@ /* module parameters: */ -static int i2c_debug=0; +static int i2c_debug; /* --- setting states on the bus with the right timing: --------------- */ @@ -101,12 +99,6 @@ static int wait_for_bb(struct i2c_algo_pcf_data *adap) { } -static inline void pcf_sleep(unsigned long timeout) -{ - schedule_timeout( timeout * HZ); -} - - static int wait_for_pin(struct i2c_algo_pcf_data *adap, int *status) { int timeout = DEF_TIMEOUT; @@ -472,6 +464,6 @@ MODULE_AUTHOR("Hans Berglund "); MODULE_DESCRIPTION("I2C-Bus PCF8584 algorithm"); MODULE_LICENSE("GPL"); -MODULE_PARM(i2c_debug,"i"); +module_param(i2c_debug, int, S_IRUGO | S_IWUSR); MODULE_PARM_DESC(i2c_debug, "debug level - 0 off; 1 normal; 2,3 more verbose; 9 pcf-protocol"); diff --git a/drivers/i2c/busses/i2c-hydra.c b/drivers/i2c/busses/i2c-hydra.c index 7d6d69182..275da79e3 100644 --- a/drivers/i2c/busses/i2c-hydra.c +++ b/drivers/i2c/busses/i2c-hydra.c @@ -158,7 +158,7 @@ static void __devexit hydra_remove(struct pci_dev *dev) static struct pci_driver hydra_driver = { - .name = "hydra smbus", + .name = "hydra_smbus", .id_table = hydra_ids, .probe = hydra_probe, .remove = __devexit_p(hydra_remove), diff --git a/drivers/i2c/busses/i2c-i810.c b/drivers/i2c/busses/i2c-i810.c index fd11366cc..46bd56660 100644 --- a/drivers/i2c/busses/i2c-i810.c +++ b/drivers/i2c/busses/i2c-i810.c @@ -231,7 +231,7 @@ static void __devexit i810_remove(struct pci_dev *dev) } static struct pci_driver i810_driver = { - .name = "i810 smbus", + .name = "i810_smbus", .id_table = i810_ids, .probe = i810_probe, .remove = __devexit_p(i810_remove), diff --git a/drivers/i2c/busses/i2c-ite.c b/drivers/i2c/busses/i2c-ite.c index 917759c42..da926a619 100644 --- a/drivers/i2c/busses/i2c-ite.c +++ b/drivers/i2c/busses/i2c-ite.c @@ -54,10 +54,10 @@ #define DEFAULT_CLOCK 0x1b0e /* default 16MHz/(27+14) = 400KHz */ #define DEFAULT_OWN 0x55 -static int base = 0; -static int irq = 0; -static int clock = 0; -static int own = 0; +static int base; +static int irq; +static int clock; +static int own; static struct iic_ite gpi; static wait_queue_head_t iic_wait; @@ -102,14 +102,6 @@ static int iic_ite_getclock(void *data) } -#if 0 -static void iic_ite_sleep(unsigned long timeout) -{ - schedule_timeout( timeout * HZ); -} -#endif - - /* Put this process to sleep. We will wake up when the * IIC controller interrupts. */ @@ -254,10 +246,10 @@ MODULE_AUTHOR("MontaVista Software "); MODULE_DESCRIPTION("I2C-Bus adapter routines for ITE IIC bus adapter"); MODULE_LICENSE("GPL"); -MODULE_PARM(base, "i"); -MODULE_PARM(irq, "i"); -MODULE_PARM(clock, "i"); -MODULE_PARM(own, "i"); +module_param(base, int, 0); +module_param(irq, int, 0); +module_param(clock, int, 0); +module_param(own, int, 0); /* Called when module is loaded or when kernel is initialized. diff --git a/drivers/i2c/busses/i2c-ixp2000.c b/drivers/i2c/busses/i2c-ixp2000.c new file mode 100644 index 000000000..21cd54d02 --- /dev/null +++ b/drivers/i2c/busses/i2c-ixp2000.c @@ -0,0 +1,171 @@ +/* + * drivers/i2c/busses/i2c-ixp2000.c + * + * I2C adapter for IXP2000 systems using GPIOs for I2C bus + * + * Author: Deepak Saxena + * Based on IXDP2400 code by: Naeem M. Afzal + * Made generic by: Jeff Daly + * + * Copyright (c) 2003-2004 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. + * + * From Jeff Daly: + * + * I2C adapter driver for Intel IXDP2xxx platforms. This should work for any + * IXP2000 platform if it uses the HW GPIO in the same manner. Basically, + * SDA and SCL GPIOs have external pullups. Setting the respective GPIO to + * an input will make the signal a '1' via the pullup. Setting them to + * outputs will pull them down. + * + * The GPIOs are open drain signals and are used as configuration strap inputs + * during power-up so there's generally a buffer on the board that needs to be + * 'enabled' to drive the GPIOs. + */ + +#include +#ifdef CONFIG_I2C_DEBUG_BUS +#define DEBUG 1 +#endif + +#include +#include +#include +#include +#include +#include + +#include /* Pick up IXP42000-specific bits */ + +static inline int ixp2000_scl_pin(void *data) +{ + return ((struct ixp2000_i2c_pins*)data)->scl_pin; +} + +static inline int ixp2000_sda_pin(void *data) +{ + return ((struct ixp2000_i2c_pins*)data)->sda_pin; +} + + +static void ixp2000_bit_setscl(void *data, int val) +{ + int i = 5000; + + if (val) { + gpio_line_config(ixp2000_scl_pin(data), GPIO_IN); + while(!gpio_line_get(ixp2000_scl_pin(data)) && i--); + } else { + gpio_line_config(ixp2000_scl_pin(data), GPIO_OUT); + } +} + +static void ixp2000_bit_setsda(void *data, int val) +{ + if (val) { + gpio_line_config(ixp2000_sda_pin(data), GPIO_IN); + } else { + gpio_line_config(ixp2000_sda_pin(data), GPIO_OUT); + } +} + +static int ixp2000_bit_getscl(void *data) +{ + return gpio_line_get(ixp2000_scl_pin(data)); +} + +static int ixp2000_bit_getsda(void *data) +{ + return gpio_line_get(ixp2000_sda_pin(data)); +} + +struct ixp2000_i2c_data { + struct ixp2000_i2c_pins *gpio_pins; + struct i2c_adapter adapter; + struct i2c_algo_bit_data algo_data; +}; + +static int ixp2000_i2c_remove(struct device *dev) +{ + struct platform_device *plat_dev = to_platform_device(dev); + struct ixp2000_i2c_data *drv_data = dev_get_drvdata(&plat_dev->dev); + + dev_set_drvdata(&plat_dev->dev, NULL); + + i2c_bit_del_bus(&drv_data->adapter); + + kfree(drv_data); + + return 0; +} + +static int ixp2000_i2c_probe(struct device *dev) +{ + int err; + struct platform_device *plat_dev = to_platform_device(dev); + struct ixp2000_i2c_pins *gpio = plat_dev->dev.platform_data; + struct ixp2000_i2c_data *drv_data = + kmalloc(sizeof(struct ixp2000_i2c_data), GFP_KERNEL); + + if (!drv_data) + return -ENOMEM; + memzero(drv_data, sizeof(*drv_data)); + drv_data->gpio_pins = gpio; + + drv_data->algo_data.data = gpio; + drv_data->algo_data.setsda = ixp2000_bit_setsda; + drv_data->algo_data.setscl = ixp2000_bit_setscl; + drv_data->algo_data.getsda = ixp2000_bit_getsda; + drv_data->algo_data.getscl = ixp2000_bit_getscl; + drv_data->algo_data.udelay = 6; + drv_data->algo_data.mdelay = 6; + drv_data->algo_data.timeout = 100; + + drv_data->adapter.id = I2C_HW_B_IXP2000, + drv_data->adapter.algo_data = &drv_data->algo_data, + + drv_data->adapter.dev.parent = &plat_dev->dev; + + gpio_line_config(gpio->sda_pin, GPIO_IN); + gpio_line_config(gpio->scl_pin, GPIO_IN); + gpio_line_set(gpio->scl_pin, 0); + gpio_line_set(gpio->sda_pin, 0); + + if ((err = i2c_bit_add_bus(&drv_data->adapter)) != 0) { + dev_err(dev, "Could not install, error %d\n", err); + kfree(drv_data); + return err; + } + + dev_set_drvdata(&plat_dev->dev, drv_data); + + return 0; +} + +static struct device_driver ixp2000_i2c_driver = { + .name = "IXP2000-I2C", + .bus = &platform_bus_type, + .probe = ixp2000_i2c_probe, + .remove = ixp2000_i2c_remove, +}; + +static int __init ixp2000_i2c_init(void) +{ + return driver_register(&ixp2000_i2c_driver); +} + +static void __exit ixp2000_i2c_exit(void) +{ + driver_unregister(&ixp2000_i2c_driver); +} + +module_init(ixp2000_i2c_init); +module_exit(ixp2000_i2c_exit); + +MODULE_AUTHOR ("Deepak Saxena "); +MODULE_DESCRIPTION("IXP2000 GPIO-based I2C bus driver"); +MODULE_LICENSE("GPL"); + diff --git a/drivers/i2c/busses/i2c-mpc.c b/drivers/i2c/busses/i2c-mpc.c new file mode 100644 index 000000000..f77245e56 --- /dev/null +++ b/drivers/i2c/busses/i2c-mpc.c @@ -0,0 +1,392 @@ +/* + * (C) Copyright 2003-2004 + * Humboldt Solutions Ltd, adrian@humboldt.co.uk. + + * This is a combined i2c adapter and algorithm driver for the + * MPC107/Tsi107 PowerPC northbridge and processors that include + * the same I2C unit (8240, 8245, 85xx). + * + * Release 0.6 + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define MPC_I2C_ADDR 0x00 +#define MPC_I2C_FDR 0x04 +#define MPC_I2C_CR 0x08 +#define MPC_I2C_SR 0x0c +#define MPC_I2C_DR 0x10 +#define MPC_I2C_DFSRR 0x14 +#define MPC_I2C_REGION 0x20 + +#define CCR_MEN 0x80 +#define CCR_MIEN 0x40 +#define CCR_MSTA 0x20 +#define CCR_MTX 0x10 +#define CCR_TXAK 0x08 +#define CCR_RSTA 0x04 + +#define CSR_MCF 0x80 +#define CSR_MAAS 0x40 +#define CSR_MBB 0x20 +#define CSR_MAL 0x10 +#define CSR_SRW 0x04 +#define CSR_MIF 0x02 +#define CSR_RXAK 0x01 + +struct mpc_i2c { + char *base; + struct ocp_def *ocpdef; + u32 interrupt; + wait_queue_head_t queue; + struct i2c_adapter adap; +}; + +static __inline__ void writeccr(struct mpc_i2c *i2c, u32 x) +{ + writeb(x, i2c->base + MPC_I2C_CR); +} + +static irqreturn_t mpc_i2c_isr(int irq, void *dev_id, struct pt_regs *regs) +{ + struct mpc_i2c *i2c = dev_id; + if (readb(i2c->base + MPC_I2C_SR) & CSR_MIF) { + /* Read again to allow register to stabilise */ + i2c->interrupt = readb(i2c->base + MPC_I2C_SR); + writeb(0, i2c->base + MPC_I2C_SR); + wake_up_interruptible(&i2c->queue); + } + return IRQ_HANDLED; +} + +static int i2c_wait(struct mpc_i2c *i2c, unsigned timeout, int writing) +{ + DECLARE_WAITQUEUE(wait, current); + unsigned long orig_jiffies = jiffies; + u32 x; + int result = 0; + + if (i2c->ocpdef->irq == OCP_IRQ_NA) { + while (!(readb(i2c->base + MPC_I2C_SR) & CSR_MIF)) { + schedule(); + if (time_after(jiffies, orig_jiffies + timeout)) { + pr_debug("I2C: timeout\n"); + result = -EIO; + break; + } + } + x = readb(i2c->base + MPC_I2C_SR); + writeb(0, i2c->base + MPC_I2C_SR); + } else { + add_wait_queue(&i2c->queue, &wait); + while (!(i2c->interrupt & CSR_MIF)) { + set_current_state(TASK_INTERRUPTIBLE); + if (signal_pending(current)) { + pr_debug("I2C: Interrupted\n"); + result = -EINTR; + break; + } + if (time_after(jiffies, orig_jiffies + timeout)) { + pr_debug("I2C: timeout\n"); + result = -EIO; + break; + } + schedule_timeout(timeout); + } + current->state = TASK_RUNNING; + remove_wait_queue(&i2c->queue, &wait); + x = i2c->interrupt; + i2c->interrupt = 0; + } + + if (result < -0) + return result; + + if (!(x & CSR_MCF)) { + pr_debug("I2C: unfinished\n"); + return -EIO; + } + + if (x & CSR_MAL) { + pr_debug("I2C: MAL\n"); + return -EIO; + } + + if (writing && (x & CSR_RXAK)) { + pr_debug("I2C: No RXAK\n"); + /* generate stop */ + writeccr(i2c, CCR_MEN); + return -EIO; + } + return 0; +} + +static void mpc_i2c_setclock(struct mpc_i2c *i2c) +{ + struct ocp_fs_i2c_data *i2c_data = i2c->ocpdef->additions; + /* Set clock and filters */ + if (i2c_data && (i2c_data->flags & FS_I2C_SEPARATE_DFSRR)) { + writeb(0x31, i2c->base + MPC_I2C_FDR); + writeb(0x10, i2c->base + MPC_I2C_DFSRR); + } else if (i2c_data && (i2c_data->flags & FS_I2C_CLOCK_5200)) + writeb(0x3f, i2c->base + MPC_I2C_FDR); + else + writel(0x1031, i2c->base + MPC_I2C_FDR); +} + +static void mpc_i2c_start(struct mpc_i2c *i2c) +{ + /* Clear arbitration */ + writeb(0, i2c->base + MPC_I2C_SR); + /* Start with MEN */ + writeccr(i2c, CCR_MEN); +} + +static void mpc_i2c_stop(struct mpc_i2c *i2c) +{ + writeccr(i2c, CCR_MEN); +} + +static int mpc_write(struct mpc_i2c *i2c, int target, + const u8 * data, int length, int restart) +{ + int i; + unsigned timeout = HZ; + u32 flags = restart ? CCR_RSTA : 0; + + /* Start with MEN */ + if (!restart) + writeccr(i2c, CCR_MEN); + /* Start as master */ + writeccr(i2c, CCR_MIEN | CCR_MEN | CCR_MSTA | CCR_MTX | flags); + /* Write target byte */ + writeb((target << 1), i2c->base + MPC_I2C_DR); + + if (i2c_wait(i2c, timeout, 1) < 0) + return -1; + + for (i = 0; i < length; i++) { + /* Write data byte */ + writeb(data[i], i2c->base + MPC_I2C_DR); + + if (i2c_wait(i2c, timeout, 1) < 0) + return -1; + } + + return 0; +} + +static int mpc_read(struct mpc_i2c *i2c, int target, + u8 * data, int length, int restart) +{ + unsigned timeout = HZ; + int i; + u32 flags = restart ? CCR_RSTA : 0; + + /* Start with MEN */ + if (!restart) + writeccr(i2c, CCR_MEN); + /* Switch to read - restart */ + writeccr(i2c, CCR_MIEN | CCR_MEN | CCR_MSTA | CCR_MTX | flags); + /* Write target address byte - this time with the read flag set */ + writeb((target << 1) | 1, i2c->base + MPC_I2C_DR); + + if (i2c_wait(i2c, timeout, 1) < 0) + return -1; + + if (length) { + if (length == 1) + writeccr(i2c, CCR_MIEN | CCR_MEN | CCR_MSTA | CCR_TXAK); + else + writeccr(i2c, CCR_MIEN | CCR_MEN | CCR_MSTA); + /* Dummy read */ + readb(i2c->base + MPC_I2C_DR); + } + + for (i = 0; i < length; i++) { + if (i2c_wait(i2c, timeout, 0) < 0) + return -1; + + /* Generate txack on next to last byte */ + if (i == length - 2) + writeccr(i2c, CCR_MIEN | CCR_MEN | CCR_MSTA | CCR_TXAK); + /* Generate stop on last byte */ + if (i == length - 1) + writeccr(i2c, CCR_MIEN | CCR_MEN | CCR_TXAK); + data[i] = readb(i2c->base + MPC_I2C_DR); + } + + return length; +} + +static int mpc_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], int num) +{ + struct i2c_msg *pmsg; + int i; + int ret = 0; + unsigned long orig_jiffies = jiffies; + struct mpc_i2c *i2c = i2c_get_adapdata(adap); + + mpc_i2c_start(i2c); + + /* Allow bus up to 1s to become not busy */ + while (readb(i2c->base + MPC_I2C_SR) & CSR_MBB) { + if (signal_pending(current)) { + pr_debug("I2C: Interrupted\n"); + return -EINTR; + } + if (time_after(jiffies, orig_jiffies + HZ)) { + pr_debug("I2C: timeout\n"); + return -EIO; + } + schedule(); + } + + for (i = 0; ret >= 0 && i < num; i++) { + pmsg = &msgs[i]; + pr_debug("Doing %s %d bytes to 0x%02x - %d of %d messages\n", + pmsg->flags & I2C_M_RD ? "read" : "write", + pmsg->len, pmsg->addr, i + 1, num); + if (pmsg->flags & I2C_M_RD) + ret = + mpc_read(i2c, pmsg->addr, pmsg->buf, pmsg->len, i); + else + ret = + mpc_write(i2c, pmsg->addr, pmsg->buf, pmsg->len, i); + } + mpc_i2c_stop(i2c); + return (ret < 0) ? ret : num; +} + +static u32 mpc_functionality(struct i2c_adapter *adap) +{ + return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL; +} + +static struct i2c_algorithm mpc_algo = { + .name = "MPC algorithm", + .id = I2C_ALGO_MPC107, + .master_xfer = mpc_xfer, + .functionality = mpc_functionality, +}; + +static struct i2c_adapter mpc_ops = { + .owner = THIS_MODULE, + .name = "MPC adapter", + .id = I2C_ALGO_MPC107 | I2C_HW_MPC107, + .algo = &mpc_algo, + .class = I2C_CLASS_HWMON, + .timeout = 1, + .retries = 1 +}; + +static int __devinit mpc_i2c_probe(struct ocp_device *ocp) +{ + int result = 0; + struct mpc_i2c *i2c; + + if (!(i2c = kmalloc(sizeof(*i2c), GFP_KERNEL))) { + return -ENOMEM; + } + i2c->ocpdef = ocp->def; + init_waitqueue_head(&i2c->queue); + + if (!request_mem_region(ocp->def->paddr, MPC_I2C_REGION, "i2c-mpc")) { + printk(KERN_ERR "i2c-mpc - resource unavailable\n"); + return -ENODEV; + } + + i2c->base = ioremap(ocp->def->paddr, MPC_I2C_REGION); + + if (!i2c->base) { + printk(KERN_ERR "i2c-mpc - failed to map controller\n"); + result = -ENOMEM; + goto fail_map; + } + + if (ocp->def->irq != OCP_IRQ_NA) + if ((result = request_irq(ocp->def->irq, mpc_i2c_isr, + 0, "i2c-mpc", i2c)) < 0) { + printk(KERN_ERR + "i2c-mpc - failed to attach interrupt\n"); + goto fail_irq; + } + + i2c->adap = mpc_ops; + i2c_set_adapdata(&i2c->adap, i2c); + if ((result = i2c_add_adapter(&i2c->adap)) < 0) { + printk(KERN_ERR "i2c-mpc - failed to add adapter\n"); + goto fail_add; + } + + mpc_i2c_setclock(i2c); + ocp_set_drvdata(ocp, i2c); + return result; + + fail_add: + if (ocp->def->irq != OCP_IRQ_NA) + free_irq(ocp->def->irq, 0); + fail_irq: + iounmap(i2c->base); + fail_map: + release_mem_region(ocp->def->paddr, MPC_I2C_REGION); + kfree(i2c); + return result; +} +static void __devexit mpc_i2c_remove(struct ocp_device *ocp) +{ + struct mpc_i2c *i2c = ocp_get_drvdata(ocp); + ocp_set_drvdata(ocp, NULL); + i2c_del_adapter(&i2c->adap); + + if (ocp->def->irq != OCP_IRQ_NA) + free_irq(i2c->ocpdef->irq, i2c); + iounmap(i2c->base); + release_mem_region(i2c->ocpdef->paddr, MPC_I2C_REGION); + kfree(i2c); +} + +static struct ocp_device_id mpc_iic_ids[] __devinitdata = { + {.vendor = OCP_VENDOR_FREESCALE,.function = OCP_FUNC_IIC}, + {.vendor = OCP_VENDOR_INVALID} +}; + +MODULE_DEVICE_TABLE(ocp, mpc_iic_ids); + +static struct ocp_driver mpc_iic_driver = { + .name = "iic", + .id_table = mpc_iic_ids, + .probe = mpc_i2c_probe, + .remove = __devexit_p(mpc_i2c_remove) +}; + +static int __init iic_init(void) +{ + return ocp_register_driver(&mpc_iic_driver); +} + +static void __exit iic_exit(void) +{ + ocp_unregister_driver(&mpc_iic_driver); +} + +module_init(iic_init); +module_exit(iic_exit); + +MODULE_AUTHOR("Adrian Cox "); +MODULE_DESCRIPTION + ("I2C-Bus adapter for MPC107 bridge and MPC824x/85xx/52xx processors"); +MODULE_LICENSE("GPL"); diff --git a/drivers/i2c/busses/i2c-pca-isa.c b/drivers/i2c/busses/i2c-pca-isa.c new file mode 100644 index 000000000..9c611134d --- /dev/null +++ b/drivers/i2c/busses/i2c-pca-isa.c @@ -0,0 +1,184 @@ +/* + * i2c-pca-isa.c driver for PCA9564 on ISA boards + * Copyright (C) 2004 Arcom Control Systems + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include + +#include "../algos/i2c-algo-pca.h" + +#define IO_SIZE 4 + +#undef DEBUG_IO +//#define DEBUG_IO + +static unsigned long base = 0x330; +static int irq = 10; + +/* Data sheet recommends 59kHz for 100kHz operation due to variation + * in the actual clock rate */ +static int clock = I2C_PCA_CON_59kHz; + +static int own = 0x55; + +static wait_queue_head_t pca_wait; + +static int pca_isa_getown(struct i2c_algo_pca_data *adap) +{ + return (own); +} + +static int pca_isa_getclock(struct i2c_algo_pca_data *adap) +{ + return (clock); +} + +static void +pca_isa_writebyte(struct i2c_algo_pca_data *adap, int reg, int val) +{ +#ifdef DEBUG_IO + static char *names[] = { "T/O", "DAT", "ADR", "CON" }; + printk("*** write %s at %#lx <= %#04x\n", names[reg], base+reg, val); +#endif + outb(val, base+reg); +} + +static int +pca_isa_readbyte(struct i2c_algo_pca_data *adap, int reg) +{ + int res = inb(base+reg); +#ifdef DEBUG_IO + { + static char *names[] = { "STA", "DAT", "ADR", "CON" }; + printk("*** read %s => %#04x\n", names[reg], res); + } +#endif + return res; +} + +static int pca_isa_waitforinterrupt(struct i2c_algo_pca_data *adap) +{ + int ret = 0; + + if (irq > -1) { + ret = wait_event_interruptible(pca_wait, + pca_isa_readbyte(adap, I2C_PCA_CON) & I2C_PCA_CON_SI); + } else { + while ((pca_isa_readbyte(adap, I2C_PCA_CON) & I2C_PCA_CON_SI) == 0) + udelay(100); + } + return ret; +} + +static irqreturn_t pca_handler(int this_irq, void *dev_id, struct pt_regs *regs) { + wake_up_interruptible(&pca_wait); + return IRQ_HANDLED; +} + +static struct i2c_algo_pca_data pca_isa_data = { + .get_own = pca_isa_getown, + .get_clock = pca_isa_getclock, + .write_byte = pca_isa_writebyte, + .read_byte = pca_isa_readbyte, + .wait_for_interrupt = pca_isa_waitforinterrupt, +}; + +static struct i2c_adapter pca_isa_ops = { + .owner = THIS_MODULE, + .id = I2C_HW_A_ISA, + .algo_data = &pca_isa_data, + .name = "PCA9564 ISA Adapter", +}; + +static int __init pca_isa_init(void) +{ + + init_waitqueue_head(&pca_wait); + + printk(KERN_INFO "i2c-pca-isa: i/o base %#08lx. irq %d\n", base, irq); + + if (!request_region(base, IO_SIZE, "i2c-pca-isa")) { + printk(KERN_ERR "i2c-pca-isa: I/O address %#08lx is in use.\n", base); + goto out; + } + + if (irq > -1) { + if (request_irq(irq, pca_handler, 0, "i2c-pca-isa", &pca_isa_ops) < 0) { + printk(KERN_ERR "i2c-pca-isa: Request irq%d failed\n", irq); + goto out_region; + } + } + + if (i2c_pca_add_bus(&pca_isa_ops) < 0) { + printk(KERN_ERR "i2c-pca-isa: Failed to add i2c bus\n"); + goto out_irq; + } + + return 0; + + out_irq: + if (irq > -1) + free_irq(irq, &pca_isa_ops); + out_region: + release_region(base, IO_SIZE); + out: + return -ENODEV; +} + +static void pca_isa_exit(void) +{ + i2c_pca_del_bus(&pca_isa_ops); + + if (irq > 0) { + disable_irq(irq); + free_irq(irq, &pca_isa_ops); + } + release_region(base, IO_SIZE); +} + +MODULE_AUTHOR("Ian Campbell "); +MODULE_DESCRIPTION("ISA base PCA9564 driver"); +MODULE_LICENSE("GPL"); + +module_param(base, ulong, 0); +MODULE_PARM_DESC(base, "I/O base address"); + +module_param(irq, int, 0); +MODULE_PARM_DESC(irq, "IRQ"); +module_param(clock, int, 0); +MODULE_PARM_DESC(clock, "Clock rate as described in table 1 of PCA9564 datasheet"); + +module_param(own, int, 0); /* the driver can't do slave mode, so there's no real point in this */ + +module_init(pca_isa_init); +module_exit(pca_isa_exit); diff --git a/drivers/i2c/busses/i2c-prosavage.c b/drivers/i2c/busses/i2c-prosavage.c index 0aabe49e5..9552e53a3 100644 --- a/drivers/i2c/busses/i2c-prosavage.c +++ b/drivers/i2c/busses/i2c-prosavage.c @@ -314,7 +314,7 @@ static struct pci_device_id prosavage_pci_tbl[] = { }; static struct pci_driver prosavage_driver = { - .name = "prosavage-smbus", + .name = "prosavage_smbus", .id_table = prosavage_pci_tbl, .probe = prosavage_probe, .remove = prosavage_remove, diff --git a/drivers/i2c/busses/i2c-savage4.c b/drivers/i2c/busses/i2c-savage4.c index 33972a779..52d51948a 100644 --- a/drivers/i2c/busses/i2c-savage4.c +++ b/drivers/i2c/busses/i2c-savage4.c @@ -178,7 +178,7 @@ static void __devexit savage4_remove(struct pci_dev *dev) } static struct pci_driver savage4_driver = { - .name = "savage4 smbus", + .name = "savage4_smbus", .id_table = savage4_ids, .probe = savage4_probe, .remove = __devexit_p(savage4_remove), diff --git a/drivers/i2c/chips/adm1025.c b/drivers/i2c/chips/adm1025.c index d38336fbc..711eaa21f 100644 --- a/drivers/i2c/chips/adm1025.c +++ b/drivers/i2c/chips/adm1025.c @@ -455,7 +455,7 @@ static void adm1025_init_client(struct i2c_client *client) struct adm1025_data *data = i2c_get_clientdata(client); int i; - data->vrm = 82; + data->vrm = i2c_which_vrm(); /* * Set high limits diff --git a/drivers/i2c/chips/ds1621.c b/drivers/i2c/chips/ds1621.c index bf3e21495..3aa72985e 100644 --- a/drivers/i2c/chips/ds1621.c +++ b/drivers/i2c/chips/ds1621.c @@ -37,7 +37,7 @@ static unsigned int normal_isa_range[] = { I2C_CLIENT_ISA_END }; /* Insmod parameters */ SENSORS_INSMOD_1(ds1621); static int polarity = -1; -MODULE_PARM(polarity, "i"); +module_param(polarity, int, 0); MODULE_PARM_DESC(polarity, "Output's polarity: 0 = active high, 1 = active low"); /* Many DS1621 constants specified below */ diff --git a/drivers/i2c/chips/isp1301_omap.c b/drivers/i2c/chips/isp1301_omap.c new file mode 100644 index 000000000..11d0df3b0 --- /dev/null +++ b/drivers/i2c/chips/isp1301_omap.c @@ -0,0 +1,1660 @@ +/* + * isp1301_omap - ISP 1301 USB transceiver, talking to OMAP OTG controller + * + * Copyright (C) 2004 Texas Instruments + * Copyright (C) 2004 David Brownell + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You 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. + */ +#undef DEBUG +#undef VERBOSE + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + + +#ifndef DEBUG +#undef VERBOSE +#endif + + +#define DRIVER_VERSION "24 August 2004" +#define DRIVER_NAME (isp1301_driver.name) + +MODULE_DESCRIPTION("ISP1301 USB OTG Transceiver Driver"); +MODULE_LICENSE("GPL"); + +struct isp1301 { + struct otg_transceiver otg; + struct i2c_client client; + void (*i2c_release)(struct device *dev); + + int irq; + + u32 last_otg_ctrl; + unsigned working:1; + + struct timer_list timer; + + /* use keventd context to change the state for us */ + struct work_struct work; + + unsigned long todo; +# define WORK_UPDATE_ISP 0 /* update ISP from OTG */ +# define WORK_UPDATE_OTG 1 /* update OTG from ISP */ +# define WORK_HOST_RESUME 4 /* resume host */ +# define WORK_TIMER 6 /* timer fired */ +# define WORK_STOP 7 /* don't resubmit */ +}; + + +/* bits in OTG_CTRL_REG */ + +#define OTG_XCEIV_OUTPUTS \ + (OTG_ASESSVLD|OTG_BSESSEND|OTG_BSESSVLD|OTG_VBUSVLD|OTG_ID) +#define OTG_XCEIV_INPUTS \ + (OTG_PULLDOWN|OTG_PULLUP|OTG_DRV_VBUS|OTG_PD_VBUS|OTG_PU_VBUS|OTG_PU_ID) +#define OTG_CTRL_BITS \ + (OTG_A_BUSREQ|OTG_A_SETB_HNPEN|OTG_B_BUSREQ|OTG_B_HNPEN|OTG_BUSDROP) + /* and OTG_PULLUP is sometimes written */ + +#define OTG_CTRL_MASK (OTG_DRIVER_SEL| \ + OTG_XCEIV_OUTPUTS|OTG_XCEIV_INPUTS| \ + OTG_CTRL_BITS) + + +/*-------------------------------------------------------------------------*/ + +#ifdef CONFIG_MACH_OMAP_H2 + +/* board-specific PM hooks */ + +#include +#include +#include + + +#if defined(CONFIG_TPS65010) || defined(CONFIG_TPS65010_MODULE) + +#include + +#else + +static inline int tps65010_set_vbus_draw(unsigned mA) +{ + pr_debug("tps65010: draw %d mA (STUB)\n", mA); + return 0; +} + +#endif + +static void enable_vbus_draw(struct isp1301 *isp, unsigned mA) +{ + int status = tps65010_set_vbus_draw(mA); + if (status < 0) + pr_debug(" VBUS %d mA error %d\n", mA, status); +} + +static void enable_vbus_source(struct isp1301 *isp) +{ + /* this board won't supply more than 8mA vbus power. + * some boards can switch a 100ma "unit load" (or more). + */ +} + + +/* products will deliver OTG messages with LEDs, GUI, etc */ +static inline void notresponding(struct isp1301 *isp) +{ + printk(KERN_NOTICE "OTG device not responding.\n"); +} + + +#endif + +/*-------------------------------------------------------------------------*/ + +/* only two addresses possible */ +#define ISP_BASE 0x2c +static unsigned short normal_i2c[] = { + ISP_BASE, ISP_BASE + 1, + I2C_CLIENT_END }; +static unsigned short normal_i2c_range[] = { I2C_CLIENT_END }; + +I2C_CLIENT_INSMOD; + +static struct i2c_driver isp1301_driver; + +/* smbus apis are used for portability */ + +static inline u8 +isp1301_get_u8(struct isp1301 *isp, u8 reg) +{ + return i2c_smbus_read_byte_data(&isp->client, reg + 0); +} + +static inline int +isp1301_get_u16(struct isp1301 *isp, u8 reg) +{ + return i2c_smbus_read_word_data(&isp->client, reg); +} + +static inline int +isp1301_set_bits(struct isp1301 *isp, u8 reg, u8 bits) +{ + return i2c_smbus_write_byte_data(&isp->client, reg + 0, bits); +} + +static inline int +isp1301_clear_bits(struct isp1301 *isp, u8 reg, u8 bits) +{ + return i2c_smbus_write_byte_data(&isp->client, reg + 1, bits); +} + +/*-------------------------------------------------------------------------*/ + +/* identification */ +#define ISP1301_VENDOR_ID 0x00 /* u16 read */ +#define ISP1301_PRODUCT_ID 0x02 /* u16 read */ +#define ISP1301_BCD_DEVICE 0x14 /* u16 read */ + +#define I2C_VENDOR_ID_PHILIPS 0x04cc +#define I2C_PRODUCT_ID_PHILIPS_1301 0x1301 + +/* operational registers */ +#define ISP1301_MODE_CONTROL_1 0x04 /* u8 read, set, +1 clear */ +# define MC1_SPEED_REG (1 << 0) +# define MC1_SUSPEND_REG (1 << 1) +# define MC1_DAT_SE0 (1 << 2) +# define MC1_TRANSPARENT (1 << 3) +# define MC1_BDIS_ACON_EN (1 << 4) +# define MC1_OE_INT_EN (1 << 5) +# define MC1_UART_EN (1 << 6) +# define MC1_MASK 0x7f +#define ISP1301_MODE_CONTROL_2 0x12 /* u8 read, set, +1 clear */ +# define MC2_GLOBAL_PWR_DN (1 << 0) +# define MC2_SPD_SUSP_CTRL (1 << 1) +# define MC2_BI_DI (1 << 2) +# define MC2_TRANSP_BDIR0 (1 << 3) +# define MC2_TRANSP_BDIR1 (1 << 4) +# define MC2_AUDIO_EN (1 << 5) +# define MC2_PSW_EN (1 << 6) +# define MC2_EN2V7 (1 << 7) +#define ISP1301_OTG_CONTROL_1 0x06 /* u8 read, set, +1 clear */ +# define OTG1_DP_PULLUP (1 << 0) +# define OTG1_DM_PULLUP (1 << 1) +# define OTG1_DP_PULLDOWN (1 << 2) +# define OTG1_DM_PULLDOWN (1 << 3) +# define OTG1_ID_PULLDOWN (1 << 4) +# define OTG1_VBUS_DRV (1 << 5) +# define OTG1_VBUS_DISCHRG (1 << 6) +# define OTG1_VBUS_CHRG (1 << 7) +#define ISP1301_OTG_STATUS 0x10 /* u8 readonly */ +# define OTG_B_SESS_END (1 << 6) +# define OTG_B_SESS_VLD (1 << 7) + +#define ISP1301_INTERRUPT_SOURCE 0x08 /* u8 read */ +#define ISP1301_INTERRUPT_LATCH 0x0A /* u8 read, set, +1 clear */ + +#define ISP1301_INTERRUPT_FALLING 0x0C /* u8 read, set, +1 clear */ +#define ISP1301_INTERRUPT_RISING 0x0E /* u8 read, set, +1 clear */ + +/* same bitfields in all interrupt registers */ +# define INTR_VBUS_VLD (1 << 0) +# define INTR_SESS_VLD (1 << 1) +# define INTR_DP_HI (1 << 2) +# define INTR_ID_GND (1 << 3) +# define INTR_DM_HI (1 << 4) +# define INTR_ID_FLOAT (1 << 5) +# define INTR_BDIS_ACON (1 << 6) +# define INTR_CR_INT (1 << 7) + +/*-------------------------------------------------------------------------*/ + +static const char *state_string(enum usb_otg_state state) +{ + switch (state) { + case OTG_STATE_A_IDLE: return "a_idle"; + case OTG_STATE_A_WAIT_VRISE: return "a_wait_vrise"; + case OTG_STATE_A_WAIT_BCON: return "a_wait_bcon"; + case OTG_STATE_A_HOST: return "a_host"; + case OTG_STATE_A_SUSPEND: return "a_suspend"; + case OTG_STATE_A_PERIPHERAL: return "a_peripheral"; + case OTG_STATE_A_WAIT_VFALL: return "a_wait_vfall"; + case OTG_STATE_A_VBUS_ERR: return "a_vbus_err"; + case OTG_STATE_B_IDLE: return "b_idle"; + case OTG_STATE_B_SRP_INIT: return "b_srp_init"; + case OTG_STATE_B_PERIPHERAL: return "b_peripheral"; + case OTG_STATE_B_WAIT_ACON: return "b_wait_acon"; + case OTG_STATE_B_HOST: return "b_host"; + default: return "UNDEFINED"; + } +} + +static inline const char *state_name(struct isp1301 *isp) +{ + return state_string(isp->otg.state); +} + +#ifdef VERBOSE +#define dev_vdbg dev_dbg +#else +#define dev_vdbg(dev, fmt, arg...) do{}while(0) +#endif + +/*-------------------------------------------------------------------------*/ + +/* NOTE: some of this ISP1301 setup is specific to H2 boards; + * not everything is guarded by board-specific checks, or even using + * omap_usb_config data to deduce MC1_DAT_SE0 and MC2_BI_DI. + * + * ALSO: this currently doesn't use ISP1301 low-power modes + * while OTG is running. + */ + +static void power_down(struct isp1301 *isp) +{ + isp->otg.state = OTG_STATE_UNDEFINED; + + // isp1301_set_bits(isp, ISP1301_MODE_CONTROL_2, MC2_GLOBAL_PWR_DN); + isp1301_set_bits(isp, ISP1301_MODE_CONTROL_1, MC1_SUSPEND_REG); + + isp1301_clear_bits(isp, ISP1301_OTG_CONTROL_1, OTG1_ID_PULLDOWN); + isp1301_clear_bits(isp, ISP1301_MODE_CONTROL_1, MC1_DAT_SE0); +} + +static void power_up(struct isp1301 *isp) +{ + // isp1301_clear_bits(isp, ISP1301_MODE_CONTROL_2, MC2_GLOBAL_PWR_DN); + isp1301_clear_bits(isp, ISP1301_MODE_CONTROL_1, MC1_SUSPEND_REG); + + /* do this only when cpu is driving transceiver, + * so host won't see a low speed device... + */ + isp1301_set_bits(isp, ISP1301_MODE_CONTROL_1, MC1_DAT_SE0); +} + +#define NO_HOST_SUSPEND + +static int host_suspend(struct isp1301 *isp) +{ +#ifdef NO_HOST_SUSPEND + return 0; +#else + struct device *dev; + + if (!isp->otg.host) + return -ENODEV; + + /* Currently ASSUMES only the OTG port matters; + * other ports could be active... + */ + dev = isp->otg.host->controller; + return dev->driver->suspend(dev, 3, 0); +#endif +} + +static int host_resume(struct isp1301 *isp) +{ +#ifdef NO_HOST_SUSPEND + return 0; +#else + struct device *dev; + + if (!isp->otg.host) + return -ENODEV; + + dev = isp->otg.host->controller; + return dev->driver->resume(dev, 0); +#endif +} + +static int gadget_suspend(struct isp1301 *isp) +{ + isp->otg.gadget->b_hnp_enable = 0; + isp->otg.gadget->a_hnp_support = 0; + isp->otg.gadget->a_alt_hnp_support = 0; + return usb_gadget_vbus_disconnect(isp->otg.gadget); +} + +/*-------------------------------------------------------------------------*/ + +#define TIMER_MINUTES 10 +#define TIMER_JIFFIES (TIMER_MINUTES * 60 * HZ) + +/* Almost all our I2C messaging comes from a work queue's task context. + * NOTE: guaranteeing certain response times might mean we shouldn't + * share keventd's work queue; a realtime task might be safest. + */ +void +isp1301_defer_work(struct isp1301 *isp, int work) +{ + int status; + + if (isp && !test_and_set_bit(work, &isp->todo)) { + (void) get_device(&isp->client.dev); + status = schedule_work(&isp->work); + if (!status && !isp->working) + dev_vdbg(&isp->client.dev, + "work item %d may be lost\n", work); + } +} + +/* called from irq handlers */ +static void a_idle(struct isp1301 *isp, const char *tag) +{ + if (isp->otg.state == OTG_STATE_A_IDLE) + return; + + isp->otg.default_a = 1; + if (isp->otg.host) { + isp->otg.host->is_b_host = 0; + host_suspend(isp); + } + if (isp->otg.gadget) { + isp->otg.gadget->is_a_peripheral = 1; + gadget_suspend(isp); + } + isp->otg.state = OTG_STATE_A_IDLE; + isp->last_otg_ctrl = OTG_CTRL_REG = OTG_CTRL_REG & OTG_XCEIV_OUTPUTS; + pr_debug(" --> %s/%s\n", state_name(isp), tag); +} + +/* called from irq handlers */ +static void b_idle(struct isp1301 *isp, const char *tag) +{ + if (isp->otg.state == OTG_STATE_B_IDLE) + return; + + isp->otg.default_a = 0; + if (isp->otg.host) { + isp->otg.host->is_b_host = 1; + host_suspend(isp); + } + if (isp->otg.gadget) { + isp->otg.gadget->is_a_peripheral = 0; + gadget_suspend(isp); + } + isp->otg.state = OTG_STATE_B_IDLE; + isp->last_otg_ctrl = OTG_CTRL_REG = OTG_CTRL_REG & OTG_XCEIV_OUTPUTS; + pr_debug(" --> %s/%s\n", state_name(isp), tag); +} + +static void +dump_regs(struct isp1301 *isp, const char *label) +{ +#ifdef DEBUG + u8 ctrl = isp1301_get_u8(isp, ISP1301_OTG_CONTROL_1); + u8 status = isp1301_get_u8(isp, ISP1301_OTG_STATUS); + u8 src = isp1301_get_u8(isp, ISP1301_INTERRUPT_SOURCE); + + pr_debug("otg: %06x, %s %s, otg/%02x stat/%02x.%02x\n", + OTG_CTRL_REG, label, state_name(isp), + ctrl, status, src); + /* mode control and irq enables don't change much */ +#endif +} + +/*-------------------------------------------------------------------------*/ + +#ifdef CONFIG_USB_OTG + +/* + * The OMAP OTG controller handles most of the OTG state transitions. + * + * We translate isp1301 outputs (mostly voltage comparator status) into + * OTG inputs; OTG outputs (mostly pullup/pulldown controls) and HNP state + * flags into isp1301 inputs ... and infer state transitions. + */ + +#ifdef VERBOSE + +static void check_state(struct isp1301 *isp, const char *tag) +{ + enum usb_otg_state state = OTG_STATE_UNDEFINED; + u8 fsm = OTG_TEST_REG & 0x0ff; + unsigned extra = 0; + + switch (fsm) { + + /* default-b */ + case 0x0: + state = OTG_STATE_B_IDLE; + break; + case 0x3: + case 0x7: + extra = 1; + case 0x1: + state = OTG_STATE_B_PERIPHERAL; + break; + case 0x11: + state = OTG_STATE_B_SRP_INIT; + break; + + /* extra dual-role default-b states */ + case 0x12: + case 0x13: + case 0x16: + extra = 1; + case 0x17: + state = OTG_STATE_B_WAIT_ACON; + break; + case 0x34: + state = OTG_STATE_B_HOST; + break; + + /* default-a */ + case 0x36: + state = OTG_STATE_A_IDLE; + break; + case 0x3c: + state = OTG_STATE_A_WAIT_VFALL; + break; + case 0x7d: + state = OTG_STATE_A_VBUS_ERR; + break; + case 0x9e: + case 0x9f: + extra = 1; + case 0x89: + state = OTG_STATE_A_PERIPHERAL; + break; + case 0xb7: + state = OTG_STATE_A_WAIT_VRISE; + break; + case 0xb8: + state = OTG_STATE_A_WAIT_BCON; + break; + case 0xb9: + state = OTG_STATE_A_HOST; + break; + case 0xba: + state = OTG_STATE_A_SUSPEND; + break; + default: + break; + } + if (isp->otg.state == state && !extra) + return; + pr_debug("otg: %s FSM %s/%02x, %s, %06x\n", tag, + state_string(state), fsm, state_name(isp), OTG_CTRL_REG); +} + +#else + +static inline void check_state(struct isp1301 *isp, const char *tag) { } + +#endif + +/* outputs from ISP1301_INTERRUPT_SOURCE */ +static void update_otg1(struct isp1301 *isp, u8 int_src) +{ + u32 otg_ctrl; + + otg_ctrl = OTG_CTRL_REG + & OTG_CTRL_MASK + & ~OTG_XCEIV_INPUTS + & ~(OTG_ID|OTG_ASESSVLD|OTG_VBUSVLD); + if (int_src & INTR_SESS_VLD) + otg_ctrl |= OTG_ASESSVLD; + else if (isp->otg.state == OTG_STATE_A_WAIT_VFALL) { + a_idle(isp, "vfall"); + otg_ctrl &= ~OTG_CTRL_BITS; + } + if (int_src & INTR_VBUS_VLD) + otg_ctrl |= OTG_VBUSVLD; + if (int_src & INTR_ID_GND) { /* default-A */ + if (isp->otg.state == OTG_STATE_B_IDLE + || isp->otg.state == OTG_STATE_UNDEFINED) { + a_idle(isp, "init"); + return; + } + } else { /* default-B */ + otg_ctrl |= OTG_ID; + if (isp->otg.state == OTG_STATE_A_IDLE + || isp->otg.state == OTG_STATE_UNDEFINED) { + b_idle(isp, "init"); + return; + } + } + OTG_CTRL_REG = otg_ctrl; +} + +/* outputs from ISP1301_OTG_STATUS */ +static void update_otg2(struct isp1301 *isp, u8 otg_status) +{ + u32 otg_ctrl; + + otg_ctrl = OTG_CTRL_REG + & OTG_CTRL_MASK + & ~OTG_XCEIV_INPUTS + & ~(OTG_BSESSVLD|OTG_BSESSEND); + if (otg_status & OTG_B_SESS_VLD) + otg_ctrl |= OTG_BSESSVLD; + else if (otg_status & OTG_B_SESS_END) + otg_ctrl |= OTG_BSESSEND; + OTG_CTRL_REG = otg_ctrl; +} + +/* inputs going to ISP1301 */ +static void otg_update_isp(struct isp1301 *isp) +{ + u32 otg_ctrl, otg_change; + u8 set = OTG1_DM_PULLDOWN, clr = OTG1_DM_PULLUP; + + otg_ctrl = OTG_CTRL_REG; + otg_change = otg_ctrl ^ isp->last_otg_ctrl; + isp->last_otg_ctrl = otg_ctrl; + otg_ctrl = otg_ctrl & OTG_XCEIV_INPUTS; + + switch (isp->otg.state) { + case OTG_STATE_B_IDLE: + case OTG_STATE_B_PERIPHERAL: + case OTG_STATE_B_SRP_INIT: + if (!(otg_ctrl & OTG_PULLUP)) { + // if (otg_ctrl & OTG_B_HNPEN) { + if (isp->otg.gadget->b_hnp_enable) { + isp->otg.state = OTG_STATE_B_WAIT_ACON; + pr_debug(" --> b_wait_acon\n"); + } + goto pulldown; + } +pullup: + set |= OTG1_DP_PULLUP; + clr |= OTG1_DP_PULLDOWN; + break; + case OTG_STATE_A_SUSPEND: + case OTG_STATE_A_PERIPHERAL: + if (otg_ctrl & OTG_PULLUP) + goto pullup; + /* FALLTHROUGH */ + // case OTG_STATE_B_WAIT_ACON: + default: +pulldown: + set |= OTG1_DP_PULLDOWN; + clr |= OTG1_DP_PULLUP; + break; + } + +# define toggle(OTG,ISP) do { \ + if (otg_ctrl & OTG) set |= ISP; \ + else clr |= ISP; \ + } while (0) + + if (!(isp->otg.host)) + otg_ctrl &= ~OTG_DRV_VBUS; + + switch (isp->otg.state) { + case OTG_STATE_A_SUSPEND: + if (otg_ctrl & OTG_DRV_VBUS) { + set |= OTG1_VBUS_DRV; + break; + } + /* HNP failed for some reason (A_AIDL_BDIS timeout) */ + notresponding(isp); + + /* FALLTHROUGH */ + case OTG_STATE_A_VBUS_ERR: + isp->otg.state = OTG_STATE_A_WAIT_VFALL; + pr_debug(" --> a_wait_vfall\n"); + /* FALLTHROUGH */ + case OTG_STATE_A_WAIT_VFALL: + /* FIXME usbcore thinks port power is still on ... */ + clr |= OTG1_VBUS_DRV; + break; + case OTG_STATE_A_IDLE: + if (otg_ctrl & OTG_DRV_VBUS) { + isp->otg.state = OTG_STATE_A_WAIT_VRISE; + pr_debug(" --> a_wait_vrise\n"); + } + /* FALLTHROUGH */ + default: + toggle(OTG_DRV_VBUS, OTG1_VBUS_DRV); + } + + toggle(OTG_PU_VBUS, OTG1_VBUS_CHRG); + toggle(OTG_PD_VBUS, OTG1_VBUS_DISCHRG); + +# undef toggle + + isp1301_set_bits(isp, ISP1301_OTG_CONTROL_1, set); + isp1301_clear_bits(isp, ISP1301_OTG_CONTROL_1, clr); + + /* HNP switch to host or peripheral; and SRP */ + if (otg_change & OTG_PULLUP) { + switch (isp->otg.state) { + case OTG_STATE_B_IDLE: + if (clr & OTG1_DP_PULLUP) + break; + isp->otg.state = OTG_STATE_B_PERIPHERAL; + pr_debug(" --> b_peripheral\n"); + break; + case OTG_STATE_A_SUSPEND: + if (clr & OTG1_DP_PULLUP) + break; + isp->otg.state = OTG_STATE_A_PERIPHERAL; + pr_debug(" --> a_peripheral\n"); + break; + default: + break; + } + OTG_CTRL_REG |= OTG_PULLUP; + } + + check_state(isp, __FUNCTION__); + dump_regs(isp, "otg->isp1301"); +} + +static irqreturn_t omap_otg_irq(int irq, void *_isp, struct pt_regs *regs) +{ + u16 otg_irq = OTG_IRQ_SRC_REG; + u32 otg_ctrl; + int ret = IRQ_NONE; + struct isp1301 *isp = _isp; + + /* update ISP1301 transciever from OTG controller */ + if (otg_irq & OPRT_CHG) { + OTG_IRQ_SRC_REG = OPRT_CHG; + isp1301_defer_work(isp, WORK_UPDATE_ISP); + ret = IRQ_HANDLED; + + /* SRP to become b_peripheral failed */ + } else if (otg_irq & B_SRP_TMROUT) { + pr_debug("otg: B_SRP_TIMEOUT, %06x\n", OTG_CTRL_REG); + notresponding(isp); + + /* gadget drivers that care should monitor all kinds of + * remote wakeup (SRP, normal) using their own timer + * to give "check cable and A-device" messages. + */ + if (isp->otg.state == OTG_STATE_B_SRP_INIT) + b_idle(isp, "srp_timeout"); + + OTG_IRQ_SRC_REG = B_SRP_TMROUT; + ret = IRQ_HANDLED; + + /* HNP to become b_host failed */ + } else if (otg_irq & B_HNP_FAIL) { + pr_debug("otg: %s B_HNP_FAIL, %06x\n", + state_name(isp), OTG_CTRL_REG); + notresponding(isp); + + otg_ctrl = OTG_CTRL_REG; + otg_ctrl |= OTG_BUSDROP; + otg_ctrl &= OTG_CTRL_MASK & ~OTG_XCEIV_INPUTS; + OTG_CTRL_REG = otg_ctrl; + + /* subset of b_peripheral()... */ + isp->otg.state = OTG_STATE_B_PERIPHERAL; + pr_debug(" --> b_peripheral\n"); + + OTG_IRQ_SRC_REG = B_HNP_FAIL; + ret = IRQ_HANDLED; + + /* detect SRP from B-device ... */ + } else if (otg_irq & A_SRP_DETECT) { + pr_debug("otg: %s SRP_DETECT, %06x\n", + state_name(isp), OTG_CTRL_REG); + + isp1301_defer_work(isp, WORK_UPDATE_OTG); + switch (isp->otg.state) { + case OTG_STATE_A_IDLE: + if (!isp->otg.host) + break; + isp1301_defer_work(isp, WORK_HOST_RESUME); + otg_ctrl = OTG_CTRL_REG; + otg_ctrl |= OTG_A_BUSREQ; + otg_ctrl &= ~(OTG_BUSDROP|OTG_B_BUSREQ) + & ~OTG_XCEIV_INPUTS + & OTG_CTRL_MASK; + OTG_CTRL_REG = otg_ctrl; + break; + default: + break; + } + + OTG_IRQ_SRC_REG = A_SRP_DETECT; + ret = IRQ_HANDLED; + + /* timer expired: T(a_wait_bcon) and maybe T(a_wait_vrise) + * we don't track them separately + */ + } else if (otg_irq & A_REQ_TMROUT) { + otg_ctrl = OTG_CTRL_REG; + pr_info("otg: BCON_TMOUT from %s, %06x\n", + state_name(isp), otg_ctrl); + notresponding(isp); + + otg_ctrl |= OTG_BUSDROP; + otg_ctrl &= ~OTG_A_BUSREQ & OTG_CTRL_MASK & ~OTG_XCEIV_INPUTS; + OTG_CTRL_REG = otg_ctrl; + isp->otg.state = OTG_STATE_A_WAIT_VFALL; + + OTG_IRQ_SRC_REG = A_REQ_TMROUT; + ret = IRQ_HANDLED; + + /* A-supplied voltage fell too low; overcurrent */ + } else if (otg_irq & A_VBUS_ERR) { + otg_ctrl = OTG_CTRL_REG; + printk(KERN_ERR "otg: %s, VBUS_ERR %04x ctrl %06x\n", + state_name(isp), otg_irq, otg_ctrl); + + otg_ctrl |= OTG_BUSDROP; + otg_ctrl &= ~OTG_A_BUSREQ & OTG_CTRL_MASK & ~OTG_XCEIV_INPUTS; + OTG_CTRL_REG = otg_ctrl; + isp->otg.state = OTG_STATE_A_VBUS_ERR; + + OTG_IRQ_SRC_REG = A_VBUS_ERR; + ret = IRQ_HANDLED; + + /* switch driver; the transciever code activates it, + * ungating the udc clock or resuming OHCI. + */ + } else if (otg_irq & DRIVER_SWITCH) { + int kick = 0; + + otg_ctrl = OTG_CTRL_REG; + printk(KERN_NOTICE "otg: %s, SWITCH to %s, ctrl %06x\n", + state_name(isp), + (otg_ctrl & OTG_DRIVER_SEL) + ? "gadget" : "host", + otg_ctrl); + isp1301_defer_work(isp, WORK_UPDATE_ISP); + + /* role is peripheral */ + if (otg_ctrl & OTG_DRIVER_SEL) { + switch (isp->otg.state) { + case OTG_STATE_A_IDLE: + b_idle(isp, __FUNCTION__); + break; + default: + break; + } + isp1301_defer_work(isp, WORK_UPDATE_ISP); + + /* role is host */ + } else { + if (!(otg_ctrl & OTG_ID)) { + otg_ctrl &= OTG_CTRL_MASK & ~OTG_XCEIV_INPUTS; + OTG_CTRL_REG = otg_ctrl | OTG_A_BUSREQ; + } + + if (isp->otg.host) { + switch (isp->otg.state) { + case OTG_STATE_B_WAIT_ACON: + isp->otg.state = OTG_STATE_B_HOST; + pr_debug(" --> b_host\n"); + kick = 1; + break; + case OTG_STATE_A_WAIT_BCON: + isp->otg.state = OTG_STATE_A_HOST; + pr_debug(" --> a_host\n"); + break; + case OTG_STATE_A_PERIPHERAL: + isp->otg.state = OTG_STATE_A_WAIT_BCON; + pr_debug(" --> a_wait_bcon\n"); + break; + default: + break; + } + isp1301_defer_work(isp, WORK_HOST_RESUME); + } + } + + OTG_IRQ_SRC_REG = DRIVER_SWITCH; + ret = IRQ_HANDLED; + + if (kick) + usb_bus_start_enum(isp->otg.host, + isp->otg.host->otg_port); + } + + check_state(isp, __FUNCTION__); + return ret; +} + +static struct platform_device *otg_dev; + +static int otg_init(struct isp1301 *isp) +{ + if (!otg_dev) + return -ENODEV; + + dump_regs(isp, __FUNCTION__); + /* some of these values are board-specific... */ + OTG_SYSCON_2_REG |= OTG_EN + /* for B-device: */ + | SRP_GPDATA /* 9msec Bdev D+ pulse */ + | SRP_GPDVBUS /* discharge after VBUS pulse */ + // | (3 << 24) /* 2msec VBUS pulse */ + /* for A-device: */ + | (0 << 20) /* 200ms nominal A_WAIT_VRISE timer */ + | SRP_DPW /* detect 167+ns SRP pulses */ + | SRP_DATA | SRP_VBUS /* accept both kinds of SRP pulse */ + ; + + update_otg1(isp, isp1301_get_u8(isp, ISP1301_INTERRUPT_SOURCE)); + update_otg2(isp, isp1301_get_u8(isp, ISP1301_OTG_STATUS)); + + check_state(isp, __FUNCTION__); + pr_debug("otg: %s, %s %06x\n", + state_name(isp), __FUNCTION__, OTG_CTRL_REG); + + OTG_IRQ_EN_REG = DRIVER_SWITCH | OPRT_CHG + | B_SRP_TMROUT | B_HNP_FAIL + | A_VBUS_ERR | A_SRP_DETECT | A_REQ_TMROUT; + OTG_SYSCON_2_REG |= OTG_EN; + + return 0; +} + +static int otg_probe(struct device *dev) +{ + // struct omap_usb_config *config = dev->platform_data; + + otg_dev = to_platform_device(dev); + return 0; +} + +static int otg_remove(struct device *dev) +{ + otg_dev = 0; + return 0; +} + +struct device_driver omap_otg_driver = { + .name = "omap_otg", + .bus = &platform_bus_type, + .probe = otg_probe, + .remove = otg_remove, +}; + +static int otg_bind(struct isp1301 *isp) +{ + int status; + + if (otg_dev) + return -EBUSY; + + status = driver_register(&omap_otg_driver); + if (status < 0) + return status; + + if (otg_dev) + status = request_irq(otg_dev->resource[1].start, omap_otg_irq, + SA_INTERRUPT, DRIVER_NAME, isp); + else + status = -ENODEV; + + if (status < 0) + driver_unregister(&omap_otg_driver); + return status; +} + +static void otg_unbind(struct isp1301 *isp) +{ + if (!otg_dev) + return; + free_irq(otg_dev->resource[1].start, isp); +} + +#else + +/* OTG controller isn't clocked */ + +#endif /* CONFIG_USB_OTG */ + +/*-------------------------------------------------------------------------*/ + +static void b_peripheral(struct isp1301 *isp) +{ + enable_vbus_draw(isp, 8); + OTG_CTRL_REG = OTG_CTRL_REG & OTG_XCEIV_OUTPUTS; + usb_gadget_vbus_connect(isp->otg.gadget); + +#ifdef CONFIG_USB_OTG + otg_update_isp(isp); +#else + /* UDC driver just set OTG_BSESSVLD */ + isp1301_set_bits(isp, ISP1301_OTG_CONTROL_1, OTG1_DP_PULLUP); + isp1301_clear_bits(isp, ISP1301_OTG_CONTROL_1, OTG1_DP_PULLDOWN); + isp->otg.state = OTG_STATE_B_PERIPHERAL; + pr_debug(" --> b_peripheral\n"); + dump_regs(isp, "2periph"); +#endif +} + +static int isp_update_otg(struct isp1301 *isp, u8 stat) +{ + u8 isp_stat, isp_bstat; + enum usb_otg_state state = isp->otg.state; + + if (stat & INTR_BDIS_ACON) + pr_debug("OTG: BDIS_ACON, %s\n", state_name(isp)); + + /* start certain state transitions right away */ + isp_stat = isp1301_get_u8(isp, ISP1301_INTERRUPT_SOURCE); + if (isp_stat & INTR_ID_GND) { + if (isp->otg.default_a) { + switch (state) { + case OTG_STATE_B_IDLE: + a_idle(isp, "idle"); + /* FALLTHROUGH */ + case OTG_STATE_A_IDLE: + enable_vbus_source(isp); + /* FALLTHROUGH */ + case OTG_STATE_A_WAIT_VRISE: + /* we skip over OTG_STATE_A_WAIT_BCON, since + * the HC will transition to A_HOST (or + * A_SUSPEND!) without our noticing except + * when HNP is used. + */ + if (isp_stat & INTR_VBUS_VLD) + isp->otg.state = OTG_STATE_A_HOST; + break; + case OTG_STATE_A_WAIT_VFALL: + if (!(isp_stat & INTR_SESS_VLD)) + a_idle(isp, "vfell"); + break; + default: + if (!(isp_stat & INTR_VBUS_VLD)) + isp->otg.state = OTG_STATE_A_VBUS_ERR; + break; + } + isp_bstat = isp1301_get_u8(isp, ISP1301_OTG_STATUS); + } else { + switch (state) { + case OTG_STATE_B_PERIPHERAL: + case OTG_STATE_B_HOST: + case OTG_STATE_B_WAIT_ACON: + usb_gadget_vbus_disconnect(isp->otg.gadget); + break; + default: + break; + } + if (state != OTG_STATE_A_IDLE) + a_idle(isp, "id"); + if (isp->otg.host && state == OTG_STATE_A_IDLE) + isp1301_defer_work(isp, WORK_HOST_RESUME); + isp_bstat = 0; + } + } else { + /* if user unplugged mini-A end of cable, + * don't bypass A_WAIT_VFALL. + */ + if (isp->otg.default_a) { + switch (state) { + default: + isp->otg.state = OTG_STATE_A_WAIT_VFALL; + break; + case OTG_STATE_A_WAIT_VFALL: + state = OTG_STATE_A_IDLE; + /* khubd may take a while to notice and + * handle this disconnect, so don't go + * to B_IDLE quite yet. + */ + break; + case OTG_STATE_A_IDLE: + host_suspend(isp); + isp1301_clear_bits(isp, ISP1301_MODE_CONTROL_1, + MC1_BDIS_ACON_EN); + isp->otg.state = OTG_STATE_B_IDLE; + OTG_CTRL_REG &= OTG_CTRL_REG & OTG_CTRL_MASK + & ~OTG_CTRL_BITS; + break; + case OTG_STATE_B_IDLE: + break; + } + } + isp_bstat = isp1301_get_u8(isp, ISP1301_OTG_STATUS); + + switch (isp->otg.state) { + case OTG_STATE_B_PERIPHERAL: + case OTG_STATE_B_WAIT_ACON: + case OTG_STATE_B_HOST: + if (likely(isp_bstat & OTG_B_SESS_VLD)) + break; + enable_vbus_draw(isp, 0); +#ifndef CONFIG_USB_OTG + /* UDC driver will clear OTG_BSESSVLD */ + isp1301_set_bits(isp, ISP1301_OTG_CONTROL_1, + OTG1_DP_PULLDOWN); + isp1301_clear_bits(isp, ISP1301_OTG_CONTROL_1, + OTG1_DP_PULLUP); + dump_regs(isp, __FUNCTION__); +#endif + /* FALLTHROUGH */ + case OTG_STATE_B_SRP_INIT: + b_idle(isp, __FUNCTION__); + OTG_CTRL_REG &= OTG_CTRL_REG & OTG_XCEIV_OUTPUTS; + /* FALLTHROUGH */ + case OTG_STATE_B_IDLE: + if (isp->otg.gadget && (isp_bstat & OTG_B_SESS_VLD)) { +#ifdef CONFIG_USB_OTG + update_otg1(isp, isp_stat); + update_otg2(isp, isp_bstat); +#endif + b_peripheral(isp); + } else if (!(isp_stat & (INTR_VBUS_VLD|INTR_SESS_VLD))) + isp_bstat |= OTG_B_SESS_END; + break; + case OTG_STATE_A_WAIT_VFALL: + break; + default: + pr_debug("otg: unsupported b-device %s\n", + state_name(isp)); + break; + } + } + + if (state != isp->otg.state) + pr_debug(" isp, %s -> %s\n", + state_string(state), state_name(isp)); + +#ifdef CONFIG_USB_OTG + /* update the OTG controller state to match the isp1301; may + * trigger OPRT_CHG irqs for changes going to the isp1301. + */ + update_otg1(isp, isp_stat); + update_otg2(isp, isp_bstat); + check_state(isp, __FUNCTION__); +#endif + + dump_regs(isp, "isp1301->otg"); +} + +/*-------------------------------------------------------------------------*/ + +static u8 isp1301_clear_latch(struct isp1301 *isp) +{ + u8 latch = isp1301_get_u8(isp, ISP1301_INTERRUPT_LATCH); + isp1301_clear_bits(isp, ISP1301_INTERRUPT_LATCH, latch); + return latch; +} + +static void +isp1301_work(void *data) +{ + struct isp1301 *isp = data; + int stop; + + /* implicit lock: we're the only task using this device */ + isp->working = 1; + do { + stop = test_bit(WORK_STOP, &isp->todo); + +#ifdef CONFIG_USB_OTG + /* transfer state from otg engine to isp1301 */ + if (test_and_clear_bit(WORK_UPDATE_ISP, &isp->todo)) { + otg_update_isp(isp); + put_device(&isp->client.dev); + } +#endif + /* transfer state from isp1301 to otg engine */ + if (test_and_clear_bit(WORK_UPDATE_OTG, &isp->todo)) { + u8 stat = isp1301_clear_latch(isp); + + isp_update_otg(isp, stat); + put_device(&isp->client.dev); + } + + if (test_and_clear_bit(WORK_HOST_RESUME, &isp->todo)) { + u32 otg_ctrl; + + /* + * skip A_WAIT_VRISE; hc transitions invisibly + * skip A_WAIT_BCON; same. + */ + switch (isp->otg.state) { + case OTG_STATE_A_WAIT_BCON: + case OTG_STATE_A_WAIT_VRISE: + isp->otg.state = OTG_STATE_A_HOST; + pr_debug(" --> a_host\n"); + otg_ctrl = OTG_CTRL_REG; + otg_ctrl |= OTG_A_BUSREQ; + otg_ctrl &= ~(OTG_BUSDROP|OTG_B_BUSREQ) + & OTG_CTRL_MASK; + OTG_CTRL_REG = otg_ctrl; + break; + case OTG_STATE_B_WAIT_ACON: + isp->otg.state = OTG_STATE_B_HOST; + pr_debug(" --> b_host (acon)\n"); + break; + case OTG_STATE_B_HOST: + case OTG_STATE_B_IDLE: + case OTG_STATE_A_IDLE: + break; + default: + pr_debug(" host resume in %s\n", + state_name(isp)); + } + host_resume(isp); + // mdelay(10); + put_device(&isp->client.dev); + } + + if (test_and_clear_bit(WORK_TIMER, &isp->todo)) { +#ifdef VERBOSE + dump_regs(isp, "timer"); + if (!stop) + mod_timer(&isp->timer, jiffies + TIMER_JIFFIES); +#endif + put_device(&isp->client.dev); + } + + if (isp->todo) + dev_vdbg(&isp->client.dev, + "work done, todo = 0x%lx\n", + isp->todo); + if (stop) { + dev_dbg(&isp->client.dev, "stop\n"); + break; + } + } while (isp->todo); + isp->working = 0; +} + +static irqreturn_t isp1301_irq(int irq, void *isp, struct pt_regs *regs) +{ + isp1301_defer_work(isp, WORK_UPDATE_OTG); + return IRQ_HANDLED; +} + +static void isp1301_timer(unsigned long _isp) +{ + isp1301_defer_work((void *)_isp, WORK_TIMER); +} + +/*-------------------------------------------------------------------------*/ + +static void isp1301_release(struct device *dev) +{ + struct isp1301 *isp; + + isp = container_of(dev, struct isp1301, client.dev); + + /* ugly -- i2c hijacks our memory hook to wait_for_completion() */ + if (isp->i2c_release) + isp->i2c_release(dev); + kfree (isp); +} + +static struct isp1301 *the_transceiver; + +static int isp1301_detach_client(struct i2c_client *i2c) +{ + struct isp1301 *isp; + + isp = container_of(i2c, struct isp1301, client); + + isp1301_clear_bits(isp, ISP1301_INTERRUPT_FALLING, ~0); + isp1301_clear_bits(isp, ISP1301_INTERRUPT_RISING, ~0); + free_irq(isp->irq, isp); +#ifdef CONFIG_USB_OTG + otg_unbind(isp); +#endif + if (machine_is_omap_h2()) + omap_free_gpio(2); + + isp->timer.data = 0; + set_bit(WORK_STOP, &isp->todo); + del_timer_sync(&isp->timer); + flush_scheduled_work(); + + put_device(&i2c->dev); + the_transceiver = 0; + + return i2c_detach_client(i2c); +} + +/*-------------------------------------------------------------------------*/ + +/* NOTE: three modes are possible here, only one of which + * will be standards-conformant on any given system: + * + * - OTG mode (dual-role), required if there's a Mini-AB connector + * - HOST mode, for when there's one or more A (host) connectors + * - DEVICE mode, for when there's a B/Mini-B (device) connector + * + * As a rule, you won't have an isp1301 chip unless it's there to + * support the OTG mode. Other modes help testing USB controllers + * in isolation from (full) OTG support, or maybe so later board + * revisions can help to support those feature. + */ + +#ifdef CONFIG_USB_OTG + +static int isp1301_otg_enable(struct isp1301 *isp) +{ + power_up(isp); + otg_init(isp); + + /* NOTE: since we don't change this, this provides + * a few more interrupts than are strictly needed. + */ + isp1301_set_bits(isp, ISP1301_INTERRUPT_RISING, + INTR_VBUS_VLD | INTR_SESS_VLD | INTR_ID_GND); + isp1301_set_bits(isp, ISP1301_INTERRUPT_FALLING, + INTR_VBUS_VLD | INTR_SESS_VLD | INTR_ID_GND); + + dev_info(&isp->client.dev, "ready for dual-role USB ...\n"); + + return 0; +} + +#endif + +/* add or disable the host device+driver */ +static int +isp1301_set_host(struct otg_transceiver *otg, struct usb_bus *host) +{ + struct isp1301 *isp = container_of(otg, struct isp1301, otg); + + if (!otg || isp != the_transceiver) + return -ENODEV; + + if (!host) { + OTG_IRQ_EN_REG = 0; + power_down(isp); + isp->otg.host = 0; + return 0; + } + +#ifdef CONFIG_USB_OTG + isp->otg.host = host; + dev_dbg(&isp->client.dev, "registered host\n"); + host_suspend(isp); + if (isp->otg.gadget) + return isp1301_otg_enable(isp); + return 0; + +#elif !defined(CONFIG_USB_GADGET_OMAP) + // FIXME update its refcount + isp->otg.host = host; + + power_up(isp); + + if (machine_is_omap_h2()) + isp1301_set_bits(isp, ISP1301_MODE_CONTROL_1, MC1_DAT_SE0); + + dev_info(&isp->client.dev, "A-Host sessions ok\n"); + isp1301_set_bits(isp, ISP1301_INTERRUPT_RISING, + INTR_ID_GND); + isp1301_set_bits(isp, ISP1301_INTERRUPT_FALLING, + INTR_ID_GND); + + /* If this has a Mini-AB connector, this mode is highly + * nonstandard ... but can be handy for testing, especially with + * the Mini-A end of an OTG cable. (Or something nonstandard + * like MiniB-to-StandardB, maybe built with a gender mender.) + */ + isp1301_set_bits(isp, ISP1301_OTG_CONTROL_1, OTG1_VBUS_DRV); + + dump_regs(isp, __FUNCTION__); + + return 0; + +#else + dev_dbg(&isp->client.dev, "host sessions not allowed\n"); + return -EINVAL; +#endif + +} + +static int +isp1301_set_peripheral(struct otg_transceiver *otg, struct usb_gadget *gadget) +{ + struct isp1301 *isp = container_of(otg, struct isp1301, otg); + + if (!otg || isp != the_transceiver) + return -ENODEV; + + if (!gadget) { + OTG_IRQ_EN_REG = 0; + if (!isp->otg.default_a) + enable_vbus_draw(isp, 0); + usb_gadget_vbus_disconnect(isp->otg.gadget); + isp->otg.gadget = 0; + power_down(isp); + return 0; + } + +#ifdef CONFIG_USB_OTG + isp->otg.gadget = gadget; + dev_dbg(&isp->client.dev, "registered gadget\n"); + /* gadget driver may be suspended until vbus_connect () */ + if (isp->otg.host) + return isp1301_otg_enable(isp); + return 0; + +#elif !defined(CONFIG_USB_OHCI_HCD) && !defined(CONFIG_USB_OHCI_HCD_MODULE) + isp->otg.gadget = gadget; + // FIXME update its refcount + + OTG_CTRL_REG = (OTG_CTRL_REG & OTG_CTRL_MASK + & ~(OTG_XCEIV_OUTPUTS|OTG_CTRL_BITS)) + | OTG_ID; + power_up(isp); + isp->otg.state = OTG_STATE_B_IDLE; + + if (machine_is_omap_h2()) + isp1301_set_bits(isp, ISP1301_MODE_CONTROL_1, MC1_DAT_SE0); + + isp1301_set_bits(isp, ISP1301_INTERRUPT_RISING, + INTR_SESS_VLD); + isp1301_set_bits(isp, ISP1301_INTERRUPT_FALLING, + INTR_VBUS_VLD); + dev_info(&isp->client.dev, "B-Peripheral sessions ok\n"); + dump_regs(isp, __FUNCTION__); + + /* If this has a Mini-AB connector, this mode is highly + * nonstandard ... but can be handy for testing, so long + * as you don't plug a Mini-A cable into the jack. + */ + if (isp1301_get_u8(isp, ISP1301_INTERRUPT_SOURCE) & INTR_VBUS_VLD) + b_peripheral(isp); + + return 0; + +#else + dev_dbg(&isp->client.dev, "peripheral sessions not allowed\n"); + return -EINVAL; +#endif +} + + +/*-------------------------------------------------------------------------*/ + +static int +isp1301_set_power(struct otg_transceiver *dev, unsigned mA) +{ + if (!the_transceiver) + return -ENODEV; + if (dev->state == OTG_STATE_B_PERIPHERAL) + enable_vbus_draw(the_transceiver, mA); + return 0; +} + +static int +isp1301_start_srp(struct otg_transceiver *dev) +{ + struct isp1301 *isp = container_of(dev, struct isp1301, otg); + u32 otg_ctrl; + + if (!dev || isp != the_transceiver + || isp->otg.state != OTG_STATE_B_IDLE) + return -ENODEV; + + otg_ctrl = OTG_CTRL_REG; + if (!(otg_ctrl & OTG_BSESSEND)) + return -EINVAL; + + otg_ctrl |= OTG_B_BUSREQ; + otg_ctrl &= ~OTG_A_BUSREQ & OTG_CTRL_MASK; + OTG_CTRL_REG = otg_ctrl; + isp->otg.state = OTG_STATE_B_SRP_INIT; + + pr_debug("otg: SRP, %s ... %06x\n", state_name(isp), OTG_CTRL_REG); +#ifdef CONFIG_USB_OTG + check_state(isp, __FUNCTION__); +#endif + return 0; +} + +static int +isp1301_start_hnp(struct otg_transceiver *dev) +{ +#ifdef CONFIG_USB_OTG + struct isp1301 *isp = container_of(dev, struct isp1301, otg); + + if (!dev || isp != the_transceiver) + return -ENODEV; + if (isp->otg.default_a && (isp->otg.host == NULL + || !isp->otg.host->b_hnp_enable)) + return -ENOTCONN; + if (!isp->otg.default_a && (isp->otg.gadget == NULL + || !isp->otg.gadget->b_hnp_enable)) + return -ENOTCONN; + + /* We want hardware to manage most HNP protocol timings. + * So do this part as early as possible... + */ + switch (isp->otg.state) { + case OTG_STATE_B_HOST: + isp->otg.state = OTG_STATE_B_PERIPHERAL; + /* caller will suspend next */ + break; + case OTG_STATE_A_HOST: +#if 0 + /* autoconnect mode avoids irq latency bugs */ + isp1301_set_bits(isp, ISP1301_MODE_CONTROL_1, + MC1_BDIS_ACON_EN); +#endif + /* caller must suspend then clear A_BUSREQ */ + usb_gadget_vbus_connect(isp->otg.gadget); + OTG_CTRL_REG |= OTG_A_SETB_HNPEN; + + break; + case OTG_STATE_A_PERIPHERAL: + /* initiated by B-Host suspend */ + break; + default: + return -EILSEQ; + } + pr_debug("otg: HNP %s, %06x ...\n", + state_name(isp), OTG_CTRL_REG); + check_state(isp, __FUNCTION__); + return 0; +#else + /* srp-only */ + return -EINVAL; +#endif +} + +/*-------------------------------------------------------------------------*/ + +/* no error returns, they'd just make bus scanning stop */ +static int isp1301_probe(struct i2c_adapter *bus, int address, int kind) +{ + int status; + struct isp1301 *isp; + struct i2c_client *i2c; + + if (the_transceiver) + return 0; + + isp = kmalloc(sizeof *isp, GFP_KERNEL); + if (!isp) + return 0; + + memset(isp, 0, sizeof *isp); + + INIT_WORK(&isp->work, isp1301_work, isp); + init_timer(&isp->timer); + isp->timer.function = isp1301_timer; + isp->timer.data = (unsigned long) isp; + + isp->irq = -1; + isp->client.addr = address; + i2c_set_clientdata(&isp->client, isp); + isp->client.adapter = bus; + isp->client.id = 1301; + isp->client.driver = &isp1301_driver; + strlcpy(isp->client.name, DRIVER_NAME, I2C_NAME_SIZE); + i2c = &isp->client; + + /* if this is a true probe, verify the chip ... */ + if (kind < 0) { + status = isp1301_get_u16(isp, ISP1301_VENDOR_ID); + if (status != I2C_VENDOR_ID_PHILIPS) { + dev_dbg(&bus->dev, "addr %d not philips id: %d\n", + address, status); + goto fail1; + } + status = isp1301_get_u16(isp, ISP1301_PRODUCT_ID); + if (status != I2C_PRODUCT_ID_PHILIPS_1301) { + dev_dbg(&bus->dev, "%d not isp1301, %d\n", + address, status); + goto fail1; + } + } + + status = i2c_attach_client(i2c); + if (status < 0) { + dev_dbg(&bus->dev, "can't attach %s to device %d, err %d\n", + DRIVER_NAME, address, status); +fail1: + kfree(isp); + return 0; + } + isp->i2c_release = i2c->dev.release; + i2c->dev.release = isp1301_release; + + /* initial development used chiprev 2.00 */ + status = i2c_smbus_read_word_data(i2c, ISP1301_BCD_DEVICE); + dev_info(&i2c->dev, "chiprev %x.%02x, driver " DRIVER_VERSION "\n", + status >> 8, status & 0xff); + + /* make like power-on reset */ + isp1301_clear_bits(isp, ISP1301_MODE_CONTROL_1, MC1_MASK); + + isp1301_set_bits(isp, ISP1301_MODE_CONTROL_2, MC2_BI_DI); + isp1301_clear_bits(isp, ISP1301_MODE_CONTROL_2, ~MC2_BI_DI); + + isp1301_set_bits(isp, ISP1301_OTG_CONTROL_1, + OTG1_DM_PULLDOWN | OTG1_DP_PULLDOWN); + isp1301_clear_bits(isp, ISP1301_OTG_CONTROL_1, + ~(OTG1_DM_PULLDOWN | OTG1_DP_PULLDOWN)); + + isp1301_clear_bits(isp, ISP1301_INTERRUPT_LATCH, ~0); + isp1301_clear_bits(isp, ISP1301_INTERRUPT_FALLING, ~0); + isp1301_clear_bits(isp, ISP1301_INTERRUPT_RISING, ~0); + +#ifdef CONFIG_USB_OTG + status = otg_bind(isp); + if (status < 0) { + dev_dbg(&i2c->dev, "can't bind OTG\n"); + goto fail2; + } +#endif + + if (machine_is_omap_h2()) { + /* full speed signaling by default */ + isp1301_set_bits(isp, ISP1301_MODE_CONTROL_1, + MC1_SPEED_REG); + isp1301_set_bits(isp, ISP1301_MODE_CONTROL_2, + MC2_SPD_SUSP_CTRL); + + /* IRQ wired at M14 */ + omap_cfg_reg(M14_1510_GPIO2); + isp->irq = OMAP_GPIO_IRQ(2); + omap_request_gpio(2); + omap_set_gpio_direction(2, 1); + omap_set_gpio_edge_ctrl(2, OMAP_GPIO_FALLING_EDGE); + } + + status = request_irq(isp->irq, isp1301_irq, + SA_SAMPLE_RANDOM, DRIVER_NAME, isp); + if (status < 0) { + dev_dbg(&i2c->dev, "can't get IRQ %d, err %d\n", + isp->irq, status); +#ifdef CONFIG_USB_OTG +fail2: +#endif + i2c_detach_client(i2c); + goto fail1; + } + + isp->otg.dev = &isp->client.dev; + isp->otg.label = DRIVER_NAME; + + isp->otg.set_host = isp1301_set_host, + isp->otg.set_peripheral = isp1301_set_peripheral, + isp->otg.set_power = isp1301_set_power, + isp->otg.start_srp = isp1301_start_srp, + isp->otg.start_hnp = isp1301_start_hnp, + + enable_vbus_draw(isp, 0); + power_down(isp); + the_transceiver = isp; + +#ifdef CONFIG_USB_OTG + update_otg1(isp, isp1301_get_u8(isp, ISP1301_INTERRUPT_SOURCE)); + update_otg2(isp, isp1301_get_u8(isp, ISP1301_OTG_STATUS)); +#endif + + dump_regs(isp, __FUNCTION__); + +#ifdef VERBOSE + mod_timer(&isp->timer, jiffies + TIMER_JIFFIES); + dev_dbg(&i2c->dev, "scheduled timer, %d min\n", TIMER_MINUTES); +#endif + + status = otg_set_transceiver(&isp->otg); + if (status < 0) + dev_err(&i2c->dev, "can't register transceiver, %d\n", + status); + + return 0; +} + +static int isp1301_scan_bus(struct i2c_adapter *bus) +{ + if (!i2c_check_functionality(bus, I2C_FUNC_SMBUS_BYTE_DATA + | I2C_FUNC_SMBUS_READ_WORD_DATA)) + return -EINVAL; + return i2c_probe(bus, &addr_data, isp1301_probe); +} + +static struct i2c_driver isp1301_driver = { + .owner = THIS_MODULE, + .name = "isp1301_omap", + .id = 1301, /* FIXME "official", i2c-ids.h */ + .class = I2C_CLASS_HWMON, + .flags = I2C_DF_NOTIFY, + .attach_adapter = isp1301_scan_bus, + .detach_client = isp1301_detach_client, +}; + +/*-------------------------------------------------------------------------*/ + +static int __init isp_init(void) +{ + return i2c_add_driver(&isp1301_driver); +} +module_init(isp_init); + +static void __exit isp_exit(void) +{ + if (the_transceiver) + otg_set_transceiver(0); + i2c_del_driver(&isp1301_driver); +} +module_exit(isp_exit); + diff --git a/drivers/i2c/chips/pcf8591.c b/drivers/i2c/chips/pcf8591.c index 87ad9b973..d63e06d37 100644 --- a/drivers/i2c/chips/pcf8591.c +++ b/drivers/i2c/chips/pcf8591.c @@ -36,7 +36,7 @@ static unsigned int normal_isa_range[] = { I2C_CLIENT_ISA_END }; SENSORS_INSMOD_1(pcf8591); static int input_mode; -MODULE_PARM(input_mode, "i"); +module_param(input_mode, int, 0); MODULE_PARM_DESC(input_mode, "Analog input mode:\n" " 0 = four single ended inputs\n" diff --git a/drivers/i2c/chips/rtc8564.c b/drivers/i2c/chips/rtc8564.c index 0fa55d45e..acfd3f804 100644 --- a/drivers/i2c/chips/rtc8564.c +++ b/drivers/i2c/chips/rtc8564.c @@ -56,8 +56,8 @@ static inline u8 _rtc8564_ctrl2(struct i2c_client *client) #define BCD_TO_BIN(val) (((val)&15) + ((val)>>4)*10) #define BIN_TO_BCD(val) ((((val)/10)<<4) + (val)%10) -static int debug = 0; -MODULE_PARM(debug, "i"); +static int debug;; +module_param(debug, int, S_IRUGO | S_IWUSR); static struct i2c_driver rtc8564_driver; diff --git a/drivers/i2c/chips/smsc47m1.c b/drivers/i2c/chips/smsc47m1.c new file mode 100644 index 000000000..078c65fe3 --- /dev/null +++ b/drivers/i2c/chips/smsc47m1.c @@ -0,0 +1,579 @@ +/* + smsc47m1.c - Part of lm_sensors, Linux kernel modules + for hardware monitoring + + Supports the SMSC LPC47B27x, LPC47M10x, LPC47M13x and LPC47M14x + Super-I/O chips. + + Copyright (C) 2002 Mark D. Studebaker + Copyright (C) 2004 Jean Delvare + Ported to Linux 2.6 by Gabriele Gorla + and Jean Delvare + + This program is free software; you can redistribute it and/or modify + it under the terms of the 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 +#include +#include +#include +#include +#include +#include + +static unsigned short normal_i2c[] = { I2C_CLIENT_END }; +static unsigned short normal_i2c_range[] = { I2C_CLIENT_END }; +/* Address is autodetected, there is no default value */ +static unsigned int normal_isa[] = { 0x0000, I2C_CLIENT_ISA_END }; +static unsigned int normal_isa_range[] = { I2C_CLIENT_ISA_END }; +static struct i2c_force_data forces[] = {{NULL}}; + +enum chips { any_chip, smsc47m1 }; +static struct i2c_address_data addr_data = { + .normal_i2c = normal_i2c, + .normal_i2c_range = normal_i2c_range, + .normal_isa = normal_isa, + .normal_isa_range = normal_isa_range, + .probe = normal_i2c, /* cheat */ + .probe_range = normal_i2c_range, /* cheat */ + .ignore = normal_i2c, /* cheat */ + .ignore_range = normal_i2c_range, /* cheat */ + .forces = forces, +}; + +/* Super-I/0 registers and commands */ + +#define REG 0x2e /* The register to read/write */ +#define VAL 0x2f /* The value to read/write */ + +static inline void +superio_outb(int reg, int val) +{ + outb(reg, REG); + outb(val, VAL); +} + +static inline int +superio_inb(int reg) +{ + outb(reg, REG); + return inb(VAL); +} + +/* logical device for fans is 0x0A */ +#define superio_select() superio_outb(0x07, 0x0A) + +static inline void +superio_enter(void) +{ + outb(0x55, REG); +} + +static inline void +superio_exit(void) +{ + outb(0xAA, REG); +} + +#define SUPERIO_REG_ACT 0x30 +#define SUPERIO_REG_BASE 0x60 +#define SUPERIO_REG_DEVID 0x20 + +/* Logical device registers */ + +#define SMSC_EXTENT 0x80 + +/* nr is 0 or 1 in the macros below */ +#define SMSC47M1_REG_ALARM 0x04 +#define SMSC47M1_REG_TPIN(nr) (0x34 - (nr)) +#define SMSC47M1_REG_PPIN(nr) (0x36 - (nr)) +#define SMSC47M1_REG_PWM(nr) (0x56 + (nr)) +#define SMSC47M1_REG_FANDIV 0x58 +#define SMSC47M1_REG_FAN(nr) (0x59 + (nr)) +#define SMSC47M1_REG_FAN_PRELOAD(nr) (0x5B + (nr)) + +#define MIN_FROM_REG(reg,div) ((reg)>=192 ? 0 : \ + 983040/((192-(reg))*(div))) +#define FAN_FROM_REG(reg,div,preload) ((reg)<=(preload) || (reg)==255 ? 0 : \ + 983040/(((reg)-(preload))*(div))) +#define DIV_FROM_REG(reg) (1 << (reg)) +#define PWM_FROM_REG(reg) (((reg) & 0x7E) << 1) +#define PWM_EN_FROM_REG(reg) ((~(reg)) & 0x01) +#define PWM_TO_REG(reg) (((reg) >> 1) & 0x7E) + +struct smsc47m1_data { + struct i2c_client client; + struct semaphore lock; + int sysctl_id; + + struct semaphore update_lock; + unsigned long last_updated; /* In jiffies */ + + u8 fan[2]; /* Register value */ + u8 fan_preload[2]; /* Register value */ + u8 fan_div[2]; /* Register encoding, shifted right */ + u8 alarms; /* Register encoding */ + u8 pwm[2]; /* Register value (bit 7 is enable) */ +}; + + +static int smsc47m1_attach_adapter(struct i2c_adapter *adapter); +static int smsc47m1_find(int *address); +static int smsc47m1_detect(struct i2c_adapter *adapter, int address, int kind); +static int smsc47m1_detach_client(struct i2c_client *client); + +static int smsc47m1_read_value(struct i2c_client *client, u8 reg); +static void smsc47m1_write_value(struct i2c_client *client, u8 reg, u8 value); + +static struct smsc47m1_data *smsc47m1_update_device(struct device *dev, + int init); + + +static int smsc47m1_id; + +static struct i2c_driver smsc47m1_driver = { + .owner = THIS_MODULE, + .name = "smsc47m1", + .id = I2C_DRIVERID_SMSC47M1, + .flags = I2C_DF_NOTIFY, + .attach_adapter = smsc47m1_attach_adapter, + .detach_client = smsc47m1_detach_client, +}; + +/* nr is 0 or 1 in the callback functions below */ + +static ssize_t get_fan(struct device *dev, char *buf, int nr) +{ + struct smsc47m1_data *data = smsc47m1_update_device(dev, 0); + /* This chip (stupidly) stops monitoring fan speed if PWM is + enabled and duty cycle is 0%. This is fine if the monitoring + and control concern the same fan, but troublesome if they are + not (which could as well happen). */ + int rpm = (data->pwm[nr] & 0x7F) == 0x00 ? 0 : + FAN_FROM_REG(data->fan[nr], + DIV_FROM_REG(data->fan_div[nr]), + data->fan_preload[nr]); + return sprintf(buf, "%d\n", rpm); +} + +static ssize_t get_fan_min(struct device *dev, char *buf, int nr) +{ + struct smsc47m1_data *data = smsc47m1_update_device(dev, 0); + int rpm = MIN_FROM_REG(data->fan_preload[nr], + DIV_FROM_REG(data->fan_div[nr])); + return sprintf(buf, "%d\n", rpm); +} + +static ssize_t get_fan_div(struct device *dev, char *buf, int nr) +{ + struct smsc47m1_data *data = smsc47m1_update_device(dev, 0); + return sprintf(buf, "%d\n", DIV_FROM_REG(data->fan_div[nr])); +} + +static ssize_t get_fan_pwm(struct device *dev, char *buf, int nr) +{ + struct smsc47m1_data *data = smsc47m1_update_device(dev, 0); + return sprintf(buf, "%d\n", PWM_FROM_REG(data->pwm[nr])); +} + +static ssize_t get_fan_pwm_en(struct device *dev, char *buf, int nr) +{ + struct smsc47m1_data *data = smsc47m1_update_device(dev, 0); + return sprintf(buf, "%d\n", PWM_EN_FROM_REG(data->pwm[nr])); +} + +static ssize_t get_alarms(struct device *dev, char *buf) +{ + struct smsc47m1_data *data = smsc47m1_update_device(dev, 0); + return sprintf(buf, "%d\n", data->alarms); +} + +static ssize_t set_fan_min(struct device *dev, const char *buf, + size_t count, int nr) +{ + struct i2c_client *client = to_i2c_client(dev); + struct smsc47m1_data *data = i2c_get_clientdata(client); + + long rpmdiv = simple_strtol(buf, NULL, 10) + * DIV_FROM_REG(data->fan_div[nr]); + + if (983040 > 192 * rpmdiv || 2 * rpmdiv > 983040) + return -EINVAL; + + data->fan_preload[nr] = 192 - ((983040 + rpmdiv / 2) / rpmdiv); + smsc47m1_write_value(client, SMSC47M1_REG_FAN_PRELOAD(nr), + data->fan_preload[nr]); + + return count; +} + +/* Note: we save and restore the fan minimum here, because its value is + determined in part by the fan clock divider. This follows the principle + of least suprise; the user doesn't expect the fan minimum to change just + because the divider changed. */ +static ssize_t set_fan_div(struct device *dev, const char *buf, + size_t count, int nr) +{ + struct i2c_client *client = to_i2c_client(dev); + struct smsc47m1_data *data = i2c_get_clientdata(client); + + long new_div = simple_strtol(buf, NULL, 10), tmp; + u8 old_div = DIV_FROM_REG(data->fan_div[nr]); + + if (new_div == old_div) /* No change */ + return count; + switch (new_div) { + case 1: data->fan_div[nr] = 0; break; + case 2: data->fan_div[nr] = 1; break; + case 4: data->fan_div[nr] = 2; break; + case 8: data->fan_div[nr] = 3; break; + default: return -EINVAL; + } + + tmp = smsc47m1_read_value(client, SMSC47M1_REG_FANDIV) & 0x0F; + tmp |= (data->fan_div[0] << 4) | (data->fan_div[1] << 6); + smsc47m1_write_value(client, SMSC47M1_REG_FANDIV, tmp); + + /* Preserve fan min */ + tmp = 192 - (old_div * (192 - data->fan_preload[nr]) + + new_div / 2) / new_div; + data->fan_preload[nr] = SENSORS_LIMIT(tmp, 0, 191); + smsc47m1_write_value(client, SMSC47M1_REG_FAN_PRELOAD(nr), + data->fan_preload[nr]); + + return count; +} + +static ssize_t set_fan_pwm(struct device *dev, const char *buf, + size_t count, int nr) +{ + struct i2c_client *client = to_i2c_client(dev); + struct smsc47m1_data *data = i2c_get_clientdata(client); + + long val = simple_strtol(buf, NULL, 10); + + if (val < 0 || val > 255) + return -EINVAL; + + data->pwm[nr] &= 0x81; /* Preserve additional bits */ + data->pwm[nr] |= PWM_TO_REG(val); + + smsc47m1_write_value(client, SMSC47M1_REG_PWM(nr), + data->pwm[nr]); + return count; +} + +static ssize_t set_fan_pwm_en(struct device *dev, const char *buf, + size_t count, int nr) +{ + struct i2c_client *client = to_i2c_client(dev); + struct smsc47m1_data *data = i2c_get_clientdata(client); + + long val = simple_strtol(buf, NULL, 10); + + if (val != 0 && val != 1) + return -EINVAL; + + data->pwm[nr] &= 0xFE; /* preserve the other bits */ + data->pwm[nr] |= !val; + + smsc47m1_write_value(client, SMSC47M1_REG_PWM(nr), + data->pwm[nr]); + + return count; +} + +#define fan_present(offset) \ +static ssize_t get_fan##offset (struct device *dev, char *buf) \ +{ \ + return get_fan(dev, buf, 0x##offset - 1); \ +} \ +static ssize_t get_fan##offset##_min (struct device *dev, char *buf) \ +{ \ + return get_fan_min(dev, buf, 0x##offset - 1); \ +} \ +static ssize_t set_fan##offset##_min (struct device *dev, \ + const char *buf, size_t count) \ +{ \ + return set_fan_min(dev, buf, count, 0x##offset - 1); \ +} \ +static ssize_t get_fan##offset##_div (struct device *dev, char *buf) \ +{ \ + return get_fan_div(dev, buf, 0x##offset - 1); \ +} \ +static ssize_t set_fan##offset##_div (struct device *dev, \ + const char *buf, size_t count) \ +{ \ + return set_fan_div(dev, buf, count, 0x##offset - 1); \ +} \ +static ssize_t get_fan##offset##_pwm (struct device *dev, char *buf) \ +{ \ + return get_fan_pwm(dev, buf, 0x##offset - 1); \ +} \ +static ssize_t set_fan##offset##_pwm (struct device *dev, \ + const char *buf, size_t count) \ +{ \ + return set_fan_pwm(dev, buf, count, 0x##offset - 1); \ +} \ +static ssize_t get_fan##offset##_pwm_en (struct device *dev, char *buf) \ +{ \ + return get_fan_pwm_en(dev, buf, 0x##offset - 1); \ +} \ +static ssize_t set_fan##offset##_pwm_en (struct device *dev, \ + const char *buf, size_t count) \ +{ \ + return set_fan_pwm_en(dev, buf, count, 0x##offset - 1); \ +} \ +static DEVICE_ATTR(fan##offset##_input, S_IRUGO, get_fan##offset, \ + NULL); \ +static DEVICE_ATTR(fan##offset##_min, S_IRUGO | S_IWUSR, \ + get_fan##offset##_min, set_fan##offset##_min); \ +static DEVICE_ATTR(fan##offset##_div, S_IRUGO | S_IWUSR, \ + get_fan##offset##_div, set_fan##offset##_div); \ +static DEVICE_ATTR(fan##offset##_pwm, S_IRUGO | S_IWUSR, \ + get_fan##offset##_pwm, set_fan##offset##_pwm); \ +static DEVICE_ATTR(fan##offset##_pwm_enable, S_IRUGO | S_IWUSR, \ + get_fan##offset##_pwm_en, set_fan##offset##_pwm_en); + +fan_present(1); +fan_present(2); + +static DEVICE_ATTR(alarms, S_IRUGO, get_alarms, NULL); + +static int smsc47m1_attach_adapter(struct i2c_adapter *adapter) +{ + if (!(adapter->class & I2C_CLASS_HWMON)) + return 0; + return i2c_detect(adapter, &addr_data, smsc47m1_detect); +} + +static int smsc47m1_find(int *address) +{ + u8 val; + + superio_enter(); + val = superio_inb(SUPERIO_REG_DEVID); + + /* + * SMSC LPC47M10x/LPC47M13x (device id 0x59), LPC47M14x (device id + * 0x5F) and LPC47B27x (device id 0x51) have fan control. + * The LPC47M15x and LPC47M192 chips "with hardware monitoring block" + * can do much more besides (device id 0x60, unsupported). + */ + if (val == 0x51) + printk(KERN_INFO "smsc47m1: Found SMSC47B27x\n"); + else if (val == 0x59) + printk(KERN_INFO "smsc47m1: Found SMSC47M10x/SMSC47M13x\n"); + else if (val == 0x5F) + printk(KERN_INFO "smsc47m1: Found SMSC47M14x\n"); + else { + superio_exit(); + return -ENODEV; + } + + superio_select(); + *address = (superio_inb(SUPERIO_REG_BASE) << 8) + | superio_inb(SUPERIO_REG_BASE + 1); + val = superio_inb(SUPERIO_REG_ACT); + if (*address == 0 || (val & 0x01) == 0) { + printk(KERN_INFO "smsc47m1: Device is disabled, will not use\n"); + superio_exit(); + return -ENODEV; + } + + superio_exit(); + return 0; +} + +static int smsc47m1_detect(struct i2c_adapter *adapter, int address, int kind) +{ + struct i2c_client *new_client; + struct smsc47m1_data *data; + int err = 0; + + if (!i2c_is_isa_adapter(adapter)) { + return 0; + } + + if (!request_region(address, SMSC_EXTENT, "smsc47m1")) { + dev_err(&adapter->dev, "Region 0x%x already in use!\n", address); + return -EBUSY; + } + + if (!(data = kmalloc(sizeof(struct smsc47m1_data), GFP_KERNEL))) { + err = -ENOMEM; + goto error_release; + } + memset(data, 0x00, sizeof(struct smsc47m1_data)); + + new_client = &data->client; + i2c_set_clientdata(new_client, data); + new_client->addr = address; + init_MUTEX(&data->lock); + new_client->adapter = adapter; + new_client->driver = &smsc47m1_driver; + new_client->flags = 0; + + strlcpy(new_client->name, "smsc47m1", I2C_NAME_SIZE); + + new_client->id = smsc47m1_id++; + init_MUTEX(&data->update_lock); + + if ((err = i2c_attach_client(new_client))) + goto error_free; + + /* Some values (fan min, clock dividers, pwm registers) may be + needed before any update is triggered, so we better read them + at least once here. We don't usually do it that way, but in + this particular case, manually reading 5 registers out of 8 + doesn't make much sense and we're better using the existing + function. */ + smsc47m1_update_device(&new_client->dev, 1); + + if ((smsc47m1_read_value(new_client, SMSC47M1_REG_TPIN(0)) & 0x05) + == 0x05) { + device_create_file(&new_client->dev, &dev_attr_fan1_input); + device_create_file(&new_client->dev, &dev_attr_fan1_min); + device_create_file(&new_client->dev, &dev_attr_fan1_div); + } else + dev_dbg(&new_client->dev, "Fan 1 not enabled by hardware, " + "skipping\n"); + + if ((smsc47m1_read_value(new_client, SMSC47M1_REG_TPIN(1)) & 0x05) + == 0x05) { + device_create_file(&new_client->dev, &dev_attr_fan2_input); + device_create_file(&new_client->dev, &dev_attr_fan2_min); + device_create_file(&new_client->dev, &dev_attr_fan2_div); + } else + dev_dbg(&new_client->dev, "Fan 2 not enabled by hardware, " + "skipping\n"); + + if ((smsc47m1_read_value(new_client, SMSC47M1_REG_PPIN(0)) & 0x05) + == 0x04) { + device_create_file(&new_client->dev, &dev_attr_fan1_pwm); + device_create_file(&new_client->dev, &dev_attr_fan1_pwm_enable); + } else + dev_dbg(&new_client->dev, "PWM 1 not enabled by hardware, " + "skipping\n"); + if ((smsc47m1_read_value(new_client, SMSC47M1_REG_PPIN(1)) & 0x05) + == 0x04) { + device_create_file(&new_client->dev, &dev_attr_fan2_pwm); + device_create_file(&new_client->dev, &dev_attr_fan2_pwm_enable); + } else + dev_dbg(&new_client->dev, "PWM 2 not enabled by hardware, " + "skipping\n"); + + device_create_file(&new_client->dev, &dev_attr_alarms); + + return 0; + +error_free: + kfree(new_client); +error_release: + release_region(address, SMSC_EXTENT); + return err; +} + +static int smsc47m1_detach_client(struct i2c_client *client) +{ + int err; + + if ((err = i2c_detach_client(client))) { + dev_err(&client->dev, "Client deregistration failed, " + "client not detached.\n"); + return err; + } + + release_region(client->addr, SMSC_EXTENT); + kfree(i2c_get_clientdata(client)); + + return 0; +} + +static int smsc47m1_read_value(struct i2c_client *client, u8 reg) +{ + int res; + + down(&((struct smsc47m1_data *) i2c_get_clientdata(client))->lock); + res = inb_p(client->addr + reg); + up(&((struct smsc47m1_data *) i2c_get_clientdata(client))->lock); + return res; +} + +static void smsc47m1_write_value(struct i2c_client *client, u8 reg, u8 value) +{ + down(&((struct smsc47m1_data *) i2c_get_clientdata(client))->lock); + outb_p(value, client->addr + reg); + up(&((struct smsc47m1_data *) i2c_get_clientdata(client))->lock); +} + +static struct smsc47m1_data *smsc47m1_update_device(struct device *dev, + int init) +{ + struct i2c_client *client = to_i2c_client(dev); + struct smsc47m1_data *data = i2c_get_clientdata(client); + + down(&data->update_lock); + + if ((jiffies - data->last_updated > HZ + HZ / 2) || + (jiffies < data->last_updated) || init) { + int i; + + for (i = 0; i < 2; i++) { + data->fan[i] = smsc47m1_read_value(client, + SMSC47M1_REG_FAN(i)); + data->fan_preload[i] = smsc47m1_read_value(client, + SMSC47M1_REG_FAN_PRELOAD(i)); + data->pwm[i] = smsc47m1_read_value(client, + SMSC47M1_REG_PWM(i)); + } + + i = smsc47m1_read_value(client, SMSC47M1_REG_FANDIV); + data->fan_div[0] = (i >> 4) & 0x03; + data->fan_div[1] = i >> 6; + + data->alarms = smsc47m1_read_value(client, + SMSC47M1_REG_ALARM) >> 6; + /* Clear alarms if needed */ + if (data->alarms) + smsc47m1_write_value(client, SMSC47M1_REG_ALARM, 0xC0); + + data->last_updated = jiffies; + } + + up(&data->update_lock); + return data; +} + +static int __init sm_smsc47m1_init(void) +{ + if (smsc47m1_find(normal_isa)) { + return -ENODEV; + } + + return i2c_add_driver(&smsc47m1_driver); +} + +static void __exit sm_smsc47m1_exit(void) +{ + i2c_del_driver(&smsc47m1_driver); +} + +MODULE_AUTHOR("Mark D. Studebaker "); +MODULE_DESCRIPTION("SMSC LPC47M1xx fan sensors driver"); +MODULE_LICENSE("GPL"); + +module_init(sm_smsc47m1_init); +module_exit(sm_smsc47m1_exit); diff --git a/drivers/i2c/i2c-sensor-detect.c b/drivers/i2c/i2c-sensor-detect.c new file mode 100644 index 000000000..70e82f23e --- /dev/null +++ b/drivers/i2c/i2c-sensor-detect.c @@ -0,0 +1,169 @@ +/* + i2c-sensor-detect.c - Part of lm_sensors, Linux kernel modules for hardware + monitoring + Copyright (c) 1998 - 2001 Frodo Looijaard and + Mark D. Studebaker + + This program is free software; you can redistribute it and/or modify + it under the terms of the 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +/* Very inefficient for ISA detects, and won't work for 10-bit addresses! */ +int i2c_detect(struct i2c_adapter *adapter, + struct i2c_address_data *address_data, + int (*found_proc) (struct i2c_adapter *, int, int)) +{ + int addr, i, found, j, err; + struct i2c_force_data *this_force; + int is_isa = i2c_is_isa_adapter(adapter); + int adapter_id = + is_isa ? ANY_I2C_ISA_BUS : i2c_adapter_id(adapter); + + /* Forget it if we can't probe using SMBUS_QUICK */ + if ((!is_isa) && + !i2c_check_functionality(adapter, I2C_FUNC_SMBUS_QUICK)) + return -1; + + for (addr = 0x00; addr <= (is_isa ? 0xffff : 0x7f); addr++) { + if (!is_isa && i2c_check_addr(adapter, addr)) + continue; + + /* If it is in one of the force entries, we don't do any + detection at all */ + found = 0; + for (i = 0; !found && (this_force = address_data->forces + i, this_force->force); i++) { + for (j = 0; !found && (this_force->force[j] != I2C_CLIENT_END); j += 2) { + if ( ((adapter_id == this_force->force[j]) || + ((this_force->force[j] == ANY_I2C_BUS) && !is_isa)) && + (addr == this_force->force[j + 1]) ) { + dev_dbg(&adapter->dev, "found force parameter for adapter %d, addr %04x\n", adapter_id, addr); + if ((err = found_proc(adapter, addr, this_force->kind))) + return err; + found = 1; + } + } + } + if (found) + continue; + + /* If this address is in one of the ignores, we can forget about it + right now */ + for (i = 0; !found && (address_data->ignore[i] != I2C_CLIENT_END); i += 2) { + if ( ((adapter_id == address_data->ignore[i]) || + ((address_data->ignore[i] == ANY_I2C_BUS) && + !is_isa)) && + (addr == address_data->ignore[i + 1])) { + dev_dbg(&adapter->dev, "found ignore parameter for adapter %d, addr %04x\n", adapter_id, addr); + found = 1; + } + } + for (i = 0; !found && (address_data->ignore_range[i] != I2C_CLIENT_END); i += 3) { + if ( ((adapter_id == address_data->ignore_range[i]) || + ((address_data-> ignore_range[i] == ANY_I2C_BUS) & + !is_isa)) && + (addr >= address_data->ignore_range[i + 1]) && + (addr <= address_data->ignore_range[i + 2])) { + dev_dbg(&adapter->dev, "found ignore_range parameter for adapter %d, addr %04x\n", adapter_id, addr); + found = 1; + } + } + if (found) + continue; + + /* Now, we will do a detection, but only if it is in the normal or + probe entries */ + if (is_isa) { + for (i = 0; !found && (address_data->normal_isa[i] != I2C_CLIENT_ISA_END); i += 1) { + if (addr == address_data->normal_isa[i]) { + dev_dbg(&adapter->dev, "found normal isa entry for adapter %d, addr %04x\n", adapter_id, addr); + found = 1; + } + } + for (i = 0; !found && (address_data->normal_isa_range[i] != I2C_CLIENT_ISA_END); i += 3) { + if ((addr >= address_data->normal_isa_range[i]) && + (addr <= address_data->normal_isa_range[i + 1]) && + ((addr - address_data->normal_isa_range[i]) % address_data->normal_isa_range[i + 2] == 0)) { + dev_dbg(&adapter->dev, "found normal isa_range entry for adapter %d, addr %04x", adapter_id, addr); + found = 1; + } + } + } else { + for (i = 0; !found && (address_data->normal_i2c[i] != I2C_CLIENT_END); i += 1) { + if (addr == address_data->normal_i2c[i]) { + found = 1; + dev_dbg(&adapter->dev, "found normal i2c entry for adapter %d, addr %02x", adapter_id, addr); + } + } + for (i = 0; !found && (address_data->normal_i2c_range[i] != I2C_CLIENT_END); i += 2) { + if ((addr >= address_data->normal_i2c_range[i]) && + (addr <= address_data->normal_i2c_range[i + 1])) { + dev_dbg(&adapter->dev, "found normal i2c_range entry for adapter %d, addr %04x\n", adapter_id, addr); + found = 1; + } + } + } + + for (i = 0; + !found && (address_data->probe[i] != I2C_CLIENT_END); + i += 2) { + if (((adapter_id == address_data->probe[i]) || + ((address_data-> + probe[i] == ANY_I2C_BUS) && !is_isa)) + && (addr == address_data->probe[i + 1])) { + dev_dbg(&adapter->dev, "found probe parameter for adapter %d, addr %04x\n", adapter_id, addr); + found = 1; + } + } + for (i = 0; !found && (address_data->probe_range[i] != I2C_CLIENT_END); i += 3) { + if ( ((adapter_id == address_data->probe_range[i]) || + ((address_data->probe_range[i] == ANY_I2C_BUS) && !is_isa)) && + (addr >= address_data->probe_range[i + 1]) && + (addr <= address_data->probe_range[i + 2])) { + found = 1; + dev_dbg(&adapter->dev, "found probe_range parameter for adapter %d, addr %04x\n", adapter_id, addr); + } + } + if (!found) + continue; + + /* OK, so we really should examine this address. First check + whether there is some client here at all! */ + if (is_isa || + (i2c_smbus_xfer (adapter, addr, 0, 0, 0, I2C_SMBUS_QUICK, NULL) >= 0)) + if ((err = found_proc(adapter, addr, -1))) + return err; + } + return 0; +} + +EXPORT_SYMBOL(i2c_detect); + +MODULE_AUTHOR("Frodo Looijaard , " + "Rudolf Marek "); + +MODULE_DESCRIPTION("i2c-sensor driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/i2c/i2c-sensor-vid.c b/drivers/i2c/i2c-sensor-vid.c new file mode 100644 index 000000000..017dcc5c6 --- /dev/null +++ b/drivers/i2c/i2c-sensor-vid.c @@ -0,0 +1,99 @@ +/* + i2c-sensor-vid.c - Part of lm_sensors, Linux kernel modules for hardware + monitoring + + Copyright (c) 2004 Rudolf Marek + + This program is free software; you can redistribute it and/or modify + it under the terms of the 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 +#include +#include + +struct vrm_model { + u8 vendor; + u8 eff_family; + u8 eff_model; + int vrm_type; +}; + +#define ANY 0xFF + +#ifdef CONFIG_X86 + +static struct vrm_model vrm_models[] = { + {X86_VENDOR_AMD, 0x6, ANY, 90}, /* Athlon Duron etc */ + {X86_VENDOR_AMD, 0xF, 0x4, 90}, /* Athlon 64 */ + {X86_VENDOR_AMD, 0xF, 0x5, 24}, /* Opteron */ + {X86_VENDOR_INTEL, 0x6, 0x9, 85}, /* 0.13um too */ + {X86_VENDOR_INTEL, 0x6, 0xB, 85}, /* 0xB Tualatin */ + {X86_VENDOR_INTEL, 0x6, ANY, 82}, /* any P6 */ + {X86_VENDOR_INTEL, 0x7, ANY, 0}, /* Itanium */ + {X86_VENDOR_INTEL, 0xF, 0x3, 100}, /* P4 Prescott */ + {X86_VENDOR_INTEL, 0xF, ANY, 90}, /* P4 before Prescott */ + {X86_VENDOR_INTEL, 0x10,ANY, 0}, /* Itanium 2 */ + {X86_VENDOR_UNKNOWN, ANY, ANY, 0} /* stop here */ + }; + +static int find_vrm(u8 eff_family, u8 eff_model, u8 vendor) +{ + int i = 0; + + while (vrm_models[i].vendor!=X86_VENDOR_UNKNOWN) { + if (vrm_models[i].vendor==vendor) + if ((vrm_models[i].eff_family==eff_family)&& \ + ((vrm_models[i].eff_model==eff_model)|| \ + (vrm_models[i].eff_model==ANY))) + return vrm_models[i].vrm_type; + i++; + } + + return 0; +} + +int i2c_which_vrm(void) +{ + struct cpuinfo_x86 *c = cpu_data; + u32 eax; + u8 eff_family, eff_model; + int vrm_ret; + + if (c->x86 < 6) return 0; /* any CPU with familly lower than 6 + dont have VID and/or CPUID */ + eax = cpuid_eax(1); + eff_family = ((eax & 0x00000F00)>>8); + eff_model = ((eax & 0x000000F0)>>4); + if (eff_family == 0xF) { /* use extended model & family */ + eff_family += ((eax & 0x00F00000)>>20); + eff_model += ((eax & 0x000F0000)>>16)<<4; + } + vrm_ret = find_vrm(eff_family,eff_model,c->x86_vendor); + if (vrm_ret == 0) + printk(KERN_INFO "i2c-sensor.o: Unknown VRM version of your" + " x86 CPU\n"); + return vrm_ret; +} + +/* and now something completely different for Non-x86 world*/ +#else +int i2c_which_vrm(void) +{ + printk(KERN_INFO "i2c-sensor.o: Unknown VRM version of your CPU\n"); + return 0; +} +#endif + +EXPORT_SYMBOL(i2c_which_vrm); diff --git a/drivers/ide/arm/Makefile b/drivers/ide/arm/Makefile index ba897493d..6a78f0755 100644 --- a/drivers/ide/arm/Makefile +++ b/drivers/ide/arm/Makefile @@ -1,5 +1,6 @@ obj-$(CONFIG_BLK_DEV_IDE_ICSIDE) += icside.o obj-$(CONFIG_BLK_DEV_IDE_RAPIDE) += rapide.o +obj-$(CONFIG_BLK_DEV_IDE_BAST) += bast-ide.o EXTRA_CFLAGS := -Idrivers/ide diff --git a/drivers/ide/arm/bast-ide.c b/drivers/ide/arm/bast-ide.c new file mode 100644 index 000000000..9d474e5fd --- /dev/null +++ b/drivers/ide/arm/bast-ide.c @@ -0,0 +1,71 @@ +/* linux/drivers/ide/arm/bast-ide.c + * + * Copyright (c) 2003-2004 Simtec Electronics + * 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. + * +*/ + +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include + +/* list of registered interfaces */ +static ide_hwif_t *ifs[2]; + +static int __init +bastide_register(unsigned int base, unsigned int aux, int irq, + ide_hwif_t **hwif) +{ + hw_regs_t hw; + int i; + + memset(&hw, 0, sizeof(hw)); + + base += BAST_IDE_CS; + aux += BAST_IDE_CS; + + for (i = IDE_DATA_OFFSET; i <= IDE_STATUS_OFFSET; i++) { + hw.io_ports[i] = (unsigned long)base; + base += 0x20; + } + + hw.io_ports[IDE_CONTROL_OFFSET] = aux + (6 * 0x20); + hw.irq = irq; + + ide_register_hw(&hw, hwif); + + return 0; +} + +static int __init bastide_init(void) +{ + /* we can treat the VR1000 and the BAST the same */ + + if (!(machine_is_bast() || machine_is_vr1000())) + return 0; + + printk("BAST: IDE driver, (c) 2003-2004 Simtec Electronics\n"); + + bastide_register(BAST_VA_IDEPRI, BAST_VA_IDEPRIAUX, IRQ_IDE0, &ifs[0]); + bastide_register(BAST_VA_IDESEC, BAST_VA_IDESECAUX, IRQ_IDE1, &ifs[1]); + return 0; +} + +module_init(bastide_init); + +MODULE_AUTHOR("Ben Dooks "); +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Simtec BAST / Thorcom VR1000 IDE driver"); diff --git a/drivers/ide/arm/icside.c b/drivers/ide/arm/icside.c index f40a61320..edc9de780 100644 --- a/drivers/ide/arm/icside.c +++ b/drivers/ide/arm/icside.c @@ -215,8 +215,6 @@ static void icside_build_sglist(ide_drive_t *drive, struct request *rq) struct scatterlist *sg = hwif->sg_table; int nents; - BUG_ON(hwif->sg_dma_active); - if (rq->flags & REQ_DRIVE_TASKFILE) { ide_task_t *args = rq->special; @@ -401,8 +399,6 @@ static int icside_dma_end(ide_drive_t *drive) dma_unmap_sg(state->dev, hwif->sg_table, hwif->sg_nents, hwif->sg_dma_direction); - hwif->sg_dma_active = 0; - return get_dma_residue(hwif->hw.dma) != 0; } @@ -454,7 +450,6 @@ icside_dma_common(ide_drive_t *drive, struct request *rq, /* * We can not enable DMA on both channels. */ - BUG_ON(hwif->sg_dma_active); BUG_ON(dma_channel_active(hwif->hw.dma)); icside_build_sglist(drive, rq); diff --git a/drivers/ide/ide-lib.c b/drivers/ide/ide-lib.c index 073c3fb82..b2fa5eccd 100644 --- a/drivers/ide/ide-lib.c +++ b/drivers/ide/ide-lib.c @@ -465,7 +465,6 @@ byte ide_dump_atapi_status (ide_drive_t *drive, const char *msg, byte stat) status.all = stat; local_irq_set(flags); printk("%s: %s: status=0x%02x", drive->name, msg, stat); -#if FANCY_STATUS_DUMPS printk(" { "); if (status.b.bsy) printk("Busy "); @@ -479,19 +478,16 @@ byte ide_dump_atapi_status (ide_drive_t *drive, const char *msg, byte stat) if (status.b.check) printk("Error "); } printk("}"); -#endif /* FANCY_STATUS_DUMPS */ printk("\n"); if ((status.all & (status.b.bsy|status.b.check)) == status.b.check) { error.all = HWIF(drive)->INB(IDE_ERROR_REG); printk("%s: %s: error=0x%02x", drive->name, msg, error.all); -#if FANCY_STATUS_DUMPS if (error.b.ili) printk("IllegalLengthIndication "); if (error.b.eom) printk("EndOfMedia "); if (error.b.abrt) printk("Aborted Command "); if (error.b.mcr) printk("MediaChangeRequested "); if (error.b.sense_key) printk("LastFailedSense 0x%02x ", error.b.sense_key); -#endif /* FANCY_STATUS_DUMPS */ printk("\n"); } local_irq_restore(flags); diff --git a/drivers/ide/legacy/Makefile b/drivers/ide/legacy/Makefile index ad8ae32b8..c0f353e57 100644 --- a/drivers/ide/legacy/Makefile +++ b/drivers/ide/legacy/Makefile @@ -10,6 +10,5 @@ obj-$(CONFIG_BLK_DEV_IDECS) += ide-cs.o # Last of all obj-$(CONFIG_BLK_DEV_HD) += hd.o -obj-$(CONFIG_BLK_DEV_HD98) += hd98.o EXTRA_CFLAGS := -Idrivers/ide diff --git a/drivers/ide/legacy/qd65xx.h b/drivers/ide/legacy/qd65xx.h index 1557a47ff..603018e44 100644 --- a/drivers/ide/legacy/qd65xx.h +++ b/drivers/ide/legacy/qd65xx.h @@ -47,10 +47,10 @@ /* Drive specific timing taken from DOS driver v3.7 */ struct qd65xx_timing_s { - char offset; /* ofset from the beginning of Model Number" */ + s8 offset; /* ofset from the beginning of Model Number" */ char model[4]; /* 4 chars from Model number, no conversion */ - short active; /* active time */ - short recovery; /* recovery time */ + s16 active; /* active time */ + s16 recovery; /* recovery time */ } qd65xx_timing [] = { { 30, "2040", 110, 225 }, /* Conner CP30204 */ { 30, "2045", 135, 225 }, /* Conner CP30254 */ diff --git a/drivers/ide/pci/alim15x3.c b/drivers/ide/pci/alim15x3.c index 2a343679a..82a449dc8 100644 --- a/drivers/ide/pci/alim15x3.c +++ b/drivers/ide/pci/alim15x3.c @@ -752,8 +752,8 @@ static void __init init_hwif_common_ali15x3 (ide_hwif_t *hwif) hwif->tuneproc = &ali15x3_tune_drive; hwif->speedproc = &ali15x3_tune_chipset; - /* Don't use LBA48 on ALi devices before rev 0xC5 */ - hwif->no_lba48 = (m5229_revision <= 0xC4) ? 1 : 0; + /* don't use LBA48 DMA on ALi devices before rev 0xC5 */ + hwif->no_lba48_dma = (m5229_revision <= 0xC4) ? 1 : 0; if (!hwif->dma_base) { hwif->drives[0].autotune = 1; @@ -893,7 +893,7 @@ static struct pci_device_id alim15x3_pci_tbl[] = { MODULE_DEVICE_TABLE(pci, alim15x3_pci_tbl); static struct pci_driver driver = { - .name = "ALI15x3 IDE", + .name = "ALI15x3_IDE", .id_table = alim15x3_pci_tbl, .probe = alim15x3_init_one, }; diff --git a/drivers/ide/pci/cmd640.c b/drivers/ide/pci/cmd640.c index 0eac8ee64..92a2b7cae 100644 --- a/drivers/ide/pci/cmd640.c +++ b/drivers/ide/pci/cmd640.c @@ -101,6 +101,8 @@ #undef REALLY_SLOW_IO /* most systems can safely undef this */ #define CMD640_PREFETCH_MASKS 1 +//#define CMD640_DUMP_REGS + #include #include #include @@ -748,7 +750,7 @@ int __init ide_probe_for_cmd640x (void) put_cmd640_reg(0x5b, 0); #ifdef CMD640_DUMP_REGS - CMD640_DUMP_REGS; + cmd640_dump_regs(); #endif /* @@ -870,7 +872,7 @@ int __init ide_probe_for_cmd640x (void) } #ifdef CMD640_DUMP_REGS - CMD640_DUMP_REGS; + cmd640_dump_regs(); #endif return 1; } diff --git a/drivers/ide/pci/cy82c693.c b/drivers/ide/pci/cy82c693.c index fe840cbe0..83bfe92d2 100644 --- a/drivers/ide/pci/cy82c693.c +++ b/drivers/ide/pci/cy82c693.c @@ -444,7 +444,7 @@ static struct pci_device_id cy82c693_pci_tbl[] = { MODULE_DEVICE_TABLE(pci, cy82c693_pci_tbl); static struct pci_driver driver = { - .name = "Cypress IDE", + .name = "Cypress_IDE", .id_table = cy82c693_pci_tbl, .probe = cy82c693_init_one, }; diff --git a/drivers/ide/pci/delkin_cb.c b/drivers/ide/pci/delkin_cb.c new file mode 100644 index 000000000..4d7de5117 --- /dev/null +++ b/drivers/ide/pci/delkin_cb.c @@ -0,0 +1,140 @@ +/* + * linux/drivers/ide/pci/delkin_cb.c + * + * Created 20 Oct 2004 by Mark Lord + * + * Basic support for Delkin/ASKA/Workbit Cardbus CompactFlash adapter + * + * Modeled after the 16-bit PCMCIA driver: ide-cs.c + * + * This is slightly peculiar, in that it is a PCI driver, + * but is NOT an IDE PCI driver -- the IDE layer does not directly + * support hot insertion/removal of PCI interfaces, so this driver + * is unable to use the IDE PCI interfaces. Instead, it uses the + * same interfaces as the ide-cs (PCMCIA) driver uses. + * On the plus side, the driver is also smaller/simpler this way. + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * No chip documentation has yet been found, + * so these configuration values were pulled from + * a running Win98 system using "debug". + * This gives around 3MByte/second read performance, + * which is about 2/3 of what the chip is capable of. + * + * There is also a 4KByte mmio region on the card, + * but its purpose has yet to be reverse-engineered. + */ +static const u8 setup[] = { + 0x00, 0x05, 0xbe, 0x01, 0x20, 0x8f, 0x00, 0x00, + 0xa4, 0x1f, 0xb3, 0x1b, 0x00, 0x00, 0x00, 0x80, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xa4, 0x83, 0x02, 0x13, +}; + +static int __devinit +delkin_cb_probe (struct pci_dev *dev, const struct pci_device_id *id) +{ + unsigned long base; + hw_regs_t hw; + ide_hwif_t *hwif = NULL; + ide_drive_t *drive; + int i, rc; + + rc = pci_enable_device(dev); + if (rc) { + printk(KERN_ERR "delkin_cb: pci_enable_device failed (%d)\n", rc); + return rc; + } + rc = pci_request_regions(dev, "delkin_cb"); + if (rc) { + printk(KERN_ERR "delkin_cb: pci_request_regions failed (%d)\n", rc); + pci_disable_device(dev); + return rc; + } + base = pci_resource_start(dev, 0); + outb(0x02, base + 0x1e); /* set nIEN to block interrupts */ + inb(base + 0x17); /* read status to clear interrupts */ + for (i = 0; i < sizeof(setup); ++i) { + if (setup[i]) + outb(setup[i], base + i); + } + pci_release_regions(dev); /* IDE layer handles regions itself */ + + memset(&hw, 0, sizeof(hw)); + ide_std_init_ports(&hw, base + 0x10, base + 0x1e); + hw.irq = dev->irq; + hw.chipset = ide_pci; /* this enables IRQ sharing */ + + rc = ide_register_hw_with_fixup(&hw, &hwif, ide_undecoded_slave); + if (rc < 0) { + printk(KERN_ERR "delkin_cb: ide_register_hw failed (%d)\n", rc); + pci_disable_device(dev); + return -ENODEV; + } + pci_set_drvdata(dev, hwif); + hwif->pci_dev = dev; + drive = &hwif->drives[0]; + if (drive->present) { + drive->io_32bit = 1; + drive->unmask = 1; + } + return 0; +} + +static void +delkin_cb_remove (struct pci_dev *dev) +{ + ide_hwif_t *hwif = pci_get_drvdata(dev); + + if (hwif) + ide_unregister_hwif(hwif); + pci_disable_device(dev); +} + +static struct pci_device_id delkin_cb_pci_tbl[] __devinitdata = { + { PCI_VENDOR_ID_WORKBIT, PCI_DEVICE_ID_WORKBIT_CB, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, + { 0, }, +}; +MODULE_DEVICE_TABLE(pci, delkin_cb_pci_tbl); + +static struct pci_driver driver = { + .name = "Delkin/ASKA/Workbit Cardbus IDE", + .id_table = delkin_cb_pci_tbl, + .probe = delkin_cb_probe, + .remove = delkin_cb_remove, +}; + +static int +delkin_cb_init (void) +{ + return pci_module_init(&driver); +} + +static void +delkin_cb_exit (void) +{ + pci_unregister_driver(&driver); +} + +module_init(delkin_cb_init); +module_exit(delkin_cb_exit); + +MODULE_AUTHOR("Mark Lord"); +MODULE_DESCRIPTION("Basic support for Delkin/ASKA/Workbit Cardbus IDE"); +MODULE_LICENSE("GPL"); + diff --git a/drivers/ide/pci/hpt34x.c b/drivers/ide/pci/hpt34x.c index f9c9ab664..8eb72f446 100644 --- a/drivers/ide/pci/hpt34x.c +++ b/drivers/ide/pci/hpt34x.c @@ -44,65 +44,6 @@ #include "hpt34x.h" -#if defined(DISPLAY_HPT34X_TIMINGS) && defined(CONFIG_PROC_FS) -#include -#include - -static u8 hpt34x_proc = 0; - -#define HPT34X_MAX_DEVS 8 -static struct pci_dev *hpt34x_devs[HPT34X_MAX_DEVS]; -static int n_hpt34x_devs; - -static int hpt34x_get_info (char *buffer, char **addr, off_t offset, int count) -{ - char *p = buffer; - int i, len; - - p += sprintf(p, "\n " - "HPT34X Chipset.\n"); - for (i = 0; i < n_hpt34x_devs; i++) { - struct pci_dev *dev = hpt34x_devs[i]; - unsigned long bibma = pci_resource_start(dev, 4); - u8 c0 = 0, c1 = 0; - - /* - * at that point bibma+0x2 et bibma+0xa are byte registers - * to investigate: - */ - c0 = inb_p((u16)bibma + 0x02); - c1 = inb_p((u16)bibma + 0x0a); - p += sprintf(p, "\nController: %d\n", i); - p += sprintf(p, "--------------- Primary Channel " - "---------------- Secondary Channel " - "-------------\n"); - p += sprintf(p, " %sabled " - " %sabled\n", - (c0&0x80) ? "dis" : " en", - (c1&0x80) ? "dis" : " en"); - p += sprintf(p, "--------------- drive0 --------- drive1 " - "-------- drive0 ---------- drive1 ------\n"); - p += sprintf(p, "DMA enabled: %s %s" - " %s %s\n", - (c0&0x20) ? "yes" : "no ", - (c0&0x40) ? "yes" : "no ", - (c1&0x20) ? "yes" : "no ", - (c1&0x40) ? "yes" : "no " ); - - p += sprintf(p, "UDMA\n"); - p += sprintf(p, "DMA\n"); - p += sprintf(p, "PIO\n"); - } - p += sprintf(p, "\n"); - - /* p - buffer must be less than 4k! */ - len = (p - buffer) - offset; - *addr = buffer + offset; - - return len > count ? count : len; -} -#endif /* defined(DISPLAY_HPT34X_TIMINGS) && defined(CONFIG_PROC_FS) */ - static u8 hpt34x_ratemask (ide_drive_t *drive) { return 1; @@ -277,15 +218,6 @@ static unsigned int __devinit init_chipset_hpt34x(struct pci_dev *dev, const cha local_irq_restore(flags); -#if defined(DISPLAY_HPT34X_TIMINGS) && defined(CONFIG_PROC_FS) - hpt34x_devs[n_hpt34x_devs++] = dev; - - if (!hpt34x_proc) { - hpt34x_proc = 1; - ide_pci_create_host_proc("hpt34x", hpt34x_get_info); - } -#endif /* DISPLAY_HPT34X_TIMINGS && CONFIG_PROC_FS */ - return dev->irq; } @@ -339,7 +271,7 @@ static struct pci_device_id hpt34x_pci_tbl[] = { MODULE_DEVICE_TABLE(pci, hpt34x_pci_tbl); static struct pci_driver driver = { - .name = "HPT34x IDE", + .name = "HPT34x_IDE", .id_table = hpt34x_pci_tbl, .probe = hpt34x_init_one, }; diff --git a/drivers/ide/pci/it8212.c b/drivers/ide/pci/it8212.c new file mode 100644 index 000000000..5c83a7a07 --- /dev/null +++ b/drivers/ide/pci/it8212.c @@ -0,0 +1,851 @@ +/* + * linux/drivers/ide/pci/it8212.c Version 0.07 November 2004 + * + * Copyright (C) 2004 Red Hat + * + * May be copied or modified under the terms of the GNU General Public License + * Based in part on the ITE vendor provided SCSI driver. + * + * Documentation available from + * http://www.ite.com.tw/pc/IT8212F_V04.pdf + * Some other documents are NDA. + * + * The ITE8212 isn't exactly a standard IDE controller. It has two + * modes. In pass through mode then it is an IDE controller. In its smart + * mode its actually quite a capable hardware raid controller disguised + * as an IDE controller. Smart mode only understands DMA read/write and + * identify, none of the fancier commands apply. + * + * Errata: + * o Rev 0x10 also requires master/slave hold the same DMA timings and + * cannot do ATAPI MWDMA. + * o The identify data for raid volumes lacks CHS info (technically ok) + * but also fails to set the LBA28 and other bits. We fix these in + * the IDE probe quirk code. + * o If you write LBA48 sized I/O's (ie > 256 sector) in smart mode + * raid then the controller firmware dies + * o Smart mode without RAID doesn't clear all the necessary identify + * bits to reduce the command set to the one used + * + * This has a few impacts on the driver + * - In pass through mode we do all the work you would expect + * - In smart mode the clocking set up is done by the controller generally + * but we must watch the other limits and filter. + * - There are a few extra vendor commands that actually talk to the + * controller but only work PIO with no IRQ. + * + * Vendor areas of the identify block in smart mode are used for the + * timing and policy set up. Each HDD in raid mode also has a serial + * block on the disk. The hardware extra commands are get/set chip status, + * rebuild, get rebuild status. + * + * In Linux the driver supports pass through mode as if the device was + * just another IDE controller. If the smart mode is running then + * volumes are managed by the controller firmware and each IDE "disk" + * is a raid volume. Even more cute - the controller can do automated + * hotplug and rebuild. + * + * The pass through controller itself is a little demented. It has a + * flaw that it has a single set of PIO/MWDMA timings per channel so + * non UDMA devices restrict each others performance. It also has a + * single clock source per channel so mixed UDMA100/133 performance + * isn't perfect and we have to pick a clock. Thankfully none of this + * matters in smart mode. ATAPI DMA is not currently supported. + * + * It seems the smart mode is a win for RAID1/RAID10 but otherwise not. + * + * TODO + * - ATAPI UDMA is ok but not MWDMA it seems + * - RAID configuration ioctls + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +struct it8212_dev +{ + unsigned int smart:1, /* Are we in smart raid mode */ + timing10:1; /* Rev 0x10 */ + u8 clock_mode; /* 0, ATA_50 or ATA_66 */ + u8 want[2][2]; /* Mode/Pri log for master slave */ + /* We need these for switching the clock when DMA goes on/off + The high byte is the 66Mhz timing */ + u16 pio[2]; /* Cached PIO values */ + u16 mwdma[2]; /* Cached MWDMA values */ + u16 udma[2]; /* Cached UDMA values (per drive) */ +}; + +#define ATA_66 0 +#define ATA_50 1 +#define ATA_ANY 2 + +#define UDMA_OFF 0 +#define MWDMA_OFF 0 + +/* + * We allow users to force the card into non raid mode without + * flashing the alternative BIOS. This is also neccessary right now + * for embedded platforms that cannot run a PC BIOS but are using this + * device. + */ + +static int it8212_noraid; + +/** + * it8212_program - program the PIO/MWDMA registers + * @drive: drive to tune + * + * Program the PIO/MWDMA timing for this channel according to the + * current clock. + */ + +static void it8212_program(ide_drive_t *drive, u16 timing) +{ + ide_hwif_t *hwif = HWIF(drive); + struct it8212_dev *itdev = ide_get_hwifdata(hwif); + int channel = hwif->channel; + u8 conf; + + /* Program PIO/MWDMA timing bits */ + if(itdev->clock_mode == ATA_66) + conf = timing >> 8; + else + conf = timing & 0xFF; + pci_write_config_byte(hwif->pci_dev, 0x54 + 4 * channel, conf); +} + +/** + * it8212_program_udma - program the UDMA registers + * @drive: drive to tune + * + * Program the UDMA timing for this drive according to the + * current clock. + */ + +static void it8212_program_udma(ide_drive_t *drive, u16 timing) +{ + ide_hwif_t *hwif = HWIF(drive); + struct it8212_dev *itdev = ide_get_hwifdata(hwif); + int channel = hwif->channel; + int unit = drive->select.b.unit; + u8 conf; + + /* Program UDMA timing bits */ + if(itdev->clock_mode == ATA_66) + conf = timing >> 8; + else + conf = timing & 0xFF; + if(itdev->timing10 == 0) + pci_write_config_byte(hwif->pci_dev, 0x56 + 4 * channel + unit, conf); + else { + pci_write_config_byte(hwif->pci_dev, 0x56 + 4 * channel, conf); + pci_write_config_byte(hwif->pci_dev, 0x56 + 4 * channel + 1, conf); + } +} + + +/** + * it8212_clock_strategy + * @hwif: hardware interface + * + * Select between the 50 and 66Mhz base clocks to get the best + * results for this interface. + */ + +static void it8212_clock_strategy(ide_drive_t *drive) +{ + ide_hwif_t *hwif = HWIF(drive); + struct it8212_dev *itdev = ide_get_hwifdata(hwif); + + u8 unit = drive->select.b.unit; + ide_drive_t *pair = &hwif->drives[1-unit]; + + int clock, altclock; + u8 v; + int sel = 0; + + if(itdev->want[0][0] > itdev->want[1][0]) { + clock = itdev->want[0][1]; + altclock = itdev->want[1][1]; + } else { + clock = itdev->want[1][1]; + altclock = itdev->want[0][1]; + } + + /* Master doesn't care does the slave ? */ + if(clock == ATA_ANY) + clock = altclock; + + /* Nobody cares - keep the same clock */ + if(clock == ATA_ANY) + return; + /* No change */ + if(clock == itdev->clock_mode) + return; + + /* Load this into the controller ? */ + if(clock == ATA_66) + itdev->clock_mode = ATA_66; + else { + itdev->clock_mode = ATA_50; + sel = 1; + } + pci_read_config_byte(hwif->pci_dev, 0x50, &v); + v &= ~(1 << (1 + hwif->channel)); + v |= sel << (1 + hwif->channel); + pci_write_config_byte(hwif->pci_dev, 0x50, v); + + /* + * Reprogram the UDMA/PIO of the pair drive for the switch + * MWDMA will be dealt with by the dma switcher + */ + if(pair && itdev->udma[1-unit] != UDMA_OFF) { + it8212_program_udma(pair, itdev->udma[1-unit]); + it8212_program(pair, itdev->pio[1-unit]); + } + /* + * Reprogram the UDMA/PIO of our drive for the switch. + * MWDMA will be dealt with by the dma switcher + */ + if(itdev->udma[unit] != UDMA_OFF) { + it8212_program_udma(drive, itdev->udma[unit]); + it8212_program(drive, itdev->pio[unit]); + } +} + +/** + * it8212_ratemask - Compute available modes + * @drive: IDE drive + * + * Compute the available speeds for the devices on the interface. This + * is all modes to ATA133 clipped by drive cable setup. + */ + +static byte it8212_ratemask (ide_drive_t *drive) +{ + u8 mode = 4; + if (!eighty_ninty_three(drive)) + mode = min(mode, (u8)1); + return mode; +} + +/** + * it8212_tuneproc - tune a drive + * @drive: drive to tune + * @mode_wanted: the target operating mode + * + * Load the timing settings for this device mode into the + * controller. By the time we are called the mode has been + * modified as neccessary to handle the absence of seperate + * master/slave timers for MWDMA/PIO. + * + * This code is only used in pass through mode. + */ + +static void it8212_tuneproc (ide_drive_t *drive, byte mode_wanted) +{ + ide_hwif_t *hwif = HWIF(drive); + struct it8212_dev *itdev = ide_get_hwifdata(hwif); + int unit = drive->select.b.unit; + + /* Spec says 89 ref driver uses 88 */ + static u16 pio[] = { 0xAA88, 0xA382, 0xA181, 0x3332, 0x3121 }; + static u8 pio_want[] = { ATA_66, ATA_66, ATA_66, ATA_66, ATA_ANY }; + + if(itdev->smart) + return; + + /* We prefer 66Mhz clock for PIO 0-3, don't care for PIO4 */ + itdev->want[unit][1] = pio_want[mode_wanted]; + itdev->want[unit][0] = 1; /* PIO is lowest priority */ + itdev->pio[unit] = pio[mode_wanted]; + it8212_clock_strategy(drive); + it8212_program(drive, itdev->pio[unit]); +} + +/** + * it8212_tune_mwdma - tune a channel for MWDMA + * @drive: drive to set up + * @mode_wanted: the target operating mode + * + * Load the timing settings for this device mode into the + * controller when doing MWDMA in pass through mode. The caller + * must manage the whole lack of per device MWDMA/PIO timings and + * the shared MWDMA/PIO timing register. + */ + +static void it8212_tune_mwdma (ide_drive_t *drive, byte mode_wanted) +{ + ide_hwif_t *hwif = HWIF(drive); + struct it8212_dev *itdev = (void *)ide_get_hwifdata(hwif); + int unit = drive->select.b.unit; + int channel = hwif->channel; + u8 conf; + + static u16 dma[] = { 0x8866, 0x3222, 0x3121 }; + static u8 mwdma_want[] = { ATA_ANY, ATA_66, ATA_ANY }; + + itdev->want[unit][1] = mwdma_want[mode_wanted]; + itdev->want[unit][0] = 2; /* MWDMA is low priority */ + itdev->mwdma[unit] = dma[mode_wanted]; + itdev->udma[unit] = UDMA_OFF; + + /* UDMA bits off - Revision 0x10 do them in pairs */ + pci_read_config_byte(hwif->pci_dev, 0x50, &conf); + if(itdev->timing10) + conf |= channel ? 0x60: 0x18; + else + conf |= 1 << (3 + 2 * channel + unit); + pci_write_config_byte(hwif->pci_dev, 0x50, conf); + + it8212_clock_strategy(drive); + /* FIXME: do we need to program this ? */ + /* it8212_program(drive, itdev->mwdma[unit]); */ +} + +/** + * it8212_tune_udma - tune a channel for UDMA + * @drive: drive to set up + * @mode_wanted: the target operating mode + * + * Load the timing settings for this device mode into the + * controller when doing UDMA modes in pass through. + */ + +static void it8212_tune_udma (ide_drive_t *drive, byte mode_wanted) +{ + ide_hwif_t *hwif = HWIF(drive); + struct it8212_dev *itdev = ide_get_hwifdata(hwif); + int unit = drive->select.b.unit; + int channel = hwif->channel; + u8 conf; + + static u16 udma[] = { 0x4433, 0x4231, 0x3121, 0x2121, 0x1111, 0x2211, 0x1111 }; + static u8 udma_want[] = { ATA_ANY, ATA_50, ATA_ANY, ATA_66, ATA_66, ATA_50, ATA_66 }; + + itdev->want[unit][1] = udma_want[mode_wanted]; + itdev->want[unit][0] = 3; /* UDMA is high priority */ + itdev->mwdma[unit] = MWDMA_OFF; + itdev->udma[unit] = udma[mode_wanted]; + if(mode_wanted >= 5) + itdev->udma[unit] |= 0x8080; /* UDMA 5/6 select on */ + + /* UDMA on. Again revision 0x10 must do the pair */ + pci_read_config_byte(hwif->pci_dev, 0x50, &conf); + if(itdev->timing10) + conf &= channel ? 0x9F: 0xE7; + else + conf &= ~ (1 << (3 + 2 * channel + unit)); + pci_write_config_byte(hwif->pci_dev, 0x50, conf); + + it8212_clock_strategy(drive); + it8212_program_udma(drive, itdev->udma[unit]); + +} + +/** + * config_it8212_chipset_for_pio - set drive timings + * @drive: drive to tune + * @speed we want + * + * Compute the best pio mode we can for a given device. We must + * pick a speed that does not cause problems with the other device + * on the cable. + */ + +static void config_it8212_chipset_for_pio (ide_drive_t *drive, byte set_speed) +{ + u8 unit = drive->select.b.unit; + ide_hwif_t *hwif = HWIF(drive); + ide_drive_t *pair = &hwif->drives[1-unit]; + u8 speed = 0, set_pio = ide_get_best_pio_mode(drive, 4, 5, NULL); + u8 pair_pio; + + /* We have to deal with this mess in pairs */ + if(pair != NULL) { + pair_pio = ide_get_best_pio_mode(pair, 4, 5, NULL); + /* Trim PIO to the slowest of the master/slave */ + if(pair_pio < set_pio) + set_pio = pair_pio; + } + it8212_tuneproc(drive, set_pio); + speed = XFER_PIO_0 + set_pio; + /* XXX - We trim to the lowest of the pair so the other drive + will always be fine at this point until we do hotplug passthru */ + + if (set_speed) + (void) ide_config_drive_speed(drive, speed); +} + +static void config_chipset_for_pio (ide_drive_t *drive, byte set_speed) +{ + config_it8212_chipset_for_pio(drive, set_speed); +} + +/** + * it8212_dma_read - DMA hook + * @drive: drive for DMA + * + * The IT8212 has a single timing register for MWDMA and for PIO + * operations. As we flip back and forth we have to reload the + * clock. In addition the rev 0x10 device only works if the same + * timing value is loaded into the master and slave UDMA clock + * so we must also reload that. + * + * FIXME: we could figure out in advance if we need to do reloads + */ + +static int it8212_dma_begin(ide_drive_t *drive) +{ + ide_hwif_t *hwif = HWIF(drive); + struct it8212_dev *itdev = ide_get_hwifdata(hwif); + int unit = drive->select.b.unit; + if(itdev->mwdma[unit] != MWDMA_OFF) + it8212_program(drive, itdev->mwdma[unit]); + else if(itdev->udma[unit] != UDMA_OFF && itdev->timing10) + it8212_program_udma(drive, itdev->udma[unit]); + return __ide_dma_begin(drive); +} + +/** + * it8212_dma_write - DMA hook + * @drive: drive for DMA stop + * + * The IT8212 has a single timing register for MWDMA and for PIO + * operations. As we flip back and forth we have to reload the + * clock. + */ + +static int it8212_dma_end(ide_drive_t *drive) +{ + ide_hwif_t *hwif = HWIF(drive); + int unit = drive->select.b.unit; + struct it8212_dev *itdev = ide_get_hwifdata(hwif); + int ret = __ide_dma_end(drive); + if(itdev->mwdma[unit] != MWDMA_OFF) + it8212_program(drive, itdev->pio[unit]); + return ret; +} + + +/** + * it8212_tune_chipset - set controller timings + * @drive: Drive to set up + * @xferspeed: speed we want to achieve + * + * Tune the ITE chipset for the desired mode. If we can't achieve + * the desired mode then tune for a lower one, but ultimately + * make the thing work. + */ + +static int it8212_tune_chipset (ide_drive_t *drive, byte xferspeed) +{ + + ide_hwif_t *hwif = HWIF(drive); + struct it8212_dev *itdev = ide_get_hwifdata(hwif); + u8 speed = ide_rate_filter(it8212_ratemask(drive), xferspeed); + + switch(speed) { + case XFER_PIO_4: + case XFER_PIO_3: + case XFER_PIO_2: + case XFER_PIO_1: + case XFER_PIO_0: + it8212_tuneproc(drive, (speed - XFER_PIO_0)); + break; + /* MWDMA tuning is really hard because our MWDMA and PIO + timings are kept in the same place. We can switch in the + host dma on/off callbacks */ + case XFER_MW_DMA_2: + case XFER_MW_DMA_1: + case XFER_MW_DMA_0: + if(!itdev->smart) + it8212_tune_mwdma(drive, (speed - XFER_MW_DMA_0)); + break; + case XFER_UDMA_6: + case XFER_UDMA_5: + case XFER_UDMA_4: + case XFER_UDMA_3: + case XFER_UDMA_2: + case XFER_UDMA_1: + case XFER_UDMA_0: + if(!itdev->smart) + it8212_tune_udma(drive, (speed - XFER_UDMA_0)); + break; + default: + return 1; + } + /* + * In smart mode the clocking is done by the host controller + * snooping the mode we picked. The rest of it is not our problem + */ + return (ide_config_drive_speed(drive, speed)); +} + +/** + * config_chipset_for_dma - configure for DMA + * @drive: drive to configure + * + * Called by the IDE layer when it wants the timings set up. + */ + +static int config_chipset_for_dma (ide_drive_t *drive) +{ + u8 speed = ide_dma_speed(drive, it8212_ratemask(drive)); + + config_chipset_for_pio(drive, !speed); + + if (!speed) + return 0; + + if (ide_set_xfer_rate(drive, speed)) + return 0; + + if (!drive->init_speed) + drive->init_speed = speed; + + return ide_dma_enable(drive); +} + +/** + * it8212_configure_drive_for_dma - set up for DMA transfers + * @drive: drive we are going to set up + * + * Set up the drive for DMA, tune the controller and drive as + * required. If the drive isn't suitable for DMA or we hit + * other problems then we will drop down to PIO and set up + * PIO appropriately + */ + +static int it8212_config_drive_for_dma (ide_drive_t *drive) +{ + ide_hwif_t *hwif = HWIF(drive); + struct hd_driveid *id = drive->id; + + if ((id->capability & 1) != 0 && drive->autodma) { + /* Consult the list of known "bad" drives */ + if (__ide_dma_bad_drive(drive)) + goto fast_ata_pio; + + if ((id->field_valid & 4) && it8212_ratemask(drive)) { + if (id->dma_ultra & hwif->ultra_mask) { + /* Force if Capable UltraDMA */ + int dma = config_chipset_for_dma(drive); + if ((id->field_valid & 2) && !dma) + goto try_dma_modes; + } + } else if (id->field_valid & 2) { +try_dma_modes: + if ((id->dma_mword & hwif->mwdma_mask) || + (id->dma_1word & hwif->swdma_mask)) { + /* Force if Capable regular DMA modes */ + if (!config_chipset_for_dma(drive)) + goto no_dma_set; + } + } else if (__ide_dma_good_drive(drive) && + (id->eide_dma_time < 150)) { + /* Consult the list of known "good" drives */ + if (!config_chipset_for_dma(drive)) + goto no_dma_set; + } else { + goto fast_ata_pio; + } + return hwif->ide_dma_on(drive); + } else if ((id->capability & 8) || (id->field_valid & 2)) { +fast_ata_pio: +no_dma_set: + config_chipset_for_pio(drive, 1); + return hwif->ide_dma_off_quietly(drive); + } + /* IORDY not supported */ + return 0; +} + +/** + * init_chipset_it8212 - set up an ITE device + * @dev: PCI device + * @name: device name + * + * PCI chipset level initialize. Nothing to do. + */ + +static unsigned int __devinit init_chipset_it8212(struct pci_dev *dev, const char *name) +{ + return 0; +} + +/** + * ata66_it8212 - check for 80 pin cable + * @hwif: interface to check + * + * Check for the presence of an ATA66 capable cable on the + * interface. Problematic as it seems some cards don't have + * the needed logic onboard. + */ + +static unsigned int __devinit ata66_it8212(ide_hwif_t *hwif) +{ + /* The reference driver also only does disk side */ + return 1; +} + +/** + * it8212_fixup - post init callback + * @hwif: interface + * + * This callback is run after the drives have been probed but + * before anything gets attached. It allows drivers to do any + * final tuning that is needed, or fixups to work around bugs. + */ + +static void __devinit it8212_fixups(ide_hwif_t *hwif) +{ + struct it8212_dev *itdev = ide_get_hwifdata(hwif); + int i; + + if(!itdev->smart) { + /* + * If we are in pass through mode then not much + * needs to be done, but we do bother to clear the + * IRQ mask in case the drives are PIO (eg rev 0x10) + * for now. + */ + for (i = 0; i < 2; i++) { + ide_drive_t *drive = &hwif->drives[i]; + if(drive->present) + drive->unmask = 1; + } + return; + } + /* + * Perform fixups on smart mode. We need to "lose" some + * capabilities the firmware lacks but does not filter, and + * also patch up some capability bits that it forgets to set + * in RAID mode. + */ + + for(i = 0; i < 2; i++) { + ide_drive_t *drive = &hwif->drives[i]; + struct hd_driveid *id; + u16 *idbits; + + if(!drive->present) + continue; + id = drive->id; + idbits = (u16 *)drive->id; + + /* Check for RAID v native */ + if(strstr(id->model, "Integrated Technology Express")) { + /* In raid mode the ident block is slightly buggy */ + id->capability |= 3; /* LBA28, DMA */ + id->command_set_2 |= 0x0400; /* LBA48 valid */ + id->cfs_enable_2 |= 0x0400; /* LBA48 on */ + /* Reporting logic */ + printk(KERN_INFO "%s: IT8212 %sRAID %d volume", + drive->name, + idbits[147] ? "Bootable ":"", + idbits[129]); + if(idbits[129] != 1) + printk("(%dK stripe)", idbits[146]); + printk(".\n"); + } else { + /* Non RAID volume. Fixups to stop the core code + doing unsupported things */ + id->field_valid &= 1; + id->queue_depth = 0; + id->command_set_1 = 0; + id->command_set_2 &= 0xC400; + id->cfsse &= 0xC000; + id->cfs_enable_1 = 0; + id->cfs_enable_2 &= 0xC400; + id->csf_default &= 0xC000; + id->word127 = 0; + id->dlf = 0; + id->csfo = 0; + id->cfa_power = 0; + printk(KERN_INFO "%s: Performing identify fixups.\n", + drive->name); + } + } + +} + +/** + * init_hwif_it8212 - set up hwif structs + * @hwif: interface to set up + * + * We do the basic set up of the interface structure. The IT8212 + * requires several custom handlers so we override the default + * ide DMA handlers appropriately + */ + +static void __devinit init_hwif_it8212(ide_hwif_t *hwif) +{ + struct it8212_dev *idev = kmalloc(sizeof(struct it8212_dev), GFP_KERNEL); + u8 conf; + static char *mode[2] = { "pass through", "smart" }; + + if(idev == NULL) { + printk(KERN_ERR "it8212: out of memory, falling back to legacy behaviour.\n"); + goto fallback; + } + memset(idev, 0, sizeof(struct it8212_dev)); + ide_set_hwifdata(hwif, idev); + + /* Force the card into bypass mode if so requested */ + if (it8212_noraid) { + printk(KERN_INFO "it8212: forcing bypass mode.\n"); + + /* Reset local CPU, and set BIOS not ready */ + pci_write_config_byte(hwif->pci_dev, 0x5E, 0x01); + + /* Set to bypass mode, and reset PCI bus */ + pci_write_config_byte(hwif->pci_dev, 0x50, 0x00); + + pci_write_config_word(hwif->pci_dev, PCI_COMMAND, + PCI_COMMAND_PARITY | PCI_COMMAND_IO | + PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER); + + pci_write_config_word(hwif->pci_dev, 0x40, 0xA0F3); + + pci_write_config_dword(hwif->pci_dev,0x4C, 0x02040204); + pci_write_config_byte(hwif->pci_dev, 0x42, 0x36); + pci_write_config_byte(hwif->pci_dev, PCI_LATENCY_TIMER, 0); + } + + pci_read_config_byte(hwif->pci_dev, 0x50, &conf); + if(conf & 1) { + idev->smart = 1; + hwif->atapi_dma = 0; + /* Long I/O's although allowed in LBA48 space cause the + onboard firmware to enter the twighlight zone */ + hwif->rqsize = 256; + } + + if(hwif->channel == 0) + printk(KERN_INFO "it8212: controller in %s mode.\n", + mode[idev->smart]); + + pci_read_config_byte(hwif->pci_dev, 0x50, &conf); + if (conf & (1 << (1 + hwif->channel))) + idev->clock_mode = ATA_50; + else + idev->clock_mode = ATA_66; + + idev->want[0][1] = ATA_ANY; + idev->want[1][1] = ATA_ANY; + + /* + * Not in the docs but according to the reference driver + * this is neccessary. + */ + + pci_read_config_byte(hwif->pci_dev, 0x08, &conf); + if(conf == 0x10) { + idev->timing10 = 1; + hwif->atapi_dma = 0; + if(!idev->smart) + printk(KERN_WARNING "it8212: Revision 0x10, workarounds activated.\n"); + } + + hwif->speedproc = &it8212_tune_chipset; + hwif->tuneproc = &it8212_tuneproc; + + /* MWDMA/PIO clock switching for pass through mode */ + if(!idev->smart) { + hwif->ide_dma_begin = &it8212_dma_begin; + hwif->ide_dma_end = &it8212_dma_end; + } + + if (!hwif->dma_base) + goto fallback; + + hwif->ultra_mask = 0x7f; + hwif->mwdma_mask = 0x07; + hwif->swdma_mask = 0x07; + + hwif->ide_dma_check = &it8212_config_drive_for_dma; + if (!(hwif->udma_four)) + hwif->udma_four = ata66_it8212(hwif); + + /* + * The BIOS often doesn't set up DMA on this controller + * so we always do it. + */ + + hwif->autodma = 1; + hwif->drives[0].autodma = hwif->autodma; + hwif->drives[1].autodma = hwif->autodma; + return; + +fallback: + hwif->autodma = 0; + hwif->drives[0].autotune = 1; + hwif->drives[1].autotune = 1; + return; +} + +#define DECLARE_ITE_DEV(name_str) \ + { \ + .name = name_str, \ + .init_chipset = init_chipset_it8212, \ + .init_hwif = init_hwif_it8212, \ + .channels = 2, \ + .autodma = AUTODMA, \ + .bootable = ON_BOARD, \ + .fixup = it8212_fixups \ + } + +static ide_pci_device_t it8212_chipsets[] __devinitdata = { + /* 0 */ DECLARE_ITE_DEV("IT8212"), +}; + +/** + * it8212_init_one - pci layer discovery entry + * @dev: PCI device + * @id: ident table entry + * + * Called by the PCI code when it finds an ITE8212 controller. + * We then use the IDE PCI generic helper to do most of the work. + */ + +static int __devinit it8212_init_one(struct pci_dev *dev, const struct pci_device_id *id) +{ + ide_setup_pci_device(dev, &it8212_chipsets[id->driver_data]); + return 0; +} + +static struct pci_device_id it8212_pci_tbl[] = { + { PCI_VENDOR_ID_ITE, PCI_DEVICE_ID_ITE_8212, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, + { 0, }, +}; + +MODULE_DEVICE_TABLE(pci, it8212_pci_tbl); + +static struct pci_driver driver = { + .name = "ITE8212 IDE", + .id_table = it8212_pci_tbl, + .probe = it8212_init_one, +}; + +static int __init it8212_ide_init(void) +{ + return ide_pci_register_driver(&driver); +} + +module_init(it8212_ide_init); + +module_param_named(noraid, it8212_noraid, int, S_IRUGO); +MODULE_PARM_DESC(it8212_noraid, "Force card into bypass mode"); + +MODULE_AUTHOR("Alan Cox"); +MODULE_DESCRIPTION("PCI driver module for the ITE 8212"); +MODULE_LICENSE("GPL"); diff --git a/drivers/ide/ppc/pmac.c b/drivers/ide/ppc/pmac.c index 7030168e6..3625e38a7 100644 --- a/drivers/ide/ppc/pmac.c +++ b/drivers/ide/ppc/pmac.c @@ -62,10 +62,10 @@ typedef struct pmac_ide_hwif { int irq; int kind; int aapl_bus_id; - int cable_80 : 1; - int mediabay : 1; - int broken_dma : 1; - int broken_dma_warn : 1; + unsigned cable_80 : 1; + unsigned mediabay : 1; + unsigned broken_dma : 1; + unsigned broken_dma_warn : 1; struct device_node* node; struct macio_dev *mdev; u32 timings[4]; @@ -75,7 +75,7 @@ typedef struct pmac_ide_hwif { * beeing done by the generic code about the kind of dma controller * and format of the dma table. This will have to be fixed though. */ - volatile struct dbdma_regs* dma_regs; + volatile struct dbdma_regs __iomem * dma_regs; struct dbdma_cmd* dma_table_cpu; dma_addr_t dma_table_dma; struct scatterlist* sg_table; @@ -497,6 +497,8 @@ pmac_ide_init_hwif_ports(hw_regs_t *hw, *irq = pmac_ide[ix].irq; } +#define PMAC_IDE_REG(x) ((void __iomem *)(IDE_DATA_REG+(x))) + /* * Apply the timings of the proper unit (master/slave) to the shared * timing register when selecting that unit. This version is for @@ -511,12 +513,10 @@ pmac_ide_selectproc(ide_drive_t *drive) return; if (drive->select.b.unit & 0x01) - writel(pmif->timings[1], - (unsigned *)(IDE_DATA_REG+IDE_TIMING_CONFIG)); + writel(pmif->timings[1], PMAC_IDE_REG(IDE_TIMING_CONFIG)); else - writel(pmif->timings[0], - (unsigned *)(IDE_DATA_REG+IDE_TIMING_CONFIG)); - (void)readl((unsigned *)(IDE_DATA_REG+IDE_TIMING_CONFIG)); + writel(pmif->timings[0], PMAC_IDE_REG(IDE_TIMING_CONFIG)); + (void)readl(PMAC_IDE_REG(IDE_TIMING_CONFIG)); } /* @@ -533,17 +533,13 @@ pmac_ide_kauai_selectproc(ide_drive_t *drive) return; if (drive->select.b.unit & 0x01) { - writel(pmif->timings[1], - (unsigned *)(IDE_DATA_REG + IDE_KAUAI_PIO_CONFIG)); - writel(pmif->timings[3], - (unsigned *)(IDE_DATA_REG + IDE_KAUAI_ULTRA_CONFIG)); + writel(pmif->timings[1], PMAC_IDE_REG(IDE_KAUAI_PIO_CONFIG)); + writel(pmif->timings[3], PMAC_IDE_REG(IDE_KAUAI_ULTRA_CONFIG)); } else { - writel(pmif->timings[0], - (unsigned *)(IDE_DATA_REG + IDE_KAUAI_PIO_CONFIG)); - writel(pmif->timings[2], - (unsigned *)(IDE_DATA_REG + IDE_KAUAI_ULTRA_CONFIG)); + writel(pmif->timings[0], PMAC_IDE_REG(IDE_KAUAI_PIO_CONFIG)); + writel(pmif->timings[2], PMAC_IDE_REG(IDE_KAUAI_ULTRA_CONFIG)); } - (void)readl((unsigned *)(IDE_DATA_REG + IDE_KAUAI_PIO_CONFIG)); + (void)readl(PMAC_IDE_REG(IDE_KAUAI_PIO_CONFIG)); } /* @@ -568,8 +564,8 @@ pmac_outbsync(ide_drive_t *drive, u8 value, unsigned long port) { u32 tmp; - writeb(value, port); - tmp = readl((unsigned *)(IDE_DATA_REG + IDE_TIMING_CONFIG)); + writeb(value, (void __iomem *) port); + tmp = readl(PMAC_IDE_REG(IDE_TIMING_CONFIG)); } /* @@ -792,7 +788,7 @@ static int __pmac set_timings_mdma(ide_drive_t *drive, int intf_type, u32 *timings, u32 *timings2, u8 speed, int drive_cycle_time) { - int cycleTime, accessTime, recTime; + int cycleTime, accessTime = 0, recTime = 0; unsigned accessTicks, recTicks; struct mdma_timings_t* tm = NULL; int i; @@ -1281,7 +1277,8 @@ pmac_ide_setup_device(pmac_ide_hwif_t *pmif, ide_hwif_t *hwif) static int __devinit pmac_ide_macio_attach(struct macio_dev *mdev, const struct of_match *match) { - unsigned long base, regbase; + void __iomem *base; + unsigned long regbase; int irq; ide_hwif_t *hwif; pmac_ide_hwif_t *pmif; @@ -1324,8 +1321,8 @@ pmac_ide_macio_attach(struct macio_dev *mdev, const struct of_match *match) } else irq = macio_irq(mdev, 0); - base = (unsigned long)ioremap(macio_resource_start(mdev, 0), 0x400); - regbase = base; + base = ioremap(macio_resource_start(mdev, 0), 0x400); + regbase = (unsigned long) base; hwif->pci_dev = mdev->bus->pdev; hwif->gendev.parent = &mdev->ofdev.dev; @@ -1339,8 +1336,7 @@ pmac_ide_macio_attach(struct macio_dev *mdev, const struct of_match *match) if (macio_request_resource(mdev, 1, "ide-pmac (dma)")) printk(KERN_WARNING "ide%d: can't request DMA resource !\n", i); else - pmif->dma_regs = (volatile struct dbdma_regs*) - ioremap(macio_resource_start(mdev, 1), 0x1000); + pmif->dma_regs = ioremap(macio_resource_start(mdev, 1), 0x1000); } else pmif->dma_regs = NULL; #endif /* CONFIG_BLK_DEV_IDEDMA_PMAC */ @@ -1350,9 +1346,9 @@ pmac_ide_macio_attach(struct macio_dev *mdev, const struct of_match *match) if (rc != 0) { /* The inteface is released to the common IDE layer */ dev_set_drvdata(&mdev->ofdev.dev, NULL); - iounmap((void *)base); + iounmap(base); if (pmif->dma_regs) - iounmap((void *)pmif->dma_regs); + iounmap(pmif->dma_regs); memset(pmif, 0, sizeof(*pmif)); macio_release_resource(mdev, 0); if (pmif->dma_regs) @@ -1401,7 +1397,7 @@ pmac_ide_pci_attach(struct pci_dev *pdev, const struct pci_device_id *id) ide_hwif_t *hwif; struct device_node *np; pmac_ide_hwif_t *pmif; - unsigned long base; + void __iomem *base; unsigned long rbase, rlen; int i, rc; @@ -1444,10 +1440,10 @@ pmac_ide_pci_attach(struct pci_dev *pdev, const struct pci_device_id *id) rbase = pci_resource_start(pdev, 0); rlen = pci_resource_len(pdev, 0); - base = (unsigned long) ioremap(rbase, rlen); - pmif->regbase = base + 0x2000; + base = ioremap(rbase, rlen); + pmif->regbase = (unsigned long) base + 0x2000; #ifdef CONFIG_BLK_DEV_IDEDMA_PMAC - pmif->dma_regs = (volatile struct dbdma_regs*)(base + 0x1000); + pmif->dma_regs = base + 0x1000; #endif /* CONFIG_BLK_DEV_IDEDMA_PMAC */ /* We use the OF node irq mapping */ @@ -1462,7 +1458,7 @@ pmac_ide_pci_attach(struct pci_dev *pdev, const struct pci_device_id *id) if (rc != 0) { /* The inteface is released to the common IDE layer */ pci_set_drvdata(pdev, NULL); - iounmap((void *)base); + iounmap(base); memset(pmif, 0, sizeof(*pmif)); pci_release_regions(pdev); } @@ -1578,9 +1574,6 @@ pmac_ide_build_sglist(ide_drive_t *drive, struct request *rq) struct scatterlist *sg = pmif->sg_table; int nents; - if (hwif->sg_dma_active) - BUG(); - nents = blk_rq_map_sg(drive->queue, rq, sg); if (rq_data_dir(rq) == READ) @@ -1639,7 +1632,7 @@ pmac_ide_build_dmatable(ide_drive_t *drive, struct request *rq) int i, count = 0; ide_hwif_t *hwif = HWIF(drive); pmac_ide_hwif_t* pmif = (pmac_ide_hwif_t *)hwif->hwif_data; - volatile struct dbdma_regs *dma = pmif->dma_regs; + volatile struct dbdma_regs __iomem *dma = pmif->dma_regs; struct scatterlist *sg; int wr = (rq_data_dir(rq) == WRITE); @@ -1715,7 +1708,6 @@ pmac_ide_build_dmatable(ide_drive_t *drive, struct request *rq) pmif->sg_table, pmif->sg_nents, pmif->sg_dma_direction); - hwif->sg_dma_active = 0; return 0; /* revert to PIO for this request */ } @@ -1731,7 +1723,6 @@ pmac_ide_destroy_dmatable (ide_drive_t *drive) if (nents) { pci_unmap_sg(dev, sg, nents, pmif->sg_dma_direction); pmif->sg_nents = 0; - HWIF(drive)->sg_dma_active = 0; } } @@ -1912,8 +1903,8 @@ pmac_ide_dma_start(ide_drive_t *drive, int reading) /* Apple adds 60ns to wrDataSetup on reads */ if (ata4 && (pmif->timings[unit] & TR_66_UDMA_EN)) { writel(pmif->timings[unit] + (reading ? 0x00800000UL : 0), - (unsigned *)(IDE_DATA_REG+IDE_TIMING_CONFIG)); - (void)readl((unsigned *)(IDE_DATA_REG + IDE_TIMING_CONFIG)); + PMAC_IDE_REG(IDE_TIMING_CONFIG)); + (void)readl(PMAC_IDE_REG(IDE_TIMING_CONFIG)); } drive->waiting_for_dma = 1; @@ -1992,7 +1983,7 @@ static int __pmac pmac_ide_dma_begin (ide_drive_t *drive) { pmac_ide_hwif_t* pmif = (pmac_ide_hwif_t *)HWIF(drive)->hwif_data; - volatile struct dbdma_regs *dma; + volatile struct dbdma_regs __iomem *dma; if (pmif == NULL) return 1; @@ -2011,7 +2002,7 @@ static int __pmac pmac_ide_dma_end (ide_drive_t *drive) { pmac_ide_hwif_t* pmif = (pmac_ide_hwif_t *)HWIF(drive)->hwif_data; - volatile struct dbdma_regs *dma; + volatile struct dbdma_regs __iomem *dma; u32 dstat; if (pmif == NULL) @@ -2039,7 +2030,7 @@ static int __pmac pmac_ide_dma_test_irq (ide_drive_t *drive) { pmac_ide_hwif_t* pmif = (pmac_ide_hwif_t *)HWIF(drive)->hwif_data; - volatile struct dbdma_regs *dma; + volatile struct dbdma_regs __iomem *dma; unsigned long status, timeout; if (pmif == NULL) @@ -2107,7 +2098,7 @@ static int __pmac pmac_ide_dma_lostirq (ide_drive_t *drive) { pmac_ide_hwif_t* pmif = (pmac_ide_hwif_t *)HWIF(drive)->hwif_data; - volatile struct dbdma_regs *dma; + volatile struct dbdma_regs __iomem *dma; unsigned long status; if (pmif == NULL) diff --git a/drivers/ieee1394/eth1394.h b/drivers/ieee1394/eth1394.h index 015f8178c..ed8f1c4b7 100644 --- a/drivers/ieee1394/eth1394.h +++ b/drivers/ieee1394/eth1394.h @@ -81,7 +81,14 @@ struct eth1394hdr { unsigned short h_proto; /* packet type ID field */ } __attribute__((packed)); +#ifdef __KERNEL__ +#include +static inline struct eth1394hdr *eth1394_hdr(const struct sk_buff *skb) +{ + return (struct eth1394hdr *)skb->mac.raw; +} +#endif typedef enum {ETH1394_GASP, ETH1394_WRREQ} eth1394_tx_type; diff --git a/drivers/ieee1394/ohci1394.h b/drivers/ieee1394/ohci1394.h index e2e6194c9..d1758d409 100644 --- a/drivers/ieee1394/ohci1394.h +++ b/drivers/ieee1394/ohci1394.h @@ -163,7 +163,7 @@ struct ti_ohci { } init_state; /* remapped memory spaces */ - void *registers; + void __iomem *registers; /* dma buffer for self-id packets */ quadlet_t *selfid_buf_cpu; diff --git a/drivers/input/gameport/cs461x.c b/drivers/input/gameport/cs461x.c index 71fcad10c..8459e05c6 100644 --- a/drivers/input/gameport/cs461x.c +++ b/drivers/input/gameport/cs461x.c @@ -312,7 +312,7 @@ static void __devexit cs461x_pci_remove(struct pci_dev *pdev) } static struct pci_driver cs461x_pci_driver = { - .name = "CS461x Gameport", + .name = "CS461x_gameport", .id_table = cs461x_pci_tbl, .probe = cs461x_pci_probe, .remove = __devexit_p(cs461x_pci_remove), diff --git a/drivers/input/gameport/emu10k1-gp.c b/drivers/input/gameport/emu10k1-gp.c index 89d469378..8f1461ad2 100644 --- a/drivers/input/gameport/emu10k1-gp.c +++ b/drivers/input/gameport/emu10k1-gp.c @@ -50,8 +50,11 @@ struct emu { }; static struct pci_device_id emu_tbl[] = { + { 0x1102, 0x7002, PCI_ANY_ID, PCI_ANY_ID }, /* SB Live gameport */ { 0x1102, 0x7003, PCI_ANY_ID, PCI_ANY_ID }, /* Audigy gameport */ + { 0x1102, 0x7004, PCI_ANY_ID, PCI_ANY_ID }, /* Dell SB Live */ + { 0x1102, 0x7005, PCI_ANY_ID, PCI_ANY_ID }, /* Audigy LS gameport */ { 0, } }; @@ -109,7 +112,7 @@ static void __devexit emu_remove(struct pci_dev *pdev) } static struct pci_driver emu_driver = { - .name = "Emu10k1 Gameport", + .name = "Emu10k1_gameport", .id_table = emu_tbl, .probe = emu_probe, .remove = __devexit_p(emu_remove), diff --git a/drivers/input/gameport/fm801-gp.c b/drivers/input/gameport/fm801-gp.c index ff3b620b8..93849a6cc 100644 --- a/drivers/input/gameport/fm801-gp.c +++ b/drivers/input/gameport/fm801-gp.c @@ -137,7 +137,7 @@ static struct pci_device_id fm801_gp_id_table[] = { }; static struct pci_driver fm801_gp_driver = { - .name = "FM801 GP", + .name = "FM801_gameport", .id_table = fm801_gp_id_table, .probe = fm801_gp_probe, .remove = __devexit_p(fm801_gp_remove), diff --git a/drivers/input/joystick/Kconfig b/drivers/input/joystick/Kconfig index 7d506f1f7..8f62f70d0 100644 --- a/drivers/input/joystick/Kconfig +++ b/drivers/input/joystick/Kconfig @@ -247,9 +247,9 @@ config JOYSTICK_AMIGA To compile this driver as a module, choose M here: the module will be called amijoy. -config INPUT_JOYDUMP +config JOYSTICK_JOYDUMP tristate "Gameport data dumper" - depends on INPUT && INPUT_JOYSTICK + depends on INPUT && INPUT_JOYSTICK && GAMEPORT help Say Y here if you want to dump data from your joystick into the system log for debugging purposes. Say N if you are making a production diff --git a/drivers/input/joystick/gamecon.c b/drivers/input/joystick/gamecon.c index 555fb362b..46326b701 100644 --- a/drivers/input/joystick/gamecon.c +++ b/drivers/input/joystick/gamecon.c @@ -1,17 +1,14 @@ /* - * $Id: gamecon.c,v 1.22 2002/07/01 15:42:25 vojtech Exp $ + * NES, SNES, N64, MultiSystem, PSX gamepad driver for Linux * - * Copyright (c) 1999-2001 Vojtech Pavlik + * Copyright (c) 1999-2004 Vojtech Pavlik + * Copyright (c) 2004 Peter Nelson * * Based on the work of: * Andree Borrmann John Dahlstrom * David Kuder Nathan Hand */ -/* - * NES, SNES, N64, MultiSystem, PSX gamepad driver for Linux - */ - /* * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -72,8 +69,9 @@ __obsolete_setup("gc_3="); #define GC_MULTI2 5 #define GC_N64 6 #define GC_PSX 7 +#define GC_DDR 8 -#define GC_MAX 7 +#define GC_MAX 8 #define GC_REFRESH_TIME HZ/100 @@ -91,7 +89,8 @@ static struct gc *gc_base[3]; static int gc_status_bit[] = { 0x40, 0x80, 0x20, 0x10, 0x08 }; static char *gc_names[] = { NULL, "SNES pad", "NES pad", "NES FourPort", "Multisystem joystick", - "Multisystem 2-button joystick", "N64 controller", "PSX controller" }; + "Multisystem 2-button joystick", "N64 controller", "PSX controller" + "PSX DDR controller" }; /* * N64 support. */ @@ -237,7 +236,7 @@ static void gc_multi_read_packet(struct gc *gc, int length, unsigned char *data) #define GC_PSX_RUMBLE 7 /* Rumble in Red mode */ #define GC_PSX_CLOCK 0x04 /* Pin 4 */ -#define GC_PSX_COMMAND 0x01 /* Pin 1 */ +#define GC_PSX_COMMAND 0x01 /* Pin 2 */ #define GC_PSX_POWER 0xf8 /* Pins 5-9 */ #define GC_PSX_SELECT 0x02 /* Pin 3 */ @@ -253,25 +252,29 @@ __obsolete_setup("gc_psx_delay="); static short gc_psx_abs[] = { ABS_X, ABS_Y, ABS_RX, ABS_RY, ABS_HAT0X, ABS_HAT0Y }; static short gc_psx_btn[] = { BTN_TL, BTN_TR, BTN_TL2, BTN_TR2, BTN_A, BTN_B, BTN_X, BTN_Y, BTN_START, BTN_SELECT, BTN_THUMBL, BTN_THUMBR }; +static short gc_psx_ddr_btn[] = { BTN_0, BTN_1, BTN_2, BTN_3 }; /* * gc_psx_command() writes 8bit command and reads 8bit data from * the psx pad. */ -static int gc_psx_command(struct gc *gc, int b) +static void gc_psx_command(struct gc *gc, int b, unsigned char data[GC_PSX_LENGTH]) { - int i, cmd, data = 0; + int i, j, cmd, read; + for (i = 0; i < 5; i++) + data[i] = 0; for (i = 0; i < 8; i++, b >>= 1) { cmd = (b & 1) ? GC_PSX_COMMAND : 0; parport_write_data(gc->pd->port, cmd | GC_PSX_POWER); udelay(gc_psx_delay); - data |= ((parport_read_status(gc->pd->port) ^ 0x80) & gc->pads[GC_PSX]) ? (1 << i) : 0; + read = parport_read_status(gc->pd->port) ^ 0x80; + for (j = 0; j < 5; j++) + data[j] |= (read & gc_status_bit[j] & gc->pads[GC_PSX]) ? (1 << i) : 0; parport_write_data(gc->pd->port, cmd | GC_PSX_CLOCK | GC_PSX_POWER); udelay(gc_psx_delay); } - return data; } /* @@ -279,30 +282,39 @@ static int gc_psx_command(struct gc *gc, int b) * device identifier code. */ -static int gc_psx_read_packet(struct gc *gc, unsigned char *data) +static void gc_psx_read_packet(struct gc *gc, unsigned char data[5][GC_PSX_LENGTH], unsigned char id[5]) { - int i, id; + int i, j, max_len = 0; unsigned long flags; + unsigned char data2[5]; parport_write_data(gc->pd->port, GC_PSX_CLOCK | GC_PSX_SELECT | GC_PSX_POWER); /* Select pad */ - udelay(gc_psx_delay * 2); + udelay(gc_psx_delay); parport_write_data(gc->pd->port, GC_PSX_CLOCK | GC_PSX_POWER); /* Deselect, begin command */ - udelay(gc_psx_delay * 2); + udelay(gc_psx_delay); local_irq_save(flags); - gc_psx_command(gc, 0x01); /* Access pad */ - id = gc_psx_command(gc, 0x42); /* Get device id */ - if (gc_psx_command(gc, 0) == 0x5a) { /* Okay? */ - for (i = 0; i < GC_PSX_LEN(id) * 2; i++) - data[i] = gc_psx_command(gc, 0); - } else id = 0; + gc_psx_command(gc, 0x01, data2); /* Access pad */ + gc_psx_command(gc, 0x42, id); /* Get device ids */ + gc_psx_command(gc, 0, data2); /* Dump status */ + + for (i =0; i < 5; i++) /* Find the longest pad */ + if((gc_status_bit[i] & gc->pads[GC_PSX]) && (GC_PSX_LEN(id[i]) > max_len)) + max_len = GC_PSX_LEN(id[i]); + + for (i = 0; i < max_len * 2; i++) { /* Read in all the data */ + gc_psx_command(gc, 0, data2); + for (j = 0; j < 5; j++) + data[j][i] = data2[j]; + } local_irq_restore(flags); parport_write_data(gc->pd->port, GC_PSX_CLOCK | GC_PSX_SELECT | GC_PSX_POWER); - return GC_PSX_ID(id); + for(i = 0; i < 5; i++) /* Set id's to the real value */ + id[i] = GC_PSX_ID(id[i]); } /* @@ -316,6 +328,7 @@ static void gc_timer(unsigned long private) struct gc *gc = (void *) private; struct input_dev *dev = gc->dev; unsigned char data[GC_MAX_LENGTH]; + unsigned char data_psx[5][GC_PSX_LENGTH]; int i, j, s; /* @@ -412,53 +425,72 @@ static void gc_timer(unsigned long private) * PSX controllers */ - if (gc->pads[GC_PSX]) { + if (gc->pads[GC_PSX] || gc->pads[GC_DDR]) { - for (i = 0; i < 5; i++) - if (gc->pads[GC_PSX] & gc_status_bit[i]) - break; + gc_psx_read_packet(gc, data_psx, data); - switch (gc_psx_read_packet(gc, data)) { + for (i = 0; i < 5; i++) { + switch (data[i]) { - case GC_PSX_RUMBLE: + case GC_PSX_RUMBLE: - input_report_key(dev + i, BTN_THUMBL, ~data[0] & 0x04); - input_report_key(dev + i, BTN_THUMBR, ~data[0] & 0x02); - input_sync(dev + i); + input_report_key(dev + i, BTN_THUMBL, ~data_psx[i][0] & 0x04); + input_report_key(dev + i, BTN_THUMBR, ~data_psx[i][0] & 0x02); - case GC_PSX_NEGCON: - case GC_PSX_ANALOG: + case GC_PSX_NEGCON: + case GC_PSX_ANALOG: - for (j = 0; j < 4; j++) - input_report_abs(dev + i, gc_psx_abs[j], data[j + 2]); + if(gc->pads[GC_DDR] & gc_status_bit[i]) { + for(j = 0; j < 4; j++) + input_report_key(dev + i, gc_psx_ddr_btn[j], ~data_psx[i][0] & (0x10 << j)); + } else { + for (j = 0; j < 4; j++) + input_report_abs(dev + i, gc_psx_abs[j+2], data_psx[i][j + 2]); - input_report_abs(dev + i, ABS_HAT0X, !(data[0] & 0x20) - !(data[0] & 0x80)); - input_report_abs(dev + i, ABS_HAT0Y, !(data[0] & 0x40) - !(data[0] & 0x10)); + input_report_abs(dev + i, ABS_X, 128 + !(data_psx[i][0] & 0x20) * 127 - !(data_psx[i][0] & 0x80) * 128); + input_report_abs(dev + i, ABS_Y, 128 + !(data_psx[i][0] & 0x40) * 127 - !(data_psx[i][0] & 0x10) * 128); + } - for (j = 0; j < 8; j++) - input_report_key(dev + i, gc_psx_btn[j], ~data[1] & (1 << j)); + for (j = 0; j < 8; j++) + input_report_key(dev + i, gc_psx_btn[j], ~data_psx[i][1] & (1 << j)); - input_report_key(dev + i, BTN_START, ~data[0] & 0x08); - input_report_key(dev + i, BTN_SELECT, ~data[0] & 0x01); + input_report_key(dev + i, BTN_START, ~data_psx[i][0] & 0x08); + input_report_key(dev + i, BTN_SELECT, ~data_psx[i][0] & 0x01); - input_sync(dev + i); + input_sync(dev + i); - break; + break; - case GC_PSX_NORMAL: + case GC_PSX_NORMAL: + if(gc->pads[GC_DDR] & gc_status_bit[i]) { + for(j = 0; j < 4; j++) + input_report_key(dev + i, gc_psx_ddr_btn[j], ~data_psx[i][0] & (0x10 << j)); + } else { + input_report_abs(dev + i, ABS_X, 128 + !(data_psx[i][0] & 0x20) * 127 - !(data_psx[i][0] & 0x80) * 128); + input_report_abs(dev + i, ABS_Y, 128 + !(data_psx[i][0] & 0x40) * 127 - !(data_psx[i][0] & 0x10) * 128); - input_report_abs(dev + i, ABS_X, 128 + !(data[0] & 0x20) * 127 - !(data[0] & 0x80) * 128); - input_report_abs(dev + i, ABS_Y, 128 + !(data[0] & 0x40) * 127 - !(data[0] & 0x10) * 128); + /* for some reason if the extra axes are left unset they drift */ + /* for (j = 0; j < 4; j++) + input_report_abs(dev + i, gc_psx_abs[j+2], 128); + * This needs to be debugged properly, + * maybe fuzz processing needs to be done in input_sync() + * --vojtech + */ + } - for (j = 0; j < 8; j++) - input_report_key(dev + i, gc_psx_btn[j], ~data[1] & (1 << j)); + for (j = 0; j < 8; j++) + input_report_key(dev + i, gc_psx_btn[j], ~data_psx[i][1] & (1 << j)); - input_report_key(dev + i, BTN_START, ~data[0] & 0x08); - input_report_key(dev + i, BTN_SELECT, ~data[0] & 0x01); + input_report_key(dev + i, BTN_START, ~data_psx[i][0] & 0x08); + input_report_key(dev + i, BTN_SELECT, ~data_psx[i][0] & 0x01); - input_sync(dev + i); + input_sync(dev + i); - break; + break; + + case 0: /* not a pad, ignore */ + break; + } } } @@ -490,8 +522,7 @@ static struct gc __init *gc_probe(int *config, int nargs) { struct gc *gc; struct parport *pp; - int i, j, psx; - unsigned char data[32]; + int i, j; if (config[0] < 0) return NULL; @@ -588,43 +619,22 @@ static struct gc __init *gc_probe(int *config, int nargs) break; case GC_PSX: - - psx = gc_psx_read_packet(gc, data); - - switch(psx) { - case GC_PSX_NEGCON: - case GC_PSX_NORMAL: - case GC_PSX_ANALOG: - case GC_PSX_RUMBLE: - - for (j = 0; j < 6; j++) { - psx = gc_psx_abs[j]; - set_bit(psx, gc->dev[i].absbit); - if (j < 4) { - gc->dev[i].absmin[psx] = 4; - gc->dev[i].absmax[psx] = 252; - gc->dev[i].absflat[psx] = 2; - } else { - gc->dev[i].absmin[psx] = -1; - gc->dev[i].absmax[psx] = 1; - } - } - - for (j = 0; j < 12; j++) - set_bit(gc_psx_btn[j], gc->dev[i].keybit); - - break; - - case 0: - gc->pads[GC_PSX] &= ~gc_status_bit[i]; - printk(KERN_ERR "gamecon.c: No PSX controller found.\n"); - break; - - default: - gc->pads[GC_PSX] &= ~gc_status_bit[i]; - printk(KERN_WARNING "gamecon.c: Unsupported PSX controller %#x," - " please report to .\n", psx); + case GC_DDR: + if(config[i + 1] == GC_DDR) { + for (j = 0; j < 4; j++) + set_bit(gc_psx_ddr_btn[j], gc->dev[i].keybit); + } else { + for (j = 0; j < 6; j++) { + set_bit(gc_psx_abs[j], gc->dev[i].absbit); + gc->dev[i].absmin[gc_psx_abs[j]] = 4; + gc->dev[i].absmax[gc_psx_abs[j]] = 252; + gc->dev[i].absflat[gc_psx_abs[j]] = 2; + } } + + for (j = 0; j < 12; j++) + set_bit(gc_psx_btn[j], gc->dev[i].keybit); + break; } diff --git a/drivers/input/joystick/iforce/iforce-main.c b/drivers/input/joystick/iforce/iforce-main.c index 71cb7db75..cf815c7ee 100644 --- a/drivers/input/joystick/iforce/iforce-main.c +++ b/drivers/input/joystick/iforce/iforce-main.c @@ -524,7 +524,7 @@ static int __init iforce_init(void) usb_register(&iforce_usb_driver); #endif #ifdef CONFIG_JOYSTICK_IFORCE_232 - serio_register_device(&iforce_serio_dev); + serio_register_driver(&iforce_serio_drv); #endif return 0; } @@ -535,7 +535,7 @@ static void __exit iforce_exit(void) usb_deregister(&iforce_usb_driver); #endif #ifdef CONFIG_JOYSTICK_IFORCE_232 - serio_unregister_device(&iforce_serio_dev); + serio_unregister_driver(&iforce_serio_drv); #endif } diff --git a/drivers/input/joystick/iforce/iforce-serio.c b/drivers/input/joystick/iforce/iforce-serio.c index 0665b5460..9422407c2 100644 --- a/drivers/input/joystick/iforce/iforce-serio.c +++ b/drivers/input/joystick/iforce/iforce-serio.c @@ -124,7 +124,7 @@ out: return IRQ_HANDLED; } -static void iforce_serio_connect(struct serio *serio, struct serio_dev *dev) +static void iforce_serio_connect(struct serio *serio, struct serio_driver *drv) { struct iforce *iforce; if (serio->type != (SERIO_RS232 | SERIO_IFORCE)) @@ -137,7 +137,7 @@ static void iforce_serio_connect(struct serio *serio, struct serio_dev *dev) iforce->serio = serio; serio->private = iforce; - if (serio_open(serio, dev)) { + if (serio_open(serio, drv)) { kfree(iforce); return; } @@ -158,9 +158,13 @@ static void iforce_serio_disconnect(struct serio *serio) kfree(iforce); } -struct serio_dev iforce_serio_dev = { - .write_wakeup = iforce_serio_write_wakeup, - .interrupt = iforce_serio_irq, - .connect = iforce_serio_connect, - .disconnect = iforce_serio_disconnect, +struct serio_driver iforce_serio_drv = { + .driver = { + .name = "iforce", + }, + .description = "RS232 I-Force joysticks and wheels driver", + .write_wakeup = iforce_serio_write_wakeup, + .interrupt = iforce_serio_irq, + .connect = iforce_serio_connect, + .disconnect = iforce_serio_disconnect, }; diff --git a/drivers/input/joystick/iforce/iforce.h b/drivers/input/joystick/iforce/iforce.h index a8586abf5..bce247bc3 100644 --- a/drivers/input/joystick/iforce/iforce.h +++ b/drivers/input/joystick/iforce/iforce.h @@ -187,5 +187,5 @@ int iforce_upload_constant(struct iforce*, struct ff_effect*, int is_update); int iforce_upload_condition(struct iforce*, struct ff_effect*, int is_update); /* Public variables */ -extern struct serio_dev iforce_serio_dev; +extern struct serio_driver iforce_serio_drv; extern struct usb_driver iforce_usb_driver; diff --git a/drivers/input/joystick/magellan.c b/drivers/input/joystick/magellan.c index f326299b4..2b303bac5 100644 --- a/drivers/input/joystick/magellan.c +++ b/drivers/input/joystick/magellan.c @@ -35,8 +35,10 @@ #include #include +#define DRIVER_DESC "Magellan and SpaceMouse 6dof controller driver" + MODULE_AUTHOR("Vojtech Pavlik "); -MODULE_DESCRIPTION("Magellan and SpaceMouse 6dof controller driver"); +MODULE_DESCRIPTION(DRIVER_DESC); MODULE_LICENSE("GPL"); /* @@ -146,7 +148,7 @@ static void magellan_disconnect(struct serio *serio) * it as an input device. */ -static void magellan_connect(struct serio *serio, struct serio_dev *dev) +static void magellan_connect(struct serio *serio, struct serio_driver *drv) { struct magellan *magellan; int i, t; @@ -184,7 +186,7 @@ static void magellan_connect(struct serio *serio, struct serio_dev *dev) serio->private = magellan; - if (serio_open(serio, dev)) { + if (serio_open(serio, drv)) { kfree(magellan); return; } @@ -199,10 +201,14 @@ static void magellan_connect(struct serio *serio, struct serio_dev *dev) * The serio device structure. */ -static struct serio_dev magellan_dev = { - .interrupt = magellan_interrupt, - .connect = magellan_connect, - .disconnect = magellan_disconnect, +static struct serio_driver magellan_drv = { + .driver = { + .name = "magellan", + }, + .description = DRIVER_DESC, + .interrupt = magellan_interrupt, + .connect = magellan_connect, + .disconnect = magellan_disconnect, }; /* @@ -211,13 +217,13 @@ static struct serio_dev magellan_dev = { int __init magellan_init(void) { - serio_register_device(&magellan_dev); + serio_register_driver(&magellan_drv); return 0; } void __exit magellan_exit(void) { - serio_unregister_device(&magellan_dev); + serio_unregister_driver(&magellan_drv); } module_init(magellan_init); diff --git a/drivers/input/joystick/spaceball.c b/drivers/input/joystick/spaceball.c index eb5b6f76c..40412528e 100644 --- a/drivers/input/joystick/spaceball.c +++ b/drivers/input/joystick/spaceball.c @@ -39,8 +39,10 @@ #include #include +#define DRIVER_DESC "SpaceTec SpaceBall 2003/3003/4000 FLX driver" + MODULE_AUTHOR("Vojtech Pavlik "); -MODULE_DESCRIPTION("SpaceTec SpaceBall 2003/3003/4000 FLX driver"); +MODULE_DESCRIPTION(DRIVER_DESC); MODULE_LICENSE("GPL"); /* @@ -201,7 +203,7 @@ static void spaceball_disconnect(struct serio *serio) * it as an input device. */ -static void spaceball_connect(struct serio *serio, struct serio_dev *dev) +static void spaceball_connect(struct serio *serio, struct serio_driver *drv) { struct spaceball *spaceball; int i, t, id; @@ -254,7 +256,7 @@ static void spaceball_connect(struct serio *serio, struct serio_dev *dev) serio->private = spaceball; - if (serio_open(serio, dev)) { + if (serio_open(serio, drv)) { kfree(spaceball); return; } @@ -269,10 +271,14 @@ static void spaceball_connect(struct serio *serio, struct serio_dev *dev) * The serio device structure. */ -static struct serio_dev spaceball_dev = { - .interrupt = spaceball_interrupt, - .connect = spaceball_connect, - .disconnect = spaceball_disconnect, +static struct serio_driver spaceball_drv = { + .driver = { + .name = "spaceball", + }, + .description = DRIVER_DESC, + .interrupt = spaceball_interrupt, + .connect = spaceball_connect, + .disconnect = spaceball_disconnect, }; /* @@ -281,13 +287,13 @@ static struct serio_dev spaceball_dev = { int __init spaceball_init(void) { - serio_register_device(&spaceball_dev); + serio_register_driver(&spaceball_drv); return 0; } void __exit spaceball_exit(void) { - serio_unregister_device(&spaceball_dev); + serio_unregister_driver(&spaceball_drv); } module_init(spaceball_init); diff --git a/drivers/input/joystick/spaceorb.c b/drivers/input/joystick/spaceorb.c index 00da66c4b..c20091a6e 100644 --- a/drivers/input/joystick/spaceorb.c +++ b/drivers/input/joystick/spaceorb.c @@ -38,8 +38,10 @@ #include #include +#define DRIVER_DESC "SpaceTec SpaceOrb 360 and Avenger 6dof controller driver" + MODULE_AUTHOR("Vojtech Pavlik "); -MODULE_DESCRIPTION("SpaceTec SpaceOrb 360 and Avenger 6dof controller driver"); +MODULE_DESCRIPTION(DRIVER_DESC); MODULE_LICENSE("GPL"); /* @@ -162,7 +164,7 @@ static void spaceorb_disconnect(struct serio *serio) * it as an input device. */ -static void spaceorb_connect(struct serio *serio, struct serio_dev *dev) +static void spaceorb_connect(struct serio *serio, struct serio_driver *drv) { struct spaceorb *spaceorb; int i, t; @@ -201,7 +203,7 @@ static void spaceorb_connect(struct serio *serio, struct serio_dev *dev) serio->private = spaceorb; - if (serio_open(serio, dev)) { + if (serio_open(serio, drv)) { kfree(spaceorb); return; } @@ -213,10 +215,14 @@ static void spaceorb_connect(struct serio *serio, struct serio_dev *dev) * The serio device structure. */ -static struct serio_dev spaceorb_dev = { - .interrupt = spaceorb_interrupt, - .connect = spaceorb_connect, - .disconnect = spaceorb_disconnect, +static struct serio_driver spaceorb_drv = { + .driver = { + .name = "spaceorb", + }, + .description = DRIVER_DESC, + .interrupt = spaceorb_interrupt, + .connect = spaceorb_connect, + .disconnect = spaceorb_disconnect, }; /* @@ -225,13 +231,13 @@ static struct serio_dev spaceorb_dev = { int __init spaceorb_init(void) { - serio_register_device(&spaceorb_dev); + serio_register_driver(&spaceorb_drv); return 0; } void __exit spaceorb_exit(void) { - serio_unregister_device(&spaceorb_dev); + serio_unregister_driver(&spaceorb_drv); } module_init(spaceorb_init); diff --git a/drivers/input/joystick/stinger.c b/drivers/input/joystick/stinger.c index ce9be4563..580e32375 100644 --- a/drivers/input/joystick/stinger.c +++ b/drivers/input/joystick/stinger.c @@ -36,8 +36,10 @@ #include #include +#define DRIVER_DESC "Gravis Stinger gamepad driver" + MODULE_AUTHOR("Vojtech Pavlik "); -MODULE_DESCRIPTION("Gravis Stinger gamepad driver"); +MODULE_DESCRIPTION(DRIVER_DESC); MODULE_LICENSE("GPL"); /* @@ -134,7 +136,7 @@ static void stinger_disconnect(struct serio *serio) * it as an input device. */ -static void stinger_connect(struct serio *serio, struct serio_dev *dev) +static void stinger_connect(struct serio *serio, struct serio_driver *drv) { struct stinger *stinger; int i; @@ -172,7 +174,7 @@ static void stinger_connect(struct serio *serio, struct serio_dev *dev) stinger->dev.private = stinger; serio->private = stinger; - if (serio_open(serio, dev)) { + if (serio_open(serio, drv)) { kfree(stinger); return; } @@ -187,10 +189,14 @@ static void stinger_connect(struct serio *serio, struct serio_dev *dev) * The serio device structure. */ -static struct serio_dev stinger_dev = { - .interrupt = stinger_interrupt, - .connect = stinger_connect, - .disconnect = stinger_disconnect, +static struct serio_driver stinger_drv = { + .driver = { + .name = "stinger", + }, + .description = DRIVER_DESC, + .interrupt = stinger_interrupt, + .connect = stinger_connect, + .disconnect = stinger_disconnect, }; /* @@ -199,13 +205,13 @@ static struct serio_dev stinger_dev = { int __init stinger_init(void) { - serio_register_device(&stinger_dev); + serio_register_driver(&stinger_drv); return 0; } void __exit stinger_exit(void) { - serio_unregister_device(&stinger_dev); + serio_unregister_driver(&stinger_drv); } module_init(stinger_init); diff --git a/drivers/input/joystick/tmdc.c b/drivers/input/joystick/tmdc.c index 6ed46ec46..e11d29148 100644 --- a/drivers/input/joystick/tmdc.c +++ b/drivers/input/joystick/tmdc.c @@ -322,7 +322,7 @@ static void tmdc_connect(struct gameport *gameport, struct gameport_dev *dev) tmdc->dev[j].evbit[0] = BIT(EV_KEY) | BIT(EV_ABS); for (i = 0; i < models[m].abs && i < TMDC_ABS; i++) { - if (tmdc->abs[i] < 0) continue; + if (tmdc->abs[j][i] < 0) continue; set_bit(tmdc->abs[j][i], tmdc->dev[j].absbit); tmdc->dev[j].absmin[tmdc->abs[j][i]] = 8; tmdc->dev[j].absmax[tmdc->abs[j][i]] = 248; diff --git a/drivers/input/joystick/twidjoy.c b/drivers/input/joystick/twidjoy.c index eb5c935a6..909b6b54a 100644 --- a/drivers/input/joystick/twidjoy.c +++ b/drivers/input/joystick/twidjoy.c @@ -187,7 +187,7 @@ static void twidjoy_disconnect(struct serio *serio) * it as an input device. */ -static void twidjoy_connect(struct serio *serio, struct serio_dev *dev) +static void twidjoy_connect(struct serio *serio, struct serio_driver *drv) { struct twidjoy_button_spec *bp; struct twidjoy *twidjoy; @@ -232,7 +232,7 @@ static void twidjoy_connect(struct serio *serio, struct serio_dev *dev) twidjoy->dev.private = twidjoy; serio->private = twidjoy; - if (serio_open(serio, dev)) { + if (serio_open(serio, drv)) { kfree(twidjoy); return; } @@ -246,10 +246,14 @@ static void twidjoy_connect(struct serio *serio, struct serio_dev *dev) * The serio device structure. */ -static struct serio_dev twidjoy_dev = { - .interrupt = twidjoy_interrupt, - .connect = twidjoy_connect, - .disconnect = twidjoy_disconnect, +static struct serio_driver twidjoy_drv = { + .driver = { + .name = "twidjoy", + }, + .description = DRIVER_DESC, + .interrupt = twidjoy_interrupt, + .connect = twidjoy_connect, + .disconnect = twidjoy_disconnect, }; /* @@ -258,13 +262,13 @@ static struct serio_dev twidjoy_dev = { int __init twidjoy_init(void) { - serio_register_device(&twidjoy_dev); + serio_register_driver(&twidjoy_drv); return 0; } void __exit twidjoy_exit(void) { - serio_unregister_device(&twidjoy_dev); + serio_unregister_driver(&twidjoy_drv); } module_init(twidjoy_init); diff --git a/drivers/input/joystick/warrior.c b/drivers/input/joystick/warrior.c index 32ec90e81..5f19b2a51 100644 --- a/drivers/input/joystick/warrior.c +++ b/drivers/input/joystick/warrior.c @@ -35,8 +35,10 @@ #include #include +#define DRIVER_DESC "Logitech WingMan Warrior joystick driver" + MODULE_AUTHOR("Vojtech Pavlik "); -MODULE_DESCRIPTION("Logitech WingMan Warrior joystick driver"); +MODULE_DESCRIPTION(DRIVER_DESC); MODULE_LICENSE("GPL"); /* @@ -139,7 +141,7 @@ static void warrior_disconnect(struct serio *serio) * it as an input device. */ -static void warrior_connect(struct serio *serio, struct serio_dev *dev) +static void warrior_connect(struct serio *serio, struct serio_driver *drv) { struct warrior *warrior; int i; @@ -185,7 +187,7 @@ static void warrior_connect(struct serio *serio, struct serio_dev *dev) serio->private = warrior; - if (serio_open(serio, dev)) { + if (serio_open(serio, drv)) { kfree(warrior); return; } @@ -199,10 +201,14 @@ static void warrior_connect(struct serio *serio, struct serio_dev *dev) * The serio device structure. */ -static struct serio_dev warrior_dev = { - .interrupt = warrior_interrupt, - .connect = warrior_connect, - .disconnect = warrior_disconnect, +static struct serio_driver warrior_drv = { + .driver = { + .name = "warrior", + }, + .description = DRIVER_DESC, + .interrupt = warrior_interrupt, + .connect = warrior_connect, + .disconnect = warrior_disconnect, }; /* @@ -211,13 +217,13 @@ static struct serio_dev warrior_dev = { int __init warrior_init(void) { - serio_register_device(&warrior_dev); + serio_register_driver(&warrior_drv); return 0; } void __exit warrior_exit(void) { - serio_unregister_device(&warrior_dev); + serio_unregister_driver(&warrior_drv); } module_init(warrior_init); diff --git a/drivers/input/keyboard/lkkbd.c b/drivers/input/keyboard/lkkbd.c index 8dc51e296..a04cf3efd 100644 --- a/drivers/input/keyboard/lkkbd.c +++ b/drivers/input/keyboard/lkkbd.c @@ -76,8 +76,10 @@ #include #include +#define DRIVER_DESC "LK keyboard driver" + MODULE_AUTHOR ("Jan-Benedict Glaw "); -MODULE_DESCRIPTION ("LK keyboard driver"); +MODULE_DESCRIPTION (DRIVER_DESC); MODULE_LICENSE ("GPL"); /* @@ -622,7 +624,7 @@ lkkbd_reinit (void *data) * lkkbd_connect() probes for a LK keyboard and fills the necessary structures. */ static void -lkkbd_connect (struct serio *serio, struct serio_dev *dev) +lkkbd_connect (struct serio *serio, struct serio_driver *drv) { struct lkkbd *lk; int i; @@ -665,7 +667,7 @@ lkkbd_connect (struct serio *serio, struct serio_dev *dev) serio->private = lk; - if (serio_open (serio, dev)) { + if (serio_open (serio, drv)) { kfree (lk); return; } @@ -703,10 +705,14 @@ lkkbd_disconnect (struct serio *serio) kfree (lk); } -static struct serio_dev lkkbd_dev = { - .connect = lkkbd_connect, - .disconnect = lkkbd_disconnect, - .interrupt = lkkbd_interrupt, +static struct serio_driver lkkbd_drv = { + .driver = { + .name = "lkkbd", + }, + .description = DRIVER_DESC, + .connect = lkkbd_connect, + .disconnect = lkkbd_disconnect, + .interrupt = lkkbd_interrupt, }; /* @@ -715,14 +721,14 @@ static struct serio_dev lkkbd_dev = { int __init lkkbd_init (void) { - serio_register_device (&lkkbd_dev); + serio_register_driver(&lkkbd_drv); return 0; } void __exit lkkbd_exit (void) { - serio_unregister_device (&lkkbd_dev); + serio_unregister_driver(&lkkbd_drv); } module_init (lkkbd_init); diff --git a/drivers/input/keyboard/newtonkbd.c b/drivers/input/keyboard/newtonkbd.c index dc47acb0a..caa92dd8b 100644 --- a/drivers/input/keyboard/newtonkbd.c +++ b/drivers/input/keyboard/newtonkbd.c @@ -32,8 +32,10 @@ #include #include +#define DRIVER_DESC "Newton keyboard driver" + MODULE_AUTHOR("Justin Cormack "); -MODULE_DESCRIPTION("Newton keyboard driver"); +MODULE_DESCRIPTION(DRIVER_DESC); MODULE_LICENSE("GPL"); #define NKBD_KEY 0x7f @@ -82,7 +84,7 @@ irqreturn_t nkbd_interrupt(struct serio *serio, } -void nkbd_connect(struct serio *serio, struct serio_dev *dev) +void nkbd_connect(struct serio *serio, struct serio_driver *drv) { struct nkbd *nkbd; int i; @@ -106,7 +108,7 @@ void nkbd_connect(struct serio *serio, struct serio_dev *dev) nkbd->dev.private = nkbd; serio->private = nkbd; - if (serio_open(serio, dev)) { + if (serio_open(serio, drv)) { kfree(nkbd); return; } @@ -138,21 +140,25 @@ void nkbd_disconnect(struct serio *serio) kfree(nkbd); } -struct serio_dev nkbd_dev = { - .interrupt = nkbd_interrupt, - .connect = nkbd_connect, - .disconnect = nkbd_disconnect +struct serio_driver nkbd_drv = { + .driver = { + .name = "newtonkbd", + }, + .description = DRIVER_DESC, + .interrupt = nkbd_interrupt, + .connect = nkbd_connect, + .disconnect = nkbd_disconnect, }; int __init nkbd_init(void) { - serio_register_device(&nkbd_dev); + serio_register_driver(&nkbd_drv); return 0; } void __exit nkbd_exit(void) { - serio_unregister_device(&nkbd_dev); + serio_unregister_driver(&nkbd_drv); } module_init(nkbd_init); diff --git a/drivers/input/keyboard/sunkbd.c b/drivers/input/keyboard/sunkbd.c index c281e726b..7f0342c56 100644 --- a/drivers/input/keyboard/sunkbd.c +++ b/drivers/input/keyboard/sunkbd.c @@ -37,8 +37,10 @@ #include #include +#define DRIVER_DESC "Sun keyboard driver" + MODULE_AUTHOR("Vojtech Pavlik "); -MODULE_DESCRIPTION("Sun keyboard driver"); +MODULE_DESCRIPTION(DRIVER_DESC); MODULE_LICENSE("GPL"); static unsigned char sunkbd_keycode[128] = { @@ -221,7 +223,7 @@ static void sunkbd_reinit(void *data) * sunkbd_connect() probes for a Sun keyboard and fills the necessary structures. */ -static void sunkbd_connect(struct serio *serio, struct serio_dev *dev) +static void sunkbd_connect(struct serio *serio, struct serio_driver *drv) { struct sunkbd *sunkbd; int i; @@ -257,7 +259,7 @@ static void sunkbd_connect(struct serio *serio, struct serio_dev *dev) serio->private = sunkbd; - if (serio_open(serio, dev)) { + if (serio_open(serio, drv)) { kfree(sunkbd); return; } @@ -301,10 +303,14 @@ static void sunkbd_disconnect(struct serio *serio) kfree(sunkbd); } -static struct serio_dev sunkbd_dev = { - .interrupt = sunkbd_interrupt, - .connect = sunkbd_connect, - .disconnect = sunkbd_disconnect +static struct serio_driver sunkbd_drv = { + .driver = { + .name = "sunkbd", + }, + .description = DRIVER_DESC, + .interrupt = sunkbd_interrupt, + .connect = sunkbd_connect, + .disconnect = sunkbd_disconnect, }; /* @@ -313,13 +319,13 @@ static struct serio_dev sunkbd_dev = { int __init sunkbd_init(void) { - serio_register_device(&sunkbd_dev); + serio_register_driver(&sunkbd_drv); return 0; } void __exit sunkbd_exit(void) { - serio_unregister_device(&sunkbd_dev); + serio_unregister_driver(&sunkbd_drv); } module_init(sunkbd_init); diff --git a/drivers/input/keyboard/xtkbd.c b/drivers/input/keyboard/xtkbd.c index 56adfa1cc..eb8a9b72b 100644 --- a/drivers/input/keyboard/xtkbd.c +++ b/drivers/input/keyboard/xtkbd.c @@ -34,8 +34,10 @@ #include #include +#define DRIVER_DESC "XT keyboard driver" + MODULE_AUTHOR("Vojtech Pavlik "); -MODULE_DESCRIPTION("XT keyboard driver"); +MODULE_DESCRIPTION(DRIVER_DESC); MODULE_LICENSE("GPL"); #define XTKBD_EMUL0 0xe0 @@ -86,7 +88,7 @@ irqreturn_t xtkbd_interrupt(struct serio *serio, return IRQ_HANDLED; } -void xtkbd_connect(struct serio *serio, struct serio_dev *dev) +void xtkbd_connect(struct serio *serio, struct serio_driver *drv) { struct xtkbd *xtkbd; int i; @@ -111,7 +113,7 @@ void xtkbd_connect(struct serio *serio, struct serio_dev *dev) serio->private = xtkbd; - if (serio_open(serio, dev)) { + if (serio_open(serio, drv)) { kfree(xtkbd); return; } @@ -143,21 +145,25 @@ void xtkbd_disconnect(struct serio *serio) kfree(xtkbd); } -struct serio_dev xtkbd_dev = { - .interrupt = xtkbd_interrupt, - .connect = xtkbd_connect, - .disconnect = xtkbd_disconnect +struct serio_driver xtkbd_drv = { + .driver = { + .name = "xtkbd", + }, + .description = DRIVER_DESC, + .interrupt = xtkbd_interrupt, + .connect = xtkbd_connect, + .disconnect = xtkbd_disconnect, }; int __init xtkbd_init(void) { - serio_register_device(&xtkbd_dev); + serio_register_driver(&xtkbd_drv); return 0; } void __exit xtkbd_exit(void) { - serio_unregister_device(&xtkbd_dev); + serio_unregister_driver(&xtkbd_drv); } module_init(xtkbd_init); diff --git a/drivers/input/mouse/logips2pp.c b/drivers/input/mouse/logips2pp.c index 0063af037..6af1455fd 100644 --- a/drivers/input/mouse/logips2pp.c +++ b/drivers/input/mouse/logips2pp.c @@ -277,7 +277,7 @@ int ps2pp_init(struct psmouse *psmouse, int set_properties) protocol = PSMOUSE_PS2TPP; } - } else if (get_model_info(model) != NULL) { + } else if (model_info != NULL) { param[0] = param[1] = param[2] = 0; ps2pp_cmd(psmouse, param, 0x39); /* Magic knock */ diff --git a/drivers/input/mouse/pc110pad.c b/drivers/input/mouse/pc110pad.c index 526a11e70..fa8cff3df 100644 --- a/drivers/input/mouse/pc110pad.c +++ b/drivers/input/mouse/pc110pad.c @@ -109,9 +109,9 @@ static int pc110pad_open(struct input_dev *dev) static int __init pc110pad_init(void) { - if (request_region(pc110pad_io, 4, "pc110pad")) - { - printk(KERN_ERR "pc110pad: I/O area %#x-%#x in use.\n", pc110pad_io, pc110pad_io + 4); + if (!request_region(pc110pad_io, 4, "pc110pad")) { + printk(KERN_ERR "pc110pad: I/O area %#x-%#x in use.\n", + pc110pad_io, pc110pad_io + 4); return -EBUSY; } diff --git a/drivers/input/mouse/psmouse-base.c b/drivers/input/mouse/psmouse-base.c index 0b3826724..7fbd926dd 100644 --- a/drivers/input/mouse/psmouse-base.c +++ b/drivers/input/mouse/psmouse-base.c @@ -22,8 +22,10 @@ #include "synaptics.h" #include "logips2pp.h" +#define DRIVER_DESC "PS/2 mouse driver" + MODULE_AUTHOR("Vojtech Pavlik "); -MODULE_DESCRIPTION("PS/2 mouse driver"); +MODULE_DESCRIPTION(DRIVER_DESC); MODULE_LICENSE("GPL"); static char *psmouse_proto; @@ -142,37 +144,67 @@ static irqreturn_t psmouse_interrupt(struct serio *serio, printk(KERN_WARNING "psmouse.c: bad data from KBC -%s%s\n", flags & SERIO_TIMEOUT ? " timeout" : "", flags & SERIO_PARITY ? " bad parity" : ""); - if (psmouse->acking) { - psmouse->ack = -1; - psmouse->acking = 0; - } - psmouse->pktcnt = 0; + psmouse->nak = 1; + clear_bit(PSMOUSE_FLAG_ACK, &psmouse->flags); + clear_bit(PSMOUSE_FLAG_CMD, &psmouse->flags); + wake_up_interruptible(&psmouse->wait); goto out; } - if (psmouse->acking) { + if (test_bit(PSMOUSE_FLAG_ACK, &psmouse->flags)) { switch (data) { case PSMOUSE_RET_ACK: - psmouse->ack = 1; + psmouse->nak = 0; break; + case PSMOUSE_RET_NAK: - psmouse->ack = -1; + psmouse->nak = 1; break; + + /* + * Workaround for mice which don't ACK the Get ID command. + * These are valid mouse IDs that we recognize. + */ + case 0x00: + case 0x03: + case 0x04: + if (test_bit(PSMOUSE_FLAG_WAITID, &psmouse->flags)) { + psmouse->nak = 0; + break; + } + /* Fall through */ default: - psmouse->ack = 1; /* Workaround for mice which don't ACK the Get ID command */ - if (psmouse->cmdcnt) - psmouse->cmdbuf[--psmouse->cmdcnt] = data; - break; + goto out; } - psmouse->acking = 0; - goto out; + + if (!psmouse->nak && psmouse->cmdcnt) { + set_bit(PSMOUSE_FLAG_CMD, &psmouse->flags); + set_bit(PSMOUSE_FLAG_CMD1, &psmouse->flags); + } + clear_bit(PSMOUSE_FLAG_ACK, &psmouse->flags); + wake_up_interruptible(&psmouse->wait); + + if (data == PSMOUSE_RET_ACK || data == PSMOUSE_RET_NAK) + goto out; } - if (psmouse->cmdcnt) { - psmouse->cmdbuf[--psmouse->cmdcnt] = data; + if (test_bit(PSMOUSE_FLAG_CMD, &psmouse->flags)) { + if (psmouse->cmdcnt) + psmouse->cmdbuf[--psmouse->cmdcnt] = data; + + if (test_and_clear_bit(PSMOUSE_FLAG_CMD1, &psmouse->flags) && psmouse->cmdcnt) + wake_up_interruptible(&psmouse->wait); + + if (!psmouse->cmdcnt) { + clear_bit(PSMOUSE_FLAG_CMD, &psmouse->flags); + wake_up_interruptible(&psmouse->wait); + } goto out; } + if (psmouse->state == PSMOUSE_INITIALIZING) + goto out; + if (psmouse->state == PSMOUSE_ACTIVATED && psmouse->pktcnt && time_after(jiffies, psmouse->last + HZ/2)) { printk(KERN_WARNING "psmouse.c: %s at %s lost synchronization, throwing %d bytes away.\n", @@ -238,78 +270,96 @@ out: * psmouse_sendbyte() sends a byte to the mouse, and waits for acknowledge. * It doesn't handle retransmission, though it could - because when there would * be need for retransmissions, the mouse has to be replaced anyway. + * + * psmouse_sendbyte() can only be called from a process context */ static int psmouse_sendbyte(struct psmouse *psmouse, unsigned char byte) { - int timeout = 10000; /* 100 msec */ - psmouse->ack = 0; - psmouse->acking = 1; - - if (serio_write(psmouse->serio, byte)) { - psmouse->acking = 0; - return -1; - } + psmouse->nak = 1; + set_bit(PSMOUSE_FLAG_ACK, &psmouse->flags); - while (!psmouse->ack && timeout--) udelay(10); + if (serio_write(psmouse->serio, byte) == 0) + wait_event_interruptible_timeout(psmouse->wait, + !test_bit(PSMOUSE_FLAG_ACK, &psmouse->flags), + msecs_to_jiffies(200)); - return -(psmouse->ack <= 0); + clear_bit(PSMOUSE_FLAG_ACK, &psmouse->flags); + return -psmouse->nak; } /* * psmouse_command() sends a command and its parameters to the mouse, * then waits for the response and puts it in the param array. + * + * psmouse_command() can only be called from a process context */ int psmouse_command(struct psmouse *psmouse, unsigned char *param, int command) { - int timeout = 500000; /* 500 msec */ + int timeout; int send = (command >> 12) & 0xf; int receive = (command >> 8) & 0xf; + int rc = -1; int i; - psmouse->cmdcnt = receive; + timeout = msecs_to_jiffies(command == PSMOUSE_CMD_RESET_BAT ? 4000 : 500); - if (command == PSMOUSE_CMD_RESET_BAT) - timeout = 4000000; /* 4 sec */ + clear_bit(PSMOUSE_FLAG_CMD, &psmouse->flags); + if (command == PSMOUSE_CMD_GETID) + set_bit(PSMOUSE_FLAG_WAITID, &psmouse->flags); - /* initialize cmdbuf with preset values from param */ - if (receive) - for (i = 0; i < receive; i++) - psmouse->cmdbuf[(receive - 1) - i] = param[i]; + if (receive && param) + for (i = 0; i < receive; i++) + psmouse->cmdbuf[(receive - 1) - i] = param[i]; + + psmouse->cmdcnt = receive; if (command & 0xff) if (psmouse_sendbyte(psmouse, command & 0xff)) - return (psmouse->cmdcnt = 0) - 1; + goto out; for (i = 0; i < send; i++) if (psmouse_sendbyte(psmouse, param[i])) - return (psmouse->cmdcnt = 0) - 1; - - while (psmouse->cmdcnt && timeout--) { - - if (psmouse->cmdcnt == 1 && command == PSMOUSE_CMD_RESET_BAT && - timeout > 100000) /* do not run in a endless loop */ - timeout = 100000; /* 1 sec */ + goto out; - if (psmouse->cmdcnt == 1 && command == PSMOUSE_CMD_GETID && - psmouse->cmdbuf[1] != 0xab && psmouse->cmdbuf[1] != 0xac) { + timeout = wait_event_interruptible_timeout(psmouse->wait, + !test_bit(PSMOUSE_FLAG_CMD1, &psmouse->flags), timeout); + + if (psmouse->cmdcnt && timeout > 0) { + if (command == PSMOUSE_CMD_RESET_BAT && jiffies_to_msecs(timeout) > 100) + timeout = msecs_to_jiffies(100); + + if (command == PSMOUSE_CMD_GETID && + psmouse->cmdbuf[receive - 1] != 0xab && psmouse->cmdbuf[receive - 1] != 0xac) { + /* + * Device behind the port is not a keyboard + * so we don't need to wait for the 2nd byte + * of ID response. + */ + clear_bit(PSMOUSE_FLAG_CMD, &psmouse->flags); psmouse->cmdcnt = 0; - break; } - udelay(1); + wait_event_interruptible_timeout(psmouse->wait, + !test_bit(PSMOUSE_FLAG_CMD, &psmouse->flags), timeout); } - for (i = 0; i < receive; i++) - param[i] = psmouse->cmdbuf[(receive - 1) - i]; + if (param) + for (i = 0; i < receive; i++) + param[i] = psmouse->cmdbuf[(receive - 1) - i]; - if (psmouse->cmdcnt) - return (psmouse->cmdcnt = 0) - 1; + if (psmouse->cmdcnt && (command != PSMOUSE_CMD_RESET_BAT || psmouse->cmdcnt != 1)) + goto out; - return 0; -} + rc = 0; +out: + clear_bit(PSMOUSE_FLAG_CMD, &psmouse->flags); + clear_bit(PSMOUSE_FLAG_CMD1, &psmouse->flags); + clear_bit(PSMOUSE_FLAG_WAITID, &psmouse->flags); + return rc; +} /* * psmouse_sliced_command() sends an extended PS/2 command to the mouse @@ -394,6 +444,8 @@ static int im_explorer_detect(struct psmouse *psmouse) { unsigned char param[2]; + intellimouse_detect(psmouse); + param[0] = 200; psmouse_command(psmouse, param, PSMOUSE_CMD_SETRATE); param[0] = 200; @@ -597,6 +649,21 @@ static void psmouse_initialize(struct psmouse *psmouse) psmouse_command(psmouse, param, PSMOUSE_CMD_SETSTREAM); } +/* + * psmouse_set_state() sets new psmouse state and resets all flags and + * counters while holding serio lock so fighting with interrupt handler + * is not a concern. + */ + +static void psmouse_set_state(struct psmouse *psmouse, enum psmouse_state new_state) +{ + serio_pause_rx(psmouse->serio); + psmouse->state = new_state; + psmouse->pktcnt = psmouse->cmdcnt = psmouse->out_of_sync = 0; + psmouse->flags = 0; + serio_continue_rx(psmouse->serio); +} + /* * psmouse_activate() enables the mouse so that we get motion reports from it. */ @@ -606,9 +673,24 @@ static void psmouse_activate(struct psmouse *psmouse) if (psmouse_command(psmouse, NULL, PSMOUSE_CMD_ENABLE)) printk(KERN_WARNING "psmouse.c: Failed to enable mouse on %s\n", psmouse->serio->phys); - psmouse->state = PSMOUSE_ACTIVATED; + psmouse_set_state(psmouse, PSMOUSE_ACTIVATED); +} + + +/* + * psmouse_deactivate() puts the mouse into poll mode so that we don't get motion + * reports from it unless we explicitely request it. + */ + +static void psmouse_deactivate(struct psmouse *psmouse) +{ + if (psmouse_command(psmouse, NULL, PSMOUSE_CMD_DISABLE)) + printk(KERN_WARNING "psmouse.c: Failed to deactivate mouse on %s\n", psmouse->serio->phys); + + psmouse_set_state(psmouse, PSMOUSE_CMD_MODE); } + /* * psmouse_cleanup() resets the mouse into power-on state. */ @@ -626,22 +708,21 @@ static void psmouse_cleanup(struct serio *serio) static void psmouse_disconnect(struct serio *serio) { - struct psmouse *psmouse = serio->private; + struct psmouse *psmouse, *parent; - psmouse->state = PSMOUSE_CMD_MODE; + psmouse = serio->private; + psmouse_set_state(psmouse, PSMOUSE_CMD_MODE); - if (psmouse->ptport) { - if (psmouse->ptport->deactivate) - psmouse->ptport->deactivate(psmouse); - __serio_unregister_port(&psmouse->ptport->serio); /* we have serio_sem */ - kfree(psmouse->ptport); - psmouse->ptport = NULL; + if (serio->parent && (serio->type & SERIO_TYPE) == SERIO_PS_PSTHRU) { + parent = serio->parent->private; + if (parent->pt_deactivate) + parent->pt_deactivate(parent); } if (psmouse->disconnect) psmouse->disconnect(psmouse); - psmouse->state = PSMOUSE_IGNORE; + psmouse_set_state(psmouse, PSMOUSE_IGNORE); input_unregister_device(&psmouse->dev); serio_close(serio); @@ -652,39 +733,49 @@ static void psmouse_disconnect(struct serio *serio) * psmouse_connect() is a callback from the serio module when * an unhandled serio port is found. */ -static void psmouse_connect(struct serio *serio, struct serio_dev *dev) +static void psmouse_connect(struct serio *serio, struct serio_driver *drv) { - struct psmouse *psmouse; + struct psmouse *psmouse, *parent = NULL; if ((serio->type & SERIO_TYPE) != SERIO_8042 && (serio->type & SERIO_TYPE) != SERIO_PS_PSTHRU) return; + /* + * If this is a pass-through port deactivate parent so the device + * connected to this port can be successfully identified + */ + if (serio->parent && (serio->type & SERIO_TYPE) == SERIO_PS_PSTHRU) { + parent = serio->parent->private; + psmouse_deactivate(parent); + } + if (!(psmouse = kmalloc(sizeof(struct psmouse), GFP_KERNEL))) - return; + goto out; memset(psmouse, 0, sizeof(struct psmouse)); + init_waitqueue_head(&psmouse->wait); init_input_dev(&psmouse->dev); psmouse->dev.evbit[0] = BIT(EV_KEY) | BIT(EV_REL); psmouse->dev.keybit[LONG(BTN_MOUSE)] = BIT(BTN_LEFT) | BIT(BTN_MIDDLE) | BIT(BTN_RIGHT); psmouse->dev.relbit[0] = BIT(REL_X) | BIT(REL_Y); - psmouse->state = PSMOUSE_CMD_MODE; psmouse->serio = serio; psmouse->dev.private = psmouse; + psmouse_set_state(psmouse, PSMOUSE_INITIALIZING); serio->private = psmouse; - if (serio_open(serio, dev)) { + if (serio_open(serio, drv)) { kfree(psmouse); serio->private = NULL; - return; + goto out; } if (psmouse_probe(psmouse) < 0) { serio_close(serio); kfree(psmouse); serio->private = NULL; - return; + goto out; } psmouse->type = psmouse_extensions(psmouse, psmouse_max_proto, 1); @@ -711,63 +802,88 @@ static void psmouse_connect(struct serio *serio, struct serio_dev *dev) printk(KERN_INFO "input: %s on %s\n", psmouse->devname, serio->phys); + psmouse_set_state(psmouse, PSMOUSE_CMD_MODE); + psmouse_initialize(psmouse); - if (psmouse->ptport) { - printk(KERN_INFO "serio: %s port at %s\n", psmouse->ptport->serio.name, psmouse->phys); - __serio_register_port(&psmouse->ptport->serio); /* we have serio_sem */ - if (psmouse->ptport->activate) - psmouse->ptport->activate(psmouse); + if (parent && parent->pt_activate) + parent->pt_activate(parent); + + if (serio->child) { + /* + * Nothing to be done here, serio core will detect that + * the driver set serio->child and will register it for us. + */ + printk(KERN_INFO "serio: %s port at %s\n", serio->child->name, psmouse->phys); } psmouse_activate(psmouse); + +out: + /* If this is a pass-through port the parent awaits to be activated */ + if (parent) + psmouse_activate(parent); } static int psmouse_reconnect(struct serio *serio) { struct psmouse *psmouse = serio->private; - struct serio_dev *dev = serio->dev; + struct psmouse *parent = NULL; + struct serio_driver *drv = serio->drv; + int rc = -1; - if (!dev || !psmouse) { + if (!drv || !psmouse) { printk(KERN_DEBUG "psmouse: reconnect request, but serio is disconnected, ignoring...\n"); return -1; } - psmouse->state = PSMOUSE_CMD_MODE; - psmouse->acking = psmouse->cmdcnt = psmouse->pktcnt = psmouse->out_of_sync = 0; + if (serio->parent && (serio->type & SERIO_TYPE) == SERIO_PS_PSTHRU) { + parent = serio->parent->private; + psmouse_deactivate(parent); + } + + psmouse_set_state(psmouse, PSMOUSE_INITIALIZING); + if (psmouse->reconnect) { if (psmouse->reconnect(psmouse)) - return -1; + goto out; } else if (psmouse_probe(psmouse) < 0 || psmouse->type != psmouse_extensions(psmouse, psmouse_max_proto, 0)) - return -1; + goto out; /* ok, the device type (and capabilities) match the old one, * we can continue using it, complete intialization */ + psmouse_set_state(psmouse, PSMOUSE_CMD_MODE); + psmouse_initialize(psmouse); - if (psmouse->ptport) { - if (psmouse_reconnect(&psmouse->ptport->serio)) { - __serio_unregister_port(&psmouse->ptport->serio); - __serio_register_port(&psmouse->ptport->serio); - if (psmouse->ptport->activate) - psmouse->ptport->activate(psmouse); - } - } + if (parent && parent->pt_activate) + parent->pt_activate(parent); psmouse_activate(psmouse); - return 0; + rc = 0; + +out: + /* If this is a pass-through port the parent waits to be activated */ + if (parent) + psmouse_activate(parent); + + return rc; } -static struct serio_dev psmouse_dev = { - .interrupt = psmouse_interrupt, - .connect = psmouse_connect, - .reconnect = psmouse_reconnect, - .disconnect = psmouse_disconnect, - .cleanup = psmouse_cleanup, +static struct serio_driver psmouse_drv = { + .driver = { + .name = "psmouse", + }, + .description = DRIVER_DESC, + .interrupt = psmouse_interrupt, + .connect = psmouse_connect, + .reconnect = psmouse_reconnect, + .disconnect = psmouse_disconnect, + .cleanup = psmouse_cleanup, }; static inline void psmouse_parse_proto(void) @@ -787,13 +903,13 @@ static inline void psmouse_parse_proto(void) int __init psmouse_init(void) { psmouse_parse_proto(); - serio_register_device(&psmouse_dev); + serio_register_driver(&psmouse_drv); return 0; } void __exit psmouse_exit(void) { - serio_unregister_device(&psmouse_dev); + serio_unregister_driver(&psmouse_drv); } module_init(psmouse_init); diff --git a/drivers/input/mouse/psmouse.h b/drivers/input/mouse/psmouse.h index 47fbc48f6..20bbab42d 100644 --- a/drivers/input/mouse/psmouse.h +++ b/drivers/input/mouse/psmouse.h @@ -9,6 +9,7 @@ #define PSMOUSE_CMD_GETID 0x02f2 #define PSMOUSE_CMD_SETRATE 0x10f3 #define PSMOUSE_CMD_ENABLE 0x00f4 +#define PSMOUSE_CMD_DISABLE 0x00f5 #define PSMOUSE_CMD_RESET_DIS 0x00f6 #define PSMOUSE_CMD_RESET_BAT 0x02ff @@ -17,10 +18,17 @@ #define PSMOUSE_RET_ACK 0xfa #define PSMOUSE_RET_NAK 0xfe -/* psmouse states */ -#define PSMOUSE_CMD_MODE 0 -#define PSMOUSE_ACTIVATED 1 -#define PSMOUSE_IGNORE 2 +#define PSMOUSE_FLAG_ACK 0 /* Waiting for ACK/NAK */ +#define PSMOUSE_FLAG_CMD 1 /* Waiting for command to finish */ +#define PSMOUSE_FLAG_CMD1 2 /* Waiting for the first byte of command response */ +#define PSMOUSE_FLAG_WAITID 3 /* Command execiting is GET ID */ + +enum psmouse_state { + PSMOUSE_IGNORE, + PSMOUSE_INITIALIZING, + PSMOUSE_CMD_MODE, + PSMOUSE_ACTIVATED, +}; /* psmouse protocol handler return codes */ typedef enum { @@ -29,20 +37,10 @@ typedef enum { PSMOUSE_FULL_PACKET } psmouse_ret_t; -struct psmouse; - -struct psmouse_ptport { - struct serio serio; - - void (*activate)(struct psmouse *parent); - void (*deactivate)(struct psmouse *parent); -}; - struct psmouse { void *private; struct input_dev dev; struct serio *serio; - struct psmouse_ptport *ptport; char *vendor; char *name; unsigned char cmdbuf[8]; @@ -53,16 +51,22 @@ struct psmouse { unsigned char model; unsigned long last; unsigned long out_of_sync; - unsigned char state; - char acking; - volatile char ack; + enum psmouse_state state; + unsigned char nak; char error; char devname[64]; char phys[32]; + unsigned long flags; - psmouse_ret_t (*protocol_handler)(struct psmouse *psmouse, struct pt_regs *regs); + /* Used to signal completion from interrupt handler */ + wait_queue_head_t wait; + + psmouse_ret_t (*protocol_handler)(struct psmouse *psmouse, struct pt_regs *regs); int (*reconnect)(struct psmouse *psmouse); void (*disconnect)(struct psmouse *psmouse); + + void (*pt_activate)(struct psmouse *psmouse); + void (*pt_deactivate)(struct psmouse *psmouse); }; #define PSMOUSE_PS2 1 diff --git a/drivers/input/mouse/sermouse.c b/drivers/input/mouse/sermouse.c index 9c97a0a40..e1db6e078 100644 --- a/drivers/input/mouse/sermouse.c +++ b/drivers/input/mouse/sermouse.c @@ -37,8 +37,10 @@ #include #include +#define DRIVER_DESC "Serial mouse driver" + MODULE_AUTHOR("Vojtech Pavlik "); -MODULE_DESCRIPTION("Serial mouse driver"); +MODULE_DESCRIPTION(DRIVER_DESC); MODULE_LICENSE("GPL"); static char *sermouse_protocols[] = { "None", "Mouse Systems Mouse", "Sun Mouse", "Microsoft Mouse", @@ -237,7 +239,7 @@ static void sermouse_disconnect(struct serio *serio) * an unhandled serio port is found. */ -static void sermouse_connect(struct serio *serio, struct serio_dev *dev) +static void sermouse_connect(struct serio *serio, struct serio_driver *drv) { struct sermouse *sermouse; unsigned char c; @@ -279,7 +281,7 @@ static void sermouse_connect(struct serio *serio, struct serio_dev *dev) sermouse->dev.id.product = c; sermouse->dev.id.version = 0x0100; - if (serio_open(serio, dev)) { + if (serio_open(serio, drv)) { kfree(sermouse); return; } @@ -289,21 +291,25 @@ static void sermouse_connect(struct serio *serio, struct serio_dev *dev) printk(KERN_INFO "input: %s on %s\n", sermouse_protocols[sermouse->type], serio->phys); } -static struct serio_dev sermouse_dev = { - .interrupt = sermouse_interrupt, - .connect = sermouse_connect, - .disconnect = sermouse_disconnect +static struct serio_driver sermouse_drv = { + .driver = { + .name = "sermouse", + }, + .description = DRIVER_DESC, + .interrupt = sermouse_interrupt, + .connect = sermouse_connect, + .disconnect = sermouse_disconnect, }; int __init sermouse_init(void) { - serio_register_device(&sermouse_dev); + serio_register_driver(&sermouse_drv); return 0; } void __exit sermouse_exit(void) { - serio_unregister_device(&sermouse_dev); + serio_unregister_driver(&sermouse_drv); } module_init(sermouse_init); diff --git a/drivers/input/mouse/synaptics.c b/drivers/input/mouse/synaptics.c index 2bd152fe0..101b80947 100644 --- a/drivers/input/mouse/synaptics.c +++ b/drivers/input/mouse/synaptics.c @@ -212,9 +212,9 @@ static int synaptics_set_mode(struct psmouse *psmouse, int mode) /***************************************************************************** * Synaptics pass-through PS/2 port support ****************************************************************************/ -static int synaptics_pt_write(struct serio *port, unsigned char c) +static int synaptics_pt_write(struct serio *serio, unsigned char c) { - struct psmouse *parent = port->driver; + struct psmouse *parent = serio->parent->private; char rate_param = SYN_PS_CLIENT_CMD; /* indicates that we want pass-through port */ if (psmouse_sliced_command(parent, c)) @@ -233,22 +233,19 @@ static void synaptics_pass_pt_packet(struct serio *ptport, unsigned char *packet { struct psmouse *child = ptport->private; - if (child) { - if (child->state == PSMOUSE_ACTIVATED) { - serio_interrupt(ptport, packet[1], 0, NULL); - serio_interrupt(ptport, packet[4], 0, NULL); - serio_interrupt(ptport, packet[5], 0, NULL); - if (child->type >= PSMOUSE_GENPS) - serio_interrupt(ptport, packet[2], 0, NULL); - } else if (child->state != PSMOUSE_IGNORE) { - serio_interrupt(ptport, packet[1], 0, NULL); - } - } + if (child && child->state == PSMOUSE_ACTIVATED) { + serio_interrupt(ptport, packet[1], 0, NULL); + serio_interrupt(ptport, packet[4], 0, NULL); + serio_interrupt(ptport, packet[5], 0, NULL); + if (child->type >= PSMOUSE_GENPS) + serio_interrupt(ptport, packet[2], 0, NULL); + } else + serio_interrupt(ptport, packet[1], 0, NULL); } static void synaptics_pt_activate(struct psmouse *psmouse) { - struct psmouse *child = psmouse->ptport->serio.private; + struct psmouse *child = psmouse->serio->child->private; /* adjust the touchpad to child's choice of protocol */ if (child && child->type >= PSMOUSE_GENPS) { @@ -259,23 +256,25 @@ static void synaptics_pt_activate(struct psmouse *psmouse) static void synaptics_pt_create(struct psmouse *psmouse) { - struct psmouse_ptport *port; + struct serio *serio; - psmouse->ptport = port = kmalloc(sizeof(struct psmouse_ptport), GFP_KERNEL); - if (!port) { + serio = kmalloc(sizeof(struct serio), GFP_KERNEL); + if (!serio) { printk(KERN_ERR "synaptics: not enough memory to allocate pass-through port\n"); return; } - memset(port, 0, sizeof(struct psmouse_ptport)); + memset(serio, 0, sizeof(struct serio)); + + serio->type = SERIO_PS_PSTHRU; + strlcpy(serio->name, "Synaptics pass-through", sizeof(serio->name)); + strlcpy(serio->phys, "synaptics-pt/serio0", sizeof(serio->name)); + serio->write = synaptics_pt_write; + serio->parent = psmouse->serio; - port->serio.type = SERIO_PS_PSTHRU; - port->serio.name = "Synaptics pass-through"; - port->serio.phys = "synaptics-pt/serio0"; - port->serio.write = synaptics_pt_write; - port->serio.driver = psmouse; + psmouse->pt_activate = synaptics_pt_activate; - port->activate = synaptics_pt_activate; + psmouse->serio->child = serio; } /***************************************************************************** @@ -470,9 +469,10 @@ static psmouse_ret_t synaptics_process_byte(struct psmouse *psmouse, struct pt_r if (unlikely(priv->pkt_type == SYN_NEWABS)) priv->pkt_type = synaptics_detect_pkt_type(psmouse); - if (psmouse->ptport && psmouse->ptport->serio.dev && synaptics_is_pt_packet(psmouse->packet)) - synaptics_pass_pt_packet(&psmouse->ptport->serio, psmouse->packet); - else + if (SYN_CAP_PASS_THROUGH(priv->capabilities) && synaptics_is_pt_packet(psmouse->packet)) { + if (psmouse->serio->child) + synaptics_pass_pt_packet(psmouse->serio->child, psmouse->packet); + } else synaptics_process_packet(psmouse); return PSMOUSE_FULL_PACKET; diff --git a/drivers/input/mouse/vsxxxaa.c b/drivers/input/mouse/vsxxxaa.c index c26d907e8..3845ca458 100644 --- a/drivers/input/mouse/vsxxxaa.c +++ b/drivers/input/mouse/vsxxxaa.c @@ -82,8 +82,10 @@ #include #include +#define DRIVER_DESC "Serial DEC VSXXX-AA/GA mouse / DEC tablet driver" + MODULE_AUTHOR ("Jan-Benedict Glaw "); -MODULE_DESCRIPTION ("Serial DEC VSXXX-AA/GA mouse / DEC tablet driver"); +MODULE_DESCRIPTION (DRIVER_DESC); MODULE_LICENSE ("GPL"); #undef VSXXXAA_DEBUG @@ -482,7 +484,7 @@ vsxxxaa_disconnect (struct serio *serio) } static void -vsxxxaa_connect (struct serio *serio, struct serio_dev *dev) +vsxxxaa_connect (struct serio *serio, struct serio_driver *drv) { struct vsxxxaa *mouse; @@ -524,7 +526,7 @@ vsxxxaa_connect (struct serio *serio, struct serio_dev *dev) mouse->dev.id.bustype = BUS_RS232; mouse->serio = serio; - if (serio_open (serio, dev)) { + if (serio_open (serio, drv)) { kfree (mouse); return; } @@ -540,23 +542,27 @@ vsxxxaa_connect (struct serio *serio, struct serio_dev *dev) printk (KERN_INFO "input: %s on %s\n", mouse->name, mouse->phys); } -static struct serio_dev vsxxxaa_dev = { - .connect = vsxxxaa_connect, - .interrupt = vsxxxaa_interrupt, - .disconnect = vsxxxaa_disconnect, +static struct serio_driver vsxxxaa_drv = { + .driver = { + .name = "vsxxxaa", + }, + .description = DRIVER_DESC, + .connect = vsxxxaa_connect, + .interrupt = vsxxxaa_interrupt, + .disconnect = vsxxxaa_disconnect, }; int __init vsxxxaa_init (void) { - serio_register_device (&vsxxxaa_dev); + serio_register_driver(&vsxxxaa_drv); return 0; } void __exit vsxxxaa_exit (void) { - serio_unregister_device (&vsxxxaa_dev); + serio_unregister_driver(&vsxxxaa_drv); } module_init (vsxxxaa_init); diff --git a/drivers/input/serio/Makefile b/drivers/input/serio/Makefile index a47dec255..cf52b6141 100644 --- a/drivers/input/serio/Makefile +++ b/drivers/input/serio/Makefile @@ -17,3 +17,4 @@ obj-$(CONFIG_SERIO_98KBD) += 98kbd-io.o obj-$(CONFIG_SERIO_GSCPS2) += gscps2.o obj-$(CONFIG_SERIO_PCIPS2) += pcips2.o obj-$(CONFIG_SERIO_MACEPS2) += maceps2.o +obj-$(CONFIG_SERIO_RAW) += serio_raw.o diff --git a/drivers/input/serio/ct82c710.c b/drivers/input/serio/ct82c710.c index 2234ff39d..ee785460b 100644 --- a/drivers/input/serio/ct82c710.c +++ b/drivers/input/serio/ct82c710.c @@ -36,6 +36,7 @@ #include #include #include +#include #include @@ -43,9 +44,6 @@ MODULE_AUTHOR("Vojtech Pavlik "); MODULE_DESCRIPTION("82C710 C&T mouse port chip driver"); MODULE_LICENSE("GPL"); -static char ct82c710_name[] = "C&T 82c710 mouse port"; -static char ct82c710_phys[16]; - /* * ct82c710 interface */ @@ -61,10 +59,22 @@ static char ct82c710_phys[16]; #define CT82C710_IRQ 12 -static int ct82c710_data; -static int ct82c710_status; +#define CT82C710_DATA ct82c710_iores.start +#define CT82C710_STATUS (ct82c710_iores.start + 1) + +static struct serio *ct82c710_port; +static struct platform_device *ct82c710_device; +static struct resource ct82c710_iores; + +/* + * Interrupt handler for the 82C710 mouse port. A character + * is waiting in the 82C710. + */ -static irqreturn_t ct82c710_interrupt(int cpl, void *dev_id, struct pt_regs * regs); +static irqreturn_t ct82c710_interrupt(int cpl, void *dev_id, struct pt_regs * regs) +{ + return serio_interrupt(ct82c710_port, inb(CT82C710_DATA), 0, regs); +} /* * Wait for device to send output char and flush any input char. @@ -74,10 +84,10 @@ static int ct82c170_wait(void) { int timeout = 60000; - while ((inb(ct82c710_status) & (CT82C710_RX_FULL | CT82C710_TX_IDLE | CT82C710_DEV_IDLE)) + while ((inb(CT82C710_STATUS) & (CT82C710_RX_FULL | CT82C710_TX_IDLE | CT82C710_DEV_IDLE)) != (CT82C710_DEV_IDLE | CT82C710_TX_IDLE) && timeout) { - if (inb_p(ct82c710_status) & CT82C710_RX_FULL) inb_p(ct82c710_data); + if (inb_p(CT82C710_STATUS) & CT82C710_RX_FULL) inb_p(CT82C710_DATA); udelay(1); timeout--; @@ -91,7 +101,7 @@ static void ct82c710_close(struct serio *serio) if (ct82c170_wait()) printk(KERN_WARNING "ct82c710.c: Device busy in close()\n"); - outb_p(inb_p(ct82c710_status) & ~(CT82C710_ENABLE | CT82C710_INTS_ON), ct82c710_status); + outb_p(inb_p(CT82C710_STATUS) & ~(CT82C710_ENABLE | CT82C710_INTS_ON), CT82C710_STATUS); if (ct82c170_wait()) printk(KERN_WARNING "ct82c710.c: Device busy in close()\n"); @@ -106,21 +116,21 @@ static int ct82c710_open(struct serio *serio) if (request_irq(CT82C710_IRQ, ct82c710_interrupt, 0, "ct82c710", NULL)) return -1; - status = inb_p(ct82c710_status); + status = inb_p(CT82C710_STATUS); status |= (CT82C710_ENABLE | CT82C710_RESET); - outb_p(status, ct82c710_status); + outb_p(status, CT82C710_STATUS); status &= ~(CT82C710_RESET); - outb_p(status, ct82c710_status); + outb_p(status, CT82C710_STATUS); status |= CT82C710_INTS_ON; - outb_p(status, ct82c710_status); /* Enable interrupts */ + outb_p(status, CT82C710_STATUS); /* Enable interrupts */ while (ct82c170_wait()) { printk(KERN_ERR "ct82c710: Device busy in open()\n"); status &= ~(CT82C710_ENABLE | CT82C710_INTS_ON); - outb_p(status, ct82c710_status); + outb_p(status, CT82C710_STATUS); free_irq(CT82C710_IRQ, NULL); return -1; } @@ -135,30 +145,10 @@ static int ct82c710_open(struct serio *serio) static int ct82c710_write(struct serio *port, unsigned char c) { if (ct82c170_wait()) return -1; - outb_p(c, ct82c710_data); + outb_p(c, CT82C710_DATA); return 0; } -static struct serio ct82c710_port = -{ - .type = SERIO_8042, - .name = ct82c710_name, - .phys = ct82c710_phys, - .write = ct82c710_write, - .open = ct82c710_open, - .close = ct82c710_close, -}; - -/* - * Interrupt handler for the 82C710 mouse port. A character - * is waiting in the 82C710. - */ - -static irqreturn_t ct82c710_interrupt(int cpl, void *dev_id, struct pt_regs * regs) -{ - return serio_interrupt(&ct82c710_port, inb(ct82c710_data), 0, regs); -} - /* * See if we can find a 82C710 device. Read mouse address. */ @@ -175,36 +165,60 @@ static int __init ct82c710_probe(void) return -1; /* No: no 82C710 here */ outb_p(0x0d, 0x390); /* Write index */ - ct82c710_data = inb_p(0x391) << 2; /* Get mouse I/O address */ - ct82c710_status = ct82c710_data + 1; + ct82c710_iores.start = inb_p(0x391) << 2; /* Get mouse I/O address */ + ct82c710_iores.end = ct82c710_iores.start + 1; + ct82c710_iores.flags = IORESOURCE_IO; outb_p(0x0f, 0x390); outb_p(0x0f, 0x391); /* Close config mode */ return 0; } +static struct serio * __init ct82c710_allocate_port(void) +{ + struct serio *serio; + + serio = kmalloc(sizeof(struct serio), GFP_KERNEL); + if (serio) { + memset(serio, 0, sizeof(struct serio)); + serio->type = SERIO_8042; + serio->open = ct82c710_open; + serio->close = ct82c710_close; + serio->write = ct82c710_write; + serio->dev.parent = &ct82c710_device->dev; + strlcpy(serio->name, "C&T 82c710 mouse port", sizeof(serio->name)); + snprintf(serio->phys, sizeof(serio->phys), "isa%04lx/serio0", CT82C710_DATA); + } + + return serio; +} + int __init ct82c710_init(void) { if (ct82c710_probe()) return -ENODEV; - if (request_region(ct82c710_data, 2, "ct82c710")) - return -EBUSY; + ct82c710_device = platform_device_register_simple("ct82c710", -1, &ct82c710_iores, 1); + if (IS_ERR(ct82c710_device)) + return PTR_ERR(ct82c710_device); - sprintf(ct82c710_phys, "isa%04x/serio0", ct82c710_data); + if (!(ct82c710_port = ct82c710_allocate_port())) { + platform_device_unregister(ct82c710_device); + return -ENOMEM; + } - serio_register_port(&ct82c710_port); + serio_register_port(ct82c710_port); - printk(KERN_INFO "serio: C&T 82c710 mouse port at %#x irq %d\n", - ct82c710_data, CT82C710_IRQ); + printk(KERN_INFO "serio: C&T 82c710 mouse port at %#lx irq %d\n", + CT82C710_DATA, CT82C710_IRQ); return 0; } void __exit ct82c710_exit(void) { - serio_unregister_port(&ct82c710_port); - release_region(ct82c710_data, 2); + serio_unregister_port(ct82c710_port); + platform_device_unregister(ct82c710_device); } module_init(ct82c710_init); diff --git a/drivers/input/serio/gscps2.c b/drivers/input/serio/gscps2.c index 48a0ad587..cd9f987fb 100644 --- a/drivers/input/serio/gscps2.c +++ b/drivers/input/serio/gscps2.c @@ -6,7 +6,7 @@ * Copyright (c) 2002 Thibaut Varene * * Pieces of code based on linux-2.4's hp_mouse.c & hp_keyb.c - * Copyright (c) 1999 Alex deVries + * Copyright (c) 1999 Alex deVries * Copyright (c) 1999-2000 Philipp Rumpf * Copyright (c) 2000 Xavier Debacker * Copyright (c) 2000-2001 Thomas Marteau @@ -91,7 +91,7 @@ static irqreturn_t gscps2_interrupt(int irq, void *dev, struct pt_regs *regs); struct gscps2port { struct list_head node; struct parisc_device *padev; - struct serio port; + struct serio *port; spinlock_t lock; char *addr; u8 act, append; /* position in buffer[] */ @@ -100,7 +100,6 @@ struct gscps2port { u8 str; } buffer[BUFFER_SIZE+1]; int id; - char name[32]; }; /* @@ -272,7 +271,7 @@ static irqreturn_t gscps2_interrupt(int irq, void *dev, struct pt_regs *regs) rxflags = ((status & GSC_STAT_TERR) ? SERIO_TIMEOUT : 0 ) | ((status & GSC_STAT_PERR) ? SERIO_PARITY : 0 ); - serio_interrupt(&ps2port->port, data, rxflags, regs); + serio_interrupt(ps2port->port, data, rxflags, regs); } /* while() */ @@ -288,7 +287,7 @@ static irqreturn_t gscps2_interrupt(int irq, void *dev, struct pt_regs *regs) static int gscps2_write(struct serio *port, unsigned char data) { - struct gscps2port *ps2port = port->driver; + struct gscps2port *ps2port = port->port_data; if (!gscps2_writeb_output(ps2port, data)) { printk(KERN_DEBUG PFX "sending byte %#x failed.\n", data); @@ -304,7 +303,7 @@ static int gscps2_write(struct serio *port, unsigned char data) static int gscps2_open(struct serio *port) { - struct gscps2port *ps2port = port->driver; + struct gscps2port *ps2port = port->port_data; gscps2_reset(ps2port); @@ -319,7 +318,7 @@ static int gscps2_open(struct serio *port) static void gscps2_close(struct serio *port) { - struct gscps2port *ps2port = port->driver; + struct gscps2port *ps2port = port->port_data; gscps2_enable(ps2port, DISABLE); } @@ -343,7 +342,8 @@ static struct serio gscps2_serio_port = static int __init gscps2_probe(struct parisc_device *dev) { - struct gscps2port *ps2port; + struct gscps2port *ps2port; + struct serio *serio; unsigned long hpa = dev->hpa; int ret; @@ -355,34 +355,45 @@ static int __init gscps2_probe(struct parisc_device *dev) hpa += GSC_DINO_OFFSET; ps2port = kmalloc(sizeof(struct gscps2port), GFP_KERNEL); - if (!ps2port) - return -ENOMEM; + serio = kmalloc(sizeof(struct serio), GFP_KERNEL); + if (!ps2port || !serio) { + ret = -ENOMEM; + goto fail_nomem; + } dev_set_drvdata(&dev->dev, ps2port); memset(ps2port, 0, sizeof(struct gscps2port)); + memset(serio, 0, sizeof(struct serio)); + ps2port->port = serio; ps2port->padev = dev; ps2port->addr = ioremap(hpa, GSC_STATUS + 4); spin_lock_init(&ps2port->lock); gscps2_reset(ps2port); - ps2port->id = readb(ps2port->addr+GSC_ID) & 0x0f; - snprintf(ps2port->name, sizeof(ps2port->name)-1, "%s %s", - gscps2_serio_port.name, - (ps2port->id == GSC_ID_KEYBOARD) ? "keyboard" : "mouse" ); - - memcpy(&ps2port->port, &gscps2_serio_port, sizeof(gscps2_serio_port)); - ps2port->port.driver = ps2port; - ps2port->port.name = ps2port->name; - ps2port->port.phys = dev->dev.bus_id; + ps2port->id = readb(ps2port->addr + GSC_ID) & 0x0f; + + snprintf(serio->name, sizeof(serio->name), "GSC PS/2 %s", + (ps2port->id == GSC_ID_KEYBOARD) ? "keyboard" : "mouse"); + strlcpy(serio->phys, dev->dev.bus_id, sizeof(serio->phys)); + serio->idbus = BUS_GSC; + serio->idvendor = PCI_VENDOR_ID_HP; + serio->idproduct = 0x0001; + serio->idversion = 0x0010; + serio->type = SERIO_8042; + serio->write = gscps2_write; + serio->open = gscps2_open; + serio->close = gscps2_close; + serio->port_data = ps2port; + serio->dev.parent = &dev->dev; list_add_tail(&ps2port->node, &ps2port_list); ret = -EBUSY; - if (request_irq(dev->irq, gscps2_interrupt, SA_SHIRQ, ps2port->name, ps2port)) + if (request_irq(dev->irq, gscps2_interrupt, SA_SHIRQ, ps2port->port->name, ps2port)) goto fail_miserably; - if ( (ps2port->id != GSC_ID_KEYBOARD) && (ps2port->id != GSC_ID_MOUSE) ) { + if (ps2port->id != GSC_ID_KEYBOARD && ps2port->id != GSC_ID_MOUSE) { printk(KERN_WARNING PFX "Unsupported PS/2 port at 0x%08lx (id=%d) ignored\n", hpa, ps2port->id); ret = -ENODEV; @@ -395,12 +406,12 @@ static int __init gscps2_probe(struct parisc_device *dev) #endif printk(KERN_INFO "serio: %s port at 0x%p irq %d @ %s\n", - ps2port->name, + ps2port->port->name, ps2port->addr, ps2port->padev->irq, - ps2port->port.phys); + ps2port->port->phys); - serio_register_port(&ps2port->port); + serio_register_port(ps2port->port); return 0; @@ -411,7 +422,10 @@ fail_miserably: list_del(&ps2port->node); iounmap(ps2port->addr); release_mem_region(dev->hpa, GSC_STATUS + 4); + +fail_nomem: kfree(ps2port); + kfree(serio); return ret; } @@ -424,7 +438,7 @@ static int __devexit gscps2_remove(struct parisc_device *dev) { struct gscps2port *ps2port = dev_get_drvdata(&dev->dev); - serio_unregister_port(&ps2port->port); + serio_unregister_port(ps2port->port); free_irq(dev->irq, ps2port); gscps2_flush(ps2port); list_del(&ps2port->node); diff --git a/drivers/input/serio/i8042-sparcio.h b/drivers/input/serio/i8042-sparcio.h index e0b8f3219..da2a19812 100644 --- a/drivers/input/serio/i8042-sparcio.h +++ b/drivers/input/serio/i8042-sparcio.h @@ -18,7 +18,7 @@ static int i8042_aux_irq = -1; #define I8042_AUX_PHYS_DESC "sparcps2/serio1" #define I8042_MUX_PHYS_DESC "sparcps2/serio%d" -static unsigned long kbd_iobase; +static void __iomem *kbd_iobase; #define I8042_COMMAND_REG (kbd_iobase + 0x64UL) #define I8042_DATA_REG (kbd_iobase + 0x60UL) @@ -64,7 +64,7 @@ static int i8042_platform_init(void) if (strncmp(prop, "SUNW,JavaStation-1", len) == 0) { /* Hardcoded values for MrCoffee. */ i8042_kbd_irq = i8042_aux_irq = 13 | 0x20; - kbd_iobase = (unsigned long) ioremap(0x71300060, 8); + kbd_iobase = ioremap(0x71300060, 8); if (!kbd_iobase) return -1; } else { @@ -85,7 +85,7 @@ static int i8042_platform_init(void) if (!strcmp(child->prom_name, OBP_PS2KBD_NAME1) || !strcmp(child->prom_name, OBP_PS2KBD_NAME2)) { i8042_kbd_irq = child->irqs[0]; - kbd_iobase = (unsigned long) + kbd_iobase = ioremap(child->resource[0].start, 8); } if (!strcmp(child->prom_name, OBP_PS2MS_NAME1) || @@ -109,7 +109,7 @@ static int i8042_platform_init(void) static inline void i8042_platform_exit(void) { #ifdef CONFIG_PCI - iounmap((void *)kbd_iobase); + iounmap(kbd_iobase); #endif } diff --git a/drivers/input/serio/i8042.h b/drivers/input/serio/i8042.h index f0f637483..cea24034d 100644 --- a/drivers/input/serio/i8042.h +++ b/drivers/input/serio/i8042.h @@ -103,6 +103,13 @@ #define I8042_BUFFER_SIZE 32 +/* + * Number of AUX ports on controllers supporting active multiplexing + * specification + */ + +#define I8042_NUM_MUX_PORTS 4 + /* * Debug. */ diff --git a/drivers/input/serio/maceps2.c b/drivers/input/serio/maceps2.c index c7db1de49..78f4d9b5a 100644 --- a/drivers/input/serio/maceps2.c +++ b/drivers/input/serio/maceps2.c @@ -17,6 +17,7 @@ #include #include #include +#include #include #include @@ -46,15 +47,18 @@ MODULE_LICENSE("GPL"); #define PS2_CONTROL_RX_CLOCK_ENABLE BIT(4) /* pause reception if set to 0 */ #define PS2_CONTROL_RESET BIT(5) /* reset */ - struct maceps2_data { struct mace_ps2port *port; int irq; }; +static struct maceps2_data port_data[2]; +static struct serio *maceps2_port[2]; +static struct platform_device *maceps2_device; + static int maceps2_write(struct serio *dev, unsigned char val) { - struct mace_ps2port *port = ((struct maceps2_data *)dev->driver)->port; + struct mace_ps2port *port = ((struct maceps2_data *)dev->port_data)->port; unsigned int timeout = MACE_PS2_TIMEOUT; do { @@ -68,11 +72,10 @@ static int maceps2_write(struct serio *dev, unsigned char val) return -1; } -static irqreturn_t maceps2_interrupt(int irq, void *dev_id, - struct pt_regs *regs) +static irqreturn_t maceps2_interrupt(int irq, void *dev_id, struct pt_regs *regs) { struct serio *dev = dev_id; - struct mace_ps2port *port = ((struct maceps2_data *)dev->driver)->port; + struct mace_ps2port *port = ((struct maceps2_data *)dev->port_data)->port; unsigned int byte; if (mace_read(port->status) & PS2_STATUS_RX_FULL) { @@ -85,7 +88,7 @@ static irqreturn_t maceps2_interrupt(int irq, void *dev_id, static int maceps2_open(struct serio *dev) { - struct maceps2_data *data = (struct maceps2_data *)dev->driver; + struct maceps2_data *data = (struct maceps2_data *)dev->port_data; if (request_irq(data->irq, maceps2_interrupt, 0, "PS/2 port", dev)) { printk(KERN_ERR "Could not allocate PS/2 IRQ\n"); @@ -106,7 +109,7 @@ static int maceps2_open(struct serio *dev) static void maceps2_close(struct serio *dev) { - struct maceps2_data *data = (struct maceps2_data *)dev->driver; + struct maceps2_data *data = (struct maceps2_data *)dev->port_data; mace_write(PS2_CONTROL_TX_CLOCK_DISABLE | PS2_CONTROL_RESET, data->port->control); @@ -114,46 +117,59 @@ static void maceps2_close(struct serio *dev) free_irq(data->irq, dev); } -static struct maceps2_data port0_data, port1_data; -static struct serio maceps2_port0 = +static struct serio * __init maceps2_allocate_port(int idx) { - .type = SERIO_8042, - .open = maceps2_open, - .close = maceps2_close, - .write = maceps2_write, - .name = "MACE PS/2 port0", - .phys = "mace/serio0", - .driver = &port0_data, -}; + struct serio *serio; + + serio = kmalloc(sizeof(struct serio), GFP_KERNEL); + if (serio) { + memset(serio, 0, sizeof(struct serio)); + serio->type = SERIO_8042; + serio->write = maceps2_write; + serio->open = maceps2_open; + serio->close = maceps2_close; + snprintf(serio->name, sizeof(serio->name), "MACE PS/2 port%d", idx); + snprintf(serio->phys, sizeof(serio->phys), "mace/serio%d", idx); + serio->port_data = &port_data[idx]; + serio->dev.parent = &maceps2_device->dev; + } + + return serio; +} -static struct serio maceps2_port1 = -{ - .type = SERIO_8042, - .open = maceps2_open, - .close = maceps2_close, - .write = maceps2_write, - .name = "MACE PS/2 port1", - .phys = "mace/serio1", - .driver = &port1_data, -}; static int __init maceps2_init(void) { - port0_data.port = &mace->perif.ps2.keyb; - port0_data.irq = MACEISA_KEYB_IRQ; - port1_data.port = &mace->perif.ps2.mouse; - port1_data.irq = MACEISA_MOUSE_IRQ; - serio_register_port(&maceps2_port0); - serio_register_port(&maceps2_port1); + maceps2_device = platform_device_register_simple("maceps2", -1, NULL, 0); + if (IS_ERR(maceps2_device)) + return PTR_ERR(maceps2_device); + + port_data[0].port = &mace->perif.ps2.keyb; + port_data[0].irq = MACEISA_KEYB_IRQ; + port_data[1].port = &mace->perif.ps2.mouse; + port_data[1].irq = MACEISA_MOUSE_IRQ; + + maceps2_port[0] = maceps2_allocate_port(0); + maceps2_port[1] = maceps2_allocate_port(1); + if (!maceps2_port[0] || !maceps2_port[1]) { + kfree(maceps2_port[0]); + kfree(maceps2_port[1]); + platform_device_unregister(maceps2_device); + return -ENOMEM; + } + + serio_register_port(maceps2_port[0]); + serio_register_port(maceps2_port[1]); return 0; } static void __exit maceps2_exit(void) { - serio_unregister_port(&maceps2_port0); - serio_unregister_port(&maceps2_port1); + serio_unregister_port(maceps2_port[0]); + serio_unregister_port(maceps2_port[1]); + platform_device_unregister(maceps2_device); } module_init(maceps2_init); diff --git a/drivers/input/serio/parkbd.c b/drivers/input/serio/parkbd.c index 5f25b21be..a859df676 100644 --- a/drivers/input/serio/parkbd.c +++ b/drivers/input/serio/parkbd.c @@ -53,9 +53,7 @@ static int parkbd_writing; static unsigned long parkbd_start; static struct pardevice *parkbd_dev; - -static char parkbd_name[] = "PARKBD AT/XT keyboard adapter"; -static char parkbd_phys[32]; +static struct serio *parkbd_port; static int parkbd_readlines(void) { @@ -86,13 +84,6 @@ static int parkbd_write(struct serio *port, unsigned char c) return 0; } -static struct serio parkbd_port = -{ - .write = parkbd_write, - .name = parkbd_name, - .phys = parkbd_phys, -}; - static void parkbd_interrupt(int irq, void *dev_id, struct pt_regs *regs) { @@ -125,7 +116,7 @@ static void parkbd_interrupt(int irq, void *dev_id, struct pt_regs *regs) parkbd_buffer |= (parkbd_readlines() >> 1) << parkbd_counter++; if (parkbd_counter == parkbd_mode + 10) - serio_interrupt(&parkbd_port, (parkbd_buffer >> (2 - parkbd_mode)) & 0xff, 0, regs); + serio_interrupt(parkbd_port, (parkbd_buffer >> (2 - parkbd_mode)) & 0xff, 0, regs); } parkbd_last = jiffies; @@ -163,16 +154,39 @@ static int parkbd_getport(void) return 0; } +static struct serio * __init parkbd_allocate_serio(void) +{ + struct serio *serio; + + serio = kmalloc(sizeof(struct serio), GFP_KERNEL); + if (serio) { + memset(serio, 0, sizeof(struct serio)); + serio->type = parkbd_mode; + serio->write = parkbd_write, + strlcpy(serio->name, "PARKBD AT/XT keyboard adapter", sizeof(serio->name)); + snprintf(serio->phys, sizeof(serio->phys), "%s/serio0", parkbd_dev->port->name); + } + + return serio; +} int __init parkbd_init(void) { - if (parkbd_getport()) return -1; - parkbd_writelines(3); - parkbd_port.type = parkbd_mode; + int err; + + err = parkbd_getport(); + if (err) + return err; - sprintf(parkbd_phys, "%s/serio0", parkbd_dev->port->name); + parkbd_port = parkbd_allocate_serio(); + if (!parkbd_port) { + parport_release(parkbd_dev); + return -ENOMEM; + } + + parkbd_writelines(3); - serio_register_port(&parkbd_port); + serio_register_port(parkbd_port); printk(KERN_INFO "serio: PARKBD %s adapter on %s\n", parkbd_mode ? "AT" : "XT", parkbd_dev->port->name); @@ -183,7 +197,7 @@ int __init parkbd_init(void) void __exit parkbd_exit(void) { parport_release(parkbd_dev); - serio_unregister_port(&parkbd_port); + serio_unregister_port(parkbd_port); parport_unregister_device(parkbd_dev); } diff --git a/drivers/input/serio/pcips2.c b/drivers/input/serio/pcips2.c index 2b4acac94..489749e7c 100644 --- a/drivers/input/serio/pcips2.c +++ b/drivers/input/serio/pcips2.c @@ -38,14 +38,14 @@ #define PS2_STAT_TXEMPTY (1<<7) struct pcips2_data { - struct serio io; + struct serio *io; unsigned int base; struct pci_dev *dev; }; static int pcips2_write(struct serio *io, unsigned char val) { - struct pcips2_data *ps2if = io->driver; + struct pcips2_data *ps2if = io->port_data; unsigned int stat; do { @@ -80,7 +80,7 @@ static irqreturn_t pcips2_interrupt(int irq, void *devid, struct pt_regs *regs) if (hweight8(scancode) & 1) flag ^= SERIO_PARITY; - serio_interrupt(&ps2if->io, scancode, flag, regs); + serio_interrupt(ps2if->io, scancode, flag, regs); } while (1); return IRQ_RETVAL(handled); } @@ -101,7 +101,7 @@ static void pcips2_flush_input(struct pcips2_data *ps2if) static int pcips2_open(struct serio *io) { - struct pcips2_data *ps2if = io->driver; + struct pcips2_data *ps2if = io->port_data; int ret, val = 0; outb(PS2_CTRL_ENABLE, ps2if->base); @@ -119,7 +119,7 @@ static int pcips2_open(struct serio *io) static void pcips2_close(struct serio *io) { - struct pcips2_data *ps2if = io->driver; + struct pcips2_data *ps2if = io->port_data; outb(0, ps2if->base); @@ -129,46 +129,51 @@ static void pcips2_close(struct serio *io) static int __devinit pcips2_probe(struct pci_dev *dev, const struct pci_device_id *id) { struct pcips2_data *ps2if; + struct serio *serio; int ret; ret = pci_enable_device(dev); if (ret) - return ret; + goto out; - if (!request_region(pci_resource_start(dev, 0), - pci_resource_len(dev, 0), "pcips2")) { - ret = -EBUSY; + ret = pci_request_regions(dev, "pcips2"); + if (ret) goto disable; - } ps2if = kmalloc(sizeof(struct pcips2_data), GFP_KERNEL); - if (!ps2if) { + serio = kmalloc(sizeof(struct serio), GFP_KERNEL); + if (!ps2if || !serio) { ret = -ENOMEM; goto release; } memset(ps2if, 0, sizeof(struct pcips2_data)); - - ps2if->io.type = SERIO_8042; - ps2if->io.write = pcips2_write; - ps2if->io.open = pcips2_open; - ps2if->io.close = pcips2_close; - ps2if->io.name = pci_name(dev); - ps2if->io.phys = dev->dev.bus_id; - ps2if->io.driver = ps2if; + memset(serio, 0, sizeof(struct serio)); + + serio->type = SERIO_8042; + serio->write = pcips2_write; + serio->open = pcips2_open; + serio->close = pcips2_close; + strlcpy(serio->name, pci_name(dev), sizeof(serio->name)); + strlcpy(serio->phys, dev->dev.bus_id, sizeof(serio->phys)); + serio->port_data = ps2if; + serio->dev.parent = &dev->dev; + ps2if->io = serio; ps2if->dev = dev; ps2if->base = pci_resource_start(dev, 0); pci_set_drvdata(dev, ps2if); - serio_register_port(&ps2if->io); + serio_register_port(ps2if->io); return 0; release: - release_region(pci_resource_start(dev, 0), - pci_resource_len(dev, 0)); + kfree(ps2if); + kfree(serio); + pci_release_regions(dev); disable: pci_disable_device(dev); + out: return ret; } @@ -176,11 +181,10 @@ static void __devexit pcips2_remove(struct pci_dev *dev) { struct pcips2_data *ps2if = pci_get_drvdata(dev); - serio_unregister_port(&ps2if->io); - release_region(pci_resource_start(dev, 0), - pci_resource_len(dev, 0)); + serio_unregister_port(ps2if->io); pci_set_drvdata(dev, NULL); kfree(ps2if); + pci_release_regions(dev); pci_disable_device(dev); } diff --git a/drivers/input/serio/q40kbd.c b/drivers/input/serio/q40kbd.c index 7f4a26287..7d5ecce92 100644 --- a/drivers/input/serio/q40kbd.c +++ b/drivers/input/serio/q40kbd.c @@ -35,6 +35,7 @@ #include #include #include +#include #include #include @@ -47,43 +48,106 @@ MODULE_AUTHOR("Vojtech Pavlik "); MODULE_DESCRIPTION("Q40 PS/2 keyboard controller driver"); MODULE_LICENSE("GPL"); -static struct serio q40kbd_port = -{ - .type = SERIO_8042, - .name = "Q40 kbd port", - .phys = "Q40", - .write = NULL, -}; - -static irqreturn_t q40kbd_interrupt(int irq, void *dev_id, - struct pt_regs *regs) +spinlock_t q40kbd_lock = SPIN_LOCK_UNLOCKED; +static struct serio *q40kbd_port; +static struct platform_device *q40kbd_device; + +static irqreturn_t q40kbd_interrupt(int irq, void *dev_id, struct pt_regs *regs) { + unsigned long flags; + + spin_lock_irqsave(&q40kbd_lock, flags); + if (Q40_IRQ_KEYB_MASK & master_inb(INTERRUPT_REG)) - serio_interrupt(&q40kbd_port, master_inb(KEYCODE_REG), 0, regs); + serio_interrupt(q40kbd_port, master_inb(KEYCODE_REG), 0, regs); master_outb(-1, KEYBOARD_UNLOCK_REG); + + spin_unlock_irqrestore(&q40kbd_lock, flags); + return IRQ_HANDLED; } -static int __init q40kbd_init(void) +/* + * q40kbd_flush() flushes all data that may be in the keyboard buffers + */ + +static void q40kbd_flush(void) +{ + int maxread = 100; + unsigned long flags; + + spin_lock_irqsave(&q40kbd_lock, flags); + + while (maxread-- && (Q40_IRQ_KEYB_MASK & master_inb(INTERRUPT_REG))) + master_inb(KEYCODE_REG); + + spin_unlock_irqrestore(&q40kbd_lock, flags); +} + +/* + * q40kbd_open() is called when a port is open by the higher layer. + * It allocates the interrupt and enables in in the chip. + */ + +static int q40kbd_open(struct serio *port) { - int maxread = 100; + q40kbd_flush(); + if (request_irq(Q40_IRQ_KEYBOARD, q40kbd_interrupt, 0, "q40kbd", NULL)) { + printk(KERN_ERR "q40kbd.c: Can't get irq %d.\n", Q40_IRQ_KEYBOARD); + return -1; + } + + /* off we go */ + master_outb(-1, KEYBOARD_UNLOCK_REG); + master_outb(1, KEY_IRQ_ENABLE_REG); + + return 0; +} + +static void q40kbd_close(struct serio *port) +{ + master_outb(0, KEY_IRQ_ENABLE_REG); + master_outb(-1, KEYBOARD_UNLOCK_REG); + free_irq(Q40_IRQ_KEYBOARD, NULL); + + q40kbd_flush(); +} + +static struct serio * __init q40kbd_allocate_port(void) +{ + struct serio *serio; + + serio = kmalloc(sizeof(struct serio), GFP_KERNEL); + if (serio) { + memset(serio, 0, sizeof(struct serio)); + serio->type = SERIO_8042; + serio->open = q40kbd_open; + serio->close = q40kbd_close; + serio->dev.parent = &q40kbd_device->dev; + strlcpy(serio->name, "Q40 Kbd Port", sizeof(serio->name)); + strlcpy(serio->phys, "Q40", sizeof(serio->phys)); + } + + return serio; +} + +static int __init q40kbd_init(void) +{ if (!MACH_IS_Q40) return -EIO; - /* allocate the IRQ */ - request_irq(Q40_IRQ_KEYBOARD, q40kbd_interrupt, 0, "q40kbd", NULL); - - /* flush any pending input */ - while (maxread-- && (Q40_IRQ_KEYB_MASK & master_inb(INTERRUPT_REG))) - master_inb(KEYCODE_REG); + q40kbd_device = platform_device_register_simple("q40kbd", -1, NULL, 0); + if (IS_ERR(q40kbd_device)) + return PTR_ERR(q40kbd_device); - /* off we go */ - master_outb(-1,KEYBOARD_UNLOCK_REG); - master_outb(1,KEY_IRQ_ENABLE_REG); + if (!(q40kbd_port = q40kbd_allocate_port())) { + platform_device_unregister(q40kbd_device); + return -ENOMEM; + } - serio_register_port(&q40kbd_port); + serio_register_port(q40kbd_port); printk(KERN_INFO "serio: Q40 kbd registered\n"); return 0; @@ -91,11 +155,8 @@ static int __init q40kbd_init(void) static void __exit q40kbd_exit(void) { - master_outb(0,KEY_IRQ_ENABLE_REG); - master_outb(-1,KEYBOARD_UNLOCK_REG); - - serio_unregister_port(&q40kbd_port); - free_irq(Q40_IRQ_KEYBOARD, NULL); + serio_unregister_port(q40kbd_port); + platform_device_unregister(q40kbd_device); } module_init(q40kbd_init); diff --git a/drivers/input/serio/rpckbd.c b/drivers/input/serio/rpckbd.c index a2e4be7ae..b25d2e3b0 100644 --- a/drivers/input/serio/rpckbd.c +++ b/drivers/input/serio/rpckbd.c @@ -33,6 +33,7 @@ #include #include #include +#include #include #include @@ -44,6 +45,9 @@ MODULE_AUTHOR("Vojtech Pavlik, Russell King"); MODULE_DESCRIPTION("Acorn RiscPC PS/2 keyboard controller driver"); MODULE_LICENSE("GPL"); +static struct serio *rpckbd_port; +static struct platform_device *rpckbd_device; + static int rpckbd_write(struct serio *port, unsigned char val) { while (!(iomd_readb(IOMD_KCTRL) & (1 << 7))) @@ -101,25 +105,49 @@ static void rpckbd_close(struct serio *port) free_irq(IRQ_KEYBOARDTX, port); } -static struct serio rpckbd_port = +/* + * Allocate and initialize serio structure for subsequent registration + * with serio core. + */ + +static struct serio * __init rpckbd_allocate_port(void) { - .type = SERIO_8042, - .open = rpckbd_open, - .close = rpckbd_close, - .write = rpckbd_write, - .name = "RiscPC PS/2 kbd port", - .phys = "rpckbd/serio0", -}; + struct serio *serio; + + serio = kmalloc(sizeof(struct serio), GFP_KERNEL); + if (serio) { + memset(serio, 0, sizeof(struct serio)); + serio->type = SERIO_8042; + serio->write = rpckbd_write; + serio->open = rpckbd_open; + serio->close = rpckbd_close; + serio->dev.parent = &rpckbd_device->dev; + strlcpy(serio->name, "RiscPC PS/2 kbd port", sizeof(serio->name)); + strlcpy(serio->phys, "rpckbd/serio0", sizeof(serio->phys)); + } + + return serio; +} static int __init rpckbd_init(void) { - serio_register_port(&rpckbd_port); + rpckbd_device = platform_device_register_simple("rpckbd", -1, NULL, 0); + if (IS_ERR(rpckbd_device)) + return PTR_ERR(rpckbd_device); + + if (!(rpckbd_port = rpckbd_allocate_port())) { + platform_device_unregister(rpckbd_device); + return -ENOMEM; + } + + serio_register_port(rpckbd_port); return 0; } static void __exit rpckbd_exit(void) { - serio_unregister_port(&rpckbd_port); + serio_unregister_port(rpckbd_port); + platform_device_unregister(rpckbd_device); } module_init(rpckbd_init); diff --git a/drivers/input/serio/serio.c b/drivers/input/serio/serio.c index 6f003197b..268ca1d63 100644 --- a/drivers/input/serio/serio.c +++ b/drivers/input/serio/serio.c @@ -1,11 +1,9 @@ -/* - * $Id: serio.c,v 1.15 2002/01/22 21:12:03 vojtech Exp $ - * - * Copyright (c) 1999-2001 Vojtech Pavlik - */ - /* * The Serio abstraction module + * + * Copyright (c) 1999-2004 Vojtech Pavlik + * Copyright (c) 2004 Dmitry Torokhov + * Copyright (c) 2003 Daniele Bellucci */ /* @@ -26,10 +24,6 @@ * Should you need to contact me, the author, you can do so either by * e-mail - mail your message to , or by paper mail: * Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic - * - * Changes: - * 20 Jul. 2003 Daniele Bellucci - * Minor cleanups. */ #include @@ -50,100 +44,178 @@ MODULE_LICENSE("GPL"); EXPORT_SYMBOL(serio_interrupt); EXPORT_SYMBOL(serio_register_port); EXPORT_SYMBOL(serio_register_port_delayed); -EXPORT_SYMBOL(__serio_register_port); EXPORT_SYMBOL(serio_unregister_port); EXPORT_SYMBOL(serio_unregister_port_delayed); -EXPORT_SYMBOL(__serio_unregister_port); -EXPORT_SYMBOL(serio_register_device); -EXPORT_SYMBOL(serio_unregister_device); +EXPORT_SYMBOL(serio_register_driver); +EXPORT_SYMBOL(serio_unregister_driver); EXPORT_SYMBOL(serio_open); EXPORT_SYMBOL(serio_close); EXPORT_SYMBOL(serio_rescan); EXPORT_SYMBOL(serio_reconnect); +static DECLARE_MUTEX(serio_sem); /* protects serio_list and serio_diriver_list */ +static LIST_HEAD(serio_list); +static LIST_HEAD(serio_driver_list); +static unsigned int serio_no; + +struct bus_type serio_bus = { + .name = "serio", +}; + +static void serio_find_driver(struct serio *serio); +static void serio_create_port(struct serio *serio); +static void serio_destroy_port(struct serio *serio); +static void serio_connect_port(struct serio *serio, struct serio_driver *drv); +static void serio_reconnect_port(struct serio *serio); +static void serio_disconnect_port(struct serio *serio); + +static int serio_bind_driver(struct serio *serio, struct serio_driver *drv) +{ + get_driver(&drv->driver); + + drv->connect(serio, drv); + if (serio->drv) { + down_write(&serio_bus.subsys.rwsem); + serio->dev.driver = &drv->driver; + device_bind_driver(&serio->dev); + up_write(&serio_bus.subsys.rwsem); + return 1; + } + + put_driver(&drv->driver); + return 0; +} + +/* serio_find_driver() must be called with serio_sem down. */ +static void serio_find_driver(struct serio *serio) +{ + struct serio_driver *drv; + + list_for_each_entry(drv, &serio_driver_list, node) + if (!drv->manual_bind) + if (serio_bind_driver(serio, drv)) + break; +} + +/* + * Serio event processing. + */ + struct serio_event { int type; struct serio *serio; struct list_head node; }; -static DECLARE_MUTEX(serio_sem); -static LIST_HEAD(serio_list); -static LIST_HEAD(serio_dev_list); +enum serio_event_type { + SERIO_RESCAN, + SERIO_RECONNECT, + SERIO_REGISTER_PORT, + SERIO_UNREGISTER_PORT, +}; + +static spinlock_t serio_event_lock = SPIN_LOCK_UNLOCKED; /* protects serio_event_list */ static LIST_HEAD(serio_event_list); +static DECLARE_WAIT_QUEUE_HEAD(serio_wait); +static DECLARE_COMPLETION(serio_exited); static int serio_pid; -static void serio_find_dev(struct serio *serio) +static void serio_queue_event(struct serio *serio, int event_type) { - struct serio_dev *dev; + unsigned long flags; + struct serio_event *event; - list_for_each_entry(dev, &serio_dev_list, node) { - if (serio->dev) - break; - if (dev->connect) - dev->connect(serio, dev); - } -} + spin_lock_irqsave(&serio_event_lock, flags); -#define SERIO_RESCAN 1 -#define SERIO_RECONNECT 2 -#define SERIO_REGISTER_PORT 3 -#define SERIO_UNREGISTER_PORT 4 + if ((event = kmalloc(sizeof(struct serio_event), GFP_ATOMIC))) { + event->type = event_type; + event->serio = serio; -static DECLARE_WAIT_QUEUE_HEAD(serio_wait); -static DECLARE_COMPLETION(serio_exited); + list_add_tail(&event->node, &serio_event_list); + wake_up(&serio_wait); + } + + spin_unlock_irqrestore(&serio_event_lock, flags); +} -static void serio_invalidate_pending_events(struct serio *serio) +static struct serio_event *serio_get_event(void) { struct serio_event *event; + struct list_head *node; + unsigned long flags; + + spin_lock_irqsave(&serio_event_lock, flags); + + if (list_empty(&serio_event_list)) { + spin_unlock_irqrestore(&serio_event_lock, flags); + return NULL; + } + + node = serio_event_list.next; + event = container_of(node, struct serio_event, node); + list_del_init(node); + + spin_unlock_irqrestore(&serio_event_lock, flags); - list_for_each_entry(event, &serio_event_list, node) - if (event->serio == serio) - event->serio = NULL; + return event; } -void serio_handle_events(void) +static void serio_handle_events(void) { - struct list_head *node, *next; struct serio_event *event; - list_for_each_safe(node, next, &serio_event_list) { - event = container_of(node, struct serio_event, node); + while ((event = serio_get_event())) { down(&serio_sem); - if (event->serio == NULL) - goto event_done; switch (event->type) { case SERIO_REGISTER_PORT : - __serio_register_port(event->serio); + serio_create_port(event->serio); + serio_connect_port(event->serio, NULL); break; case SERIO_UNREGISTER_PORT : - __serio_unregister_port(event->serio); + serio_disconnect_port(event->serio); + serio_destroy_port(event->serio); break; case SERIO_RECONNECT : - if (event->serio->dev && event->serio->dev->reconnect) - if (event->serio->dev->reconnect(event->serio) == 0) - break; - /* reconnect failed - fall through to rescan */ + serio_reconnect_port(event->serio); + break; case SERIO_RESCAN : - if (event->serio->dev && event->serio->dev->disconnect) - event->serio->dev->disconnect(event->serio); - serio_find_dev(event->serio); + serio_disconnect_port(event->serio); + serio_connect_port(event->serio, NULL); break; default: break; } -event_done: + up(&serio_sem); - list_del_init(node); kfree(event); } } +static void serio_remove_pending_events(struct serio *serio) +{ + struct list_head *node, *next; + struct serio_event *event; + unsigned long flags; + + spin_lock_irqsave(&serio_event_lock, flags); + + list_for_each_safe(node, next, &serio_event_list) { + event = container_of(node, struct serio_event, node); + if (event->serio == serio) { + list_del_init(node); + kfree(event); + } + } + + spin_unlock_irqrestore(&serio_event_lock, flags); +} + + static int serio_thread(void *nothing) { lock_kernel(); @@ -163,52 +235,239 @@ static int serio_thread(void *nothing) complete_and_exit(&serio_exited, 0); } -static void serio_queue_event(struct serio *serio, int event_type) + +/* + * Serio port operations + */ + +static ssize_t serio_show_description(struct device *dev, char *buf) { - struct serio_event *event; + struct serio *serio = to_serio_port(dev); + return sprintf(buf, "%s\n", serio->name); +} - if ((event = kmalloc(sizeof(struct serio_event), GFP_ATOMIC))) { - event->type = event_type; - event->serio = serio; +static ssize_t serio_show_driver(struct device *dev, char *buf) +{ + return sprintf(buf, "%s\n", dev->driver ? dev->driver->name : "(none)"); +} - list_add_tail(&event->node, &serio_event_list); - wake_up(&serio_wait); +static ssize_t serio_rebind_driver(struct device *dev, const char *buf, size_t count) +{ + struct serio *serio = to_serio_port(dev); + struct device_driver *drv; + int retval; + + retval = down_interruptible(&serio_sem); + if (retval) + return retval; + + retval = count; + if (!strncmp(buf, "none", count)) { + serio_disconnect_port(serio); + } else if (!strncmp(buf, "reconnect", count)) { + serio_reconnect_port(serio); + } else if (!strncmp(buf, "rescan", count)) { + serio_disconnect_port(serio); + serio_connect_port(serio, NULL); + } else if ((drv = driver_find(buf, &serio_bus)) != NULL) { + serio_disconnect_port(serio); + serio_connect_port(serio, to_serio_driver(drv)); + put_driver(drv); + } else { + retval = -EINVAL; } + + up(&serio_sem); + + return retval; } -void serio_rescan(struct serio *serio) +static ssize_t serio_show_bind_mode(struct device *dev, char *buf) { - serio_queue_event(serio, SERIO_RESCAN); + struct serio *serio = to_serio_port(dev); + return sprintf(buf, "%s\n", serio->manual_bind ? "manual" : "auto"); } -void serio_reconnect(struct serio *serio) +static ssize_t serio_set_bind_mode(struct device *dev, const char *buf, size_t count) { - serio_queue_event(serio, SERIO_RECONNECT); + struct serio *serio = to_serio_port(dev); + int retval; + + retval = count; + if (!strncmp(buf, "manual", count)) { + serio->manual_bind = 1; + } else if (!strncmp(buf, "auto", count)) { + serio->manual_bind = 0; + } else { + retval = -EINVAL; + } + + return retval; } -irqreturn_t serio_interrupt(struct serio *serio, - unsigned char data, unsigned int flags, struct pt_regs *regs) +static struct device_attribute serio_device_attrs[] = { + __ATTR(description, S_IRUGO, serio_show_description, NULL), + __ATTR(driver, S_IWUSR | S_IRUGO, serio_show_driver, serio_rebind_driver), + __ATTR(bind_mode, S_IWUSR | S_IRUGO, serio_show_bind_mode, serio_set_bind_mode), + __ATTR_NULL +}; + + +static void serio_release_port(struct device *dev) { - irqreturn_t ret = IRQ_NONE; + struct serio *serio = to_serio_port(dev); - if (serio->dev && serio->dev->interrupt) { - ret = serio->dev->interrupt(serio, data, flags, regs); - } else { - if (!flags) { - if ((serio->type == SERIO_8042 || - serio->type == SERIO_8042_XL) && (data != 0xaa)) - return ret; - serio_rescan(serio); - ret = IRQ_HANDLED; + kfree(serio); + module_put(THIS_MODULE); +} + +static void serio_create_port(struct serio *serio) +{ + try_module_get(THIS_MODULE); + + spin_lock_init(&serio->lock); + list_add_tail(&serio->node, &serio_list); + snprintf(serio->dev.bus_id, sizeof(serio->dev.bus_id), "serio%d", serio_no++); + serio->dev.bus = &serio_bus; + serio->dev.release = serio_release_port; + if (serio->parent) + serio->dev.parent = &serio->parent->dev; + device_register(&serio->dev); +} + +/* + * serio_destroy_port() completes deregistration process and removes + * port from the system + */ +static void serio_destroy_port(struct serio *serio) +{ + struct serio_driver *drv = serio->drv; + unsigned long flags; + + serio_remove_pending_events(serio); + list_del_init(&serio->node); + + if (drv) { + drv->disconnect(serio); + down_write(&serio_bus.subsys.rwsem); + device_release_driver(&serio->dev); + up_write(&serio_bus.subsys.rwsem); + put_driver(&drv->driver); + } + + if (serio->parent) { + spin_lock_irqsave(&serio->parent->lock, flags); + serio->parent->child = NULL; + spin_unlock_irqrestore(&serio->parent->lock, flags); + } + + device_unregister(&serio->dev); +} + +/* + * serio_connect_port() tries to bind the port and possible all its + * children to appropriate drivers. If driver passed in the function will not + * try otehr drivers when binding parent port. + */ +static void serio_connect_port(struct serio *serio, struct serio_driver *drv) +{ + WARN_ON(serio->drv); + WARN_ON(serio->child); + + if (drv) + serio_bind_driver(serio, drv); + else if (!serio->manual_bind) + serio_find_driver(serio); + + /* Ok, now bind children, if any */ + while (serio->child) { + serio = serio->child; + + WARN_ON(serio->drv); + WARN_ON(serio->child); + + serio_create_port(serio); + + if (!serio->manual_bind) { + /* + * With children we just _prefer_ passed in driver, + * but we will try other options in case preferred + * is not the one + */ + if (!drv || !serio_bind_driver(serio, drv)) + serio_find_driver(serio); } } - return ret; +} + +/* + * + */ +static void serio_reconnect_port(struct serio *serio) +{ + do { + if (!serio->drv || !serio->drv->reconnect || serio->drv->reconnect(serio)) { + serio_disconnect_port(serio); + serio_connect_port(serio, NULL); + /* Ok, old children are now gone, we are done */ + break; + } + serio = serio->child; + } while (serio); +} + +/* + * serio_disconnect_port() unbinds a port from its driver. As a side effect + * all child ports are unbound and destroyed. + */ +static void serio_disconnect_port(struct serio *serio) +{ + struct serio_driver *drv = serio->drv; + struct serio *s; + + if (serio->child) { + /* + * Children ports should be disconnected and destroyed + * first, staring with the leaf one, since we don't want + * to do recursion + */ + do { + s = serio->child; + } while (s->child); + + while (s != serio) { + s = s->parent; + serio_destroy_port(s->child); + } + } + + /* + * Ok, no children left, now disconnect this port + */ + if (drv) { + drv->disconnect(serio); + down_write(&serio_bus.subsys.rwsem); + device_release_driver(&serio->dev); + up_write(&serio_bus.subsys.rwsem); + put_driver(&drv->driver); + } +} + +void serio_rescan(struct serio *serio) +{ + serio_queue_event(serio, SERIO_RESCAN); +} + +void serio_reconnect(struct serio *serio) +{ + serio_queue_event(serio, SERIO_RECONNECT); } void serio_register_port(struct serio *serio) { down(&serio_sem); - __serio_register_port(serio); + serio_create_port(serio); + serio_connect_port(serio, NULL); up(&serio_sem); } @@ -222,21 +481,11 @@ void serio_register_port_delayed(struct serio *serio) serio_queue_event(serio, SERIO_REGISTER_PORT); } -/* - * Should only be called directly if serio_sem has already been taken, - * for example when unregistering a serio from other input device's - * connect() function. - */ -void __serio_register_port(struct serio *serio) -{ - list_add_tail(&serio->node, &serio_list); - serio_find_dev(serio); -} - void serio_unregister_port(struct serio *serio) { down(&serio_sem); - __serio_unregister_port(serio); + serio_disconnect_port(serio); + serio_destroy_port(serio); up(&serio_sem); } @@ -250,82 +499,171 @@ void serio_unregister_port_delayed(struct serio *serio) serio_queue_event(serio, SERIO_UNREGISTER_PORT); } + /* - * Should only be called directly if serio_sem has already been taken, - * for example when unregistering a serio from other input device's - * disconnect() function. + * Serio driver operations */ -void __serio_unregister_port(struct serio *serio) + +static ssize_t serio_driver_show_description(struct device_driver *drv, char *buf) { - serio_invalidate_pending_events(serio); - list_del_init(&serio->node); - if (serio->dev && serio->dev->disconnect) - serio->dev->disconnect(serio); + struct serio_driver *driver = to_serio_driver(drv); + return sprintf(buf, "%s\n", driver->description ? driver->description : "(none)"); +} + +static ssize_t serio_driver_show_bind_mode(struct device_driver *drv, char *buf) +{ + struct serio_driver *serio_drv = to_serio_driver(drv); + return sprintf(buf, "%s\n", serio_drv->manual_bind ? "manual" : "auto"); } -void serio_register_device(struct serio_dev *dev) +static ssize_t serio_driver_set_bind_mode(struct device_driver *drv, const char *buf, size_t count) +{ + struct serio_driver *serio_drv = to_serio_driver(drv); + int retval; + + retval = count; + if (!strncmp(buf, "manual", count)) { + serio_drv->manual_bind = 1; + } else if (!strncmp(buf, "auto", count)) { + serio_drv->manual_bind = 0; + } else { + retval = -EINVAL; + } + + return retval; +} + + +static struct driver_attribute serio_driver_attrs[] = { + __ATTR(description, S_IRUGO, serio_driver_show_description, NULL), + __ATTR(bind_mode, S_IWUSR | S_IRUGO, + serio_driver_show_bind_mode, serio_driver_set_bind_mode), + __ATTR_NULL +}; + +void serio_register_driver(struct serio_driver *drv) { struct serio *serio; + down(&serio_sem); - list_add_tail(&dev->node, &serio_dev_list); - list_for_each_entry(serio, &serio_list, node) - if (!serio->dev && dev->connect) - dev->connect(serio, dev); + + list_add_tail(&drv->node, &serio_driver_list); + + drv->driver.bus = &serio_bus; + driver_register(&drv->driver); + + if (drv->manual_bind) + goto out; + +start_over: + list_for_each_entry(serio, &serio_list, node) { + if (!serio->drv) { + serio_connect_port(serio, drv); + /* + * if new child appeared then the list is changed, + * we need to start over + */ + if (serio->child) + goto start_over; + } + } + +out: up(&serio_sem); } -void serio_unregister_device(struct serio_dev *dev) +void serio_unregister_driver(struct serio_driver *drv) { struct serio *serio; down(&serio_sem); - list_del_init(&dev->node); + list_del_init(&drv->node); + +start_over: list_for_each_entry(serio, &serio_list, node) { - if (serio->dev == dev && dev->disconnect) - dev->disconnect(serio); - serio_find_dev(serio); + if (serio->drv == drv) { + serio_disconnect_port(serio); + serio_connect_port(serio, NULL); + /* we could've deleted some ports, restart */ + goto start_over; + } } + + driver_unregister(&drv->driver); + up(&serio_sem); } -/* called from serio_dev->connect/disconnect methods under serio_sem */ -int serio_open(struct serio *serio, struct serio_dev *dev) +/* called from serio_driver->connect/disconnect methods under serio_sem */ +int serio_open(struct serio *serio, struct serio_driver *drv) { - serio->dev = dev; + serio_pause_rx(serio); + serio->drv = drv; + serio_continue_rx(serio); + if (serio->open && serio->open(serio)) { - serio->dev = NULL; + serio_pause_rx(serio); + serio->drv = NULL; + serio_continue_rx(serio); return -1; } return 0; } -/* called from serio_dev->connect/disconnect methods under serio_sem */ +/* called from serio_driver->connect/disconnect methods under serio_sem */ void serio_close(struct serio *serio) { if (serio->close) serio->close(serio); - serio->dev = NULL; + + serio_pause_rx(serio); + serio->drv = NULL; + serio_continue_rx(serio); } -static int __init serio_init(void) +irqreturn_t serio_interrupt(struct serio *serio, + unsigned char data, unsigned int dfl, struct pt_regs *regs) { - int pid; + unsigned long flags; + irqreturn_t ret = IRQ_NONE; + + spin_lock_irqsave(&serio->lock, flags); + + if (likely(serio->drv)) { + ret = serio->drv->interrupt(serio, data, dfl, regs); + } else { + if (!dfl) { + if ((serio->type != SERIO_8042 && + serio->type != SERIO_8042_XL) || (data == 0xaa)) { + serio_rescan(serio); + ret = IRQ_HANDLED; + } + } + } + + spin_unlock_irqrestore(&serio->lock, flags); - pid = kernel_thread(serio_thread, NULL, CLONE_KERNEL); + return ret; +} - if (!pid) { +static int __init serio_init(void) +{ + if (!(serio_pid = kernel_thread(serio_thread, NULL, CLONE_KERNEL))) { printk(KERN_WARNING "serio: Failed to start kseriod\n"); return -1; } - serio_pid = pid; + serio_bus.dev_attrs = serio_device_attrs; + serio_bus.drv_attrs = serio_driver_attrs; + bus_register(&serio_bus); return 0; } static void __exit serio_exit(void) { + bus_unregister(&serio_bus); kill_proc(serio_pid, SIGTERM, 1); wait_for_completion(&serio_exited); } diff --git a/drivers/input/serio/serio_raw.c b/drivers/input/serio/serio_raw.c new file mode 100644 index 000000000..f424fdf19 --- /dev/null +++ b/drivers/input/serio/serio_raw.c @@ -0,0 +1,390 @@ +/* + * Raw serio device providing access to a raw byte stream from underlying + * serio port. Closely emulates behavior of pre-2.6 /dev/psaux device + * + * Copyright (c) 2004 Dmitry Torokhov + * + * This program is free software; you can 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define DRIVER_DESC "Raw serio driver" + +MODULE_AUTHOR("Dmitry Torokhov "); +MODULE_DESCRIPTION(DRIVER_DESC); +MODULE_LICENSE("GPL"); + +#define SERIO_RAW_QUEUE_LEN 64 +struct serio_raw { + unsigned char queue[SERIO_RAW_QUEUE_LEN]; + unsigned int tail, head; + + char name[16]; + unsigned int refcnt; + struct serio *serio; + struct miscdevice dev; + wait_queue_head_t wait; + struct list_head list; + struct list_head node; +}; + +struct serio_raw_list { + struct fasync_struct *fasync; + struct serio_raw *serio_raw; + struct list_head node; +}; + +static DECLARE_MUTEX(serio_raw_sem); +static LIST_HEAD(serio_raw_list); +static unsigned int serio_raw_no; + +/********************************************************************* + * Interface with userspace (file operations) * + *********************************************************************/ + +static int serio_raw_fasync(int fd, struct file *file, int on) +{ + struct serio_raw_list *list = file->private_data; + int retval; + + retval = fasync_helper(fd, file, on, &list->fasync); + return retval < 0 ? retval : 0; +} + +static struct serio_raw *serio_raw_locate(int minor) +{ + struct serio_raw *serio_raw; + + list_for_each_entry(serio_raw, &serio_raw_list, node) { + if (serio_raw->dev.minor == minor) + return serio_raw; + } + + return NULL; +} + +static int serio_raw_open(struct inode *inode, struct file *file) +{ + struct serio_raw *serio_raw; + struct serio_raw_list *list; + int retval = 0; + + retval = down_interruptible(&serio_raw_sem); + if (retval) + return retval; + + if (!(serio_raw = serio_raw_locate(iminor(inode)))) { + retval = -ENODEV; + goto out; + } + + if (!serio_raw->serio) { + retval = -ENODEV; + goto out; + } + + if (!(list = kmalloc(sizeof(struct serio_raw_list), GFP_KERNEL))) { + retval = -ENOMEM; + goto out; + } + + memset(list, 0, sizeof(struct serio_raw_list)); + list->serio_raw = serio_raw; + file->private_data = list; + + serio_raw->refcnt++; + list_add_tail(&list->node, &serio_raw->list); + +out: + up(&serio_raw_sem); + return retval; +} + +static int serio_raw_cleanup(struct serio_raw *serio_raw) +{ + if (--serio_raw->refcnt == 0) { + misc_deregister(&serio_raw->dev); + list_del_init(&serio_raw->node); + kfree(serio_raw); + + return 1; + } + + return 0; +} + +static int serio_raw_release(struct inode *inode, struct file *file) +{ + struct serio_raw_list *list = file->private_data; + struct serio_raw *serio_raw = list->serio_raw; + + down(&serio_raw_sem); + + serio_raw_fasync(-1, file, 0); + serio_raw_cleanup(serio_raw); + + up(&serio_raw_sem); + return 0; +} + +static int serio_raw_fetch_byte(struct serio_raw *serio_raw, char *c) +{ + unsigned long flags; + int empty; + + spin_lock_irqsave(&serio_raw->serio->lock, flags); + + empty = serio_raw->head == serio_raw->tail; + if (!empty) { + *c = serio_raw->queue[serio_raw->tail]; + serio_raw->tail = (serio_raw->tail + 1) % SERIO_RAW_QUEUE_LEN; + } + + spin_unlock_irqrestore(&serio_raw->serio->lock, flags); + + return !empty; +} + +static ssize_t serio_raw_read(struct file *file, char __user *buffer, size_t count, loff_t *ppos) +{ + struct serio_raw_list *list = file->private_data; + struct serio_raw *serio_raw = list->serio_raw; + char c; + ssize_t retval = 0; + + if (!serio_raw->serio) + return -ENODEV; + + if (serio_raw->head == serio_raw->tail && (file->f_flags & O_NONBLOCK)) + return -EAGAIN; + + retval = wait_event_interruptible(list->serio_raw->wait, + serio_raw->head != serio_raw->tail || !serio_raw->serio); + if (retval) + return retval; + + if (!serio_raw->serio) + return -ENODEV; + + while (retval < count && serio_raw_fetch_byte(serio_raw, &c)) { + if (put_user(c, buffer++)) + return -EFAULT; + retval++; + } + + return retval; +} + +static ssize_t serio_raw_write(struct file *file, const char __user *buffer, size_t count, loff_t *ppos) +{ + struct serio_raw_list *list = file->private_data; + ssize_t written = 0; + int retval; + unsigned char c; + + retval = down_interruptible(&serio_raw_sem); + if (retval) + return retval; + + if (!list->serio_raw->serio) { + retval = -ENODEV; + goto out; + } + + if (count > 32) + count = 32; + + while (count--) { + if (get_user(c, buffer++)) { + retval = -EFAULT; + goto out; + } + if (serio_write(list->serio_raw->serio, c)) { + retval = -EIO; + goto out; + } + written++; + }; + +out: + up(&serio_raw_sem); + return written; +} + +static unsigned int serio_raw_poll(struct file *file, poll_table *wait) +{ + struct serio_raw_list *list = file->private_data; + + poll_wait(file, &list->serio_raw->wait, wait); + + if (list->serio_raw->head != list->serio_raw->tail) + return POLLIN | POLLRDNORM; + + return 0; +} + +struct file_operations serio_raw_fops = { + .owner = THIS_MODULE, + .open = serio_raw_open, + .release = serio_raw_release, + .read = serio_raw_read, + .write = serio_raw_write, + .poll = serio_raw_poll, + .fasync = serio_raw_fasync, +}; + + +/********************************************************************* + * Interface with serio port * + *********************************************************************/ + +static irqreturn_t serio_raw_interrupt(struct serio *serio, unsigned char data, + unsigned int dfl, struct pt_regs *regs) +{ + struct serio_raw *serio_raw = serio->private; + struct serio_raw_list *list; + unsigned int head = serio_raw->head; + + /* we are holding serio->lock here so we are prootected */ + serio_raw->queue[head] = data; + head = (head + 1) % SERIO_RAW_QUEUE_LEN; + if (likely(head != serio_raw->tail)) { + serio_raw->head = head; + list_for_each_entry(list, &serio_raw->list, node) + kill_fasync(&list->fasync, SIGIO, POLL_IN); + wake_up_interruptible(&serio_raw->wait); + } + + return IRQ_HANDLED; +} + +static void serio_raw_connect(struct serio *serio, struct serio_driver *drv) +{ + struct serio_raw *serio_raw; + int err; + + if ((serio->type & SERIO_TYPE) != SERIO_8042) + return; + + if (!(serio_raw = kmalloc(sizeof(struct serio_raw), GFP_KERNEL))) { + printk(KERN_ERR "serio_raw.c: can't allocate memory for a device\n"); + return; + } + + down(&serio_raw_sem); + + memset(serio_raw, 0, sizeof(struct serio_raw)); + snprintf(serio_raw->name, sizeof(serio_raw->name), "serio_raw%d", serio_raw_no++); + serio_raw->refcnt = 1; + serio_raw->serio = serio; + INIT_LIST_HEAD(&serio_raw->list); + init_waitqueue_head(&serio_raw->wait); + + serio->private = serio_raw; + if (serio_open(serio, drv)) + goto out_free; + + list_add_tail(&serio_raw->node, &serio_raw_list); + + serio_raw->dev.minor = PSMOUSE_MINOR; + serio_raw->dev.name = serio_raw->name; + serio_raw->dev.fops = &serio_raw_fops; + + err = misc_register(&serio_raw->dev); + if (err) { + serio_raw->dev.minor = MISC_DYNAMIC_MINOR; + err = misc_register(&serio_raw->dev); + } + + if (err) { + printk(KERN_INFO "serio_raw: failed to register raw access device for %s\n", + serio->phys); + goto out_close; + } + + printk(KERN_INFO "serio_raw: raw access enabled on %s (%s, minor %d)\n", + serio->phys, serio_raw->name, serio_raw->dev.minor); + goto out; + +out_close: + serio_close(serio); + list_del_init(&serio_raw->node); +out_free: + serio->private = NULL; + kfree(serio_raw); +out: + up(&serio_raw_sem); +} + +static int serio_raw_reconnect(struct serio *serio) +{ + struct serio_raw *serio_raw = serio->private; + struct serio_driver *drv = serio->drv; + + if (!drv || !serio_raw) { + printk(KERN_DEBUG "serio_raw: reconnect request, but serio is disconnected, ignoring...\n"); + return -1; + } + + /* + * Nothing needs to be done here, we just need this method to + * keep the same device. + */ + return 0; +} + +static void serio_raw_disconnect(struct serio *serio) +{ + struct serio_raw *serio_raw; + + down(&serio_raw_sem); + + serio_raw = serio->private; + + serio_close(serio); + serio->private = NULL; + + serio_raw->serio = NULL; + if (!serio_raw_cleanup(serio_raw)) + wake_up_interruptible(&serio_raw->wait); + + up(&serio_raw_sem); +} + +static struct serio_driver serio_raw_drv = { + .driver = { + .name = "serio_raw", + }, + .description = DRIVER_DESC, + .interrupt = serio_raw_interrupt, + .connect = serio_raw_connect, + .reconnect = serio_raw_reconnect, + .disconnect = serio_raw_disconnect, + .manual_bind = 1, +}; + +int __init serio_raw_init(void) +{ + serio_register_driver(&serio_raw_drv); + return 0; +} + +void __exit serio_raw_exit(void) +{ + serio_unregister_driver(&serio_raw_drv); +} + +module_init(serio_raw_init); +module_exit(serio_raw_exit); diff --git a/drivers/input/touchscreen/gunze.c b/drivers/input/touchscreen/gunze.c index 775914980..e5e38dd4a 100644 --- a/drivers/input/touchscreen/gunze.c +++ b/drivers/input/touchscreen/gunze.c @@ -36,8 +36,10 @@ #include #include +#define DRIVER_DESC "Gunze AHL-51S touchscreen driver" + MODULE_AUTHOR("Vojtech Pavlik "); -MODULE_DESCRIPTION("Gunze AHL-51S touchscreen driver"); +MODULE_DESCRIPTION(DRIVER_DESC); MODULE_LICENSE("GPL"); /* @@ -111,7 +113,7 @@ static void gunze_disconnect(struct serio *serio) * and if yes, registers it as an input device. */ -static void gunze_connect(struct serio *serio, struct serio_dev *dev) +static void gunze_connect(struct serio *serio, struct serio_driver *drv) { struct gunze *gunze; @@ -142,7 +144,7 @@ static void gunze_connect(struct serio *serio, struct serio_dev *dev) gunze->dev.id.product = 0x0051; gunze->dev.id.version = 0x0100; - if (serio_open(serio, dev)) { + if (serio_open(serio, drv)) { kfree(gunze); return; } @@ -156,10 +158,14 @@ static void gunze_connect(struct serio *serio, struct serio_dev *dev) * The serio device structure. */ -static struct serio_dev gunze_dev = { - .interrupt = gunze_interrupt, - .connect = gunze_connect, - .disconnect = gunze_disconnect, +static struct serio_driver gunze_drv = { + .driver = { + .name = "gunze", + }, + .description = DRIVER_DESC, + .interrupt = gunze_interrupt, + .connect = gunze_connect, + .disconnect = gunze_disconnect, }; /* @@ -168,13 +174,13 @@ static struct serio_dev gunze_dev = { int __init gunze_init(void) { - serio_register_device(&gunze_dev); + serio_register_driver(&gunze_drv); return 0; } void __exit gunze_exit(void) { - serio_unregister_device(&gunze_dev); + serio_unregister_driver(&gunze_drv); } module_init(gunze_init); diff --git a/drivers/input/touchscreen/h3600_ts_input.c b/drivers/input/touchscreen/h3600_ts_input.c index 7b4c16d6f..5d719671c 100644 --- a/drivers/input/touchscreen/h3600_ts_input.c +++ b/drivers/input/touchscreen/h3600_ts_input.c @@ -45,8 +45,10 @@ #include #include +#define DRIVER_DESC "H3600 touchscreen driver" + MODULE_AUTHOR("James Simmons "); -MODULE_DESCRIPTION("H3600 touchscreen driver"); +MODULE_DESCRIPTION(DRIVER_DESC); MODULE_LICENSE("GPL"); /* @@ -373,7 +375,7 @@ static irqreturn_t h3600ts_interrupt(struct serio *serio, unsigned char data, * new serio device. It looks whether it was registered as a H3600 touchscreen * and if yes, registers it as an input device. */ -static void h3600ts_connect(struct serio *serio, struct serio_dev *dev) +static void h3600ts_connect(struct serio *serio, struct serio_driver *drv) { struct h3600_dev *ts; @@ -441,7 +443,7 @@ static void h3600ts_connect(struct serio *serio, struct serio_dev *dev) ts->dev.id.product = 0x0666; /* FIXME !!! We can ask the hardware */ ts->dev.id.version = 0x0100; - if (serio_open(serio, dev)) { + if (serio_open(serio, drv)) { free_irq(IRQ_GPIO_BITSY_ACTION_BUTTON, ts); free_irq(IRQ_GPIO_BITSY_NPOWER_BUTTON, ts); kfree(ts); @@ -478,10 +480,14 @@ static void h3600ts_disconnect(struct serio *serio) * The serio device structure. */ -static struct serio_dev h3600ts_dev = { - .interrupt = h3600ts_interrupt, - .connect = h3600ts_connect, - .disconnect = h3600ts_disconnect, +static struct serio_driver h3600ts_drv = { + .driver = { + .name = "h3600ts", + }, + .description = DRIVER_DESC, + .interrupt = h3600ts_interrupt, + .connect = h3600ts_connect, + .disconnect = h3600ts_disconnect, }; /* @@ -490,13 +496,13 @@ static struct serio_dev h3600ts_dev = { static int __init h3600ts_init(void) { - serio_register_device(&h3600ts_dev); + serio_register_driver(&h3600ts_drv); return 0; } static void __exit h3600ts_exit(void) { - serio_unregister_device(&h3600ts_dev); + serio_unregister_driver(&h3600ts_drv); } module_init(h3600ts_init); diff --git a/drivers/isdn/hardware/avm/b1.c b/drivers/isdn/hardware/avm/b1.c index c87516ab2..0c7061d55 100644 --- a/drivers/isdn/hardware/avm/b1.c +++ b/drivers/isdn/hardware/avm/b1.c @@ -389,7 +389,7 @@ u16 b1_send_message(struct capi_ctr *ctrl, struct sk_buff *skb) CAPIMSG_NCCI(skb->data), CAPIMSG_MSGID(skb->data)); if (retval != CAPI_NOERROR) - goto out; + return retval; dlen = CAPIMSG_DATALEN(skb->data); @@ -399,16 +399,14 @@ u16 b1_send_message(struct capi_ctr *ctrl, struct sk_buff *skb) b1_put_slice(port, skb->data + len, dlen); spin_unlock_irqrestore(&card->lock, flags); } else { - retval = CAPI_NOERROR; - spin_lock_irqsave(&card->lock, flags); b1_put_byte(port, SEND_MESSAGE); b1_put_slice(port, skb->data, len); spin_unlock_irqrestore(&card->lock, flags); } - out: + dev_kfree_skb_any(skb); - return retval; + return CAPI_NOERROR; } /* ------------------------------------------------------------- */ diff --git a/drivers/isdn/hardware/avm/b1dma.c b/drivers/isdn/hardware/avm/b1dma.c index d2c69b727..55bed00ca 100644 --- a/drivers/isdn/hardware/avm/b1dma.c +++ b/drivers/isdn/hardware/avm/b1dma.c @@ -839,8 +839,6 @@ u16 b1dma_send_message(struct capi_ctr *ctrl, struct sk_buff *skb) } if (retval == CAPI_NOERROR) b1dma_queue_tx(card, skb); - else - dev_kfree_skb_any(skb); return retval; } diff --git a/drivers/isdn/hardware/avm/c4.c b/drivers/isdn/hardware/avm/c4.c index 9e1285ca5..72b57d4fb 100644 --- a/drivers/isdn/hardware/avm/c4.c +++ b/drivers/isdn/hardware/avm/c4.c @@ -1029,8 +1029,6 @@ static u16 c4_send_message(struct capi_ctr *ctrl, struct sk_buff *skb) spin_lock_irqsave(&card->lock, flags); c4_dispatch_tx(card); spin_unlock_irqrestore(&card->lock, flags); - } else { - dev_kfree_skb_any(skb); } return retval; } diff --git a/drivers/isdn/hardware/avm/t1isa.c b/drivers/isdn/hardware/avm/t1isa.c index 9d777ebc8..cb9d9cee2 100644 --- a/drivers/isdn/hardware/avm/t1isa.c +++ b/drivers/isdn/hardware/avm/t1isa.c @@ -472,7 +472,7 @@ static u16 t1isa_send_message(struct capi_ctr *ctrl, struct sk_buff *skb) CAPIMSG_NCCI(skb->data), CAPIMSG_MSGID(skb->data)); if (retval != CAPI_NOERROR) - goto out; + return retval; dlen = CAPIMSG_DATALEN(skb->data); @@ -482,16 +482,15 @@ static u16 t1isa_send_message(struct capi_ctr *ctrl, struct sk_buff *skb) t1_put_slice(port, skb->data + len, dlen); spin_unlock_irqrestore(&card->lock, flags); } else { - retval = CAPI_NOERROR; spin_lock_irqsave(&card->lock, flags); b1_put_byte(port, SEND_MESSAGE); t1_put_slice(port, skb->data, len); spin_unlock_irqrestore(&card->lock, flags); } - out: + dev_kfree_skb_any(skb); - return retval; + return CAPI_NOERROR; } /* ------------------------------------------------------------- */ diff --git a/drivers/isdn/hardware/eicon/capifunc.h b/drivers/isdn/hardware/eicon/capifunc.h index 13f6ad242..bd256f297 100644 --- a/drivers/isdn/hardware/eicon/capifunc.h +++ b/drivers/isdn/hardware/eicon/capifunc.h @@ -1,4 +1,4 @@ -/* $Id: capifunc.h,v 1.11 2004/03/20 17:19:58 armin Exp $ +/* $Id: capifunc.h,v 1.11.4.1 2004/08/28 20:03:53 armin Exp $ * * ISDN interface module for Eicon active cards DIVA. * CAPI Interface common functions @@ -13,8 +13,6 @@ #ifndef __CAPIFUNC_H__ #define __CAPIFUNC_H__ -#define MAX_DESCRIPTORS 32 - #define DRRELMAJOR 2 #define DRRELMINOR 0 #define DRRELEXTRA "" diff --git a/drivers/isdn/hardware/eicon/debug_if.h b/drivers/isdn/hardware/eicon/debug_if.h index 0136eea12..4db739d58 100644 --- a/drivers/isdn/hardware/eicon/debug_if.h +++ b/drivers/isdn/hardware/eicon/debug_if.h @@ -57,12 +57,18 @@ void diva_mnt_remove_xdi_adapter (const DESCRIPTOR* d); void diva_mnt_add_xdi_adapter (const DESCRIPTOR* d); int diva_mnt_shutdown_xdi_adapters (void); +#define DIVA_MAX_SELECTIVE_FILTER_LENGTH 127 +int diva_set_trace_filter (int filter_length, const char* filter); +int diva_get_trace_filter (int max_length, char* filter); + #define DITRACE_CMD_GET_DRIVER_INFO 1 #define DITRACE_READ_DRIVER_DBG_MASK 2 #define DITRACE_WRITE_DRIVER_DBG_MASK 3 #define DITRACE_READ_TRACE_ENTRY 4 #define DITRACE_READ_TRACE_ENTRYS 5 +#define DITRACE_WRITE_SELECTIVE_TRACE_FILTER 6 +#define DITRACE_READ_SELECTIVE_TRACE_FILTER 7 /* Trace lavels for debug via management interface diff --git a/drivers/isdn/hardware/eicon/debuglib.h b/drivers/isdn/hardware/eicon/debuglib.h index 516d2bd8b..11b3b9edd 100644 --- a/drivers/isdn/hardware/eicon/debuglib.h +++ b/drivers/isdn/hardware/eicon/debuglib.h @@ -232,7 +232,7 @@ typedef struct _DbgHandle_ *pDbgHandle ; typedef void ( * DbgEnd) (pDbgHandle) ; typedef void ( * DbgLog) (unsigned short, int, char *, va_list) ; typedef void ( * DbgOld) (unsigned short, char *, va_list) ; -typedef void ( * DbgEv) (unsigned short, unsigned int, va_list) ; +typedef void ( * DbgEv) (unsigned short, unsigned long, va_list) ; typedef void ( * DbgIrq) (unsigned short, int, char *, va_list) ; typedef struct _DbgHandle_ { char Registered ; /* driver successfull registered */ @@ -259,7 +259,7 @@ typedef struct _DbgHandle_ void *pReserved3 ; } _DbgHandle_ ; extern _DbgHandle_ myDriverDebugHandle ; -typedef struct +typedef struct _OldDbgHandle_ { struct _OldDbgHandle_ *next ; void *pIrp ; long regTime[2] ; @@ -310,7 +310,7 @@ typedef struct unsigned long B_ChannelMask; unsigned long LogBufferSize; } CardTrace; - } u1; + }Data; } _DbgExtendedInfo_; #ifndef DIVA_NO_DEBUGLIB /* ------------------------------------------------------------- diff --git a/drivers/isdn/hardware/eicon/diva_didd.c b/drivers/isdn/hardware/eicon/diva_didd.c index 0afd015e0..48b3f15bc 100644 --- a/drivers/isdn/hardware/eicon/diva_didd.c +++ b/drivers/isdn/hardware/eicon/diva_didd.c @@ -1,4 +1,4 @@ -/* $Id: diva_didd.c,v 1.13 2003/08/27 10:11:21 schindler Exp $ +/* $Id: diva_didd.c,v 1.13.6.1 2004/08/28 20:03:53 armin Exp $ * * DIDD Interface module for Eicon active cards. * @@ -23,7 +23,7 @@ #include "divasync.h" #include "did_vers.h" -static char *main_revision = "$Revision: 1.13 $"; +static char *main_revision = "$Revision: 1.13.6.1 $"; static char *DRIVERNAME = "Eicon DIVA - DIDD table (http://www.melware.net)"; @@ -37,8 +37,6 @@ MODULE_AUTHOR("Cytronics & Melware, Eicon Networks"); MODULE_SUPPORTED_DEVICE("Eicon diva drivers"); MODULE_LICENSE("GPL"); -#define MAX_DESCRIPTORS 32 - #define DBG_MINIMUM (DL_LOG + DL_FTL + DL_ERR) #define DBG_DEFAULT (DBG_MINIMUM + DL_XLOG + DL_REG) @@ -50,8 +48,8 @@ extern void DIVA_DIDD_Read(void *, int); static struct proc_dir_entry *proc_didd; struct proc_dir_entry *proc_net_eicon = NULL; -EXPORT_SYMBOL_NOVERS(DIVA_DIDD_Read); -EXPORT_SYMBOL_NOVERS(proc_net_eicon); +EXPORT_SYMBOL(DIVA_DIDD_Read); +EXPORT_SYMBOL(proc_net_eicon); static char *getrev(const char *revision) { diff --git a/drivers/isdn/hardware/eicon/divasync.h b/drivers/isdn/hardware/eicon/divasync.h index 81c444828..0a5be7f96 100644 --- a/drivers/isdn/hardware/eicon/divasync.h +++ b/drivers/isdn/hardware/eicon/divasync.h @@ -31,33 +31,31 @@ #define IDI_SYNC_REQ_SET_POSTCALL 0x03 #define IDI_SYNC_REQ_GET_XLOG 0x04 #define IDI_SYNC_REQ_GET_FEATURES 0x05 -/* Added for DIVA USB support */ #define IDI_SYNC_REQ_USB_REGISTER 0x06 #define IDI_SYNC_REQ_USB_RELEASE 0x07 #define IDI_SYNC_REQ_USB_ADD_DEVICE 0x08 #define IDI_SYNC_REQ_USB_START_DEVICE 0x09 #define IDI_SYNC_REQ_USB_STOP_DEVICE 0x0A #define IDI_SYNC_REQ_USB_REMOVE_DEVICE 0x0B -/* Added for Diva Server Monitor */ #define IDI_SYNC_REQ_GET_CARDTYPE 0x0C #define IDI_SYNC_REQ_GET_DBG_XLOG 0x0D -#define IDI_SYNC_REQ_GET_LINE_IDX 0x0E #define DIVA_USB #define DIVA_USB_REQ 0xAC #define DIVA_USB_TEST 0xAB #define DIVA_USB_ADD_ADAPTER 0xAC #define DIVA_USB_REMOVE_ADAPTER 0xAD -/******************************************************************************/ #define IDI_SYNC_REQ_SERIAL_HOOK 0x80 #define IDI_SYNC_REQ_XCHANGE_STATUS 0x81 #define IDI_SYNC_REQ_USB_HOOK 0x82 #define IDI_SYNC_REQ_PORTDRV_HOOK 0x83 -#define IDI_SYNC_REQ_SLI (0x84) /* SLI request from 3signal modem drivers */ +#define IDI_SYNC_REQ_SLI 0x84 /* SLI request from 3signal modem drivers */ #define IDI_SYNC_REQ_RECONFIGURE 0x85 #define IDI_SYNC_REQ_RESET 0x86 +#define IDI_SYNC_REQ_GET_85X_DEVICE_DATA 0x87 #define IDI_SYNC_REQ_LOCK_85X 0x88 +#define IDI_SYNC_REQ_DIVA_85X_USB_DATA_EXCHANGE 0x99 +#define IDI_SYNC_REQ_DIPORT_EXCHANGE_REQ 0x98 #define IDI_SYNC_REQ_GET_85X_EXT_PORT_TYPE 0xA0 -#define IDI_SYNC_REQ_DIPORT_GET_85X_TX_CTRL_FN 0x98 /******************************************************************************/ #define IDI_SYNC_REQ_XDI_GET_EXTENDED_FEATURES 0x92 /* @@ -87,6 +85,8 @@ typedef struct _diva_xdi_get_extended_xdi_features { #define DIVA_XDI_EXTENDED_FEATURE_CAPI_PRMS 0x08 #define DIVA_XDI_EXTENDED_FEATURE_NO_CANCEL_RC 0x10 #define DIVA_XDI_EXTENDED_FEATURE_RX_DMA 0x20 +#define DIVA_XDI_EXTENDED_FEATURE_MANAGEMENT_DMA 0x40 +#define DIVA_XDI_EXTENDED_FEATURE_WIDE_ID 0x80 #define DIVA_XDI_EXTENDED_FEATURES_MAX_SZ 1 /******************************************************************************/ #define IDI_SYNC_REQ_XDI_GET_ADAPTER_SDRAM_BAR 0x93 @@ -115,6 +115,7 @@ typedef struct _diva_xdi_get_capi_parameters { typedef struct _diva_xdi_get_logical_adapter_number { dword logical_adapter_number; dword controller; + dword total_controllers; } diva_xdi_get_logical_adapter_number_s_t; /******************************************************************************/ #define IDI_SYNC_REQ_UP1DM_OPERATION 0x96 @@ -134,6 +135,7 @@ typedef struct _diva_xdi_dma_descriptor_operation { #define IDI_SYNC_REQ_DIDD_ADD_ADAPTER 0x03 #define IDI_SYNC_REQ_DIDD_REMOVE_ADAPTER 0x04 #define IDI_SYNC_REQ_DIDD_READ_ADAPTER_ARRAY 0x05 +#define IDI_SYNC_REQ_DIDD_GET_CFG_LIB_IFC 0x10 typedef struct _diva_didd_adapter_notify { dword handle; /* Notification handle */ void * callback; @@ -149,6 +151,9 @@ typedef struct _diva_didd_read_adapter_array { void * buffer; dword length; } diva_didd_read_adapter_array_t; +typedef struct _diva_didd_get_cfg_lib_ifc { + void* ifc; +} diva_didd_get_cfg_lib_ifc_t; /******************************************************************************/ #define IDI_SYNC_REQ_XDI_GET_STREAM 0x91 #define DIVA_XDI_SYNCHRONOUS_SERVICE 0x01 @@ -466,6 +471,10 @@ typedef union ENTITY e; diva_didd_read_adapter_array_t info; } didd_read_adapter_array; + struct { + ENTITY e; + diva_didd_get_cfg_lib_ifc_t info; + } didd_get_cfg_lib_ifc; struct { unsigned char Req; unsigned char Rc; diff --git a/drivers/isdn/hardware/eicon/io.c b/drivers/isdn/hardware/eicon/io.c index 6f94ee932..15bb9c636 100644 --- a/drivers/isdn/hardware/eicon/io.c +++ b/drivers/isdn/hardware/eicon/io.c @@ -77,6 +77,7 @@ static byte extended_xdi_features[DIVA_XDI_EXTENDED_FEATURES_MAX_SZ+1] = { #if defined(DIVA_IDI_RX_DMA) DIVA_XDI_EXTENDED_FEATURE_CMA | DIVA_XDI_EXTENDED_FEATURE_RX_DMA | + DIVA_XDI_EXTENDED_FEATURE_MANAGEMENT_DMA | #endif DIVA_XDI_EXTENDED_FEATURE_NO_CANCEL_RC), 0 @@ -226,8 +227,10 @@ void request(PISDN_ADAPTER IoAdapter, ENTITY * e) if (pI->descriptor_number >= 0) { dword dma_magic; void* local_addr; +#if 0 DBG_TRC(("A(%d) dma_alloc(%d)", IoAdapter->ANum, pI->descriptor_number)) +#endif diva_get_dma_map_entry (\ (struct _diva_dma_map_entry*)IoAdapter->dma_map, pI->descriptor_number, @@ -240,7 +243,9 @@ void request(PISDN_ADAPTER IoAdapter, ENTITY * e) } } else if ((pI->operation == IDI_SYNC_REQ_DMA_DESCRIPTOR_FREE) && (pI->descriptor_number >= 0)) { +#if 0 DBG_TRC(("A(%d) dma_free(%d)", IoAdapter->ANum, pI->descriptor_number)) +#endif diva_free_dma_map_entry((struct _diva_dma_map_entry*)IoAdapter->dma_map, pI->descriptor_number); pI->descriptor_number = -1; @@ -257,6 +262,7 @@ void request(PISDN_ADAPTER IoAdapter, ENTITY * e) &syncReq->xdi_logical_adapter_number.info; pI->logical_adapter_number = IoAdapter->ANum; pI->controller = IoAdapter->ControllerNumber; + pI->total_controllers = IoAdapter->Properties.Adapters; } return; case IDI_SYNC_REQ_XDI_GET_CAPI_PARAMS: { diva_xdi_get_capi_parameters_t prms, *pI = &syncReq->xdi_capi_prms.info; @@ -318,6 +324,16 @@ void request(PISDN_ADAPTER IoAdapter, ENTITY * e) } syncReq->GetSerial.serial = 0 ; break ; + case IDI_SYNC_REQ_GET_CARDTYPE: + if ( IoAdapter ) + { + syncReq->GetCardType.cardtype = IoAdapter->cardType ; + DBG_TRC(("xdi: Adapter %d / CardType %ld", + IoAdapter->ANum, IoAdapter->cardType)) + return ; + } + syncReq->GetCardType.cardtype = 0 ; + break ; case IDI_SYNC_REQ_GET_XLOG: if ( IoAdapter ) { @@ -326,6 +342,14 @@ void request(PISDN_ADAPTER IoAdapter, ENTITY * e) } e->Ind = 0 ; break ; + case IDI_SYNC_REQ_GET_DBG_XLOG: + if ( IoAdapter ) + { + pcm_req (IoAdapter, e) ; + return ; + } + e->Ind = 0 ; + break ; case IDI_SYNC_REQ_GET_FEATURES: if ( IoAdapter ) { @@ -345,7 +369,9 @@ void request(PISDN_ADAPTER IoAdapter, ENTITY * e) } if ( IoAdapter ) { +#if 0 DBG_FTL(("xdi: unknown Req 0 / Rc %d !", e->Rc)) +#endif return ; } } @@ -496,7 +522,7 @@ pcm_req (PISDN_ADAPTER IoAdapter, ENTITY *e) diva_os_enter_spin_lock (&IoAdapter->data_spin_lock, &OldIrql, "data_pcm_1"); - IoAdapter->pcm_data = (unsigned long)pcm; + IoAdapter->pcm_data = (void *)pcm; IoAdapter->pcm_pending = 1; diva_os_schedule_soft_isr (&IoAdapter->req_soft_isr); diva_os_leave_spin_lock (&IoAdapter->data_spin_lock, @@ -510,7 +536,7 @@ pcm_req (PISDN_ADAPTER IoAdapter, ENTITY *e) &OldIrql, "data_pcm_3"); IoAdapter->pcm_pending = 0; - IoAdapter->pcm_data = 0; + IoAdapter->pcm_data = NULL ; diva_os_leave_spin_lock (&IoAdapter->data_spin_lock, &OldIrql, "data_pcm_3"); @@ -528,7 +554,7 @@ pcm_req (PISDN_ADAPTER IoAdapter, ENTITY *e) &OldIrql, "data_pcm_4"); IoAdapter->pcm_pending = 0; - IoAdapter->pcm_data = 0; + IoAdapter->pcm_data = NULL ; diva_os_leave_spin_lock (&IoAdapter->data_spin_lock, &OldIrql, "data_pcm_4"); @@ -668,7 +694,7 @@ word io_inw(ADAPTER * a, void * adr) void io_in_buffer(ADAPTER * a, void * adr, void * buffer, word len) { byte *Port = (byte*)DIVA_OS_MEM_ATTACH_PORT((PISDN_ADAPTER)a->io); - byte* P = (byte*)buffer; + byte* P = (byte*)buffer; if ((long)adr & 1) { outppw(Port+4, (word)(unsigned long)adr); *P = inpp(Port); @@ -678,7 +704,7 @@ void io_in_buffer(ADAPTER * a, void * adr, void * buffer, word len) if (!len) { DIVA_OS_MEM_DETACH_PORT((PISDN_ADAPTER)a->io, Port); return; - } + } } outppw(Port+4, (word)(unsigned long)adr); inppw_buffer (Port, P, len+1); @@ -710,7 +736,7 @@ void io_outw(ADAPTER * a, void * adr, word data) void io_out_buffer(ADAPTER * a, void * adr, void * buffer, word len) { byte *Port = (byte*)DIVA_OS_MEM_ATTACH_PORT((PISDN_ADAPTER)a->io); - byte* P = (byte*)buffer; + byte* P = (byte*)buffer; if ((long)adr & 1) { outppw(Port+4, (word)(unsigned long)adr); outpp(Port, *P); @@ -839,21 +865,21 @@ void CALLBACK(ADAPTER * a, ENTITY * e) /* -------------------------------------------------------------------------- routines for aligned reading and writing on RISC -------------------------------------------------------------------------- */ -void outp_words_from_buffer (word* adr, byte* P, word len) +void outp_words_from_buffer (word* adr, byte* P, dword len) { - word i = 0; + dword i = 0; word w; - while (i < (len & 0xfffe)) { + while (i < (len & 0xfffffffe)) { w = P[i++]; w += (P[i++])<<8; outppw (adr, w); } } -void inp_words_to_buffer (word* adr, byte* P, word len) +void inp_words_to_buffer (word* adr, byte* P, dword len) { - word i = 0; + dword i = 0; word w; - while (i < (len & 0xfffe)) { + while (i < (len & 0xfffffffe)) { w = inppw (adr); P[i++] = (byte)(w); P[i++] = (byte)(w>>8); diff --git a/drivers/isdn/hardware/eicon/io.h b/drivers/isdn/hardware/eicon/io.h index 3e5403983..32ce17dfe 100644 --- a/drivers/isdn/hardware/eicon/io.h +++ b/drivers/isdn/hardware/eicon/io.h @@ -39,6 +39,14 @@ typedef struct { DEVICE_NAME DeviceName[4] ; PISDN_ADAPTER QuadroAdapter[4] ; } ADAPTER_LIST_ENTRY, *PADAPTER_LIST_ENTRY ; +/* -------------------------------------------------------------------------- + Special OS memory support structures + -------------------------------------------------------------------------- */ +#define MAX_MAPPED_ENTRIES 8 +typedef struct { + void * Address; + dword Length; +} ADAPTER_MEMORY ; /* -------------------------------------------------------------------------- Configuration of XDI clients carried by XDI -------------------------------------------------------------------------- */ @@ -52,6 +60,7 @@ typedef struct _diva_xdi_capi_cfg { -------------------------------------------------------------------------- */ struct _ISDN_ADAPTER { void (* DIRequest)(PISDN_ADAPTER, ENTITY *) ; + int State ; /* from NT4 1.srv, a good idea, but a poor achievment */ int Initialized ; int RegisteredWithDidd ; int Unavailable ; /* callback function possible? */ @@ -63,6 +72,7 @@ struct _ISDN_ADAPTER { /* remember mapped memory areas */ + ADAPTER_MEMORY MappedMemory[MAX_MAPPED_ENTRIES] ; CARD_PROPERTIES Properties ; dword cardType ; dword protocol_id ; /* configured protocol identifier */ @@ -87,15 +97,15 @@ struct _ISDN_ADAPTER { dword downloadAddrTable[4] ; /* add. for MultiMaster */ dword MemoryBase ; dword MemorySize ; - byte *Address ; + byte *Address ; byte *Config ; byte *Control ; - byte *reset ; - byte *port ; - byte *ram ; - byte *cfg ; - byte *prom ; - byte *ctlReg ; + byte *reset ; + byte *port ; + byte *ram ; + byte *cfg ; + byte *prom ; + byte *ctlReg ; struct pc_maint *pcm ; diva_os_dependent_devica_name_t os_name; byte Name[32] ; @@ -105,6 +115,7 @@ struct _ISDN_ADAPTER { char *ProtocolSuffix ; /* internal protocolfile table */ char Archive[32] ; char Protocol[32] ; + char AddDownload[32] ; /* Dsp- or other additional download files */ char Oad1[ISDN_MAX_NUM_LEN] ; char Osa1[ISDN_MAX_NUM_LEN] ; char Oad2[ISDN_MAX_NUM_LEN] ; @@ -153,8 +164,26 @@ struct _ISDN_ADAPTER { byte ModemCarrierWaitTimeSec; byte ModemCarrierLossWaitTimeTenthSec; byte PiafsLinkTurnaroundInFrames; + byte DiscAfterProgress; + byte AniDniLimiter[3]; + byte TxAttenuation; /* PRI/E1 only: attenuate TX signal */ word QsigFeatures; dword GenerateRingtone ; + dword SupplementaryServicesFeatures; + dword R2Dialect; + dword R2CasOptions; + dword FaxV34Options; + dword DisabledDspMask; + dword AdapterTestMask; + dword DspImageLength; + word AlertToIn20mSecTicks; + word ModemEyeSetup; + byte R2CtryLength; + byte CCBSRelTimer; + byte *PcCfgBufferFile;/* flexible parameter via file */ + byte *PcCfgBuffer ; /* flexible parameter via multistring */ + diva_os_dump_file_t dump_file; /* dump memory to file at lowest irq level */ + diva_os_board_trace_t board_trace ; /* traces from the board */ diva_os_spin_lock_t isr_spin_lock; diva_os_spin_lock_t data_spin_lock; diva_os_soft_isr_t req_soft_isr; @@ -180,15 +209,21 @@ struct _ISDN_ADAPTER { void (* stop)(PISDN_ADAPTER) ; void (* rstFnc)(PISDN_ADAPTER) ; void (* trapFnc)(PISDN_ADAPTER) ; + dword (* DetectDsps)(PISDN_ADAPTER) ; void (* os_trap_nfy_Fnc)(PISDN_ADAPTER, dword) ; diva_os_isr_callback_t diva_isr_handler; - dword sdram_bar; + dword sdram_bar; /* must be 32 bit */ dword fpga_features; volatile int pcm_pending; - volatile unsigned long pcm_data; + volatile void * pcm_data; diva_xdi_capi_cfg_t capi_cfg; dword tasks; - void* dma_map; + void *dma_map; + int (*DivaAdapterTestProc)(PISDN_ADAPTER); + void *AdapterTestMemoryStart; + dword AdapterTestMemoryLength; + const byte* cfg_lib_memory_init; + dword cfg_lib_memory_init_length; }; /* --------------------------------------------------------------------- Entity table @@ -219,8 +254,8 @@ struct s_load { /* --------------------------------------------------------------------- Functions for port io --------------------------------------------------------------------- */ -void outp_words_from_buffer (word* adr, byte* P, word len); -void inp_words_to_buffer (word* adr, byte* P, word len); +void outp_words_from_buffer (word* adr, byte* P, dword len); +void inp_words_to_buffer (word* adr, byte* P, dword len); /* --------------------------------------------------------------------- platform specific conversions --------------------------------------------------------------------- */ @@ -240,6 +275,10 @@ void io_out(ADAPTER * a, void * adr, byte data); void io_outw(ADAPTER * a, void * adr, word data); void io_out_buffer(ADAPTER * a, void * adr, void * P, word length); void io_inc(ADAPTER * a, void * adr); +void bri_in_buffer (PISDN_ADAPTER IoAdapter, dword Pos, + void *Buf, dword Len); +int bri_out_buffer (PISDN_ADAPTER IoAdapter, dword Pos, + void *Buf, dword Len, int Verify); /* --------------------------------------------------------------------- ram access functions for memory mapped cards --------------------------------------------------------------------- */ diff --git a/drivers/isdn/hardware/eicon/maintidi.h b/drivers/isdn/hardware/eicon/maintidi.h index 184142cdf..4f0629496 100644 --- a/drivers/isdn/hardware/eicon/maintidi.h +++ b/drivers/isdn/hardware/eicon/maintidi.h @@ -49,6 +49,8 @@ typedef struct _diva_strace_path2action { void* variable; /* Variable that will receive value */ } diva_strace_path2action_t; +#define DIVA_MAX_MANAGEMENT_TRANSFER_SIZE 4096 + typedef struct _diva_strace_context { diva_strace_library_interface_t instance; @@ -62,7 +64,7 @@ typedef struct _diva_strace_context { IDI_CALL request; BUFFERS XData; BUFFERS RData; - byte buffer[2048+512+1]; + byte buffer[DIVA_MAX_MANAGEMENT_TRANSFER_SIZE + 1]; int removal_state; int general_b_ch_event; int general_fax_event; diff --git a/drivers/isdn/hardware/eicon/pc.h b/drivers/isdn/hardware/eicon/pc.h index a65550605..1c6945768 100644 --- a/drivers/isdn/hardware/eicon/pc.h +++ b/drivers/isdn/hardware/eicon/pc.h @@ -143,6 +143,7 @@ struct dual #define N_DATA_ACK 12 /* data ack ind for D-bit procedure */ #define N_EDATA_ACK 13 /* data ack ind for INTERRUPT */ #define N_XON 15 /* clear RNR state */ +#define N_COMBI_IND N_XON /* combined indication */ #define N_Q_BIT 0x10 /* Q-bit for req/ind */ #define N_M_BIT 0x20 /* M-bit for req/ind */ #define N_D_BIT 0x40 /* D-bit for req/ind */ @@ -228,6 +229,10 @@ struct dual #define VSWITCH_IND 66 /* capifunctions for D-CH-switching */ #define MWI_POLL 67 /* Message Waiting Status Request fkt */ #define CALL_PEND_NOTIFY 68 /* notify capi to set new listen */ +#define DO_NOTHING 69 /* dont do somethin if you get this */ +#define INT_CT_REJ 70 /* ECT rejected internal command */ +#define CALL_HOLD_COMPLETE 71 /* In NT Mode indicate hold complete */ +#define CALL_RETRIEVE_COMPLETE 72 /* In NT Mode indicate retrieve complete */ /*------------------------------------------------------------------*/ /* management service primitives */ /*------------------------------------------------------------------*/ @@ -241,6 +246,7 @@ struct dual #define MAN_INFO_IND 2 #define MAN_EVENT_IND 3 #define MAN_TRACE_IND 4 +#define MAN_COMBI_IND 9 #define MAN_ESC 0x80 /*------------------------------------------------------------------*/ /* return code coding */ @@ -265,6 +271,7 @@ struct dual /*------------------------------------------------------------------*/ #define SHIFT 0x90 /* codeset shift */ #define MORE 0xa0 /* more data */ +#define SDNCMPL 0xa1 /* sending complete */ #define CL 0xb0 /* congestion level */ /* codeset 0 */ #define SMSG 0x00 /* segmented message */ @@ -288,6 +295,8 @@ struct dual #define RDX 0x73 /* redirecting number extended */ #define RDN 0x74 /* redirecting number */ #define RIN 0x76 /* redirection number */ +#define IUP 0x76 /* VN6 rerouter->PCS (codeset 6) */ +#define IPU 0x77 /* VN6 PCS->rerouter (codeset 6) */ #define RI 0x79 /* restart indicator */ #define MIE 0x7a /* management info element */ #define LLC 0x7c /* low layer compatibility */ @@ -296,6 +305,8 @@ struct dual #define ESC 0x7f /* escape extension */ #define DLC 0x20 /* data link layer configuration */ #define NLC 0x21 /* network layer configuration */ +#define REDIRECT_IE 0x22 /* redirection request/indication data */ +#define REDIRECT_NET_IE 0x23 /* redirection network override data */ /* codeset 6 */ #define SIN 0x01 /* service indicator */ #define CIF 0x02 /* charging information */ @@ -306,6 +317,7 @@ struct dual /*------------------------------------------------------------------*/ #define MSGTYPEIE 0x7a /* Messagetype info element */ #define CRIE 0x7b /* INFO info element */ +#define CODESET6IE 0xec /* Tunnel for Codeset 6 IEs */ #define VSWITCHIE 0xed /* VSwitch info element */ #define SSEXTIE 0xee /* Supplem. Service info element */ #define PROFILEIE 0xef /* Profile info element */ @@ -344,6 +356,13 @@ struct dual #define CCBS_REQUEST 0x32 #define CCBS_DEACTIVATE 0x33 #define CCBS_INTERROGATE 0x34 +#define CCBS_STATUS 0x35 +#define CCBS_ERASE 0x36 +#define CCBS_B_FREE 0x37 +#define CCNR_INFO_RETAIN 0x38 +#define CCBS_REMOTE_USER_FREE 0x39 +#define CCNR_REQUEST 0x3a +#define CCNR_INTERROGATE 0x3b #define GET_SUPPORTED_SERVICES 0xff #define DIVERSION_PROCEDURE_CFU 0x70 #define DIVERSION_PROCEDURE_CFB 0x71 @@ -362,6 +381,7 @@ struct dual #define SMASK_3PTY 0x00000008 #define SMASK_CALL_FORWARDING 0x00000010 #define SMASK_CALL_DEFLECTION 0x00000020 +#define SMASK_MCID 0x00000040 #define SMASK_CCBS 0x00000080 #define SMASK_MWI 0x00000100 #define SMASK_CCNR 0x00000200 @@ -406,6 +426,8 @@ struct dual #define RTPL2_IN 13 /* RTP layer-2 protocol, incomming */ #define RTPL2 14 /* RTP layer-2 protocol */ #define V120_V42BIS 15 /* V.120 asynchronous mode supporting V.42bis compression */ +#define LISTENER 27 /* Layer 2 to listen line */ +#define MTP2 28 /* MTP2 Layer 2 */ #define PIAFS_CRC 29 /* PIAFS Layer 2 with CRC calculation at L2 */ /* ------------------------------------------------------ PIAFS DLC DEFINITIONS @@ -506,6 +528,22 @@ Byte | 8 7 6 5 4 3 2 1 | | | data transfer. | +---------------------+------+-----------------------------------------+ */ +/* ------------------------------------------------------ + LISTENER DLC DEFINITIONS + ------------------------------------------------------ */ +#define LISTENER_FEATURE_MASK_CUMMULATIVE 0x0001 +/* ------------------------------------------------------ + LISTENER META-FRAME CODE/PRIMITIVE DEFINITIONS + ------------------------------------------------------ */ +#define META_CODE_LL_UDATA_RX 0x01 +#define META_CODE_LL_UDATA_TX 0x02 +#define META_CODE_LL_DATA_RX 0x03 +#define META_CODE_LL_DATA_TX 0x04 +#define META_CODE_LL_MDATA_RX 0x05 +#define META_CODE_LL_MDATA_TX 0x06 +#define META_CODE_EMPTY 0x10 +#define META_CODE_LOST_FRAMES 0x11 +#define META_FLAG_TRUNCATED 0x0001 /*------------------------------------------------------------------*/ /* CAPI-like profile to indicate features on LAW_REQ */ /*------------------------------------------------------------------*/ @@ -577,6 +615,14 @@ Byte | 8 7 6 5 4 3 2 1 #define MANUFACTURER_FEATURE_DMACONNECT 0x04000000L #define MANUFACTURER_FEATURE_AUDIO_TAP 0x08000000L #define MANUFACTURER_FEATURE_FAX_NONSTANDARD 0x10000000L +#define MANUFACTURER_FEATURE_SS7 0x20000000L +#define MANUFACTURER_FEATURE_MADAPTER 0x40000000L +#define MANUFACTURER_FEATURE_MEASURE 0x80000000L +#define MANUFACTURER_FEATURE2_LISTENING 0x00000001L +#define MANUFACTURER_FEATURE2_SS_DIFFCONTPOSSIBLE 0x00000002L +#define MANUFACTURER_FEATURE2_GENERIC_TONE 0x00000004L +#define MANUFACTURER_FEATURE2_COLOR_FAX 0x00000008L +#define MANUFACTURER_FEATURE2_SS_ECT_DIFFCONTPOSSIBLE 0x00000010L #define RTP_PRIM_PAYLOAD_PCMU_8000 0 #define RTP_PRIM_PAYLOAD_1016_8000 1 #define RTP_PRIM_PAYLOAD_G726_32_8000 2 @@ -624,6 +670,15 @@ Byte | 8 7 6 5 4 3 2 1 #define VSINVOKEID 4 #define VSCLMRKS 5 #define VSTBCTIDENT 6 +#define VSETSILINKID 7 +#define VSSAMECONTROLLER 8 +/* Errorcodes for VSETSILINKID begin */ +#define VSETSILINKIDRRWC 1 +#define VSETSILINKIDREJECT 2 +#define VSETSILINKIDTIMEOUT 3 +#define VSETSILINKIDFAILCOUNT 4 +#define VSETSILINKIDERROR 5 +/* Errorcodes for VSETSILINKID end */ /* -----------------------------------------------------------** ** The PROTOCOL_FEATURE_STRING in feature.h (included ** ** in prstart.sx and astart.sx) defines capabilities and ** @@ -647,5 +702,37 @@ Byte | 8 7 6 5 4 3 2 1 #define PROTCAP_FREE13 0x2000 /* not used */ #define PROTCAP_FREE14 0x4000 /* not used */ #define PROTCAP_EXTENSION 0x8000 /* used for future extentions */ +/* -----------------------------------------------------------* */ +/* Onhook data transmission ETS30065901 */ +/* Message Type */ +/*#define RESERVED4 0x4*/ +#define CALL_SETUP 0x80 +#define MESSAGE_WAITING_INDICATOR 0x82 +/*#define RESERVED84 0x84*/ +/*#define RESERVED85 0x85*/ +#define ADVICE_OF_CHARGE 0x86 +/*1111 0001 +to +1111 1111 +F1H - Reserved for network operator use +to +FFH*/ +/* Parameter Types */ +#define DATE_AND_TIME 1 +#define CLI_PARAMETER_TYPE 2 +#define CALLED_DIRECTORY_NUMBER_PARAMETER_TYPE 3 +#define REASON_FOR_ABSENCE_OF_CLI_PARAMETER_TYPE 4 +#define NAME_PARAMETER_TYPE 7 +#define REASON_FOR_ABSENCE_OF_CALLING_PARTY_NAME_PARAMETER_TYPE 8 +#define VISUAL_INDICATOR_PARAMETER_TYPE 0xb +#define COMPLEMENTARY_CLI_PARAMETER_TYPE 0x10 +#define CALL_TYPE_PARAMETER_TYPE 0x11 +#define FIRST_CALLED_LINE_DIRECTORY_NUMBER_PARAMETER_TYPE 0x12 +#define NETWORK_MESSAGE_SYSTEM_STATUS_PARAMETER_TYPE 0x13 +#define FORWARDED_CALL_TYPE_PARAMETER_TYPE 0x15 +#define TYPE_OF_CALLING_USER_PARAMETER_TYPE 0x16 +#define REDIRECTING_NUMBER_PARAMETER_TYPE 0x1a +#define EXTENSION_FOR_NETWORK_OPERATOR_USE_PARAMETER_TYPE 0xe0 +/* -----------------------------------------------------------* */ #else #endif /* PC_H_INCLUDED } */ diff --git a/drivers/isdn/hardware/eicon/platform.h b/drivers/isdn/hardware/eicon/platform.h index 31299feb7..0457682b0 100644 --- a/drivers/isdn/hardware/eicon/platform.h +++ b/drivers/isdn/hardware/eicon/platform.h @@ -1,4 +1,4 @@ -/* $Id: platform.h,v 1.37.4.1 2004/07/28 14:47:21 armin Exp $ +/* $Id: platform.h,v 1.37.4.2 2004/08/28 20:03:53 armin Exp $ * * platform.h * @@ -269,20 +269,6 @@ static __inline__ void diva_os_leave_spin_lock (diva_os_spin_lock_t* a, \ diva_os_spin_lock_magic_t* old_irql, \ void* dbg) { spin_unlock_bh(a); } -static __inline__ void diva_os_enter_spin_lock_hard (diva_os_spin_lock_t* a, \ - diva_os_spin_lock_magic_t* old_irql, \ - void* dbg) { \ - unsigned long flags; \ - spin_lock_irqsave (a, flags); \ - *old_irql = (diva_os_spin_lock_magic_t)flags; \ -} -static __inline__ void diva_os_leave_spin_lock_hard (diva_os_spin_lock_t* a, \ - diva_os_spin_lock_magic_t* old_irql, \ - void* dbg) { \ - unsigned long flags = (unsigned long)*old_irql; \ - spin_unlock_irqrestore (a, flags); \ -} - #define diva_os_destroy_spin_lock(a,b) do { } while(0) /* @@ -347,12 +333,18 @@ diva_os_atomic_decrement(diva_os_atomic_t* pv) #define DIVA_IDI_RX_DMA 1 +/* +** endian macros +*/ #define READ_WORD(addr) readw(addr) #define READ_DWORD(addr) readl(addr) #define WRITE_WORD(addr,v) writew(v,addr) #define WRITE_DWORD(addr,v) writel(v,addr) +/* +** 32/64 bit macors +*/ #ifdef BITS_PER_LONG #if BITS_PER_LONG > 32 #define PLATFORM_GT_32BIT @@ -360,8 +352,23 @@ diva_os_atomic_decrement(diva_os_atomic_t* pv) #endif #endif +/* +** undef os definitions of macros we use +*/ #undef ID_MASK #undef N_DATA #undef ADDR +/* +** dump file +*/ +#define diva_os_dump_file_t char +#define diva_os_board_trace_t char +#define diva_os_dump_file(__x__) do { } while(0) + +/* +** size of internal arrays +*/ +#define MAX_DESCRIPTORS 64 + #endif /* __PLATFORM_H__ */ diff --git a/drivers/isdn/hisax/avm_pci.c b/drivers/isdn/hisax/avm_pci.c index 235f28a46..3a6acdead 100644 --- a/drivers/isdn/hisax/avm_pci.c +++ b/drivers/isdn/hisax/avm_pci.c @@ -752,70 +752,70 @@ setup_avm_pcipnp(struct IsdnCard *card) cs->hw.avm.cfg_reg = card->para[1]; cs->irq = card->para[0]; cs->subtyp = AVM_FRITZ_PNP; - } else { + goto ready; + } #ifdef __ISAPNP__ - if (isapnp_present()) { - struct pnp_dev *pnp_avm_d = NULL; - if ((pnp_avm_c = pnp_find_card( + if (isapnp_present()) { + struct pnp_dev *pnp_avm_d = NULL; + if ((pnp_avm_c = pnp_find_card( + ISAPNP_VENDOR('A', 'V', 'M'), + ISAPNP_FUNCTION(0x0900), pnp_avm_c))) { + if ((pnp_avm_d = pnp_find_dev(pnp_avm_c, ISAPNP_VENDOR('A', 'V', 'M'), - ISAPNP_FUNCTION(0x0900), pnp_avm_c))) { - if ((pnp_avm_d = pnp_find_dev(pnp_avm_c, - ISAPNP_VENDOR('A', 'V', 'M'), - ISAPNP_FUNCTION(0x0900), pnp_avm_d))) { - int err; - - pnp_disable_dev(pnp_avm_d); - err = pnp_activate_dev(pnp_avm_d); - if (err<0) { - printk(KERN_WARNING "%s: pnp_activate_dev ret(%d)\n", - __FUNCTION__, err); - return(0); - } - cs->hw.avm.cfg_reg = - pnp_port_start(pnp_avm_d, 0); - cs->irq = pnp_irq(pnp_avm_d, 0); - if (!cs->irq) { - printk(KERN_ERR "FritzPnP:No IRQ\n"); - return(0); - } - if (!cs->hw.avm.cfg_reg) { - printk(KERN_ERR "FritzPnP:No IO address\n"); - return(0); - } - cs->subtyp = AVM_FRITZ_PNP; - goto ready; + ISAPNP_FUNCTION(0x0900), pnp_avm_d))) { + int err; + + pnp_disable_dev(pnp_avm_d); + err = pnp_activate_dev(pnp_avm_d); + if (err<0) { + printk(KERN_WARNING "%s: pnp_activate_dev ret(%d)\n", + __FUNCTION__, err); + return(0); + } + cs->hw.avm.cfg_reg = + pnp_port_start(pnp_avm_d, 0); + cs->irq = pnp_irq(pnp_avm_d, 0); + if (!cs->irq) { + printk(KERN_ERR "FritzPnP:No IRQ\n"); + return(0); } + if (!cs->hw.avm.cfg_reg) { + printk(KERN_ERR "FritzPnP:No IO address\n"); + return(0); + } + cs->subtyp = AVM_FRITZ_PNP; + goto ready; } - } else { - printk(KERN_INFO "FritzPnP: no ISA PnP present\n"); } + } else { + printk(KERN_INFO "FritzPnP: no ISA PnP present\n"); + } #endif #ifdef CONFIG_PCI - if ((dev_avm = pci_find_device(PCI_VENDOR_ID_AVM, - PCI_DEVICE_ID_AVM_A1, dev_avm))) { - cs->irq = dev_avm->irq; - if (!cs->irq) { - printk(KERN_ERR "FritzPCI: No IRQ for PCI card found\n"); - return(0); - } - if (pci_enable_device(dev_avm)) - return(0); - cs->hw.avm.cfg_reg = pci_resource_start(dev_avm, 1); - if (!cs->hw.avm.cfg_reg) { - printk(KERN_ERR "FritzPCI: No IO-Adr for PCI card found\n"); - return(0); - } - cs->subtyp = AVM_FRITZ_PCI; - } else { - printk(KERN_WARNING "FritzPCI: No PCI card found\n"); + if ((dev_avm = pci_find_device(PCI_VENDOR_ID_AVM, + PCI_DEVICE_ID_AVM_A1, dev_avm))) { + cs->irq = dev_avm->irq; + if (!cs->irq) { + printk(KERN_ERR "FritzPCI: No IRQ for PCI card found\n"); + return(0); + } + if (pci_enable_device(dev_avm)) + return(0); + cs->hw.avm.cfg_reg = pci_resource_start(dev_avm, 1); + if (!cs->hw.avm.cfg_reg) { + printk(KERN_ERR "FritzPCI: No IO-Adr for PCI card found\n"); return(0); } - cs->irq_flags |= SA_SHIRQ; + cs->subtyp = AVM_FRITZ_PCI; + } else { + printk(KERN_WARNING "FritzPCI: No PCI card found\n"); + return(0); + } + cs->irq_flags |= SA_SHIRQ; #else - printk(KERN_WARNING "FritzPCI: NO_PCI_BIOS\n"); - return (0); + printk(KERN_WARNING "FritzPCI: NO_PCI_BIOS\n"); + return (0); #endif /* CONFIG_PCI */ - } ready: cs->hw.avm.isac = cs->hw.avm.cfg_reg + 0x10; if (!request_region(cs->hw.avm.cfg_reg, 32, diff --git a/drivers/isdn/hisax/bkm_a4t.c b/drivers/isdn/hisax/bkm_a4t.c index f6a21dffa..f410f628a 100644 --- a/drivers/isdn/hisax/bkm_a4t.c +++ b/drivers/isdn/hisax/bkm_a4t.c @@ -265,7 +265,7 @@ setup_bkm_a4t(struct IsdnCard *card) char tmp[64]; u_int pci_memaddr = 0, found = 0; I20_REGISTER_FILE *pI20_Regs; -#if CONFIG_PCI +#ifdef CONFIG_PCI #endif strcpy(tmp, bkm_a4t_revision); @@ -275,7 +275,7 @@ setup_bkm_a4t(struct IsdnCard *card) } else return (0); -#if CONFIG_PCI +#ifdef CONFIG_PCI while ((dev_a4t = pci_find_device(PCI_VENDOR_ID_ZORAN, PCI_DEVICE_ID_ZORAN_36120, dev_a4t))) { u16 sub_sys; diff --git a/drivers/isdn/hisax/bkm_a8.c b/drivers/isdn/hisax/bkm_a8.c index 0c32d3700..94bb83ce7 100644 --- a/drivers/isdn/hisax/bkm_a8.c +++ b/drivers/isdn/hisax/bkm_a8.c @@ -21,7 +21,7 @@ #include #include "bkm_ax.h" -#if CONFIG_PCI +#ifdef CONFIG_PCI #define ATTEMPT_PCI_REMAPPING /* Required for PLX rev 1 */ @@ -285,7 +285,7 @@ static u_char pci_irq __initdata = 0; int __init setup_sct_quadro(struct IsdnCard *card) { -#if CONFIG_PCI +#ifdef CONFIG_PCI struct IsdnCardState *cs = card->cs; char tmp[64]; u_char pci_rev_id; diff --git a/drivers/isdn/hisax/elsa.c b/drivers/isdn/hisax/elsa.c index b33678129..3b526d704 100644 --- a/drivers/isdn/hisax/elsa.c +++ b/drivers/isdn/hisax/elsa.c @@ -1022,7 +1022,7 @@ setup_elsa(struct IsdnCard *card) cs->hw.elsa.base, cs->irq); } else if (cs->typ == ISDN_CTYPE_ELSA_PCI) { -#if CONFIG_PCI +#ifdef CONFIG_PCI cs->subtyp = 0; if ((dev_qs1000 = pci_find_device(PCI_VENDOR_ID_ELSA, PCI_DEVICE_ID_ELSA_MICROLINK, dev_qs1000))) { diff --git a/drivers/isdn/hisax/enternow_pci.c b/drivers/isdn/hisax/enternow_pci.c index a7acf9bd6..1cc4d11e0 100644 --- a/drivers/isdn/hisax/enternow_pci.c +++ b/drivers/isdn/hisax/enternow_pci.c @@ -299,7 +299,7 @@ setup_enternow_pci(struct IsdnCard *card) struct IsdnCardState *cs = card->cs; char tmp[64]; -#if CONFIG_PCI +#ifdef CONFIG_PCI #ifdef __BIG_ENDIAN #error "not running on big endian machines now" #endif diff --git a/drivers/isdn/hisax/gazel.c b/drivers/isdn/hisax/gazel.c index 0d5931798..24a05a43f 100644 --- a/drivers/isdn/hisax/gazel.c +++ b/drivers/isdn/hisax/gazel.c @@ -634,7 +634,7 @@ setup_gazel(struct IsdnCard *card) return (0); } else { -#if CONFIG_PCI +#ifdef CONFIG_PCI if (setup_gazelpci(cs)) return (0); #else diff --git a/drivers/isdn/hisax/hfc_pci.c b/drivers/isdn/hisax/hfc_pci.c index 20042fda8..3946536d1 100644 --- a/drivers/isdn/hisax/hfc_pci.c +++ b/drivers/isdn/hisax/hfc_pci.c @@ -65,7 +65,7 @@ static const PCI_ENTRY id_list[] = }; -#if CONFIG_PCI +#ifdef CONFIG_PCI /******************************************/ /* free hardware resources used by driver */ @@ -1655,7 +1655,7 @@ setup_hfcpci(struct IsdnCard *card) #endif strcpy(tmp, hfcpci_revision); printk(KERN_INFO "HiSax: HFC-PCI driver Rev. %s\n", HiSax_getrev(tmp)); -#if CONFIG_PCI +#ifdef CONFIG_PCI cs->hw.hfcpci.int_s1 = 0; cs->dc.hfcpci.ph_state = 0; cs->hw.hfcpci.fifo = 255; diff --git a/drivers/isdn/hisax/niccy.c b/drivers/isdn/hisax/niccy.c index 88cce430c..cf77d8360 100644 --- a/drivers/isdn/hisax/niccy.c +++ b/drivers/isdn/hisax/niccy.c @@ -309,7 +309,7 @@ setup_niccy(struct IsdnCard *card) return (0); } } else { -#if CONFIG_PCI +#ifdef CONFIG_PCI u_int pci_ioaddr; cs->subtyp = 0; if ((niccy_dev = pci_find_device(PCI_VENDOR_ID_SATSAGEM, diff --git a/drivers/isdn/hisax/nj_s.c b/drivers/isdn/hisax/nj_s.c index 8b5822c42..fd664697f 100644 --- a/drivers/isdn/hisax/nj_s.c +++ b/drivers/isdn/hisax/nj_s.c @@ -167,7 +167,7 @@ setup_netjet_s(struct IsdnCard *card) return(0); test_and_clear_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags); -#if CONFIG_PCI +#ifdef CONFIG_PCI for ( ;; ) { diff --git a/drivers/isdn/hisax/nj_u.c b/drivers/isdn/hisax/nj_u.c index efb5d8fbf..3d6441e96 100644 --- a/drivers/isdn/hisax/nj_u.c +++ b/drivers/isdn/hisax/nj_u.c @@ -137,7 +137,7 @@ setup_netjet_u(struct IsdnCard *card) int bytecnt; struct IsdnCardState *cs = card->cs; char tmp[64]; -#if CONFIG_PCI +#ifdef CONFIG_PCI #endif #ifdef __BIG_ENDIAN #error "not running on big endian machines now" @@ -148,7 +148,7 @@ setup_netjet_u(struct IsdnCard *card) return(0); test_and_clear_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags); -#if CONFIG_PCI +#ifdef CONFIG_PCI for ( ;; ) { diff --git a/drivers/isdn/hisax/sedlbauer.c b/drivers/isdn/hisax/sedlbauer.c index 863bfa50b..3393370ce 100644 --- a/drivers/isdn/hisax/sedlbauer.c +++ b/drivers/isdn/hisax/sedlbauer.c @@ -618,7 +618,7 @@ setup_sedlbauer(struct IsdnCard *card) } #endif /* Probe for Sedlbauer speed pci */ -#if CONFIG_PCI +#ifdef CONFIG_PCI if ((dev_sedl = pci_find_device(PCI_VENDOR_ID_TIGERJET, PCI_DEVICE_ID_TIGERJET_100, dev_sedl))) { if (pci_enable_device(dev_sedl)) diff --git a/drivers/isdn/hisax/telespci.c b/drivers/isdn/hisax/telespci.c index 5e305554d..7713c467e 100644 --- a/drivers/isdn/hisax/telespci.c +++ b/drivers/isdn/hisax/telespci.c @@ -300,7 +300,7 @@ setup_telespci(struct IsdnCard *card) printk(KERN_INFO "HiSax: Teles/PCI driver Rev. %s\n", HiSax_getrev(tmp)); if (cs->typ != ISDN_CTYPE_TELESPCI) return (0); -#if CONFIG_PCI +#ifdef CONFIG_PCI if ((dev_tel = pci_find_device (PCI_VENDOR_ID_ZORAN, PCI_DEVICE_ID_ZORAN_36120, dev_tel))) { if (pci_enable_device(dev_tel)) return(0); diff --git a/drivers/isdn/hisax/w6692.c b/drivers/isdn/hisax/w6692.c index cfe876043..36cfc4a48 100644 --- a/drivers/isdn/hisax/w6692.c +++ b/drivers/isdn/hisax/w6692.c @@ -1012,7 +1012,7 @@ setup_w6692(struct IsdnCard *card) printk(KERN_INFO "HiSax: W6692 driver Rev. %s\n", HiSax_getrev(tmp)); if (cs->typ != ISDN_CTYPE_W6692) return (0); -#if CONFIG_PCI +#ifdef CONFIG_PCI while (id_list[id_idx].vendor_id) { dev_w6692 = pci_find_device(id_list[id_idx].vendor_id, id_list[id_idx].device_id, diff --git a/drivers/isdn/tpam/tpam_main.c b/drivers/isdn/tpam/tpam_main.c index 91392c469..b418d37c7 100644 --- a/drivers/isdn/tpam/tpam_main.c +++ b/drivers/isdn/tpam/tpam_main.c @@ -23,7 +23,7 @@ /* Local functions prototypes */ static int __devinit tpam_probe(struct pci_dev *, const struct pci_device_id *); -static void __devexit tpam_unregister_card(tpam_card *); +static void __devexit tpam_unregister_card(struct pci_dev *, tpam_card *); static void __devexit tpam_remove(struct pci_dev *); static int __init tpam_init(void); static void __exit tpam_exit(void); @@ -86,13 +86,20 @@ u32 tpam_findchannel(tpam_card *card, u32 ncoid) { */ static int __devinit tpam_probe(struct pci_dev *dev, const struct pci_device_id *pci_id) { tpam_card *card, *c; - int i; + int i, err; + + if (pci_enable_device(dev)) { + printk(KERN_ERR "TurboPAM: can't enable PCI device at %s\n", + pci_name(dev)); + return -ENODEV; + } /* allocate memory for the board structure */ if (!(card = (tpam_card *)kmalloc(sizeof(tpam_card), GFP_KERNEL))) { printk(KERN_ERR "TurboPAM: tpam_register_card: " "kmalloc failed!\n"); - return -ENOMEM; + err = -ENOMEM; + goto err_out_disable_dev; } memset((char *)card, 0, sizeof(tpam_card)); @@ -106,8 +113,8 @@ static int __devinit tpam_probe(struct pci_dev *dev, const struct pci_device_id card->interface.id, card)) { printk(KERN_ERR "TurboPAM: tpam_register_card: " "could not request irq %d\n", card->irq); - kfree(card); - return -EIO; + err = -EIO; + goto err_out_free_card; } /* remap board memory */ @@ -115,9 +122,8 @@ static int __devinit tpam_probe(struct pci_dev *dev, const struct pci_device_id 0x800000))) { printk(KERN_ERR "TurboPAM: tpam_register_card: " "unable to remap bar0\n"); - free_irq(card->irq, card); - kfree(card); - return -EIO; + err = -EIO; + goto err_out_free_irq; } /* reset the board */ @@ -150,10 +156,8 @@ static int __devinit tpam_probe(struct pci_dev *dev, const struct pci_device_id if (!register_isdn(&card->interface)) { printk(KERN_ERR "TurboPAM: tpam_register_card: " "unable to register %s\n", card->interface.id); - free_irq(card->irq, card); - iounmap((void *)card->bar0); - kfree(card); - return -EIO; + err = -EIO; + goto err_out_iounmap; } card->id = card->interface.channels; @@ -195,6 +199,19 @@ static int __devinit tpam_probe(struct pci_dev *dev, const struct pci_device_id pci_set_drvdata(dev, card); return 0; + +err_out_iounmap: + iounmap((void *)card->bar0); + +err_out_free_irq: + free_irq(card->irq, card); + +err_out_free_card: + kfree(card); + +err_out_disable_dev: + pci_disable_device(dev); + return err; } /* @@ -202,7 +219,7 @@ static int __devinit tpam_probe(struct pci_dev *dev, const struct pci_device_id * * card: the board. */ -static void __devexit tpam_unregister_card(tpam_card *card) { +static void __devexit tpam_unregister_card(struct pci_dev *pcidev, tpam_card *card) { isdn_ctrl cmd; /* prevent the ISDN link layer that the driver will be unloaded */ @@ -215,6 +232,8 @@ static void __devexit tpam_unregister_card(tpam_card *card) { /* release mapped memory */ iounmap((void *)card->bar0); + + pci_disable_device(pcidev); } /* @@ -235,7 +254,7 @@ static void __devexit tpam_remove(struct pci_dev *pcidev) { } /* unregister each board */ - tpam_unregister_card(card); + tpam_unregister_card(pcidev, card); /* and free the board structure itself */ kfree(card); diff --git a/drivers/macintosh/adbhid.c b/drivers/macintosh/adbhid.c index c0ab8589b..d3b8f819e 100644 --- a/drivers/macintosh/adbhid.c +++ b/drivers/macintosh/adbhid.c @@ -74,15 +74,135 @@ static struct notifier_block adbhid_adb_notifier = { #define ADB_KEY_POWER_OLD 0x7e #define ADB_KEY_POWER 0x7f -unsigned char adb_to_linux_keycodes[128] = { - 30, 31, 32, 33, 35, 34, 44, 45, 46, 47, 86, 48, 16, 17, 18, 19, - 21, 20, 2, 3, 4, 5, 7, 6, 13, 10, 8, 12, 9, 11, 27, 24, - 22, 26, 23, 25, 28, 38, 36, 40, 37, 39, 43, 51, 53, 49, 50, 52, - 15, 57, 41, 14, 96, 1, 29,125, 42, 58, 56,105,106,108,103, 0, - 0, 83, 0, 55, 0, 78, 0, 69, 0, 0, 0, 98, 96, 0, 74, 0, - 0,117, 82, 79, 80, 81, 75, 76, 77, 71, 0, 72, 73,124, 89,121, - 63, 64, 65, 61, 66, 67,123, 87,122, 99, 0, 70, 0, 68,101, 88, - 0,119,110,102,104,111, 62,107, 60,109, 59, 54,100, 97,126,116 +u8 adb_to_linux_keycodes[128] = { + /* 0x00 */ KEY_A, /* 30 */ + /* 0x01 */ KEY_S, /* 31 */ + /* 0x02 */ KEY_D, /* 32 */ + /* 0x03 */ KEY_F, /* 33 */ + /* 0x04 */ KEY_H, /* 35 */ + /* 0x05 */ KEY_G, /* 34 */ + /* 0x06 */ KEY_Z, /* 44 */ + /* 0x07 */ KEY_X, /* 45 */ + /* 0x08 */ KEY_C, /* 46 */ + /* 0x09 */ KEY_V, /* 47 */ + /* 0x0a */ KEY_102ND, /* 86 */ + /* 0x0b */ KEY_B, /* 48 */ + /* 0x0c */ KEY_Q, /* 16 */ + /* 0x0d */ KEY_W, /* 17 */ + /* 0x0e */ KEY_E, /* 18 */ + /* 0x0f */ KEY_R, /* 19 */ + /* 0x10 */ KEY_Y, /* 21 */ + /* 0x11 */ KEY_T, /* 20 */ + /* 0x12 */ KEY_1, /* 2 */ + /* 0x13 */ KEY_2, /* 3 */ + /* 0x14 */ KEY_3, /* 4 */ + /* 0x15 */ KEY_4, /* 5 */ + /* 0x16 */ KEY_6, /* 7 */ + /* 0x17 */ KEY_5, /* 6 */ + /* 0x18 */ KEY_EQUAL, /* 13 */ + /* 0x19 */ KEY_9, /* 10 */ + /* 0x1a */ KEY_7, /* 8 */ + /* 0x1b */ KEY_MINUS, /* 12 */ + /* 0x1c */ KEY_8, /* 9 */ + /* 0x1d */ KEY_0, /* 11 */ + /* 0x1e */ KEY_RIGHTBRACE, /* 27 */ + /* 0x1f */ KEY_O, /* 24 */ + /* 0x20 */ KEY_U, /* 22 */ + /* 0x21 */ KEY_LEFTBRACE, /* 26 */ + /* 0x22 */ KEY_I, /* 23 */ + /* 0x23 */ KEY_P, /* 25 */ + /* 0x24 */ KEY_ENTER, /* 28 */ + /* 0x25 */ KEY_L, /* 38 */ + /* 0x26 */ KEY_J, /* 36 */ + /* 0x27 */ KEY_APOSTROPHE, /* 40 */ + /* 0x28 */ KEY_K, /* 37 */ + /* 0x29 */ KEY_SEMICOLON, /* 39 */ + /* 0x2a */ KEY_BACKSLASH, /* 43 */ + /* 0x2b */ KEY_COMMA, /* 51 */ + /* 0x2c */ KEY_SLASH, /* 53 */ + /* 0x2d */ KEY_N, /* 49 */ + /* 0x2e */ KEY_M, /* 50 */ + /* 0x2f */ KEY_DOT, /* 52 */ + /* 0x30 */ KEY_TAB, /* 15 */ + /* 0x31 */ KEY_SPACE, /* 57 */ + /* 0x32 */ KEY_GRAVE, /* 41 */ + /* 0x33 */ KEY_BACKSPACE, /* 14 */ + /* 0x34 */ KEY_KPENTER, /* 96 */ + /* 0x35 */ KEY_ESC, /* 1 */ + /* 0x36 */ KEY_LEFTCTRL, /* 29 */ + /* 0x37 */ KEY_LEFTMETA, /* 125 */ + /* 0x38 */ KEY_LEFTSHIFT, /* 42 */ + /* 0x39 */ KEY_CAPSLOCK, /* 58 */ + /* 0x3a */ KEY_LEFTALT, /* 56 */ + /* 0x3b */ KEY_LEFT, /* 105 */ + /* 0x3c */ KEY_RIGHT, /* 106 */ + /* 0x3d */ KEY_DOWN, /* 108 */ + /* 0x3e */ KEY_UP, /* 103 */ + /* 0x3f */ 0, + /* 0x40 */ 0, + /* 0x41 */ KEY_KPDOT, /* 83 */ + /* 0x42 */ 0, + /* 0x43 */ KEY_KPASTERISK, /* 55 */ + /* 0x44 */ 0, + /* 0x45 */ KEY_KPPLUS, /* 78 */ + /* 0x46 */ 0, + /* 0x47 */ KEY_NUMLOCK, /* 69 */ + /* 0x48 */ 0, + /* 0x49 */ 0, + /* 0x4a */ 0, + /* 0x4b */ KEY_KPSLASH, /* 98 */ + /* 0x4c */ KEY_KPENTER, /* 96 */ + /* 0x4d */ 0, + /* 0x4e */ KEY_KPMINUS, /* 74 */ + /* 0x4f */ 0, + /* 0x50 */ 0, + /* 0x51 */ KEY_KPEQUAL, /* 117 */ + /* 0x52 */ KEY_KP0, /* 82 */ + /* 0x53 */ KEY_KP1, /* 79 */ + /* 0x54 */ KEY_KP2, /* 80 */ + /* 0x55 */ KEY_KP3, /* 81 */ + /* 0x56 */ KEY_KP4, /* 75 */ + /* 0x57 */ KEY_KP5, /* 76 */ + /* 0x58 */ KEY_KP6, /* 77 */ + /* 0x59 */ KEY_KP7, /* 71 */ + /* 0x5a */ 0, + /* 0x5b */ KEY_KP8, /* 72 */ + /* 0x5c */ KEY_KP9, /* 73 */ + /* 0x5d */ KEY_YEN, /* 124 */ + /* 0x5e */ KEY_RO, /* 89 */ + /* 0x5f */ KEY_KPCOMMA, /* 121 */ + /* 0x60 */ KEY_F5, /* 63 */ + /* 0x61 */ KEY_F6, /* 64 */ + /* 0x62 */ KEY_F7, /* 65 */ + /* 0x63 */ KEY_F3, /* 61 */ + /* 0x64 */ KEY_F8, /* 66 */ + /* 0x65 */ KEY_F9, /* 67 */ + /* 0x66 */ KEY_HANJA, /* 123 */ + /* 0x67 */ KEY_F11, /* 87 */ + /* 0x68 */ KEY_HANGUEL, /* 122 */ + /* 0x69 */ KEY_SYSRQ, /* 99 */ + /* 0x6a */ 0, + /* 0x6b */ KEY_SCROLLLOCK, /* 70 */ + /* 0x6c */ 0, + /* 0x6d */ KEY_F10, /* 68 */ + /* 0x6e */ KEY_COMPOSE, /* 127 */ + /* 0x6f */ KEY_F12, /* 88 */ + /* 0x70 */ 0, + /* 0x71 */ KEY_PAUSE, /* 119 */ + /* 0x72 */ KEY_INSERT, /* 110 */ + /* 0x73 */ KEY_HOME, /* 102 */ + /* 0x74 */ KEY_PAGEUP, /* 104 */ + /* 0x75 */ KEY_DELETE, /* 111 */ + /* 0x76 */ KEY_F4, /* 62 */ + /* 0x77 */ KEY_END, /* 107 */ + /* 0x78 */ KEY_F2, /* 60 */ + /* 0x79 */ KEY_PAGEDOWN, /* 109 */ + /* 0x7a */ KEY_F1, /* 59 */ + /* 0x7b */ KEY_RIGHTSHIFT, /* 54 */ + /* 0x7c */ KEY_RIGHTALT, /* 100 */ + /* 0x7d */ KEY_RIGHTCTRL, /* 97 */ + /* 0x7e */ KEY_RIGHTMETA, /* 126 */ + /* 0x7f */ KEY_POWER, /* 116 */ }; struct adbhid { @@ -326,7 +446,7 @@ adbhid_mouse_input(unsigned char *data, int nb, struct pt_regs *regs, int autopo input_report_key(&adbhid[id]->input, BTN_LEFT, !((data[1] >> 7) & 1)); input_report_key(&adbhid[id]->input, BTN_MIDDLE, !((data[2] >> 7) & 1)); - if (nb >= 4) + if (nb >= 4 && adbhid[id]->mouse_kind != ADBMOUSE_TRACKPAD) input_report_key(&adbhid[id]->input, BTN_RIGHT, !((data[3] >> 7) & 1)); input_report_rel(&adbhid[id]->input, REL_X, @@ -453,7 +573,7 @@ static spinlock_t leds_lock = SPIN_LOCK_UNLOCKED; static void leds_done(struct adb_request *req) { - int leds, device; + int leds = 0, device = 0, pending = 0; unsigned long flags; spin_lock_irqsave(&leds_lock, flags); @@ -464,11 +584,11 @@ static void leds_done(struct adb_request *req) leds_pending[device] = 0; pending_led_start++; pending_led_start = (pending_led_start < 16) ? pending_led_start : 0; + pending = leds_req_pending; } else leds_req_pending = 0; - spin_unlock_irqrestore(&leds_lock, flags); - if (leds_req_pending) + if (pending) adb_request(&led_request, leds_done, 0, 3, ADB_WRITEREG(device, KEYB_LEDREG), 0xff, ~leds); } diff --git a/drivers/macintosh/ans-lcd.c b/drivers/macintosh/ans-lcd.c index 0e130f598..26bdcd7da 100644 --- a/drivers/macintosh/ans-lcd.c +++ b/drivers/macintosh/ans-lcd.c @@ -9,6 +9,8 @@ #include #include #include +#include + #include #include #include @@ -154,7 +156,7 @@ anslcd_init(void) retval = misc_register(&anslcd_dev); if(retval < 0){ printk(KERN_INFO "LCD: misc_register failed\n"); - iounmap(anslcd_ptr); + iounmap((void *)anslcd_ptr); return retval; } @@ -177,7 +179,7 @@ static void __exit anslcd_exit(void) { misc_deregister(&anslcd_dev); - iounmap(anslcd_ptr); + iounmap((void *)anslcd_ptr); } module_init(anslcd_init); diff --git a/drivers/macintosh/macserial.c b/drivers/macintosh/macserial.c index 1d61a9af6..a3a5372f7 100644 --- a/drivers/macintosh/macserial.c +++ b/drivers/macintosh/macserial.c @@ -713,12 +713,8 @@ static void do_softint(void *private_) if (!tty) return; - if (test_and_clear_bit(RS_EVENT_WRITE_WAKEUP, &info->event)) { - if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && - tty->ldisc.write_wakeup) - (tty->ldisc.write_wakeup)(tty); - wake_up_interruptible(&tty->write_wait); - } + if (test_and_clear_bit(RS_EVENT_WRITE_WAKEUP, &info->event)) + tty_wakeup(tty); } static int startup(struct mac_serial * info) @@ -1564,10 +1560,7 @@ static void rs_flush_buffer(struct tty_struct *tty) spin_lock_irqsave(&info->lock, flags); info->xmit_cnt = info->xmit_head = info->xmit_tail = 0; spin_unlock_irqrestore(&info->lock, flags); - wake_up_interruptible(&tty->write_wait); - if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && - tty->ldisc.write_wakeup) - (tty->ldisc.write_wakeup)(tty); + tty_wakeup(tty); } /* @@ -1994,16 +1987,14 @@ static void rs_close(struct tty_struct *tty, struct file * filp) if (tty->driver->flush_buffer) tty->driver->flush_buffer(tty); - if (tty->ldisc.flush_buffer) - tty->ldisc.flush_buffer(tty); + tty_ldisc_flush(tty); tty->closing = 0; info->event = 0; info->tty = 0; if (info->blocked_open) { if (info->close_delay) { - current->state = TASK_INTERRUPTIBLE; - schedule_timeout(info->close_delay); + msleep_interruptible(jiffies_to_msecs(info->close_delay)); } wake_up_interruptible(&info->open_wait); } @@ -2048,8 +2039,7 @@ static void rs_wait_until_sent(struct tty_struct *tty, int timeout) if (timeout) char_time = min_t(unsigned long, char_time, timeout); while ((read_zsreg(info->zs_channel, 1) & ALL_SNT) == 0) { - current->state = TASK_INTERRUPTIBLE; - schedule_timeout(char_time); + msleep_interruptible(jiffies_to_msecs(char_time)); if (signal_pending(current)) break; if (timeout && time_after(jiffies, orig_jiffies + timeout)) diff --git a/drivers/macintosh/therm_windtunnel.c b/drivers/macintosh/therm_windtunnel.c index f4a1777c6..7dbc5cb4d 100644 --- a/drivers/macintosh/therm_windtunnel.c +++ b/drivers/macintosh/therm_windtunnel.c @@ -292,8 +292,7 @@ control_loop( void *dummy ) while( x.running ) { up( &x.lock ); - set_current_state(TASK_INTERRUPTIBLE); - schedule_timeout( 8*HZ ); + msleep_interruptible(8000); down( &x.lock ); poll_temp(); diff --git a/drivers/macintosh/via-pmu.c b/drivers/macintosh/via-pmu.c index 27e14a726..9f4147e3d 100644 --- a/drivers/macintosh/via-pmu.c +++ b/drivers/macintosh/via-pmu.c @@ -52,7 +52,6 @@ #include #include #include -#include #include #include #include @@ -138,7 +137,6 @@ static int data_len; static volatile int adb_int_pending; static volatile int disable_poll; static struct adb_request bright_req_1, bright_req_2; -static unsigned long async_req_locks; static struct device_node *vias; static int pmu_kind = PMU_UNKNOWN; static int pmu_fully_inited = 0; @@ -155,6 +153,7 @@ static int drop_interrupts; static int option_lid_wakeup = 1; static int sleep_in_progress; static int can_sleep; +static unsigned long async_req_locks; #endif /* CONFIG_PMAC_PBOOK */ static unsigned int pmu_irq_stats[11]; @@ -494,12 +493,9 @@ static int __init via_pmu_dev_init(void) /* Create /proc/pmu */ proc_pmu_root = proc_mkdir("pmu", NULL); if (proc_pmu_root) { - int i; - proc_pmu_info = create_proc_read_entry("info", 0, proc_pmu_root, - proc_get_info, NULL); - proc_pmu_irqstats = create_proc_read_entry("interrupts", 0, proc_pmu_root, - proc_get_irqstats, NULL); #ifdef CONFIG_PMAC_PBOOK + int i; + for (i=0; inlink = 1; @@ -746,6 +747,8 @@ done_battery_state_smart(struct adb_request* req) pmu_power_flags &= ~PMU_PWR_AC_PRESENT; + capa = max = amperage = voltage = 0; + if (req->reply[1] & 0x04) { bat_flags |= PMU_BATT_PRESENT; switch(req->reply[0]) { @@ -765,8 +768,7 @@ done_battery_state_smart(struct adb_request* req) req->reply_len, req->reply[0], req->reply[1], req->reply[2], req->reply[3]); break; } - } else - capa = max = amperage = voltage = 0; + } if ((req->reply[1] & 0x01) && (amperage > 0)) bat_flags |= PMU_BATT_CHARGING; @@ -1445,7 +1447,7 @@ static struct adb_request* __pmac pmu_sr_intr(struct pt_regs *regs) { struct adb_request *req; - int bite; + int bite = 0; if (via[B] & TREQ) { printk(KERN_ERR "PMU: spurious SR intr (%x)\n", via[B]); diff --git a/drivers/md/dm-io.c b/drivers/md/dm-io.c index 9f3fb61fd..cb7a43fde 100644 --- a/drivers/md/dm-io.c +++ b/drivers/md/dm-io.c @@ -267,7 +267,7 @@ static int resize_pool(unsigned int new_ios) /* create new pool */ _io_pool = mempool_create(new_ios, alloc_io, free_io, NULL); if (!_io_pool) - r = -ENOMEM; + return -ENOMEM; r = bio_set_init(&_bios, "dm-io", 512, 1); if (r) { diff --git a/drivers/md/dm-log.c b/drivers/md/dm-log.c index 7a1c77dee..43a12285d 100644 --- a/drivers/md/dm-log.c +++ b/drivers/md/dm-log.c @@ -140,6 +140,13 @@ struct log_c { int sync_search; + /* Resync flag */ + enum sync { + DEFAULTSYNC, /* Synchronize if necessary */ + NOSYNC, /* Devices known to be already in sync */ + FORCESYNC, /* Force a sync to happen */ + } sync; + /* * Disk log fields */ @@ -205,7 +212,8 @@ static int read_header(struct log_c *log) header_from_disk(&log->header, log->disk_header); - if (log->header.magic != MIRROR_MAGIC) { + /* New log required? */ + if (log->sync != DEFAULTSYNC || log->header.magic != MIRROR_MAGIC) { log->header.magic = MIRROR_MAGIC; log->header.version = MIRROR_DISK_VERSION; log->header.nr_regions = 0; @@ -273,22 +281,38 @@ static int write_bits(struct log_c *log) } /*---------------------------------------------------------------- - * constructor/destructor + * core log constructor/destructor + * + * argv contains region_size followed optionally by [no]sync *--------------------------------------------------------------*/ #define BYTE_SHIFT 3 static int core_ctr(struct dirty_log *log, struct dm_target *ti, unsigned int argc, char **argv) { + enum sync sync = DEFAULTSYNC; + struct log_c *lc; sector_t region_size; unsigned int region_count; size_t bitset_size; - if (argc != 1) { - DMWARN("wrong number of arguments to log_c"); + if (argc < 1 || argc > 2) { + DMWARN("wrong number of arguments to mirror log"); return -EINVAL; } + if (argc > 1) { + if (!strcmp(argv[1], "sync")) + sync = FORCESYNC; + else if (!strcmp(argv[1], "nosync")) + sync = NOSYNC; + else { + DMWARN("unrecognised sync argument to mirror log: %s", + argv[1]); + return -EINVAL; + } + } + if (sscanf(argv[0], SECTOR_FORMAT, ®ion_size) != 1) { DMWARN("invalid region size string"); return -EINVAL; @@ -306,6 +330,7 @@ static int core_ctr(struct dirty_log *log, struct dm_target *ti, lc->touched = 0; lc->region_size = region_size; lc->region_count = region_count; + lc->sync = sync; /* * Work out how many words we need to hold the bitset. @@ -330,8 +355,8 @@ static int core_ctr(struct dirty_log *log, struct dm_target *ti, kfree(lc); return -ENOMEM; } - memset(lc->sync_bits, 0, bitset_size); - lc->sync_count = 0; + memset(lc->sync_bits, (sync == NOSYNC) ? -1 : 0, bitset_size); + lc->sync_count = (sync == NOSYNC) ? region_count : 0; lc->recovering_bits = vmalloc(bitset_size); if (!lc->recovering_bits) { @@ -356,6 +381,11 @@ static void core_dtr(struct dirty_log *log) kfree(lc); } +/*---------------------------------------------------------------- + * disk log constructor/destructor + * + * argv contains log_device region_size followed optionally by [no]sync + *--------------------------------------------------------------*/ static int disk_ctr(struct dirty_log *log, struct dm_target *ti, unsigned int argc, char **argv) { @@ -364,8 +394,8 @@ static int disk_ctr(struct dirty_log *log, struct dm_target *ti, struct log_c *lc; struct dm_dev *dev; - if (argc != 2) { - DMWARN("wrong number of arguments to log_d"); + if (argc < 2 || argc > 3) { + DMWARN("wrong number of arguments to disk mirror log"); return -EINVAL; } @@ -452,10 +482,15 @@ static int disk_resume(struct dirty_log *log) if (r) return r; - /* zero any new bits if the mirror has grown */ - for (i = lc->header.nr_regions; i < lc->region_count; i++) - /* FIXME: amazingly inefficient */ - log_clear_bit(lc, lc->clean_bits, i); + /* set or clear any new bits */ + if (lc->sync == NOSYNC) + for (i = lc->header.nr_regions; i < lc->region_count; i++) + /* FIXME: amazingly inefficient */ + log_set_bit(lc, lc->clean_bits, i); + else + for (i = lc->header.nr_regions; i < lc->region_count; i++) + /* FIXME: amazingly inefficient */ + log_clear_bit(lc, lc->clean_bits, i); /* copy clean across to sync */ memcpy(lc->sync_bits, lc->clean_bits, size); @@ -566,6 +601,51 @@ static region_t core_get_sync_count(struct dirty_log *log) return lc->sync_count; } +#define DMEMIT_SYNC \ + if (lc->sync != DEFAULTSYNC) \ + DMEMIT("%ssync ", lc->sync == NOSYNC ? "no" : "") + +static int core_status(struct dirty_log *log, status_type_t status, + char *result, unsigned int maxlen) +{ + int sz = 0; + struct log_c *lc = log->context; + + switch(status) { + case STATUSTYPE_INFO: + break; + + case STATUSTYPE_TABLE: + DMEMIT("%s %u " SECTOR_FORMAT " ", log->type->name, + lc->sync == DEFAULTSYNC ? 1 : 2, lc->region_size); + DMEMIT_SYNC; + } + + return sz; +} + +static int disk_status(struct dirty_log *log, status_type_t status, + char *result, unsigned int maxlen) +{ + int sz = 0; + char buffer[16]; + struct log_c *lc = log->context; + + switch(status) { + case STATUSTYPE_INFO: + break; + + case STATUSTYPE_TABLE: + format_dev_t(buffer, lc->log_dev->bdev->bd_dev); + DMEMIT("%s %u %s " SECTOR_FORMAT " ", log->type->name, + lc->sync == DEFAULTSYNC ? 2 : 3, buffer, + lc->region_size); + DMEMIT_SYNC; + } + + return sz; +} + static struct dirty_log_type _core_type = { .name = "core", .module = THIS_MODULE, @@ -579,7 +659,8 @@ static struct dirty_log_type _core_type = { .clear_region = core_clear_region, .get_resync_work = core_get_resync_work, .complete_resync_work = core_complete_resync_work, - .get_sync_count = core_get_sync_count + .get_sync_count = core_get_sync_count, + .status = core_status, }; static struct dirty_log_type _disk_type = { @@ -597,7 +678,8 @@ static struct dirty_log_type _disk_type = { .clear_region = core_clear_region, .get_resync_work = core_get_resync_work, .complete_resync_work = core_complete_resync_work, - .get_sync_count = core_get_sync_count + .get_sync_count = core_get_sync_count, + .status = disk_status, }; int __init dm_dirty_log_init(void) diff --git a/drivers/md/dm-log.h b/drivers/md/dm-log.h index ced4ebacd..05ef594b5 100644 --- a/drivers/md/dm-log.h +++ b/drivers/md/dm-log.h @@ -101,6 +101,12 @@ struct dirty_log_type { * Returns the number of regions that are in sync. */ region_t (*get_sync_count)(struct dirty_log *log); + + /* + * Support function for mirror status requests. + */ + int (*status)(struct dirty_log *log, status_type_t status_type, + char *result, unsigned int maxlen); }; int dm_register_dirty_log_type(struct dirty_log_type *type); diff --git a/drivers/md/dm-raid1.c b/drivers/md/dm-raid1.c index 92792bb7d..abab26516 100644 --- a/drivers/md/dm-raid1.c +++ b/drivers/md/dm-raid1.c @@ -1009,8 +1009,8 @@ static struct dirty_log *create_dirty_log(struct dm_target *ti, * log_type #log_params * #mirrors [mirror_path offset]{2,} * - * For now, #log_params = 1, log_type = "core" - * + * log_type is "core" or "disk" + * #log_params is between 1 and 3 */ #define DM_IO_PAGES 64 static int mirror_ctr(struct dm_target *ti, unsigned int argc, char **argv) @@ -1182,35 +1182,30 @@ static int mirror_status(struct dm_target *ti, status_type_t type, char *result, unsigned int maxlen) { char buffer[32]; - unsigned int m, sz = 0; + unsigned int m, sz; struct mirror_set *ms = (struct mirror_set *) ti->private; -#define EMIT(x...) sz += ((sz >= maxlen) ? \ - 0 : scnprintf(result + sz, maxlen - sz, x)) + sz = ms->rh.log->type->status(ms->rh.log, type, result, maxlen); switch (type) { case STATUSTYPE_INFO: - EMIT("%d ", ms->nr_mirrors); - + DMEMIT("%d ", ms->nr_mirrors); for (m = 0; m < ms->nr_mirrors; m++) { format_dev_t(buffer, ms->mirror[m].dev->bdev->bd_dev); - EMIT("%s ", buffer); + DMEMIT("%s ", buffer); } - EMIT(SECTOR_FORMAT "/" SECTOR_FORMAT, - ms->rh.log->type->get_sync_count(ms->rh.log), - ms->nr_regions); + DMEMIT(SECTOR_FORMAT "/" SECTOR_FORMAT, + ms->rh.log->type->get_sync_count(ms->rh.log), + ms->nr_regions); break; case STATUSTYPE_TABLE: - EMIT("%s 1 " SECTOR_FORMAT " %d ", - ms->rh.log->type->name, ms->rh.region_size, - ms->nr_mirrors); - + DMEMIT("%d ", ms->nr_mirrors); for (m = 0; m < ms->nr_mirrors; m++) { format_dev_t(buffer, ms->mirror[m].dev->bdev->bd_dev); - EMIT("%s " SECTOR_FORMAT " ", - buffer, ms->mirror[m].offset); + DMEMIT("%s " SECTOR_FORMAT " ", + buffer, ms->mirror[m].offset); } } diff --git a/drivers/md/dm-stripe.c b/drivers/md/dm-stripe.c index b5bacd717..6a3b9454c 100644 --- a/drivers/md/dm-stripe.c +++ b/drivers/md/dm-stripe.c @@ -191,20 +191,17 @@ static int stripe_status(struct dm_target *ti, unsigned int i; char buffer[32]; -#define EMIT(x...) sz += ((sz >= maxlen) ? \ - 0 : scnprintf(result + sz, maxlen - sz, x)) - switch (type) { case STATUSTYPE_INFO: result[0] = '\0'; break; case STATUSTYPE_TABLE: - EMIT("%d " SECTOR_FORMAT, sc->stripes, sc->chunk_mask + 1); + DMEMIT("%d " SECTOR_FORMAT, sc->stripes, sc->chunk_mask + 1); for (i = 0; i < sc->stripes; i++) { format_dev_t(buffer, sc->stripe[i].dev->bdev->bd_dev); - EMIT(" %s " SECTOR_FORMAT, buffer, - sc->stripe[i].physical_start); + DMEMIT(" %s " SECTOR_FORMAT, buffer, + sc->stripe[i].physical_start); } break; } diff --git a/drivers/md/linear.c b/drivers/md/linear.c index e0aa017a2..0f57e5edd 100644 --- a/drivers/md/linear.c +++ b/drivers/md/linear.c @@ -47,7 +47,6 @@ static inline dev_info_t *which_dev(mddev_t *mddev, sector_t sector) return hash->dev0; } - /** * linear_mergeable_bvec -- tell bio layer if a two requests can be merged * @q: request queue @@ -93,6 +92,27 @@ static void linear_unplug(request_queue_t *q) } } +static int linear_issue_flush(request_queue_t *q, struct gendisk *disk, + sector_t *error_sector) +{ + mddev_t *mddev = q->queuedata; + linear_conf_t *conf = mddev_to_conf(mddev); + int i, ret = 0; + + for (i=0; i < mddev->raid_disks; i++) { + struct block_device *bdev = conf->disks[i].rdev->bdev; + request_queue_t *r_queue = bdev_get_queue(bdev); + + if (!r_queue->issue_flush_fn) { + ret = -EOPNOTSUPP; + break; + } + ret = r_queue->issue_flush_fn(r_queue, bdev->bd_disk, error_sector); + if (ret) + break; + } + return ret; +} static int linear_run (mddev_t *mddev) { @@ -137,7 +157,7 @@ static int linear_run (mddev_t *mddev) */ if (rdev->bdev->bd_disk->queue->merge_bvec_fn && mddev->queue->max_sectors > (PAGE_SIZE>>9)) - mddev->queue->max_sectors = (PAGE_SIZE>>9); + blk_queue_max_sectors(mddev->queue, PAGE_SIZE>>9); disk->size = rdev->size; mddev->array_size += rdev->size; @@ -200,6 +220,7 @@ static int linear_run (mddev_t *mddev) blk_queue_merge_bvec(mddev->queue, linear_mergeable_bvec); mddev->queue->unplug_fn = linear_unplug; + mddev->queue->issue_flush_fn = linear_issue_flush; return 0; out: diff --git a/drivers/md/raid0.c b/drivers/md/raid0.c index 5f4b8bfef..ae45ae92a 100644 --- a/drivers/md/raid0.c +++ b/drivers/md/raid0.c @@ -40,6 +40,31 @@ static void raid0_unplug(request_queue_t *q) } } +static int raid0_issue_flush(request_queue_t *q, struct gendisk *disk, + sector_t *error_sector) +{ + mddev_t *mddev = q->queuedata; + raid0_conf_t *conf = mddev_to_conf(mddev); + mdk_rdev_t **devlist = conf->strip_zone[0].dev; + int i, ret = 0; + + for (i=0; iraid_disks; i++) { + struct block_device *bdev = devlist[i]->bdev; + request_queue_t *r_queue = bdev_get_queue(bdev); + + if (!r_queue->issue_flush_fn) { + ret = -EOPNOTSUPP; + break; + } + + ret =r_queue->issue_flush_fn(r_queue, bdev->bd_disk, error_sector); + if (ret) + break; + } + return ret; +} + + static int create_strip_zones (mddev_t *mddev) { int i, c, j; @@ -137,7 +162,7 @@ static int create_strip_zones (mddev_t *mddev) if (rdev1->bdev->bd_disk->queue->merge_bvec_fn && mddev->queue->max_sectors > (PAGE_SIZE>>9)) - mddev->queue->max_sectors = (PAGE_SIZE>>9); + blk_queue_max_sectors(mddev->queue, PAGE_SIZE>>9); if (!smallest || (rdev1->size size)) smallest = rdev1; @@ -219,6 +244,8 @@ static int create_strip_zones (mddev_t *mddev) mddev->queue->unplug_fn = raid0_unplug; + mddev->queue->issue_flush_fn = raid0_issue_flush; + printk("raid0: done.\n"); return 0; abort: diff --git a/drivers/md/raid10.c b/drivers/md/raid10.c new file mode 100644 index 000000000..6c3fde9ba --- /dev/null +++ b/drivers/md/raid10.c @@ -0,0 +1,1780 @@ +/* + * raid10.c : Multiple Devices driver for Linux + * + * Copyright (C) 2000-2004 Neil Brown + * + * RAID-10 support for md. + * + * Base on code in raid1.c. See raid1.c for futher copyright information. + * + * + * This program is free software; you can redistribute 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. + * + * You should have received a copy of the GNU General Public License + * (for example /usr/src/linux/COPYING); if not, write to the Free + * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include + +/* + * RAID10 provides a combination of RAID0 and RAID1 functionality. + * The layout of data is defined by + * chunk_size + * raid_disks + * near_copies (stored in low byte of layout) + * far_copies (stored in second byte of layout) + * + * The data to be stored is divided into chunks using chunksize. + * Each device is divided into far_copies sections. + * In each section, chunks are laid out in a style similar to raid0, but + * near_copies copies of each chunk is stored (each on a different drive). + * The starting device for each section is offset near_copies from the starting + * device of the previous section. + * Thus there are (near_copies*far_copies) of each chunk, and each is on a different + * drive. + * near_copies and far_copies must be at least one, and there product is at most + * raid_disks. + */ + +/* + * Number of guaranteed r10bios in case of extreme VM load: + */ +#define NR_RAID10_BIOS 256 + +static void unplug_slaves(mddev_t *mddev); + +static void * r10bio_pool_alloc(int gfp_flags, void *data) +{ + conf_t *conf = data; + r10bio_t *r10_bio; + int size = offsetof(struct r10bio_s, devs[conf->copies]); + + /* allocate a r10bio with room for raid_disks entries in the bios array */ + r10_bio = kmalloc(size, gfp_flags); + if (r10_bio) + memset(r10_bio, 0, size); + else + unplug_slaves(conf->mddev); + + return r10_bio; +} + +static void r10bio_pool_free(void *r10_bio, void *data) +{ + kfree(r10_bio); +} + +#define RESYNC_BLOCK_SIZE (64*1024) +//#define RESYNC_BLOCK_SIZE PAGE_SIZE +#define RESYNC_SECTORS (RESYNC_BLOCK_SIZE >> 9) +#define RESYNC_PAGES ((RESYNC_BLOCK_SIZE + PAGE_SIZE-1) / PAGE_SIZE) +#define RESYNC_WINDOW (2048*1024) + +/* + * When performing a resync, we need to read and compare, so + * we need as many pages are there are copies. + * When performing a recovery, we need 2 bios, one for read, + * one for write (we recover only one drive per r10buf) + * + */ +static void * r10buf_pool_alloc(int gfp_flags, void *data) +{ + conf_t *conf = data; + struct page *page; + r10bio_t *r10_bio; + struct bio *bio; + int i, j; + int nalloc; + + r10_bio = r10bio_pool_alloc(gfp_flags, conf); + if (!r10_bio) { + unplug_slaves(conf->mddev); + return NULL; + } + + if (test_bit(MD_RECOVERY_SYNC, &conf->mddev->recovery)) + nalloc = conf->copies; /* resync */ + else + nalloc = 2; /* recovery */ + + /* + * Allocate bios. + */ + for (j = nalloc ; j-- ; ) { + bio = bio_alloc(gfp_flags, RESYNC_PAGES); + if (!bio) + goto out_free_bio; + r10_bio->devs[j].bio = bio; + } + /* + * Allocate RESYNC_PAGES data pages and attach them + * where needed. + */ + for (j = 0 ; j < nalloc; j++) { + bio = r10_bio->devs[j].bio; + for (i = 0; i < RESYNC_PAGES; i++) { + page = alloc_page(gfp_flags); + if (unlikely(!page)) + goto out_free_pages; + + bio->bi_io_vec[i].bv_page = page; + } + } + + return r10_bio; + +out_free_pages: + for ( ; i > 0 ; i--) + __free_page(bio->bi_io_vec[i-1].bv_page); + while (j--) + for (i = 0; i < RESYNC_PAGES ; i++) + __free_page(r10_bio->devs[j].bio->bi_io_vec[i].bv_page); + j = -1; +out_free_bio: + while ( ++j < nalloc ) + bio_put(r10_bio->devs[j].bio); + r10bio_pool_free(r10_bio, conf); + return NULL; +} + +static void r10buf_pool_free(void *__r10_bio, void *data) +{ + int i; + conf_t *conf = data; + r10bio_t *r10bio = __r10_bio; + int j; + + for (j=0; j < conf->copies; j++) { + struct bio *bio = r10bio->devs[j].bio; + if (bio) { + for (i = 0; i < RESYNC_PAGES; i++) { + __free_page(bio->bi_io_vec[i].bv_page); + bio->bi_io_vec[i].bv_page = NULL; + } + bio_put(bio); + } + } + r10bio_pool_free(r10bio, conf); +} + +static void put_all_bios(conf_t *conf, r10bio_t *r10_bio) +{ + int i; + + for (i = 0; i < conf->copies; i++) { + struct bio **bio = & r10_bio->devs[i].bio; + if (*bio) + bio_put(*bio); + *bio = NULL; + } +} + +static inline void free_r10bio(r10bio_t *r10_bio) +{ + unsigned long flags; + + conf_t *conf = mddev_to_conf(r10_bio->mddev); + + /* + * Wake up any possible resync thread that waits for the device + * to go idle. + */ + spin_lock_irqsave(&conf->resync_lock, flags); + if (!--conf->nr_pending) { + wake_up(&conf->wait_idle); + wake_up(&conf->wait_resume); + } + spin_unlock_irqrestore(&conf->resync_lock, flags); + + put_all_bios(conf, r10_bio); + mempool_free(r10_bio, conf->r10bio_pool); +} + +static inline void put_buf(r10bio_t *r10_bio) +{ + conf_t *conf = mddev_to_conf(r10_bio->mddev); + unsigned long flags; + + mempool_free(r10_bio, conf->r10buf_pool); + + spin_lock_irqsave(&conf->resync_lock, flags); + if (!conf->barrier) + BUG(); + --conf->barrier; + wake_up(&conf->wait_resume); + wake_up(&conf->wait_idle); + + if (!--conf->nr_pending) { + wake_up(&conf->wait_idle); + wake_up(&conf->wait_resume); + } + spin_unlock_irqrestore(&conf->resync_lock, flags); +} + +static void reschedule_retry(r10bio_t *r10_bio) +{ + unsigned long flags; + mddev_t *mddev = r10_bio->mddev; + conf_t *conf = mddev_to_conf(mddev); + + spin_lock_irqsave(&conf->device_lock, flags); + list_add(&r10_bio->retry_list, &conf->retry_list); + spin_unlock_irqrestore(&conf->device_lock, flags); + + md_wakeup_thread(mddev->thread); +} + +/* + * raid_end_bio_io() is called when we have finished servicing a mirrored + * operation and are ready to return a success/failure code to the buffer + * cache layer. + */ +static void raid_end_bio_io(r10bio_t *r10_bio) +{ + struct bio *bio = r10_bio->master_bio; + + bio_endio(bio, bio->bi_size, + test_bit(R10BIO_Uptodate, &r10_bio->state) ? 0 : -EIO); + free_r10bio(r10_bio); +} + +/* + * Update disk head position estimator based on IRQ completion info. + */ +static inline void update_head_pos(int slot, r10bio_t *r10_bio) +{ + conf_t *conf = mddev_to_conf(r10_bio->mddev); + + conf->mirrors[r10_bio->devs[slot].devnum].head_position = + r10_bio->devs[slot].addr + (r10_bio->sectors); +} + +static int raid10_end_read_request(struct bio *bio, unsigned int bytes_done, int error) +{ + int uptodate = test_bit(BIO_UPTODATE, &bio->bi_flags); + r10bio_t * r10_bio = (r10bio_t *)(bio->bi_private); + int slot, dev; + conf_t *conf = mddev_to_conf(r10_bio->mddev); + + if (bio->bi_size) + return 1; + + slot = r10_bio->read_slot; + dev = r10_bio->devs[slot].devnum; + /* + * this branch is our 'one mirror IO has finished' event handler: + */ + if (!uptodate) + md_error(r10_bio->mddev, conf->mirrors[dev].rdev); + else + /* + * Set R10BIO_Uptodate in our master bio, so that + * we will return a good error code to the higher + * levels even if IO on some other mirrored buffer fails. + * + * The 'master' represents the composite IO operation to + * user-side. So if something waits for IO, then it will + * wait for the 'master' bio. + */ + set_bit(R10BIO_Uptodate, &r10_bio->state); + + update_head_pos(slot, r10_bio); + + /* + * we have only one bio on the read side + */ + if (uptodate) + raid_end_bio_io(r10_bio); + else { + /* + * oops, read error: + */ + char b[BDEVNAME_SIZE]; + if (printk_ratelimit()) + printk(KERN_ERR "raid10: %s: rescheduling sector %llu\n", + bdevname(conf->mirrors[dev].rdev->bdev,b), (unsigned long long)r10_bio->sector); + reschedule_retry(r10_bio); + } + + rdev_dec_pending(conf->mirrors[dev].rdev, conf->mddev); + return 0; +} + +static int raid10_end_write_request(struct bio *bio, unsigned int bytes_done, int error) +{ + int uptodate = test_bit(BIO_UPTODATE, &bio->bi_flags); + r10bio_t * r10_bio = (r10bio_t *)(bio->bi_private); + int slot, dev; + conf_t *conf = mddev_to_conf(r10_bio->mddev); + + if (bio->bi_size) + return 1; + + for (slot = 0; slot < conf->copies; slot++) + if (r10_bio->devs[slot].bio == bio) + break; + dev = r10_bio->devs[slot].devnum; + + /* + * this branch is our 'one mirror IO has finished' event handler: + */ + if (!uptodate) + md_error(r10_bio->mddev, conf->mirrors[dev].rdev); + else + /* + * Set R10BIO_Uptodate in our master bio, so that + * we will return a good error code for to the higher + * levels even if IO on some other mirrored buffer fails. + * + * The 'master' represents the composite IO operation to + * user-side. So if something waits for IO, then it will + * wait for the 'master' bio. + */ + set_bit(R10BIO_Uptodate, &r10_bio->state); + + update_head_pos(slot, r10_bio); + + /* + * + * Let's see if all mirrored write operations have finished + * already. + */ + if (atomic_dec_and_test(&r10_bio->remaining)) { + md_write_end(r10_bio->mddev); + raid_end_bio_io(r10_bio); + } + + rdev_dec_pending(conf->mirrors[dev].rdev, conf->mddev); + return 0; +} + + +/* + * RAID10 layout manager + * Aswell as the chunksize and raid_disks count, there are two + * parameters: near_copies and far_copies. + * near_copies * far_copies must be <= raid_disks. + * Normally one of these will be 1. + * If both are 1, we get raid0. + * If near_copies == raid_disks, we get raid1. + * + * Chunks are layed out in raid0 style with near_copies copies of the + * first chunk, followed by near_copies copies of the next chunk and + * so on. + * If far_copies > 1, then after 1/far_copies of the array has been assigned + * as described above, we start again with a device offset of near_copies. + * So we effectively have another copy of the whole array further down all + * the drives, but with blocks on different drives. + * With this layout, and block is never stored twice on the one device. + * + * raid10_find_phys finds the sector offset of a given virtual sector + * on each device that it is on. If a block isn't on a device, + * that entry in the array is set to MaxSector. + * + * raid10_find_virt does the reverse mapping, from a device and a + * sector offset to a virtual address + */ + +static void raid10_find_phys(conf_t *conf, r10bio_t *r10bio) +{ + int n,f; + sector_t sector; + sector_t chunk; + sector_t stripe; + int dev; + + int slot = 0; + + /* now calculate first sector/dev */ + chunk = r10bio->sector >> conf->chunk_shift; + sector = r10bio->sector & conf->chunk_mask; + + chunk *= conf->near_copies; + stripe = chunk; + dev = sector_div(stripe, conf->raid_disks); + + sector += stripe << conf->chunk_shift; + + /* and calculate all the others */ + for (n=0; n < conf->near_copies; n++) { + int d = dev; + sector_t s = sector; + r10bio->devs[slot].addr = sector; + r10bio->devs[slot].devnum = d; + slot++; + + for (f = 1; f < conf->far_copies; f++) { + d += conf->near_copies; + if (d >= conf->raid_disks) + d -= conf->raid_disks; + s += conf->stride; + r10bio->devs[slot].devnum = d; + r10bio->devs[slot].addr = s; + slot++; + } + dev++; + if (dev >= conf->raid_disks) { + dev = 0; + sector += (conf->chunk_mask + 1); + } + } + BUG_ON(slot != conf->copies); +} + +static sector_t raid10_find_virt(conf_t *conf, sector_t sector, int dev) +{ + sector_t offset, chunk, vchunk; + + while (sector > conf->stride) { + sector -= conf->stride; + if (dev < conf->near_copies) + dev += conf->raid_disks - conf->near_copies; + else + dev -= conf->near_copies; + } + + offset = sector & conf->chunk_mask; + chunk = sector >> conf->chunk_shift; + vchunk = chunk * conf->raid_disks + dev; + sector_div(vchunk, conf->near_copies); + return (vchunk << conf->chunk_shift) + offset; +} + +/** + * raid10_mergeable_bvec -- tell bio layer if a two requests can be merged + * @q: request queue + * @bio: the buffer head that's been built up so far + * @biovec: the request that could be merged to it. + * + * Return amount of bytes we can accept at this offset + * If near_copies == raid_disk, there are no striping issues, + * but in that case, the function isn't called at all. + */ +static int raid10_mergeable_bvec(request_queue_t *q, struct bio *bio, + struct bio_vec *bio_vec) +{ + mddev_t *mddev = q->queuedata; + sector_t sector = bio->bi_sector + get_start_sect(bio->bi_bdev); + int max; + unsigned int chunk_sectors = mddev->chunk_size >> 9; + unsigned int bio_sectors = bio->bi_size >> 9; + + max = (chunk_sectors - ((sector & (chunk_sectors - 1)) + bio_sectors)) << 9; + if (max < 0) max = 0; /* bio_add cannot handle a negative return */ + if (max <= bio_vec->bv_len && bio_sectors == 0) + return bio_vec->bv_len; + else + return max; +} + +/* + * This routine returns the disk from which the requested read should + * be done. There is a per-array 'next expected sequential IO' sector + * number - if this matches on the next IO then we use the last disk. + * There is also a per-disk 'last know head position' sector that is + * maintained from IRQ contexts, both the normal and the resync IO + * completion handlers update this position correctly. If there is no + * perfect sequential match then we pick the disk whose head is closest. + * + * If there are 2 mirrors in the same 2 devices, performance degrades + * because position is mirror, not device based. + * + * The rdev for the device selected will have nr_pending incremented. + */ + +/* + * FIXME: possibly should rethink readbalancing and do it differently + * depending on near_copies / far_copies geometry. + */ +static int read_balance(conf_t *conf, r10bio_t *r10_bio) +{ + const unsigned long this_sector = r10_bio->sector; + int disk, slot, nslot; + const int sectors = r10_bio->sectors; + sector_t new_distance, current_distance; + + raid10_find_phys(conf, r10_bio); + spin_lock_irq(&conf->device_lock); + /* + * Check if we can balance. We can balance on the whole + * device if no resync is going on, or below the resync window. + * We take the first readable disk when above the resync window. + */ + if (conf->mddev->recovery_cp < MaxSector + && (this_sector + sectors >= conf->next_resync)) { + /* make sure that disk is operational */ + slot = 0; + disk = r10_bio->devs[slot].devnum; + + while (!conf->mirrors[disk].rdev || + !conf->mirrors[disk].rdev->in_sync) { + slot++; + if (slot == conf->copies) { + slot = 0; + disk = -1; + break; + } + disk = r10_bio->devs[slot].devnum; + } + goto rb_out; + } + + + /* make sure the disk is operational */ + slot = 0; + disk = r10_bio->devs[slot].devnum; + while (!conf->mirrors[disk].rdev || + !conf->mirrors[disk].rdev->in_sync) { + slot ++; + if (slot == conf->copies) { + disk = -1; + goto rb_out; + } + disk = r10_bio->devs[slot].devnum; + } + + + current_distance = abs(this_sector - conf->mirrors[disk].head_position); + + /* Find the disk whose head is closest */ + + for (nslot = slot; nslot < conf->copies; nslot++) { + int ndisk = r10_bio->devs[nslot].devnum; + + + if (!conf->mirrors[ndisk].rdev || + !conf->mirrors[ndisk].rdev->in_sync) + continue; + + if (!atomic_read(&conf->mirrors[ndisk].rdev->nr_pending)) { + disk = ndisk; + slot = nslot; + break; + } + new_distance = abs(r10_bio->devs[nslot].addr - + conf->mirrors[ndisk].head_position); + if (new_distance < current_distance) { + current_distance = new_distance; + disk = ndisk; + slot = nslot; + } + } + +rb_out: + r10_bio->read_slot = slot; +/* conf->next_seq_sect = this_sector + sectors;*/ + + if (disk >= 0 && conf->mirrors[disk].rdev) + atomic_inc(&conf->mirrors[disk].rdev->nr_pending); + spin_unlock_irq(&conf->device_lock); + + return disk; +} + +static void unplug_slaves(mddev_t *mddev) +{ + conf_t *conf = mddev_to_conf(mddev); + int i; + unsigned long flags; + + spin_lock_irqsave(&conf->device_lock, flags); + for (i=0; iraid_disks; i++) { + mdk_rdev_t *rdev = conf->mirrors[i].rdev; + if (rdev && atomic_read(&rdev->nr_pending)) { + request_queue_t *r_queue = bdev_get_queue(rdev->bdev); + + atomic_inc(&rdev->nr_pending); + spin_unlock_irqrestore(&conf->device_lock, flags); + + if (r_queue->unplug_fn) + r_queue->unplug_fn(r_queue); + + spin_lock_irqsave(&conf->device_lock, flags); + atomic_dec(&rdev->nr_pending); + } + } + spin_unlock_irqrestore(&conf->device_lock, flags); +} +static void raid10_unplug(request_queue_t *q) +{ + unplug_slaves(q->queuedata); +} + +static int raid10_issue_flush(request_queue_t *q, struct gendisk *disk, + sector_t *error_sector) +{ + mddev_t *mddev = q->queuedata; + conf_t *conf = mddev_to_conf(mddev); + unsigned long flags; + int i, ret = 0; + + spin_lock_irqsave(&conf->device_lock, flags); + for (i=0; iraid_disks; i++) { + mdk_rdev_t *rdev = conf->mirrors[i].rdev; + if (rdev && !rdev->faulty) { + struct block_device *bdev = rdev->bdev; + request_queue_t *r_queue = bdev_get_queue(bdev); + + if (r_queue->issue_flush_fn) { + ret = r_queue->issue_flush_fn(r_queue, bdev->bd_disk, error_sector); + if (ret) + break; + } + } + } + spin_unlock_irqrestore(&conf->device_lock, flags); + return ret; +} + +/* + * Throttle resync depth, so that we can both get proper overlapping of + * requests, but are still able to handle normal requests quickly. + */ +#define RESYNC_DEPTH 32 + +static void device_barrier(conf_t *conf, sector_t sect) +{ + spin_lock_irq(&conf->resync_lock); + wait_event_lock_irq(conf->wait_idle, !waitqueue_active(&conf->wait_resume), + conf->resync_lock, unplug_slaves(conf->mddev)); + + if (!conf->barrier++) { + wait_event_lock_irq(conf->wait_idle, !conf->nr_pending, + conf->resync_lock, unplug_slaves(conf->mddev)); + if (conf->nr_pending) + BUG(); + } + wait_event_lock_irq(conf->wait_resume, conf->barrier < RESYNC_DEPTH, + conf->resync_lock, unplug_slaves(conf->mddev)); + conf->next_resync = sect; + spin_unlock_irq(&conf->resync_lock); +} + +static int make_request(request_queue_t *q, struct bio * bio) +{ + mddev_t *mddev = q->queuedata; + conf_t *conf = mddev_to_conf(mddev); + mirror_info_t *mirror; + r10bio_t *r10_bio; + struct bio *read_bio; + int i; + int chunk_sects = conf->chunk_mask + 1; + + /* If this request crosses a chunk boundary, we need to + * split it. This will only happen for 1 PAGE (or less) requests. + */ + if (unlikely( (bio->bi_sector & conf->chunk_mask) + (bio->bi_size >> 9) + > chunk_sects && + conf->near_copies < conf->raid_disks)) { + struct bio_pair *bp; + /* Sanity check -- queue functions should prevent this happening */ + if (bio->bi_vcnt != 1 || + bio->bi_idx != 0) + goto bad_map; + /* This is a one page bio that upper layers + * refuse to split for us, so we need to split it. + */ + bp = bio_split(bio, bio_split_pool, + chunk_sects - (bio->bi_sector & (chunk_sects - 1)) ); + if (make_request(q, &bp->bio1)) + generic_make_request(&bp->bio1); + if (make_request(q, &bp->bio2)) + generic_make_request(&bp->bio2); + + bio_pair_release(bp); + return 0; + bad_map: + printk("raid10_make_request bug: can't convert block across chunks" + " or bigger than %dk %llu %d\n", chunk_sects/2, + (unsigned long long)bio->bi_sector, bio->bi_size >> 10); + + bio_io_error(bio, bio->bi_size); + return 0; + } + + /* + * Register the new request and wait if the reconstruction + * thread has put up a bar for new requests. + * Continue immediately if no resync is active currently. + */ + spin_lock_irq(&conf->resync_lock); + wait_event_lock_irq(conf->wait_resume, !conf->barrier, conf->resync_lock, ); + conf->nr_pending++; + spin_unlock_irq(&conf->resync_lock); + + if (bio_data_dir(bio)==WRITE) { + disk_stat_inc(mddev->gendisk, writes); + disk_stat_add(mddev->gendisk, write_sectors, bio_sectors(bio)); + } else { + disk_stat_inc(mddev->gendisk, reads); + disk_stat_add(mddev->gendisk, read_sectors, bio_sectors(bio)); + } + + r10_bio = mempool_alloc(conf->r10bio_pool, GFP_NOIO); + + r10_bio->master_bio = bio; + r10_bio->sectors = bio->bi_size >> 9; + + r10_bio->mddev = mddev; + r10_bio->sector = bio->bi_sector; + + if (bio_data_dir(bio) == READ) { + /* + * read balancing logic: + */ + int disk = read_balance(conf, r10_bio); + int slot = r10_bio->read_slot; + if (disk < 0) { + raid_end_bio_io(r10_bio); + return 0; + } + mirror = conf->mirrors + disk; + + read_bio = bio_clone(bio, GFP_NOIO); + + r10_bio->devs[slot].bio = read_bio; + + read_bio->bi_sector = r10_bio->devs[slot].addr + + mirror->rdev->data_offset; + read_bio->bi_bdev = mirror->rdev->bdev; + read_bio->bi_end_io = raid10_end_read_request; + read_bio->bi_rw = READ; + read_bio->bi_private = r10_bio; + + generic_make_request(read_bio); + return 0; + } + + /* + * WRITE: + */ + /* first select target devices under spinlock and + * inc refcount on their rdev. Record them by setting + * bios[x] to bio + */ + raid10_find_phys(conf, r10_bio); + spin_lock_irq(&conf->device_lock); + for (i = 0; i < conf->copies; i++) { + int d = r10_bio->devs[i].devnum; + if (conf->mirrors[d].rdev && + !conf->mirrors[d].rdev->faulty) { + atomic_inc(&conf->mirrors[d].rdev->nr_pending); + r10_bio->devs[i].bio = bio; + } else + r10_bio->devs[i].bio = NULL; + } + spin_unlock_irq(&conf->device_lock); + + atomic_set(&r10_bio->remaining, 1); + md_write_start(mddev); + for (i = 0; i < conf->copies; i++) { + struct bio *mbio; + int d = r10_bio->devs[i].devnum; + if (!r10_bio->devs[i].bio) + continue; + + mbio = bio_clone(bio, GFP_NOIO); + r10_bio->devs[i].bio = mbio; + + mbio->bi_sector = r10_bio->devs[i].addr+ + conf->mirrors[d].rdev->data_offset; + mbio->bi_bdev = conf->mirrors[d].rdev->bdev; + mbio->bi_end_io = raid10_end_write_request; + mbio->bi_rw = WRITE; + mbio->bi_private = r10_bio; + + atomic_inc(&r10_bio->remaining); + generic_make_request(mbio); + } + + if (atomic_dec_and_test(&r10_bio->remaining)) { + md_write_end(mddev); + raid_end_bio_io(r10_bio); + } + + return 0; +} + +static void status(struct seq_file *seq, mddev_t *mddev) +{ + conf_t *conf = mddev_to_conf(mddev); + int i; + + if (conf->near_copies < conf->raid_disks) + seq_printf(seq, " %dK chunks", mddev->chunk_size/1024); + if (conf->near_copies > 1) + seq_printf(seq, " %d near-copies", conf->near_copies); + if (conf->far_copies > 1) + seq_printf(seq, " %d far-copies", conf->far_copies); + + seq_printf(seq, " [%d/%d] [", conf->raid_disks, + conf->working_disks); + for (i = 0; i < conf->raid_disks; i++) + seq_printf(seq, "%s", + conf->mirrors[i].rdev && + conf->mirrors[i].rdev->in_sync ? "U" : "_"); + seq_printf(seq, "]"); +} + +static void error(mddev_t *mddev, mdk_rdev_t *rdev) +{ + char b[BDEVNAME_SIZE]; + conf_t *conf = mddev_to_conf(mddev); + + /* + * If it is not operational, then we have already marked it as dead + * else if it is the last working disks, ignore the error, let the + * next level up know. + * else mark the drive as failed + */ + if (rdev->in_sync + && conf->working_disks == 1) + /* + * Don't fail the drive, just return an IO error. + * The test should really be more sophisticated than + * "working_disks == 1", but it isn't critical, and + * can wait until we do more sophisticated "is the drive + * really dead" tests... + */ + return; + if (rdev->in_sync) { + mddev->degraded++; + conf->working_disks--; + /* + * if recovery is running, make sure it aborts. + */ + set_bit(MD_RECOVERY_ERR, &mddev->recovery); + } + rdev->in_sync = 0; + rdev->faulty = 1; + mddev->sb_dirty = 1; + printk(KERN_ALERT "raid10: Disk failure on %s, disabling device. \n" + " Operation continuing on %d devices\n", + bdevname(rdev->bdev,b), conf->working_disks); +} + +static void print_conf(conf_t *conf) +{ + int i; + mirror_info_t *tmp; + + printk("RAID10 conf printout:\n"); + if (!conf) { + printk("(!conf)\n"); + return; + } + printk(" --- wd:%d rd:%d\n", conf->working_disks, + conf->raid_disks); + + for (i = 0; i < conf->raid_disks; i++) { + char b[BDEVNAME_SIZE]; + tmp = conf->mirrors + i; + if (tmp->rdev) + printk(" disk %d, wo:%d, o:%d, dev:%s\n", + i, !tmp->rdev->in_sync, !tmp->rdev->faulty, + bdevname(tmp->rdev->bdev,b)); + } +} + +static void close_sync(conf_t *conf) +{ + spin_lock_irq(&conf->resync_lock); + wait_event_lock_irq(conf->wait_resume, !conf->barrier, + conf->resync_lock, unplug_slaves(conf->mddev)); + spin_unlock_irq(&conf->resync_lock); + + if (conf->barrier) BUG(); + if (waitqueue_active(&conf->wait_idle)) BUG(); + + mempool_destroy(conf->r10buf_pool); + conf->r10buf_pool = NULL; +} + +static int raid10_spare_active(mddev_t *mddev) +{ + int i; + conf_t *conf = mddev->private; + mirror_info_t *tmp; + + spin_lock_irq(&conf->device_lock); + /* + * Find all non-in_sync disks within the RAID10 configuration + * and mark them in_sync + */ + for (i = 0; i < conf->raid_disks; i++) { + tmp = conf->mirrors + i; + if (tmp->rdev + && !tmp->rdev->faulty + && !tmp->rdev->in_sync) { + conf->working_disks++; + mddev->degraded--; + tmp->rdev->in_sync = 1; + } + } + spin_unlock_irq(&conf->device_lock); + + print_conf(conf); + return 0; +} + + +static int raid10_add_disk(mddev_t *mddev, mdk_rdev_t *rdev) +{ + conf_t *conf = mddev->private; + int found = 0; + int mirror; + mirror_info_t *p; + + if (mddev->recovery_cp < MaxSector) + /* only hot-add to in-sync arrays, as recovery is + * very different from resync + */ + return 0; + spin_lock_irq(&conf->device_lock); + for (mirror=0; mirror < mddev->raid_disks; mirror++) + if ( !(p=conf->mirrors+mirror)->rdev) { + p->rdev = rdev; + + blk_queue_stack_limits(mddev->queue, + rdev->bdev->bd_disk->queue); + /* as we don't honour merge_bvec_fn, we must never risk + * violating it, so limit ->max_sector to one PAGE, as + * a one page request is never in violation. + */ + if (rdev->bdev->bd_disk->queue->merge_bvec_fn && + mddev->queue->max_sectors > (PAGE_SIZE>>9)) + mddev->queue->max_sectors = (PAGE_SIZE>>9); + + p->head_position = 0; + rdev->raid_disk = mirror; + found = 1; + break; + } + spin_unlock_irq(&conf->device_lock); + + print_conf(conf); + return found; +} + +static int raid10_remove_disk(mddev_t *mddev, int number) +{ + conf_t *conf = mddev->private; + int err = 1; + mirror_info_t *p = conf->mirrors+ number; + + print_conf(conf); + spin_lock_irq(&conf->device_lock); + if (p->rdev) { + if (p->rdev->in_sync || + atomic_read(&p->rdev->nr_pending)) { + err = -EBUSY; + goto abort; + } + p->rdev = NULL; + err = 0; + } + if (err) + MD_BUG(); +abort: + spin_unlock_irq(&conf->device_lock); + + print_conf(conf); + return err; +} + + +static int end_sync_read(struct bio *bio, unsigned int bytes_done, int error) +{ + int uptodate = test_bit(BIO_UPTODATE, &bio->bi_flags); + r10bio_t * r10_bio = (r10bio_t *)(bio->bi_private); + conf_t *conf = mddev_to_conf(r10_bio->mddev); + int i,d; + + if (bio->bi_size) + return 1; + + for (i=0; icopies; i++) + if (r10_bio->devs[i].bio == bio) + break; + if (i == conf->copies) + BUG(); + update_head_pos(i, r10_bio); + d = r10_bio->devs[i].devnum; + if (!uptodate) + md_error(r10_bio->mddev, + conf->mirrors[d].rdev); + + /* for reconstruct, we always reschedule after a read. + * for resync, only after all reads + */ + if (test_bit(R10BIO_IsRecover, &r10_bio->state) || + atomic_dec_and_test(&r10_bio->remaining)) { + /* we have read all the blocks, + * do the comparison in process context in raid10d + */ + reschedule_retry(r10_bio); + } + rdev_dec_pending(conf->mirrors[d].rdev, conf->mddev); + return 0; +} + +static int end_sync_write(struct bio *bio, unsigned int bytes_done, int error) +{ + int uptodate = test_bit(BIO_UPTODATE, &bio->bi_flags); + r10bio_t * r10_bio = (r10bio_t *)(bio->bi_private); + mddev_t *mddev = r10_bio->mddev; + conf_t *conf = mddev_to_conf(mddev); + int i,d; + + if (bio->bi_size) + return 1; + + for (i = 0; i < conf->copies; i++) + if (r10_bio->devs[i].bio == bio) + break; + d = r10_bio->devs[i].devnum; + + if (!uptodate) + md_error(mddev, conf->mirrors[d].rdev); + update_head_pos(i, r10_bio); + + while (atomic_dec_and_test(&r10_bio->remaining)) { + if (r10_bio->master_bio == NULL) { + /* the primary of several recovery bios */ + md_done_sync(mddev, r10_bio->sectors, 1); + put_buf(r10_bio); + break; + } else { + r10bio_t *r10_bio2 = (r10bio_t *)r10_bio->master_bio; + put_buf(r10_bio); + r10_bio = r10_bio2; + } + } + rdev_dec_pending(conf->mirrors[d].rdev, mddev); + return 0; +} + +/* + * Note: sync and recover and handled very differently for raid10 + * This code is for resync. + * For resync, we read through virtual addresses and read all blocks. + * If there is any error, we schedule a write. The lowest numbered + * drive is authoritative. + * However requests come for physical address, so we need to map. + * For every physical address there are raid_disks/copies virtual addresses, + * which is always are least one, but is not necessarly an integer. + * This means that a physical address can span multiple chunks, so we may + * have to submit multiple io requests for a single sync request. + */ +/* + * We check if all blocks are in-sync and only write to blocks that + * aren't in sync + */ +static void sync_request_write(mddev_t *mddev, r10bio_t *r10_bio) +{ + conf_t *conf = mddev_to_conf(mddev); + int i, first; + struct bio *tbio, *fbio; + + atomic_set(&r10_bio->remaining, 1); + + /* find the first device with a block */ + for (i=0; icopies; i++) + if (test_bit(BIO_UPTODATE, &r10_bio->devs[i].bio->bi_flags)) + break; + + if (i == conf->copies) + goto done; + + first = i; + fbio = r10_bio->devs[i].bio; + + /* now find blocks with errors */ + for (i=first+1 ; i < conf->copies ; i++) { + int vcnt, j, d; + + if (!test_bit(BIO_UPTODATE, &r10_bio->devs[i].bio->bi_flags)) + continue; + /* We know that the bi_io_vec layout is the same for + * both 'first' and 'i', so we just compare them. + * All vec entries are PAGE_SIZE; + */ + tbio = r10_bio->devs[i].bio; + vcnt = r10_bio->sectors >> (PAGE_SHIFT-9); + for (j = 0; j < vcnt; j++) + if (memcmp(page_address(fbio->bi_io_vec[j].bv_page), + page_address(tbio->bi_io_vec[j].bv_page), + PAGE_SIZE)) + break; + if (j == vcnt) + continue; + /* Ok, we need to write this bio + * First we need to fixup bv_offset, bv_len and + * bi_vecs, as the read request might have corrupted these + */ + tbio->bi_vcnt = vcnt; + tbio->bi_size = r10_bio->sectors << 9; + tbio->bi_idx = 0; + tbio->bi_phys_segments = 0; + tbio->bi_hw_segments = 0; + tbio->bi_hw_front_size = 0; + tbio->bi_hw_back_size = 0; + tbio->bi_flags &= ~(BIO_POOL_MASK - 1); + tbio->bi_flags |= 1 << BIO_UPTODATE; + tbio->bi_next = NULL; + tbio->bi_rw = WRITE; + tbio->bi_private = r10_bio; + tbio->bi_sector = r10_bio->devs[i].addr; + + for (j=0; j < vcnt ; j++) { + tbio->bi_io_vec[j].bv_offset = 0; + tbio->bi_io_vec[j].bv_len = PAGE_SIZE; + + memcpy(page_address(tbio->bi_io_vec[j].bv_page), + page_address(fbio->bi_io_vec[j].bv_page), + PAGE_SIZE); + } + tbio->bi_end_io = end_sync_write; + + d = r10_bio->devs[i].devnum; + atomic_inc(&conf->mirrors[d].rdev->nr_pending); + atomic_inc(&r10_bio->remaining); + md_sync_acct(conf->mirrors[d].rdev->bdev, tbio->bi_size >> 9); + + generic_make_request(tbio); + } + +done: + if (atomic_dec_and_test(&r10_bio->remaining)) { + md_done_sync(mddev, r10_bio->sectors, 1); + put_buf(r10_bio); + } +} + +/* + * Now for the recovery code. + * Recovery happens across physical sectors. + * We recover all non-is_sync drives by finding the virtual address of + * each, and then choose a working drive that also has that virt address. + * There is a separate r10_bio for each non-in_sync drive. + * Only the first two slots are in use. The first for reading, + * The second for writing. + * + */ + +static void recovery_request_write(mddev_t *mddev, r10bio_t *r10_bio) +{ + conf_t *conf = mddev_to_conf(mddev); + int i, d; + struct bio *bio, *wbio; + + + /* move the pages across to the second bio + * and submit the write request + */ + bio = r10_bio->devs[0].bio; + wbio = r10_bio->devs[1].bio; + for (i=0; i < wbio->bi_vcnt; i++) { + struct page *p = bio->bi_io_vec[i].bv_page; + bio->bi_io_vec[i].bv_page = wbio->bi_io_vec[i].bv_page; + wbio->bi_io_vec[i].bv_page = p; + } + d = r10_bio->devs[1].devnum; + + atomic_inc(&conf->mirrors[d].rdev->nr_pending); + md_sync_acct(conf->mirrors[d].rdev->bdev, wbio->bi_size >> 9); + generic_make_request(wbio); +} + + +/* + * This is a kernel thread which: + * + * 1. Retries failed read operations on working mirrors. + * 2. Updates the raid superblock when problems encounter. + * 3. Performs writes following reads for array syncronising. + */ + +static void raid10d(mddev_t *mddev) +{ + r10bio_t *r10_bio; + struct bio *bio; + unsigned long flags; + conf_t *conf = mddev_to_conf(mddev); + struct list_head *head = &conf->retry_list; + int unplug=0; + mdk_rdev_t *rdev; + + md_check_recovery(mddev); + md_handle_safemode(mddev); + + for (;;) { + char b[BDEVNAME_SIZE]; + spin_lock_irqsave(&conf->device_lock, flags); + if (list_empty(head)) + break; + r10_bio = list_entry(head->prev, r10bio_t, retry_list); + list_del(head->prev); + spin_unlock_irqrestore(&conf->device_lock, flags); + + mddev = r10_bio->mddev; + conf = mddev_to_conf(mddev); + if (test_bit(R10BIO_IsSync, &r10_bio->state)) { + sync_request_write(mddev, r10_bio); + unplug = 1; + } else if (test_bit(R10BIO_IsRecover, &r10_bio->state)) { + recovery_request_write(mddev, r10_bio); + unplug = 1; + } else { + int mirror; + bio = r10_bio->devs[r10_bio->read_slot].bio; + r10_bio->devs[r10_bio->read_slot].bio = NULL; + mirror = read_balance(conf, r10_bio); + r10_bio->devs[r10_bio->read_slot].bio = bio; + if (mirror == -1) { + printk(KERN_ALERT "raid10: %s: unrecoverable I/O" + " read error for block %llu\n", + bdevname(bio->bi_bdev,b), + (unsigned long long)r10_bio->sector); + raid_end_bio_io(r10_bio); + } else { + rdev = conf->mirrors[mirror].rdev; + if (printk_ratelimit()) + printk(KERN_ERR "raid10: %s: redirecting sector %llu to" + " another mirror\n", + bdevname(rdev->bdev,b), + (unsigned long long)r10_bio->sector); + bio->bi_bdev = rdev->bdev; + bio->bi_sector = r10_bio->devs[r10_bio->read_slot].addr + + rdev->data_offset; + bio->bi_next = NULL; + bio->bi_flags &= (1<bi_flags |= 1 << BIO_UPTODATE; + bio->bi_idx = 0; + bio->bi_size = r10_bio->sectors << 9; + bio->bi_rw = READ; + unplug = 1; + generic_make_request(bio); + } + } + } + spin_unlock_irqrestore(&conf->device_lock, flags); + if (unplug) + unplug_slaves(mddev); +} + + +static int init_resync(conf_t *conf) +{ + int buffs; + + buffs = RESYNC_WINDOW / RESYNC_BLOCK_SIZE; + if (conf->r10buf_pool) + BUG(); + conf->r10buf_pool = mempool_create(buffs, r10buf_pool_alloc, r10buf_pool_free, conf); + if (!conf->r10buf_pool) + return -ENOMEM; + conf->next_resync = 0; + return 0; +} + +/* + * perform a "sync" on one "block" + * + * We need to make sure that no normal I/O request - particularly write + * requests - conflict with active sync requests. + * + * This is achieved by tracking pending requests and a 'barrier' concept + * that can be installed to exclude normal IO requests. + * + * Resync and recovery are handled very differently. + * We differentiate by looking at MD_RECOVERY_SYNC in mddev->recovery. + * + * For resync, we iterate over virtual addresses, read all copies, + * and update if there are differences. If only one copy is live, + * skip it. + * For recovery, we iterate over physical addresses, read a good + * value for each non-in_sync drive, and over-write. + * + * So, for recovery we may have several outstanding complex requests for a + * given address, one for each out-of-sync device. We model this by allocating + * a number of r10_bio structures, one for each out-of-sync device. + * As we setup these structures, we collect all bio's together into a list + * which we then process collectively to add pages, and then process again + * to pass to generic_make_request. + * + * The r10_bio structures are linked using a borrowed master_bio pointer. + * This link is counted in ->remaining. When the r10_bio that points to NULL + * has its remaining count decremented to 0, the whole complex operation + * is complete. + * + */ + +static int sync_request(mddev_t *mddev, sector_t sector_nr, int go_faster) +{ + conf_t *conf = mddev_to_conf(mddev); + r10bio_t *r10_bio; + struct bio *biolist = NULL, *bio; + sector_t max_sector, nr_sectors; + int disk; + int i; + + sector_t sectors_skipped = 0; + int chunks_skipped = 0; + + if (!conf->r10buf_pool) + if (init_resync(conf)) + return -ENOMEM; + + skipped: + max_sector = mddev->size << 1; + if (test_bit(MD_RECOVERY_SYNC, &mddev->recovery)) + max_sector = mddev->resync_max_sectors; + if (sector_nr >= max_sector) { + close_sync(conf); + return sectors_skipped; + } + if (chunks_skipped >= conf->raid_disks) { + /* if there has been nothing to do on any drive, + * then there is nothing to do at all.. + */ + sector_t sec = max_sector - sector_nr; + md_done_sync(mddev, sec, 1); + return sec + sectors_skipped; + } + + /* make sure whole request will fit in a chunk - if chunks + * are meaningful + */ + if (conf->near_copies < conf->raid_disks && + max_sector > (sector_nr | conf->chunk_mask)) + max_sector = (sector_nr | conf->chunk_mask) + 1; + /* + * If there is non-resync activity waiting for us then + * put in a delay to throttle resync. + */ + if (!go_faster && waitqueue_active(&conf->wait_resume)) + schedule_timeout(HZ); + device_barrier(conf, sector_nr + RESYNC_SECTORS); + + /* Again, very different code for resync and recovery. + * Both must result in an r10bio with a list of bios that + * have bi_end_io, bi_sector, bi_bdev set, + * and bi_private set to the r10bio. + * For recovery, we may actually create several r10bios + * with 2 bios in each, that correspond to the bios in the main one. + * In this case, the subordinate r10bios link back through a + * borrowed master_bio pointer, and the counter in the master + * includes a ref from each subordinate. + */ + /* First, we decide what to do and set ->bi_end_io + * To end_sync_read if we want to read, and + * end_sync_write if we will want to write. + */ + + if (!test_bit(MD_RECOVERY_SYNC, &mddev->recovery)) { + /* recovery... the complicated one */ + int i, j, k; + r10_bio = NULL; + + for (i=0 ; iraid_disks; i++) + if (conf->mirrors[i].rdev && + !conf->mirrors[i].rdev->in_sync) { + /* want to reconstruct this device */ + r10bio_t *rb2 = r10_bio; + + r10_bio = mempool_alloc(conf->r10buf_pool, GFP_NOIO); + spin_lock_irq(&conf->resync_lock); + conf->nr_pending++; + if (rb2) conf->barrier++; + spin_unlock_irq(&conf->resync_lock); + atomic_set(&r10_bio->remaining, 0); + + r10_bio->master_bio = (struct bio*)rb2; + if (rb2) + atomic_inc(&rb2->remaining); + r10_bio->mddev = mddev; + set_bit(R10BIO_IsRecover, &r10_bio->state); + r10_bio->sector = raid10_find_virt(conf, sector_nr, i); + raid10_find_phys(conf, r10_bio); + for (j=0; jcopies;j++) { + int d = r10_bio->devs[j].devnum; + if (conf->mirrors[d].rdev && + conf->mirrors[d].rdev->in_sync) { + /* This is where we read from */ + bio = r10_bio->devs[0].bio; + bio->bi_next = biolist; + biolist = bio; + bio->bi_private = r10_bio; + bio->bi_end_io = end_sync_read; + bio->bi_rw = 0; + bio->bi_sector = r10_bio->devs[j].addr + + conf->mirrors[d].rdev->data_offset; + bio->bi_bdev = conf->mirrors[d].rdev->bdev; + atomic_inc(&conf->mirrors[d].rdev->nr_pending); + atomic_inc(&r10_bio->remaining); + /* and we write to 'i' */ + + for (k=0; kcopies; k++) + if (r10_bio->devs[k].devnum == i) + break; + bio = r10_bio->devs[1].bio; + bio->bi_next = biolist; + biolist = bio; + bio->bi_private = r10_bio; + bio->bi_end_io = end_sync_write; + bio->bi_rw = 1; + bio->bi_sector = r10_bio->devs[k].addr + + conf->mirrors[i].rdev->data_offset; + bio->bi_bdev = conf->mirrors[i].rdev->bdev; + + r10_bio->devs[0].devnum = d; + r10_bio->devs[1].devnum = i; + + break; + } + } + if (j == conf->copies) { + BUG(); + } + } + if (biolist == NULL) { + while (r10_bio) { + r10bio_t *rb2 = r10_bio; + r10_bio = (r10bio_t*) rb2->master_bio; + rb2->master_bio = NULL; + put_buf(rb2); + } + goto giveup; + } + } else { + /* resync. Schedule a read for every block at this virt offset */ + int count = 0; + r10_bio = mempool_alloc(conf->r10buf_pool, GFP_NOIO); + + spin_lock_irq(&conf->resync_lock); + conf->nr_pending++; + spin_unlock_irq(&conf->resync_lock); + + r10_bio->mddev = mddev; + atomic_set(&r10_bio->remaining, 0); + + r10_bio->master_bio = NULL; + r10_bio->sector = sector_nr; + set_bit(R10BIO_IsSync, &r10_bio->state); + raid10_find_phys(conf, r10_bio); + r10_bio->sectors = (sector_nr | conf->chunk_mask) - sector_nr +1; + spin_lock_irq(&conf->device_lock); + for (i=0; icopies; i++) { + int d = r10_bio->devs[i].devnum; + bio = r10_bio->devs[i].bio; + bio->bi_end_io = NULL; + if (conf->mirrors[d].rdev == NULL || + conf->mirrors[d].rdev->faulty) + continue; + atomic_inc(&conf->mirrors[d].rdev->nr_pending); + atomic_inc(&r10_bio->remaining); + bio->bi_next = biolist; + biolist = bio; + bio->bi_private = r10_bio; + bio->bi_end_io = end_sync_read; + bio->bi_rw = 0; + bio->bi_sector = r10_bio->devs[i].addr + + conf->mirrors[d].rdev->data_offset; + bio->bi_bdev = conf->mirrors[d].rdev->bdev; + count++; + } + spin_unlock_irq(&conf->device_lock); + if (count < 2) { + for (i=0; icopies; i++) { + int d = r10_bio->devs[i].devnum; + if (r10_bio->devs[i].bio->bi_end_io) + atomic_dec(&conf->mirrors[d].rdev->nr_pending); + } + put_buf(r10_bio); + goto giveup; + } + } + + for (bio = biolist; bio ; bio=bio->bi_next) { + + bio->bi_flags &= ~(BIO_POOL_MASK - 1); + if (bio->bi_end_io) + bio->bi_flags |= 1 << BIO_UPTODATE; + bio->bi_vcnt = 0; + bio->bi_idx = 0; + bio->bi_phys_segments = 0; + bio->bi_hw_segments = 0; + bio->bi_size = 0; + } + + nr_sectors = 0; + do { + struct page *page; + int len = PAGE_SIZE; + disk = 0; + if (sector_nr + (len>>9) > max_sector) + len = (max_sector - sector_nr) << 9; + if (len == 0) + break; + for (bio= biolist ; bio ; bio=bio->bi_next) { + page = bio->bi_io_vec[bio->bi_vcnt].bv_page; + if (bio_add_page(bio, page, len, 0) == 0) { + /* stop here */ + struct bio *bio2; + bio->bi_io_vec[bio->bi_vcnt].bv_page = page; + for (bio2 = biolist; bio2 && bio2 != bio; bio2 = bio2->bi_next) { + /* remove last page from this bio */ + bio2->bi_vcnt--; + bio2->bi_size -= len; + bio2->bi_flags &= ~(1<< BIO_SEG_VALID); + } + goto bio_full; + } + disk = i; + } + nr_sectors += len>>9; + sector_nr += len>>9; + } while (biolist->bi_vcnt < RESYNC_PAGES); + bio_full: + r10_bio->sectors = nr_sectors; + + while (biolist) { + bio = biolist; + biolist = biolist->bi_next; + + bio->bi_next = NULL; + r10_bio = bio->bi_private; + r10_bio->sectors = nr_sectors; + + if (bio->bi_end_io == end_sync_read) { + md_sync_acct(bio->bi_bdev, nr_sectors); + generic_make_request(bio); + } + } + + return nr_sectors; + giveup: + /* There is nowhere to write, so all non-sync + * drives must be failed, so try the next chunk... + */ + { + int sec = max_sector - sector_nr; + sectors_skipped += sec; + chunks_skipped ++; + sector_nr = max_sector; + md_done_sync(mddev, sec, 1); + goto skipped; + } +} + +static int run(mddev_t *mddev) +{ + conf_t *conf; + int i, disk_idx; + mirror_info_t *disk; + mdk_rdev_t *rdev; + struct list_head *tmp; + int nc, fc; + sector_t stride, size; + + if (mddev->level != 10) { + printk(KERN_ERR "raid10: %s: raid level not set correctly... (%d)\n", + mdname(mddev), mddev->level); + goto out; + } + nc = mddev->layout & 255; + fc = (mddev->layout >> 8) & 255; + if ((nc*fc) <2 || (nc*fc) > mddev->raid_disks || + (mddev->layout >> 16)) { + printk(KERN_ERR "raid10: %s: unsupported raid10 layout: 0x%8x\n", + mdname(mddev), mddev->layout); + goto out; + } + /* + * copy the already verified devices into our private RAID10 + * bookkeeping area. [whatever we allocate in run(), + * should be freed in stop()] + */ + conf = kmalloc(sizeof(conf_t), GFP_KERNEL); + mddev->private = conf; + if (!conf) { + printk(KERN_ERR "raid10: couldn't allocate memory for %s\n", + mdname(mddev)); + goto out; + } + memset(conf, 0, sizeof(*conf)); + conf->mirrors = kmalloc(sizeof(struct mirror_info)*mddev->raid_disks, + GFP_KERNEL); + if (!conf->mirrors) { + printk(KERN_ERR "raid10: couldn't allocate memory for %s\n", + mdname(mddev)); + goto out_free_conf; + } + memset(conf->mirrors, 0, sizeof(struct mirror_info)*mddev->raid_disks); + + conf->near_copies = nc; + conf->far_copies = fc; + conf->copies = nc*fc; + conf->chunk_mask = (sector_t)(mddev->chunk_size>>9)-1; + conf->chunk_shift = ffz(~mddev->chunk_size) - 9; + stride = mddev->size >> (conf->chunk_shift-1); + sector_div(stride, fc); + conf->stride = stride << conf->chunk_shift; + + conf->r10bio_pool = mempool_create(NR_RAID10_BIOS, r10bio_pool_alloc, + r10bio_pool_free, conf); + if (!conf->r10bio_pool) { + printk(KERN_ERR "raid10: couldn't allocate memory for %s\n", + mdname(mddev)); + goto out_free_conf; + } + mddev->queue->unplug_fn = raid10_unplug; + + mddev->queue->issue_flush_fn = raid10_issue_flush; + + ITERATE_RDEV(mddev, rdev, tmp) { + disk_idx = rdev->raid_disk; + if (disk_idx >= mddev->raid_disks + || disk_idx < 0) + continue; + disk = conf->mirrors + disk_idx; + + disk->rdev = rdev; + + blk_queue_stack_limits(mddev->queue, + rdev->bdev->bd_disk->queue); + /* as we don't honour merge_bvec_fn, we must never risk + * violating it, so limit ->max_sector to one PAGE, as + * a one page request is never in violation. + */ + if (rdev->bdev->bd_disk->queue->merge_bvec_fn && + mddev->queue->max_sectors > (PAGE_SIZE>>9)) + mddev->queue->max_sectors = (PAGE_SIZE>>9); + + disk->head_position = 0; + if (!rdev->faulty && rdev->in_sync) + conf->working_disks++; + } + conf->raid_disks = mddev->raid_disks; + conf->mddev = mddev; + conf->device_lock = SPIN_LOCK_UNLOCKED; + INIT_LIST_HEAD(&conf->retry_list); + + conf->resync_lock = SPIN_LOCK_UNLOCKED; + init_waitqueue_head(&conf->wait_idle); + init_waitqueue_head(&conf->wait_resume); + + if (!conf->working_disks) { + printk(KERN_ERR "raid10: no operational mirrors for %s\n", + mdname(mddev)); + goto out_free_conf; + } + + mddev->degraded = 0; + for (i = 0; i < conf->raid_disks; i++) { + + disk = conf->mirrors + i; + + if (!disk->rdev) { + disk->head_position = 0; + mddev->degraded++; + } + } + + + mddev->thread = md_register_thread(raid10d, mddev, "%s_raid10"); + if (!mddev->thread) { + printk(KERN_ERR + "raid10: couldn't allocate thread for %s\n", + mdname(mddev)); + goto out_free_conf; + } + + printk(KERN_INFO + "raid10: raid set %s active with %d out of %d devices\n", + mdname(mddev), mddev->raid_disks - mddev->degraded, + mddev->raid_disks); + /* + * Ok, everything is just fine now + */ + size = conf->stride * conf->raid_disks; + sector_div(size, conf->near_copies); + mddev->array_size = size/2; + mddev->resync_max_sectors = size; + + /* Calculate max read-ahead size. + * We need to readahead at least twice a whole stripe.... + * maybe... + */ + { + int stripe = conf->raid_disks * mddev->chunk_size / PAGE_CACHE_SIZE; + stripe /= conf->near_copies; + if (mddev->queue->backing_dev_info.ra_pages < 2* stripe) + mddev->queue->backing_dev_info.ra_pages = 2* stripe; + } + + if (conf->near_copies < mddev->raid_disks) + blk_queue_merge_bvec(mddev->queue, raid10_mergeable_bvec); + return 0; + +out_free_conf: + if (conf->r10bio_pool) + mempool_destroy(conf->r10bio_pool); + if (conf->mirrors) + kfree(conf->mirrors); + kfree(conf); + mddev->private = NULL; +out: + return -EIO; +} + +static int stop(mddev_t *mddev) +{ + conf_t *conf = mddev_to_conf(mddev); + + md_unregister_thread(mddev->thread); + mddev->thread = NULL; + if (conf->r10bio_pool) + mempool_destroy(conf->r10bio_pool); + if (conf->mirrors) + kfree(conf->mirrors); + kfree(conf); + mddev->private = NULL; + return 0; +} + + +static mdk_personality_t raid10_personality = +{ + .name = "raid10", + .owner = THIS_MODULE, + .make_request = make_request, + .run = run, + .stop = stop, + .status = status, + .error_handler = error, + .hot_add_disk = raid10_add_disk, + .hot_remove_disk= raid10_remove_disk, + .spare_active = raid10_spare_active, + .sync_request = sync_request, +}; + +static int __init raid_init(void) +{ + return register_md_personality(RAID10, &raid10_personality); +} + +static void raid_exit(void) +{ + unregister_md_personality(RAID10); +} + +module_init(raid_init); +module_exit(raid_exit); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("md-personality-9"); /* RAID10 */ diff --git a/drivers/media/common/saa7146_i2c.c b/drivers/media/common/saa7146_i2c.c index 18bc5b671..118cace19 100644 --- a/drivers/media/common/saa7146_i2c.c +++ b/drivers/media/common/saa7146_i2c.c @@ -1,13 +1,6 @@ #include #include -/* helper function */ -static void my_wait(struct saa7146_dev *dev, long ms) -{ - set_current_state(TASK_INTERRUPTIBLE); - schedule_timeout((((ms+10)/10)*HZ)/1000); -} - u32 saa7146_i2c_func(struct i2c_adapter *adapter) { //fm DEB_I2C(("'%s'.\n", adapter->name)); @@ -136,12 +129,12 @@ static int saa7146_i2c_reset(struct saa7146_dev *dev) /* set "ABORT-OPERATION"-bit (bit 7)*/ saa7146_write(dev, I2C_STATUS, (dev->i2c_bitrate | MASK_07)); saa7146_write(dev, MC2, (MASK_00 | MASK_16)); - my_wait(dev,SAA7146_I2C_DELAY); + msleep(SAA7146_I2C_DELAY); /* clear all error-bits pending; this is needed because p.123, note 1 */ saa7146_write(dev, I2C_STATUS, dev->i2c_bitrate); saa7146_write(dev, MC2, (MASK_00 | MASK_16)); - my_wait(dev,SAA7146_I2C_DELAY); + msleep(SAA7146_I2C_DELAY); } /* check if any error is (still) present. (this can be necessary because p.123, note 1) */ @@ -155,18 +148,18 @@ static int saa7146_i2c_reset(struct saa7146_dev *dev) after serious protocol errors caused by e.g. the SAA7740 */ saa7146_write(dev, I2C_STATUS, (dev->i2c_bitrate | MASK_07)); saa7146_write(dev, MC2, (MASK_00 | MASK_16)); - my_wait(dev,SAA7146_I2C_DELAY); + msleep(SAA7146_I2C_DELAY); /* clear all error-bits pending */ saa7146_write(dev, I2C_STATUS, dev->i2c_bitrate); saa7146_write(dev, MC2, (MASK_00 | MASK_16)); - my_wait(dev,SAA7146_I2C_DELAY); + msleep(SAA7146_I2C_DELAY); /* the data sheet says it might be necessary to clear the status twice after an abort */ saa7146_write(dev, I2C_STATUS, dev->i2c_bitrate); saa7146_write(dev, MC2, (MASK_00 | MASK_16)); - my_wait(dev,SAA7146_I2C_DELAY); + msleep(SAA7146_I2C_DELAY); } /* if any error is still present, a fatal error has occured ... */ @@ -243,7 +236,7 @@ static int saa7146_i2c_writeout(struct saa7146_dev *dev, u32* dword, int short_d if ((++trial < 20) && short_delay) udelay(10); else - my_wait(dev,1); + msleep(1); } } @@ -345,7 +338,7 @@ int saa7146_i2c_transfer(struct saa7146_dev *dev, const struct i2c_msg msgs[], i } /* delay a bit before retrying */ - my_wait(dev, 10); + msleep(10); } while (err != num && retries--); diff --git a/drivers/media/dvb/bt8xx/bt878.c b/drivers/media/dvb/bt8xx/bt878.c index 1619f4c3f..7f7ec3314 100644 --- a/drivers/media/dvb/bt8xx/bt878.c +++ b/drivers/media/dvb/bt8xx/bt878.c @@ -417,6 +417,8 @@ static int __devinit bt878_probe(struct pci_dev *dev, printk(KERN_INFO "bt878: Bt878 AUDIO function found (%d).\n", bt878_num); + if (pci_enable_device(dev)) + return -EIO; bt = &bt878[bt878_num]; bt->dev = dev; @@ -426,11 +428,10 @@ static int __devinit bt878_probe(struct pci_dev *dev, bt->id = dev->device; bt->irq = dev->irq; bt->bt878_adr = pci_resource_start(dev, 0); - if (pci_enable_device(dev)) - return -EIO; if (!request_mem_region(pci_resource_start(dev, 0), pci_resource_len(dev, 0), "bt878")) { - return -EBUSY; + result = -EBUSY; + goto fail0; } pci_read_config_byte(dev, PCI_CLASS_REVISION, &bt->revision); @@ -501,6 +502,8 @@ static int __devinit bt878_probe(struct pci_dev *dev, fail1: release_mem_region(pci_resource_start(bt->dev, 0), pci_resource_len(bt->dev, 0)); + fail0: + pci_disable_device(dev); return result; } @@ -517,7 +520,7 @@ static void __devexit bt878_remove(struct pci_dev *pci_dev) /* first disable interrupts before unmapping the memory! */ btwrite(0, BT878_AINT_MASK); - btwrite(~0x0UL, BT878_AINT_STAT); + btwrite(~0U, BT878_AINT_STAT); /* disable PCI bus-mastering */ pci_read_config_byte(bt->dev, PCI_COMMAND, &command); @@ -540,6 +543,7 @@ static void __devexit bt878_remove(struct pci_dev *pci_dev) bt878_mem_free(bt); pci_set_drvdata(pci_dev, NULL); + pci_disable_device(pci_dev); return; } diff --git a/drivers/media/dvb/frontends/alps_tdlb7.c b/drivers/media/dvb/frontends/alps_tdlb7.c index cf165f2d5..8ca5b0289 100644 --- a/drivers/media/dvb/frontends/alps_tdlb7.c +++ b/drivers/media/dvb/frontends/alps_tdlb7.c @@ -54,9 +54,6 @@ static int debug = 0; /* starting point for firmware in file 'Sc_main.mc' */ #define SP8870_FIRMWARE_OFFSET 0x0A - -static int errno; - static struct dvb_frontend_info tdlb7_info = { .name = "Alps TDLB7", .type = FE_OFDM, diff --git a/drivers/media/dvb/frontends/dst.c b/drivers/media/dvb/frontends/dst.c index 431ea9253..4834db951 100644 --- a/drivers/media/dvb/frontends/dst.c +++ b/drivers/media/dvb/frontends/dst.c @@ -1145,8 +1145,8 @@ static int dst_attach (struct dvb_i2c_bus *i2c, void **data) } dst_init (dst); - dprintk("%s: register dst %8.8x bt %8.8x i2c %8.8x\n", __FUNCTION__, - (u32)dst, (u32)(dst->bt), (u32)(dst->i2c)); + dprintk("%s: register dst %p bt %p i2c %p\n", __FUNCTION__, + dst, dst->bt, dst->i2c); info = &dst_info_sat; if (dst->dst_type == DST_TYPE_IS_TERR) @@ -1162,7 +1162,7 @@ static int dst_attach (struct dvb_i2c_bus *i2c, void **data) static void dst_detach (struct dvb_i2c_bus *i2c, void *data) { dvb_unregister_frontend (dst_ioctl, i2c); - dprintk("%s: unregister dst %8.8x\n", __FUNCTION__, (u32)(data)); + dprintk("%s: unregister dst %p\n", __FUNCTION__, data); if (data) kfree(data); } diff --git a/drivers/media/dvb/frontends/sp887x.c b/drivers/media/dvb/frontends/sp887x.c index 53ebf4a69..0aeb8124c 100644 --- a/drivers/media/dvb/frontends/sp887x.c +++ b/drivers/media/dvb/frontends/sp887x.c @@ -67,8 +67,6 @@ struct dvb_frontend_info sp887x_info = { FE_CAN_RECOVER }; -static int errno; - static int i2c_writebytes (struct dvb_frontend *fe, u8 addr, u8 *buf, u8 len) { diff --git a/drivers/media/dvb/ttpci/Kconfig b/drivers/media/dvb/ttpci/Kconfig index 99647b5ff..a5ff5ec02 100644 --- a/drivers/media/dvb/ttpci/Kconfig +++ b/drivers/media/dvb/ttpci/Kconfig @@ -1,6 +1,6 @@ config DVB_AV7110 tristate "AV7110 cards" - depends on DVB_CORE + depends on DVB_CORE && PCI select FW_LOADER select VIDEO_DEV select VIDEO_SAA7146_VV @@ -45,7 +45,7 @@ config DVB_AV7110_OSD config DVB_BUDGET tristate "Budget cards" - depends on DVB_CORE + depends on DVB_CORE && PCI select VIDEO_SAA7146 help Support for simple SAA7146 based DVB cards @@ -59,7 +59,7 @@ config DVB_BUDGET config DVB_BUDGET_CI tristate "Budget cards with onboard CI connector" - depends on DVB_CORE + depends on DVB_CORE && PCI select VIDEO_SAA7146 help Support for simple SAA7146 based DVB cards @@ -76,7 +76,7 @@ config DVB_BUDGET_CI config DVB_BUDGET_AV tristate "Budget cards with analog video inputs" - depends on DVB_CORE + depends on DVB_CORE && PCI select VIDEO_DEV select VIDEO_SAA7146_VV help diff --git a/drivers/media/dvb/ttpci/Makefile b/drivers/media/dvb/ttpci/Makefile index 2e8403b89..043468a1d 100644 --- a/drivers/media/dvb/ttpci/Makefile +++ b/drivers/media/dvb/ttpci/Makefile @@ -13,7 +13,7 @@ obj-$(CONFIG_DVB_AV7110) += dvb-ttpci.o ttpci-eeprom.o EXTRA_CFLAGS = -Idrivers/media/dvb/dvb-core/ -host-progs := fdump +hostprogs-y := fdump ifdef CONFIG_DVB_AV7110_FIRMWARE $(obj)/av7110.o: $(obj)/fdump $(obj)/av7110_firm.h diff --git a/drivers/media/radio/radio-aimslab.c b/drivers/media/radio/radio-aimslab.c index c6e648753..7aab2ed62 100644 --- a/drivers/media/radio/radio-aimslab.c +++ b/drivers/media/radio/radio-aimslab.c @@ -63,12 +63,7 @@ static void sleep_delay(long n) if(!d) udelay(n); else - { - /* Yield CPU time */ - unsigned long x=jiffies; - while((jiffies-x)<=d) - schedule(); - } + msleep(jiffies_to_msecs(d)); } static void rt_decvol(void) diff --git a/drivers/media/radio/radio-maestro.c b/drivers/media/radio/radio-maestro.c index 6a18c0088..b226aa437 100644 --- a/drivers/media/radio/radio-maestro.c +++ b/drivers/media/radio/radio-maestro.c @@ -93,27 +93,6 @@ static struct radio_device struct semaphore lock; } radio_unit = {0, 0, 0, 0, }; -static void sleep_125ms(void) -{ - current->state = TASK_INTERRUPTIBLE; - schedule_timeout(HZ >> 3); -} - -static void udelay2(void) -{ - udelay(2); -} - -static void udelay4(void) -{ - udelay(4); -} - -static void udelay16(void) -{ - udelay(16); -} - static __u32 radio_bits_get(struct radio_device *dev) { register __u16 io=dev->io, l, rdata; @@ -122,14 +101,15 @@ static __u32 radio_bits_get(struct radio_device *dev) omask = inw(io + IO_MASK); outw(~(STR_CLK | STR_WREN), io + IO_MASK); outw(0, io); - udelay16(); + udelay(16); + for (l=24;l--;) { outw(STR_CLK, io); /* HI state */ - udelay2(); + udelay(2); if(!l) dev->tuned = inw(io) & STR_MOST ? 0 : 0xffff; outw(0, io); /* LO state */ - udelay2(); + udelay(2); data <<= 1; /* shift data */ rdata = inw(io); if(!l) @@ -138,11 +118,11 @@ static __u32 radio_bits_get(struct radio_device *dev) else if(rdata & STR_DATA) data++; - udelay2(); + udelay(2); } if(dev->muted) outw(STR_WREN, io); - udelay4(); + udelay(4); outw(omask, io + IO_MASK); return data & 0x3ffe; } @@ -155,23 +135,23 @@ static void radio_bits_set(struct radio_device *dev, __u32 data) odir = (inw(io + IO_DIR) & ~STR_DATA) | (STR_CLK | STR_WREN); outw(odir | STR_DATA, io + IO_DIR); outw(~(STR_DATA | STR_CLK | STR_WREN), io + IO_MASK); - udelay16(); + udelay(16); for (l=25;l;l--) { bits = ((data >> 18) & STR_DATA) | STR_WREN ; data <<= 1; /* shift data */ outw(bits, io); /* start strobe */ - udelay2(); + udelay(2); outw(bits | STR_CLK, io); /* HI level */ - udelay2(); + udelay(2); outw(bits, io); /* LO level */ - udelay4(); + udelay(4); } if(!dev->muted) outw(0, io); - udelay4(); + udelay(4); outw(omask, io + IO_MASK); outw(odir, io + IO_DIR); - sleep_125ms(); + msleep(125); } inline static int radio_function(struct inode *inode, struct file *file, @@ -238,9 +218,9 @@ inline static int radio_function(struct inode *inode, struct file *file, outw(~STR_WREN, io + IO_MASK); outw((card->muted = v->flags & VIDEO_AUDIO_MUTE) ? STR_WREN : 0, io); - udelay4(); + udelay(4); outw(omask, io + IO_MASK); - sleep_125ms(); + msleep(125); return 0; } } @@ -270,7 +250,7 @@ static int radio_ioctl(struct inode *inode, struct file *file, return ret; } -inline static __u16 radio_install(struct pci_dev *pcidev); +static __u16 radio_install(struct pci_dev *pcidev); MODULE_AUTHOR("Adam Tlalka, atlka@pg.gda.pl"); MODULE_DESCRIPTION("Radio driver for the Maestro PCI sound card radio."); @@ -315,7 +295,7 @@ inline static __u16 radio_power_on(struct radio_device *dev) outw(odir, io + IO_DIR); outw(~(STR_WREN | STR_CLK), io + IO_MASK); outw(dev->muted ? 0 : STR_WREN, io); - udelay16(); + udelay(16); outw(omask, io + IO_MASK); ofreq = radio_bits_get(dev); if((ofreqFREQ2BITS(FREQ_HI))) @@ -324,7 +304,7 @@ inline static __u16 radio_power_on(struct radio_device *dev) return (ofreq == radio_bits_get(dev)); } -inline static __u16 radio_install(struct pci_dev *pcidev) +static __u16 radio_install(struct pci_dev *pcidev) { if(((pcidev->class >> 8) & 0xffff) != PCI_CLASS_MULTIMEDIA_AUDIO) return 0; diff --git a/drivers/media/radio/radio-maxiradio.c b/drivers/media/radio/radio-maxiradio.c index c2fa20d77..c3c62095f 100644 --- a/drivers/media/radio/radio-maxiradio.c +++ b/drivers/media/radio/radio-maxiradio.c @@ -104,13 +104,6 @@ static struct radio_device } radio_unit = {0, 0, 0, 0, }; -static void sleep_125ms(void) -{ - current->state = TASK_INTERRUPTIBLE; - schedule_timeout(HZ >> 3); -} - - static void outbit(unsigned long bit, __u16 io) { if(bit != 0) @@ -228,7 +221,7 @@ inline static int radio_function(struct inode *inode, struct file *file, return -EINVAL; card->freq = *freq; set_freq(card->io, FREQ2BITS(card->freq)); - sleep_125ms(); + msleep(125); return 0; } case VIDIOCGAUDIO: { diff --git a/drivers/media/radio/radio-sf16fmi.c b/drivers/media/radio/radio-sf16fmi.c index 9962c43c7..f0575fb2c 100644 --- a/drivers/media/radio/radio-sf16fmi.c +++ b/drivers/media/radio/radio-sf16fmi.c @@ -89,8 +89,7 @@ static inline int fmi_setfreq(struct fmi_device *dev) outbits(16, RSF16_ENCODE(freq), myport); outbits(8, 0xC0, myport); - current->state = TASK_UNINTERRUPTIBLE; - schedule_timeout(HZ/7); + msleep(143); /* was schedule_timeout(HZ/7) */ up(&lock); if (dev->curvol) fmi_unmute(myport); return 0; @@ -107,8 +106,7 @@ static inline int fmi_getsigstr(struct fmi_device *dev) val = dev->curvol ? 0x08 : 0x00; /* unmute/mute */ outb(val, myport); outb(val | 0x10, myport); - set_current_state(TASK_UNINTERRUPTIBLE); - schedule_timeout(HZ/7); + msleep(143); /* was schedule_timeout(HZ/7) */ res = (int)inb(myport+1); outb(val, myport); diff --git a/drivers/media/radio/radio-sf16fmr2.c b/drivers/media/radio/radio-sf16fmr2.c index aa49c8e5c..7e980d15f 100644 --- a/drivers/media/radio/radio-sf16fmr2.c +++ b/drivers/media/radio/radio-sf16fmr2.c @@ -55,19 +55,6 @@ static int radio_nr = -1; #define RSF16_MINFREQ 87*16000 #define RSF16_MAXFREQ 108*16000 -/* from radio-aimslab */ -static void sleep_delay(unsigned long n) -{ - unsigned d=n/(1000000U/HZ); - if (!d) - udelay(n); - else - { - set_current_state(TASK_UNINTERRUPTIBLE); - schedule_timeout(d); - } -} - static inline void wait(int n,int port) { for (;n;--n) inb(port); @@ -153,7 +140,7 @@ static int fmr2_setfreq(struct fmr2_device *dev) fmr2_unmute(port); /* wait 0.11 sec */ - sleep_delay(110000LU); + msleep(110); /* NOTE if mute this stop radio you must set freq on unmute */ diff --git a/drivers/media/video/adv7175.c b/drivers/media/video/adv7175.c index 16bfdb9ec..74fefc2df 100644 --- a/drivers/media/video/adv7175.c +++ b/drivers/media/video/adv7175.c @@ -156,6 +156,22 @@ adv7175_write_block (struct i2c_client *client, return ret; } +static void +set_subcarrier_freq (struct i2c_client *client, + int pass_through) +{ + /* for some reason pass_through NTSC needs + * a different sub-carrier freq to remain stable. */ + if(pass_through) + adv7175_write(client, 0x02, 0x00); + else + adv7175_write(client, 0x02, 0x55); + + adv7175_write(client, 0x03, 0x55); + adv7175_write(client, 0x04, 0x55); + adv7175_write(client, 0x05, 0x25); +} + #ifdef ENCODER_DUMP static void dump (struct i2c_client *client) @@ -322,6 +338,10 @@ adv7175_command (struct i2c_client *client, case 0: adv7175_write(client, 0x01, 0x00); + + if (encoder->norm == VIDEO_MODE_NTSC) + set_subcarrier_freq(client, 1); + adv7175_write(client, 0x0c, TR1CAPT); /* TR1 */ if (encoder->norm == VIDEO_MODE_SECAM) adv7175_write(client, 0x0d, 0x49); // Disable genlock @@ -334,6 +354,10 @@ adv7175_command (struct i2c_client *client, case 1: adv7175_write(client, 0x01, 0x00); + + if (encoder->norm == VIDEO_MODE_NTSC) + set_subcarrier_freq(client, 0); + adv7175_write(client, 0x0c, TR1PLAY); /* TR1 */ adv7175_write(client, 0x0d, 0x49); adv7175_write(client, 0x07, TR0MODE | TR0RST); @@ -343,6 +367,10 @@ adv7175_command (struct i2c_client *client, case 2: adv7175_write(client, 0x01, 0x80); + + if (encoder->norm == VIDEO_MODE_NTSC) + set_subcarrier_freq(client, 0); + adv7175_write(client, 0x0d, 0x49); adv7175_write(client, 0x07, TR0MODE | TR0RST); adv7175_write(client, 0x07, TR0MODE); diff --git a/drivers/media/video/bttv-gpio.c b/drivers/media/video/bttv-gpio.c index 01ce7bc32..585f67dde 100644 --- a/drivers/media/video/bttv-gpio.c +++ b/drivers/media/video/bttv-gpio.c @@ -106,6 +106,20 @@ void bttv_gpio_irq(struct bttv_core *core) } } +void bttv_i2c_info(struct bttv_core *core, struct i2c_client *client, int attach) +{ + struct bttv_sub_driver *drv; + struct bttv_sub_device *dev; + struct list_head *item; + + list_for_each(item,&core->subs) { + dev = list_entry(item,struct bttv_sub_device,list); + drv = to_bttv_sub_drv(dev->dev.driver); + if (drv && drv->i2c_info) + drv->i2c_info(dev,client,attach); + } +} + /* ----------------------------------------------------------------------- */ /* external: sub-driver register/unregister */ diff --git a/drivers/media/video/zoran_device.c b/drivers/media/video/zoran_device.c index 705cff870..a35d518e2 100644 --- a/drivers/media/video/zoran_device.c +++ b/drivers/media/video/zoran_device.c @@ -1105,8 +1105,7 @@ zr36057_enable_jpg (struct zoran *zr, ZR36057_ISR); btand(~ZR36057_JMC_Go_en, ZR36057_JMC); // \Go_en - current->state = TASK_UNINTERRUPTIBLE; - schedule_timeout(HZ / 20); + msleep(50); set_videobus_dir(zr, 0); set_frame(zr, 1); // /FRAME diff --git a/drivers/message/fusion/Makefile b/drivers/message/fusion/Makefile index b709839fe..f6fdcaaef 100644 --- a/drivers/message/fusion/Makefile +++ b/drivers/message/fusion/Makefile @@ -2,7 +2,7 @@ # Makefile for the LSI Logic Fusion MPT (Message Passing Technology) drivers. # # Note! If you want to turn on various debug defines for an extended period of -# time but don't want them lingering around in the Makefile when you pass it on +# time but don't want them lingering around in the Makefile when you pass it on # to someone else, use the MPT_CFLAGS env variable (thanks Steve). -nromer #=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-{ LSI_LOGIC @@ -48,6 +48,5 @@ EXTRA_CFLAGS += ${MPT_CFLAGS} #=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-} LSI_LOGIC obj-$(CONFIG_FUSION) += mptbase.o mptscsih.o -obj-$(CONFIG_FUSION_ISENSE) += isense.o obj-$(CONFIG_FUSION_CTL) += mptctl.o obj-$(CONFIG_FUSION_LAN) += mptlan.o diff --git a/drivers/message/i2o/debug.c b/drivers/message/i2o/debug.c new file mode 100644 index 000000000..7227a8a1e --- /dev/null +++ b/drivers/message/i2o/debug.c @@ -0,0 +1,571 @@ +#include +#include +#include +#include +#include + +static int verbose; +extern struct i2o_driver **i2o_drivers; +extern unsigned int i2o_max_drivers; +static void i2o_report_util_cmd(u8 cmd); +static void i2o_report_exec_cmd(u8 cmd); +void i2o_report_fail_status(u8 req_status, u32 * msg); +void i2o_report_common_status(u8 req_status); +static void i2o_report_common_dsc(u16 detailed_status); + +void i2o_dump_status_block(i2o_status_block * sb) +{ + pr_debug("Organization ID: %d\n", sb->org_id); + pr_debug("IOP ID: %d\n", sb->iop_id); + pr_debug("Host Unit ID: %d\n", sb->host_unit_id); + pr_debug("Segment Number: %d\n", sb->segment_number); + pr_debug("I2O Version: %d\n", sb->i2o_version); + pr_debug("IOP State: %d\n", sb->iop_state); + pr_debug("Messanger Type: %d\n", sb->msg_type); + pr_debug("Inbound Frame Size: %d\n", sb->inbound_frame_size); + pr_debug("Init Code: %d\n", sb->init_code); + pr_debug("Max Inbound MFrames: %d\n", sb->max_inbound_frames); + pr_debug("Current Inbound MFrames: %d\n", sb->cur_inbound_frames); + pr_debug("Max Outbound MFrames: %d\n", sb->max_outbound_frames); + pr_debug("Product ID String: %s\n", sb->product_id); + pr_debug("Expected LCT Size: %d\n", sb->expected_lct_size); + pr_debug("IOP Capabilities: %d\n", sb->iop_capabilities); + pr_debug("Desired Private MemSize: %d\n", sb->desired_mem_size); + pr_debug("Current Private MemSize: %d\n", sb->current_mem_size); + pr_debug("Current Private MemBase: %d\n", sb->current_mem_base); + pr_debug("Desired Private IO Size: %d\n", sb->desired_io_size); + pr_debug("Current Private IO Size: %d\n", sb->current_io_size); + pr_debug("Current Private IO Base: %d\n", sb->current_io_base); +}; + +/* + * Used for error reporting/debugging purposes. + * Report Cmd name, Request status, Detailed Status. + */ +void i2o_report_status(const char *severity, const char *str, + struct i2o_message *m) +{ + u32 *msg = (u32 *) m; + u8 cmd = (msg[1] >> 24) & 0xFF; + u8 req_status = (msg[4] >> 24) & 0xFF; + u16 detailed_status = msg[4] & 0xFFFF; + //struct i2o_driver *h = i2o_drivers[msg[2] & (i2o_max_drivers-1)]; + + if (cmd == I2O_CMD_UTIL_EVT_REGISTER) + return; // No status in this reply + + printk("%s%s: ", severity, str); + + if (cmd < 0x1F) // Utility cmd + i2o_report_util_cmd(cmd); + + else if (cmd >= 0xA0 && cmd <= 0xEF) // Executive cmd + i2o_report_exec_cmd(cmd); + else + printk("Cmd = %0#2x, ", cmd); // Other cmds + + if (msg[0] & MSG_FAIL) { + i2o_report_fail_status(req_status, msg); + return; + } + + i2o_report_common_status(req_status); + + if (cmd < 0x1F || (cmd >= 0xA0 && cmd <= 0xEF)) + i2o_report_common_dsc(detailed_status); + else + printk(" / DetailedStatus = %0#4x.\n", detailed_status); +} + +/* Used to dump a message to syslog during debugging */ +void i2o_dump_message(struct i2o_message *m) +{ +#ifdef DEBUG + u32 *msg = (u32 *) m; + int i; + printk(KERN_INFO "Dumping I2O message size %d @ %p\n", + msg[0] >> 16 & 0xffff, msg); + for (i = 0; i < ((msg[0] >> 16) & 0xffff); i++) + printk(KERN_INFO " msg[%d] = %0#10x\n", i, msg[i]); +#endif +} + +/** + * i2o_report_controller_unit - print information about a tid + * @c: controller + * @d: device + * + * Dump an information block associated with a given unit (TID). The + * tables are read and a block of text is output to printk that is + * formatted intended for the user. + */ + +void i2o_report_controller_unit(struct i2o_controller *c, struct i2o_device *d) +{ + char buf[64]; + char str[22]; + int ret; + + if (verbose == 0) + return; + + printk(KERN_INFO "Target ID %03x.\n", d->lct_data.tid); + if ((ret = i2o_parm_field_get(d, 0xF100, 3, buf, 16)) >= 0) { + buf[16] = 0; + printk(KERN_INFO " Vendor: %s\n", buf); + } + if ((ret = i2o_parm_field_get(d, 0xF100, 4, buf, 16)) >= 0) { + buf[16] = 0; + printk(KERN_INFO " Device: %s\n", buf); + } + if (i2o_parm_field_get(d, 0xF100, 5, buf, 16) >= 0) { + buf[16] = 0; + printk(KERN_INFO " Description: %s\n", buf); + } + if ((ret = i2o_parm_field_get(d, 0xF100, 6, buf, 8)) >= 0) { + buf[8] = 0; + printk(KERN_INFO " Rev: %s\n", buf); + } + + printk(KERN_INFO " Class: "); + //sprintf(str, "%-21s", i2o_get_class_name(d->lct_data.class_id)); + printk("%s\n", str); + + printk(KERN_INFO " Subclass: 0x%04X\n", d->lct_data.sub_class); + printk(KERN_INFO " Flags: "); + + if (d->lct_data.device_flags & (1 << 0)) + printk("C"); // ConfigDialog requested + if (d->lct_data.device_flags & (1 << 1)) + printk("U"); // Multi-user capable + if (!(d->lct_data.device_flags & (1 << 4))) + printk("P"); // Peer service enabled! + if (!(d->lct_data.device_flags & (1 << 5))) + printk("M"); // Mgmt service enabled! + printk("\n"); +} + +/* +MODULE_PARM(verbose, "i"); +MODULE_PARM_DESC(verbose, "Verbose diagnostics"); +*/ +/* + * Used for error reporting/debugging purposes. + * Following fail status are common to all classes. + * The preserved message must be handled in the reply handler. + */ +void i2o_report_fail_status(u8 req_status, u32 * msg) +{ + static char *FAIL_STATUS[] = { + "0x80", /* not used */ + "SERVICE_SUSPENDED", /* 0x81 */ + "SERVICE_TERMINATED", /* 0x82 */ + "CONGESTION", + "FAILURE", + "STATE_ERROR", + "TIME_OUT", + "ROUTING_FAILURE", + "INVALID_VERSION", + "INVALID_OFFSET", + "INVALID_MSG_FLAGS", + "FRAME_TOO_SMALL", + "FRAME_TOO_LARGE", + "INVALID_TARGET_ID", + "INVALID_INITIATOR_ID", + "INVALID_INITIATOR_CONTEX", /* 0x8F */ + "UNKNOWN_FAILURE" /* 0xFF */ + }; + + if (req_status == I2O_FSC_TRANSPORT_UNKNOWN_FAILURE) + printk("TRANSPORT_UNKNOWN_FAILURE (%0#2x)\n.", req_status); + else + printk("TRANSPORT_%s.\n", FAIL_STATUS[req_status & 0x0F]); + + /* Dump some details */ + + printk(KERN_ERR " InitiatorId = %d, TargetId = %d\n", + (msg[1] >> 12) & 0xFFF, msg[1] & 0xFFF); + printk(KERN_ERR " LowestVersion = 0x%02X, HighestVersion = 0x%02X\n", + (msg[4] >> 8) & 0xFF, msg[4] & 0xFF); + printk(KERN_ERR " FailingHostUnit = 0x%04X, FailingIOP = 0x%03X\n", + msg[5] >> 16, msg[5] & 0xFFF); + + printk(KERN_ERR " Severity: 0x%02X ", (msg[4] >> 16) & 0xFF); + if (msg[4] & (1 << 16)) + printk("(FormatError), " + "this msg can never be delivered/processed.\n"); + if (msg[4] & (1 << 17)) + printk("(PathError), " + "this msg can no longer be delivered/processed.\n"); + if (msg[4] & (1 << 18)) + printk("(PathState), " + "the system state does not allow delivery.\n"); + if (msg[4] & (1 << 19)) + printk("(Congestion), resources temporarily not available;" + "do not retry immediately.\n"); +} + +/* + * Used for error reporting/debugging purposes. + * Following reply status are common to all classes. + */ +void i2o_report_common_status(u8 req_status) +{ + static char *REPLY_STATUS[] = { + "SUCCESS", + "ABORT_DIRTY", + "ABORT_NO_DATA_TRANSFER", + "ABORT_PARTIAL_TRANSFER", + "ERROR_DIRTY", + "ERROR_NO_DATA_TRANSFER", + "ERROR_PARTIAL_TRANSFER", + "PROCESS_ABORT_DIRTY", + "PROCESS_ABORT_NO_DATA_TRANSFER", + "PROCESS_ABORT_PARTIAL_TRANSFER", + "TRANSACTION_ERROR", + "PROGRESS_REPORT" + }; + + if (req_status >= ARRAY_SIZE(REPLY_STATUS)) + printk("RequestStatus = %0#2x", req_status); + else + printk("%s", REPLY_STATUS[req_status]); +} + +/* + * Used for error reporting/debugging purposes. + * Following detailed status are valid for executive class, + * utility class, DDM class and for transaction error replies. + */ +static void i2o_report_common_dsc(u16 detailed_status) +{ + static char *COMMON_DSC[] = { + "SUCCESS", + "0x01", // not used + "BAD_KEY", + "TCL_ERROR", + "REPLY_BUFFER_FULL", + "NO_SUCH_PAGE", + "INSUFFICIENT_RESOURCE_SOFT", + "INSUFFICIENT_RESOURCE_HARD", + "0x08", // not used + "CHAIN_BUFFER_TOO_LARGE", + "UNSUPPORTED_FUNCTION", + "DEVICE_LOCKED", + "DEVICE_RESET", + "INAPPROPRIATE_FUNCTION", + "INVALID_INITIATOR_ADDRESS", + "INVALID_MESSAGE_FLAGS", + "INVALID_OFFSET", + "INVALID_PARAMETER", + "INVALID_REQUEST", + "INVALID_TARGET_ADDRESS", + "MESSAGE_TOO_LARGE", + "MESSAGE_TOO_SMALL", + "MISSING_PARAMETER", + "TIMEOUT", + "UNKNOWN_ERROR", + "UNKNOWN_FUNCTION", + "UNSUPPORTED_VERSION", + "DEVICE_BUSY", + "DEVICE_NOT_AVAILABLE" + }; + + if (detailed_status > I2O_DSC_DEVICE_NOT_AVAILABLE) + printk(" / DetailedStatus = %0#4x.\n", detailed_status); + else + printk(" / %s.\n", COMMON_DSC[detailed_status]); +} + +/* + * Used for error reporting/debugging purposes + */ +static void i2o_report_util_cmd(u8 cmd) +{ + switch (cmd) { + case I2O_CMD_UTIL_NOP: + printk("UTIL_NOP, "); + break; + case I2O_CMD_UTIL_ABORT: + printk("UTIL_ABORT, "); + break; + case I2O_CMD_UTIL_CLAIM: + printk("UTIL_CLAIM, "); + break; + case I2O_CMD_UTIL_RELEASE: + printk("UTIL_CLAIM_RELEASE, "); + break; + case I2O_CMD_UTIL_CONFIG_DIALOG: + printk("UTIL_CONFIG_DIALOG, "); + break; + case I2O_CMD_UTIL_DEVICE_RESERVE: + printk("UTIL_DEVICE_RESERVE, "); + break; + case I2O_CMD_UTIL_DEVICE_RELEASE: + printk("UTIL_DEVICE_RELEASE, "); + break; + case I2O_CMD_UTIL_EVT_ACK: + printk("UTIL_EVENT_ACKNOWLEDGE, "); + break; + case I2O_CMD_UTIL_EVT_REGISTER: + printk("UTIL_EVENT_REGISTER, "); + break; + case I2O_CMD_UTIL_LOCK: + printk("UTIL_LOCK, "); + break; + case I2O_CMD_UTIL_LOCK_RELEASE: + printk("UTIL_LOCK_RELEASE, "); + break; + case I2O_CMD_UTIL_PARAMS_GET: + printk("UTIL_PARAMS_GET, "); + break; + case I2O_CMD_UTIL_PARAMS_SET: + printk("UTIL_PARAMS_SET, "); + break; + case I2O_CMD_UTIL_REPLY_FAULT_NOTIFY: + printk("UTIL_REPLY_FAULT_NOTIFY, "); + break; + default: + printk("Cmd = %0#2x, ", cmd); + } +} + +/* + * Used for error reporting/debugging purposes + */ +static void i2o_report_exec_cmd(u8 cmd) +{ + switch (cmd) { + case I2O_CMD_ADAPTER_ASSIGN: + printk("EXEC_ADAPTER_ASSIGN, "); + break; + case I2O_CMD_ADAPTER_READ: + printk("EXEC_ADAPTER_READ, "); + break; + case I2O_CMD_ADAPTER_RELEASE: + printk("EXEC_ADAPTER_RELEASE, "); + break; + case I2O_CMD_BIOS_INFO_SET: + printk("EXEC_BIOS_INFO_SET, "); + break; + case I2O_CMD_BOOT_DEVICE_SET: + printk("EXEC_BOOT_DEVICE_SET, "); + break; + case I2O_CMD_CONFIG_VALIDATE: + printk("EXEC_CONFIG_VALIDATE, "); + break; + case I2O_CMD_CONN_SETUP: + printk("EXEC_CONN_SETUP, "); + break; + case I2O_CMD_DDM_DESTROY: + printk("EXEC_DDM_DESTROY, "); + break; + case I2O_CMD_DDM_ENABLE: + printk("EXEC_DDM_ENABLE, "); + break; + case I2O_CMD_DDM_QUIESCE: + printk("EXEC_DDM_QUIESCE, "); + break; + case I2O_CMD_DDM_RESET: + printk("EXEC_DDM_RESET, "); + break; + case I2O_CMD_DDM_SUSPEND: + printk("EXEC_DDM_SUSPEND, "); + break; + case I2O_CMD_DEVICE_ASSIGN: + printk("EXEC_DEVICE_ASSIGN, "); + break; + case I2O_CMD_DEVICE_RELEASE: + printk("EXEC_DEVICE_RELEASE, "); + break; + case I2O_CMD_HRT_GET: + printk("EXEC_HRT_GET, "); + break; + case I2O_CMD_ADAPTER_CLEAR: + printk("EXEC_IOP_CLEAR, "); + break; + case I2O_CMD_ADAPTER_CONNECT: + printk("EXEC_IOP_CONNECT, "); + break; + case I2O_CMD_ADAPTER_RESET: + printk("EXEC_IOP_RESET, "); + break; + case I2O_CMD_LCT_NOTIFY: + printk("EXEC_LCT_NOTIFY, "); + break; + case I2O_CMD_OUTBOUND_INIT: + printk("EXEC_OUTBOUND_INIT, "); + break; + case I2O_CMD_PATH_ENABLE: + printk("EXEC_PATH_ENABLE, "); + break; + case I2O_CMD_PATH_QUIESCE: + printk("EXEC_PATH_QUIESCE, "); + break; + case I2O_CMD_PATH_RESET: + printk("EXEC_PATH_RESET, "); + break; + case I2O_CMD_STATIC_MF_CREATE: + printk("EXEC_STATIC_MF_CREATE, "); + break; + case I2O_CMD_STATIC_MF_RELEASE: + printk("EXEC_STATIC_MF_RELEASE, "); + break; + case I2O_CMD_STATUS_GET: + printk("EXEC_STATUS_GET, "); + break; + case I2O_CMD_SW_DOWNLOAD: + printk("EXEC_SW_DOWNLOAD, "); + break; + case I2O_CMD_SW_UPLOAD: + printk("EXEC_SW_UPLOAD, "); + break; + case I2O_CMD_SW_REMOVE: + printk("EXEC_SW_REMOVE, "); + break; + case I2O_CMD_SYS_ENABLE: + printk("EXEC_SYS_ENABLE, "); + break; + case I2O_CMD_SYS_MODIFY: + printk("EXEC_SYS_MODIFY, "); + break; + case I2O_CMD_SYS_QUIESCE: + printk("EXEC_SYS_QUIESCE, "); + break; + case I2O_CMD_SYS_TAB_SET: + printk("EXEC_SYS_TAB_SET, "); + break; + default: + printk("Cmd = %#02x, ", cmd); + } +} + +void i2o_debug_state(struct i2o_controller *c) +{ + printk(KERN_INFO "%s: State = ", c->name); + switch (((i2o_status_block *) c->status_block.virt)->iop_state) { + case 0x01: + printk("INIT\n"); + break; + case 0x02: + printk("RESET\n"); + break; + case 0x04: + printk("HOLD\n"); + break; + case 0x05: + printk("READY\n"); + break; + case 0x08: + printk("OPERATIONAL\n"); + break; + case 0x10: + printk("FAILED\n"); + break; + case 0x11: + printk("FAULTED\n"); + break; + default: + printk("%x (unknown !!)\n", + ((i2o_status_block *) c->status_block.virt)->iop_state); + } +}; + +void i2o_systab_debug(struct i2o_sys_tbl *sys_tbl) +{ + u32 *table; + int count; + u32 size; + + table = (u32 *) sys_tbl; + size = sizeof(struct i2o_sys_tbl) + sys_tbl->num_entries + * sizeof(struct i2o_sys_tbl_entry); + + for (count = 0; count < (size >> 2); count++) + printk(KERN_INFO "sys_tbl[%d] = %0#10x\n", count, table[count]); +} + +void i2o_dump_hrt(struct i2o_controller *c) +{ + u32 *rows = (u32 *) c->hrt.virt; + u8 *p = (u8 *) c->hrt.virt; + u8 *d; + int count; + int length; + int i; + int state; + + if (p[3] != 0) { + printk(KERN_ERR + "%s: HRT table for controller is too new a version.\n", + c->name); + return; + } + + count = p[0] | (p[1] << 8); + length = p[2]; + + printk(KERN_INFO "%s: HRT has %d entries of %d bytes each.\n", + c->name, count, length << 2); + + rows += 2; + + for (i = 0; i < count; i++) { + printk(KERN_INFO "Adapter %08X: ", rows[0]); + p = (u8 *) (rows + 1); + d = (u8 *) (rows + 2); + state = p[1] << 8 | p[0]; + + printk("TID %04X:[", state & 0xFFF); + state >>= 12; + if (state & (1 << 0)) + printk("H"); /* Hidden */ + if (state & (1 << 2)) { + printk("P"); /* Present */ + if (state & (1 << 1)) + printk("C"); /* Controlled */ + } + if (state > 9) + printk("*"); /* Hard */ + + printk("]:"); + + switch (p[3] & 0xFFFF) { + case 0: + /* Adapter private bus - easy */ + printk("Local bus %d: I/O at 0x%04X Mem 0x%08X", + p[2], d[1] << 8 | d[0], *(u32 *) (d + 4)); + break; + case 1: + /* ISA bus */ + printk("ISA %d: CSN %d I/O at 0x%04X Mem 0x%08X", + p[2], d[2], d[1] << 8 | d[0], *(u32 *) (d + 4)); + break; + + case 2: /* EISA bus */ + printk("EISA %d: Slot %d I/O at 0x%04X Mem 0x%08X", + p[2], d[3], d[1] << 8 | d[0], *(u32 *) (d + 4)); + break; + + case 3: /* MCA bus */ + printk("MCA %d: Slot %d I/O at 0x%04X Mem 0x%08X", + p[2], d[3], d[1] << 8 | d[0], *(u32 *) (d + 4)); + break; + + case 4: /* PCI bus */ + printk("PCI %d: Bus %d Device %d Function %d", + p[2], d[2], d[1], d[0]); + break; + + case 0x80: /* Other */ + default: + printk("Unsupported bus type."); + break; + } + printk("\n"); + rows += length; + } +} + +EXPORT_SYMBOL(i2o_dump_status_block); +EXPORT_SYMBOL(i2o_dump_message); diff --git a/drivers/message/i2o/device.c b/drivers/message/i2o/device.c new file mode 100644 index 000000000..ff4822ed4 --- /dev/null +++ b/drivers/message/i2o/device.c @@ -0,0 +1,674 @@ +/* + * Functions to handle I2O devices + * + * Copyright (C) 2004 Markus Lidel + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * Fixes/additions: + * Markus Lidel + * initial version. + */ + +#include +#include + +/* Exec OSM functions */ +extern struct bus_type i2o_bus_type; + +/** + * i2o_device_issue_claim - claim or release a device + * @dev: I2O device to claim or release + * @cmd: claim or release command + * @type: type of claim + * + * Issue I2O UTIL_CLAIM or UTIL_RELEASE messages. The message to be sent + * is set by cmd. dev is the I2O device which should be claim or + * released and the type is the claim type (see the I2O spec). + * + * Returs 0 on success or negative error code on failure. + */ +static inline int i2o_device_issue_claim(struct i2o_device *dev, u32 cmd, + u32 type) +{ + struct i2o_message *msg; + u32 m; + + m = i2o_msg_get_wait(dev->iop, &msg, I2O_TIMEOUT_MESSAGE_GET); + if (m == I2O_QUEUE_EMPTY) + return -ETIMEDOUT; + + writel(FIVE_WORD_MSG_SIZE | SGL_OFFSET_0, &msg->u.head[0]); + writel(cmd << 24 | HOST_TID << 12 | dev->lct_data.tid, &msg->u.head[1]); + writel(type, &msg->body[0]); + + return i2o_msg_post_wait(dev->iop, m, 60); +}; + +/** + * i2o_device_claim - claim a device for use by an OSM + * @dev: I2O device to claim + * @drv: I2O driver which wants to claim the device + * + * Do the leg work to assign a device to a given OSM. If the claim succeed + * the owner of the rimary. If the attempt fails a negative errno code + * is returned. On success zero is returned. + */ +int i2o_device_claim(struct i2o_device *dev) +{ + int rc = 0; + + down(&dev->lock); + + rc = i2o_device_issue_claim(dev, I2O_CMD_UTIL_CLAIM, I2O_CLAIM_PRIMARY); + if (!rc) + pr_debug("claim of device %d succeded\n", dev->lct_data.tid); + else + pr_debug("claim of device %d failed %d\n", dev->lct_data.tid, + rc); + + up(&dev->lock); + + return rc; +}; + +/** + * i2o_device_claim_release - release a device that the OSM is using + * @dev: device to release + * @drv: driver which claimed the device + * + * Drop a claim by an OSM on a given I2O device. + * + * AC - some devices seem to want to refuse an unclaim until they have + * finished internal processing. It makes sense since you don't want a + * new device to go reconfiguring the entire system until you are done. + * Thus we are prepared to wait briefly. + * + * Returns 0 on success or negative error code on failure. + */ +int i2o_device_claim_release(struct i2o_device *dev) +{ + int tries; + int rc = 0; + + down(&dev->lock); + + /* + * If the controller takes a nonblocking approach to + * releases we have to sleep/poll for a few times. + */ + for (tries = 0; tries < 10; tries++) { + rc = i2o_device_issue_claim(dev, I2O_CMD_UTIL_RELEASE, + I2O_CLAIM_PRIMARY); + if (!rc) + break; + + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout(HZ); + } + + if (!rc) + pr_debug("claim release of device %d succeded\n", + dev->lct_data.tid); + else + pr_debug("claim release of device %d failed %d\n", + dev->lct_data.tid, rc); + + up(&dev->lock); + + return rc; +}; + +/** + * i2o_device_release - release the memory for a I2O device + * @dev: I2O device which should be released + * + * Release the allocated memory. This function is called if refcount of + * device reaches 0 automatically. + */ +static void i2o_device_release(struct device *dev) +{ + struct i2o_device *i2o_dev = to_i2o_device(dev); + + pr_debug("Release I2O device %s\n", dev->bus_id); + + kfree(i2o_dev); +}; + +/** + * i2o_device_class_release - Remove I2O device attributes + * @cd: I2O class device which is added to the I2O device class + * + * Removes attributes from the I2O device again. Also search each device + * on the controller for I2O devices which refert to this device as parent + * or user and remove this links also. + */ +static void i2o_device_class_release(struct class_device *cd) +{ + struct i2o_device *i2o_dev, *tmp; + struct i2o_controller *c; + + i2o_dev = to_i2o_device(cd->dev); + c = i2o_dev->iop; + + sysfs_remove_link(&i2o_dev->device.kobj, "parent"); + sysfs_remove_link(&i2o_dev->device.kobj, "user"); + + list_for_each_entry(tmp, &c->devices, list) { + if (tmp->lct_data.parent_tid == i2o_dev->lct_data.tid) + sysfs_remove_link(&tmp->device.kobj, "parent"); + if (tmp->lct_data.user_tid == i2o_dev->lct_data.tid) + sysfs_remove_link(&tmp->device.kobj, "user"); + } +}; + +/* I2O device class */ +static struct class i2o_device_class = { + .name = "i2o_device", + .release = i2o_device_class_release +}; + +/** + * i2o_device_alloc - Allocate a I2O device and initialize it + * + * Allocate the memory for a I2O device and initialize locks and lists + * + * Returns the allocated I2O device or a negative error code if the device + * could not be allocated. + */ +static struct i2o_device *i2o_device_alloc(void) +{ + struct i2o_device *dev; + + dev = kmalloc(sizeof(*dev), GFP_KERNEL); + if (!dev) + return ERR_PTR(-ENOMEM); + + memset(dev, 0, sizeof(*dev)); + + INIT_LIST_HEAD(&dev->list); + init_MUTEX(&dev->lock); + + dev->device.bus = &i2o_bus_type; + dev->device.release = &i2o_device_release; + dev->classdev.class = &i2o_device_class; + dev->classdev.dev = &dev->device; + + return dev; +}; + +/** + * i2o_device_add - allocate a new I2O device and add it to the IOP + * @iop: I2O controller where the device is on + * @entry: LCT entry of the I2O device + * + * Allocate a new I2O device and initialize it with the LCT entry. The + * device is appended to the device list of the controller. + * + * Returns a pointer to the I2O device on success or negative error code + * on failure. + */ +struct i2o_device *i2o_device_add(struct i2o_controller *c, + i2o_lct_entry * entry) +{ + struct i2o_device *dev; + + dev = i2o_device_alloc(); + if (IS_ERR(dev)) { + printk(KERN_ERR "i2o: unable to allocate i2o device\n"); + return dev; + } + + dev->lct_data = *entry; + + snprintf(dev->device.bus_id, BUS_ID_SIZE, "%d:%03x", c->unit, + dev->lct_data.tid); + + snprintf(dev->classdev.class_id, BUS_ID_SIZE, "%d:%03x", c->unit, + dev->lct_data.tid); + + dev->iop = c; + dev->device.parent = &c->device; + + device_register(&dev->device); + + list_add_tail(&dev->list, &c->devices); + + class_device_register(&dev->classdev); + + i2o_driver_notify_device_add_all(dev); + + pr_debug("I2O device %s added\n", dev->device.bus_id); + + return dev; +}; + +/** + * i2o_device_remove - remove an I2O device from the I2O core + * @dev: I2O device which should be released + * + * Is used on I2O controller removal or LCT modification, when the device + * is removed from the system. Note that the device could still hang + * around until the refcount reaches 0. + */ +void i2o_device_remove(struct i2o_device *i2o_dev) +{ + i2o_driver_notify_device_remove_all(i2o_dev); + class_device_unregister(&i2o_dev->classdev); + list_del(&i2o_dev->list); + device_unregister(&i2o_dev->device); +}; + +/** + * i2o_device_parse_lct - Parse a previously fetched LCT and create devices + * @c: I2O controller from which the LCT should be parsed. + * + * The Logical Configuration Table tells us what we can talk to on the + * board. For every entry we create an I2O device, which is registered in + * the I2O core. + * + * Returns 0 on success or negative error code on failure. + */ +int i2o_device_parse_lct(struct i2o_controller *c) +{ + struct i2o_device *dev, *tmp; + i2o_lct *lct; + int i; + int max; + + down(&c->lct_lock); + + if (c->lct) + kfree(c->lct); + + lct = c->dlct.virt; + + c->lct = kmalloc(lct->table_size * 4, GFP_KERNEL); + if (!c->lct) { + up(&c->lct_lock); + return -ENOMEM; + } + + if (lct->table_size * 4 > c->dlct.len) { + memcpy_fromio(c->lct, c->dlct.virt, c->dlct.len); + up(&c->lct_lock); + return -EAGAIN; + } + + memcpy_fromio(c->lct, c->dlct.virt, lct->table_size * 4); + + lct = c->lct; + + max = (lct->table_size - 3) / 9; + + pr_debug("LCT has %d entries (LCT size: %d)\n", max, lct->table_size); + + /* remove devices, which are not in the LCT anymore */ + list_for_each_entry_safe(dev, tmp, &c->devices, list) { + int found = 0; + + for (i = 0; i < max; i++) { + if (lct->lct_entry[i].tid == dev->lct_data.tid) { + found = 1; + break; + } + } + + if (!found) + i2o_device_remove(dev); + } + + /* add new devices, which are new in the LCT */ + for (i = 0; i < max; i++) { + int found = 0; + + list_for_each_entry_safe(dev, tmp, &c->devices, list) { + if (lct->lct_entry[i].tid == dev->lct_data.tid) { + found = 1; + break; + } + } + + if (!found) + i2o_device_add(c, &lct->lct_entry[i]); + } + up(&c->lct_lock); + + return 0; +}; + +/** + * i2o_device_class_show_class_id - Displays class id of I2O device + * @cd: class device of which the class id should be displayed + * @buf: buffer into which the class id should be printed + * + * Returns the number of bytes which are printed into the buffer. + */ +static ssize_t i2o_device_class_show_class_id(struct class_device *cd, + char *buf) +{ + struct i2o_device *dev = to_i2o_device(cd->dev); + + sprintf(buf, "%03x\n", dev->lct_data.class_id); + return strlen(buf) + 1; +}; + +/** + * i2o_device_class_show_tid - Displays TID of I2O device + * @cd: class device of which the TID should be displayed + * @buf: buffer into which the class id should be printed + * + * Returns the number of bytes which are printed into the buffer. + */ +static ssize_t i2o_device_class_show_tid(struct class_device *cd, char *buf) +{ + struct i2o_device *dev = to_i2o_device(cd->dev); + + sprintf(buf, "%03x\n", dev->lct_data.tid); + return strlen(buf) + 1; +}; + +/* I2O device class attributes */ +static CLASS_DEVICE_ATTR(class_id, S_IRUGO, i2o_device_class_show_class_id, + NULL); +static CLASS_DEVICE_ATTR(tid, S_IRUGO, i2o_device_class_show_tid, NULL); + +/** + * i2o_device_class_add - Adds attributes to the I2O device + * @cd: I2O class device which is added to the I2O device class + * + * This function get called when a I2O device is added to the class. It + * creates the attributes for each device and creates user/parent symlink + * if necessary. + * + * Returns 0 on success or negative error code on failure. + */ +static int i2o_device_class_add(struct class_device *cd) +{ + struct i2o_device *i2o_dev, *tmp; + struct i2o_controller *c; + + i2o_dev = to_i2o_device(cd->dev); + c = i2o_dev->iop; + + class_device_create_file(cd, &class_device_attr_class_id); + class_device_create_file(cd, &class_device_attr_tid); + + /* create user entries for this device */ + tmp = i2o_iop_find_device(i2o_dev->iop, i2o_dev->lct_data.user_tid); + if (tmp) + sysfs_create_link(&i2o_dev->device.kobj, &tmp->device.kobj, + "user"); + + /* create user entries refering to this device */ + list_for_each_entry(tmp, &c->devices, list) + if (tmp->lct_data.user_tid == i2o_dev->lct_data.tid) + sysfs_create_link(&tmp->device.kobj, + &i2o_dev->device.kobj, "user"); + + /* create parent entries for this device */ + tmp = i2o_iop_find_device(i2o_dev->iop, i2o_dev->lct_data.parent_tid); + if (tmp) + sysfs_create_link(&i2o_dev->device.kobj, &tmp->device.kobj, + "parent"); + + /* create parent entries refering to this device */ + list_for_each_entry(tmp, &c->devices, list) + if (tmp->lct_data.parent_tid == i2o_dev->lct_data.tid) + sysfs_create_link(&tmp->device.kobj, + &i2o_dev->device.kobj, "parent"); + + return 0; +}; + +/* I2O device class interface */ +static struct class_interface i2o_device_class_interface = { + .class = &i2o_device_class, + .add = i2o_device_class_add +}; + +/* + * Run time support routines + */ + +/* Issue UTIL_PARAMS_GET or UTIL_PARAMS_SET + * + * This function can be used for all UtilParamsGet/Set operations. + * The OperationList is given in oplist-buffer, + * and results are returned in reslist-buffer. + * Note that the minimum sized reslist is 8 bytes and contains + * ResultCount, ErrorInfoSize, BlockStatus and BlockSize. + */ + +int i2o_parm_issue(struct i2o_device *i2o_dev, int cmd, void *oplist, + int oplen, void *reslist, int reslen) +{ + struct i2o_message *msg; + u32 m; + u32 *res32 = (u32 *) reslist; + u32 *restmp = (u32 *) reslist; + int len = 0; + int i = 0; + int rc; + struct i2o_dma res; + struct i2o_controller *c = i2o_dev->iop; + struct device *dev = &c->pdev->dev; + + res.virt = NULL; + + if (i2o_dma_alloc(dev, &res, reslen, GFP_KERNEL)) + return -ENOMEM; + + m = i2o_msg_get_wait(c, &msg, I2O_TIMEOUT_MESSAGE_GET); + if (m == I2O_QUEUE_EMPTY) { + i2o_dma_free(dev, &res); + return -ETIMEDOUT; + } + + i = 0; + writel(cmd << 24 | HOST_TID << 12 | i2o_dev->lct_data.tid, + &msg->u.head[1]); + writel(0, &msg->body[i++]); + writel(0x4C000000 | oplen, &msg->body[i++]); /* OperationList */ + memcpy_toio(&msg->body[i], oplist, oplen); + i += (oplen / 4 + (oplen % 4 ? 1 : 0)); + writel(0xD0000000 | res.len, &msg->body[i++]); /* ResultList */ + writel(res.phys, &msg->body[i++]); + + writel(I2O_MESSAGE_SIZE(i + sizeof(struct i2o_message) / 4) | + SGL_OFFSET_5, &msg->u.head[0]); + + rc = i2o_msg_post_wait_mem(c, m, 10, &res); + + /* This only looks like a memory leak - don't "fix" it. */ + if (rc == -ETIMEDOUT) + return rc; + + memcpy_fromio(reslist, res.virt, res.len); + i2o_dma_free(dev, &res); + + /* Query failed */ + if (rc) + return rc; + /* + * Calculate number of bytes of Result LIST + * We need to loop through each Result BLOCK and grab the length + */ + restmp = res32 + 1; + len = 1; + for (i = 0; i < (res32[0] & 0X0000FFFF); i++) { + if (restmp[0] & 0x00FF0000) { /* BlockStatus != SUCCESS */ + printk(KERN_WARNING + "%s - Error:\n ErrorInfoSize = 0x%02x, " + "BlockStatus = 0x%02x, BlockSize = 0x%04x\n", + (cmd == + I2O_CMD_UTIL_PARAMS_SET) ? "PARAMS_SET" : + "PARAMS_GET", res32[1] >> 24, + (res32[1] >> 16) & 0xFF, res32[1] & 0xFFFF); + + /* + * If this is the only request,than we return an error + */ + if ((res32[0] & 0x0000FFFF) == 1) { + return -((res32[1] >> 16) & 0xFF); /* -BlockStatus */ + } + } + len += restmp[0] & 0x0000FFFF; /* Length of res BLOCK */ + restmp += restmp[0] & 0x0000FFFF; /* Skip to next BLOCK */ + } + return (len << 2); /* bytes used by result list */ +} + +/* + * Query one field group value or a whole scalar group. + */ +int i2o_parm_field_get(struct i2o_device *i2o_dev, int group, int field, + void *buf, int buflen) +{ + u16 opblk[] = { 1, 0, I2O_PARAMS_FIELD_GET, group, 1, field }; + u8 resblk[8 + buflen]; /* 8 bytes for header */ + int size; + + if (field == -1) /* whole group */ + opblk[4] = -1; + + size = i2o_parm_issue(i2o_dev, I2O_CMD_UTIL_PARAMS_GET, opblk, + sizeof(opblk), resblk, sizeof(resblk)); + + memcpy(buf, resblk + 8, buflen); /* cut off header */ + + if (size > buflen) + return buflen; + + return size; +} + +/* + * Set a scalar group value or a whole group. + */ +int i2o_parm_field_set(struct i2o_device *i2o_dev, int group, int field, + void *buf, int buflen) +{ + u16 *opblk; + u8 resblk[8 + buflen]; /* 8 bytes for header */ + int size; + + opblk = kmalloc(buflen + 64, GFP_KERNEL); + if (opblk == NULL) { + printk(KERN_ERR "i2o: no memory for operation buffer.\n"); + return -ENOMEM; + } + + opblk[0] = 1; /* operation count */ + opblk[1] = 0; /* pad */ + opblk[2] = I2O_PARAMS_FIELD_SET; + opblk[3] = group; + + if (field == -1) { /* whole group */ + opblk[4] = -1; + memcpy(opblk + 5, buf, buflen); + } else { /* single field */ + + opblk[4] = 1; + opblk[5] = field; + memcpy(opblk + 6, buf, buflen); + } + + size = i2o_parm_issue(i2o_dev, I2O_CMD_UTIL_PARAMS_SET, opblk, + 12 + buflen, resblk, sizeof(resblk)); + + kfree(opblk); + if (size > buflen) + return buflen; + + return size; +} + +/* + * if oper == I2O_PARAMS_TABLE_GET, get from all rows + * if fieldcount == -1 return all fields + * ibuf and ibuflen are unused (use NULL, 0) + * else return specific fields + * ibuf contains fieldindexes + * + * if oper == I2O_PARAMS_LIST_GET, get from specific rows + * if fieldcount == -1 return all fields + * ibuf contains rowcount, keyvalues + * else return specific fields + * fieldcount is # of fieldindexes + * ibuf contains fieldindexes, rowcount, keyvalues + * + * You could also use directly function i2o_issue_params(). + */ +int i2o_parm_table_get(struct i2o_device *dev, int oper, int group, + int fieldcount, void *ibuf, int ibuflen, void *resblk, + int reslen) +{ + u16 *opblk; + int size; + + size = 10 + ibuflen; + if (size % 4) + size += 4 - size % 4; + + opblk = kmalloc(size, GFP_KERNEL); + if (opblk == NULL) { + printk(KERN_ERR "i2o: no memory for query buffer.\n"); + return -ENOMEM; + } + + opblk[0] = 1; /* operation count */ + opblk[1] = 0; /* pad */ + opblk[2] = oper; + opblk[3] = group; + opblk[4] = fieldcount; + memcpy(opblk + 5, ibuf, ibuflen); /* other params */ + + size = i2o_parm_issue(dev, I2O_CMD_UTIL_PARAMS_GET, opblk, + size, resblk, reslen); + + kfree(opblk); + if (size > reslen) + return reslen; + + return size; +} + +/** + * i2o_device_init - Initialize I2O devices + * + * Registers the I2O device class. + * + * Returns 0 on success or negative error code on failure. + */ +int i2o_device_init(void) +{ + int rc; + + rc = class_register(&i2o_device_class); + if (rc) + return rc; + + return class_interface_register(&i2o_device_class_interface); +}; + +/** + * i2o_device_exit - I2O devices exit function + * + * Unregisters the I2O device class. + */ +void i2o_device_exit(void) +{ + class_interface_register(&i2o_device_class_interface); + class_unregister(&i2o_device_class); +}; + +EXPORT_SYMBOL(i2o_device_claim); +EXPORT_SYMBOL(i2o_device_claim_release); +EXPORT_SYMBOL(i2o_parm_field_get); +EXPORT_SYMBOL(i2o_parm_field_set); +EXPORT_SYMBOL(i2o_parm_table_get); +EXPORT_SYMBOL(i2o_parm_issue); diff --git a/drivers/message/i2o/driver.c b/drivers/message/i2o/driver.c new file mode 100644 index 000000000..bc69d66c2 --- /dev/null +++ b/drivers/message/i2o/driver.c @@ -0,0 +1,367 @@ +/* + * Functions to handle I2O drivers (OSMs) and I2O bus type for sysfs + * + * Copyright (C) 2004 Markus Lidel + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * Fixes/additions: + * Markus Lidel + * initial version. + */ + +#include +#include +#include +#include + + +/* max_drivers - Maximum I2O drivers (OSMs) which could be registered */ +unsigned int i2o_max_drivers = I2O_MAX_DRIVERS; +module_param_named(max_drivers, i2o_max_drivers, uint, 0); +MODULE_PARM_DESC(max_drivers, "maximum number of OSM's to support"); + +/* I2O drivers lock and array */ +static spinlock_t i2o_drivers_lock = SPIN_LOCK_UNLOCKED; +static struct i2o_driver **i2o_drivers; + +/** + * i2o_bus_match - Tell if a I2O device class id match the class ids of + * the I2O driver (OSM) + * + * @dev: device which should be verified + * @drv: the driver to match against + * + * Used by the bus to check if the driver wants to handle the device. + * + * Returns 1 if the class ids of the driver match the class id of the + * device, otherwise 0. + */ +static int i2o_bus_match(struct device *dev, struct device_driver *drv) +{ + struct i2o_device *i2o_dev = to_i2o_device(dev); + struct i2o_driver *i2o_drv = to_i2o_driver(drv); + struct i2o_class_id *ids = i2o_drv->classes; + + if (ids) + while (ids->class_id != I2O_CLASS_END) { + if (ids->class_id == i2o_dev->lct_data.class_id) + return 1; + ids++; + } + return 0; +}; + +/* I2O bus type */ +struct bus_type i2o_bus_type = { + .name = "i2o", + .match = i2o_bus_match, +}; + +/** + * i2o_driver_register - Register a I2O driver (OSM) in the I2O core + * @drv: I2O driver which should be registered + * + * Registers the OSM drv in the I2O core and creates an event queues if + * necessary. + * + * Returns 0 on success or negative error code on failure. + */ +int i2o_driver_register(struct i2o_driver *drv) +{ + struct i2o_controller *c; + int i; + int rc = 0; + unsigned long flags; + + pr_debug("Register driver %s\n", drv->name); + + if (drv->event) { + drv->event_queue = create_workqueue(drv->name); + if (!drv->event_queue) { + printk(KERN_ERR "i2o: Could not initialize event queue " + "for driver %s\n", drv->name); + return -EFAULT; + } + pr_debug("Event queue initialized for driver %s\n", drv->name); + } else + drv->event_queue = NULL; + + drv->driver.name = drv->name; + drv->driver.bus = &i2o_bus_type; + + spin_lock_irqsave(&i2o_drivers_lock, flags); + + for (i = 0; i2o_drivers[i]; i++) + if (i >= i2o_max_drivers) { + printk(KERN_ERR "i2o: too many drivers registered, " + "increase max_drivers\n"); + spin_unlock_irqrestore(&i2o_drivers_lock, flags); + return -EFAULT; + } + + drv->context = i; + i2o_drivers[i] = drv; + + spin_unlock_irqrestore(&i2o_drivers_lock, flags); + + pr_debug("driver %s gets context id %d\n", drv->name, drv->context); + + list_for_each_entry(c, &i2o_controllers, list) { + struct i2o_device *i2o_dev; + + i2o_driver_notify_controller_add(drv, c); + list_for_each_entry(i2o_dev, &c->devices, list) + i2o_driver_notify_device_add(drv, i2o_dev); + } + + + rc = driver_register(&drv->driver); + if (rc) + destroy_workqueue(drv->event_queue); + + return rc; +}; + +/** + * i2o_driver_unregister - Unregister a I2O driver (OSM) from the I2O core + * @drv: I2O driver which should be unregistered + * + * Unregisters the OSM drv from the I2O core and cleanup event queues if + * necessary. + */ +void i2o_driver_unregister(struct i2o_driver *drv) +{ + struct i2o_controller *c; + unsigned long flags; + + pr_debug("unregister driver %s\n", drv->name); + + driver_unregister(&drv->driver); + + list_for_each_entry(c, &i2o_controllers, list) { + struct i2o_device *i2o_dev; + + list_for_each_entry(i2o_dev, &c->devices, list) + i2o_driver_notify_device_remove(drv, i2o_dev); + + i2o_driver_notify_controller_remove(drv, c); + } + + spin_lock_irqsave(&i2o_drivers_lock, flags); + i2o_drivers[drv->context] = NULL; + spin_unlock_irqrestore(&i2o_drivers_lock, flags); + + if (drv->event_queue) { + destroy_workqueue(drv->event_queue); + drv->event_queue = NULL; + pr_debug("event queue removed for %s\n", drv->name); + } +}; + +/** + * i2o_driver_dispatch - dispatch an I2O reply message + * @c: I2O controller of the message + * @m: I2O message number + * @msg: I2O message to be delivered + * + * The reply is delivered to the driver from which the original message + * was. This function is only called from interrupt context. + * + * Returns 0 on success and the message should not be flushed. Returns > 0 + * on success and if the message should be flushed afterwords. Returns + * negative error code on failure (the message will be flushed too). + */ +int i2o_driver_dispatch(struct i2o_controller *c, u32 m, + struct i2o_message *msg) +{ + struct i2o_driver *drv; + u32 context = readl(&msg->u.s.icntxt); + + if (likely(context < i2o_max_drivers)) { + spin_lock(&i2o_drivers_lock); + drv = i2o_drivers[context]; + spin_unlock(&i2o_drivers_lock); + + if (unlikely(!drv)) { + printk(KERN_WARNING "i2o: Spurious reply to unknown " + "driver %d\n", context); + return -EIO; + } + + if ((readl(&msg->u.head[1]) >> 24) == I2O_CMD_UTIL_EVT_REGISTER) { + struct i2o_device *dev, *tmp; + struct i2o_event *evt; + u16 size; + u16 tid; + + tid = readl(&msg->u.head[1]) & 0x1fff; + + pr_debug("%s: event received from device %d\n", c->name, + tid); + + /* cut of header from message size (in 32-bit words) */ + size = (readl(&msg->u.head[0]) >> 16) - 5; + + evt = kmalloc(size * 4 + sizeof(*evt), GFP_ATOMIC); + if (!evt) + return -ENOMEM; + memset(evt, 0, size * 4 + sizeof(*evt)); + + evt->size = size; + memcpy_fromio(&evt->tcntxt, &msg->u.s.tcntxt, + (size + 2) * 4); + + list_for_each_entry_safe(dev, tmp, &c->devices, list) + if (dev->lct_data.tid == tid) { + evt->i2o_dev = dev; + break; + } + + INIT_WORK(&evt->work, (void (*)(void *))drv->event, + evt); + queue_work(drv->event_queue, &evt->work); + return 1; + } + + if (likely(drv->reply)) + return drv->reply(c, m, msg); + else + pr_debug("%s: Reply to driver %s, but no reply function" + " defined!\n", c->name, drv->name); + return -EIO; + } else + printk(KERN_WARNING "i2o: Spurious reply to unknown driver " + "%d\n", readl(&msg->u.s.icntxt)); + return -EIO; +} + +/** + * i2o_driver_notify_controller_add_all - Send notify of added controller + * to all I2O drivers + * + * Send notifications to all registered drivers that a new controller was + * added. + */ +void i2o_driver_notify_controller_add_all(struct i2o_controller *c) { + int i; + struct i2o_driver *drv; + + for(i = 0; i < I2O_MAX_DRIVERS; i ++) { + drv = i2o_drivers[i]; + + if(drv) + i2o_driver_notify_controller_add(drv, c); + } +} + +/** + * i2o_driver_notify_controller_remove_all - Send notify of removed + * controller to all I2O drivers + * + * Send notifications to all registered drivers that a controller was + * removed. + */ +void i2o_driver_notify_controller_remove_all(struct i2o_controller *c) { + int i; + struct i2o_driver *drv; + + for(i = 0; i < I2O_MAX_DRIVERS; i ++) { + drv = i2o_drivers[i]; + + if(drv) + i2o_driver_notify_controller_remove(drv, c); + } +} + +/** + * i2o_driver_notify_device_add_all - Send notify of added device to all + * I2O drivers + * + * Send notifications to all registered drivers that a device was added. + */ +void i2o_driver_notify_device_add_all(struct i2o_device *i2o_dev) { + int i; + struct i2o_driver *drv; + + for(i = 0; i < I2O_MAX_DRIVERS; i ++) { + drv = i2o_drivers[i]; + + if(drv) + i2o_driver_notify_device_add(drv, i2o_dev); + } +} + +/** + * i2o_driver_notify_device_remove_all - Send notify of removed device to + * all I2O drivers + * + * Send notifications to all registered drivers that a device was removed. + */ +void i2o_driver_notify_device_remove_all(struct i2o_device *i2o_dev) { + int i; + struct i2o_driver *drv; + + for(i = 0; i < I2O_MAX_DRIVERS; i ++) { + drv = i2o_drivers[i]; + + if(drv) + i2o_driver_notify_device_remove(drv, i2o_dev); + } +} + +/** + * i2o_driver_init - initialize I2O drivers (OSMs) + * + * Registers the I2O bus and allocate memory for the array of OSMs. + * + * Returns 0 on success or negative error code on failure. + */ +int __init i2o_driver_init(void) +{ + int rc = 0; + + if ((i2o_max_drivers < 2) || (i2o_max_drivers > 64) || + ((i2o_max_drivers ^ (i2o_max_drivers - 1)) != + (2 * i2o_max_drivers - 1))) { + printk(KERN_WARNING "i2o: max_drivers set to %d, but must be " + ">=2 and <= 64 and a power of 2\n", i2o_max_drivers); + i2o_max_drivers = I2O_MAX_DRIVERS; + } + printk(KERN_INFO "i2o: max_drivers=%d\n", i2o_max_drivers); + + i2o_drivers = + kmalloc(i2o_max_drivers * sizeof(*i2o_drivers), GFP_KERNEL); + if (!i2o_drivers) + return -ENOMEM; + + memset(i2o_drivers, 0, i2o_max_drivers * sizeof(*i2o_drivers)); + + rc = bus_register(&i2o_bus_type); + + if (rc < 0) + kfree(i2o_drivers); + + return rc; +}; + +/** + * i2o_driver_exit - clean up I2O drivers (OSMs) + * + * Unregisters the I2O bus and free driver array. + */ +void __exit i2o_driver_exit(void) +{ + bus_unregister(&i2o_bus_type); + kfree(i2o_drivers); +}; + +EXPORT_SYMBOL(i2o_driver_register); +EXPORT_SYMBOL(i2o_driver_unregister); +EXPORT_SYMBOL(i2o_driver_notify_controller_add_all); +EXPORT_SYMBOL(i2o_driver_notify_controller_remove_all); +EXPORT_SYMBOL(i2o_driver_notify_device_add_all); +EXPORT_SYMBOL(i2o_driver_notify_device_remove_all); diff --git a/drivers/message/i2o/exec-osm.c b/drivers/message/i2o/exec-osm.c new file mode 100644 index 000000000..117f26106 --- /dev/null +++ b/drivers/message/i2o/exec-osm.c @@ -0,0 +1,505 @@ +/* + * Executive OSM + * + * Copyright (C) 1999-2002 Red Hat Software + * + * Written by Alan Cox, Building Number Three 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. + * + * A lot of the I2O message side code from this is taken from the Red + * Creek RCPCI45 adapter driver by Red Creek Communications + * + * Fixes/additions: + * Philipp Rumpf + * Juha Sievänen + * Auvo Häkkinen + * Deepak Saxena + * Boji T Kannanthanam + * Alan Cox : + * Ported to Linux 2.5. + * Markus Lidel : + * Minor fixes for 2.6. + * Markus Lidel : + * Support for sysfs included. + */ + +#include +#include + +struct i2o_driver i2o_exec_driver; + +/* Module internal functions from other sources */ +extern int i2o_device_parse_lct(struct i2o_controller *); + +/* global wait list for POST WAIT */ +static LIST_HEAD(i2o_exec_wait_list); + +/* Wait struct needed for POST WAIT */ +struct i2o_exec_wait { + wait_queue_head_t *wq; /* Pointer to Wait queue */ + struct i2o_dma dma; /* DMA buffers to free on failure */ + u32 tcntxt; /* transaction context from reply */ + int complete; /* 1 if reply received otherwise 0 */ + u32 m; /* message id */ + struct i2o_message *msg; /* pointer to the reply message */ + struct list_head list; /* node in global wait list */ +}; + +/* Exec OSM class handling definition */ +static struct i2o_class_id i2o_exec_class_id[] = { + {I2O_CLASS_EXECUTIVE}, + {I2O_CLASS_END} +}; + +/** + * i2o_exec_wait_alloc - Allocate a i2o_exec_wait struct an initialize it + * + * Allocate the i2o_exec_wait struct and initialize the wait. + * + * Returns i2o_exec_wait pointer on success or negative error code on + * failure. + */ +static struct i2o_exec_wait *i2o_exec_wait_alloc(void) +{ + struct i2o_exec_wait *wait; + + wait = kmalloc(sizeof(*wait), GFP_KERNEL); + if (!wait) + return ERR_PTR(-ENOMEM); + + memset(wait, 0, sizeof(*wait)); + + INIT_LIST_HEAD(&wait->list); + + return wait; +}; + +/** + * i2o_exec_wait_free - Free a i2o_exec_wait struct + * @i2o_exec_wait: I2O wait data which should be cleaned up + */ +static void i2o_exec_wait_free(struct i2o_exec_wait *wait) +{ + kfree(wait); +}; + +/** + * i2o_msg_post_wait_mem - Post and wait a message with DMA buffers + * @c: controller + * @m: message to post + * @timeout: time in seconds to wait + * @dma: i2o_dma struct of the DMA buffer to free on failure + * + * This API allows an OSM to post a message and then be told whether or + * not the system received a successful reply. If the message times out + * then the value '-ETIMEDOUT' is returned. This is a special case. In + * this situation the message may (should) complete at an indefinite time + * in the future. When it completes it will use the memory buffer + * attached to the request. If -ETIMEDOUT is returned then the memory + * buffer must not be freed. Instead the event completion will free them + * for you. In all other cases the buffer are your problem. + * + * Returns 0 on success or negative error code on failure. + */ +int i2o_msg_post_wait_mem(struct i2o_controller *c, u32 m, unsigned long + timeout, struct i2o_dma *dma) +{ + DECLARE_WAIT_QUEUE_HEAD(wq); + DEFINE_WAIT(wait); + struct i2o_exec_wait *iwait; + static u32 tcntxt = 0x80000000; + struct i2o_message *msg = c->in_queue.virt + m; + int rc = 0; + + iwait = i2o_exec_wait_alloc(); + if (!iwait) + return -ENOMEM; + + if (tcntxt == 0xffffffff) + tcntxt = 0x80000000; + + if (dma) + iwait->dma = *dma; + + /* + * Fill in the message initiator context and transaction context. + * We will only use transaction contexts >= 0x80000000 for POST WAIT, + * so we could find a POST WAIT reply easier in the reply handler. + */ + writel(i2o_exec_driver.context, &msg->u.s.icntxt); + iwait->tcntxt = tcntxt++; + writel(iwait->tcntxt, &msg->u.s.tcntxt); + + /* + * Post the message to the controller. At some point later it will + * return. If we time out before it returns then complete will be zero. + */ + i2o_msg_post(c, m); + + if (!iwait->complete) { + iwait->wq = &wq; + /* + * we add elements add the head, because if a entry in the list + * will never be removed, we have to iterate over it every time + */ + list_add(&iwait->list, &i2o_exec_wait_list); + + prepare_to_wait(&wq, &wait, TASK_INTERRUPTIBLE); + + if (!iwait->complete) + schedule_timeout(timeout * HZ); + + finish_wait(&wq, &wait); + + iwait->wq = NULL; + } + + barrier(); + + if (iwait->complete) { + if (readl(&iwait->msg->body[0]) >> 24) + rc = readl(&iwait->msg->body[0]) & 0xff; + i2o_flush_reply(c, iwait->m); + i2o_exec_wait_free(iwait); + } else { + /* + * We cannot remove it now. This is important. When it does + * terminate (which it must do if the controller has not + * died...) then it will otherwise scribble on stuff. + * + * FIXME: try abort message + */ + if (dma) + dma->virt = NULL; + + rc = -ETIMEDOUT; + } + + return rc; +}; + +/** + * i2o_msg_post_wait_complete - Reply to a i2o_msg_post request from IOP + * @c: I2O controller which answers + * @m: message id + * @msg: pointer to the I2O reply message + * + * This function is called in interrupt context only. If the reply reached + * before the timeout, the i2o_exec_wait struct is filled with the message + * and the task will be waked up. The task is now responsible for returning + * the message m back to the controller! If the message reaches us after + * the timeout clean up the i2o_exec_wait struct (including allocated + * DMA buffer). + * + * Return 0 on success and if the message m should not be given back to the + * I2O controller, or >0 on success and if the message should be given back + * afterwords. Returns negative error code on failure. In this case the + * message must also be given back to the controller. + */ +static int i2o_msg_post_wait_complete(struct i2o_controller *c, u32 m, + struct i2o_message *msg) +{ + struct i2o_exec_wait *wait, *tmp; + static spinlock_t lock = SPIN_LOCK_UNLOCKED; + int rc = 1; + u32 context; + + context = readl(&msg->u.s.tcntxt); + + /* + * We need to search through the i2o_exec_wait_list to see if the given + * message is still outstanding. If not, it means that the IOP took + * longer to respond to the message than we had allowed and timer has + * already expired. Not much we can do about that except log it for + * debug purposes, increase timeout, and recompile. + */ + spin_lock(&lock); + list_for_each_entry_safe(wait, tmp, &i2o_exec_wait_list, list) { + if (wait->tcntxt == context) { + list_del(&wait->list); + + wait->m = m; + wait->msg = msg; + wait->complete = 1; + + barrier(); + + if (wait->wq) { + wake_up_interruptible(wait->wq); + rc = 0; + } else { + struct device *dev; + + dev = &c->pdev->dev; + + pr_debug("timedout reply received!\n"); + i2o_dma_free(dev, &wait->dma); + i2o_exec_wait_free(wait); + rc = -1; + } + + spin_unlock(&lock); + + return rc; + } + } + + spin_unlock(&lock); + + pr_debug("i2o: Bogus reply in POST WAIT (tr-context: %08x)!\n", + context); + + return -1; +}; + +/** + * i2o_exec_probe - Called if a new I2O device (executive class) appears + * @dev: I2O device which should be probed + * + * Registers event notification for every event from Executive device. The + * return is always 0, because we want all devices of class Executive. + * + * Returns 0 on success. + */ +static int i2o_exec_probe(struct device *dev) +{ + struct i2o_device *i2o_dev = to_i2o_device(dev); + + i2o_event_register(i2o_dev, &i2o_exec_driver, 0, 0xffffffff); + + i2o_dev->iop->exec = i2o_dev; + + return 0; +}; + +/** + * i2o_exec_remove - Called on I2O device removal + * @dev: I2O device which was removed + * + * Unregisters event notification from Executive I2O device. + * + * Returns 0 on success. + */ +static int i2o_exec_remove(struct device *dev) +{ + i2o_event_register(to_i2o_device(dev), &i2o_exec_driver, 0, 0); + + return 0; +}; + +/** + * i2o_exec_lct_modified - Called on LCT NOTIFY reply + * @c: I2O controller on which the LCT has modified + * + * This function handles asynchronus LCT NOTIFY replies. It parses the + * new LCT and if the buffer for the LCT was to small sends a LCT NOTIFY + * again. + */ +static void i2o_exec_lct_modified(struct i2o_controller *c) +{ + if (i2o_device_parse_lct(c) == -EAGAIN) + i2o_exec_lct_notify(c, 0); +}; + +/** + * i2o_exec_reply - I2O Executive reply handler + * @c: I2O controller from which the reply comes + * @m: message id + * @msg: pointer to the I2O reply message + * + * This function is always called from interrupt context. If a POST WAIT + * reply was received, pass it to the complete function. If a LCT NOTIFY + * reply was received, a new event is created to handle the update. + * + * Returns 0 on success and if the reply should not be flushed or > 0 + * on success and if the reply should be flushed. Returns negative error + * code on failure and if the reply should be flushed. + */ +static int i2o_exec_reply(struct i2o_controller *c, u32 m, + struct i2o_message *msg) +{ + if (readl(&msg->u.head[0]) & MSG_FAIL) { // Fail bit is set + struct i2o_message *pmsg; /* preserved message */ + u32 pm; + + pm = readl(&msg->body[3]); + + pmsg = c->in_queue.virt + pm; + + i2o_report_status(KERN_INFO, "i2o_core", msg); + + /* Release the preserved msg by resubmitting it as a NOP */ + i2o_msg_nop(c, pm); + + /* If reply to i2o_post_wait failed, return causes a timeout */ + return -1; + } + + if (readl(&msg->u.s.tcntxt) & 0x80000000) + return i2o_msg_post_wait_complete(c, m, msg); + + if ((readl(&msg->u.head[1]) >> 24) == I2O_CMD_LCT_NOTIFY) { + struct work_struct *work; + + pr_debug("%s: LCT notify received\n", c->name); + + work = kmalloc(sizeof(*work), GFP_ATOMIC); + if (!work) + return -ENOMEM; + + INIT_WORK(work, (void (*)(void *))i2o_exec_lct_modified, c); + queue_work(i2o_exec_driver.event_queue, work); + return 1; + } + + /* + * If this happens, we want to dump the message to the syslog so + * it can be sent back to the card manufacturer by the end user + * to aid in debugging. + * + */ + printk(KERN_WARNING "%s: Unsolicited message reply sent to core!" + "Message dumped to syslog\n", c->name); + i2o_dump_message(msg); + + return -EFAULT; +} + +/** + * i2o_exec_event - Event handling function + * @evt: Event which occurs + * + * Handles events send by the Executive device. At the moment does not do + * anything useful. + */ +static void i2o_exec_event(struct i2o_event *evt) +{ + printk(KERN_INFO "Event received from device: %d\n", + evt->i2o_dev->lct_data.tid); + kfree(evt); +}; + +/** + * i2o_exec_lct_get - Get the IOP's Logical Configuration Table + * @c: I2O controller from which the LCT should be fetched + * + * Send a LCT NOTIFY request to the controller, and wait + * I2O_TIMEOUT_LCT_GET seconds until arrival of response. If the LCT is + * to large, retry it. + * + * Returns 0 on success or negative error code on failure. + */ +int i2o_exec_lct_get(struct i2o_controller *c) +{ + struct i2o_message *msg; + u32 m; + int i = 0; + int rc = -EAGAIN; + + for (i = 1; i <= I2O_LCT_GET_TRIES; i++) { + m = i2o_msg_get_wait(c, &msg, I2O_TIMEOUT_MESSAGE_GET); + if (m == I2O_QUEUE_EMPTY) + return -ETIMEDOUT; + + writel(EIGHT_WORD_MSG_SIZE | SGL_OFFSET_6, &msg->u.head[0]); + writel(I2O_CMD_LCT_NOTIFY << 24 | HOST_TID << 12 | ADAPTER_TID, + &msg->u.head[1]); + writel(0xffffffff, &msg->body[0]); + writel(0x00000000, &msg->body[1]); + writel(0xd0000000 | c->dlct.len, &msg->body[2]); + writel(c->dlct.phys, &msg->body[3]); + + rc = i2o_msg_post_wait(c, m, I2O_TIMEOUT_LCT_GET); + if (rc < 0) + break; + + rc = i2o_device_parse_lct(c); + if (rc != -EAGAIN) + break; + } + + return rc; +} + +/** + * i2o_exec_lct_notify - Send a asynchronus LCT NOTIFY request + * @c: I2O controller to which the request should be send + * @change_ind: change indicator + * + * This function sends a LCT NOTIFY request to the I2O controller with + * the change indicator change_ind. If the change_ind == 0 the controller + * replies immediately after the request. If change_ind > 0 the reply is + * send after change indicator of the LCT is > change_ind. + */ +int i2o_exec_lct_notify(struct i2o_controller *c, u32 change_ind) +{ + i2o_status_block *sb = c->status_block.virt; + struct device *dev; + struct i2o_message *msg; + u32 m; + + dev = &c->pdev->dev; + + if (i2o_dma_realloc(dev, &c->dlct, sb->expected_lct_size, GFP_KERNEL)) + return -ENOMEM; + + m = i2o_msg_get_wait(c, &msg, I2O_TIMEOUT_MESSAGE_GET); + if (m == I2O_QUEUE_EMPTY) + return -ETIMEDOUT; + + writel(EIGHT_WORD_MSG_SIZE | SGL_OFFSET_6, &msg->u.head[0]); + writel(I2O_CMD_LCT_NOTIFY << 24 | HOST_TID << 12 | ADAPTER_TID, + &msg->u.head[1]); + writel(i2o_exec_driver.context, &msg->u.s.icntxt); + writel(0, &msg->u.s.tcntxt); /* FIXME */ + writel(0xffffffff, &msg->body[0]); + writel(change_ind, &msg->body[1]); + writel(0xd0000000 | c->dlct.len, &msg->body[2]); + writel(c->dlct.phys, &msg->body[3]); + + i2o_msg_post(c, m); + + return 0; +}; + +/* Exec OSM driver struct */ +struct i2o_driver i2o_exec_driver = { + .name = "exec-osm", + .reply = i2o_exec_reply, + .event = i2o_exec_event, + .classes = i2o_exec_class_id, + .driver = { + .probe = i2o_exec_probe, + .remove = i2o_exec_remove, + }, +}; + +/** + * i2o_exec_init - Registers the Exec OSM + * + * Registers the Exec OSM in the I2O core. + * + * Returns 0 on success or negative error code on failure. + */ +int __init i2o_exec_init(void) +{ + return i2o_driver_register(&i2o_exec_driver); +}; + +/** + * i2o_exec_exit - Removes the Exec OSM + * + * Unregisters the Exec OSM from the I2O core. + */ +void __exit i2o_exec_exit(void) +{ + i2o_driver_unregister(&i2o_exec_driver); +}; + +EXPORT_SYMBOL(i2o_msg_post_wait_mem); +EXPORT_SYMBOL(i2o_exec_lct_get); +EXPORT_SYMBOL(i2o_exec_lct_notify); diff --git a/drivers/message/i2o/i2o_block.h b/drivers/message/i2o/i2o_block.h new file mode 100644 index 000000000..ddd9a1567 --- /dev/null +++ b/drivers/message/i2o/i2o_block.h @@ -0,0 +1,99 @@ +/* + * Block OSM structures/API + * + * Copyright (C) 1999-2002 Red Hat Software + * + * Written by Alan Cox, Building Number Three 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. + * + * For the purpose of avoiding doubt the preferred form of the work + * for making modifications shall be a standards compliant form such + * gzipped tar and not one requiring a proprietary or patent encumbered + * tool to unpack. + * + * Fixes/additions: + * Steve Ralston: + * Multiple device handling error fixes, + * Added a queue depth. + * Alan Cox: + * FC920 has an rmw bug. Dont or in the end marker. + * Removed queue walk, fixed for 64bitness. + * Rewrote much of the code over time + * Added indirect block lists + * Handle 64K limits on many controllers + * Don't use indirects on the Promise (breaks) + * Heavily chop down the queue depths + * Deepak Saxena: + * Independent queues per IOP + * Support for dynamic device creation/deletion + * Code cleanup + * Support for larger I/Os through merge* functions + * (taken from DAC960 driver) + * Boji T Kannanthanam: + * Set the I2O Block devices to be detected in increasing + * order of TIDs during boot. + * Search and set the I2O block device that we boot off + * from as the first device to be claimed (as /dev/i2o/hda) + * Properly attach/detach I2O gendisk structure from the + * system gendisk list. The I2O block devices now appear in + * /proc/partitions. + * Markus Lidel : + * Minor bugfixes for 2.6. + */ + +#ifndef I2O_BLOCK_OSM_H +#define I2O_BLOCK_OSM_H + +#define I2O_BLOCK_RETRY_TIME HZ/4 +#define I2O_BLOCK_MAX_OPEN_REQUESTS 50 + +/* I2O Block OSM mempool struct */ +struct i2o_block_mempool { + kmem_cache_t *slab; + mempool_t *pool; +}; + +/* I2O Block device descriptor */ +struct i2o_block_device { + struct i2o_device *i2o_dev; /* pointer to I2O device */ + struct gendisk *gd; + spinlock_t lock; /* queue lock */ + struct list_head open_queue; /* list of transfered, but unfinished + requests */ + unsigned int open_queue_depth; /* number of requests in the queue */ + + int rcache; /* read cache flags */ + int wcache; /* write cache flags */ + int flags; + int power; /* power state */ + int media_change_flag; /* media changed flag */ +}; + +/* I2O Block device request */ +struct i2o_block_request +{ + struct list_head queue; + struct request *req; /* corresponding request */ + struct i2o_block_device *i2o_blk_dev; /* I2O block device */ + int sg_dma_direction; /* direction of DMA buffer read/write */ + int sg_nents; /* number of SG elements */ + struct scatterlist sg_table[I2O_MAX_SEGMENTS]; /* SG table */ +}; + +/* I2O Block device delayed request */ +struct i2o_block_delayed_request +{ + struct work_struct work; + struct request_queue *queue; +}; + +#endif diff --git a/drivers/message/i2o/iop.c b/drivers/message/i2o/iop.c new file mode 100644 index 000000000..699723e3a --- /dev/null +++ b/drivers/message/i2o/iop.c @@ -0,0 +1,1258 @@ +/* + * Functions to handle I2O controllers and I2O message handling + * + * Copyright (C) 1999-2002 Red Hat Software + * + * Written by Alan Cox, Building Number Three 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. + * + * A lot of the I2O message side code from this is taken from the + * Red Creek RCPCI45 adapter driver by Red Creek Communications + * + * Fixes/additions: + * Philipp Rumpf + * Juha Sievänen + * Auvo Häkkinen + * Deepak Saxena + * Boji T Kannanthanam + * Alan Cox : + * Ported to Linux 2.5. + * Markus Lidel : + * Minor fixes for 2.6. + */ + +#include +#include + +/* global I2O controller list */ +LIST_HEAD(i2o_controllers); + +/* + * global I2O System Table. Contains information about all the IOPs in the + * system. Used to inform IOPs about each others existence. + */ +static struct i2o_dma i2o_systab; + +/* Module internal functions from other sources */ +extern struct i2o_driver i2o_exec_driver; +extern int i2o_exec_lct_get(struct i2o_controller *); +extern void i2o_device_remove(struct i2o_device *); + +extern int __init i2o_driver_init(void); +extern void __exit i2o_driver_exit(void); +extern int __init i2o_exec_init(void); +extern void __exit i2o_exec_exit(void); +extern int __init i2o_pci_init(void); +extern void __exit i2o_pci_exit(void); +extern int i2o_device_init(void); +extern void i2o_device_exit(void); + +/** + * i2o_msg_nop - Returns a message which is not used + * @c: I2O controller from which the message was created + * @m: message which should be returned + * + * If you fetch a message via i2o_msg_get, and can't use it, you must + * return the message with this function. Otherwise the message frame + * is lost. + */ +void i2o_msg_nop(struct i2o_controller *c, u32 m) +{ + struct i2o_message *msg = c->in_queue.virt + m; + + writel(THREE_WORD_MSG_SIZE | SGL_OFFSET_0, &msg->u.head[0]); + writel(I2O_CMD_UTIL_NOP << 24 | HOST_TID << 12 | ADAPTER_TID, + &msg->u.head[1]); + writel(0, &msg->u.head[2]); + writel(0, &msg->u.head[3]); + i2o_msg_post(c, m); +}; + +/** + * i2o_msg_get_wait - obtain an I2O message from the IOP + * @c: I2O controller + * @msg: pointer to a I2O message pointer + * @wait: how long to wait until timeout + * + * This function waits up to wait seconds for a message slot to be + * available. + * + * On a success the message is returned and the pointer to the message is + * set in msg. The returned message is the physical page frame offset + * address from the read port (see the i2o spec). If no message is + * available returns I2O_QUEUE_EMPTY and msg is leaved untouched. + */ +u32 i2o_msg_get_wait(struct i2o_controller *c, struct i2o_message **msg, + int wait) +{ + unsigned long timeout = jiffies + wait * HZ; + u32 m; + + while ((m = i2o_msg_get(c, msg)) == I2O_QUEUE_EMPTY) { + if (time_after(jiffies, timeout)) { + pr_debug("%s: Timeout waiting for message frame.\n", + c->name); + return I2O_QUEUE_EMPTY; + } + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout(1); + } + + return m; +}; + +#if BITS_PER_LONG == 64 +/** + * i2o_cntxt_list_add - Append a pointer to context list and return a id + * @c: controller to which the context list belong + * @ptr: pointer to add to the context list + * + * Because the context field in I2O is only 32-bit large, on 64-bit the + * pointer is to large to fit in the context field. The i2o_cntxt_list + * functions therefore map pointers to context fields. + * + * Returns context id > 0 on success or 0 on failure. + */ +u32 i2o_cntxt_list_add(struct i2o_controller *c, void *ptr) +{ + struct i2o_context_list_element *entry; + unsigned long flags; + + if (!ptr) + printk(KERN_ERR "NULL pointer found!\n"); + + entry = kmalloc(sizeof(*entry), GFP_ATOMIC); + if (!entry) { + printk(KERN_ERR "i2o: Could not allocate memory for context " + "list element\n"); + return 0; + } + + entry->ptr = ptr; + entry->timestamp = jiffies; + INIT_LIST_HEAD(&entry->list); + + spin_lock_irqsave(&c->context_list_lock, flags); + + if (unlikely(atomic_inc_and_test(&c->context_list_counter))) + atomic_inc(&c->context_list_counter); + + entry->context = atomic_read(&c->context_list_counter); + + list_add(&entry->list, &c->context_list); + + spin_unlock_irqrestore(&c->context_list_lock, flags); + + pr_debug("Add context to list %p -> %d\n", ptr, context); + + return entry->context; +}; + +/** + * i2o_cntxt_list_remove - Remove a pointer from the context list + * @c: controller to which the context list belong + * @ptr: pointer which should be removed from the context list + * + * Removes a previously added pointer from the context list and returns + * the matching context id. + * + * Returns context id on succes or 0 on failure. + */ +u32 i2o_cntxt_list_remove(struct i2o_controller *c, void *ptr) +{ + struct i2o_context_list_element *entry; + u32 context = 0; + unsigned long flags; + + spin_lock_irqsave(&c->context_list_lock, flags); + list_for_each_entry(entry, &c->context_list, list) + if (entry->ptr == ptr) { + list_del(&entry->list); + context = entry->context; + kfree(entry); + break; + } + spin_unlock_irqrestore(&c->context_list_lock, flags); + + if (!context) + printk(KERN_WARNING "i2o: Could not remove nonexistent ptr " + "%p\n", ptr); + + pr_debug("remove ptr from context list %d -> %p\n", context, ptr); + + return context; +}; + +/** + * i2o_cntxt_list_get - Get a pointer from the context list and remove it + * @c: controller to which the context list belong + * @context: context id to which the pointer belong + * + * Returns pointer to the matching context id on success or NULL on + * failure. + */ +void *i2o_cntxt_list_get(struct i2o_controller *c, u32 context) +{ + struct i2o_context_list_element *entry; + unsigned long flags; + void *ptr = NULL; + + spin_lock_irqsave(&c->context_list_lock, flags); + list_for_each_entry(entry, &c->context_list, list) + if (entry->context == context) { + list_del(&entry->list); + ptr = entry->ptr; + kfree(entry); + break; + } + spin_unlock_irqrestore(&c->context_list_lock, flags); + + if (!ptr) + printk(KERN_WARNING "i2o: context id %d not found\n", context); + + pr_debug("get ptr from context list %d -> %p\n", context, ptr); + + return ptr; +}; + +/** + * i2o_cntxt_list_get_ptr - Get a context id from the context list + * @c: controller to which the context list belong + * @ptr: pointer to which the context id should be fetched + * + * Returns context id which matches to the pointer on succes or 0 on + * failure. + */ +u32 i2o_cntxt_list_get_ptr(struct i2o_controller * c, void *ptr) +{ + struct i2o_context_list_element *entry; + u32 context = 0; + unsigned long flags; + + spin_lock_irqsave(&c->context_list_lock, flags); + list_for_each_entry(entry, &c->context_list, list) + if (entry->ptr == ptr) { + context = entry->context; + break; + } + spin_unlock_irqrestore(&c->context_list_lock, flags); + + if (!context) + printk(KERN_WARNING "i2o: Could not find nonexistent ptr " + "%p\n", ptr); + + pr_debug("get context id from context list %p -> %d\n", ptr, context); + + return context; +}; +#endif + +/** + * i2o_iop_find - Find an I2O controller by id + * @unit: unit number of the I2O controller to search for + * + * Lookup the I2O controller on the controller list. + * + * Returns pointer to the I2O controller on success or NULL if not found. + */ +struct i2o_controller *i2o_find_iop(int unit) +{ + struct i2o_controller *c; + + list_for_each_entry(c, &i2o_controllers, list) { + if (c->unit == unit) + return c; + } + + return NULL; +}; + +/** + * i2o_iop_find_device - Find a I2O device on an I2O controller + * @c: I2O controller where the I2O device hangs on + * @tid: TID of the I2O device to search for + * + * Searches the devices of the I2O controller for a device with TID tid and + * returns it. + * + * Returns a pointer to the I2O device if found, otherwise NULL. + */ +struct i2o_device *i2o_iop_find_device(struct i2o_controller *c, u16 tid) +{ + struct i2o_device *dev; + + list_for_each_entry(dev, &c->devices, list) + if (dev->lct_data.tid == tid) + return dev; + + return NULL; +}; + +/** + * i2o_quiesce_controller - quiesce controller + * @c: controller + * + * Quiesce an IOP. Causes IOP to make external operation quiescent + * (i2o 'READY' state). Internal operation of the IOP continues normally. + * + * Returns 0 on success or negative error code on failure. + */ +static int i2o_iop_quiesce(struct i2o_controller *c) +{ + struct i2o_message *msg; + u32 m; + i2o_status_block *sb = c->status_block.virt; + int rc; + + i2o_status_get(c); + + /* SysQuiesce discarded if IOP not in READY or OPERATIONAL state */ + if ((sb->iop_state != ADAPTER_STATE_READY) && + (sb->iop_state != ADAPTER_STATE_OPERATIONAL)) + return 0; + + m = i2o_msg_get_wait(c, &msg, I2O_TIMEOUT_MESSAGE_GET); + if (m == I2O_QUEUE_EMPTY) + return -ETIMEDOUT; + + writel(FOUR_WORD_MSG_SIZE | SGL_OFFSET_0, &msg->u.head[0]); + writel(I2O_CMD_SYS_QUIESCE << 24 | HOST_TID << 12 | ADAPTER_TID, + &msg->u.head[1]); + + /* Long timeout needed for quiesce if lots of devices */ + if ((rc = i2o_msg_post_wait(c, m, 240))) + printk(KERN_INFO "%s: Unable to quiesce (status=%#x).\n", + c->name, -rc); + else + pr_debug("%s: Quiesced.\n", c->name); + + i2o_status_get(c); // Entered READY state + + return rc; +}; + +/** + * i2o_iop_enable - move controller from ready to OPERATIONAL + * @c: I2O controller + * + * Enable IOP. This allows the IOP to resume external operations and + * reverses the effect of a quiesce. Returns zero or an error code if + * an error occurs. + */ +static int i2o_iop_enable(struct i2o_controller *c) +{ + struct i2o_message *msg; + u32 m; + i2o_status_block *sb = c->status_block.virt; + int rc; + + i2o_status_get(c); + + /* Enable only allowed on READY state */ + if (sb->iop_state != ADAPTER_STATE_READY) + return -EINVAL; + + m = i2o_msg_get_wait(c, &msg, I2O_TIMEOUT_MESSAGE_GET); + if (m == I2O_QUEUE_EMPTY) + return -ETIMEDOUT; + + writel(FOUR_WORD_MSG_SIZE | SGL_OFFSET_0, &msg->u.head[0]); + writel(I2O_CMD_SYS_ENABLE << 24 | HOST_TID << 12 | ADAPTER_TID, + &msg->u.head[1]); + + /* How long of a timeout do we need? */ + if ((rc = i2o_msg_post_wait(c, m, 240))) + printk(KERN_ERR "%s: Could not enable (status=%#x).\n", + c->name, -rc); + else + pr_debug("%s: Enabled.\n", c->name); + + i2o_status_get(c); // entered OPERATIONAL state + + return rc; +}; + +/** + * i2o_iop_quiesce_all - Quiesce all I2O controllers on the system + * + * Quiesce all I2O controllers which are connected to the system. + */ +static inline void i2o_iop_quiesce_all(void) +{ + struct i2o_controller *c, *tmp; + + list_for_each_entry_safe(c, tmp, &i2o_controllers, list) { + if (!c->no_quiesce) + i2o_iop_quiesce(c); + } +}; + +/** + * i2o_iop_enable_all - Enables all controllers on the system + * + * Enables all I2O controllers which are connected to the system. + */ +static inline void i2o_iop_enable_all(void) +{ + struct i2o_controller *c, *tmp; + + list_for_each_entry_safe(c, tmp, &i2o_controllers, list) + i2o_iop_enable(c); +}; + +/** + * i2o_clear_controller - Bring I2O controller into HOLD state + * @c: controller + * + * Clear an IOP to HOLD state, ie. terminate external operations, clear all + * input queues and prepare for a system restart. IOP's internal operation + * continues normally and the outbound queue is alive. The IOP is not + * expected to rebuild its LCT. + * + * Returns 0 on success or negative error code on failure. + */ +static int i2o_iop_clear(struct i2o_controller *c) +{ + struct i2o_message *msg; + u32 m; + int rc; + + m = i2o_msg_get_wait(c, &msg, I2O_TIMEOUT_MESSAGE_GET); + if (m == I2O_QUEUE_EMPTY) + return -ETIMEDOUT; + + /* Quiesce all IOPs first */ + i2o_iop_quiesce_all(); + + writel(FOUR_WORD_MSG_SIZE | SGL_OFFSET_0, &msg->u.head[0]); + writel(I2O_CMD_ADAPTER_CLEAR << 24 | HOST_TID << 12 | ADAPTER_TID, + &msg->u.head[1]); + + if ((rc = i2o_msg_post_wait(c, m, 30))) + printk(KERN_INFO "%s: Unable to clear (status=%#x).\n", + c->name, -rc); + else + pr_debug("%s: Cleared.\n", c->name); + + /* Enable all IOPs */ + i2o_iop_enable_all(); + + i2o_status_get(c); + + return rc; +} + +/** + * i2o_iop_reset - reset an I2O controller + * @c: controller to reset + * + * Reset the IOP into INIT state and wait until IOP gets into RESET state. + * Terminate all external operations, clear IOP's inbound and outbound + * queues, terminate all DDMs, and reload the IOP's operating environment + * and all local DDMs. The IOP rebuilds its LCT. + */ +static int i2o_iop_reset(struct i2o_controller *c) +{ + u8 *status = c->status.virt; + struct i2o_message *msg; + u32 m; + unsigned long timeout; + i2o_status_block *sb = c->status_block.virt; + int rc = 0; + + pr_debug("Resetting controller\n"); + + m = i2o_msg_get_wait(c, &msg, I2O_TIMEOUT_MESSAGE_GET); + if (m == I2O_QUEUE_EMPTY) + return -ETIMEDOUT; + + memset(status, 0, 4); + + /* Quiesce all IOPs first */ + i2o_iop_quiesce_all(); + + writel(EIGHT_WORD_MSG_SIZE | SGL_OFFSET_0, &msg->u.head[0]); + writel(I2O_CMD_ADAPTER_RESET << 24 | HOST_TID << 12 | ADAPTER_TID, + &msg->u.head[1]); + writel(i2o_exec_driver.context, &msg->u.s.icntxt); + writel(0, &msg->u.s.tcntxt); //FIXME: use reasonable transaction context + writel(0, &msg->body[0]); + writel(0, &msg->body[1]); + writel(i2o_ptr_low((void *)c->status.phys), &msg->body[2]); + writel(i2o_ptr_high((void *)c->status.phys), &msg->body[3]); + + i2o_msg_post(c, m); + + /* Wait for a reply */ + timeout = jiffies + I2O_TIMEOUT_RESET * HZ; + while (!*status) { + if (time_after(jiffies, timeout)) { + printk(KERN_ERR "IOP reset timeout.\n"); + rc = -ETIMEDOUT; + goto exit; + } + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout(1); + + rmb(); + } + + if (*status == I2O_CMD_IN_PROGRESS) { + /* + * Once the reset is sent, the IOP goes into the INIT state + * which is indeterminate. We need to wait until the IOP + * has rebooted before we can let the system talk to + * it. We read the inbound Free_List until a message is + * available. If we can't read one in the given ammount of + * time, we assume the IOP could not reboot properly. + */ + pr_debug("%s: Reset in progress, waiting for reboot...\n", + c->name); + + m = i2o_msg_get_wait(c, &msg, I2O_TIMEOUT_RESET); + while (m == I2O_QUEUE_EMPTY) { + if (time_after(jiffies, timeout)) { + printk(KERN_ERR "IOP reset timeout.\n"); + rc = -ETIMEDOUT; + goto exit; + } + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout(1); + + m = i2o_msg_get_wait(c, &msg, I2O_TIMEOUT_RESET); + } + i2o_msg_nop(c, m); + } + + /* from here all quiesce commands are safe */ + c->no_quiesce = 0; + + /* If IopReset was rejected or didn't perform reset, try IopClear */ + i2o_status_get(c); + if (*status == I2O_CMD_REJECTED || sb->iop_state != ADAPTER_STATE_RESET) { + printk(KERN_WARNING "%s: Reset rejected, trying to clear\n", + c->name); + i2o_iop_clear(c); + } else + pr_debug("%s: Reset completed.\n", c->name); + + exit: + /* Enable all IOPs */ + i2o_iop_enable_all(); + + return rc; +}; + +/** + * i2o_iop_init_outbound_queue - setup the outbound message queue + * @c: I2O controller + * + * Clear and (re)initialize IOP's outbound queue and post the message + * frames to the IOP. + * + * Returns 0 on success or a negative errno code on failure. + */ +int i2o_iop_init_outbound_queue(struct i2o_controller *c) +{ + u8 *status = c->status.virt; + u32 m; + struct i2o_message *msg; + ulong timeout; + int i; + + pr_debug("%s: Initializing Outbound Queue...\n", c->name); + + memset(status, 0, 4); + + m = i2o_msg_get_wait(c, &msg, I2O_TIMEOUT_MESSAGE_GET); + if (m == I2O_QUEUE_EMPTY) + return -ETIMEDOUT; + + writel(EIGHT_WORD_MSG_SIZE | TRL_OFFSET_6, &msg->u.head[0]); + writel(I2O_CMD_OUTBOUND_INIT << 24 | HOST_TID << 12 | ADAPTER_TID, + &msg->u.head[1]); + writel(i2o_exec_driver.context, &msg->u.s.icntxt); + writel(0x0106, &msg->u.s.tcntxt); /* FIXME: why 0x0106, maybe in + Spec? */ + writel(PAGE_SIZE, &msg->body[0]); + writel(MSG_FRAME_SIZE << 16 | 0x80, &msg->body[1]); /* Outbound msg frame + size in words and Initcode */ + writel(0xd0000004, &msg->body[2]); + writel(i2o_ptr_low((void *)c->status.phys), &msg->body[3]); + writel(i2o_ptr_high((void *)c->status.phys), &msg->body[4]); + + i2o_msg_post(c, m); + + timeout = jiffies + I2O_TIMEOUT_INIT_OUTBOUND_QUEUE * HZ; + while (*status <= I2O_CMD_IN_PROGRESS) { + if (time_after(jiffies, timeout)) { + printk(KERN_WARNING "%s: Timeout Initializing\n", + c->name); + return -ETIMEDOUT; + } + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout(1); + + rmb(); + } + + m = c->out_queue.phys; + + /* Post frames */ + for (i = 0; i < NMBR_MSG_FRAMES; i++) { + i2o_flush_reply(c, m); + m += MSG_FRAME_SIZE * 4; + } + + return 0; +} + +/** + * i2o_iop_activate - Bring controller up to HOLD + * @c: controller + * + * This function brings an I2O controller into HOLD state. The adapter + * is reset if necessary and then the queues and resource table are read. + * + * Returns 0 on success or negative error code on failure. + */ +static int i2o_iop_activate(struct i2o_controller *c) +{ + i2o_status_block *sb = c->status_block.virt; + int rc; + /* In INIT state, Wait Inbound Q to initialize (in i2o_status_get) */ + /* In READY state, Get status */ + + rc = i2o_status_get(c); + if (rc) { + printk(KERN_INFO "Unable to obtain status of %s, " + "attempting a reset.\n", c->name); + if (i2o_iop_reset(c)) + return rc; + } + + if (sb->i2o_version > I2OVER15) { + printk(KERN_ERR "%s: Not running vrs. 1.5. of the I2O " + "Specification.\n", c->name); + return -ENODEV; + } + + switch (sb->iop_state) { + case ADAPTER_STATE_FAULTED: + printk(KERN_CRIT "%s: hardware fault\n", c->name); + return -ENODEV; + + case ADAPTER_STATE_READY: + case ADAPTER_STATE_OPERATIONAL: + case ADAPTER_STATE_HOLD: + case ADAPTER_STATE_FAILED: + pr_debug("already running, trying to reset...\n"); + if (i2o_iop_reset(c)) + return -ENODEV; + } + + rc = i2o_iop_init_outbound_queue(c); + if (rc) + return rc; + + /* In HOLD state */ + + rc = i2o_hrt_get(c); + if (rc) + return rc; + + return 0; +}; + +/** + * i2o_iop_systab_set - Set the I2O System Table of the specified IOP + * @c: I2O controller to which the system table should be send + * + * Before the systab could be set i2o_systab_build() must be called. + * + * Returns 0 on success or negative error code on failure. + */ +static int i2o_iop_systab_set(struct i2o_controller *c) +{ + struct i2o_message *msg; + u32 m; + i2o_status_block *sb = c->status_block.virt; + struct device *dev = &c->pdev->dev; + struct resource *root; + int rc; + + if (sb->current_mem_size < sb->desired_mem_size) { + struct resource *res = &c->mem_resource; + res->name = c->pdev->bus->name; + res->flags = IORESOURCE_MEM; + res->start = 0; + res->end = 0; + printk("%s: requires private memory resources.\n", c->name); + root = pci_find_parent_resource(c->pdev, res); + if (root == NULL) + printk("Can't find parent resource!\n"); + if (root && allocate_resource(root, res, sb->desired_mem_size, sb->desired_mem_size, sb->desired_mem_size, 1 << 20, /* Unspecified, so use 1Mb and play safe */ + NULL, NULL) >= 0) { + c->mem_alloc = 1; + sb->current_mem_size = 1 + res->end - res->start; + sb->current_mem_base = res->start; + printk(KERN_INFO + "%s: allocated %ld bytes of PCI memory at 0x%08lX.\n", + c->name, 1 + res->end - res->start, res->start); + } + } + + if (sb->current_io_size < sb->desired_io_size) { + struct resource *res = &c->io_resource; + res->name = c->pdev->bus->name; + res->flags = IORESOURCE_IO; + res->start = 0; + res->end = 0; + printk("%s: requires private memory resources.\n", c->name); + root = pci_find_parent_resource(c->pdev, res); + if (root == NULL) + printk("Can't find parent resource!\n"); + if (root && allocate_resource(root, res, sb->desired_io_size, sb->desired_io_size, sb->desired_io_size, 1 << 20, /* Unspecified, so use 1Mb and play safe */ + NULL, NULL) >= 0) { + c->io_alloc = 1; + sb->current_io_size = 1 + res->end - res->start; + sb->current_mem_base = res->start; + printk(KERN_INFO + "%s: allocated %ld bytes of PCI I/O at 0x%08lX.\n", + c->name, 1 + res->end - res->start, res->start); + } + } + + m = i2o_msg_get_wait(c, &msg, I2O_TIMEOUT_MESSAGE_GET); + if (m == I2O_QUEUE_EMPTY) + return -ETIMEDOUT; + + i2o_systab.phys = dma_map_single(dev, i2o_systab.virt, i2o_systab.len, + PCI_DMA_TODEVICE); + if (!i2o_systab.phys) { + i2o_msg_nop(c, m); + return -ENOMEM; + } + + writel(I2O_MESSAGE_SIZE(12) | SGL_OFFSET_6, &msg->u.head[0]); + writel(I2O_CMD_SYS_TAB_SET << 24 | HOST_TID << 12 | ADAPTER_TID, + &msg->u.head[1]); + + /* + * Provide three SGL-elements: + * System table (SysTab), Private memory space declaration and + * Private i/o space declaration + * + * FIXME: is this still true? + * Nasty one here. We can't use dma_alloc_coherent to send the + * same table to everyone. We have to go remap it for them all + */ + + writel(c->unit + 2, &msg->body[0]); + writel(0, &msg->body[1]); + writel(0x54000000 | i2o_systab.phys, &msg->body[2]); + writel(i2o_systab.phys, &msg->body[3]); + writel(0x54000000 | sb->current_mem_size, &msg->body[4]); + writel(sb->current_mem_base, &msg->body[5]); + writel(0xd4000000 | sb->current_io_size, &msg->body[6]); + writel(sb->current_io_base, &msg->body[6]); + + rc = i2o_msg_post_wait(c, m, 120); + + dma_unmap_single(dev, i2o_systab.phys, i2o_systab.len, + PCI_DMA_TODEVICE); + + if (rc < 0) + printk(KERN_ERR "%s: Unable to set SysTab (status=%#x).\n", + c->name, -rc); + else + pr_debug("%s: SysTab set.\n", c->name); + + i2o_status_get(c); // Entered READY state + + return rc; +} + +/** + * i2o_iop_online - Bring a controller online into OPERATIONAL state. + * @c: I2O controller + * + * Send the system table and enable the I2O controller. + * + * Returns 0 on success or negativer error code on failure. + */ +static int i2o_iop_online(struct i2o_controller *c) +{ + int rc; + + rc = i2o_iop_systab_set(c); + if (rc) + return rc; + + /* In READY state */ + pr_debug("%s: Attempting to enable...\n", c->name); + rc = i2o_iop_enable(c); + if (rc) + return rc; + + return 0; +}; + +/** + * i2o_iop_remove - Remove the I2O controller from the I2O core + * @c: I2O controller + * + * Remove the I2O controller from the I2O core. If devices are attached to + * the controller remove these also and finally reset the controller. + */ +void i2o_iop_remove(struct i2o_controller *c) +{ + struct i2o_device *dev, *tmp; + + pr_debug("Deleting controller %s\n", c->name); + + i2o_driver_notify_controller_remove_all(c); + + list_del(&c->list); + + list_for_each_entry_safe(dev, tmp, &c->devices, list) + i2o_device_remove(dev); + + /* Ask the IOP to switch to RESET state */ + i2o_iop_reset(c); +} + +/** + * i2o_systab_build - Build system table + * + * The system table contains information about all the IOPs in the system + * (duh) and is used by the Executives on the IOPs to establish peer2peer + * connections. We're not supporting peer2peer at the moment, but this + * will be needed down the road for things like lan2lan forwarding. + * + * Returns 0 on success or negative error code on failure. + */ +static int i2o_systab_build(void) +{ + struct i2o_controller *c, *tmp; + int num_controllers = 0; + u32 change_ind = 0; + int count = 0; + struct i2o_sys_tbl *systab = i2o_systab.virt; + + list_for_each_entry_safe(c, tmp, &i2o_controllers, list) + num_controllers++; + + if (systab) { + change_ind = systab->change_ind; + kfree(i2o_systab.virt); + } + + /* Header + IOPs */ + i2o_systab.len = sizeof(struct i2o_sys_tbl) + num_controllers * + sizeof(struct i2o_sys_tbl_entry); + + systab = i2o_systab.virt = kmalloc(i2o_systab.len, GFP_KERNEL); + if (!systab) { + printk(KERN_ERR "i2o: unable to allocate memory for System " + "Table\n"); + return -ENOMEM; + } + memset(systab, 0, i2o_systab.len); + + systab->version = I2OVERSION; + systab->change_ind = change_ind + 1; + + list_for_each_entry_safe(c, tmp, &i2o_controllers, list) { + i2o_status_block *sb; + + if (count >= num_controllers) { + printk(KERN_ERR "i2o: controller added while building " + "system table\n"); + break; + } + + sb = c->status_block.virt; + + /* + * Get updated IOP state so we have the latest information + * + * We should delete the controller at this point if it + * doesn't respond since if it's not on the system table + * it is techninically not part of the I2O subsystem... + */ + if (unlikely(i2o_status_get(c))) { + printk(KERN_ERR "%s: Deleting b/c could not get status" + " while attempting to build system table\n", + c->name); + i2o_iop_remove(c); + continue; // try the next one + } + + systab->iops[count].org_id = sb->org_id; + systab->iops[count].iop_id = c->unit + 2; + systab->iops[count].seg_num = 0; + systab->iops[count].i2o_version = sb->i2o_version; + systab->iops[count].iop_state = sb->iop_state; + systab->iops[count].msg_type = sb->msg_type; + systab->iops[count].frame_size = sb->inbound_frame_size; + systab->iops[count].last_changed = change_ind; + systab->iops[count].iop_capabilities = sb->iop_capabilities; + systab->iops[count].inbound_low = i2o_ptr_low(c->post_port); + systab->iops[count].inbound_high = i2o_ptr_high(c->post_port); + + count++; + } + + systab->num_entries = count; + + return 0; +}; + +/** + * i2o_parse_hrt - Parse the hardware resource table. + * @c: I2O controller + * + * We don't do anything with it except dumping it (in debug mode). + * + * Returns 0. + */ +static int i2o_parse_hrt(struct i2o_controller *c) +{ + i2o_dump_hrt(c); + return 0; +}; + +/** + * i2o_status_get - Get the status block from the I2O controller + * @c: I2O controller + * + * Issue a status query on the controller. This updates the attached + * status block. The status block could then be accessed through + * c->status_block. + * + * Returns 0 on sucess or negative error code on failure. + */ +int i2o_status_get(struct i2o_controller *c) +{ + struct i2o_message *msg; + u32 m; + u8 *status_block; + unsigned long timeout; + + status_block = (u8 *) c->status_block.virt; + memset(status_block, 0, sizeof(i2o_status_block)); + + m = i2o_msg_get_wait(c, &msg, I2O_TIMEOUT_MESSAGE_GET); + if (m == I2O_QUEUE_EMPTY) + return -ETIMEDOUT; + + writel(NINE_WORD_MSG_SIZE | SGL_OFFSET_0, &msg->u.head[0]); + writel(I2O_CMD_STATUS_GET << 24 | HOST_TID << 12 | ADAPTER_TID, + &msg->u.head[1]); + writel(i2o_exec_driver.context, &msg->u.s.icntxt); + writel(0, &msg->u.s.tcntxt); // FIXME: use resonable transaction context + writel(0, &msg->body[0]); + writel(0, &msg->body[1]); + writel(i2o_ptr_low((void *)c->status_block.phys), &msg->body[2]); + writel(i2o_ptr_high((void *)c->status_block.phys), &msg->body[3]); + writel(sizeof(i2o_status_block), &msg->body[4]); /* always 88 bytes */ + + i2o_msg_post(c, m); + + /* Wait for a reply */ + timeout = jiffies + I2O_TIMEOUT_STATUS_GET * HZ; + while (status_block[87] != 0xFF) { + if (time_after(jiffies, timeout)) { + printk(KERN_ERR "%s: Get status timeout.\n", c->name); + return -ETIMEDOUT; + } + + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout(1); + + rmb(); + } + +#ifdef DEBUG + i2o_debug_state(c); +#endif + + return 0; +} + +/* + * i2o_hrt_get - Get the Hardware Resource Table from the I2O controller + * @c: I2O controller from which the HRT should be fetched + * + * The HRT contains information about possible hidden devices but is + * mostly useless to us. + * + * Returns 0 on success or negativer error code on failure. + */ +int i2o_hrt_get(struct i2o_controller *c) +{ + int rc; + int i; + i2o_hrt *hrt = c->hrt.virt; + u32 size = sizeof(i2o_hrt); + struct device *dev = &c->pdev->dev; + + for (i = 0; i < I2O_HRT_GET_TRIES; i++) { + struct i2o_message *msg; + u32 m; + + m = i2o_msg_get_wait(c, &msg, I2O_TIMEOUT_MESSAGE_GET); + if (m == I2O_QUEUE_EMPTY) + return -ETIMEDOUT; + + writel(SIX_WORD_MSG_SIZE | SGL_OFFSET_4, &msg->u.head[0]); + writel(I2O_CMD_HRT_GET << 24 | HOST_TID << 12 | ADAPTER_TID, + &msg->u.head[1]); + writel(0xd0000000 | c->hrt.len, &msg->body[0]); + writel(c->hrt.phys, &msg->body[1]); + + rc = i2o_msg_post_wait_mem(c, m, 20, &c->hrt); + + if (rc < 0) { + printk(KERN_ERR "%s: Unable to get HRT (status=%#x)\n", + c->name, -rc); + return rc; + } + + size = hrt->num_entries * hrt->entry_len << 2; + if (size > c->hrt.len) { + if (i2o_dma_realloc(dev, &c->hrt, size, GFP_KERNEL)) + return -ENOMEM; + else + hrt = c->hrt.virt; + } else + return i2o_parse_hrt(c); + } + + printk(KERN_ERR "%s: Unable to get HRT after %d tries, giving up\n", + c->name, I2O_HRT_GET_TRIES); + + return -EBUSY; +} + +/** + * i2o_iop_alloc - Allocate and initialize a i2o_controller struct + * + * Allocate the necessary memory for a i2o_controller struct and + * initialize the lists. + * + * Returns a pointer to the I2O controller or a negative error code on + * failure. + */ +struct i2o_controller *i2o_iop_alloc(void) +{ + static int unit = 0; /* 0 and 1 are NULL IOP and Local Host */ + struct i2o_controller *c; + + c = kmalloc(sizeof(*c), GFP_KERNEL); + if (!c) { + printk(KERN_ERR "i2o: Insufficient memory to allocate the " + "controller.\n"); + return ERR_PTR(-ENOMEM); + } + memset(c, 0, sizeof(*c)); + + INIT_LIST_HEAD(&c->devices); + c->lock = SPIN_LOCK_UNLOCKED; + init_MUTEX(&c->lct_lock); + c->unit = unit++; + sprintf(c->name, "iop%d", c->unit); + +#if BITS_PER_LONG == 64 + c->context_list_lock = SPIN_LOCK_UNLOCKED; + atomic_set(&c->context_list_counter, 0); + INIT_LIST_HEAD(&c->context_list); +#endif + + return c; +}; + +/** + * i2o_iop_free - Free the i2o_controller struct + * @c: I2O controller to free + */ +void i2o_iop_free(struct i2o_controller *c) +{ + kfree(c); +}; + +/** + * i2o_iop_add - Initialize the I2O controller and add him to the I2O core + * @c: controller + * + * Initialize the I2O controller and if no error occurs add him to the I2O + * core. + * + * Returns 0 on success or negative error code on failure. + */ +int i2o_iop_add(struct i2o_controller *c) +{ + int rc; + + printk(KERN_INFO "%s: Activating I2O controller...\n", c->name); + printk(KERN_INFO "%s: This may take a few minutes if there are many " + "devices\n", c->name); + + if ((rc = i2o_iop_activate(c))) { + printk(KERN_ERR "%s: controller could not activated\n", + c->name); + i2o_iop_reset(c); + return rc; + } + + pr_debug("building sys table %s...\n", c->name); + + if ((rc = i2o_systab_build())) { + i2o_iop_reset(c); + return rc; + } + + pr_debug("online controller %s...\n", c->name); + + if ((rc = i2o_iop_online(c))) { + i2o_iop_reset(c); + return rc; + } + + pr_debug("getting LCT %s...\n", c->name); + + if ((rc = i2o_exec_lct_get(c))) { + i2o_iop_reset(c); + return rc; + } + + list_add(&c->list, &i2o_controllers); + + i2o_driver_notify_controller_add_all(c); + + printk(KERN_INFO "%s: Controller added\n", c->name); + + return 0; +}; + +/** + * i2o_event_register - Turn on/off event notification for a I2O device + * @dev: I2O device which should receive the event registration request + * @drv: driver which want to get notified + * @tcntxt: transaction context to use with this notifier + * @evt_mask: mask of events + * + * Create and posts an event registration message to the task. No reply + * is waited for, or expected. If you do not want further notifications, + * call the i2o_event_register again with a evt_mask of 0. + * + * Returns 0 on success or -ETIMEDOUT if no message could be fetched for + * sending the request. + */ +int i2o_event_register(struct i2o_device *dev, struct i2o_driver *drv, + int tcntxt, u32 evt_mask) +{ + struct i2o_controller *c = dev->iop; + struct i2o_message *msg; + u32 m; + + m = i2o_msg_get_wait(c, &msg, I2O_TIMEOUT_MESSAGE_GET); + if (m == I2O_QUEUE_EMPTY) + return -ETIMEDOUT; + + writel(FIVE_WORD_MSG_SIZE | SGL_OFFSET_0, &msg->u.head[0]); + writel(I2O_CMD_UTIL_EVT_REGISTER << 24 | HOST_TID << 12 | dev->lct_data. + tid, &msg->u.head[1]); + writel(drv->context, &msg->u.s.icntxt); + writel(tcntxt, &msg->u.s.tcntxt); + writel(evt_mask, &msg->body[0]); + + i2o_msg_post(c, m); + + return 0; +}; + +/** + * i2o_iop_init - I2O main initialization function + * + * Initialize the I2O drivers (OSM) functions, register the Executive OSM, + * initialize the I2O PCI part and finally initialize I2O device stuff. + * + * Returns 0 on success or negative error code on failure. + */ +static int __init i2o_iop_init(void) +{ + int rc = 0; + + printk(KERN_INFO "I2O Core - (C) Copyright 1999 Red Hat Software\n"); + + rc = i2o_device_init(); + if (rc) + goto exit; + + rc = i2o_driver_init(); + if (rc) + goto device_exit; + + rc = i2o_exec_init(); + if (rc) + goto driver_exit; + + rc = i2o_pci_init(); + if (rc < 0) + goto exec_exit; + + return 0; + + exec_exit: + i2o_exec_exit(); + + driver_exit: + i2o_driver_exit(); + + device_exit: + i2o_device_exit(); + + exit: + return rc; +} + +/** + * i2o_iop_exit - I2O main exit function + * + * Removes I2O controllers from PCI subsystem and shut down OSMs. + */ +static void __exit i2o_iop_exit(void) +{ + i2o_pci_exit(); + i2o_exec_exit(); + i2o_driver_exit(); + i2o_device_exit(); +}; + +module_init(i2o_iop_init); +module_exit(i2o_iop_exit); + +MODULE_AUTHOR("Red Hat Software"); +MODULE_DESCRIPTION("I2O Core"); +MODULE_LICENSE("GPL"); + +#if BITS_PER_LONG == 64 +EXPORT_SYMBOL(i2o_cntxt_list_add); +EXPORT_SYMBOL(i2o_cntxt_list_get); +EXPORT_SYMBOL(i2o_cntxt_list_remove); +EXPORT_SYMBOL(i2o_cntxt_list_get_ptr); +#endif +EXPORT_SYMBOL(i2o_msg_get_wait); +EXPORT_SYMBOL(i2o_msg_nop); +EXPORT_SYMBOL(i2o_find_iop); +EXPORT_SYMBOL(i2o_iop_find_device); +EXPORT_SYMBOL(i2o_event_register); +EXPORT_SYMBOL(i2o_status_get); +EXPORT_SYMBOL(i2o_hrt_get); +EXPORT_SYMBOL(i2o_controllers); diff --git a/drivers/message/i2o/pci.c b/drivers/message/i2o/pci.c new file mode 100644 index 000000000..9ee58b6cf --- /dev/null +++ b/drivers/message/i2o/pci.c @@ -0,0 +1,528 @@ +/* + * PCI handling of I2O controller + * + * Copyright (C) 1999-2002 Red Hat Software + * + * Written by Alan Cox, Building Number Three 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. + * + * A lot of the I2O message side code from this is taken from the Red + * Creek RCPCI45 adapter driver by Red Creek Communications + * + * Fixes/additions: + * Philipp Rumpf + * Juha Sievänen + * Auvo Häkkinen + * Deepak Saxena + * Boji T Kannanthanam + * Alan Cox : + * Ported to Linux 2.5. + * Markus Lidel : + * Minor fixes for 2.6. + * Markus Lidel : + * Support for sysfs included. + */ + +#include +#include +#include + +#ifdef CONFIG_MTRR +#include +#endif // CONFIG_MTRR + +/* Module internal functions from other sources */ +extern struct i2o_controller *i2o_iop_alloc(void); +extern void i2o_iop_free(struct i2o_controller *); + +extern int i2o_iop_add(struct i2o_controller *); +extern void i2o_iop_remove(struct i2o_controller *); + +extern int i2o_driver_dispatch(struct i2o_controller *, u32, + struct i2o_message *); + +/* PCI device id table for all I2O controllers */ +static struct pci_device_id __devinitdata i2o_pci_ids[] = { + {PCI_DEVICE_CLASS(PCI_CLASS_INTELLIGENT_I2O << 8, 0xffff00)}, + {PCI_DEVICE(PCI_VENDOR_ID_DPT, 0xa511)}, + {0} +}; + +/** + * i2o_dma_realloc - Realloc DMA memory + * @dev: struct device pointer to the PCI device of the I2O controller + * @addr: pointer to a i2o_dma struct DMA buffer + * @len: new length of memory + * @gfp_mask: GFP mask + * + * If there was something allocated in the addr, free it first. If len > 0 + * than try to allocate it and write the addresses back to the addr + * structure. If len == 0 set the virtual address to NULL. + * + * Returns the 0 on success or negative error code on failure. + */ +int i2o_dma_realloc(struct device *dev, struct i2o_dma *addr, size_t len, + unsigned int gfp_mask) +{ + i2o_dma_free(dev, addr); + + if (len) + return i2o_dma_alloc(dev, addr, len, gfp_mask); + + return 0; +}; + +/** + * i2o_pci_free - Frees the DMA memory for the I2O controller + * @c: I2O controller to free + * + * Remove all allocated DMA memory and unmap memory IO regions. If MTRR + * is enabled, also remove it again. + */ +static void __devexit i2o_pci_free(struct i2o_controller *c) +{ + struct device *dev; + + dev = &c->pdev->dev; + + i2o_dma_free(dev, &c->out_queue); + i2o_dma_free(dev, &c->status_block); + if (c->lct) + kfree(c->lct); + i2o_dma_free(dev, &c->dlct); + i2o_dma_free(dev, &c->hrt); + i2o_dma_free(dev, &c->status); + +#ifdef CONFIG_MTRR + if (c->mtrr_reg0 >= 0) + mtrr_del(c->mtrr_reg0, 0, 0); + if (c->mtrr_reg1 >= 0) + mtrr_del(c->mtrr_reg1, 0, 0); +#endif + + if (c->raptor && c->in_queue.virt) + iounmap(c->in_queue.virt); + + if (c->base.virt) + iounmap(c->base.virt); +} + +/** + * i2o_pci_alloc - Allocate DMA memory, map IO memory for I2O controller + * @c: I2O controller + * + * Allocate DMA memory for a PCI (or in theory AGP) I2O controller. All + * IO mappings are also done here. If MTRR is enabled, also do add memory + * regions here. + * + * Returns 0 on success or negative error code on failure. + */ +static int __devinit i2o_pci_alloc(struct i2o_controller *c) +{ + struct pci_dev *pdev = c->pdev; + struct device *dev = &pdev->dev; + int i; + + for (i = 0; i < 6; i++) { + /* Skip I/O spaces */ + if (!(pci_resource_flags(pdev, i) & IORESOURCE_IO)) { + if (!c->base.phys) { + c->base.phys = pci_resource_start(pdev, i); + c->base.len = pci_resource_len(pdev, i); + + /* + * If we know what card it is, set the size + * correctly. Code is taken from dpt_i2o.c + */ + if(pdev->device == 0xa501) { + if(pdev->subsystem_device >= 0xc032 && + pdev->subsystem_device <= 0xc03b) { + if(c->base.len > 0x400000) + c->base.len = 0x400000; + } else { + if(c->base.len > 0x100000) + c->base.len = 0x100000; + } + } + if (!c->raptor) + break; + } else { + c->in_queue.phys = pci_resource_start(pdev, i); + c->in_queue.len = pci_resource_len(pdev, i); + break; + } + } + } + + if (i == 6) { + printk(KERN_ERR "i2o: I2O controller has no memory regions" + " defined.\n"); + i2o_pci_free(c); + return -EINVAL; + } + + /* Map the I2O controller */ + if (c->raptor) { + printk(KERN_INFO "i2o: PCI I2O controller\n"); + printk(KERN_INFO " BAR0 at 0x%08lX size=%ld\n", + (unsigned long)c->base.phys, (unsigned long)c->base.len); + printk(KERN_INFO " BAR1 at 0x%08lX size=%ld\n", + (unsigned long)c->in_queue.phys, + (unsigned long)c->in_queue.len); + } else + printk(KERN_INFO "i2o: PCI I2O controller at %08lX size=%ld\n", + (unsigned long)c->base.phys, (unsigned long)c->base.len); + + c->base.virt = ioremap(c->base.phys, c->base.len); + if (!c->base.virt) { + printk(KERN_ERR "i2o: Unable to map controller.\n"); + return -ENOMEM; + } + + if (c->raptor) { + c->in_queue.virt = ioremap(c->in_queue.phys, c->in_queue.len); + if (!c->in_queue.virt) { + printk(KERN_ERR "i2o: Unable to map controller.\n"); + i2o_pci_free(c); + return -ENOMEM; + } + } else + c->in_queue = c->base; + + c->irq_mask = c->base.virt + 0x34; + c->post_port = c->base.virt + 0x40; + c->reply_port = c->base.virt + 0x44; + +#ifdef CONFIG_MTRR + /* Enable Write Combining MTRR for IOP's memory region */ + c->mtrr_reg0 = mtrr_add(c->in_queue.phys, c->in_queue.len, + MTRR_TYPE_WRCOMB, 1); + c->mtrr_reg1 = -1; + + if (c->mtrr_reg0 < 0) + printk(KERN_WARNING "i2o: could not enable write combining " + "MTRR\n"); + else + printk(KERN_INFO "i2o: using write combining MTRR\n"); + + /* + * If it is an INTEL i960 I/O processor then set the first 64K to + * Uncacheable since the region contains the messaging unit which + * shouldn't be cached. + */ + if ((pdev->vendor == PCI_VENDOR_ID_INTEL || + pdev->vendor == PCI_VENDOR_ID_DPT) && !c->raptor) { + printk(KERN_INFO "i2o: MTRR workaround for Intel i960 processor" + "\n"); + c->mtrr_reg1 = mtrr_add(c->base.phys, 0x10000, + MTRR_TYPE_UNCACHABLE, 1); + + if (c->mtrr_reg1 < 0) { + printk(KERN_WARNING "i2o_pci: Error in setting " + "MTRR_TYPE_UNCACHABLE\n"); + mtrr_del(c->mtrr_reg0, c->in_queue.phys, + c->in_queue.len); + c->mtrr_reg0 = -1; + } + } +#endif + + if (i2o_dma_alloc(dev, &c->status, 4, GFP_KERNEL)) { + i2o_pci_free(c); + return -ENOMEM; + } + + if (i2o_dma_alloc(dev, &c->hrt, sizeof(i2o_hrt), GFP_KERNEL)) { + i2o_pci_free(c); + return -ENOMEM; + } + + if (i2o_dma_alloc(dev, &c->dlct, 8192, GFP_KERNEL)) { + i2o_pci_free(c); + return -ENOMEM; + } + + if (i2o_dma_alloc(dev, &c->status_block, sizeof(i2o_status_block), + GFP_KERNEL)) { + i2o_pci_free(c); + return -ENOMEM; + } + + if (i2o_dma_alloc(dev, &c->out_queue, MSG_POOL_SIZE, GFP_KERNEL)) { + i2o_pci_free(c); + return -ENOMEM; + } + + pci_set_drvdata(pdev, c); + + return 0; +} + +/** + * i2o_pci_interrupt - Interrupt handler for I2O controller + * @irq: interrupt line + * @dev_id: pointer to the I2O controller + * @r: pointer to registers + * + * Handle an interrupt from a PCI based I2O controller. This turns out + * to be rather simple. We keep the controller pointer in the cookie. + */ +static irqreturn_t i2o_pci_interrupt(int irq, void *dev_id, struct pt_regs *r) +{ + struct i2o_controller *c = dev_id; + struct device *dev = &c->pdev->dev; + struct i2o_message *m; + u32 mv; + u32 *msg; + + /* + * Old 960 steppings had a bug in the I2O unit that caused + * the queue to appear empty when it wasn't. + */ + mv = I2O_REPLY_READ32(c); + if (mv == I2O_QUEUE_EMPTY) { + mv = I2O_REPLY_READ32(c); + if (unlikely(mv == I2O_QUEUE_EMPTY)) { + return IRQ_NONE; + } else + pr_debug("960 bug detected\n"); + } + + while (mv != I2O_QUEUE_EMPTY) { + /* + * Map the message from the page frame map to kernel virtual. + * Because bus_to_virt is deprecated, we have calculate the + * location by ourself! + */ + m = (struct i2o_message *)(mv - + (unsigned long)c->out_queue.phys + + (unsigned long)c->out_queue.virt); + + msg = (u32 *) m; + + /* + * Ensure this message is seen coherently but cachably by + * the processor + */ + dma_sync_single_for_cpu(dev, c->out_queue.phys, MSG_FRAME_SIZE, + PCI_DMA_FROMDEVICE); + + /* dispatch it */ + if (i2o_driver_dispatch(c, mv, m)) + /* flush it if result != 0 */ + i2o_flush_reply(c, mv); + + /* + * That 960 bug again... + */ + mv = I2O_REPLY_READ32(c); + if (mv == I2O_QUEUE_EMPTY) + mv = I2O_REPLY_READ32(c); + } + return IRQ_HANDLED; +} + +/** + * i2o_pci_irq_enable - Allocate interrupt for I2O controller + * + * Allocate an interrupt for the I2O controller, and activate interrupts + * on the I2O controller. + * + * Returns 0 on success or negative error code on failure. + */ +static int i2o_pci_irq_enable(struct i2o_controller *c) +{ + struct pci_dev *pdev = c->pdev; + int rc; + + I2O_IRQ_WRITE32(c, 0xffffffff); + + if (pdev->irq) { + rc = request_irq(pdev->irq, i2o_pci_interrupt, SA_SHIRQ, + c->name, c); + if (rc < 0) { + printk(KERN_ERR "%s: unable to allocate interrupt %d." + "\n", c->name, pdev->irq); + return rc; + } + } + + I2O_IRQ_WRITE32(c, 0x00000000); + + printk(KERN_INFO "%s: Installed at IRQ %d\n", c->name, pdev->irq); + + return 0; +} + +/** + * i2o_pci_irq_disable - Free interrupt for I2O controller + * @c: I2O controller + * + * Disable interrupts in I2O controller and then free interrupt. + */ +static void i2o_pci_irq_disable(struct i2o_controller *c) +{ + I2O_IRQ_WRITE32(c, 0xffffffff); + + if (c->pdev->irq > 0) + free_irq(c->pdev->irq, c); +} + +/** + * i2o_pci_probe - Probe the PCI device for an I2O controller + * @dev: PCI device to test + * @id: id which matched with the PCI device id table + * + * Probe the PCI device for any device which is a memory of the + * Intelligent, I2O class or an Adaptec Zero Channel Controller. We + * attempt to set up each such device and register it with the core. + * + * Returns 0 on success or negative error code on failure. + */ +static int __devinit i2o_pci_probe(struct pci_dev *pdev, + const struct pci_device_id *id) +{ + struct i2o_controller *c; + int rc; + + printk(KERN_INFO "i2o: Checking for PCI I2O controllers...\n"); + + if ((pdev->class & 0xff) > 1) { + printk(KERN_WARNING "i2o: I2O controller found but does not " + "support I2O 1.5 (skipping).\n"); + return -ENODEV; + } + + if ((rc = pci_enable_device(pdev))) { + printk(KERN_WARNING "i2o: I2O controller found but could not be" + " enabled.\n"); + return rc; + } + + printk(KERN_INFO "i2o: I2O controller found on bus %d at %d.\n", + pdev->bus->number, pdev->devfn); + + if (pci_set_dma_mask(pdev, DMA_32BIT_MASK)) { + printk(KERN_WARNING "i2o: I2O controller on bus %d at %d: No " + "suitable DMA available!\n", pdev->bus->number, + pdev->devfn); + rc = -ENODEV; + goto disable; + } + + pci_set_master(pdev); + + c = i2o_iop_alloc(); + if (IS_ERR(c)) { + printk(KERN_ERR "i2o: memory for I2O controller could not be " + "allocated\n"); + rc = PTR_ERR(c); + goto disable; + } + + c->pdev = pdev; + c->device = pdev->dev; + + /* Cards that fall apart if you hit them with large I/O loads... */ + if (pdev->vendor == PCI_VENDOR_ID_NCR && pdev->device == 0x0630) { + c->short_req = 1; + printk(KERN_INFO "i2o: Symbios FC920 workarounds activated.\n"); + } + + if (pdev->subsystem_vendor == PCI_VENDOR_ID_PROMISE) { + c->promise = 1; + printk(KERN_INFO "i2o: Promise workarounds activated.\n"); + } + + /* Cards that go bananas if you quiesce them before you reset them. */ + if (pdev->vendor == PCI_VENDOR_ID_DPT) { + c->no_quiesce = 1; + if (pdev->device == 0xa511) + c->raptor = 1; + } + + if ((rc = i2o_pci_alloc(c))) { + printk(KERN_ERR "i2o: DMA / IO allocation for I2O controller " + " failed\n"); + goto free_controller; + } + + if (i2o_pci_irq_enable(c)) { + printk(KERN_ERR "i2o: unable to enable interrupts for I2O " + "controller\n"); + goto free_pci; + } + + if ((rc = i2o_iop_add(c))) + goto uninstall; + + return 0; + + uninstall: + i2o_pci_irq_disable(c); + + free_pci: + i2o_pci_free(c); + + free_controller: + i2o_iop_free(c); + + disable: + pci_disable_device(pdev); + + return rc; +} + +/** + * i2o_pci_remove - Removes a I2O controller from the system + * pdev: I2O controller which should be removed + * + * Reset the I2O controller, disable interrupts and remove all allocated + * resources. + */ +static void __devexit i2o_pci_remove(struct pci_dev *pdev) +{ + struct i2o_controller *c; + c = pci_get_drvdata(pdev); + + i2o_iop_remove(c); + i2o_pci_irq_disable(c); + i2o_pci_free(c); + + printk(KERN_INFO "%s: Controller removed.\n", c->name); + + i2o_iop_free(c); + pci_disable_device(pdev); +}; + +/* PCI driver for I2O controller */ +static struct pci_driver i2o_pci_driver = { + .name = "I2O controller", + .id_table = i2o_pci_ids, + .probe = i2o_pci_probe, + .remove = __devexit_p(i2o_pci_remove), +}; + +/** + * i2o_pci_init - registers I2O PCI driver in PCI subsystem + * + * Returns > 0 on success or negative error code on failure. + */ +int __init i2o_pci_init(void) +{ + return pci_register_driver(&i2o_pci_driver); +}; + +/** + * i2o_pci_exit - unregisters I2O PCI driver from PCI subsystem + */ +void __exit i2o_pci_exit(void) +{ + pci_unregister_driver(&i2o_pci_driver); +}; + +EXPORT_SYMBOL(i2o_dma_realloc); diff --git a/drivers/mmc/Kconfig b/drivers/mmc/Kconfig new file mode 100644 index 000000000..6b1b5177d --- /dev/null +++ b/drivers/mmc/Kconfig @@ -0,0 +1,52 @@ +# +# MMC subsystem configuration +# + +menu "MMC/SD Card support" + +config MMC + tristate "MMC support" + help + MMC is the "multi-media card" bus protocol. + + If you want MMC support, you should say Y here and also + to the specific driver for your MMC interface. + +config MMC_DEBUG + bool "MMC debugging" + depends on MMC != n + help + This is an option for use by developers; most people should + say N here. This enables MMC core and driver debugging. + +config MMC_BLOCK + tristate "MMC block device driver" + depends on MMC + default y + help + Say Y here to enable the MMC block device driver support. + This provides a block device driver, which you can use to + mount the filesystem. Almost everyone wishing MMC support + should say Y or M here. + +config MMC_ARMMMCI + tristate "ARM AMBA Multimedia Card Interface support" + depends on ARM_AMBA && MMC + help + This selects the ARM(R) AMBA(R) PrimeCell Multimedia Card + Interface (PL180 and PL181) support. If you have an ARM(R) + platform with a Multimedia Card slot, say Y or M here. + + If unsure, say N. + +config MMC_PXA + tristate "Intel PXA255 Multimedia Card Interface support" + depends on ARCH_PXA && MMC + help + This selects the Intel(R) PXA(R) Multimedia card Interface. + If you have a PXA(R) platform with a Multimedia Card slot, + say Y or M here. + + If unsure, say N. + +endmenu diff --git a/drivers/mmc/Makefile b/drivers/mmc/Makefile new file mode 100644 index 000000000..def01111d --- /dev/null +++ b/drivers/mmc/Makefile @@ -0,0 +1,21 @@ +# +# Makefile for the kernel mmc device drivers. +# + +# +# Core +# +obj-$(CONFIG_MMC) += mmc_core.o + +# +# Media drivers +# +obj-$(CONFIG_MMC_BLOCK) += mmc_block.o + +# +# Host drivers +# +obj-$(CONFIG_MMC_ARMMMCI) += mmci.o +obj-$(CONFIG_MMC_PXA) += pxamci.o + +mmc_core-y := mmc.o mmc_queue.o mmc_sysfs.o diff --git a/drivers/mmc/mmc.c b/drivers/mmc/mmc.c new file mode 100644 index 000000000..5c3d84894 --- /dev/null +++ b/drivers/mmc/mmc.c @@ -0,0 +1,913 @@ +/* + * linux/drivers/mmc/mmc.c + * + * Copyright (C) 2003-2004 Russell King, All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "mmc.h" + +#ifdef CONFIG_MMC_DEBUG +#define DBG(x...) printk(KERN_DEBUG x) +#else +#define DBG(x...) do { } while (0) +#endif + +#define CMD_RETRIES 3 + +/* + * OCR Bit positions to 10s of Vdd mV. + */ +static const unsigned short mmc_ocr_bit_to_vdd[] = { + 150, 155, 160, 165, 170, 180, 190, 200, + 210, 220, 230, 240, 250, 260, 270, 280, + 290, 300, 310, 320, 330, 340, 350, 360 +}; + +static const unsigned int tran_exp[] = { + 10000, 100000, 1000000, 10000000, + 0, 0, 0, 0 +}; + +static const unsigned char tran_mant[] = { + 0, 10, 12, 13, 15, 20, 25, 30, + 35, 40, 45, 50, 55, 60, 70, 80, +}; + +static const unsigned int tacc_exp[] = { + 1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, +}; + +static const unsigned int tacc_mant[] = { + 0, 10, 12, 13, 15, 20, 25, 30, + 35, 40, 45, 50, 55, 60, 70, 80, +}; + + +/** + * mmc_request_done - finish processing an MMC command + * @host: MMC host which completed command + * @mrq: MMC request which completed + * + * MMC drivers should call this function when they have completed + * their processing of a command. This should be called before the + * data part of the command has completed. + */ +void mmc_request_done(struct mmc_host *host, struct mmc_request *mrq) +{ + struct mmc_command *cmd = mrq->cmd; + int err = mrq->cmd->error; + DBG("MMC: req done (%02x): %d: %08x %08x %08x %08x\n", cmd->opcode, + err, cmd->resp[0], cmd->resp[1], cmd->resp[2], cmd->resp[3]); + + if (err && cmd->retries) { + cmd->retries--; + cmd->error = 0; + host->ops->request(host, mrq); + } else if (mrq->done) { + mrq->done(mrq); + } +} + +EXPORT_SYMBOL(mmc_request_done); + +/** + * mmc_start_request - start a command on a host + * @host: MMC host to start command on + * @mrq: MMC request to start + * + * Queue a command on the specified host. We expect the + * caller to be holding the host lock with interrupts disabled. + */ +void +mmc_start_request(struct mmc_host *host, struct mmc_request *mrq) +{ + DBG("MMC: starting cmd %02x arg %08x flags %08x\n", + mrq->cmd->opcode, mrq->cmd->arg, mrq->cmd->flags); + + WARN_ON(host->card_busy == NULL); + + mrq->cmd->error = 0; + mrq->cmd->mrq = mrq; + if (mrq->data) { + mrq->cmd->data = mrq->data; + mrq->data->error = 0; + mrq->data->mrq = mrq; + if (mrq->stop) { + mrq->data->stop = mrq->stop; + mrq->stop->error = 0; + mrq->stop->mrq = mrq; + } + } + host->ops->request(host, mrq); +} + +EXPORT_SYMBOL(mmc_start_request); + +static void mmc_wait_done(struct mmc_request *mrq) +{ + complete(mrq->done_data); +} + +int mmc_wait_for_req(struct mmc_host *host, struct mmc_request *mrq) +{ + DECLARE_COMPLETION(complete); + + mrq->done_data = &complete; + mrq->done = mmc_wait_done; + + mmc_start_request(host, mrq); + + wait_for_completion(&complete); + + return 0; +} + +EXPORT_SYMBOL(mmc_wait_for_req); + +/** + * mmc_wait_for_cmd - start a command and wait for completion + * @host: MMC host to start command + * @cmd: MMC command to start + * @retries: maximum number of retries + * + * Start a new MMC command for a host, and wait for the command + * to complete. Return any error that occurred while the command + * was executing. Do not attempt to parse the response. + */ +int mmc_wait_for_cmd(struct mmc_host *host, struct mmc_command *cmd, int retries) +{ + struct mmc_request mrq; + + BUG_ON(host->card_busy == NULL); + + memset(&mrq, 0, sizeof(struct mmc_request)); + + memset(cmd->resp, 0, sizeof(cmd->resp)); + cmd->retries = retries; + + mrq.cmd = cmd; + cmd->data = NULL; + + mmc_wait_for_req(host, &mrq); + + return cmd->error; +} + +EXPORT_SYMBOL(mmc_wait_for_cmd); + + + +/** + * __mmc_claim_host - exclusively claim a host + * @host: mmc host to claim + * @card: mmc card to claim host for + * + * Claim a host for a set of operations. If a valid card + * is passed and this wasn't the last card selected, select + * the card before returning. + * + * Note: you should use mmc_card_claim_host or mmc_claim_host. + */ +int __mmc_claim_host(struct mmc_host *host, struct mmc_card *card) +{ + DECLARE_WAITQUEUE(wait, current); + unsigned long flags; + int err = 0; + + add_wait_queue(&host->wq, &wait); + spin_lock_irqsave(&host->lock, flags); + while (1) { + set_current_state(TASK_UNINTERRUPTIBLE); + if (host->card_busy == NULL) + break; + spin_unlock_irqrestore(&host->lock, flags); + schedule(); + spin_lock_irqsave(&host->lock, flags); + } + set_current_state(TASK_RUNNING); + host->card_busy = card; + spin_unlock_irqrestore(&host->lock, flags); + remove_wait_queue(&host->wq, &wait); + + if (card != (void *)-1 && host->card_selected != card) { + struct mmc_command cmd; + + host->card_selected = card; + + cmd.opcode = MMC_SELECT_CARD; + cmd.arg = card->rca << 16; + cmd.flags = MMC_RSP_R1; + + err = mmc_wait_for_cmd(host, &cmd, CMD_RETRIES); + } + + return err; +} + +EXPORT_SYMBOL(__mmc_claim_host); + +/** + * mmc_release_host - release a host + * @host: mmc host to release + * + * Release a MMC host, allowing others to claim the host + * for their operations. + */ +void mmc_release_host(struct mmc_host *host) +{ + unsigned long flags; + + BUG_ON(host->card_busy == NULL); + + spin_lock_irqsave(&host->lock, flags); + host->card_busy = NULL; + spin_unlock_irqrestore(&host->lock, flags); + + wake_up(&host->wq); +} + +EXPORT_SYMBOL(mmc_release_host); + +/* + * Ensure that no card is selected. + */ +static void mmc_deselect_cards(struct mmc_host *host) +{ + struct mmc_command cmd; + + if (host->card_selected) { + host->card_selected = NULL; + + cmd.opcode = MMC_SELECT_CARD; + cmd.arg = 0; + cmd.flags = MMC_RSP_NONE; + + mmc_wait_for_cmd(host, &cmd, 0); + } +} + + +static inline void mmc_delay(unsigned int ms) +{ + if (ms < HZ / 1000) { + yield(); + mdelay(ms); + } else { + msleep_interruptible (ms); + } +} + +/* + * Mask off any voltages we don't support and select + * the lowest voltage + */ +static u32 mmc_select_voltage(struct mmc_host *host, u32 ocr) +{ + int bit; + + ocr &= host->ocr_avail; + + bit = ffs(ocr); + if (bit) { + bit -= 1; + + ocr = 3 << bit; + + host->ios.vdd = bit; + host->ops->set_ios(host, &host->ios); + } else { + ocr = 0; + } + + return ocr; +} + +#define UNSTUFF_BITS(resp,start,size) \ + ({ \ + const u32 __mask = (1 << (size)) - 1; \ + const int __off = 3 - ((start) / 32); \ + const int __shft = (start) & 31; \ + u32 __res; \ + \ + __res = resp[__off] >> __shft; \ + if ((size) + __shft >= 32) \ + __res |= resp[__off-1] << (32 - __shft); \ + __res & __mask; \ + }) + +/* + * Given the decoded CSD structure, decode the raw CID to our CID structure. + */ +static void mmc_decode_cid(struct mmc_card *card) +{ + u32 *resp = card->raw_cid; + + memset(&card->cid, 0, sizeof(struct mmc_cid)); + + /* + * The selection of the format here is guesswork based upon + * information people have sent to date. + */ + switch (card->csd.mmca_vsn) { + case 0: /* MMC v1.? */ + case 1: /* MMC v1.4 */ + card->cid.manfid = UNSTUFF_BITS(resp, 104, 24); + card->cid.prod_name[0] = UNSTUFF_BITS(resp, 96, 8); + card->cid.prod_name[1] = UNSTUFF_BITS(resp, 88, 8); + card->cid.prod_name[2] = UNSTUFF_BITS(resp, 80, 8); + card->cid.prod_name[3] = UNSTUFF_BITS(resp, 72, 8); + card->cid.prod_name[4] = UNSTUFF_BITS(resp, 64, 8); + card->cid.prod_name[5] = UNSTUFF_BITS(resp, 56, 8); + card->cid.prod_name[6] = UNSTUFF_BITS(resp, 48, 8); + card->cid.hwrev = UNSTUFF_BITS(resp, 44, 4); + card->cid.fwrev = UNSTUFF_BITS(resp, 40, 4); + card->cid.serial = UNSTUFF_BITS(resp, 16, 24); + card->cid.month = UNSTUFF_BITS(resp, 12, 4); + card->cid.year = UNSTUFF_BITS(resp, 8, 4) + 1997; + break; + + case 2: /* MMC v2.x ? */ + case 3: /* MMC v3.x ? */ + card->cid.manfid = UNSTUFF_BITS(resp, 120, 8); + card->cid.oemid = UNSTUFF_BITS(resp, 104, 16); + card->cid.prod_name[0] = UNSTUFF_BITS(resp, 96, 8); + card->cid.prod_name[1] = UNSTUFF_BITS(resp, 88, 8); + card->cid.prod_name[2] = UNSTUFF_BITS(resp, 80, 8); + card->cid.prod_name[3] = UNSTUFF_BITS(resp, 72, 8); + card->cid.prod_name[4] = UNSTUFF_BITS(resp, 64, 8); + card->cid.prod_name[5] = UNSTUFF_BITS(resp, 56, 8); + card->cid.serial = UNSTUFF_BITS(resp, 16, 32); + card->cid.month = UNSTUFF_BITS(resp, 12, 4); + card->cid.year = UNSTUFF_BITS(resp, 8, 4) + 1997; + break; + + default: + printk("%s: card has unknown MMCA version %d\n", + card->host->host_name, card->csd.mmca_vsn); + mmc_card_set_bad(card); + break; + } +} + +/* + * Given a 128-bit response, decode to our card CSD structure. + */ +static void mmc_decode_csd(struct mmc_card *card) +{ + struct mmc_csd *csd = &card->csd; + unsigned int e, m, csd_struct; + u32 *resp = card->raw_csd; + + /* + * We only understand CSD structure v1.1 and v2. + * v2 has extra information in bits 15, 11 and 10. + */ + csd_struct = UNSTUFF_BITS(resp, 126, 2); + if (csd_struct != 1 && csd_struct != 2) { + printk("%s: unrecognised CSD structure version %d\n", + card->host->host_name, csd_struct); + mmc_card_set_bad(card); + return; + } + + csd->mmca_vsn = UNSTUFF_BITS(resp, 122, 4); + m = UNSTUFF_BITS(resp, 115, 4); + e = UNSTUFF_BITS(resp, 112, 3); + csd->tacc_ns = (tacc_exp[e] * tacc_mant[m] + 9) / 10; + csd->tacc_clks = UNSTUFF_BITS(resp, 104, 8) * 100; + + m = UNSTUFF_BITS(resp, 99, 4); + e = UNSTUFF_BITS(resp, 96, 3); + csd->max_dtr = tran_exp[e] * tran_mant[m]; + csd->cmdclass = UNSTUFF_BITS(resp, 84, 12); + + e = UNSTUFF_BITS(resp, 47, 3); + m = UNSTUFF_BITS(resp, 62, 12); + csd->capacity = (1 + m) << (e + 2); + + csd->read_blkbits = UNSTUFF_BITS(resp, 80, 4); +} + +/* + * Locate a MMC card on this MMC host given a raw CID. + */ +static struct mmc_card *mmc_find_card(struct mmc_host *host, u32 *raw_cid) +{ + struct mmc_card *card; + + list_for_each_entry(card, &host->cards, node) { + if (memcmp(card->raw_cid, raw_cid, sizeof(card->raw_cid)) == 0) + return card; + } + return NULL; +} + +/* + * Allocate a new MMC card, and assign a unique RCA. + */ +static struct mmc_card * +mmc_alloc_card(struct mmc_host *host, u32 *raw_cid, unsigned int *frca) +{ + struct mmc_card *card, *c; + unsigned int rca = *frca; + + card = kmalloc(sizeof(struct mmc_card), GFP_KERNEL); + if (!card) + return ERR_PTR(-ENOMEM); + + mmc_init_card(card, host); + memcpy(card->raw_cid, raw_cid, sizeof(card->raw_cid)); + + again: + list_for_each_entry(c, &host->cards, node) + if (c->rca == rca) { + rca++; + goto again; + } + + card->rca = rca; + + *frca = rca; + + return card; +} + +/* + * Tell attached cards to go to IDLE state + */ +static void mmc_idle_cards(struct mmc_host *host) +{ + struct mmc_command cmd; + + cmd.opcode = MMC_GO_IDLE_STATE; + cmd.arg = 0; + cmd.flags = MMC_RSP_NONE; + + mmc_wait_for_cmd(host, &cmd, 0); + + mmc_delay(1); +} + +/* + * Apply power to the MMC stack. + */ +static void mmc_power_up(struct mmc_host *host) +{ + int bit = fls(host->ocr_avail) - 1; + + host->ios.vdd = bit; + host->ios.bus_mode = MMC_BUSMODE_OPENDRAIN; + host->ios.power_mode = MMC_POWER_UP; + host->ops->set_ios(host, &host->ios); + + mmc_delay(1); + + host->ios.clock = host->f_min; + host->ios.power_mode = MMC_POWER_ON; + host->ops->set_ios(host, &host->ios); + + mmc_delay(2); +} + +static void mmc_power_off(struct mmc_host *host) +{ + host->ios.clock = 0; + host->ios.vdd = 0; + host->ios.bus_mode = MMC_BUSMODE_OPENDRAIN; + host->ios.power_mode = MMC_POWER_OFF; + host->ops->set_ios(host, &host->ios); +} + +static int mmc_send_op_cond(struct mmc_host *host, u32 ocr, u32 *rocr) +{ + struct mmc_command cmd; + int i, err = 0; + + cmd.opcode = MMC_SEND_OP_COND; + cmd.arg = ocr; + cmd.flags = MMC_RSP_R3; + + for (i = 100; i; i--) { + err = mmc_wait_for_cmd(host, &cmd, 0); + if (err != MMC_ERR_NONE) + break; + + if (cmd.resp[0] & MMC_CARD_BUSY || ocr == 0) + break; + + err = MMC_ERR_TIMEOUT; + + mmc_delay(10); + } + + if (rocr) + *rocr = cmd.resp[0]; + + return err; +} + +/* + * Discover cards by requesting their CID. If this command + * times out, it is not an error; there are no further cards + * to be discovered. Add new cards to the list. + * + * Create a mmc_card entry for each discovered card, assigning + * it an RCA, and save the raw CID for decoding later. + */ +static void mmc_discover_cards(struct mmc_host *host) +{ + struct mmc_card *card; + unsigned int first_rca = 1, err; + + while (1) { + struct mmc_command cmd; + + cmd.opcode = MMC_ALL_SEND_CID; + cmd.arg = 0; + cmd.flags = MMC_RSP_R2; + + err = mmc_wait_for_cmd(host, &cmd, CMD_RETRIES); + if (err == MMC_ERR_TIMEOUT) { + err = MMC_ERR_NONE; + break; + } + if (err != MMC_ERR_NONE) { + printk(KERN_ERR "%s: error requesting CID: %d\n", + host->host_name, err); + break; + } + + card = mmc_find_card(host, cmd.resp); + if (!card) { + card = mmc_alloc_card(host, cmd.resp, &first_rca); + if (IS_ERR(card)) { + err = PTR_ERR(card); + break; + } + list_add(&card->node, &host->cards); + } + + card->state &= ~MMC_STATE_DEAD; + + cmd.opcode = MMC_SET_RELATIVE_ADDR; + cmd.arg = card->rca << 16; + cmd.flags = MMC_RSP_R1; + + err = mmc_wait_for_cmd(host, &cmd, CMD_RETRIES); + if (err != MMC_ERR_NONE) + mmc_card_set_dead(card); + } +} + +static void mmc_read_csds(struct mmc_host *host) +{ + struct mmc_card *card; + + list_for_each_entry(card, &host->cards, node) { + struct mmc_command cmd; + int err; + + if (card->state & (MMC_STATE_DEAD|MMC_STATE_PRESENT)) + continue; + + cmd.opcode = MMC_SEND_CSD; + cmd.arg = card->rca << 16; + cmd.flags = MMC_RSP_R2; + + err = mmc_wait_for_cmd(host, &cmd, CMD_RETRIES); + if (err != MMC_ERR_NONE) { + mmc_card_set_dead(card); + continue; + } + + memcpy(card->raw_csd, cmd.resp, sizeof(card->raw_csd)); + + mmc_decode_csd(card); + mmc_decode_cid(card); + } +} + +static unsigned int mmc_calculate_clock(struct mmc_host *host) +{ + struct mmc_card *card; + unsigned int max_dtr = host->f_max; + + list_for_each_entry(card, &host->cards, node) + if (!mmc_card_dead(card) && max_dtr > card->csd.max_dtr) + max_dtr = card->csd.max_dtr; + + DBG("MMC: selected %d.%03dMHz transfer rate\n", + max_dtr / 1000000, (max_dtr / 1000) % 1000); + + return max_dtr; +} + +/* + * Check whether cards we already know about are still present. + * We do this by requesting status, and checking whether a card + * responds. + * + * A request for status does not cause a state change in data + * transfer mode. + */ +static void mmc_check_cards(struct mmc_host *host) +{ + struct list_head *l, *n; + + mmc_deselect_cards(host); + + list_for_each_safe(l, n, &host->cards) { + struct mmc_card *card = mmc_list_to_card(l); + struct mmc_command cmd; + int err; + + cmd.opcode = MMC_SEND_STATUS; + cmd.arg = card->rca << 16; + cmd.flags = MMC_RSP_R1; + + err = mmc_wait_for_cmd(host, &cmd, CMD_RETRIES); + if (err == MMC_ERR_NONE) + continue; + + mmc_card_set_dead(card); + } +} + +static void mmc_setup(struct mmc_host *host) +{ + if (host->ios.power_mode != MMC_POWER_ON) { + int err; + u32 ocr; + + mmc_power_up(host); + mmc_idle_cards(host); + + err = mmc_send_op_cond(host, 0, &ocr); + if (err != MMC_ERR_NONE) + return; + + host->ocr = mmc_select_voltage(host, ocr); + + /* + * Since we're changing the OCR value, we seem to + * need to tell some cards to go back to the idle + * state. We wait 1ms to give cards time to + * respond. + */ + if (host->ocr) + mmc_idle_cards(host); + } else { + host->ios.bus_mode = MMC_BUSMODE_OPENDRAIN; + host->ios.clock = host->f_min; + host->ops->set_ios(host, &host->ios); + + /* + * We should remember the OCR mask from the existing + * cards, and detect the new cards OCR mask, combine + * the two and re-select the VDD. However, if we do + * change VDD, we should do an idle, and then do a + * full re-initialisation. We would need to notify + * drivers so that they can re-setup the cards as + * well, while keeping their queues at bay. + * + * For the moment, we take the easy way out - if the + * new cards don't like our currently selected VDD, + * they drop off the bus. + */ + } + + if (host->ocr == 0) + return; + + /* + * Send the selected OCR multiple times... until the cards + * all get the idea that they should be ready for CMD2. + * (My SanDisk card seems to need this.) + */ + mmc_send_op_cond(host, host->ocr, NULL); + + mmc_discover_cards(host); + + /* + * Ok, now switch to push-pull mode. + */ + host->ios.bus_mode = MMC_BUSMODE_PUSHPULL; + host->ops->set_ios(host, &host->ios); + + mmc_read_csds(host); +} + + +/** + * mmc_detect_change - process change of state on a MMC socket + * @host: host which changed state. + * + * All we know is that card(s) have been inserted or removed + * from the socket(s). We don't know which socket or cards. + */ +void mmc_detect_change(struct mmc_host *host) +{ + schedule_work(&host->detect); +} + +EXPORT_SYMBOL(mmc_detect_change); + + +static void mmc_rescan(void *data) +{ + struct mmc_host *host = data; + struct list_head *l, *n; + + mmc_claim_host(host); + + if (host->ios.power_mode == MMC_POWER_ON) + mmc_check_cards(host); + + mmc_setup(host); + + if (!list_empty(&host->cards)) { + /* + * (Re-)calculate the fastest clock rate which the + * attached cards and the host support. + */ + host->ios.clock = mmc_calculate_clock(host); + host->ops->set_ios(host, &host->ios); + } + + mmc_release_host(host); + + list_for_each_safe(l, n, &host->cards) { + struct mmc_card *card = mmc_list_to_card(l); + + /* + * If this is a new and good card, register it. + */ + if (!mmc_card_present(card) && !mmc_card_dead(card)) { + if (mmc_register_card(card)) + mmc_card_set_dead(card); + else + mmc_card_set_present(card); + } + + /* + * If this card is dead, destroy it. + */ + if (mmc_card_dead(card)) { + list_del(&card->node); + mmc_remove_card(card); + } + } + + /* + * If we discover that there are no cards on the + * bus, turn off the clock and power down. + */ + if (list_empty(&host->cards)) + mmc_power_off(host); +} + + +/** + * mmc_alloc_host - initialise the per-host structure. + * @extra: sizeof private data structure + * @dev: pointer to host device model structure + * + * Initialise the per-host structure. + */ +struct mmc_host *mmc_alloc_host(int extra, struct device *dev) +{ + struct mmc_host *host; + + host = kmalloc(sizeof(struct mmc_host) + extra, GFP_KERNEL); + if (host) { + memset(host, 0, sizeof(struct mmc_host) + extra); + + spin_lock_init(&host->lock); + init_waitqueue_head(&host->wq); + INIT_LIST_HEAD(&host->cards); + INIT_WORK(&host->detect, mmc_rescan, host); + + host->dev = dev; + + /* + * By default, hosts do not support SGIO or large requests. + * They have to set these according to their abilities. + */ + host->max_hw_segs = 1; + host->max_phys_segs = 1; + host->max_sectors = 1 << (PAGE_CACHE_SHIFT - 9); + host->max_seg_size = PAGE_CACHE_SIZE; + } + + return host; +} + +EXPORT_SYMBOL(mmc_alloc_host); + +/** + * mmc_add_host - initialise host hardware + * @host: mmc host + */ +int mmc_add_host(struct mmc_host *host) +{ + static unsigned int host_num; + + snprintf(host->host_name, sizeof(host->host_name), + "mmc%d", host_num++); + + mmc_power_off(host); + mmc_detect_change(host); + + return 0; +} + +EXPORT_SYMBOL(mmc_add_host); + +/** + * mmc_remove_host - remove host hardware + * @host: mmc host + * + * Unregister and remove all cards associated with this host, + * and power down the MMC bus. + */ +void mmc_remove_host(struct mmc_host *host) +{ + struct list_head *l, *n; + + list_for_each_safe(l, n, &host->cards) { + struct mmc_card *card = mmc_list_to_card(l); + + mmc_remove_card(card); + } + + mmc_power_off(host); +} + +EXPORT_SYMBOL(mmc_remove_host); + +/** + * mmc_free_host - free the host structure + * @host: mmc host + * + * Free the host once all references to it have been dropped. + */ +void mmc_free_host(struct mmc_host *host) +{ + flush_scheduled_work(); + kfree(host); +} + +EXPORT_SYMBOL(mmc_free_host); + +#ifdef CONFIG_PM + +/** + * mmc_suspend_host - suspend a host + * @host: mmc host + * @state: suspend mode (PM_SUSPEND_xxx) + */ +int mmc_suspend_host(struct mmc_host *host, u32 state) +{ + mmc_claim_host(host); + mmc_deselect_cards(host); + mmc_power_off(host); + mmc_release_host(host); + + return 0; +} + +EXPORT_SYMBOL(mmc_suspend_host); + +/** + * mmc_resume_host - resume a previously suspended host + * @host: mmc host + */ +int mmc_resume_host(struct mmc_host *host) +{ + mmc_detect_change(host); + + return 0; +} + +EXPORT_SYMBOL(mmc_resume_host); + +#endif + +MODULE_LICENSE("GPL"); diff --git a/drivers/mmc/mmc.h b/drivers/mmc/mmc.h new file mode 100644 index 000000000..b498dffe0 --- /dev/null +++ b/drivers/mmc/mmc.h @@ -0,0 +1,16 @@ +/* + * linux/drivers/mmc/mmc.h + * + * Copyright (C) 2003 Russell King, 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 _MMC_H +#define _MMC_H +/* core-internal functions */ +void mmc_init_card(struct mmc_card *card, struct mmc_host *host); +int mmc_register_card(struct mmc_card *card); +void mmc_remove_card(struct mmc_card *card); +#endif diff --git a/drivers/mmc/mmc_block.c b/drivers/mmc/mmc_block.c new file mode 100644 index 000000000..f76673a47 --- /dev/null +++ b/drivers/mmc/mmc_block.c @@ -0,0 +1,495 @@ +/* + * Block driver for media (i.e., flash cards) + * + * Copyright 2002 Hewlett-Packard Company + * + * Use consistent with the GNU GPL is permitted, + * provided that this copyright notice is + * preserved in its entirety in all copies and derived works. + * + * HEWLETT-PACKARD COMPANY MAKES NO WARRANTIES, EXPRESSED OR IMPLIED, + * AS TO THE USEFULNESS OR CORRECTNESS OF THIS CODE OR ITS + * FITNESS FOR ANY PARTICULAR PURPOSE. + * + * Many thanks to Alessandro Rubini and Jonathan Corbet! + * + * Author: Andrew Christian + * 28 May 2002 + */ +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include + +#include "mmc_queue.h" + +/* + * max 8 partitions per card + */ +#define MMC_SHIFT 3 + +static int major; + +/* + * There is one mmc_blk_data per slot. + */ +struct mmc_blk_data { + spinlock_t lock; + struct gendisk *disk; + struct mmc_queue queue; + + unsigned int usage; + unsigned int block_bits; +}; + +static DECLARE_MUTEX(open_lock); + +static struct mmc_blk_data *mmc_blk_get(struct gendisk *disk) +{ + struct mmc_blk_data *md; + + down(&open_lock); + md = disk->private_data; + if (md && md->usage == 0) + md = NULL; + if (md) + md->usage++; + up(&open_lock); + + return md; +} + +static void mmc_blk_put(struct mmc_blk_data *md) +{ + down(&open_lock); + md->usage--; + if (md->usage == 0) { + put_disk(md->disk); + mmc_cleanup_queue(&md->queue); + kfree(md); + } + up(&open_lock); +} + +static int mmc_blk_open(struct inode *inode, struct file *filp) +{ + struct mmc_blk_data *md; + int ret = -ENXIO; + + md = mmc_blk_get(inode->i_bdev->bd_disk); + if (md) { + if (md->usage == 2) + check_disk_change(inode->i_bdev); + ret = 0; + } + + return ret; +} + +static int mmc_blk_release(struct inode *inode, struct file *filp) +{ + struct mmc_blk_data *md = inode->i_bdev->bd_disk->private_data; + + mmc_blk_put(md); + return 0; +} + +static int +mmc_blk_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg) +{ + struct block_device *bdev = inode->i_bdev; + + if (cmd == HDIO_GETGEO) { + struct hd_geometry geo; + + memset(&geo, 0, sizeof(struct hd_geometry)); + + geo.cylinders = get_capacity(bdev->bd_disk) / (4 * 16); + geo.heads = 4; + geo.sectors = 16; + geo.start = get_start_sect(bdev); + + return copy_to_user((void __user *)arg, &geo, sizeof(geo)) + ? -EFAULT : 0; + } + + return -ENOTTY; +} + +static struct block_device_operations mmc_bdops = { + .open = mmc_blk_open, + .release = mmc_blk_release, + .ioctl = mmc_blk_ioctl, + .owner = THIS_MODULE, +}; + +struct mmc_blk_request { + struct mmc_request mrq; + struct mmc_command cmd; + struct mmc_command stop; + struct mmc_data data; +}; + +static int mmc_blk_prep_rq(struct mmc_queue *mq, struct request *req) +{ + struct mmc_blk_data *md = mq->data; + int stat = BLKPREP_OK; + + /* + * If we have no device, we haven't finished initialising. + */ + if (!md || !mq->card) { + printk(KERN_ERR "%s: killing request - no device/host\n", + req->rq_disk->disk_name); + stat = BLKPREP_KILL; + } + + return stat; +} + +static int mmc_blk_issue_rq(struct mmc_queue *mq, struct request *req) +{ + struct mmc_blk_data *md = mq->data; + struct mmc_card *card = md->queue.card; + int ret; + + if (mmc_card_claim_host(card)) + goto cmd_err; + + do { + struct mmc_blk_request brq; + struct mmc_command cmd; + + memset(&brq, 0, sizeof(struct mmc_blk_request)); + brq.mrq.cmd = &brq.cmd; + brq.mrq.data = &brq.data; + + brq.cmd.arg = req->sector << 9; + brq.cmd.flags = MMC_RSP_R1; + brq.data.req = req; + brq.data.timeout_ns = card->csd.tacc_ns * 10; + brq.data.timeout_clks = card->csd.tacc_clks * 10; + brq.data.blksz_bits = md->block_bits; + brq.data.blocks = req->nr_sectors >> (md->block_bits - 9); + brq.stop.opcode = MMC_STOP_TRANSMISSION; + brq.stop.arg = 0; + brq.stop.flags = MMC_RSP_R1B; + + if (rq_data_dir(req) == READ) { + brq.cmd.opcode = brq.data.blocks > 1 ? MMC_READ_MULTIPLE_BLOCK : MMC_READ_SINGLE_BLOCK; + brq.data.flags |= MMC_DATA_READ; + } else { + brq.cmd.opcode = MMC_WRITE_BLOCK; + brq.cmd.flags = MMC_RSP_R1B; + brq.data.flags |= MMC_DATA_WRITE; + brq.data.blocks = 1; + } + brq.mrq.stop = brq.data.blocks > 1 ? &brq.stop : NULL; + + mmc_wait_for_req(card->host, &brq.mrq); + if (brq.cmd.error) { + printk(KERN_ERR "%s: error %d sending read/write command\n", + req->rq_disk->disk_name, brq.cmd.error); + goto cmd_err; + } + + if (brq.data.error) { + printk(KERN_ERR "%s: error %d transferring data\n", + req->rq_disk->disk_name, brq.data.error); + goto cmd_err; + } + + if (brq.stop.error) { + printk(KERN_ERR "%s: error %d sending stop command\n", + req->rq_disk->disk_name, brq.stop.error); + goto cmd_err; + } + + do { + int err; + + cmd.opcode = MMC_SEND_STATUS; + cmd.arg = card->rca << 16; + cmd.flags = MMC_RSP_R1; + err = mmc_wait_for_cmd(card->host, &cmd, 5); + if (err) { + printk(KERN_ERR "%s: error %d requesting status\n", + req->rq_disk->disk_name, err); + goto cmd_err; + } + } while (!(cmd.resp[0] & R1_READY_FOR_DATA)); + +#if 0 + if (cmd.resp[0] & ~0x00000900) + printk(KERN_ERR "%s: status = %08x\n", + req->rq_disk->disk_name, cmd.resp[0]); + if (mmc_decode_status(cmd.resp)) + goto cmd_err; +#endif + + /* + * A block was successfully transferred. + */ + spin_lock_irq(&md->lock); + ret = end_that_request_chunk(req, 1, brq.data.bytes_xfered); + if (!ret) { + /* + * The whole request completed successfully. + */ + add_disk_randomness(req->rq_disk); + blkdev_dequeue_request(req); + end_that_request_last(req); + } + spin_unlock_irq(&md->lock); + } while (ret); + + mmc_card_release_host(card); + + return 1; + + cmd_err: + mmc_card_release_host(card); + + /* + * This is a little draconian, but until we get proper + * error handling sorted out here, its the best we can + * do - especially as some hosts have no idea how much + * data was transferred before the error occurred. + */ + spin_lock_irq(&md->lock); + do { + ret = end_that_request_chunk(req, 0, + req->current_nr_sectors << 9); + } while (ret); + + add_disk_randomness(req->rq_disk); + blkdev_dequeue_request(req); + end_that_request_last(req); + spin_unlock_irq(&md->lock); + + return 0; +} + +#define MMC_NUM_MINORS (256 >> MMC_SHIFT) + +static unsigned long dev_use[MMC_NUM_MINORS/(8*sizeof(unsigned long))]; + +static struct mmc_blk_data *mmc_blk_alloc(struct mmc_card *card) +{ + struct mmc_blk_data *md; + int devidx, ret; + + devidx = find_first_zero_bit(dev_use, MMC_NUM_MINORS); + if (devidx >= MMC_NUM_MINORS) + return ERR_PTR(-ENOSPC); + __set_bit(devidx, dev_use); + + md = kmalloc(sizeof(struct mmc_blk_data), GFP_KERNEL); + if (md) { + memset(md, 0, sizeof(struct mmc_blk_data)); + + md->disk = alloc_disk(1 << MMC_SHIFT); + if (md->disk == NULL) { + kfree(md); + md = ERR_PTR(-ENOMEM); + goto out; + } + + spin_lock_init(&md->lock); + md->usage = 1; + + ret = mmc_init_queue(&md->queue, card, &md->lock); + if (ret) { + put_disk(md->disk); + kfree(md); + md = ERR_PTR(ret); + goto out; + } + md->queue.prep_fn = mmc_blk_prep_rq; + md->queue.issue_fn = mmc_blk_issue_rq; + md->queue.data = md; + + md->disk->major = major; + md->disk->first_minor = devidx << MMC_SHIFT; + md->disk->fops = &mmc_bdops; + md->disk->private_data = md; + md->disk->queue = md->queue.queue; + md->disk->driverfs_dev = &card->dev; + + sprintf(md->disk->disk_name, "mmcblk%d", devidx); + sprintf(md->disk->devfs_name, "mmc/blk%d", devidx); + + md->block_bits = card->csd.read_blkbits; + + blk_queue_hardsect_size(md->queue.queue, 1 << md->block_bits); + set_capacity(md->disk, card->csd.capacity); + } + out: + return md; +} + +static int +mmc_blk_set_blksize(struct mmc_blk_data *md, struct mmc_card *card) +{ + struct mmc_command cmd; + int err; + + mmc_card_claim_host(card); + cmd.opcode = MMC_SET_BLOCKLEN; + cmd.arg = 1 << card->csd.read_blkbits; + cmd.flags = MMC_RSP_R1; + err = mmc_wait_for_cmd(card->host, &cmd, 5); + mmc_card_release_host(card); + + if (err) { + printk(KERN_ERR "%s: unable to set block size to %d: %d\n", + md->disk->disk_name, cmd.arg, err); + return -EINVAL; + } + + return 0; +} + +static int mmc_blk_probe(struct mmc_card *card) +{ + struct mmc_blk_data *md; + int err; + + if (card->csd.cmdclass & ~0x1ff) + return -ENODEV; + + if (card->csd.read_blkbits < 9) { + printk(KERN_WARNING "%s: read blocksize too small (%u)\n", + mmc_card_id(card), 1 << card->csd.read_blkbits); + return -ENODEV; + } + + md = mmc_blk_alloc(card); + if (IS_ERR(md)) + return PTR_ERR(md); + + err = mmc_blk_set_blksize(md, card); + if (err) + goto out; + + printk(KERN_INFO "%s: %s %s %dKiB\n", + md->disk->disk_name, mmc_card_id(card), mmc_card_name(card), + (card->csd.capacity << card->csd.read_blkbits) / 1024); + + mmc_set_drvdata(card, md); + add_disk(md->disk); + return 0; + + out: + mmc_blk_put(md); + + return err; +} + +static void mmc_blk_remove(struct mmc_card *card) +{ + struct mmc_blk_data *md = mmc_get_drvdata(card); + + if (md) { + int devidx; + + del_gendisk(md->disk); + + /* + * I think this is needed. + */ + md->disk->queue = NULL; + + devidx = md->disk->first_minor >> MMC_SHIFT; + __clear_bit(devidx, dev_use); + + mmc_blk_put(md); + } + mmc_set_drvdata(card, NULL); +} + +#ifdef CONFIG_PM +static int mmc_blk_suspend(struct mmc_card *card, u32 state) +{ + struct mmc_blk_data *md = mmc_get_drvdata(card); + + if (md) { + mmc_queue_suspend(&md->queue); + } + return 0; +} + +static int mmc_blk_resume(struct mmc_card *card) +{ + struct mmc_blk_data *md = mmc_get_drvdata(card); + + if (md) { + mmc_blk_set_blksize(md, card); + mmc_queue_resume(&md->queue); + } + return 0; +} +#else +#define mmc_blk_suspend NULL +#define mmc_blk_resume NULL +#endif + +static struct mmc_driver mmc_driver = { + .drv = { + .name = "mmcblk", + }, + .probe = mmc_blk_probe, + .remove = mmc_blk_remove, + .suspend = mmc_blk_suspend, + .resume = mmc_blk_resume, +}; + +static int __init mmc_blk_init(void) +{ + int res = -ENOMEM; + + res = register_blkdev(major, "mmc"); + if (res < 0) { + printk(KERN_WARNING "Unable to get major %d for MMC media: %d\n", + major, res); + goto out; + } + if (major == 0) + major = res; + + devfs_mk_dir("mmc"); + return mmc_register_driver(&mmc_driver); + + out: + return res; +} + +static void __exit mmc_blk_exit(void) +{ + mmc_unregister_driver(&mmc_driver); + devfs_remove("mmc"); + unregister_blkdev(major, "mmc"); +} + +module_init(mmc_blk_init); +module_exit(mmc_blk_exit); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Multimedia Card (MMC) block device driver"); + +module_param(major, int, 0444); +MODULE_PARM_DESC(major, "specify the major device number for MMC block driver"); diff --git a/drivers/mmc/mmc_queue.c b/drivers/mmc/mmc_queue.c new file mode 100644 index 000000000..e818b9233 --- /dev/null +++ b/drivers/mmc/mmc_queue.c @@ -0,0 +1,222 @@ +/* + * linux/drivers/mmc/mmc_queue.c + * + * Copyright (C) 2003 Russell King, All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + */ +#include +#include + +#include +#include +#include "mmc_queue.h" + +#define MMC_QUEUE_EXIT (1 << 0) +#define MMC_QUEUE_SUSPENDED (1 << 1) + +/* + * Prepare a MMC request. Essentially, this means passing the + * preparation off to the media driver. The media driver will + * create a mmc_io_request in req->special. + */ +static int mmc_prep_request(struct request_queue *q, struct request *req) +{ + struct mmc_queue *mq = q->queuedata; + int ret = BLKPREP_KILL; + + if (req->flags & REQ_SPECIAL) { + /* + * Special commands already have the command + * blocks already setup in req->special. + */ + BUG_ON(!req->special); + + ret = BLKPREP_OK; + } else if (req->flags & (REQ_CMD | REQ_BLOCK_PC)) { + /* + * Block I/O requests need translating according + * to the protocol. + */ + ret = mq->prep_fn(mq, req); + } else { + /* + * Everything else is invalid. + */ + blk_dump_rq_flags(req, "MMC bad request"); + } + + if (ret == BLKPREP_OK) + req->flags |= REQ_DONTPREP; + + return ret; +} + +static int mmc_queue_thread(void *d) +{ + struct mmc_queue *mq = d; + struct request_queue *q = mq->queue; + DECLARE_WAITQUEUE(wait, current); + + /* + * Set iothread to ensure that we aren't put to sleep by + * the process freezing. We handle suspension ourselves. + */ + current->flags |= PF_MEMALLOC|PF_NOFREEZE; + + daemonize("mmcqd"); + + complete(&mq->thread_complete); + + down(&mq->thread_sem); + add_wait_queue(&mq->thread_wq, &wait); + do { + struct request *req = NULL; + + spin_lock_irq(q->queue_lock); + set_current_state(TASK_INTERRUPTIBLE); + if (!blk_queue_plugged(q)) + mq->req = req = elv_next_request(q); + spin_unlock(q->queue_lock); + + if (!req) { + if (mq->flags & MMC_QUEUE_EXIT) + break; + up(&mq->thread_sem); + schedule(); + down(&mq->thread_sem); + continue; + } + set_current_state(TASK_RUNNING); + + mq->issue_fn(mq, req); + } while (1); + remove_wait_queue(&mq->thread_wq, &wait); + up(&mq->thread_sem); + + complete_and_exit(&mq->thread_complete, 0); + return 0; +} + +/* + * Generic MMC request handler. This is called for any queue on a + * particular host. When the host is not busy, we look for a request + * on any queue on this host, and attempt to issue it. This may + * not be the queue we were asked to process. + */ +static void mmc_request(request_queue_t *q) +{ + struct mmc_queue *mq = q->queuedata; + + if (!mq->req) + wake_up(&mq->thread_wq); +} + +/** + * mmc_init_queue - initialise a queue structure. + * @mq: mmc queue + * @card: mmc card to attach this queue + * @lock: queue lock + * + * Initialise a MMC card request queue. + */ +int mmc_init_queue(struct mmc_queue *mq, struct mmc_card *card, spinlock_t *lock) +{ + struct mmc_host *host = card->host; + u64 limit = BLK_BOUNCE_HIGH; + int ret; + + if (host->dev->dma_mask && *host->dev->dma_mask) + limit = *host->dev->dma_mask; + + mq->card = card; + mq->queue = blk_init_queue(mmc_request, lock); + if (!mq->queue) + return -ENOMEM; + + blk_queue_prep_rq(mq->queue, mmc_prep_request); + blk_queue_bounce_limit(mq->queue, limit); + blk_queue_max_sectors(mq->queue, host->max_sectors); + blk_queue_max_phys_segments(mq->queue, host->max_phys_segs); + blk_queue_max_hw_segments(mq->queue, host->max_hw_segs); + blk_queue_max_segment_size(mq->queue, host->max_seg_size); + + mq->queue->queuedata = mq; + mq->req = NULL; + + init_completion(&mq->thread_complete); + init_waitqueue_head(&mq->thread_wq); + init_MUTEX(&mq->thread_sem); + + ret = kernel_thread(mmc_queue_thread, mq, CLONE_KERNEL); + if (ret < 0) { + blk_cleanup_queue(mq->queue); + } else { + wait_for_completion(&mq->thread_complete); + init_completion(&mq->thread_complete); + ret = 0; + } + + return ret; +} +EXPORT_SYMBOL(mmc_init_queue); + +void mmc_cleanup_queue(struct mmc_queue *mq) +{ + mq->flags |= MMC_QUEUE_EXIT; + wake_up(&mq->thread_wq); + wait_for_completion(&mq->thread_complete); + blk_cleanup_queue(mq->queue); + + mq->card = NULL; +} +EXPORT_SYMBOL(mmc_cleanup_queue); + +/** + * mmc_queue_suspend - suspend a MMC request queue + * @mq: MMC queue to suspend + * + * Stop the block request queue, and wait for our thread to + * complete any outstanding requests. This ensures that we + * won't suspend while a request is being processed. + */ +void mmc_queue_suspend(struct mmc_queue *mq) +{ + request_queue_t *q = mq->queue; + unsigned long flags; + + if (!(mq->flags & MMC_QUEUE_SUSPENDED)) { + mq->flags |= MMC_QUEUE_SUSPENDED; + + spin_lock_irqsave(q->queue_lock, flags); + blk_stop_queue(q); + spin_unlock_irqrestore(q->queue_lock, flags); + + down(&mq->thread_sem); + } +} +EXPORT_SYMBOL(mmc_queue_suspend); + +/** + * mmc_queue_resume - resume a previously suspended MMC request queue + * @mq: MMC queue to resume + */ +void mmc_queue_resume(struct mmc_queue *mq) +{ + request_queue_t *q = mq->queue; + unsigned long flags; + + if (mq->flags & MMC_QUEUE_SUSPENDED) { + mq->flags &= ~MMC_QUEUE_SUSPENDED; + + up(&mq->thread_sem); + + spin_lock_irqsave(q->queue_lock, flags); + blk_start_queue(q); + spin_unlock_irqrestore(q->queue_lock, flags); + } +} +EXPORT_SYMBOL(mmc_queue_resume); diff --git a/drivers/mmc/mmc_queue.h b/drivers/mmc/mmc_queue.h new file mode 100644 index 000000000..6bef90087 --- /dev/null +++ b/drivers/mmc/mmc_queue.h @@ -0,0 +1,32 @@ +#ifndef MMC_QUEUE_H +#define MMC_QUEUE_H + +struct request; +struct task_struct; + +struct mmc_queue { + struct mmc_card *card; + struct completion thread_complete; + wait_queue_head_t thread_wq; + struct semaphore thread_sem; + unsigned int flags; + struct request *req; + int (*prep_fn)(struct mmc_queue *, struct request *); + int (*issue_fn)(struct mmc_queue *, struct request *); + void *data; + struct request_queue *queue; +}; + +struct mmc_io_request { + struct request *rq; + int num; + struct mmc_command selcmd; /* mmc_queue private */ + struct mmc_command cmd[4]; /* max 4 commands */ +}; + +extern int mmc_init_queue(struct mmc_queue *, struct mmc_card *, spinlock_t *); +extern void mmc_cleanup_queue(struct mmc_queue *); +extern void mmc_queue_suspend(struct mmc_queue *); +extern void mmc_queue_resume(struct mmc_queue *); + +#endif diff --git a/drivers/mmc/mmc_sysfs.c b/drivers/mmc/mmc_sysfs.c new file mode 100644 index 000000000..2b7ebdac7 --- /dev/null +++ b/drivers/mmc/mmc_sysfs.c @@ -0,0 +1,241 @@ +/* + * linux/drivers/mmc/mmc_sysfs.c + * + * Copyright (C) 2003 Russell King, 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. + * + * MMC sysfs/driver model support. + */ +#include +#include +#include + +#include +#include + +#include "mmc.h" + +#define dev_to_mmc_card(d) container_of(d, struct mmc_card, dev) +#define to_mmc_driver(d) container_of(d, struct mmc_driver, drv) + +static void mmc_release_card(struct device *dev) +{ + struct mmc_card *card = dev_to_mmc_card(dev); + + kfree(card); +} + +/* + * This currently matches any MMC driver to any MMC card - drivers + * themselves make the decision whether to drive this card in their + * probe method. However, we force "bad" cards to fail. + */ +static int mmc_bus_match(struct device *dev, struct device_driver *drv) +{ + struct mmc_card *card = dev_to_mmc_card(dev); + return !mmc_card_bad(card); +} + +static int +mmc_bus_hotplug(struct device *dev, char **envp, int num_envp, char *buf, + int buf_size) +{ + struct mmc_card *card = dev_to_mmc_card(dev); + char ccc[13]; + int i = 0; + +#define add_env(fmt,val) \ + ({ \ + int len, ret = -ENOMEM; \ + if (i < num_envp) { \ + envp[i++] = buf; \ + len = snprintf(buf, buf_size, fmt, val) + 1; \ + buf_size -= len; \ + buf += len; \ + if (buf_size >= 0) \ + ret = 0; \ + } \ + ret; \ + }) + + for (i = 0; i < 12; i++) + ccc[i] = card->csd.cmdclass & (1 << i) ? '1' : '0'; + ccc[12] = '\0'; + + i = 0; + add_env("MMC_CCC=%s", ccc); + add_env("MMC_MANFID=%06x", card->cid.manfid); + add_env("MMC_NAME=%s", mmc_card_name(card)); + add_env("MMC_OEMID=%04x", card->cid.oemid); + + return 0; +} + +static int mmc_bus_suspend(struct device *dev, u32 state) +{ + struct mmc_driver *drv = to_mmc_driver(dev->driver); + struct mmc_card *card = dev_to_mmc_card(dev); + int ret = 0; + + if (dev->driver && drv->suspend) + ret = drv->suspend(card, state); + return ret; +} + +static int mmc_bus_resume(struct device *dev) +{ + struct mmc_driver *drv = to_mmc_driver(dev->driver); + struct mmc_card *card = dev_to_mmc_card(dev); + int ret = 0; + + if (dev->driver && drv->resume) + ret = drv->resume(card); + return ret; +} + +static struct bus_type mmc_bus_type = { + .name = "mmc", + .match = mmc_bus_match, + .hotplug = mmc_bus_hotplug, + .suspend = mmc_bus_suspend, + .resume = mmc_bus_resume, +}; + + +static int mmc_drv_probe(struct device *dev) +{ + struct mmc_driver *drv = to_mmc_driver(dev->driver); + struct mmc_card *card = dev_to_mmc_card(dev); + + return drv->probe(card); +} + +static int mmc_drv_remove(struct device *dev) +{ + struct mmc_driver *drv = to_mmc_driver(dev->driver); + struct mmc_card *card = dev_to_mmc_card(dev); + + drv->remove(card); + + return 0; +} + + +/** + * mmc_register_driver - register a media driver + * @drv: MMC media driver + */ +int mmc_register_driver(struct mmc_driver *drv) +{ + drv->drv.bus = &mmc_bus_type; + drv->drv.probe = mmc_drv_probe; + drv->drv.remove = mmc_drv_remove; + return driver_register(&drv->drv); +} + +EXPORT_SYMBOL(mmc_register_driver); + +/** + * mmc_unregister_driver - unregister a media driver + * @drv: MMC media driver + */ +void mmc_unregister_driver(struct mmc_driver *drv) +{ + drv->drv.bus = &mmc_bus_type; + driver_unregister(&drv->drv); +} + +EXPORT_SYMBOL(mmc_unregister_driver); + + +#define MMC_ATTR(name, fmt, args...) \ +static ssize_t mmc_dev_show_##name (struct device *dev, char *buf) \ +{ \ + struct mmc_card *card = dev_to_mmc_card(dev); \ + return sprintf(buf, fmt, args); \ +} \ +static DEVICE_ATTR(name, S_IRUGO, mmc_dev_show_##name, NULL) + +MMC_ATTR(cid, "%08x%08x%08x%08x\n", card->raw_cid[0], card->raw_cid[1], + card->raw_cid[2], card->raw_cid[3]); +MMC_ATTR(csd, "%08x%08x%08x%08x\n", card->raw_csd[0], card->raw_csd[1], + card->raw_csd[2], card->raw_csd[3]); +MMC_ATTR(date, "%02d/%04d\n", card->cid.month, card->cid.year); +MMC_ATTR(fwrev, "0x%x\n", card->cid.fwrev); +MMC_ATTR(hwrev, "0x%x\n", card->cid.hwrev); +MMC_ATTR(manfid, "0x%06x\n", card->cid.manfid); +MMC_ATTR(name, "%s\n", card->cid.prod_name); +MMC_ATTR(oemid, "0x%04x\n", card->cid.oemid); +MMC_ATTR(serial, "0x%08x\n", card->cid.serial); + +static struct device_attribute *mmc_dev_attributes[] = { + &dev_attr_cid, + &dev_attr_csd, + &dev_attr_date, + &dev_attr_fwrev, + &dev_attr_hwrev, + &dev_attr_manfid, + &dev_attr_name, + &dev_attr_oemid, + &dev_attr_serial, +}; + +/* + * Internal function. Initialise a MMC card structure. + */ +void mmc_init_card(struct mmc_card *card, struct mmc_host *host) +{ + memset(card, 0, sizeof(struct mmc_card)); + card->host = host; + device_initialize(&card->dev); + card->dev.parent = card->host->dev; + card->dev.bus = &mmc_bus_type; + card->dev.release = mmc_release_card; +} + +/* + * Internal function. Register a new MMC card with the driver model. + */ +int mmc_register_card(struct mmc_card *card) +{ + int ret, i; + + snprintf(card->dev.bus_id, sizeof(card->dev.bus_id), + "%s:%04x", card->host->host_name, card->rca); + + ret = device_add(&card->dev); + if (ret == 0) + for (i = 0; i < ARRAY_SIZE(mmc_dev_attributes); i++) + device_create_file(&card->dev, mmc_dev_attributes[i]); + + return ret; +} + +/* + * Internal function. Unregister a new MMC card with the + * driver model, and (eventually) free it. + */ +void mmc_remove_card(struct mmc_card *card) +{ + if (mmc_card_present(card)) + device_del(&card->dev); + + put_device(&card->dev); +} + + +static int __init mmc_init(void) +{ + return bus_register(&mmc_bus_type); +} + +static void __exit mmc_exit(void) +{ + bus_unregister(&mmc_bus_type); +} + +module_init(mmc_init); +module_exit(mmc_exit); diff --git a/drivers/mmc/mmci.c b/drivers/mmc/mmci.c new file mode 100644 index 000000000..47e1636de --- /dev/null +++ b/drivers/mmc/mmci.c @@ -0,0 +1,679 @@ +/* + * linux/drivers/mmc/mmci.c - ARM PrimeCell MMCI PL180/1 driver + * + * Copyright (C) 2003 Deep Blue Solutions, Ltd, All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "mmci.h" + +#define DRIVER_NAME "mmci-pl18x" + +#ifdef CONFIG_MMC_DEBUG +#define DBG(host,fmt,args...) \ + pr_debug("%s: %s: " fmt, host->mmc->host_name, __func__ , args) +#else +#define DBG(host,fmt,args...) do { } while (0) +#endif + +static unsigned int fmax = 515633; + +static void +mmci_request_end(struct mmci_host *host, struct mmc_request *mrq) +{ + writel(0, host->base + MMCICOMMAND); + + host->mrq = NULL; + host->cmd = NULL; + + if (mrq->data) + mrq->data->bytes_xfered = host->data_xfered; + + /* + * Need to drop the host lock here; mmc_request_done may call + * back into the driver... + */ + spin_unlock(&host->lock); + mmc_request_done(host->mmc, mrq); + spin_lock(&host->lock); +} + +static void mmci_stop_data(struct mmci_host *host) +{ + writel(0, host->base + MMCIDATACTRL); + writel(0, host->base + MMCIMASK1); + host->data = NULL; +} + +static void mmci_start_data(struct mmci_host *host, struct mmc_data *data) +{ + unsigned int datactrl, timeout, irqmask; + void *base; + + DBG(host, "blksz %04x blks %04x flags %08x\n", + 1 << data->blksz_bits, data->blocks, data->flags); + + host->data = data; + host->size = data->blocks << data->blksz_bits; + host->data_xfered = 0; + + mmci_init_sg(host, data); + + timeout = data->timeout_clks + + ((unsigned long long)data->timeout_ns * host->cclk) / + 1000000000ULL; + + base = host->base; + writel(timeout, base + MMCIDATATIMER); + writel(host->size, base + MMCIDATALENGTH); + + datactrl = MCI_DPSM_ENABLE | data->blksz_bits << 4; + if (data->flags & MMC_DATA_READ) { + datactrl |= MCI_DPSM_DIRECTION; + irqmask = MCI_RXFIFOHALFFULLMASK; + } else { + /* + * We don't actually need to include "FIFO empty" here + * since its implicit in "FIFO half empty". + */ + irqmask = MCI_TXFIFOHALFEMPTYMASK; + } + + writel(datactrl, base + MMCIDATACTRL); + writel(readl(base + MMCIMASK0) & ~MCI_DATAENDMASK, base + MMCIMASK0); + writel(irqmask, base + MMCIMASK1); +} + +static void +mmci_start_command(struct mmci_host *host, struct mmc_command *cmd, u32 c) +{ + void *base = host->base; + + DBG(host, "op %02x arg %08x flags %08x\n", + cmd->opcode, cmd->arg, cmd->flags); + + if (readl(base + MMCICOMMAND) & MCI_CPSM_ENABLE) { + writel(0, base + MMCICOMMAND); + udelay(1); + } + + c |= cmd->opcode | MCI_CPSM_ENABLE; + switch (cmd->flags & MMC_RSP_MASK) { + case MMC_RSP_NONE: + default: + break; + case MMC_RSP_LONG: + c |= MCI_CPSM_LONGRSP; + case MMC_RSP_SHORT: + c |= MCI_CPSM_RESPONSE; + break; + } + if (/*interrupt*/0) + c |= MCI_CPSM_INTERRUPT; + + host->cmd = cmd; + + writel(cmd->arg, base + MMCIARGUMENT); + writel(c, base + MMCICOMMAND); +} + +static void +mmci_data_irq(struct mmci_host *host, struct mmc_data *data, + unsigned int status) +{ + if (status & MCI_DATABLOCKEND) { + host->data_xfered += 1 << data->blksz_bits; + } + if (status & (MCI_DATACRCFAIL|MCI_DATATIMEOUT|MCI_TXUNDERRUN|MCI_RXOVERRUN)) { + if (status & MCI_DATACRCFAIL) + data->error = MMC_ERR_BADCRC; + else if (status & MCI_DATATIMEOUT) + data->error = MMC_ERR_TIMEOUT; + else if (status & (MCI_TXUNDERRUN|MCI_RXOVERRUN)) + data->error = MMC_ERR_FIFO; + status |= MCI_DATAEND; + } + if (status & MCI_DATAEND) { + mmci_stop_data(host); + + if (!data->stop) { + mmci_request_end(host, data->mrq); + } else { + mmci_start_command(host, data->stop, 0); + } + } +} + +static void +mmci_cmd_irq(struct mmci_host *host, struct mmc_command *cmd, + unsigned int status) +{ + void *base = host->base; + + host->cmd = NULL; + + cmd->resp[0] = readl(base + MMCIRESPONSE0); + cmd->resp[1] = readl(base + MMCIRESPONSE1); + cmd->resp[2] = readl(base + MMCIRESPONSE2); + cmd->resp[3] = readl(base + MMCIRESPONSE3); + + if (status & MCI_CMDTIMEOUT) { + cmd->error = MMC_ERR_TIMEOUT; + } else if (status & MCI_CMDCRCFAIL && cmd->flags & MMC_RSP_CRC) { + cmd->error = MMC_ERR_BADCRC; + } + + if (!cmd->data || cmd->error != MMC_ERR_NONE) { + mmci_request_end(host, cmd->mrq); + } else if (!(cmd->data->flags & MMC_DATA_READ)) { + mmci_start_data(host, cmd->data); + } +} + +static int mmci_pio_read(struct mmci_host *host, char *buffer, unsigned int remain) +{ + void *base = host->base; + char *ptr = buffer; + u32 status; + + do { + int count = host->size - (readl(base + MMCIFIFOCNT) << 2); + + if (count > remain) + count = remain; + + if (count <= 0) + break; + + readsl(base + MMCIFIFO, ptr, count >> 2); + + ptr += count; + remain -= count; + + if (remain == 0) + break; + + status = readl(base + MMCISTATUS); + } while (status & MCI_RXDATAAVLBL); + + return ptr - buffer; +} + +static int mmci_pio_write(struct mmci_host *host, char *buffer, unsigned int remain, u32 status) +{ + void *base = host->base; + char *ptr = buffer; + + do { + unsigned int count, maxcnt; + + maxcnt = status & MCI_TXFIFOEMPTY ? MCI_FIFOSIZE : MCI_FIFOHALFSIZE; + count = min(remain, maxcnt); + + writesl(base + MMCIFIFO, ptr, count >> 2); + + ptr += count; + remain -= count; + + if (remain == 0) + break; + + status = readl(base + MMCISTATUS); + } while (status & MCI_TXFIFOHALFEMPTY); + + return ptr - buffer; +} + +/* + * PIO data transfer IRQ handler. + */ +static irqreturn_t mmci_pio_irq(int irq, void *dev_id, struct pt_regs *regs) +{ + struct mmci_host *host = dev_id; + void *base = host->base; + u32 status; + + status = readl(base + MMCISTATUS); + + DBG(host, "irq1 %08x\n", status); + + do { + unsigned long flags; + unsigned int remain, len; + char *buffer; + + /* + * For write, we only need to test the half-empty flag + * here - if the FIFO is completely empty, then by + * definition it is more than half empty. + * + * For read, check for data available. + */ + if (!(status & (MCI_TXFIFOHALFEMPTY|MCI_RXDATAAVLBL))) + break; + + /* + * Map the current scatter buffer. + */ + buffer = mmci_kmap_atomic(host, &flags) + host->sg_off; + remain = host->sg_ptr->length - host->sg_off; + + len = 0; + if (status & MCI_RXACTIVE) + len = mmci_pio_read(host, buffer, remain); + if (status & MCI_TXACTIVE) + len = mmci_pio_write(host, buffer, remain, status); + + /* + * Unmap the buffer. + */ + mmci_kunmap_atomic(host, &flags); + + host->sg_off += len; + host->size -= len; + remain -= len; + + if (remain) + break; + + if (!mmci_next_sg(host)) + break; + + status = readl(base + MMCISTATUS); + } while (1); + + /* + * If we're nearing the end of the read, switch to + * "any data available" mode. + */ + if (status & MCI_RXACTIVE && host->size < MCI_FIFOSIZE) + writel(MCI_RXDATAAVLBLMASK, base + MMCIMASK1); + + /* + * If we run out of data, disable the data IRQs; this + * prevents a race where the FIFO becomes empty before + * the chip itself has disabled the data path, and + * stops us racing with our data end IRQ. + */ + if (host->size == 0) { + writel(0, base + MMCIMASK1); + writel(readl(base + MMCIMASK0) | MCI_DATAENDMASK, base + MMCIMASK0); + } + + return IRQ_HANDLED; +} + +/* + * Handle completion of command and data transfers. + */ +static irqreturn_t mmci_irq(int irq, void *dev_id, struct pt_regs *regs) +{ + struct mmci_host *host = dev_id; + u32 status; + int ret = 0; + + spin_lock(&host->lock); + + do { + struct mmc_command *cmd; + struct mmc_data *data; + + status = readl(host->base + MMCISTATUS); + status &= readl(host->base + MMCIMASK0); + writel(status, host->base + MMCICLEAR); + + DBG(host, "irq0 %08x\n", status); + + data = host->data; + if (status & (MCI_DATACRCFAIL|MCI_DATATIMEOUT|MCI_TXUNDERRUN| + MCI_RXOVERRUN|MCI_DATAEND|MCI_DATABLOCKEND) && data) + mmci_data_irq(host, data, status); + + cmd = host->cmd; + if (status & (MCI_CMDCRCFAIL|MCI_CMDTIMEOUT|MCI_CMDSENT|MCI_CMDRESPEND) && cmd) + mmci_cmd_irq(host, cmd, status); + + ret = 1; + } while (status); + + spin_unlock(&host->lock); + + return IRQ_RETVAL(ret); +} + +static void mmci_request(struct mmc_host *mmc, struct mmc_request *mrq) +{ + struct mmci_host *host = mmc_priv(mmc); + + WARN_ON(host->mrq != NULL); + + spin_lock_irq(&host->lock); + + host->mrq = mrq; + + if (mrq->data && mrq->data->flags & MMC_DATA_READ) + mmci_start_data(host, mrq->data); + + mmci_start_command(host, mrq->cmd, 0); + + spin_unlock_irq(&host->lock); +} + +static void mmci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) +{ + struct mmci_host *host = mmc_priv(mmc); + u32 clk = 0, pwr = 0; + + DBG(host, "clock %uHz busmode %u powermode %u Vdd %u\n", + ios->clock, ios->bus_mode, ios->power_mode, ios->vdd); + + if (ios->clock) { + if (ios->clock >= host->mclk) { + clk = MCI_CLK_BYPASS; + host->cclk = host->mclk; + } else { + clk = host->mclk / (2 * ios->clock) - 1; + if (clk > 256) + clk = 255; + host->cclk = host->mclk / (2 * (clk + 1)); + } + clk |= MCI_CLK_ENABLE; + } + + if (host->plat->translate_vdd) + pwr |= host->plat->translate_vdd(mmc_dev(mmc), ios->vdd); + + switch (ios->power_mode) { + case MMC_POWER_OFF: + break; + case MMC_POWER_UP: + pwr |= MCI_PWR_UP; + break; + case MMC_POWER_ON: + pwr |= MCI_PWR_ON; + break; + } + + if (ios->bus_mode == MMC_BUSMODE_OPENDRAIN) + pwr |= MCI_ROD; + + writel(clk, host->base + MMCICLOCK); + + if (host->pwr != pwr) { + host->pwr = pwr; + writel(pwr, host->base + MMCIPOWER); + } +} + +static struct mmc_host_ops mmci_ops = { + .request = mmci_request, + .set_ios = mmci_set_ios, +}; + +static void mmci_check_status(unsigned long data) +{ + struct mmci_host *host = (struct mmci_host *)data; + unsigned int status; + + status = host->plat->status(mmc_dev(host->mmc)); + if (status ^ host->oldstat) + mmc_detect_change(host->mmc); + + host->oldstat = status; + mod_timer(&host->timer, jiffies + HZ); +} + +static int mmci_probe(struct amba_device *dev, void *id) +{ + struct mmc_platform_data *plat = dev->dev.platform_data; + struct mmci_host *host; + struct mmc_host *mmc; + int ret; + + /* must have platform data */ + if (!plat) { + ret = -EINVAL; + goto out; + } + + ret = amba_request_regions(dev, DRIVER_NAME); + if (ret) + goto out; + + mmc = mmc_alloc_host(sizeof(struct mmci_host), &dev->dev); + if (!mmc) { + ret = -ENOMEM; + goto rel_regions; + } + + host = mmc_priv(mmc); + host->clk = clk_get(&dev->dev, "MCLK"); + if (IS_ERR(host->clk)) { + ret = PTR_ERR(host->clk); + host->clk = NULL; + goto host_free; + } + + ret = clk_use(host->clk); + if (ret) + goto clk_free; + + ret = clk_enable(host->clk); + if (ret) + goto clk_unuse; + + host->plat = plat; + host->mclk = clk_get_rate(host->clk); + host->mmc = mmc; + host->base = ioremap(dev->res.start, SZ_4K); + if (!host->base) { + ret = -ENOMEM; + goto clk_disable; + } + + mmc->ops = &mmci_ops; + mmc->f_min = (host->mclk + 511) / 512; + mmc->f_max = min(host->mclk, fmax); + mmc->ocr_avail = plat->ocr_mask; + + /* + * We can do SGIO + */ + mmc->max_hw_segs = 16; + mmc->max_phys_segs = 16; + + /* + * Since we only have a 16-bit data length register, we must + * ensure that we don't exceed 2^16-1 bytes in a single request. + * Choose 64 (512-byte) sectors as the limit. + */ + mmc->max_sectors = 64; + + /* + * Set the maximum segment size. Since we aren't doing DMA + * (yet) we are only limited by the data length register. + */ + mmc->max_seg_size = mmc->max_sectors << 9; + + spin_lock_init(&host->lock); + + writel(0, host->base + MMCIMASK0); + writel(0, host->base + MMCIMASK1); + writel(0xfff, host->base + MMCICLEAR); + + ret = request_irq(dev->irq[0], mmci_irq, SA_SHIRQ, DRIVER_NAME " (cmd)", host); + if (ret) + goto unmap; + + ret = request_irq(dev->irq[1], mmci_pio_irq, SA_SHIRQ, DRIVER_NAME " (pio)", host); + if (ret) + goto irq0_free; + + writel(MCI_IRQENABLE, host->base + MMCIMASK0); + + amba_set_drvdata(dev, mmc); + + mmc_add_host(mmc); + + printk(KERN_INFO "%s: MMCI rev %x cfg %02x at 0x%08lx irq %d,%d\n", + mmc->host_name, amba_rev(dev), amba_config(dev), + dev->res.start, dev->irq[0], dev->irq[1]); + + init_timer(&host->timer); + host->timer.data = (unsigned long)host; + host->timer.function = mmci_check_status; + host->timer.expires = jiffies + HZ; + add_timer(&host->timer); + + return 0; + + irq0_free: + free_irq(dev->irq[0], host); + unmap: + iounmap(host->base); + clk_disable: + clk_disable(host->clk); + clk_unuse: + clk_unuse(host->clk); + clk_free: + clk_put(host->clk); + host_free: + mmc_free_host(mmc); + rel_regions: + amba_release_regions(dev); + out: + return ret; +} + +static int mmci_remove(struct amba_device *dev) +{ + struct mmc_host *mmc = amba_get_drvdata(dev); + + amba_set_drvdata(dev, NULL); + + if (mmc) { + struct mmci_host *host = mmc_priv(mmc); + + del_timer_sync(&host->timer); + + mmc_remove_host(mmc); + + writel(0, host->base + MMCIMASK0); + writel(0, host->base + MMCIMASK1); + + writel(0, host->base + MMCICOMMAND); + writel(0, host->base + MMCIDATACTRL); + + free_irq(dev->irq[0], host); + free_irq(dev->irq[1], host); + + iounmap(host->base); + clk_disable(host->clk); + clk_unuse(host->clk); + clk_put(host->clk); + + mmc_free_host(mmc); + + amba_release_regions(dev); + } + + return 0; +} + +#ifdef CONFIG_PM +static int mmci_suspend(struct amba_device *dev, u32 state) +{ + struct mmc_host *mmc = amba_get_drvdata(dev); + int ret = 0; + + if (mmc) { + struct mmci_host *host = mmc_priv(mmc); + + ret = mmc_suspend_host(mmc, state); + if (ret == 0) + writel(0, host->base + MMCIMASK0); + } + + return ret; +} + +static int mmci_resume(struct amba_device *dev) +{ + struct mmc_host *mmc = amba_get_drvdata(dev); + int ret = 0; + + if (mmc) { + struct mmci_host *host = mmc_priv(mmc); + + writel(MCI_IRQENABLE, host->base + MMCIMASK0); + + ret = mmc_resume_host(mmc); + } + + return ret; +} +#else +#define mmci_suspend NULL +#define mmci_resume NULL +#endif + +static struct amba_id mmci_ids[] = { + { + .id = 0x00041180, + .mask = 0x000fffff, + }, + { + .id = 0x00041181, + .mask = 0x000fffff, + }, + { 0, 0 }, +}; + +static struct amba_driver mmci_driver = { + .drv = { + .name = DRIVER_NAME, + }, + .probe = mmci_probe, + .remove = mmci_remove, + .suspend = mmci_suspend, + .resume = mmci_resume, + .id_table = mmci_ids, +}; + +static int __init mmci_init(void) +{ + return amba_driver_register(&mmci_driver); +} + +static void __exit mmci_exit(void) +{ + amba_driver_unregister(&mmci_driver); +} + +module_init(mmci_init); +module_exit(mmci_exit); +module_param(fmax, uint, 0444); + +MODULE_DESCRIPTION("ARM PrimeCell PL180/181 Multimedia Card Interface driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/mmc/mmci.h b/drivers/mmc/mmci.h new file mode 100644 index 000000000..ce3a026d1 --- /dev/null +++ b/drivers/mmc/mmci.h @@ -0,0 +1,183 @@ +/* + * linux/drivers/mmc/mmci.h - ARM PrimeCell MMCI PL180/1 driver + * + * Copyright (C) 2003 Deep Blue Solutions, Ltd, All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#define MMCIPOWER 0x000 +#define MCI_PWR_OFF 0x00 +#define MCI_PWR_UP 0x02 +#define MCI_PWR_ON 0x03 +#define MCI_OD (1 << 6) +#define MCI_ROD (1 << 7) + +#define MMCICLOCK 0x004 +#define MCI_CLK_ENABLE (1 << 8) +#define MCI_CLK_PWRSAVE (1 << 9) +#define MCI_CLK_BYPASS (1 << 10) + +#define MMCIARGUMENT 0x008 +#define MMCICOMMAND 0x00c +#define MCI_CPSM_RESPONSE (1 << 6) +#define MCI_CPSM_LONGRSP (1 << 7) +#define MCI_CPSM_INTERRUPT (1 << 8) +#define MCI_CPSM_PENDING (1 << 9) +#define MCI_CPSM_ENABLE (1 << 10) + +#define MMCIRESPCMD 0x010 +#define MMCIRESPONSE0 0x014 +#define MMCIRESPONSE1 0x018 +#define MMCIRESPONSE2 0x01c +#define MMCIRESPONSE3 0x020 +#define MMCIDATATIMER 0x024 +#define MMCIDATALENGTH 0x028 +#define MMCIDATACTRL 0x02c +#define MCI_DPSM_ENABLE (1 << 0) +#define MCI_DPSM_DIRECTION (1 << 1) +#define MCI_DPSM_MODE (1 << 2) +#define MCI_DPSM_DMAENABLE (1 << 3) + +#define MMCIDATACNT 0x030 +#define MMCISTATUS 0x034 +#define MCI_CMDCRCFAIL (1 << 0) +#define MCI_DATACRCFAIL (1 << 1) +#define MCI_CMDTIMEOUT (1 << 2) +#define MCI_DATATIMEOUT (1 << 3) +#define MCI_TXUNDERRUN (1 << 4) +#define MCI_RXOVERRUN (1 << 5) +#define MCI_CMDRESPEND (1 << 6) +#define MCI_CMDSENT (1 << 7) +#define MCI_DATAEND (1 << 8) +#define MCI_DATABLOCKEND (1 << 10) +#define MCI_CMDACTIVE (1 << 11) +#define MCI_TXACTIVE (1 << 12) +#define MCI_RXACTIVE (1 << 13) +#define MCI_TXFIFOHALFEMPTY (1 << 14) +#define MCI_RXFIFOHALFFULL (1 << 15) +#define MCI_TXFIFOFULL (1 << 16) +#define MCI_RXFIFOFULL (1 << 17) +#define MCI_TXFIFOEMPTY (1 << 18) +#define MCI_RXFIFOEMPTY (1 << 19) +#define MCI_TXDATAAVLBL (1 << 20) +#define MCI_RXDATAAVLBL (1 << 21) + +#define MMCICLEAR 0x038 +#define MCI_CMDCRCFAILCLR (1 << 0) +#define MCI_DATACRCFAILCLR (1 << 1) +#define MCI_CMDTIMEOUTCLR (1 << 2) +#define MCI_DATATIMEOUTCLR (1 << 3) +#define MCI_TXUNDERRUNCLR (1 << 4) +#define MCI_RXOVERRUNCLR (1 << 5) +#define MCI_CMDRESPENDCLR (1 << 6) +#define MCI_CMDSENTCLR (1 << 7) +#define MCI_DATAENDCLR (1 << 8) +#define MCI_DATABLOCKENDCLR (1 << 10) + +#define MMCIMASK0 0x03c +#define MCI_CMDCRCFAILMASK (1 << 0) +#define MCI_DATACRCFAILMASK (1 << 1) +#define MCI_CMDTIMEOUTMASK (1 << 2) +#define MCI_DATATIMEOUTMASK (1 << 3) +#define MCI_TXUNDERRUNMASK (1 << 4) +#define MCI_RXOVERRUNMASK (1 << 5) +#define MCI_CMDRESPENDMASK (1 << 6) +#define MCI_CMDSENTMASK (1 << 7) +#define MCI_DATAENDMASK (1 << 8) +#define MCI_DATABLOCKENDMASK (1 << 10) +#define MCI_CMDACTIVEMASK (1 << 11) +#define MCI_TXACTIVEMASK (1 << 12) +#define MCI_RXACTIVEMASK (1 << 13) +#define MCI_TXFIFOHALFEMPTYMASK (1 << 14) +#define MCI_RXFIFOHALFFULLMASK (1 << 15) +#define MCI_TXFIFOFULLMASK (1 << 16) +#define MCI_RXFIFOFULLMASK (1 << 17) +#define MCI_TXFIFOEMPTYMASK (1 << 18) +#define MCI_RXFIFOEMPTYMASK (1 << 19) +#define MCI_TXDATAAVLBLMASK (1 << 20) +#define MCI_RXDATAAVLBLMASK (1 << 21) + +#define MMCIMASK1 0x040 +#define MMCIFIFOCNT 0x048 +#define MMCIFIFO 0x080 /* to 0x0bc */ + +#define MCI_IRQENABLE \ + (MCI_CMDCRCFAILMASK|MCI_DATACRCFAILMASK|MCI_CMDTIMEOUTMASK| \ + MCI_DATATIMEOUTMASK|MCI_TXUNDERRUNMASK|MCI_RXOVERRUNMASK| \ + MCI_CMDRESPENDMASK|MCI_CMDSENTMASK|MCI_DATABLOCKENDMASK) + +/* + * The size of the FIFO in bytes. + */ +#define MCI_FIFOSIZE (16*4) + +#define MCI_FIFOHALFSIZE (MCI_FIFOSIZE / 2) + +#define NR_SG 16 + +struct clk; + +struct mmci_host { + void *base; + struct mmc_request *mrq; + struct mmc_command *cmd; + struct mmc_data *data; + struct mmc_host *mmc; + struct clk *clk; + + unsigned int data_xfered; + + spinlock_t lock; + + unsigned int mclk; + unsigned int cclk; + u32 pwr; + struct mmc_platform_data *plat; + + struct timer_list timer; + unsigned int oldstat; + + struct scatterlist sg[NR_SG]; + unsigned int sg_len; + + /* pio stuff */ + struct scatterlist *sg_ptr; + unsigned int sg_off; + unsigned int size; +}; + +static inline void mmci_init_sg(struct mmci_host *host, struct mmc_data *data) +{ + struct scatterlist *sg = host->sg; + struct request *req = data->req; + + /* + * Ideally, we want the higher levels to pass us a scatter list. + */ + host->sg_len = blk_rq_map_sg(req->q, req, sg); + host->sg_ptr = sg; + host->sg_off = 0; +} + +static inline int mmci_next_sg(struct mmci_host *host) +{ + host->sg_ptr++; + host->sg_off = 0; + return --host->sg_len; +} + +static inline char *mmci_kmap_atomic(struct mmci_host *host, unsigned long *flags) +{ + struct scatterlist *sg = host->sg_ptr; + + local_irq_save(*flags); + return kmap_atomic(sg->page, KM_BIO_SRC_IRQ) + sg->offset; +} + +static inline void mmci_kunmap_atomic(struct mmci_host *host, unsigned long *flags) +{ + kunmap_atomic(host->sg_ptr->page, KM_BIO_SRC_IRQ); + local_irq_restore(*flags); +} diff --git a/drivers/mmc/pxamci.c b/drivers/mmc/pxamci.c new file mode 100644 index 000000000..23a509196 --- /dev/null +++ b/drivers/mmc/pxamci.c @@ -0,0 +1,604 @@ +/* + * linux/drivers/mmc/pxa.c - PXA MMCI driver + * + * Copyright (C) 2003 Russell King, All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This hardware is really sick: + * - No way to clear interrupts. + * - Have to turn off the clock whenever we touch the device. + * - Doesn't tell you how many data blocks were transferred. + * Yuck! + * + * 1 and 3 byte data transfers not supported + * max block length up to 1023 + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include + +#include "pxamci.h" + +#ifdef CONFIG_MMC_DEBUG +#define DBG(x...) printk(KERN_DEBUG x) +#else +#define DBG(x...) do { } while (0) +#endif + +struct pxamci_host { + struct mmc_host *mmc; + spinlock_t lock; + struct resource *res; + void *base; + int irq; + int dma; + unsigned int clkrt; + unsigned int cmdat; + unsigned int imask; + unsigned int power_mode; + struct pxamci_platform_data *pdata; + + struct mmc_request *mrq; + struct mmc_command *cmd; + struct mmc_data *data; + + dma_addr_t sg_dma; + struct pxa_dma_desc *sg_cpu; + + dma_addr_t dma_buf; + unsigned int dma_size; + unsigned int dma_dir; +}; + +/* + * The base MMC clock rate + */ +#define CLOCKRATE 20000000 + +static inline unsigned int ns_to_clocks(unsigned int ns) +{ + return (ns * (CLOCKRATE / 1000000) + 999) / 1000; +} + +static void pxamci_stop_clock(struct pxamci_host *host) +{ + if (readl(host->base + MMC_STAT) & STAT_CLK_EN) { + unsigned long timeout = 10000; + unsigned int v; + + writel(STOP_CLOCK, host->base + MMC_STRPCL); + + do { + v = readl(host->base + MMC_STAT); + if (!(v & STAT_CLK_EN)) + break; + udelay(1); + } while (timeout--); + + if (v & STAT_CLK_EN) + dev_err(mmc_dev(host->mmc), "unable to stop clock\n"); + } +} + +static void pxamci_enable_irq(struct pxamci_host *host, unsigned int mask) +{ + unsigned long flags; + + spin_lock_irqsave(&host->lock, flags); + host->imask &= ~mask; + writel(host->imask, host->base + MMC_I_MASK); + spin_unlock_irqrestore(&host->lock, flags); +} + +static void pxamci_disable_irq(struct pxamci_host *host, unsigned int mask) +{ + unsigned long flags; + + spin_lock_irqsave(&host->lock, flags); + host->imask |= mask; + writel(host->imask, host->base + MMC_I_MASK); + spin_unlock_irqrestore(&host->lock, flags); +} + +static void pxamci_setup_data(struct pxamci_host *host, struct mmc_data *data) +{ + unsigned int nob = data->blocks; + unsigned int timeout, size; + dma_addr_t dma; + u32 dcmd; + int i; + + host->data = data; + + if (data->flags & MMC_DATA_STREAM) + nob = 0xffff; + + writel(nob, host->base + MMC_NOB); + writel(1 << data->blksz_bits, host->base + MMC_BLKLEN); + + timeout = ns_to_clocks(data->timeout_ns) + data->timeout_clks; + writel((timeout + 255) / 256, host->base + MMC_RDTO); + + if (data->flags & MMC_DATA_READ) { + host->dma_dir = DMA_FROM_DEVICE; + dcmd = DCMD_INCTRGADDR | DCMD_FLOWTRG; + DRCMRTXMMC = 0; + DRCMRRXMMC = host->dma | DRCMR_MAPVLD; + } else { + host->dma_dir = DMA_TO_DEVICE; + dcmd = DCMD_INCSRCADDR | DCMD_FLOWSRC; + DRCMRRXMMC = 0; + DRCMRTXMMC = host->dma | DRCMR_MAPVLD; + } + + dcmd |= DCMD_BURST32 | DCMD_WIDTH1; + + host->dma_size = data->blocks << data->blksz_bits; + host->dma_buf = dma_map_single(mmc_dev(host->mmc), data->req->buffer, + host->dma_size, host->dma_dir); + + for (i = 0, size = host->dma_size, dma = host->dma_buf; size; i++) { + u32 len = size; + + if (len > DCMD_LENGTH) + len = 0x1000; + + if (data->flags & MMC_DATA_READ) { + host->sg_cpu[i].dsadr = host->res->start + MMC_RXFIFO; + host->sg_cpu[i].dtadr = dma; + } else { + host->sg_cpu[i].dsadr = dma; + host->sg_cpu[i].dtadr = host->res->start + MMC_TXFIFO; + } + host->sg_cpu[i].dcmd = dcmd | len; + + dma += len; + size -= len; + + if (size) { + host->sg_cpu[i].ddadr = host->sg_dma + (i + 1) * + sizeof(struct pxa_dma_desc); + } else { + host->sg_cpu[i].ddadr = DDADR_STOP; + } + } + wmb(); + + DDADR(host->dma) = host->sg_dma; + DCSR(host->dma) = DCSR_RUN; +} + +static void pxamci_start_cmd(struct pxamci_host *host, struct mmc_command *cmd, unsigned int cmdat) +{ + WARN_ON(host->cmd != NULL); + host->cmd = cmd; + + if (cmd->flags & MMC_RSP_BUSY) + cmdat |= CMDAT_BUSY; + + switch (cmd->flags & (MMC_RSP_MASK | MMC_RSP_CRC)) { + case MMC_RSP_SHORT | MMC_RSP_CRC: + cmdat |= CMDAT_RESP_SHORT; + break; + case MMC_RSP_SHORT: + cmdat |= CMDAT_RESP_R3; + break; + case MMC_RSP_LONG | MMC_RSP_CRC: + cmdat |= CMDAT_RESP_R2; + break; + default: + break; + } + + writel(cmd->opcode, host->base + MMC_CMD); + writel(cmd->arg >> 16, host->base + MMC_ARGH); + writel(cmd->arg & 0xffff, host->base + MMC_ARGL); + writel(cmdat, host->base + MMC_CMDAT); + writel(host->clkrt, host->base + MMC_CLKRT); + + writel(START_CLOCK, host->base + MMC_STRPCL); + + pxamci_enable_irq(host, END_CMD_RES); +} + +static void pxamci_finish_request(struct pxamci_host *host, struct mmc_request *mrq) +{ + DBG("PXAMCI: request done\n"); + host->mrq = NULL; + host->cmd = NULL; + host->data = NULL; + mmc_request_done(host->mmc, mrq); +} + +static int pxamci_cmd_done(struct pxamci_host *host, unsigned int stat) +{ + struct mmc_command *cmd = host->cmd; + int i; + u32 v; + + if (!cmd) + return 0; + + host->cmd = NULL; + + /* + * Did I mention this is Sick. We always need to + * discard the upper 8 bits of the first 16-bit word. + */ + v = readl(host->base + MMC_RES) & 0xffff; + for (i = 0; i < 4; i++) { + u32 w1 = readl(host->base + MMC_RES) & 0xffff; + u32 w2 = readl(host->base + MMC_RES) & 0xffff; + cmd->resp[i] = v << 24 | w1 << 8 | w2 >> 8; + v = w2; + } + + if (stat & STAT_TIME_OUT_RESPONSE) { + cmd->error = MMC_ERR_TIMEOUT; + } else if (stat & STAT_RES_CRC_ERR && cmd->flags & MMC_RSP_CRC) { + cmd->error = MMC_ERR_BADCRC; + } + + pxamci_disable_irq(host, END_CMD_RES); + if (host->data && cmd->error == MMC_ERR_NONE) { + pxamci_enable_irq(host, DATA_TRAN_DONE); + } else { + pxamci_finish_request(host, host->mrq); + } + + return 1; +} + +static int pxamci_data_done(struct pxamci_host *host, unsigned int stat) +{ + struct mmc_data *data = host->data; + + if (!data) + return 0; + + DCSR(host->dma) = 0; + dma_unmap_single(mmc_dev(host->mmc), host->dma_buf, host->dma_size, + host->dma_dir); + + if (stat & STAT_READ_TIME_OUT) + data->error = MMC_ERR_TIMEOUT; + else if (stat & (STAT_CRC_READ_ERROR|STAT_CRC_WRITE_ERROR)) + data->error = MMC_ERR_BADCRC; + + /* + * There appears to be a hardware design bug here. There seems to + * be no way to find out how much data was transferred to the card. + * This means that if there was an error on any block, we mark all + * data blocks as being in error. + */ + if (data->error == MMC_ERR_NONE) + data->bytes_xfered = data->blocks << data->blksz_bits; + else + data->bytes_xfered = 0; + + pxamci_disable_irq(host, DATA_TRAN_DONE); + + host->data = NULL; + if (host->mrq->stop && data->error == MMC_ERR_NONE) { + pxamci_stop_clock(host); + pxamci_start_cmd(host, host->mrq->stop, 0); + } else { + pxamci_finish_request(host, host->mrq); + } + + return 1; +} + +static irqreturn_t pxamci_irq(int irq, void *devid, struct pt_regs *regs) +{ + struct pxamci_host *host = devid; + unsigned int ireg; + int handled = 0; + + ireg = readl(host->base + MMC_I_REG); + + DBG("PXAMCI: irq %08x\n", ireg); + + if (ireg) { + unsigned stat = readl(host->base + MMC_STAT); + + DBG("PXAMCI: stat %08x\n", stat); + + if (ireg & END_CMD_RES) + handled |= pxamci_cmd_done(host, stat); + if (ireg & DATA_TRAN_DONE) + handled |= pxamci_data_done(host, stat); + } + + return IRQ_RETVAL(handled); +} + +static void pxamci_request(struct mmc_host *mmc, struct mmc_request *mrq) +{ + struct pxamci_host *host = mmc_priv(mmc); + unsigned int cmdat; + + WARN_ON(host->mrq != NULL); + + host->mrq = mrq; + + pxamci_stop_clock(host); + + cmdat = host->cmdat; + host->cmdat &= ~CMDAT_INIT; + + if (mrq->data) { + pxamci_setup_data(host, mrq->data); + + cmdat &= ~CMDAT_BUSY; + cmdat |= CMDAT_DATAEN | CMDAT_DMAEN; + if (mrq->data->flags & MMC_DATA_WRITE) + cmdat |= CMDAT_WRITE; + + if (mrq->data->flags & MMC_DATA_STREAM) + cmdat |= CMDAT_STREAM; + } + + pxamci_start_cmd(host, mrq->cmd, cmdat); +} + +static void pxamci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) +{ + struct pxamci_host *host = mmc_priv(mmc); + + DBG("pxamci_set_ios: clock %u power %u vdd %u.%02u\n", + ios->clock, ios->power_mode, ios->vdd / 100, + ios->vdd % 100); + + if (ios->clock) { + unsigned int clk = CLOCKRATE / ios->clock; + if (CLOCKRATE / clk > ios->clock) + clk <<= 1; + host->clkrt = fls(clk) - 1; + + /* + * we write clkrt on the next command + */ + } else if (readl(host->base + MMC_STAT) & STAT_CLK_EN) { + /* + * Ensure that the clock is off. + */ + writel(STOP_CLOCK, host->base + MMC_STRPCL); + } + + if (host->power_mode != ios->power_mode) { + host->power_mode = ios->power_mode; + + if (host->pdata && host->pdata->setpower) + host->pdata->setpower(mmc->dev, ios->vdd); + + if (ios->power_mode == MMC_POWER_ON) + host->cmdat |= CMDAT_INIT; + } + + DBG("pxamci_set_ios: clkrt = %x cmdat = %x\n", + host->clkrt, host->cmdat); +} + +static struct mmc_host_ops pxamci_ops = { + .request = pxamci_request, + .set_ios = pxamci_set_ios, +}; + +static void pxamci_dma_irq(int dma, void *devid, struct pt_regs *regs) +{ + printk(KERN_ERR "DMA%d: IRQ???\n", dma); + DCSR(dma) = DCSR_STARTINTR|DCSR_ENDINTR|DCSR_BUSERR; +} + +static irqreturn_t pxamci_detect_irq(int irq, void *devid, struct pt_regs *regs) +{ + mmc_detect_change(devid); + return IRQ_HANDLED; +} + +static int pxamci_probe(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct mmc_host *mmc; + struct pxamci_host *host = NULL; + struct resource *r; + int ret, irq; + + r = platform_get_resource(pdev, IORESOURCE_MEM, 0); + irq = platform_get_irq(pdev, 0); + if (!r || irq == NO_IRQ) + return -ENXIO; + + r = request_mem_region(r->start, SZ_4K, "PXAMCI"); + if (!r) + return -EBUSY; + + mmc = mmc_alloc_host(sizeof(struct pxamci_host), dev); + if (!mmc) { + ret = -ENOMEM; + goto out; + } + + mmc->ops = &pxamci_ops; + mmc->f_min = 312500; + mmc->f_max = 20000000; + + host = mmc_priv(mmc); + host->mmc = mmc; + host->dma = -1; + host->pdata = pdev->dev.platform_data; + mmc->ocr_avail = host->pdata ? + host->pdata->ocr_mask : + MMC_VDD_32_33|MMC_VDD_33_34; + + host->sg_cpu = dma_alloc_coherent(dev, PAGE_SIZE, &host->sg_dma, GFP_KERNEL); + if (!host->sg_cpu) { + ret = -ENOMEM; + goto out; + } + + spin_lock_init(&host->lock); + host->res = r; + host->irq = irq; + host->imask = TXFIFO_WR_REQ|RXFIFO_RD_REQ|CLK_IS_OFF|STOP_CMD| + END_CMD_RES|PRG_DONE|DATA_TRAN_DONE; + + host->base = ioremap(r->start, SZ_4K); + if (!host->base) { + ret = -ENOMEM; + goto out; + } + + /* + * Ensure that the host controller is shut down, and setup + * with our defaults. + */ + pxamci_stop_clock(host); + writel(0, host->base + MMC_SPI); + writel(64, host->base + MMC_RESTO); + writel(host->imask, host->base + MMC_I_MASK); + + pxa_gpio_mode(GPIO6_MMCCLK_MD); + pxa_gpio_mode(GPIO8_MMCCS0_MD); + pxa_set_cken(CKEN12_MMC, 1); + + host->dma = pxa_request_dma("PXAMCI", DMA_PRIO_LOW, pxamci_dma_irq, host); + if (host->dma < 0) { + ret = -EBUSY; + goto out; + } + + ret = request_irq(host->irq, pxamci_irq, 0, "PXAMCI", host); + if (ret) + goto out; + + dev_set_drvdata(dev, mmc); + + if (host->pdata && host->pdata->init) + host->pdata->init(dev, pxamci_detect_irq, mmc); + + mmc_add_host(mmc); + + return 0; + + out: + if (host) { + if (host->dma >= 0) + pxa_free_dma(host->dma); + if (host->base) + iounmap(host->base); + if (host->sg_cpu) + dma_free_coherent(dev, PAGE_SIZE, host->sg_cpu, host->sg_dma); + } + if (mmc) + mmc_free_host(mmc); + release_resource(r); + return ret; +} + +static int pxamci_remove(struct device *dev) +{ + struct mmc_host *mmc = dev_get_drvdata(dev); + + dev_set_drvdata(dev, NULL); + + if (mmc) { + struct pxamci_host *host = mmc_priv(mmc); + + if (host->pdata && host->pdata->exit) + host->pdata->exit(dev, mmc); + + mmc_remove_host(mmc); + + pxamci_stop_clock(host); + writel(TXFIFO_WR_REQ|RXFIFO_RD_REQ|CLK_IS_OFF|STOP_CMD| + END_CMD_RES|PRG_DONE|DATA_TRAN_DONE, + host->base + MMC_I_MASK); + + pxa_set_cken(CKEN12_MMC, 0); + + free_irq(host->irq, host); + pxa_free_dma(host->dma); + iounmap(host->base); + dma_free_coherent(dev, PAGE_SIZE, host->sg_cpu, host->sg_dma); + + pxa_set_cken(CKEN12_MMC, 0); + + release_resource(host->res); + + mmc_free_host(mmc); + } + return 0; +} + +#ifdef CONFIG_PM +static int pxamci_suspend(struct device *dev, u32 state, u32 level) +{ + struct mmc_host *mmc = dev_get_drvdata(dev); + int ret = 0; + + if (mmc && level == SUSPEND_DISABLE) + ret = mmc_suspend_host(mmc, state); + + return ret; +} + +static int pxamci_resume(struct device *dev, u32 level) +{ + struct mmc_host *mmc = dev_get_drvdata(dev); + int ret = 0; + + if (mmc && level == RESUME_ENABLE) + ret = mmc_resume_host(mmc); + + return ret; +} +#else +#define pxamci_suspend NULL +#define pxamci_resume NULL +#endif + +static struct device_driver pxamci_driver = { + .name = "pxa2xx-mci", + .bus = &platform_bus_type, + .probe = pxamci_probe, + .remove = pxamci_remove, + .suspend = pxamci_suspend, + .resume = pxamci_resume, +}; + +static int __init pxamci_init(void) +{ + return driver_register(&pxamci_driver); +} + +static void __exit pxamci_exit(void) +{ + driver_unregister(&pxamci_driver); +} + +module_init(pxamci_init); +module_exit(pxamci_exit); + +MODULE_DESCRIPTION("PXA Multimedia Card Interface Driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/mmc/pxamci.h b/drivers/mmc/pxamci.h new file mode 100644 index 000000000..a80b24db9 --- /dev/null +++ b/drivers/mmc/pxamci.h @@ -0,0 +1,94 @@ +#undef MMC_STRPCL +#undef MMC_STAT +#undef MMC_CLKRT +#undef MMC_SPI +#undef MMC_CMDAT +#undef MMC_RESTO +#undef MMC_RDTO +#undef MMC_BLKLEN +#undef MMC_NOB +#undef MMC_PRTBUF +#undef MMC_I_MASK +#undef END_CMD_RES +#undef PRG_DONE +#undef DATA_TRAN_DONE +#undef MMC_I_REG +#undef MMC_CMD +#undef MMC_ARGH +#undef MMC_ARGL +#undef MMC_RES +#undef MMC_RXFIFO +#undef MMC_TXFIFO + +#define MMC_STRPCL 0x0000 +#define STOP_CLOCK (1 << 0) +#define START_CLOCK (2 << 0) + +#define MMC_STAT 0x0004 +#define STAT_END_CMD_RES (1 << 13) +#define STAT_PRG_DONE (1 << 12) +#define STAT_DATA_TRAN_DONE (1 << 11) +#define STAT_CLK_EN (1 << 8) +#define STAT_RECV_FIFO_FULL (1 << 7) +#define STAT_XMIT_FIFO_EMPTY (1 << 6) +#define STAT_RES_CRC_ERR (1 << 5) +#define STAT_SPI_READ_ERROR_TOKEN (1 << 4) +#define STAT_CRC_READ_ERROR (1 << 3) +#define STAT_CRC_WRITE_ERROR (1 << 2) +#define STAT_TIME_OUT_RESPONSE (1 << 1) +#define STAT_READ_TIME_OUT (1 << 0) + +#define MMC_CLKRT 0x0008 /* 3 bit */ + +#define MMC_SPI 0x000c +#define SPI_CS_ADDRESS (1 << 3) +#define SPI_CS_EN (1 << 2) +#define CRC_ON (1 << 1) +#define SPI_EN (1 << 0) + +#define MMC_CMDAT 0x0010 +#define CMDAT_DMAEN (1 << 7) +#define CMDAT_INIT (1 << 6) +#define CMDAT_BUSY (1 << 5) +#define CMDAT_STREAM (1 << 4) /* 1 = stream */ +#define CMDAT_WRITE (1 << 3) /* 1 = write */ +#define CMDAT_DATAEN (1 << 2) +#define CMDAT_RESP_NONE (0 << 0) +#define CMDAT_RESP_SHORT (1 << 0) +#define CMDAT_RESP_R2 (2 << 0) +#define CMDAT_RESP_R3 (3 << 0) + +#define MMC_RESTO 0x0014 /* 7 bit */ + +#define MMC_RDTO 0x0018 /* 16 bit */ + +#define MMC_BLKLEN 0x001c /* 10 bit */ + +#define MMC_NOB 0x0020 /* 16 bit */ + +#define MMC_PRTBUF 0x0024 +#define BUF_PART_FULL (1 << 0) + +#define MMC_I_MASK 0x0028 +#define TXFIFO_WR_REQ (1 << 6) +#define RXFIFO_RD_REQ (1 << 5) +#define CLK_IS_OFF (1 << 4) +#define STOP_CMD (1 << 3) +#define END_CMD_RES (1 << 2) +#define PRG_DONE (1 << 1) +#define DATA_TRAN_DONE (1 << 0) + +#define MMC_I_REG 0x002c +/* same as MMC_I_MASK */ + +#define MMC_CMD 0x0030 + +#define MMC_ARGH 0x0034 /* 16 bit */ + +#define MMC_ARGL 0x0038 /* 16 bit */ + +#define MMC_RES 0x003c /* 16 bit */ + +#define MMC_RXFIFO 0x0040 /* 8 bit */ + +#define MMC_TXFIFO 0x0044 /* 8 bit */ diff --git a/drivers/mtd/maps/ixp2000.c b/drivers/mtd/maps/ixp2000.c new file mode 100644 index 000000000..104488c0b --- /dev/null +++ b/drivers/mtd/maps/ixp2000.c @@ -0,0 +1,281 @@ +/* + * $Id: ixp2000.c,v 1.1 2004/09/02 00:13:41 dsaxena Exp $ + * + * drivers/mtd/maps/ixp2000.c + * + * Mapping for the Intel XScale IXP2000 based systems + * + * Copyright (C) 2002 Intel Corp. + * Copyright (C) 2003-2004 MontaVista Software, Inc. + * + * Original Author: Naeem M Afzal + * Maintainer: Deepak Saxena + * + * This program is free software; you can 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include + +struct ixp2000_flash_info { + struct mtd_info *mtd; + struct map_info map; + struct mtd_partition *partitions; + struct resource *res; + int nr_banks; +}; + +static inline unsigned long flash_bank_setup(struct map_info *map, unsigned long ofs) +{ + unsigned long (*set_bank)(unsigned long) = + (unsigned long(*)(unsigned long))map->map_priv_2; + + return (set_bank ? set_bank(ofs) : ofs); +} + +#ifdef __ARMEB__ +/* + * Rev A0 and A1 of IXP2400 silicon have a broken addressing unit which + * causes the lower address bits to be XORed with 0x11 on 8 bit accesses + * and XORed with 0x10 on 16 bit accesses. See the spec update, erratta 44. + */ +static int errata44_workaround = 0; + +static inline unsigned long address_fix8_write(unsigned long addr) +{ + if (errata44_workaround) { + return (addr ^ 3); + } + return addr; +} +#else + +#define address_fix8_write(x) (x) +#endif + +static map_word ixp2000_flash_read8(struct map_info *map, unsigned long ofs) +{ + map_word val; + + val.x[0] = *((u8 *)(map->map_priv_1 + flash_bank_setup(map, ofs))); + return val; +} + +/* + * We can't use the standard memcpy due to the broken SlowPort + * address translation on rev A0 and A1 silicon and the fact that + * we have banked flash. + */ +static void ixp2000_flash_copy_from(struct map_info *map, void *to, + unsigned long from, ssize_t len) +{ + from = flash_bank_setup(map, from); + while(len--) + *(__u8 *) to++ = *(__u8 *)(map->map_priv_1 + from++); +} + +static void ixp2000_flash_write8(struct map_info *map, map_word d, unsigned long ofs) +{ + *(__u8 *) (address_fix8_write(map->map_priv_1 + + flash_bank_setup(map, ofs))) = d.x[0]; +} + +static void ixp2000_flash_copy_to(struct map_info *map, unsigned long to, + const void *from, ssize_t len) +{ + to = flash_bank_setup(map, to); + while(len--) { + unsigned long tmp = address_fix8_write(map->map_priv_1 + to++); + *(__u8 *)(tmp) = *(__u8 *)(from++); + } +} + + +static int ixp2000_flash_remove(struct device *_dev) +{ + struct platform_device *dev = to_platform_device(_dev); + struct flash_platform_data *plat = dev->dev.platform_data; + struct ixp2000_flash_info *info = dev_get_drvdata(&dev->dev); + + dev_set_drvdata(&dev->dev, NULL); + + if(!info) + return 0; + + if (info->mtd) { + del_mtd_partitions(info->mtd); + map_destroy(info->mtd); + } + if (info->map.map_priv_1) + iounmap((void *) info->map.map_priv_1); + + if (info->partitions) + kfree(info->partitions); + + if (info->res) { + release_resource(info->res); + kfree(info->res); + } + + if (plat->exit) + plat->exit(); + + return 0; +} + + +static int ixp2000_flash_probe(struct device *_dev) +{ + static const char *probes[] = { "RedBoot", "cmdlinepart", NULL }; + struct platform_device *dev = to_platform_device(_dev); + struct ixp2000_flash_data *ixp_data = dev->dev.platform_data; + struct flash_platform_data *plat; + struct ixp2000_flash_info *info; + unsigned long window_size; + int err = -1; + + if (!ixp_data) + return -ENODEV; + + plat = ixp_data->platform_data; + if (!plat) + return -ENODEV; + + window_size = dev->resource->end - dev->resource->start + 1; + dev_info(_dev, "Probe of IXP2000 flash(%d banks x %dM)\n", + ixp_data->nr_banks, ((u32)window_size >> 20)); + + if (plat->width != 1) { + dev_err(_dev, "IXP2000 MTD map only supports 8-bit mode, asking for %d\n", + plat->width * 8); + return -EIO; + } + + info = kmalloc(sizeof(struct ixp2000_flash_info), GFP_KERNEL); + if(!info) { + err = -ENOMEM; + goto Error; + } + memzero(info, sizeof(struct ixp2000_flash_info)); + + dev_set_drvdata(&dev->dev, info); + + /* + * Tell the MTD layer we're not 1:1 mapped so that it does + * not attempt to do a direct access on us. + */ + info->map.phys = NO_XIP; + + info->nr_banks = ixp_data->nr_banks; + info->map.size = ixp_data->nr_banks * window_size; + info->map.bankwidth = 1; + + /* + * map_priv_2 is used to store a ptr to to the bank_setup routine + */ + info->map.map_priv_2 = (u32) ixp_data->bank_setup; + + info->map.name = dev->dev.bus_id; + info->map.read = ixp2000_flash_read8; + info->map.write = ixp2000_flash_write8; + info->map.copy_from = ixp2000_flash_copy_from; + info->map.copy_to = ixp2000_flash_copy_to; + + info->res = request_mem_region(dev->resource->start, + dev->resource->end - dev->resource->start + 1, + dev->dev.bus_id); + if (!info->res) { + dev_err(_dev, "Could not reserve memory region\n"); + err = -ENOMEM; + goto Error; + } + + info->map.map_priv_1 = + (unsigned long) ioremap(dev->resource->start, + dev->resource->end - dev->resource->start + 1); + if (!info->map.map_priv_1) { + dev_err(_dev, "Failed to ioremap flash region\n"); + err = -EIO; + goto Error; + } + + /* + * Setup read mode for FLASH + */ + *IXP2000_SLOWPORT_FRM = 1; + +#if defined(__ARMEB__) + /* + * Enable errata 44 workaround for NPUs with broken slowport + */ + + errata44_workaround = ixp2000_has_broken_slowport(); + dev_info(_dev, "Errata 44 workaround %s\n", + errata44_workaround ? "enabled" : "disabled"); +#endif + + info->mtd = do_map_probe(plat->map_name, &info->map); + if (!info->mtd) { + dev_err(_dev, "map_probe failed\n"); + err = -ENXIO; + goto Error; + } + info->mtd->owner = THIS_MODULE; + + err = parse_mtd_partitions(info->mtd, probes, &info->partitions, 0); + if (err > 0) { + err = add_mtd_partitions(info->mtd, info->partitions, err); + if(err) + dev_err(_dev, "Could not parse partitions\n"); + } + + if (err) + goto Error; + + return 0; + +Error: + ixp2000_flash_remove(_dev); + return err; +} + +static struct device_driver ixp2000_flash_driver = { + .name = "IXP2000-Flash", + .bus = &platform_bus_type, + .probe = &ixp2000_flash_probe, + .remove = &ixp2000_flash_remove +}; + +static int __init ixp2000_flash_init(void) +{ + return driver_register(&ixp2000_flash_driver); +} + +static void __exit ixp2000_flash_exit(void) +{ + driver_unregister(&ixp2000_flash_driver); +} + +module_init(ixp2000_flash_init); +module_exit(ixp2000_flash_exit); +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Deepak Saxena "); + diff --git a/drivers/mtd/maps/ixp4xx.c b/drivers/mtd/maps/ixp4xx.c index 7ebc8cc5b..1770b1acc 100644 --- a/drivers/mtd/maps/ixp4xx.c +++ b/drivers/mtd/maps/ixp4xx.c @@ -1,5 +1,5 @@ /* - * $Id: ixp4xx.c,v 1.3 2004/07/12 22:38:29 dwmw2 Exp $ + * $Id: ixp4xx.c,v 1.6 2004/09/17 00:25:06 gleixner Exp $ * * drivers/mtd/maps/ixp4xx.c * @@ -69,9 +69,22 @@ static void ixp4xx_copy_from(struct map_info *map, void *to, dest[len - 1] = BYTE0(src[i]); } +/* + * Unaligned writes are ignored, causing the 8-bit + * probe to fail and proceed to the 16-bit probe (which succeeds). + */ +static void ixp4xx_probe_write16(struct map_info *map, map_word d, unsigned long adr) +{ + if (!(adr & 1)) + *(__u16 *) (map->map_priv_1 + adr) = d.x[0]; +} + +/* + * Fast write16 function without the probing check above + */ static void ixp4xx_write16(struct map_info *map, map_word d, unsigned long adr) { - *(__u16 *) (map->map_priv_1 + adr) = d.x[0]; + *(__u16 *) (map->map_priv_1 + adr) = d.x[0]; } struct ixp4xx_flash_info { @@ -88,6 +101,7 @@ static int ixp4xx_flash_remove(struct device *_dev) struct platform_device *dev = to_platform_device(_dev); struct flash_platform_data *plat = dev->dev.platform_data; struct ixp4xx_flash_info *info = dev_get_drvdata(&dev->dev); + map_word d; dev_set_drvdata(&dev->dev, NULL); @@ -97,7 +111,8 @@ static int ixp4xx_flash_remove(struct device *_dev) /* * This is required for a soft reboot to work. */ - ixp4xx_write16(&info->map, 0xff, 0x55 * 0x2); + d.x[0] = 0xff; + ixp4xx_write16(&info->map, d, 0x55 * 0x2); if (info->mtd) { del_mtd_partitions(info->mtd); @@ -169,7 +184,7 @@ static int ixp4xx_flash_probe(struct device *_dev) info->map.bankwidth = 2; info->map.name = dev->dev.bus_id; info->map.read = ixp4xx_read16, - info->map.write = ixp4xx_write16, + info->map.write = ixp4xx_probe_write16, info->map.copy_from = ixp4xx_copy_from, info->res = request_mem_region(dev->resource->start, @@ -182,7 +197,7 @@ static int ixp4xx_flash_probe(struct device *_dev) } info->map.map_priv_1 = - (unsigned long) ioremap(dev->resource->start, + (void __iomem *) ioremap(dev->resource->start, dev->resource->end - dev->resource->start + 1); if (!info->map.map_priv_1) { printk(KERN_ERR "IXP4XXFlash: Failed to ioremap region\n"); @@ -198,6 +213,9 @@ static int ixp4xx_flash_probe(struct device *_dev) } info->mtd->owner = THIS_MODULE; + /* Use the fast version */ + info->map.write = ixp4xx_write16, + err = parse_mtd_partitions(info->mtd, probes, &info->partitions, 0); if (err > 0) { err = add_mtd_partitions(info->mtd, info->partitions, err); diff --git a/drivers/net/8139cp.c b/drivers/net/8139cp.c index 62dd13951..1b18b3aca 100644 --- a/drivers/net/8139cp.c +++ b/drivers/net/8139cp.c @@ -1698,7 +1698,7 @@ static int cp_init_one (struct pci_dev *pdev, const struct pci_device_id *ent) } /* Configure DMA attributes. */ - if ((sizeof(dma_addr_t) > 32) && + if ((sizeof(dma_addr_t) > 4) && !pci_set_consistent_dma_mask(pdev, 0xffffffffffffffffULL) && !pci_set_dma_mask(pdev, 0xffffffffffffffffULL)) { pci_using_dac = 1; diff --git a/drivers/net/8390.c b/drivers/net/8390.c index 22e7dfb47..04dcacda8 100644 --- a/drivers/net/8390.c +++ b/drivers/net/8390.c @@ -41,6 +41,8 @@ module by all drivers that require it. Alan Cox : Spinlocking work, added 'BUG_83C690' Paul Gortmaker : Separate out Tx timeout code from Tx path. + Paul Gortmaker : Remove old unused single Tx buffer code. + Hayato Fujiwara : Add m32r support. Sources: The National Semiconductor LAN Databook, and the 3Com 3c503 databook. @@ -218,6 +220,15 @@ void ei_tx_timeout(struct net_device *dev) int txsr, isr, tickssofar = jiffies - dev->trans_start; unsigned long flags; +#if defined(CONFIG_M32R) && defined(CONFIG_SMP) + unsigned long icucr; + + local_irq_save(flags); + icucr = inl(ICUCR1); + icucr |= M32R_ICUCR_ISMOD11; + outl(icucr, ICUCR1); + local_irq_restore(flags); +#endif ei_local->stat.tx_errors++; spin_lock_irqsave(&ei_local->page_lock, flags); @@ -289,8 +300,6 @@ static int ei_start_xmit(struct sk_buff *skb, struct net_device *dev) send_length = ETH_ZLEN < length ? length : ETH_ZLEN; -#ifdef EI_PINGPONG - /* * We have two Tx slots available for use. Find the first free * slot, and then perform some sanity checks. With two Tx bufs, @@ -309,7 +318,7 @@ static int ei_start_xmit(struct sk_buff *skb, struct net_device *dev) } else if (ei_local->tx2 == 0) { - output_page = ei_local->tx_start_page + TX_1X_PAGES; + output_page = ei_local->tx_start_page + TX_PAGES/2; ei_local->tx2 = send_length; if (ei_debug && ei_local->tx1 > 0) printk(KERN_DEBUG "%s: idle transmitter, tx1=%d, lasttx=%d, txing=%d.\n", @@ -366,28 +375,6 @@ static int ei_start_xmit(struct sk_buff *skb, struct net_device *dev) else netif_start_queue(dev); -#else /* EI_PINGPONG */ - - /* - * Only one Tx buffer in use. You need two Tx bufs to come close to - * back-to-back transmits. Expect a 20 -> 25% performance hit on - * reasonable hardware if you only use one Tx buffer. - */ - - if (length == send_length) - ei_block_output(dev, length, skb->data, ei_local->tx_start_page); - else { - memset(scratch, 0, ETH_ZLEN); - memcpy(scratch, skb->data, skb->len); - ei_block_output(dev, ETH_ZLEN, scratch, ei_local->tx_start_page); - } - ei_local->txing = 1; - NS8390_trigger_send(dev, send_length, ei_local->tx_start_page); - dev->trans_start = jiffies; - netif_stop_queue(dev); - -#endif /* EI_PINGPONG */ - /* Turn 8390 interrupts back on. */ ei_local->irqlock = 0; outb_p(ENISR_ALL, e8390_base + EN0_IMR); @@ -590,8 +577,6 @@ static void ei_tx_intr(struct net_device *dev) outb_p(ENISR_TX, e8390_base + EN0_ISR); /* Ack intr. */ -#ifdef EI_PINGPONG - /* * There are two Tx buffers, see which one finished, and trigger * the send of another one if it exists. @@ -634,13 +619,6 @@ static void ei_tx_intr(struct net_device *dev) // else printk(KERN_WARNING "%s: unexpected TX-done interrupt, lasttx=%d.\n", // dev->name, ei_local->lasttx); -#else /* EI_PINGPONG */ - /* - * Single Tx buffer: mark it free so another packet can be loaded. - */ - ei_local->txing = 0; -#endif - /* Minimize Tx latency: update the statistics after we restart TXing. */ if (status & ENTSR_COL) ei_local->stat.collisions++; diff --git a/drivers/net/amd8111e.c b/drivers/net/amd8111e.c index ca57a7a53..91fa987dc 100755 --- a/drivers/net/amd8111e.c +++ b/drivers/net/amd8111e.c @@ -719,7 +719,7 @@ static int amd8111e_tx(struct net_device *dev) return 0; } -#if CONFIG_AMD8111E_NAPI +#ifdef CONFIG_AMD8111E_NAPI /* This function handles the driver receive operation in polling mode */ static int amd8111e_rx_poll(struct net_device *dev, int * budget) { diff --git a/drivers/net/arcnet/arc-rimi.c b/drivers/net/arcnet/arc-rimi.c index 77ff7df32..edcc38761 100644 --- a/drivers/net/arcnet/arc-rimi.c +++ b/drivers/net/arcnet/arc-rimi.c @@ -231,7 +231,7 @@ err_free_irq: static int arcrimi_reset(struct net_device *dev, int really_reset) { struct arcnet_local *lp = (struct arcnet_local *) dev->priv; - void *ioaddr = lp->mem_start + 0x800; + void __iomem *ioaddr = lp->mem_start + 0x800; BUGMSG(D_INIT, "Resetting %s (status=%02Xh)\n", dev->name, ASTATUS()); @@ -252,7 +252,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 = (struct arcnet_local *) dev->priv; - void *ioaddr = lp->mem_start + 0x800; + void __iomem *ioaddr = lp->mem_start + 0x800; AINTMASK(mask); } @@ -260,7 +260,7 @@ static void arcrimi_setmask(struct net_device *dev, int mask) static int arcrimi_status(struct net_device *dev) { struct arcnet_local *lp = (struct arcnet_local *) dev->priv; - void *ioaddr = lp->mem_start + 0x800; + void __iomem *ioaddr = lp->mem_start + 0x800; return ASTATUS(); } @@ -268,7 +268,7 @@ static int arcrimi_status(struct net_device *dev) static void arcrimi_command(struct net_device *dev, int cmd) { struct arcnet_local *lp = (struct arcnet_local *) dev->priv; - void *ioaddr = lp->mem_start + 0x800; + void __iomem *ioaddr = lp->mem_start + 0x800; ACOMMAND(cmd); } @@ -277,7 +277,7 @@ static void arcrimi_copy_to_card(struct net_device *dev, int bufnum, int offset, void *buf, int count) { struct arcnet_local *lp = (struct arcnet_local *) dev->priv; - void *memaddr = lp->mem_start + 0x800 + bufnum * 512 + offset; + void __iomem *memaddr = lp->mem_start + 0x800 + bufnum * 512 + offset; TIME("memcpy_toio", count, memcpy_toio(memaddr, buf, count)); } @@ -286,7 +286,7 @@ static void arcrimi_copy_from_card(struct net_device *dev, int bufnum, int offse void *buf, int count) { struct arcnet_local *lp = (struct arcnet_local *) dev->priv; - void *memaddr = lp->mem_start + 0x800 + bufnum * 512 + offset; + void __iomem *memaddr = lp->mem_start + 0x800 + bufnum * 512 + offset; TIME("memcpy_fromio", count, memcpy_fromio(buf, memaddr, count)); } diff --git a/drivers/net/bonding/bond_alb.c b/drivers/net/bonding/bond_alb.c index 49f5ceee5..a76519afa 100644 --- a/drivers/net/bonding/bond_alb.c +++ b/drivers/net/bonding/bond_alb.c @@ -1285,7 +1285,7 @@ int bond_alb_xmit(struct sk_buff *skb, struct net_device *bond_dev) int res = 1; skb->mac.raw = (unsigned char *)skb->data; - eth_data = (struct ethhdr *)skb->data; + eth_data = eth_hdr(skb); /* make sure that the curr_active_slave and the slaves list do * not change during tx diff --git a/drivers/net/cs89x0.h b/drivers/net/cs89x0.h index f1507b549..b0ef7ad2b 100644 --- a/drivers/net/cs89x0.h +++ b/drivers/net/cs89x0.h @@ -16,6 +16,13 @@ #include +#ifdef CONFIG_ARCH_IXDP2X01 +/* IXDP2401/IXDP2801 uses dword-aligned register addressing */ +#define CS89x0_PORT(reg) ((reg) * 2) +#else +#define CS89x0_PORT(reg) (reg) +#endif + #define PP_ChipID 0x0000 /* offset 0h -> Corp -ID */ /* offset 2h -> Model/Product Number */ /* offset 3h -> Chip Revision Number */ @@ -324,16 +331,16 @@ #define RAM_SIZE 0x1000 /* The card has 4k bytes or RAM */ #define PKT_START PP_TxFrame /* Start of packet RAM */ -#define RX_FRAME_PORT 0x0000 +#define RX_FRAME_PORT CS89x0_PORT(0x0000) #define TX_FRAME_PORT RX_FRAME_PORT -#define TX_CMD_PORT 0x0004 +#define TX_CMD_PORT CS89x0_PORT(0x0004) #define TX_NOW 0x0000 /* Tx packet after 5 bytes copied */ #define TX_AFTER_381 0x0040 /* Tx packet after 381 bytes copied */ #define TX_AFTER_ALL 0x00c0 /* Tx packet after all bytes copied */ -#define TX_LEN_PORT 0x0006 -#define ISQ_PORT 0x0008 -#define ADD_PORT 0x000A -#define DATA_PORT 0x000C +#define TX_LEN_PORT CS89x0_PORT(0x0006) +#define ISQ_PORT CS89x0_PORT(0x0008) +#define ADD_PORT CS89x0_PORT(0x000A) +#define DATA_PORT CS89x0_PORT(0x000C) #define EEPROM_WRITE_EN 0x00F0 #define EEPROM_WRITE_DIS 0x0000 diff --git a/drivers/net/dl2k.h b/drivers/net/dl2k.h index 86302faf9..d46367392 100644 --- a/drivers/net/dl2k.h +++ b/drivers/net/dl2k.h @@ -92,7 +92,7 @@ enum dl2x_offsets { EepromCtrl = 0x4a, ExpromAddr = 0x4c, Exprodata = 0x50, - WakeEvent0x51, + WakeEvent = 0x51, CountDown = 0x54, IntStatusAck = 0x5a, IntEnable = 0x5c, diff --git a/drivers/net/gt64240eth.h b/drivers/net/gt64240eth.h new file mode 100644 index 000000000..7e7af0d56 --- /dev/null +++ b/drivers/net/gt64240eth.h @@ -0,0 +1,402 @@ +/* + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file "COPYING" in the main directory of this archive + * for more details. + * + * Copyright (C) 2001 Patton Electronics Company + * Copyright (C) 2002 Momentum Computer + * + * Copyright 2000 MontaVista Software Inc. + * Author: MontaVista Software, Inc. + * stevel@mvista.com or support@mvista.com + * + * This program is free software; you can distribute 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 it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + * + * You 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. + * + * Ethernet driver definitions for the MIPS GT96100 Advanced + * Communication Controller. + * + * Modified for the Marvellous GT64240 Retarded Communication Controller. + */ +#ifndef _GT64240ETH_H +#define _GT64240ETH_H + +#include + +#define ETHERNET_PORTS_DIFFERENCE_OFFSETS 0x400 + +/* Translate those weanie names from Galileo/VxWorks header files: */ + +#define GT64240_MRR MAIN_ROUTING_REGISTER +#define GT64240_CIU_ARBITER_CONFIG COMM_UNIT_ARBITER_CONFIGURATION_REGISTER +#define GT64240_CIU_ARBITER_CONTROL COMM_UNIT_ARBITER_CONTROL +#define GT64240_MAIN_LOW_CAUSE LOW_INTERRUPT_CAUSE_REGISTER +#define GT64240_MAIN_HIGH_CAUSE HIGH_INTERRUPT_CAUSE_REGISTER +#define GT64240_CPU_LOW_MASK CPU_INTERRUPT_MASK_REGISTER_LOW +#define GT64240_CPU_HIGH_MASK CPU_INTERRUPT_MASK_REGISTER_HIGH +#define GT64240_CPU_SELECT_CAUSE CPU_SELECT_CAUSE_REGISTER + +#define GT64240_ETH_PHY_ADDR_REG ETHERNET_PHY_ADDRESS_REGISTER +#define GT64240_ETH_PORT_CONFIG ETHERNET0_PORT_CONFIGURATION_REGISTER +#define GT64240_ETH_PORT_CONFIG_EXT ETHERNET0_PORT_CONFIGURATION_EXTEND_REGISTER +#define GT64240_ETH_PORT_COMMAND ETHERNET0_PORT_COMMAND_REGISTER +#define GT64240_ETH_PORT_STATUS ETHERNET0_PORT_STATUS_REGISTER +#define GT64240_ETH_IO_SIZE ETHERNET_PORTS_DIFFERENCE_OFFSETS +#define GT64240_ETH_SMI_REG ETHERNET_SMI_REGISTER +#define GT64240_ETH_MIB_COUNT_BASE ETHERNET0_MIB_COUNTER_BASE +#define GT64240_ETH_SDMA_CONFIG ETHERNET0_SDMA_CONFIGURATION_REGISTER +#define GT64240_ETH_SDMA_COMM ETHERNET0_SDMA_COMMAND_REGISTER +#define GT64240_ETH_INT_MASK ETHERNET0_INTERRUPT_MASK_REGISTER +#define GT64240_ETH_INT_CAUSE ETHERNET0_INTERRUPT_CAUSE_REGISTER +#define GT64240_ETH_CURR_TX_DESC_PTR0 ETHERNET0_CURRENT_TX_DESCRIPTOR_POINTER0 +#define GT64240_ETH_CURR_TX_DESC_PTR1 ETHERNET0_CURRENT_TX_DESCRIPTOR_POINTER1 +#define GT64240_ETH_1ST_RX_DESC_PTR0 ETHERNET0_FIRST_RX_DESCRIPTOR_POINTER0 +#define GT64240_ETH_CURR_RX_DESC_PTR0 ETHERNET0_CURRENT_RX_DESCRIPTOR_POINTER0 +#define GT64240_ETH_HASH_TBL_PTR ETHERNET0_HASH_TABLE_POINTER_REGISTER + +/* Turn on NAPI by default */ + +#define GT64240_NAPI 1 + +/* Some 64240 settings that SHOULD eventually be setup in PROM monitor: */ +/* (Board-specific to the DSL3224 Rev A board ONLY!) */ +#define D3224_MPP_CTRL0_SETTING 0x66669900 +#define D3224_MPP_CTRL1_SETTING 0x00000000 +#define D3224_MPP_CTRL2_SETTING 0x00887700 +#define D3224_MPP_CTRL3_SETTING 0x00000044 +#define D3224_GPP_IO_CTRL_SETTING 0x0000e800 +#define D3224_GPP_LEVEL_CTRL_SETTING 0xf001f703 +#define D3224_GPP_VALUE_SETTING 0x00000000 + +/* Keep the ring sizes a power of two for efficiency. */ +//-#define TX_RING_SIZE 16 +#define TX_RING_SIZE 64 /* TESTING !!! */ +#define RX_RING_SIZE 32 +#define PKT_BUF_SZ 1536 /* Size of each temporary Rx buffer. */ + +#define RX_HASH_TABLE_SIZE 16384 +#define HASH_HOP_NUMBER 12 + +#define NUM_INTERFACES 3 + +#define GT64240ETH_TX_TIMEOUT HZ/4 + +#define MIPS_GT64240_BASE 0xf4000000 +#define GT64240_ETH0_BASE (MIPS_GT64240_BASE + GT64240_ETH_PORT_CONFIG) +#define GT64240_ETH1_BASE (GT64240_ETH0_BASE + GT64240_ETH_IO_SIZE) +#define GT64240_ETH2_BASE (GT64240_ETH1_BASE + GT64240_ETH_IO_SIZE) + +#if defined(CONFIG_MIPS_DSL3224) +#define GT64240_ETHER0_IRQ 4 +#define GT64240_ETHER1_IRQ 4 +#else +#define GT64240_ETHER0_IRQ -1 +#define GT64240_ETHER1_IRQ -1 +#endif + +#define REV_GT64240 0x1 +#define REV_GT64240A 0x10 + +#define GT64240ETH_READ(gp, offset) \ + GT_READ((gp)->port_offset + (offset)) + +#define GT64240ETH_WRITE(gp, offset, data) \ + GT_WRITE((gp)->port_offset + (offset), (data)) + +#define GT64240ETH_SETBIT(gp, offset, bits) \ + GT64240ETH_WRITE((gp), (offset), \ + GT64240ETH_READ((gp), (offset)) | (bits)) + +#define GT64240ETH_CLRBIT(gp, offset, bits) \ + GT64240ETH_WRITE((gp), (offset), \ + GT64240ETH_READ((gp), (offset)) & ~(bits)) + +#define GT64240_READ(ofs) GT_READ(ofs) +#define GT64240_WRITE(ofs, data) GT_WRITE((ofs), (data)) + +/* Bit definitions of the SMI Reg */ +enum { + smirDataMask = 0xffff, + smirPhyAdMask = 0x1f << 16, + smirPhyAdBit = 16, + smirRegAdMask = 0x1f << 21, + smirRegAdBit = 21, + smirOpCode = 1 << 26, + smirReadValid = 1 << 27, + smirBusy = 1 << 28 +}; + +/* Bit definitions of the Port Config Reg */ +enum pcr_bits { + pcrPM = 1 << 0, + pcrRBM = 1 << 1, + pcrPBF = 1 << 2, + pcrEN = 1 << 7, + pcrLPBKMask = 0x3 << 8, + pcrLPBKBit = 1 << 8, + pcrFC = 1 << 10, + pcrHS = 1 << 12, + pcrHM = 1 << 13, + pcrHDM = 1 << 14, + pcrHD = 1 << 15, + pcrISLMask = 0x7 << 28, + pcrISLBit = 28, + pcrACCS = 1 << 31 +}; + +/* Bit definitions of the Port Config Extend Reg */ +enum pcxr_bits { + pcxrIGMP = 1, + pcxrSPAN = 2, + pcxrPAR = 4, + pcxrPRIOtxMask = 0x7 << 3, + pcxrPRIOtxBit = 3, + pcxrPRIOrxMask = 0x3 << 6, + pcxrPRIOrxBit = 6, + pcxrPRIOrxOverride = 1 << 8, + pcxrDPLXen = 1 << 9, + pcxrFCTLen = 1 << 10, + pcxrFLP = 1 << 11, + pcxrFCTL = 1 << 12, + pcxrMFLMask = 0x3 << 14, + pcxrMFLBit = 14, + pcxrMIBclrMode = 1 << 16, + pcxrSpeed = 1 << 18, + pcxrSpeeden = 1 << 19, + pcxrRMIIen = 1 << 20, + pcxrDSCPen = 1 << 21 +}; + +/* Bit definitions of the Port Command Reg */ +enum pcmr_bits { + pcmrFJ = 1 << 15 +}; + + +/* Bit definitions of the Port Status Reg */ +enum psr_bits { + psrSpeed = 1, + psrDuplex = 2, + psrFctl = 4, + psrLink = 8, + psrPause = 1 << 4, + psrTxLow = 1 << 5, + psrTxHigh = 1 << 6, + psrTxInProg = 1 << 7 +}; + +/* Bit definitions of the SDMA Config Reg */ +enum sdcr_bits { + sdcrRCMask = 0xf << 2, + sdcrRCBit = 2, + sdcrBLMR = 1 << 6, + sdcrBLMT = 1 << 7, + sdcrPOVR = 1 << 8, + sdcrRIFB = 1 << 9, + sdcrBSZMask = 0x3 << 12, + sdcrBSZBit = 12 +}; + +/* Bit definitions of the SDMA Command Reg */ +enum sdcmr_bits { + sdcmrERD = 1 << 7, + sdcmrAR = 1 << 15, + sdcmrSTDH = 1 << 16, + sdcmrSTDL = 1 << 17, + sdcmrTXDH = 1 << 23, + sdcmrTXDL = 1 << 24, + sdcmrAT = 1 << 31 +}; + +/* Bit definitions of the Interrupt Cause Reg */ +enum icr_bits { + icrRxBuffer = 1, + icrTxBufferHigh = 1 << 2, + icrTxBufferLow = 1 << 3, + icrTxEndHigh = 1 << 6, + icrTxEndLow = 1 << 7, + icrRxError = 1 << 8, + icrTxErrorHigh = 1 << 10, + icrTxErrorLow = 1 << 11, + icrRxOVR = 1 << 12, + icrTxUdr = 1 << 13, + icrRxBufferQ0 = 1 << 16, + icrRxBufferQ1 = 1 << 17, + icrRxBufferQ2 = 1 << 18, + icrRxBufferQ3 = 1 << 19, + icrRxErrorQ0 = 1 << 20, + icrRxErrorQ1 = 1 << 21, + icrRxErrorQ2 = 1 << 22, + icrRxErrorQ3 = 1 << 23, + icrMIIPhySTC = 1 << 28, + icrSMIdone = 1 << 29, + icrEtherIntSum = 1 << 31 +}; + + +/* The Rx and Tx descriptor lists. */ +#ifdef __LITTLE_ENDIAN +typedef struct { + u32 cmdstat; + u16 reserved; //-prk21aug01 u32 reserved:16; + u16 byte_cnt; //-prk21aug01 u32 byte_cnt:16; + u32 buff_ptr; + u32 next; +} gt64240_td_t; + +typedef struct { + u32 cmdstat; + u16 byte_cnt; //-prk21aug01 u32 byte_cnt:16; + u16 buff_sz; //-prk21aug01 u32 buff_sz:16; + u32 buff_ptr; + u32 next; +} gt64240_rd_t; +#elif defined(__BIG_ENDIAN) +typedef struct { + u16 byte_cnt; //-prk21aug01 u32 byte_cnt:16; + u16 reserved; //-prk21aug01 u32 reserved:16; + u32 cmdstat; + u32 next; + u32 buff_ptr; +} gt64240_td_t; + +typedef struct { + u16 buff_sz; //-prk21aug01 u32 buff_sz:16; + u16 byte_cnt; //-prk21aug01 u32 byte_cnt:16; + u32 cmdstat; + u32 next; + u32 buff_ptr; +} gt64240_rd_t; +#else +#error Either __BIG_ENDIAN or __LITTLE_ENDIAN must be defined! +#endif + + +/* Values for the Tx command-status descriptor entry. */ +enum td_cmdstat { + txOwn = 1 << 31, + txAutoMode = 1 << 30, + txEI = 1 << 23, + txGenCRC = 1 << 22, + txPad = 1 << 18, + txFirst = 1 << 17, + txLast = 1 << 16, + txErrorSummary = 1 << 15, + txReTxCntMask = 0x0f << 10, + txReTxCntBit = 10, + txCollision = 1 << 9, + txReTxLimit = 1 << 8, + txUnderrun = 1 << 6, + txLateCollision = 1 << 5 +}; + + +/* Values for the Rx command-status descriptor entry. */ +enum rd_cmdstat { + rxOwn = 1 << 31, + rxAutoMode = 1 << 30, + rxEI = 1 << 23, + rxFirst = 1 << 17, + rxLast = 1 << 16, + rxErrorSummary = 1 << 15, + rxIGMP = 1 << 14, + rxHashExpired = 1 << 13, + rxMissedFrame = 1 << 12, + rxFrameType = 1 << 11, + rxShortFrame = 1 << 8, + rxMaxFrameLen = 1 << 7, + rxOverrun = 1 << 6, + rxCollision = 1 << 4, + rxCRCError = 1 +}; + +/* Bit fields of a Hash Table Entry */ +enum hash_table_entry { + hteValid = 1, + hteSkip = 2, + hteRD = 4 +}; + +// The MIB counters +typedef struct { + u32 byteReceived; + u32 byteSent; + u32 framesReceived; + u32 framesSent; + u32 totalByteReceived; + u32 totalFramesReceived; + u32 broadcastFramesReceived; + u32 multicastFramesReceived; + u32 cRCError; + u32 oversizeFrames; + u32 fragments; + u32 jabber; + u32 collision; + u32 lateCollision; + u32 frames64; + u32 frames65_127; + u32 frames128_255; + u32 frames256_511; + u32 frames512_1023; + u32 frames1024_MaxSize; + u32 macRxError; + u32 droppedFrames; + u32 outMulticastFrames; + u32 outBroadcastFrames; + u32 undersizeFrames; +} mib_counters_t; + + +struct gt64240_private { + gt64240_rd_t *rx_ring; + gt64240_td_t *tx_ring; + // The Rx and Tx rings must be 16-byte aligned + dma_addr_t rx_ring_dma; + dma_addr_t tx_ring_dma; + char *hash_table; + // The Hash Table must be 8-byte aligned + dma_addr_t hash_table_dma; + int hash_mode; + + // The Rx buffers must be 8-byte aligned + char *rx_buff; + dma_addr_t rx_buff_dma; + // Tx buffers (tx_skbuff[i]->data) with less than 8 bytes + // of payload must be 8-byte aligned + struct sk_buff *tx_skbuff[TX_RING_SIZE]; + int rx_next_out; /* The next free ring entry to receive */ + int tx_next_in; /* The next free ring entry to send */ + int tx_next_out; /* The last ring entry the ISR processed */ + int tx_count; /* current # of pkts waiting to be sent in Tx ring */ + int intr_work_done; /* number of Rx and Tx pkts processed in the isr */ + int tx_full; /* Tx ring is full */ + + mib_counters_t mib; + struct net_device_stats stats; + + int io_size; + int port_num; // 0 or 1 + u32 port_offset; + + int phy_addr; // PHY address + u32 last_psr; // last value of the port status register + + int options; /* User-settable misc. driver options. */ + int drv_flags; + spinlock_t lock; /* Serialise access to device */ + struct mii_if_info mii_if; + + u32 msg_enable; +}; + +#endif /* _GT64240ETH_H */ diff --git a/drivers/net/hamradio/bpqether.c b/drivers/net/hamradio/bpqether.c index 6b85ab3c9..803d5a137 100644 --- a/drivers/net/hamradio/bpqether.c +++ b/drivers/net/hamradio/bpqether.c @@ -193,7 +193,7 @@ static int bpq_rcv(struct sk_buff *skb, struct net_device *dev, struct packet_ty bpq = (struct bpqdev *)dev->priv; - eth = (struct ethhdr *)skb->mac.raw; + eth = eth_hdr(skb); if (!(bpq->acpt_addr[0] & 0x01) && memcmp(eth->h_source, bpq->acpt_addr, ETH_ALEN)) diff --git a/drivers/net/hamradio/mkiss.c b/drivers/net/hamradio/mkiss.c index c7076aa0b..e2a28b562 100644 --- a/drivers/net/hamradio/mkiss.c +++ b/drivers/net/hamradio/mkiss.c @@ -602,8 +602,6 @@ static int ax25_open(struct tty_struct *tty) if (tty->driver->flush_buffer) tty->driver->flush_buffer(tty); - if (tty->ldisc.flush_buffer) - tty->ldisc.flush_buffer(tty); /* Restore default settings */ ax->dev->type = ARPHRD_AX25; diff --git a/drivers/net/ibm_emac/ibm_emac.h b/drivers/net/ibm_emac/ibm_emac.h index 5310033ad..7436af605 100644 --- a/drivers/net/ibm_emac/ibm_emac.h +++ b/drivers/net/ibm_emac/ibm_emac.h @@ -228,6 +228,21 @@ typedef struct emac_regs { (desc & EMAC_BAD_RX_PACKET) #endif +/* SoC implementation specific EMAC register defaults */ +#if defined(CONFIG_440GP) +#define EMAC_RWMR_DEFAULT 0x80009000 +#define EMAC_TMR0_DEFAULT 0x00000000 +#define EMAC_TMR1_DEFAULT 0xf8640000 +#elif defined(CONFIG_440GX) +#define EMAC_RWMR_DEFAULT 0x1000a200 +#define EMAC_TMR0_DEFAULT EMAC_TMR0_TFAE_2_32 +#define EMAC_TMR1_DEFAULT 0xa00f0000 +#else +#define EMAC_RWMR_DEFAULT 0x0f002000 +#define EMAC_TMR0_DEFAULT 0x00000000 +#define EMAC_TMR1_DEFAULT 0x380f0000 +#endif /* CONFIG_440GP */ + /* Revision specific EMAC register defaults */ #ifdef CONFIG_IBM_EMAC4 #define EMAC_M1_DEFAULT (EMAC_M1_BASE | \ @@ -236,7 +251,7 @@ typedef struct emac_regs { #define EMAC_RMR_DEFAULT (EMAC_RMR_BASE | \ EMAC_RMR_RFAF_128_2048) #define EMAC_TMR0_XMIT (EMAC_TMR0_GNP0 | \ - EMAC_TMR0_TFAE_128_2048) + EMAC_TMR0_DEFAULT) #define EMAC_TRTR_DEFAULT EMAC_TRTR_1024 #else /* !CONFIG_IBM_EMAC4 */ #define EMAC_M1_DEFAULT EMAC_M1_BASE @@ -245,19 +260,4 @@ typedef struct emac_regs { #define EMAC_TRTR_DEFAULT EMAC_TRTR_1600 #endif /* CONFIG_IBM_EMAC4 */ -/* SoC implementation specific EMAC register defaults */ -#if defined(CONFIG_440GP) -#define EMAC_RWMR_DEFAULT 0x80009000 -#define EMAC_TMR0_DEFAULT 0x00000000 -#define EMAC_TMR1_DEFAULT 0xf8640000 -#elif defined(CONFIG_440GX) -#define EMAC_RWMR_DEFAULT 0x1000a200 -#define EMAC_TMR0_DEFAULT EMAC_TMR0_TFAE_128_2048 -#define EMAC_TMR1_DEFAULT 0x88810000 -#else -#define EMAC_RWMR_DEFAULT 0x0f002000 -#define EMAC_TMR0_DEFAULT 0x00000000 -#define EMAC_TMR1_DEFAULT 0x380f0000 -#endif /* CONFIG_440GP */ - #endif diff --git a/drivers/net/ibm_emac/ibm_emac_core.c b/drivers/net/ibm_emac/ibm_emac_core.c index 2ca3de076..6b0328fbc 100644 --- a/drivers/net/ibm_emac/ibm_emac_core.c +++ b/drivers/net/ibm_emac/ibm_emac_core.c @@ -90,23 +90,24 @@ MODULE_PARM_DESC(skb_res, "Amount of data to reserve on skb buffs\n" #define RGMII_PRIV(ocpdev) ((struct ibm_ocp_rgmii*)ocp_get_drvdata(ocpdev)) -static unsigned int rgmii_enable[] = - { RGMII_RTBI, RGMII_RGMII, RGMII_TBI, RGMII_GMII }; +static unsigned int rgmii_enable[] = { + RGMII_RTBI, + RGMII_RGMII, + RGMII_TBI, + RGMII_GMII +}; -static unsigned int rgmii_speed_mask[] = { 0, - 0, +static unsigned int rgmii_speed_mask[] = { RGMII_MII2_SPDMASK, RGMII_MII3_SPDMASK }; -static unsigned int rgmii_speed100[] = { 0, - 0, +static unsigned int rgmii_speed100[] = { RGMII_MII2_100MB, RGMII_MII3_100MB }; -static unsigned int rgmii_speed1000[] = { 0, - 0, +static unsigned int rgmii_speed1000[] = { RGMII_MII2_1000MB, RGMII_MII3_1000MB }; @@ -122,11 +123,21 @@ static unsigned int zmii_enable[][4] = { ~(ZMII_MDI0 | ZMII_MDI1 | ZMII_MDI3)}, {ZMII_SMII3, ZMII_RMII3, ZMII_MII3, ~(ZMII_MDI0 | ZMII_MDI1 | ZMII_MDI2)} }; -static unsigned int mdi_enable[] = - { ZMII_MDI0, ZMII_MDI1, ZMII_MDI2, ZMII_MDI3 }; + +static unsigned int mdi_enable[] = { + ZMII_MDI0, + ZMII_MDI1, + ZMII_MDI2, + ZMII_MDI3 +}; static unsigned int zmii_speed = 0x0; -static unsigned int zmii_speed100[] = { ZMII_MII0_100MB, ZMII_MII1_100MB }; +static unsigned int zmii_speed100[] = { + ZMII_MII0_100MB, + ZMII_MII1_100MB, + ZMII_MII2_100MB, + ZMII_MII3_100MB +}; /* Since multiple EMACs share MDIO lines in various ways, we need * to avoid re-using the same PHY ID in cases where the arch didn't @@ -367,6 +378,7 @@ static void emac_close_zmii(struct ocp_device *ocpdev) int emac_phy_read(struct net_device *dev, int mii_id, int reg) { + int count; uint32_t stacr; struct ocp_enet_private *fep = dev->priv; emac_t *emacp = fep->emacp; @@ -385,9 +397,13 @@ int emac_phy_read(struct net_device *dev, int mii_id, int reg) emacp = fep->emacp; } - udelay(MDIO_DELAY); + count = 0; + while ((((stacr = in_be32(&emacp->em0stacr)) & EMAC_STACR_OC) == 0) + && (count++ < MDIO_DELAY)) + udelay(1); + MDIO_DEBUG((" (count was %d)\n", count)); - if ((in_be32(&emacp->em0stacr) & EMAC_STACR_OC) == 0) { + if ((stacr & EMAC_STACR_OC) == 0) { printk(KERN_WARNING "%s: PHY read timeout #1!\n", dev->name); return -1; } @@ -398,8 +414,11 @@ int emac_phy_read(struct net_device *dev, int mii_id, int reg) out_be32(&emacp->em0stacr, stacr); - udelay(MDIO_DELAY); - stacr = in_be32(&emacp->em0stacr); + count = 0; + while ((((stacr = in_be32(&emacp->em0stacr)) & EMAC_STACR_OC) == 0) + && (count++ < MDIO_DELAY)) + udelay(1); + MDIO_DEBUG((" (count was %d)\n", count)); if ((stacr & EMAC_STACR_OC) == 0) { printk(KERN_WARNING "%s: PHY read timeout #2!\n", dev->name); @@ -419,6 +438,7 @@ int emac_phy_read(struct net_device *dev, int mii_id, int reg) void emac_phy_write(struct net_device *dev, int mii_id, int reg, int data) { + int count; uint32_t stacr; struct ocp_enet_private *fep = dev->priv; emac_t *emacp = fep->emacp; @@ -437,9 +457,13 @@ void emac_phy_write(struct net_device *dev, int mii_id, int reg, int data) emacp = fep->emacp; } - udelay(MDIO_DELAY); + count = 0; + while ((((stacr = in_be32(&emacp->em0stacr)) & EMAC_STACR_OC) == 0) + && (count++ < MDIO_DELAY)) + udelay(1); + MDIO_DEBUG((" (count was %d)\n", count)); - if ((in_be32(&emacp->em0stacr) & EMAC_STACR_OC) == 0) { + if ((stacr & EMAC_STACR_OC) == 0) { printk(KERN_WARNING "%s: PHY write timeout #2!\n", dev->name); return; } @@ -451,9 +475,12 @@ void emac_phy_write(struct net_device *dev, int mii_id, int reg, int data) out_be32(&emacp->em0stacr, stacr); - udelay(MDIO_DELAY); + while (((stacr = in_be32(&emacp->em0stacr) & EMAC_STACR_OC) == 0) + && (count++ < 5000)) + udelay(1); + MDIO_DEBUG((" (count was %d)\n", count)); - if ((in_be32(&emacp->em0stacr) & EMAC_STACR_OC) == 0) + if ((stacr & EMAC_STACR_OC) == 0) printk(KERN_WARNING "%s: PHY write timeout #2!\n", dev->name); /* Check for a write error */ @@ -1940,8 +1967,6 @@ static struct ocp_driver emac_driver = { static int __init emac_init(void) { - int rc; - printk(KERN_INFO DRV_NAME ": " DRV_DESC ", version " DRV_VERSION "\n"); printk(KERN_INFO "Maintained by " DRV_AUTHOR "\n"); @@ -1950,13 +1975,8 @@ static int __init emac_init(void) skb_res); skb_res = 2; } - rc = ocp_register_driver(&emac_driver); - if (rc < 0) { - ocp_unregister_driver(&emac_driver); - return -ENODEV; - } - return 0; + return ocp_register_driver(&emac_driver); } static void __exit emac_exit(void) diff --git a/drivers/net/ibm_emac/ibm_emac_core.h b/drivers/net/ibm_emac/ibm_emac_core.h index 691ce4e5c..97e6e1ea8 100644 --- a/drivers/net/ibm_emac/ibm_emac_core.h +++ b/drivers/net/ibm_emac/ibm_emac_core.h @@ -67,7 +67,7 @@ #define TX_TIMEOUT (2*HZ) /* MDIO latency delay */ -#define MDIO_DELAY 50 +#define MDIO_DELAY 250 /* Power managment shift registers */ #define IBM_CPM_EMMII 0 /* Shift value for MII */ diff --git a/drivers/net/ibm_emac/ibm_emac_mal.c b/drivers/net/ibm_emac/ibm_emac_mal.c index 02d847cfa..6597791a7 100644 --- a/drivers/net/ibm_emac/ibm_emac_mal.c +++ b/drivers/net/ibm_emac/ibm_emac_mal.c @@ -55,8 +55,6 @@ int mal_register_commac(struct ibm_ocp_mal *mal, struct mal_commac *commac) write_unlock_irqrestore(&mal_list_lock, flags); - MOD_INC_USE_COUNT; - return 0; } @@ -73,8 +71,6 @@ int mal_unregister_commac(struct ibm_ocp_mal *mal, struct mal_commac *commac) write_unlock_irqrestore(&mal_list_lock, flags); - MOD_DEC_USE_COUNT; - return 0; } diff --git a/drivers/net/ibmveth.h b/drivers/net/ibmveth.h index 599d15b2f..51a470da9 100644 --- a/drivers/net/ibmveth.h +++ b/drivers/net/ibmveth.h @@ -77,6 +77,8 @@ #define IbmVethPool1DftCnt 256 #define IbmVethPool2DftCnt 256 +#define IBM_VETH_INVALID_MAP ((u16)0xffff) + struct ibmveth_buff_pool { u32 size; u32 index; diff --git a/drivers/net/ioc3-eth.c b/drivers/net/ioc3-eth.c index a93cda669..e16f8b20a 100644 --- a/drivers/net/ioc3-eth.c +++ b/drivers/net/ioc3-eth.c @@ -506,7 +506,7 @@ static struct net_device_stats *ioc3_get_stats(struct net_device *dev) static void ioc3_tcpudp_checksum(struct sk_buff *skb, uint32_t hwsum, int len) { - struct ethhdr *eh = skb->mac.ethernet; + struct ethhdr *eh = eth_hdr(skb); uint32_t csum, ehsum; unsigned int proto; struct iphdr *ih; @@ -1333,7 +1333,7 @@ static int ioc3_start_xmit(struct sk_buff *skb, struct net_device *dev) uint32_t csum, ehsum; uint16_t *eh; - /* The MAC header. skb->mac.ethernet seem the logic approach + /* The MAC header. skb->mac seem the logic approach to find the MAC header - except it's a NULL pointer ... */ eh = (uint16_t *) skb->data; diff --git a/drivers/net/mv643xx_eth.c b/drivers/net/mv643xx_eth.c new file mode 100644 index 000000000..41d38b3b3 --- /dev/null +++ b/drivers/net/mv643xx_eth.c @@ -0,0 +1,2646 @@ +/* + * drivers/net/mv64340_eth.c - Driver for MV64340X ethernet ports + * Copyright (C) 2002 Matthew Dharm + * + * Based on the 64360 driver from: + * Copyright (C) 2002 rabeeh@galileo.co.il + * + * Copyright (C) 2003 PMC-Sierra, Inc., + * written by Manish Lachwani (lachwani@pmc-sierra.com) + * + * Copyright (C) 2003 Ralf Baechle + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include "mv643xx_eth.h" + +/* + * The first part is the high level driver of the gigE ethernet ports. + */ + +/* Definition for configuring driver */ +#undef MV64340_RX_QUEUE_FILL_ON_TASK + +/* Constants */ +#define EXTRA_BYTES 32 +#define WRAP ETH_HLEN + 2 + 4 + 16 +#define BUFFER_MTU dev->mtu + WRAP +#define INT_CAUSE_UNMASK_ALL 0x0007ffff +#define INT_CAUSE_UNMASK_ALL_EXT 0x0011ffff +#ifdef MV64340_RX_FILL_ON_TASK +#define INT_CAUSE_MASK_ALL 0x00000000 +#define INT_CAUSE_CHECK_BITS INT_CAUSE_UNMASK_ALL +#define INT_CAUSE_CHECK_BITS_EXT INT_CAUSE_UNMASK_ALL_EXT +#endif + +/* Static function declarations */ +static int mv64340_eth_real_open(struct net_device *); +static int mv64340_eth_real_stop(struct net_device *); +static int mv64340_eth_change_mtu(struct net_device *, int); +static struct net_device_stats *mv64340_eth_get_stats(struct net_device *); +static void eth_port_init_mac_tables(unsigned int eth_port_num); +#ifdef MV64340_NAPI +static int mv64340_poll(struct net_device *dev, int *budget); +#endif + +unsigned char prom_mac_addr_base[6]; +unsigned long mv64340_sram_base; + +/* + * Changes MTU (maximum transfer unit) of the gigabit ethenret port + * + * Input : pointer to ethernet interface network device structure + * new mtu size + * Output : 0 upon success, -EINVAL upon failure + */ +static int mv64340_eth_change_mtu(struct net_device *dev, int new_mtu) +{ + struct mv64340_private *mp = netdev_priv(dev); + unsigned long flags; + + spin_lock_irqsave(&mp->lock, flags); + + if ((new_mtu > 9500) || (new_mtu < 64)) { + spin_unlock_irqrestore(&mp->lock, flags); + return -EINVAL; + } + + dev->mtu = new_mtu; + /* + * Stop then re-open the interface. This will allocate RX skb's with + * the new MTU. + * There is a possible danger that the open will not successed, due + * to memory is full, which might fail the open function. + */ + if (netif_running(dev)) { + if (mv64340_eth_real_stop(dev)) + printk(KERN_ERR + "%s: Fatal error on stopping device\n", + dev->name); + if (mv64340_eth_real_open(dev)) + printk(KERN_ERR + "%s: Fatal error on opening device\n", + dev->name); + } + + spin_unlock_irqrestore(&mp->lock, flags); + return 0; +} + +/* + * mv64340_eth_rx_task + * + * Fills / refills RX queue on a certain gigabit ethernet port + * + * Input : pointer to ethernet interface network device structure + * Output : N/A + */ +static void mv64340_eth_rx_task(void *data) +{ + struct net_device *dev = (struct net_device *) data; + struct mv64340_private *mp = netdev_priv(dev); + struct pkt_info pkt_info; + struct sk_buff *skb; + + if (test_and_set_bit(0, &mp->rx_task_busy)) + panic("%s: Error in test_set_bit / clear_bit", dev->name); + + while (mp->rx_ring_skbs < (mp->rx_ring_size - 5)) { + /* The +8 for buffer allignment and another 32 byte extra */ + + skb = dev_alloc_skb(BUFFER_MTU + 8 + EXTRA_BYTES); + if (!skb) + /* Better luck next time */ + break; + mp->rx_ring_skbs++; + pkt_info.cmd_sts = ETH_RX_ENABLE_INTERRUPT; + pkt_info.byte_cnt = dev->mtu + ETH_HLEN + 4 + 2 + EXTRA_BYTES; + /* Allign buffer to 8 bytes */ + if (pkt_info.byte_cnt & ~0x7) { + pkt_info.byte_cnt &= ~0x7; + pkt_info.byte_cnt += 8; + } + pkt_info.buf_ptr = + pci_map_single(0, skb->data, + dev->mtu + ETH_HLEN + 4 + 2 + EXTRA_BYTES, + PCI_DMA_FROMDEVICE); + pkt_info.return_info = skb; + if (eth_rx_return_buff(mp, &pkt_info) != ETH_OK) { + printk(KERN_ERR + "%s: Error allocating RX Ring\n", dev->name); + break; + } + skb_reserve(skb, 2); + } + clear_bit(0, &mp->rx_task_busy); + /* + * If RX ring is empty of SKB, set a timer to try allocating + * again in a later time . + */ + if ((mp->rx_ring_skbs == 0) && (mp->rx_timer_flag == 0)) { + printk(KERN_INFO "%s: Rx ring is empty\n", dev->name); + /* After 100mSec */ + mp->timeout.expires = jiffies + (HZ / 10); + add_timer(&mp->timeout); + mp->rx_timer_flag = 1; + } +#if MV64340_RX_QUEUE_FILL_ON_TASK + else { + /* Return interrupts */ + MV_WRITE(MV64340_ETH_INTERRUPT_MASK_REG(mp->port_num), + INT_CAUSE_UNMASK_ALL); + } +#endif +} + +/* + * mv64340_eth_rx_task_timer_wrapper + * + * Timer routine to wake up RX queue filling task. This function is + * used only in case the RX queue is empty, and all alloc_skb has + * failed (due to out of memory event). + * + * Input : pointer to ethernet interface network device structure + * Output : N/A + */ +static void mv64340_eth_rx_task_timer_wrapper(unsigned long data) +{ + struct net_device *dev = (struct net_device *) data; + struct mv64340_private *mp = netdev_priv(dev); + + mp->rx_timer_flag = 0; + mv64340_eth_rx_task((void *) data); +} + + +/* + * mv64340_eth_update_mac_address + * + * Update the MAC address of the port in the address table + * + * Input : pointer to ethernet interface network device structure + * Output : N/A + */ +static void mv64340_eth_update_mac_address(struct net_device *dev) +{ + struct mv64340_private *mp = netdev_priv(dev); + unsigned int port_num = mp->port_num; + + eth_port_init_mac_tables(port_num); + memcpy(mp->port_mac_addr, dev->dev_addr, 6); + eth_port_uc_addr_set(port_num, mp->port_mac_addr); +} + +/* + * mv64340_eth_set_rx_mode + * + * Change from promiscuos to regular rx mode + * + * Input : pointer to ethernet interface network device structure + * Output : N/A + */ +static void mv64340_eth_set_rx_mode(struct net_device *dev) +{ + struct mv64340_private *mp = netdev_priv(dev); + + if (dev->flags & IFF_PROMISC) { + ethernet_set_config_reg + (mp->port_num, + ethernet_get_config_reg(mp->port_num) | + ETH_UNICAST_PROMISCUOUS_MODE); + } else { + ethernet_set_config_reg + (mp->port_num, + ethernet_get_config_reg(mp->port_num) & + ~(unsigned int) ETH_UNICAST_PROMISCUOUS_MODE); + } +} + + +/* + * mv64340_eth_set_mac_address + * + * Change the interface's mac address. + * No special hardware thing should be done because interface is always + * put in promiscuous mode. + * + * Input : pointer to ethernet interface network device structure and + * a pointer to the designated entry to be added to the cache. + * Output : zero upon success, negative upon failure + */ +static int mv64340_eth_set_mac_address(struct net_device *dev, void *addr) +{ + int i; + + for (i = 0; i < 6; i++) + /* +2 is for the offset of the HW addr type */ + dev->dev_addr[i] = ((unsigned char *) addr)[i + 2]; + mv64340_eth_update_mac_address(dev); + return 0; +} + +/* + * mv64340_eth_tx_timeout + * + * Called upon a timeout on transmitting a packet + * + * Input : pointer to ethernet interface network device structure. + * Output : N/A + */ +static void mv64340_eth_tx_timeout(struct net_device *dev) +{ + struct mv64340_private *mp = netdev_priv(dev); + + printk(KERN_INFO "%s: TX timeout ", dev->name); + + /* Do the reset outside of interrupt context */ + schedule_work(&mp->tx_timeout_task); +} + +/* + * mv64340_eth_tx_timeout_task + * + * Actual routine to reset the adapter when a timeout on Tx has occurred + */ +static void mv64340_eth_tx_timeout_task(struct net_device *dev) +{ + struct mv64340_private *mp = netdev_priv(dev); + + netif_device_detach(dev); + eth_port_reset(mp->port_num); + eth_port_start(mp); + netif_device_attach(dev); +} + +/* + * mv64340_eth_free_tx_queue + * + * Input : dev - a pointer to the required interface + * + * Output : 0 if was able to release skb , nonzero otherwise + */ +static int mv64340_eth_free_tx_queue(struct net_device *dev, + unsigned int eth_int_cause_ext) +{ + struct mv64340_private *mp = netdev_priv(dev); + struct net_device_stats *stats = &mp->stats; + struct pkt_info pkt_info; + int released = 1; + + if (!(eth_int_cause_ext & (BIT0 | BIT8))) + return released; + + spin_lock(&mp->lock); + + /* Check only queue 0 */ + while (eth_tx_return_desc(mp, &pkt_info) == ETH_OK) { + if (pkt_info.cmd_sts & BIT0) { + printk("%s: Error in TX\n", dev->name); + stats->tx_errors++; + } + + /* + * If return_info is different than 0, release the skb. + * The case where return_info is not 0 is only in case + * when transmitted a scatter/gather packet, where only + * last skb releases the whole chain. + */ + if (pkt_info.return_info) { + dev_kfree_skb_irq((struct sk_buff *) + pkt_info.return_info); + released = 0; + if (skb_shinfo(pkt_info.return_info)->nr_frags) + pci_unmap_page(NULL, pkt_info.buf_ptr, + pkt_info.byte_cnt, PCI_DMA_TODEVICE); + + if (mp->tx_ring_skbs != 1) + mp->tx_ring_skbs--; + } else + pci_unmap_page(NULL, pkt_info.buf_ptr, + pkt_info.byte_cnt, PCI_DMA_TODEVICE); + + /* + * Decrement the number of outstanding skbs counter on + * the TX queue. + */ + if (mp->tx_ring_skbs == 0) + panic("ERROR - TX outstanding SKBs counter is corrupted"); + + } + + spin_unlock(&mp->lock); + + return released; +} + +/* + * mv64340_eth_receive + * + * This function is forward packets that are received from the port's + * queues toward kernel core or FastRoute them to another interface. + * + * Input : dev - a pointer to the required interface + * max - maximum number to receive (0 means unlimted) + * + * Output : number of served packets + */ +#ifdef MV64340_NAPI +static int mv64340_eth_receive_queue(struct net_device *dev, unsigned int max, + int budget) +#else +static int mv64340_eth_receive_queue(struct net_device *dev, unsigned int max) +#endif +{ + struct mv64340_private *mp = netdev_priv(dev); + struct net_device_stats *stats = &mp->stats; + unsigned int received_packets = 0; + struct sk_buff *skb; + struct pkt_info pkt_info; + +#ifdef MV64340_NAPI + while (eth_port_receive(mp, &pkt_info) == ETH_OK && budget > 0) { +#else + while ((--max) && eth_port_receive(mp, &pkt_info) == ETH_OK) { +#endif + mp->rx_ring_skbs--; + received_packets++; +#ifdef MV64340_NAPI + budget--; +#endif + /* Update statistics. Note byte count includes 4 byte CRC count */ + stats->rx_packets++; + stats->rx_bytes += pkt_info.byte_cnt; + skb = (struct sk_buff *) pkt_info.return_info; + /* + * In case received a packet without first / last bits on OR + * the error summary bit is on, the packets needs to be dropeed. + */ + if (((pkt_info.cmd_sts + & (ETH_RX_FIRST_DESC | ETH_RX_LAST_DESC)) != + (ETH_RX_FIRST_DESC | ETH_RX_LAST_DESC)) + || (pkt_info.cmd_sts & ETH_ERROR_SUMMARY)) { + stats->rx_dropped++; + if ((pkt_info.cmd_sts & (ETH_RX_FIRST_DESC | + ETH_RX_LAST_DESC)) != + (ETH_RX_FIRST_DESC | ETH_RX_LAST_DESC)) { + if (net_ratelimit()) + printk(KERN_ERR + "%s: Received packet spread on multiple" + " descriptors\n", + dev->name); + } + if (pkt_info.cmd_sts & ETH_ERROR_SUMMARY) + stats->rx_errors++; + + dev_kfree_skb_irq(skb); + } else { + /* + * The -4 is for the CRC in the trailer of the + * received packet + */ + skb_put(skb, pkt_info.byte_cnt - 4); + skb->dev = dev; + + if (pkt_info.cmd_sts & ETH_LAYER_4_CHECKSUM_OK) { + skb->ip_summed = CHECKSUM_UNNECESSARY; + skb->csum = htons((pkt_info.cmd_sts + & 0x0007fff8) >> 3); + } + skb->protocol = eth_type_trans(skb, dev); +#ifdef MV64340_NAPI + netif_receive_skb(skb); +#else + netif_rx(skb); +#endif + } + } + + return received_packets; +} + +/* + * mv64340_eth_int_handler + * + * Main interrupt handler for the gigbit ethernet ports + * + * Input : irq - irq number (not used) + * dev_id - a pointer to the required interface's data structure + * regs - not used + * Output : N/A + */ + +static irqreturn_t mv64340_eth_int_handler(int irq, void *dev_id, + struct pt_regs *regs) +{ + struct net_device *dev = (struct net_device *) dev_id; + struct mv64340_private *mp = netdev_priv(dev); + u32 eth_int_cause, eth_int_cause_ext = 0; + unsigned int port_num = mp->port_num; + + /* Read interrupt cause registers */ + eth_int_cause = MV_READ(MV64340_ETH_INTERRUPT_CAUSE_REG(port_num)) & + INT_CAUSE_UNMASK_ALL; + + if (eth_int_cause & BIT1) + eth_int_cause_ext = + MV_READ(MV64340_ETH_INTERRUPT_CAUSE_EXTEND_REG(port_num)) & + INT_CAUSE_UNMASK_ALL_EXT; + +#ifdef MV64340_NAPI + if (!(eth_int_cause & 0x0007fffd)) { + /* Dont ack the Rx interrupt */ +#endif + /* + * Clear specific ethernet port intrerrupt registers by + * acknowleding relevant bits. + */ + MV_WRITE(MV64340_ETH_INTERRUPT_CAUSE_REG(port_num), + ~eth_int_cause); + if (eth_int_cause_ext != 0x0) + MV_WRITE(MV64340_ETH_INTERRUPT_CAUSE_EXTEND_REG(port_num), + ~eth_int_cause_ext); + + /* UDP change : We may need this */ + if ((eth_int_cause_ext & 0x0000ffff) && + (mv64340_eth_free_tx_queue(dev, eth_int_cause_ext) == 0) && + (MV64340_TX_QUEUE_SIZE > mp->tx_ring_skbs + 1)) + netif_wake_queue(dev); +#ifdef MV64340_NAPI + } else { + if (netif_rx_schedule_prep(dev)) { + /* Mask all the interrupts */ + MV_WRITE(MV64340_ETH_INTERRUPT_MASK_REG(port_num),0); + MV_WRITE(MV64340_ETH_INTERRUPT_EXTEND_MASK_REG(port_num), 0); + __netif_rx_schedule(dev); + } +#else + { + if (eth_int_cause & (BIT2 | BIT11)) + mv64340_eth_receive_queue(dev, 0); + + /* + * After forwarded received packets to upper layer, add a task + * in an interrupts enabled context that refills the RX ring + * with skb's. + */ +#if MV64340_RX_QUEUE_FILL_ON_TASK + /* Unmask all interrupts on ethernet port */ + MV_WRITE(MV64340_ETH_INTERRUPT_MASK_REG(port_num), + INT_CAUSE_MASK_ALL); + queue_task(&mp->rx_task, &tq_immediate); + mark_bh(IMMEDIATE_BH); +#else + mp->rx_task.func(dev); +#endif +#endif + } + /* PHY status changed */ + if (eth_int_cause_ext & (BIT16 | BIT20)) { + unsigned int phy_reg_data; + + /* Check Link status on ethernet port */ + eth_port_read_smi_reg(port_num, 1, &phy_reg_data); + if (!(phy_reg_data & 0x20)) { + netif_stop_queue(dev); + } else { + netif_wake_queue(dev); + + /* + * Start all TX queues on ethernet port. This is good in + * case of previous packets where not transmitted, due + * to link down and this command re-enables all TX + * queues. + * Note that it is possible to get a TX resource error + * interrupt after issuing this, since not all TX queues + * are enabled, or has anything to send. + */ + MV_WRITE(MV64340_ETH_TRANSMIT_QUEUE_COMMAND_REG(port_num), 1); + } + } + + /* + * If no real interrupt occured, exit. + * This can happen when using gigE interrupt coalescing mechanism. + */ + if ((eth_int_cause == 0x0) && (eth_int_cause_ext == 0x0)) + return IRQ_NONE; + + return IRQ_HANDLED; +} + +#ifdef MV64340_COAL + +/* + * eth_port_set_rx_coal - Sets coalescing interrupt mechanism on RX path + * + * DESCRIPTION: + * This routine sets the RX coalescing interrupt mechanism parameter. + * This parameter is a timeout counter, that counts in 64 t_clk + * chunks ; that when timeout event occurs a maskable interrupt + * occurs. + * The parameter is calculated using the tClk of the MV-643xx chip + * , and the required delay of the interrupt in usec. + * + * INPUT: + * unsigned int eth_port_num Ethernet port number + * unsigned int t_clk t_clk of the MV-643xx chip in HZ units + * unsigned int delay Delay in usec + * + * OUTPUT: + * Interrupt coalescing mechanism value is set in MV-643xx chip. + * + * RETURN: + * The interrupt coalescing value set in the gigE port. + * + */ +static unsigned int eth_port_set_rx_coal(unsigned int eth_port_num, + unsigned int t_clk, unsigned int delay) +{ + unsigned int coal = ((t_clk / 1000000) * delay) / 64; + + /* Set RX Coalescing mechanism */ + MV_WRITE(MV64340_ETH_SDMA_CONFIG_REG(eth_port_num), + ((coal & 0x3fff) << 8) | + (MV_READ(MV64340_ETH_SDMA_CONFIG_REG(eth_port_num)) + & 0xffc000ff)); + + return coal; +} +#endif + +/* + * eth_port_set_tx_coal - Sets coalescing interrupt mechanism on TX path + * + * DESCRIPTION: + * This routine sets the TX coalescing interrupt mechanism parameter. + * This parameter is a timeout counter, that counts in 64 t_clk + * chunks ; that when timeout event occurs a maskable interrupt + * occurs. + * The parameter is calculated using the t_cLK frequency of the + * MV-643xx chip and the required delay in the interrupt in uSec + * + * INPUT: + * unsigned int eth_port_num Ethernet port number + * unsigned int t_clk t_clk of the MV-643xx chip in HZ units + * unsigned int delay Delay in uSeconds + * + * OUTPUT: + * Interrupt coalescing mechanism value is set in MV-643xx chip. + * + * RETURN: + * The interrupt coalescing value set in the gigE port. + * + */ +static unsigned int eth_port_set_tx_coal(unsigned int eth_port_num, + unsigned int t_clk, unsigned int delay) +{ + unsigned int coal; + coal = ((t_clk / 1000000) * delay) / 64; + /* Set TX Coalescing mechanism */ + MV_WRITE(MV64340_ETH_TX_FIFO_URGENT_THRESHOLD_REG(eth_port_num), + coal << 4); + return coal; +} + +/* + * mv64340_eth_open + * + * This function is called when openning the network device. The function + * should initialize all the hardware, initialize cyclic Rx/Tx + * descriptors chain and buffers and allocate an IRQ to the network + * device. + * + * Input : a pointer to the network device structure + * + * Output : zero of success , nonzero if fails. + */ + +static int mv64340_eth_open(struct net_device *dev) +{ + struct mv64340_private *mp = netdev_priv(dev); + unsigned int port_num = mp->port_num; + int err = err; + + spin_lock_irq(&mp->lock); + + err = request_irq(dev->irq, mv64340_eth_int_handler, + SA_INTERRUPT | SA_SAMPLE_RANDOM, dev->name, dev); + + if (err) { + printk(KERN_ERR "Can not assign IRQ number to MV64340_eth%d\n", + port_num); + err = -EAGAIN; + goto out; + } + + if (mv64340_eth_real_open(dev)) { + printk("%s: Error opening interface\n", dev->name); + err = -EBUSY; + goto out_free; + } + + spin_unlock_irq(&mp->lock); + + return 0; + +out_free: + free_irq(dev->irq, dev); + +out: + spin_unlock_irq(&mp->lock); + + return err; +} + +/* + * ether_init_rx_desc_ring - Curve a Rx chain desc list and buffer in memory. + * + * DESCRIPTION: + * This function prepares a Rx chained list of descriptors and packet + * buffers in a form of a ring. The routine must be called after port + * initialization routine and before port start routine. + * The Ethernet SDMA engine uses CPU bus addresses to access the various + * devices in the system (i.e. DRAM). This function uses the ethernet + * struct 'virtual to physical' routine (set by the user) to set the ring + * with physical addresses. + * + * INPUT: + * struct mv64340_private *mp Ethernet Port Control srtuct. + * int rx_desc_num Number of Rx descriptors + * int rx_buff_size Size of Rx buffer + * unsigned int rx_desc_base_addr Rx descriptors memory area base addr. + * unsigned int rx_buff_base_addr Rx buffer memory area base addr. + * + * OUTPUT: + * The routine updates the Ethernet port control struct with information + * regarding the Rx descriptors and buffers. + * + * RETURN: + * false if the given descriptors memory area is not aligned according to + * Ethernet SDMA specifications. + * true otherwise. + */ +static int ether_init_rx_desc_ring(struct mv64340_private * mp, + unsigned long rx_buff_base_addr) +{ + unsigned long buffer_addr = rx_buff_base_addr; + volatile struct eth_rx_desc *p_rx_desc; + int rx_desc_num = mp->rx_ring_size; + unsigned long rx_desc_base_addr = (unsigned long) mp->p_rx_desc_area; + int rx_buff_size = 1536; /* Dummy, will be replaced later */ + int i; + + p_rx_desc = (struct eth_rx_desc *) rx_desc_base_addr; + + /* Rx desc Must be 4LW aligned (i.e. Descriptor_Address[3:0]=0000). */ + if (rx_buff_base_addr & 0xf) + return 0; + + /* Rx buffers are limited to 64K bytes and Minimum size is 8 bytes */ + if ((rx_buff_size < 8) || (rx_buff_size > RX_BUFFER_MAX_SIZE)) + return 0; + + /* Rx buffers must be 64-bit aligned. */ + if ((rx_buff_base_addr + rx_buff_size) & 0x7) + return 0; + + /* initialize the Rx descriptors ring */ + for (i = 0; i < rx_desc_num; i++) { + p_rx_desc[i].buf_size = rx_buff_size; + p_rx_desc[i].byte_cnt = 0x0000; + p_rx_desc[i].cmd_sts = + ETH_BUFFER_OWNED_BY_DMA | ETH_RX_ENABLE_INTERRUPT; + p_rx_desc[i].next_desc_ptr = mp->rx_desc_dma + + ((i + 1) % rx_desc_num) * sizeof(struct eth_rx_desc); + p_rx_desc[i].buf_ptr = buffer_addr; + + mp->rx_skb[i] = NULL; + buffer_addr += rx_buff_size; + } + + /* Save Rx desc pointer to driver struct. */ + mp->rx_curr_desc_q = 0; + mp->rx_used_desc_q = 0; + + mp->rx_desc_area_size = rx_desc_num * sizeof(struct eth_rx_desc); + + mp->port_rx_queue_command |= 1; + + return 1; +} + +/* + * ether_init_tx_desc_ring - Curve a Tx chain desc list and buffer in memory. + * + * DESCRIPTION: + * This function prepares a Tx chained list of descriptors and packet + * buffers in a form of a ring. The routine must be called after port + * initialization routine and before port start routine. + * The Ethernet SDMA engine uses CPU bus addresses to access the various + * devices in the system (i.e. DRAM). This function uses the ethernet + * struct 'virtual to physical' routine (set by the user) to set the ring + * with physical addresses. + * + * INPUT: + * struct mv64340_private *mp Ethernet Port Control srtuct. + * int tx_desc_num Number of Tx descriptors + * int tx_buff_size Size of Tx buffer + * unsigned int tx_desc_base_addr Tx descriptors memory area base addr. + * + * OUTPUT: + * The routine updates the Ethernet port control struct with information + * regarding the Tx descriptors and buffers. + * + * RETURN: + * false if the given descriptors memory area is not aligned according to + * Ethernet SDMA specifications. + * true otherwise. + */ +static int ether_init_tx_desc_ring(struct mv64340_private *mp) +{ + unsigned long tx_desc_base_addr = (unsigned long) mp->p_tx_desc_area; + int tx_desc_num = mp->tx_ring_size; + struct eth_tx_desc *p_tx_desc; + int i; + + /* Tx desc Must be 4LW aligned (i.e. Descriptor_Address[3:0]=0000). */ + if (tx_desc_base_addr & 0xf) + return 0; + + /* save the first desc pointer to link with the last descriptor */ + p_tx_desc = (struct eth_tx_desc *) tx_desc_base_addr; + + /* Initialize the Tx descriptors ring */ + for (i = 0; i < tx_desc_num; i++) { + p_tx_desc[i].byte_cnt = 0x0000; + p_tx_desc[i].l4i_chk = 0x0000; + p_tx_desc[i].cmd_sts = 0x00000000; + p_tx_desc[i].next_desc_ptr = mp->tx_desc_dma + + ((i + 1) % tx_desc_num) * sizeof(struct eth_tx_desc); + p_tx_desc[i].buf_ptr = 0x00000000; + mp->tx_skb[i] = NULL; + } + + /* Set Tx desc pointer in driver struct. */ + mp->tx_curr_desc_q = 0; + mp->tx_used_desc_q = 0; +#ifdef MV64340_CHECKSUM_OFFLOAD_TX + mp->tx_first_desc_q = 0; +#endif + /* Init Tx ring base and size parameters */ + mp->tx_desc_area_size = tx_desc_num * sizeof(struct eth_tx_desc); + + /* Add the queue to the list of Tx queues of this port */ + mp->port_tx_queue_command |= 1; + + return 1; +} + +/* Helper function for mv64340_eth_open */ +static int mv64340_eth_real_open(struct net_device *dev) +{ + struct mv64340_private *mp = netdev_priv(dev); + unsigned int port_num = mp->port_num; + u32 phy_reg_data; + unsigned int size; + + /* Stop RX Queues */ + MV_WRITE(MV64340_ETH_RECEIVE_QUEUE_COMMAND_REG(port_num), + 0x0000ff00); + + /* Clear the ethernet port interrupts */ + MV_WRITE(MV64340_ETH_INTERRUPT_CAUSE_REG(port_num), 0); + MV_WRITE(MV64340_ETH_INTERRUPT_CAUSE_EXTEND_REG(port_num), 0); + + /* Unmask RX buffer and TX end interrupt */ + MV_WRITE(MV64340_ETH_INTERRUPT_MASK_REG(port_num), + INT_CAUSE_UNMASK_ALL); + + /* Unmask phy and link status changes interrupts */ + MV_WRITE(MV64340_ETH_INTERRUPT_EXTEND_MASK_REG(port_num), + INT_CAUSE_UNMASK_ALL_EXT); + + /* Set the MAC Address */ + memcpy(mp->port_mac_addr, dev->dev_addr, 6); + + eth_port_init(mp); + + INIT_WORK(&mp->rx_task, (void (*)(void *)) mv64340_eth_rx_task, dev); + + memset(&mp->timeout, 0, sizeof(struct timer_list)); + mp->timeout.function = mv64340_eth_rx_task_timer_wrapper; + mp->timeout.data = (unsigned long) dev; + + mp->rx_task_busy = 0; + mp->rx_timer_flag = 0; + + /* Allocate TX ring */ + mp->tx_ring_skbs = 0; + mp->tx_ring_size = MV64340_TX_QUEUE_SIZE; + size = mp->tx_ring_size * sizeof(struct eth_tx_desc); + mp->tx_desc_area_size = size; + + /* Assumes allocated ring is 16 bytes alligned */ + mp->p_tx_desc_area = pci_alloc_consistent(NULL, size, &mp->tx_desc_dma); + if (!mp->p_tx_desc_area) { + printk(KERN_ERR "%s: Cannot allocate Tx Ring (size %d bytes)\n", + dev->name, size); + return -ENOMEM; + } + memset((void *) mp->p_tx_desc_area, 0, mp->tx_desc_area_size); + + /* Dummy will be replaced upon real tx */ + ether_init_tx_desc_ring(mp); + + /* Allocate RX ring */ + /* Meantime RX Ring are fixed - but must be configurable by user */ + mp->rx_ring_size = MV64340_RX_QUEUE_SIZE; + mp->rx_ring_skbs = 0; + size = mp->rx_ring_size * sizeof(struct eth_rx_desc); + mp->rx_desc_area_size = size; + + /* Assumes allocated ring is 16 bytes aligned */ + + mp->p_rx_desc_area = pci_alloc_consistent(NULL, size, &mp->rx_desc_dma); + + if (!mp->p_rx_desc_area) { + printk(KERN_ERR "%s: Cannot allocate Rx ring (size %d bytes)\n", + dev->name, size); + printk(KERN_ERR "%s: Freeing previously allocated TX queues...", + dev->name); + pci_free_consistent(0, mp->tx_desc_area_size, + (void *) mp->p_tx_desc_area, + mp->tx_desc_dma); + return -ENOMEM; + } + memset(mp->p_rx_desc_area, 0, size); + + if (!(ether_init_rx_desc_ring(mp, 0))) + panic("%s: Error initializing RX Ring", dev->name); + + mv64340_eth_rx_task(dev); /* Fill RX ring with skb's */ + + eth_port_start(mp); + + /* Interrupt Coalescing */ + +#ifdef MV64340_COAL + mp->rx_int_coal = + eth_port_set_rx_coal(port_num, 133000000, MV64340_RX_COAL); +#endif + + mp->tx_int_coal = + eth_port_set_tx_coal (port_num, 133000000, MV64340_TX_COAL); + + /* Increase the Rx side buffer size */ + + MV_WRITE (MV64340_ETH_PORT_SERIAL_CONTROL_REG(port_num), (0x5 << 17) | + (MV_READ(MV64340_ETH_PORT_SERIAL_CONTROL_REG(port_num)) + & 0xfff1ffff)); + + /* Check Link status on phy */ + eth_port_read_smi_reg(port_num, 1, &phy_reg_data); + if (!(phy_reg_data & 0x20)) + netif_stop_queue(dev); + else + netif_start_queue(dev); + + return 0; +} + +static void mv64340_eth_free_tx_rings(struct net_device *dev) +{ + struct mv64340_private *mp = netdev_priv(dev); + unsigned int port_num = mp->port_num; + unsigned int curr; + + /* Stop Tx Queues */ + MV_WRITE(MV64340_ETH_TRANSMIT_QUEUE_COMMAND_REG(port_num), + 0x0000ff00); + + /* Free TX rings */ + /* Free outstanding skb's on TX rings */ + for (curr = 0; + (mp->tx_ring_skbs) && (curr < MV64340_TX_QUEUE_SIZE); + curr++) { + if (mp->tx_skb[curr]) { + dev_kfree_skb(mp->tx_skb[curr]); + mp->tx_ring_skbs--; + } + } + if (mp->tx_ring_skbs != 0) + printk("%s: Error on Tx descriptor free - could not free %d" + " descriptors\n", dev->name, + mp->tx_ring_skbs); + pci_free_consistent(0, mp->tx_desc_area_size, + (void *) mp->p_tx_desc_area, mp->tx_desc_dma); +} + +static void mv64340_eth_free_rx_rings(struct net_device *dev) +{ + struct mv64340_private *mp = netdev_priv(dev); + unsigned int port_num = mp->port_num; + int curr; + + /* Stop RX Queues */ + MV_WRITE(MV64340_ETH_RECEIVE_QUEUE_COMMAND_REG(port_num), + 0x0000ff00); + + /* Free RX rings */ + /* Free preallocated skb's on RX rings */ + for (curr = 0; + mp->rx_ring_skbs && (curr < MV64340_RX_QUEUE_SIZE); + curr++) { + if (mp->rx_skb[curr]) { + dev_kfree_skb(mp->rx_skb[curr]); + mp->rx_ring_skbs--; + } + } + + if (mp->rx_ring_skbs != 0) + printk(KERN_ERR + "%s: Error in freeing Rx Ring. %d skb's still" + " stuck in RX Ring - ignoring them\n", dev->name, + mp->rx_ring_skbs); + pci_free_consistent(0, mp->rx_desc_area_size, + (void *) mp->p_rx_desc_area, + mp->rx_desc_dma); +} + +/* + * mv64340_eth_stop + * + * This function is used when closing the network device. + * It updates the hardware, + * release all memory that holds buffers and descriptors and release the IRQ. + * Input : a pointer to the device structure + * Output : zero if success , nonzero if fails + */ + +/* Helper function for mv64340_eth_stop */ + +static int mv64340_eth_real_stop(struct net_device *dev) +{ + struct mv64340_private *mp = netdev_priv(dev); + unsigned int port_num = mp->port_num; + + netif_stop_queue(dev); + + mv64340_eth_free_tx_rings(dev); + mv64340_eth_free_rx_rings(dev); + + eth_port_reset(mp->port_num); + + /* Disable ethernet port interrupts */ + MV_WRITE(MV64340_ETH_INTERRUPT_CAUSE_REG(port_num), 0); + MV_WRITE(MV64340_ETH_INTERRUPT_CAUSE_EXTEND_REG(port_num), 0); + + /* Mask RX buffer and TX end interrupt */ + MV_WRITE(MV64340_ETH_INTERRUPT_MASK_REG(port_num), 0); + + /* Mask phy and link status changes interrupts */ + MV_WRITE(MV64340_ETH_INTERRUPT_EXTEND_MASK_REG(port_num), 0); + + return 0; +} + +static int mv64340_eth_stop(struct net_device *dev) +{ + struct mv64340_private *mp = netdev_priv(dev); + + spin_lock_irq(&mp->lock); + + mv64340_eth_real_stop(dev); + + free_irq(dev->irq, dev); + spin_unlock_irq(&mp->lock); + + return 0; +} + +#ifdef MV64340_NAPI +static void mv64340_tx(struct net_device *dev) +{ + struct mv64340_private *mp = netdev_priv(dev); + struct pkt_info pkt_info; + + while (eth_tx_return_desc(mp, &pkt_info) == ETH_OK) { + if (pkt_info.return_info) { + dev_kfree_skb_irq((struct sk_buff *) + pkt_info.return_info); + if (skb_shinfo(pkt_info.return_info)->nr_frags) + pci_unmap_page(NULL, pkt_info.buf_ptr, + pkt_info.byte_cnt, + PCI_DMA_TODEVICE); + + if (mp->tx_ring_skbs != 1) + mp->tx_ring_skbs--; + } else + pci_unmap_page(NULL, pkt_info.buf_ptr, pkt_info.byte_cnt, + PCI_DMA_TODEVICE); + } + + if (netif_queue_stopped(dev) && + MV64340_TX_QUEUE_SIZE > mp->tx_ring_skbs + 1) + netif_wake_queue(dev); +} + +/* + * mv64340_poll + * + * This function is used in case of NAPI + */ +static int mv64340_poll(struct net_device *dev, int *budget) +{ + struct mv64340_private *mp = netdev_priv(dev); + int done = 1, orig_budget, work_done; + unsigned int port_num = mp->port_num; + unsigned long flags; + +#ifdef MV64340_TX_FAST_REFILL + if (++mp->tx_clean_threshold > 5) { + spin_lock_irqsave(&mp->lock, flags); + mv64340_tx(dev); + mp->tx_clean_threshold = 0; + spin_unlock_irqrestore(&mp->lock, flags); + } +#endif + + if ((u32)(MV_READ(MV64340_ETH_RX_CURRENT_QUEUE_DESC_PTR_0(port_num))) != (u32)mp->rx_used_desc_q) { + orig_budget = *budget; + if (orig_budget > dev->quota) + orig_budget = dev->quota; + work_done = mv64340_eth_receive_queue(dev, 0, orig_budget); + mp->rx_task.func(dev); + *budget -= work_done; + dev->quota -= work_done; + if (work_done >= orig_budget) + done = 0; + } + + if (done) { + spin_lock_irqsave(&mp->lock, flags); + __netif_rx_complete(dev); + MV_WRITE(MV64340_ETH_INTERRUPT_CAUSE_REG(port_num),0); + MV_WRITE(MV64340_ETH_INTERRUPT_CAUSE_EXTEND_REG(port_num),0); + MV_WRITE(MV64340_ETH_INTERRUPT_MASK_REG(port_num), + INT_CAUSE_UNMASK_ALL); + MV_WRITE(MV64340_ETH_INTERRUPT_EXTEND_MASK_REG(port_num), + INT_CAUSE_UNMASK_ALL_EXT); + spin_unlock_irqrestore(&mp->lock, flags); + } + + return done ? 0 : 1; +} +#endif + +/* + * mv64340_eth_start_xmit + * + * This function is queues a packet in the Tx descriptor for + * required port. + * + * Input : skb - a pointer to socket buffer + * dev - a pointer to the required port + * + * Output : zero upon success + */ +static int mv64340_eth_start_xmit(struct sk_buff *skb, struct net_device *dev) +{ + struct mv64340_private *mp = netdev_priv(dev); + struct net_device_stats *stats = &mp->stats; + ETH_FUNC_RET_STATUS status; + unsigned long flags; + struct pkt_info pkt_info; + + if (netif_queue_stopped(dev)) { + printk(KERN_ERR + "%s: Tried sending packet when interface is stopped\n", + dev->name); + return 1; + } + + /* This is a hard error, log it. */ + if ((MV64340_TX_QUEUE_SIZE - mp->tx_ring_skbs) <= + (skb_shinfo(skb)->nr_frags + 1)) { + netif_stop_queue(dev); + printk(KERN_ERR + "%s: Bug in mv64340_eth - Trying to transmit when" + " queue full !\n", dev->name); + return 1; + } + + /* Paranoid check - this shouldn't happen */ + if (skb == NULL) { + stats->tx_dropped++; + return 1; + } + + spin_lock_irqsave(&mp->lock, flags); + + /* Update packet info data structure -- DMA owned, first last */ +#ifdef MV64340_CHECKSUM_OFFLOAD_TX + if (!skb_shinfo(skb)->nr_frags || (skb_shinfo(skb)->nr_frags > 3)) { +#endif + pkt_info.cmd_sts = ETH_TX_ENABLE_INTERRUPT | + ETH_TX_FIRST_DESC | ETH_TX_LAST_DESC; + + pkt_info.byte_cnt = skb->len; + pkt_info.buf_ptr = pci_map_single(0, skb->data, skb->len, + PCI_DMA_TODEVICE); + + + pkt_info.return_info = skb; + status = eth_port_send(mp, &pkt_info); + if ((status == ETH_ERROR) || (status == ETH_QUEUE_FULL)) + printk(KERN_ERR "%s: Error on transmitting packet\n", + dev->name); + mp->tx_ring_skbs++; +#ifdef MV64340_CHECKSUM_OFFLOAD_TX + } else { + unsigned int frag; + u32 ipheader; + + /* first frag which is skb header */ + pkt_info.byte_cnt = skb_headlen(skb); + pkt_info.buf_ptr = pci_map_single(0, skb->data, + skb_headlen(skb), PCI_DMA_TODEVICE); + pkt_info.return_info = 0; + ipheader = skb->nh.iph->ihl << 11; + pkt_info.cmd_sts = ETH_TX_FIRST_DESC | + ETH_GEN_TCP_UDP_CHECKSUM | + ETH_GEN_IP_V_4_CHECKSUM | + ipheader; + /* CPU already calculated pseudo header checksum. So, use it */ + pkt_info.l4i_chk = skb->h.th->check; + status = eth_port_send(mp, &pkt_info); + if (status != ETH_OK) { + if ((status == ETH_ERROR)) + printk(KERN_ERR "%s: Error on transmitting packet\n", dev->name); + if (status == ETH_QUEUE_FULL) + printk("Error on Queue Full \n"); + if (status == ETH_QUEUE_LAST_RESOURCE) + printk("Tx resource error \n"); + } + + /* Check for the remaining frags */ + for (frag = 0; frag < skb_shinfo(skb)->nr_frags; frag++) { + skb_frag_t *this_frag = &skb_shinfo(skb)->frags[frag]; + pkt_info.l4i_chk = 0x0000; + pkt_info.cmd_sts = 0x00000000; + + /* Last Frag enables interrupt and frees the skb */ + if (frag == (skb_shinfo(skb)->nr_frags - 1)) { + pkt_info.cmd_sts |= ETH_TX_ENABLE_INTERRUPT | + ETH_TX_LAST_DESC; + pkt_info.return_info = skb; + mp->tx_ring_skbs++; + } + else { + pkt_info.return_info = 0; + } + pkt_info.byte_cnt = this_frag->size; + if (this_frag->size < 8) + printk("%d : \n", skb_shinfo(skb)->nr_frags); + + pkt_info.buf_ptr = pci_map_page(NULL, this_frag->page, + this_frag->page_offset, + this_frag->size, PCI_DMA_TODEVICE); + + status = eth_port_send(mp, &pkt_info); + + if (status != ETH_OK) { + if ((status == ETH_ERROR)) + printk(KERN_ERR "%s: Error on transmitting packet\n", dev->name); + + if (status == ETH_QUEUE_LAST_RESOURCE) + printk("Tx resource error \n"); + + if (status == ETH_QUEUE_FULL) + printk("Queue is full \n"); + } + } + } +#endif + + /* Check if TX queue can handle another skb. If not, then + * signal higher layers to stop requesting TX + */ + if (MV64340_TX_QUEUE_SIZE <= (mp->tx_ring_skbs + 1)) + /* + * Stop getting skb's from upper layers. + * Getting skb's from upper layers will be enabled again after + * packets are released. + */ + netif_stop_queue(dev); + + /* Update statistics and start of transmittion time */ + stats->tx_bytes += skb->len; + stats->tx_packets++; + dev->trans_start = jiffies; + + spin_unlock_irqrestore(&mp->lock, flags); + + return 0; /* success */ +} + +/* + * mv64340_eth_get_stats + * + * Returns a pointer to the interface statistics. + * + * Input : dev - a pointer to the required interface + * + * Output : a pointer to the interface's statistics + */ + +static struct net_device_stats *mv64340_eth_get_stats(struct net_device *dev) +{ + struct mv64340_private *mp = netdev_priv(dev); + + return &mp->stats; +} + +/*/ + * mv64340_eth_init + * + * First function called after registering the network device. + * It's purpose is to initialize the device as an ethernet device, + * fill the structure that was given in registration with pointers + * to functions, and setting the MAC address of the interface + * + * Input : number of port to initialize + * Output : -ENONMEM if failed , 0 if success + */ +static struct net_device *mv64340_eth_init(int port_num) +{ + struct mv64340_private *mp; + struct net_device *dev; + int err; + + dev = alloc_etherdev(sizeof(struct mv64340_private)); + if (!dev) + return NULL; + + mp = netdev_priv(dev); + + dev->irq = ETH_PORT0_IRQ_NUM + port_num; + + dev->open = mv64340_eth_open; + dev->stop = mv64340_eth_stop; + dev->hard_start_xmit = mv64340_eth_start_xmit; + dev->get_stats = mv64340_eth_get_stats; + dev->set_mac_address = mv64340_eth_set_mac_address; + dev->set_multicast_list = mv64340_eth_set_rx_mode; + + /* No need to Tx Timeout */ + dev->tx_timeout = mv64340_eth_tx_timeout; +#ifdef MV64340_NAPI + dev->poll = mv64340_poll; + dev->weight = 64; +#endif + + dev->watchdog_timeo = 2 * HZ; + dev->tx_queue_len = MV64340_TX_QUEUE_SIZE; + dev->base_addr = 0; + dev->change_mtu = mv64340_eth_change_mtu; + +#ifdef MV64340_CHECKSUM_OFFLOAD_TX +#ifdef MAX_SKB_FRAGS +#ifndef CONFIG_JAGUAR_DMALOW + /* + * Zero copy can only work if we use Discovery II memory. Else, we will + * have to map the buffers to ISA memory which is only 16 MB + */ + dev->features = NETIF_F_SG | NETIF_F_IP_CSUM | NETIF_F_HW_CSUM; +#endif +#endif +#endif + + mp->port_num = port_num; + + /* Configure the timeout task */ + INIT_WORK(&mp->tx_timeout_task, + (void (*)(void *))mv64340_eth_tx_timeout_task, dev); + + spin_lock_init(&mp->lock); + + /* set MAC addresses */ + memcpy(dev->dev_addr, prom_mac_addr_base, 6); + dev->dev_addr[5] += port_num; + + err = register_netdev(dev); + if (err) + goto out_free_dev; + + printk(KERN_NOTICE "%s: port %d with MAC address %02x:%02x:%02x:%02x:%02x:%02x\n", + dev->name, port_num, + dev->dev_addr[0], dev->dev_addr[1], dev->dev_addr[2], + dev->dev_addr[3], dev->dev_addr[4], dev->dev_addr[5]); + + if (dev->features & NETIF_F_SG) + printk("Scatter Gather Enabled "); + + if (dev->features & NETIF_F_IP_CSUM) + printk("TX TCP/IP Checksumming Supported \n"); + + printk("RX TCP/UDP Checksum Offload ON, \n"); + printk("TX and RX Interrupt Coalescing ON \n"); + +#ifdef MV64340_NAPI + printk("RX NAPI Enabled \n"); +#endif + + return dev; + +out_free_dev: + free_netdev(dev); + + return NULL; +} + +static void mv64340_eth_remove(struct net_device *dev) +{ + struct mv64340_private *mp = netdev_priv(dev); + + unregister_netdev(dev); + flush_scheduled_work(); + free_netdev(dev); +} + +static struct net_device *mv64340_dev0; +static struct net_device *mv64340_dev1; +static struct net_device *mv64340_dev2; + +/* + * mv64340_init_module + * + * Registers the network drivers into the Linux kernel + * + * Input : N/A + * + * Output : N/A + */ +static int __init mv64340_init_module(void) +{ + printk(KERN_NOTICE "MV-643xx 10/100/1000 Ethernet Driver\n"); + +#ifdef CONFIG_MV643XX_ETH_0 + mv64340_dev0 = mv64340_eth_init(0); + if (!mv64340_dev0) { + printk(KERN_ERR + "Error registering MV-64360 ethernet port 0\n"); + } +#endif +#ifdef CONFIG_MV643XX_ETH_1 + mv64340_dev1 = mv64340_eth_init(1); + if (!mv64340_dev1) { + printk(KERN_ERR + "Error registering MV-64360 ethernet port 1\n"); + } +#endif +#ifdef CONFIG_MV643XX_ETH_2 + mv64340_dev2 = mv64340_eth_init(2); + if (!mv64340_dev2) { + printk(KERN_ERR + "Error registering MV-64360 ethernet port 2\n"); + } +#endif + return 0; +} + +/* + * mv64340_cleanup_module + * + * Registers the network drivers into the Linux kernel + * + * Input : N/A + * + * Output : N/A + */ +static void __exit mv64340_cleanup_module(void) +{ + if (mv64340_dev2) + mv64340_eth_remove(mv64340_dev2); + if (mv64340_dev1) + mv64340_eth_remove(mv64340_dev1); + if (mv64340_dev0) + mv64340_eth_remove(mv64340_dev0); +} + +module_init(mv64340_init_module); +module_exit(mv64340_cleanup_module); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Rabeeh Khoury, Assaf Hoffman, Matthew Dharm and Manish Lachwani"); +MODULE_DESCRIPTION("Ethernet driver for Marvell MV64340"); + +/* + * The second part is the low level driver of the gigE ethernet ports. + */ + +/* + * Marvell's Gigabit Ethernet controller low level driver + * + * DESCRIPTION: + * This file introduce low level API to Marvell's Gigabit Ethernet + * controller. This Gigabit Ethernet Controller driver API controls + * 1) Operations (i.e. port init, start, reset etc'). + * 2) Data flow (i.e. port send, receive etc'). + * Each Gigabit Ethernet port is controlled via + * struct mv64340_private. + * This struct includes user configuration information as well as + * driver internal data needed for its operations. + * + * Supported Features: + * - This low level driver is OS independent. Allocating memory for + * the descriptor rings and buffers are not within the scope of + * this driver. + * - The user is free from Rx/Tx queue managing. + * - This low level driver introduce functionality API that enable + * the to operate Marvell's Gigabit Ethernet Controller in a + * convenient way. + * - Simple Gigabit Ethernet port operation API. + * - Simple Gigabit Ethernet port data flow API. + * - Data flow and operation API support per queue functionality. + * - Support cached descriptors for better performance. + * - Enable access to all four DRAM banks and internal SRAM memory + * spaces. + * - PHY access and control API. + * - Port control register configuration API. + * - Full control over Unicast and Multicast MAC configurations. + * + * Operation flow: + * + * Initialization phase + * This phase complete the initialization of the the mv64340_private + * struct. + * User information regarding port configuration has to be set + * prior to calling the port initialization routine. + * + * In this phase any port Tx/Rx activity is halted, MIB counters + * are cleared, PHY address is set according to user parameter and + * access to DRAM and internal SRAM memory spaces. + * + * Driver ring initialization + * Allocating memory for the descriptor rings and buffers is not + * within the scope of this driver. Thus, the user is required to + * allocate memory for the descriptors ring and buffers. Those + * memory parameters are used by the Rx and Tx ring initialization + * routines in order to curve the descriptor linked list in a form + * of a ring. + * Note: Pay special attention to alignment issues when using + * cached descriptors/buffers. In this phase the driver store + * information in the mv64340_private struct regarding each queue + * ring. + * + * Driver start + * This phase prepares the Ethernet port for Rx and Tx activity. + * It uses the information stored in the mv64340_private struct to + * initialize the various port registers. + * + * Data flow: + * All packet references to/from the driver are done using + * struct pkt_info. + * This struct is a unified struct used with Rx and Tx operations. + * This way the user is not required to be familiar with neither + * Tx nor Rx descriptors structures. + * The driver's descriptors rings are management by indexes. + * Those indexes controls the ring resources and used to indicate + * a SW resource error: + * 'current' + * This index points to the current available resource for use. For + * example in Rx process this index will point to the descriptor + * that will be passed to the user upon calling the receive routine. + * In Tx process, this index will point to the descriptor + * that will be assigned with the user packet info and transmitted. + * 'used' + * This index points to the descriptor that need to restore its + * resources. For example in Rx process, using the Rx buffer return + * API will attach the buffer returned in packet info to the + * descriptor pointed by 'used'. In Tx process, using the Tx + * descriptor return will merely return the user packet info with + * the command status of the transmitted buffer pointed by the + * 'used' index. Nevertheless, it is essential to use this routine + * to update the 'used' index. + * 'first' + * This index supports Tx Scatter-Gather. It points to the first + * descriptor of a packet assembled of multiple buffers. For example + * when in middle of Such packet we have a Tx resource error the + * 'curr' index get the value of 'first' to indicate that the ring + * returned to its state before trying to transmit this packet. + * + * Receive operation: + * The eth_port_receive API set the packet information struct, + * passed by the caller, with received information from the + * 'current' SDMA descriptor. + * It is the user responsibility to return this resource back + * to the Rx descriptor ring to enable the reuse of this source. + * Return Rx resource is done using the eth_rx_return_buff API. + * + * Transmit operation: + * The eth_port_send API supports Scatter-Gather which enables to + * send a packet spanned over multiple buffers. This means that + * for each packet info structure given by the user and put into + * the Tx descriptors ring, will be transmitted only if the 'LAST' + * bit will be set in the packet info command status field. This + * API also consider restriction regarding buffer alignments and + * sizes. + * The user must return a Tx resource after ensuring the buffer + * has been transmitted to enable the Tx ring indexes to update. + * + * BOARD LAYOUT + * This device is on-board. No jumper diagram is necessary. + * + * EXTERNAL INTERFACE + * + * Prior to calling the initialization routine eth_port_init() the user + * must set the following fields under mv64340_private struct: + * port_num User Ethernet port number. + * port_mac_addr[6] User defined port MAC address. + * port_config User port configuration value. + * port_config_extend User port config extend value. + * port_sdma_config User port SDMA config value. + * port_serial_control User port serial control value. + * + * This driver introduce a set of default values: + * PORT_CONFIG_VALUE Default port configuration value + * PORT_CONFIG_EXTEND_VALUE Default port extend configuration value + * PORT_SDMA_CONFIG_VALUE Default sdma control value + * PORT_SERIAL_CONTROL_VALUE Default port serial control value + * + * This driver data flow is done using the struct pkt_info which + * is a unified struct for Rx and Tx operations: + * + * byte_cnt Tx/Rx descriptor buffer byte count. + * l4i_chk CPU provided TCP Checksum. For Tx operation + * only. + * cmd_sts Tx/Rx descriptor command status. + * buf_ptr Tx/Rx descriptor buffer pointer. + * return_info Tx/Rx user resource return information. + */ + +/* defines */ +/* SDMA command macros */ +#define ETH_ENABLE_TX_QUEUE(eth_port) \ + MV_WRITE(MV64340_ETH_TRANSMIT_QUEUE_COMMAND_REG(eth_port), 1) + +#define ETH_DISABLE_TX_QUEUE(eth_port) \ + MV_WRITE(MV64340_ETH_TRANSMIT_QUEUE_COMMAND_REG(eth_port), \ + (1 << 8)) + +#define ETH_ENABLE_RX_QUEUE(rx_queue, eth_port) \ + MV_WRITE(MV64340_ETH_RECEIVE_QUEUE_COMMAND_REG(eth_port), \ + (1 << rx_queue)) + +#define ETH_DISABLE_RX_QUEUE(rx_queue, eth_port) \ + MV_WRITE(MV64340_ETH_RECEIVE_QUEUE_COMMAND_REG(eth_port), \ + (1 << (8 + rx_queue))) + +#define LINK_UP_TIMEOUT 100000 +#define PHY_BUSY_TIMEOUT 10000000 + +/* locals */ + +/* PHY routines */ +static int ethernet_phy_get(unsigned int eth_port_num); + +/* Ethernet Port routines */ +static int eth_port_uc_addr(unsigned int eth_port_num, unsigned char uc_nibble, + int option); + +/* + * eth_port_init - Initialize the Ethernet port driver + * + * DESCRIPTION: + * This function prepares the ethernet port to start its activity: + * 1) Completes the ethernet port driver struct initialization toward port + * start routine. + * 2) Resets the device to a quiescent state in case of warm reboot. + * 3) Enable SDMA access to all four DRAM banks as well as internal SRAM. + * 4) Clean MAC tables. The reset status of those tables is unknown. + * 5) Set PHY address. + * Note: Call this routine prior to eth_port_start routine and after + * setting user values in the user fields of Ethernet port control + * struct. + * + * INPUT: + * struct mv64340_private *mp Ethernet port control struct + * + * OUTPUT: + * See description. + * + * RETURN: + * None. + */ +static void eth_port_init(struct mv64340_private * mp) +{ + mp->port_config = PORT_CONFIG_VALUE; + mp->port_config_extend = PORT_CONFIG_EXTEND_VALUE; +#if defined(__BIG_ENDIAN) + mp->port_sdma_config = PORT_SDMA_CONFIG_VALUE; +#elif defined(__LITTLE_ENDIAN) + mp->port_sdma_config = PORT_SDMA_CONFIG_VALUE | + ETH_BLM_RX_NO_SWAP | ETH_BLM_TX_NO_SWAP; +#else +#error One of __LITTLE_ENDIAN or __BIG_ENDIAN must be defined! +#endif + mp->port_serial_control = PORT_SERIAL_CONTROL_VALUE; + + mp->port_rx_queue_command = 0; + mp->port_tx_queue_command = 0; + + mp->rx_resource_err = 0; + mp->tx_resource_err = 0; + + eth_port_reset(mp->port_num); + + eth_port_init_mac_tables(mp->port_num); + + ethernet_phy_reset(mp->port_num); +} + +/* + * eth_port_start - Start the Ethernet port activity. + * + * DESCRIPTION: + * This routine prepares the Ethernet port for Rx and Tx activity: + * 1. Initialize Tx and Rx Current Descriptor Pointer for each queue that + * has been initialized a descriptor's ring (using + * ether_init_tx_desc_ring for Tx and ether_init_rx_desc_ring for Rx) + * 2. Initialize and enable the Ethernet configuration port by writing to + * the port's configuration and command registers. + * 3. Initialize and enable the SDMA by writing to the SDMA's + * configuration and command registers. After completing these steps, + * the ethernet port SDMA can starts to perform Rx and Tx activities. + * + * Note: Each Rx and Tx queue descriptor's list must be initialized prior + * to calling this function (use ether_init_tx_desc_ring for Tx queues + * and ether_init_rx_desc_ring for Rx queues). + * + * INPUT: + * struct mv64340_private *mp Ethernet port control struct + * + * OUTPUT: + * Ethernet port is ready to receive and transmit. + * + * RETURN: + * false if the port PHY is not up. + * true otherwise. + */ +static int eth_port_start(struct mv64340_private *mp) +{ + unsigned int eth_port_num = mp->port_num; + int tx_curr_desc, rx_curr_desc; + unsigned int phy_reg_data; + + /* Assignment of Tx CTRP of given queue */ + tx_curr_desc = mp->tx_curr_desc_q; + MV_WRITE(MV64340_ETH_TX_CURRENT_QUEUE_DESC_PTR_0(eth_port_num), + (struct eth_tx_desc *) mp->tx_desc_dma + tx_curr_desc); + + /* Assignment of Rx CRDP of given queue */ + rx_curr_desc = mp->rx_curr_desc_q; + MV_WRITE(MV64340_ETH_RX_CURRENT_QUEUE_DESC_PTR_0(eth_port_num), + (struct eth_rx_desc *) mp->rx_desc_dma + rx_curr_desc); + + /* Add the assigned Ethernet address to the port's address table */ + eth_port_uc_addr_set(mp->port_num, mp->port_mac_addr); + + /* Assign port configuration and command. */ + MV_WRITE(MV64340_ETH_PORT_CONFIG_REG(eth_port_num), + mp->port_config); + + MV_WRITE(MV64340_ETH_PORT_CONFIG_EXTEND_REG(eth_port_num), + mp->port_config_extend); + + MV_WRITE(MV64340_ETH_PORT_SERIAL_CONTROL_REG(eth_port_num), + mp->port_serial_control); + + MV_SET_REG_BITS(MV64340_ETH_PORT_SERIAL_CONTROL_REG(eth_port_num), + ETH_SERIAL_PORT_ENABLE); + + /* Assign port SDMA configuration */ + MV_WRITE(MV64340_ETH_SDMA_CONFIG_REG(eth_port_num), + mp->port_sdma_config); + + /* Enable port Rx. */ + MV_WRITE(MV64340_ETH_RECEIVE_QUEUE_COMMAND_REG(eth_port_num), + mp->port_rx_queue_command); + + /* Check if link is up */ + eth_port_read_smi_reg(eth_port_num, 1, &phy_reg_data); + + if (!(phy_reg_data & 0x20)) + return 0; + + return 1; +} + +/* + * eth_port_uc_addr_set - This function Set the port Unicast address. + * + * DESCRIPTION: + * This function Set the port Ethernet MAC address. + * + * INPUT: + * unsigned int eth_port_num Port number. + * char * p_addr Address to be set + * + * OUTPUT: + * Set MAC address low and high registers. also calls eth_port_uc_addr() + * To set the unicast table with the proper information. + * + * RETURN: + * N/A. + * + */ +static void eth_port_uc_addr_set(unsigned int eth_port_num, + unsigned char *p_addr) +{ + unsigned int mac_h; + unsigned int mac_l; + + mac_l = (p_addr[4] << 8) | (p_addr[5]); + mac_h = (p_addr[0] << 24) | (p_addr[1] << 16) | + (p_addr[2] << 8) | (p_addr[3] << 0); + + MV_WRITE(MV64340_ETH_MAC_ADDR_LOW(eth_port_num), mac_l); + MV_WRITE(MV64340_ETH_MAC_ADDR_HIGH(eth_port_num), mac_h); + + /* Accept frames of this address */ + eth_port_uc_addr(eth_port_num, p_addr[5], ACCEPT_MAC_ADDR); + + return; +} + +/* + * eth_port_uc_addr - This function Set the port unicast address table + * + * DESCRIPTION: + * This function locates the proper entry in the Unicast table for the + * specified MAC nibble and sets its properties according to function + * parameters. + * + * INPUT: + * unsigned int eth_port_num Port number. + * unsigned char uc_nibble Unicast MAC Address last nibble. + * int option 0 = Add, 1 = remove address. + * + * OUTPUT: + * This function add/removes MAC addresses from the port unicast address + * table. + * + * RETURN: + * true is output succeeded. + * false if option parameter is invalid. + * + */ +static int eth_port_uc_addr(unsigned int eth_port_num, + unsigned char uc_nibble, int option) +{ + unsigned int unicast_reg; + unsigned int tbl_offset; + unsigned int reg_offset; + + /* Locate the Unicast table entry */ + uc_nibble = (0xf & uc_nibble); + tbl_offset = (uc_nibble / 4) * 4; /* Register offset from unicast table base */ + reg_offset = uc_nibble % 4; /* Entry offset within the above register */ + + switch (option) { + case REJECT_MAC_ADDR: + /* Clear accepts frame bit at specified unicast DA table entry */ + unicast_reg = MV_READ((MV64340_ETH_DA_FILTER_UNICAST_TABLE_BASE + (eth_port_num) + tbl_offset)); + + unicast_reg &= (0x0E << (8 * reg_offset)); + + MV_WRITE( + (MV64340_ETH_DA_FILTER_UNICAST_TABLE_BASE + (eth_port_num) + tbl_offset), unicast_reg); + break; + + case ACCEPT_MAC_ADDR: + /* Set accepts frame bit at unicast DA filter table entry */ + unicast_reg = + MV_READ( + (MV64340_ETH_DA_FILTER_UNICAST_TABLE_BASE + (eth_port_num) + tbl_offset)); + + unicast_reg |= (0x01 << (8 * reg_offset)); + + MV_WRITE( + (MV64340_ETH_DA_FILTER_UNICAST_TABLE_BASE + (eth_port_num) + tbl_offset), unicast_reg); + + break; + + default: + return 0; + } + + return 1; +} + +/* + * eth_port_init_mac_tables - Clear all entrance in the UC, SMC and OMC tables + * + * DESCRIPTION: + * Go through all the DA filter tables (Unicast, Special Multicast & + * Other Multicast) and set each entry to 0. + * + * INPUT: + * unsigned int eth_port_num Ethernet Port number. + * + * OUTPUT: + * Multicast and Unicast packets are rejected. + * + * RETURN: + * None. + */ +static void eth_port_init_mac_tables(unsigned int eth_port_num) +{ + int table_index; + + /* Clear DA filter unicast table (Ex_dFUT) */ + for (table_index = 0; table_index <= 0xC; table_index += 4) + MV_WRITE( + (MV64340_ETH_DA_FILTER_UNICAST_TABLE_BASE + (eth_port_num) + table_index), 0); + + for (table_index = 0; table_index <= 0xFC; table_index += 4) { + /* Clear DA filter special multicast table (Ex_dFSMT) */ + MV_WRITE( + (MV64340_ETH_DA_FILTER_SPECIAL_MULTICAST_TABLE_BASE + (eth_port_num) + table_index), 0); + /* Clear DA filter other multicast table (Ex_dFOMT) */ + MV_WRITE((MV64340_ETH_DA_FILTER_OTHER_MULTICAST_TABLE_BASE + (eth_port_num) + table_index), 0); + } +} + +/* + * eth_clear_mib_counters - Clear all MIB counters + * + * DESCRIPTION: + * This function clears all MIB counters of a specific ethernet port. + * A read from the MIB counter will reset the counter. + * + * INPUT: + * unsigned int eth_port_num Ethernet Port number. + * + * OUTPUT: + * After reading all MIB counters, the counters resets. + * + * RETURN: + * MIB counter value. + * + */ +static void eth_clear_mib_counters(unsigned int eth_port_num) +{ + int i; + + /* Perform dummy reads from MIB counters */ + for (i = ETH_MIB_GOOD_OCTETS_RECEIVED_LOW; i < ETH_MIB_LATE_COLLISION; i += 4) + MV_READ(MV64340_ETH_MIB_COUNTERS_BASE(eth_port_num) + i); +} + + +/* + * ethernet_phy_get - Get the ethernet port PHY address. + * + * DESCRIPTION: + * This routine returns the given ethernet port PHY address. + * + * INPUT: + * unsigned int eth_port_num Ethernet Port number. + * + * OUTPUT: + * None. + * + * RETURN: + * PHY address. + * + */ +static int ethernet_phy_get(unsigned int eth_port_num) +{ + unsigned int reg_data; + + reg_data = MV_READ(MV64340_ETH_PHY_ADDR_REG); + + return ((reg_data >> (5 * eth_port_num)) & 0x1f); +} + +/* + * ethernet_phy_reset - Reset Ethernet port PHY. + * + * DESCRIPTION: + * This routine utilize the SMI interface to reset the ethernet port PHY. + * The routine waits until the link is up again or link up is timeout. + * + * INPUT: + * unsigned int eth_port_num Ethernet Port number. + * + * OUTPUT: + * The ethernet port PHY renew its link. + * + * RETURN: + * None. + * + */ +static int ethernet_phy_reset(unsigned int eth_port_num) +{ + unsigned int time_out = 50; + unsigned int phy_reg_data; + + /* Reset the PHY */ + eth_port_read_smi_reg(eth_port_num, 0, &phy_reg_data); + phy_reg_data |= 0x8000; /* Set bit 15 to reset the PHY */ + eth_port_write_smi_reg(eth_port_num, 0, phy_reg_data); + + /* Poll on the PHY LINK */ + do { + eth_port_read_smi_reg(eth_port_num, 1, &phy_reg_data); + + if (time_out-- == 0) + return 0; + } while (!(phy_reg_data & 0x20)); + + return 1; +} + +/* + * eth_port_reset - Reset Ethernet port + * + * DESCRIPTION: + * This routine resets the chip by aborting any SDMA engine activity and + * clearing the MIB counters. The Receiver and the Transmit unit are in + * idle state after this command is performed and the port is disabled. + * + * INPUT: + * unsigned int eth_port_num Ethernet Port number. + * + * OUTPUT: + * Channel activity is halted. + * + * RETURN: + * None. + * + */ +static void eth_port_reset(unsigned int eth_port_num) +{ + unsigned int reg_data; + + /* Stop Tx port activity. Check port Tx activity. */ + reg_data = + MV_READ(MV64340_ETH_TRANSMIT_QUEUE_COMMAND_REG(eth_port_num)); + + if (reg_data & 0xFF) { + /* Issue stop command for active channels only */ + MV_WRITE(MV64340_ETH_TRANSMIT_QUEUE_COMMAND_REG + (eth_port_num), (reg_data << 8)); + + /* Wait for all Tx activity to terminate. */ + do { + /* Check port cause register that all Tx queues are stopped */ + reg_data = + MV_READ + (MV64340_ETH_TRANSMIT_QUEUE_COMMAND_REG + (eth_port_num)); + } + while (reg_data & 0xFF); + } + + /* Stop Rx port activity. Check port Rx activity. */ + reg_data = + MV_READ(MV64340_ETH_RECEIVE_QUEUE_COMMAND_REG + (eth_port_num)); + + if (reg_data & 0xFF) { + /* Issue stop command for active channels only */ + MV_WRITE(MV64340_ETH_RECEIVE_QUEUE_COMMAND_REG + (eth_port_num), (reg_data << 8)); + + /* Wait for all Rx activity to terminate. */ + do { + /* Check port cause register that all Rx queues are stopped */ + reg_data = + MV_READ + (MV64340_ETH_RECEIVE_QUEUE_COMMAND_REG + (eth_port_num)); + } + while (reg_data & 0xFF); + } + + + /* Clear all MIB counters */ + eth_clear_mib_counters(eth_port_num); + + /* Reset the Enable bit in the Configuration Register */ + reg_data = + MV_READ(MV64340_ETH_PORT_SERIAL_CONTROL_REG (eth_port_num)); + reg_data &= ~ETH_SERIAL_PORT_ENABLE; + MV_WRITE(MV64340_ETH_PORT_SERIAL_CONTROL_REG(eth_port_num), reg_data); + + return; +} + +/* + * ethernet_set_config_reg - Set specified bits in configuration register. + * + * DESCRIPTION: + * This function sets specified bits in the given ethernet + * configuration register. + * + * INPUT: + * unsigned int eth_port_num Ethernet Port number. + * unsigned int value 32 bit value. + * + * OUTPUT: + * The set bits in the value parameter are set in the configuration + * register. + * + * RETURN: + * None. + * + */ +static void ethernet_set_config_reg(unsigned int eth_port_num, + unsigned int value) +{ + unsigned int eth_config_reg; + + eth_config_reg = + MV_READ(MV64340_ETH_PORT_CONFIG_REG(eth_port_num)); + eth_config_reg |= value; + MV_WRITE(MV64340_ETH_PORT_CONFIG_REG(eth_port_num), + eth_config_reg); +} + +/* + * ethernet_get_config_reg - Get the port configuration register + * + * DESCRIPTION: + * This function returns the configuration register value of the given + * ethernet port. + * + * INPUT: + * unsigned int eth_port_num Ethernet Port number. + * + * OUTPUT: + * None. + * + * RETURN: + * Port configuration register value. + */ +static unsigned int ethernet_get_config_reg(unsigned int eth_port_num) +{ + unsigned int eth_config_reg; + + eth_config_reg = MV_READ(MV64340_ETH_PORT_CONFIG_EXTEND_REG + (eth_port_num)); + return eth_config_reg; +} + + +/* + * eth_port_read_smi_reg - Read PHY registers + * + * DESCRIPTION: + * This routine utilize the SMI interface to interact with the PHY in + * order to perform PHY register read. + * + * INPUT: + * unsigned int eth_port_num Ethernet Port number. + * unsigned int phy_reg PHY register address offset. + * unsigned int *value Register value buffer. + * + * OUTPUT: + * Write the value of a specified PHY register into given buffer. + * + * RETURN: + * false if the PHY is busy or read data is not in valid state. + * true otherwise. + * + */ +static int eth_port_read_smi_reg(unsigned int eth_port_num, + unsigned int phy_reg, unsigned int *value) +{ + int phy_addr = ethernet_phy_get(eth_port_num); + unsigned int time_out = PHY_BUSY_TIMEOUT; + unsigned int reg_value; + + /* first check that it is not busy */ + do { + reg_value = MV_READ(MV64340_ETH_SMI_REG); + if (time_out-- == 0) + return 0; + } while (reg_value & ETH_SMI_BUSY); + + /* not busy */ + + MV_WRITE(MV64340_ETH_SMI_REG, + (phy_addr << 16) | (phy_reg << 21) | ETH_SMI_OPCODE_READ); + + time_out = PHY_BUSY_TIMEOUT; /* initialize the time out var again */ + + do { + reg_value = MV_READ(MV64340_ETH_SMI_REG); + if (time_out-- == 0) + return 0; + } while (reg_value & ETH_SMI_READ_VALID); + + /* Wait for the data to update in the SMI register */ + for (time_out = 0; time_out < PHY_BUSY_TIMEOUT; time_out++); + + reg_value = MV_READ(MV64340_ETH_SMI_REG); + + *value = reg_value & 0xffff; + + return 1; +} + +/* + * eth_port_write_smi_reg - Write to PHY registers + * + * DESCRIPTION: + * This routine utilize the SMI interface to interact with the PHY in + * order to perform writes to PHY registers. + * + * INPUT: + * unsigned int eth_port_num Ethernet Port number. + * unsigned int phy_reg PHY register address offset. + * unsigned int value Register value. + * + * OUTPUT: + * Write the given value to the specified PHY register. + * + * RETURN: + * false if the PHY is busy. + * true otherwise. + * + */ +static int eth_port_write_smi_reg(unsigned int eth_port_num, + unsigned int phy_reg, unsigned int value) +{ + unsigned int time_out = PHY_BUSY_TIMEOUT; + unsigned int reg_value; + int phy_addr; + + phy_addr = ethernet_phy_get(eth_port_num); + + /* first check that it is not busy */ + do { + reg_value = MV_READ(MV64340_ETH_SMI_REG); + if (time_out-- == 0) + return 0; + } while (reg_value & ETH_SMI_BUSY); + + /* not busy */ + MV_WRITE(MV64340_ETH_SMI_REG, (phy_addr << 16) | (phy_reg << 21) | + ETH_SMI_OPCODE_WRITE | (value & 0xffff)); + + return 1; +} + +/* + * eth_port_send - Send an Ethernet packet + * + * DESCRIPTION: + * This routine send a given packet described by p_pktinfo parameter. It + * supports transmitting of a packet spaned over multiple buffers. The + * routine updates 'curr' and 'first' indexes according to the packet + * segment passed to the routine. In case the packet segment is first, + * the 'first' index is update. In any case, the 'curr' index is updated. + * If the routine get into Tx resource error it assigns 'curr' index as + * 'first'. This way the function can abort Tx process of multiple + * descriptors per packet. + * + * INPUT: + * struct mv64340_private *mp Ethernet Port Control srtuct. + * struct pkt_info *p_pkt_info User packet buffer. + * + * OUTPUT: + * Tx ring 'curr' and 'first' indexes are updated. + * + * RETURN: + * ETH_QUEUE_FULL in case of Tx resource error. + * ETH_ERROR in case the routine can not access Tx desc ring. + * ETH_QUEUE_LAST_RESOURCE if the routine uses the last Tx resource. + * ETH_OK otherwise. + * + */ +#ifdef MV64340_CHECKSUM_OFFLOAD_TX +/* + * Modified to include the first descriptor pointer in case of SG + */ +static ETH_FUNC_RET_STATUS eth_port_send(struct mv64340_private * mp, + struct pkt_info * p_pkt_info) +{ + int tx_desc_curr, tx_desc_used, tx_first_desc, tx_next_desc; + volatile struct eth_tx_desc *current_descriptor; + volatile struct eth_tx_desc *first_descriptor; + u32 command_status, first_chip_ptr; + + /* Do not process Tx ring in case of Tx ring resource error */ + if (mp->tx_resource_err) + return ETH_QUEUE_FULL; + + /* Get the Tx Desc ring indexes */ + tx_desc_curr = mp->tx_curr_desc_q; + tx_desc_used = mp->tx_used_desc_q; + + current_descriptor = &mp->p_tx_desc_area[tx_desc_curr]; + if (current_descriptor == NULL) + return ETH_ERROR; + + tx_next_desc = (tx_desc_curr + 1) % MV64340_TX_QUEUE_SIZE; + command_status = p_pkt_info->cmd_sts | ETH_ZERO_PADDING | ETH_GEN_CRC; + + if (command_status & ETH_TX_FIRST_DESC) { + tx_first_desc = tx_desc_curr; + mp->tx_first_desc_q = tx_first_desc; + + /* fill first descriptor */ + first_descriptor = &mp->p_tx_desc_area[tx_desc_curr]; + first_descriptor->l4i_chk = p_pkt_info->l4i_chk; + first_descriptor->cmd_sts = command_status; + first_descriptor->byte_cnt = p_pkt_info->byte_cnt; + first_descriptor->buf_ptr = p_pkt_info->buf_ptr; + first_descriptor->next_desc_ptr = mp->tx_desc_dma + + tx_next_desc * sizeof(struct eth_tx_desc); + wmb(); + } else { + tx_first_desc = mp->tx_first_desc_q; + first_descriptor = &mp->p_tx_desc_area[tx_first_desc]; + if (first_descriptor == NULL) { + printk("First desc is NULL !!\n"); + return ETH_ERROR; + } + if (command_status & ETH_TX_LAST_DESC) + current_descriptor->next_desc_ptr = 0x00000000; + else { + command_status |= ETH_BUFFER_OWNED_BY_DMA; + current_descriptor->next_desc_ptr = mp->tx_desc_dma + + tx_next_desc * sizeof(struct eth_tx_desc); + } + } + + if (p_pkt_info->byte_cnt < 8) { + printk(" < 8 problem \n"); + return ETH_ERROR; + } + + current_descriptor->buf_ptr = p_pkt_info->buf_ptr; + current_descriptor->byte_cnt = p_pkt_info->byte_cnt; + current_descriptor->l4i_chk = p_pkt_info->l4i_chk; + current_descriptor->cmd_sts = command_status; + + mp->tx_skb[tx_desc_curr] = (struct sk_buff*) p_pkt_info->return_info; + + wmb(); + + /* Set last desc with DMA ownership and interrupt enable. */ + if (command_status & ETH_TX_LAST_DESC) { + current_descriptor->cmd_sts = command_status | + ETH_TX_ENABLE_INTERRUPT | + ETH_BUFFER_OWNED_BY_DMA; + + if (!(command_status & ETH_TX_FIRST_DESC)) + first_descriptor->cmd_sts |= ETH_BUFFER_OWNED_BY_DMA; + wmb(); + + first_chip_ptr = MV_READ(MV64340_ETH_CURRENT_SERVED_TX_DESC_PTR(mp->port_num)); + + /* Apply send command */ + if (first_chip_ptr == 0x00000000) + MV_WRITE(MV64340_ETH_TX_CURRENT_QUEUE_DESC_PTR_0(mp->port_num), (struct eth_tx_desc *) mp->tx_desc_dma + tx_first_desc); + + ETH_ENABLE_TX_QUEUE(mp->port_num); + + /* + * Finish Tx packet. Update first desc in case of Tx resource + * error */ + tx_first_desc = tx_next_desc; + mp->tx_first_desc_q = tx_first_desc; + } else { + if (! (command_status & ETH_TX_FIRST_DESC) ) { + current_descriptor->cmd_sts = command_status; + wmb(); + } + } + + /* Check for ring index overlap in the Tx desc ring */ + if (tx_next_desc == tx_desc_used) { + mp->tx_resource_err = 1; + mp->tx_curr_desc_q = tx_first_desc; + + return ETH_QUEUE_LAST_RESOURCE; + } + + mp->tx_curr_desc_q = tx_next_desc; + wmb(); + + return ETH_OK; +} +#else +static ETH_FUNC_RET_STATUS eth_port_send(struct mv64340_private * mp, + struct pkt_info * p_pkt_info) +{ + int tx_desc_curr; + int tx_desc_used; + volatile struct eth_tx_desc* current_descriptor; + unsigned int command_status; + + /* Do not process Tx ring in case of Tx ring resource error */ + if (mp->tx_resource_err) + return ETH_QUEUE_FULL; + + /* Get the Tx Desc ring indexes */ + tx_desc_curr = mp->tx_curr_desc_q; + tx_desc_used = mp->tx_used_desc_q; + current_descriptor = &mp->p_tx_desc_area[tx_desc_curr]; + + if (current_descriptor == NULL) + return ETH_ERROR; + + command_status = p_pkt_info->cmd_sts | ETH_ZERO_PADDING | ETH_GEN_CRC; + +/* XXX Is this for real ?!?!? */ + /* Buffers with a payload smaller than 8 bytes must be aligned to a + * 64-bit boundary. We use the memory allocated for Tx descriptor. + * This memory is located in TX_BUF_OFFSET_IN_DESC offset within the + * Tx descriptor. */ + if (p_pkt_info->byte_cnt <= 8) { + printk(KERN_ERR + "You have failed in the < 8 bytes errata - fixme\n"); + return ETH_ERROR; + } + current_descriptor->buf_ptr = p_pkt_info->buf_ptr; + current_descriptor->byte_cnt = p_pkt_info->byte_cnt; + mp->tx_skb[tx_desc_curr] = (struct sk_buff *) p_pkt_info->return_info; + + mb(); + + /* Set last desc with DMA ownership and interrupt enable. */ + current_descriptor->cmd_sts = command_status | + ETH_BUFFER_OWNED_BY_DMA | ETH_TX_ENABLE_INTERRUPT; + + /* Apply send command */ + ETH_ENABLE_TX_QUEUE(mp->port_num); + + /* Finish Tx packet. Update first desc in case of Tx resource error */ + tx_desc_curr = (tx_desc_curr + 1) % MV64340_TX_QUEUE_SIZE; + + /* Update the current descriptor */ + mp->tx_curr_desc_q = tx_desc_curr; + + /* Check for ring index overlap in the Tx desc ring */ + if (tx_desc_curr == tx_desc_used) { + mp->tx_resource_err = 1; + return ETH_QUEUE_LAST_RESOURCE; + } + + return ETH_OK; +} +#endif + +/* + * eth_tx_return_desc - Free all used Tx descriptors + * + * DESCRIPTION: + * This routine returns the transmitted packet information to the caller. + * It uses the 'first' index to support Tx desc return in case a transmit + * of a packet spanned over multiple buffer still in process. + * In case the Tx queue was in "resource error" condition, where there are + * no available Tx resources, the function resets the resource error flag. + * + * INPUT: + * struct mv64340_private *mp Ethernet Port Control srtuct. + * struct pkt_info *p_pkt_info User packet buffer. + * + * OUTPUT: + * Tx ring 'first' and 'used' indexes are updated. + * + * RETURN: + * ETH_ERROR in case the routine can not access Tx desc ring. + * ETH_RETRY in case there is transmission in process. + * ETH_END_OF_JOB if the routine has nothing to release. + * ETH_OK otherwise. + * + */ +static ETH_FUNC_RET_STATUS eth_tx_return_desc(struct mv64340_private * mp, + struct pkt_info * p_pkt_info) +{ + int tx_desc_used, tx_desc_curr; +#ifdef MV64340_CHECKSUM_OFFLOAD_TX + int tx_first_desc; +#endif + volatile struct eth_tx_desc *p_tx_desc_used; + unsigned int command_status; + + /* Get the Tx Desc ring indexes */ + tx_desc_curr = mp->tx_curr_desc_q; + tx_desc_used = mp->tx_used_desc_q; +#ifdef MV64340_CHECKSUM_OFFLOAD_TX + tx_first_desc = mp->tx_first_desc_q; +#endif + p_tx_desc_used = &mp->p_tx_desc_area[tx_desc_used]; + + /* XXX Sanity check */ + if (p_tx_desc_used == NULL) + return ETH_ERROR; + + command_status = p_tx_desc_used->cmd_sts; + + /* Still transmitting... */ +#ifndef MV64340_CHECKSUM_OFFLOAD_TX + if (command_status & (ETH_BUFFER_OWNED_BY_DMA)) + return ETH_RETRY; +#endif + /* Stop release. About to overlap the current available Tx descriptor */ +#ifdef MV64340_CHECKSUM_OFFLOAD_TX + if (tx_desc_used == tx_first_desc && !mp->tx_resource_err) + return ETH_END_OF_JOB; +#else + if (tx_desc_used == tx_desc_curr && !mp->tx_resource_err) + return ETH_END_OF_JOB; +#endif + + /* Pass the packet information to the caller */ + p_pkt_info->cmd_sts = command_status; + p_pkt_info->return_info = mp->tx_skb[tx_desc_used]; + mp->tx_skb[tx_desc_used] = NULL; + + /* Update the next descriptor to release. */ + mp->tx_used_desc_q = (tx_desc_used + 1) % MV64340_TX_QUEUE_SIZE; + + /* Any Tx return cancels the Tx resource error status */ + mp->tx_resource_err = 0; + + return ETH_OK; +} + +/* + * eth_port_receive - Get received information from Rx ring. + * + * DESCRIPTION: + * This routine returns the received data to the caller. There is no + * data copying during routine operation. All information is returned + * using pointer to packet information struct passed from the caller. + * If the routine exhausts Rx ring resources then the resource error flag + * is set. + * + * INPUT: + * struct mv64340_private *mp Ethernet Port Control srtuct. + * struct pkt_info *p_pkt_info User packet buffer. + * + * OUTPUT: + * Rx ring current and used indexes are updated. + * + * RETURN: + * ETH_ERROR in case the routine can not access Rx desc ring. + * ETH_QUEUE_FULL if Rx ring resources are exhausted. + * ETH_END_OF_JOB if there is no received data. + * ETH_OK otherwise. + */ +static ETH_FUNC_RET_STATUS eth_port_receive(struct mv64340_private * mp, + struct pkt_info * p_pkt_info) +{ + int rx_next_curr_desc, rx_curr_desc, rx_used_desc; + volatile struct eth_rx_desc * p_rx_desc; + unsigned int command_status; + + /* Do not process Rx ring in case of Rx ring resource error */ + if (mp->rx_resource_err) + return ETH_QUEUE_FULL; + + /* Get the Rx Desc ring 'curr and 'used' indexes */ + rx_curr_desc = mp->rx_curr_desc_q; + rx_used_desc = mp->rx_used_desc_q; + + p_rx_desc = &mp->p_rx_desc_area[rx_curr_desc]; + + /* The following parameters are used to save readings from memory */ + command_status = p_rx_desc->cmd_sts; + + /* Nothing to receive... */ + if (command_status & (ETH_BUFFER_OWNED_BY_DMA)) + return ETH_END_OF_JOB; + + p_pkt_info->byte_cnt = (p_rx_desc->byte_cnt) - RX_BUF_OFFSET; + p_pkt_info->cmd_sts = command_status; + p_pkt_info->buf_ptr = (p_rx_desc->buf_ptr) + RX_BUF_OFFSET; + p_pkt_info->return_info = mp->rx_skb[rx_curr_desc]; + p_pkt_info->l4i_chk = p_rx_desc->buf_size; + + /* Clean the return info field to indicate that the packet has been */ + /* moved to the upper layers */ + mp->rx_skb[rx_curr_desc] = NULL; + + /* Update current index in data structure */ + rx_next_curr_desc = (rx_curr_desc + 1) % MV64340_RX_QUEUE_SIZE; + mp->rx_curr_desc_q = rx_next_curr_desc; + + /* Rx descriptors exhausted. Set the Rx ring resource error flag */ + if (rx_next_curr_desc == rx_used_desc) + mp->rx_resource_err = 1; + + mb(); + return ETH_OK; +} + +/* + * eth_rx_return_buff - Returns a Rx buffer back to the Rx ring. + * + * DESCRIPTION: + * This routine returns a Rx buffer back to the Rx ring. It retrieves the + * next 'used' descriptor and attached the returned buffer to it. + * In case the Rx ring was in "resource error" condition, where there are + * no available Rx resources, the function resets the resource error flag. + * + * INPUT: + * struct mv64340_private *mp Ethernet Port Control srtuct. + * struct pkt_info *p_pkt_info Information on the returned buffer. + * + * OUTPUT: + * New available Rx resource in Rx descriptor ring. + * + * RETURN: + * ETH_ERROR in case the routine can not access Rx desc ring. + * ETH_OK otherwise. + */ +static ETH_FUNC_RET_STATUS eth_rx_return_buff(struct mv64340_private * mp, + struct pkt_info * p_pkt_info) +{ + int used_rx_desc; /* Where to return Rx resource */ + volatile struct eth_rx_desc* p_used_rx_desc; + + /* Get 'used' Rx descriptor */ + used_rx_desc = mp->rx_used_desc_q; + p_used_rx_desc = &mp->p_rx_desc_area[used_rx_desc]; + + p_used_rx_desc->buf_ptr = p_pkt_info->buf_ptr; + p_used_rx_desc->buf_size = p_pkt_info->byte_cnt; + mp->rx_skb[used_rx_desc] = p_pkt_info->return_info; + + /* Flush the write pipe */ + mb(); + + /* Return the descriptor to DMA ownership */ + p_used_rx_desc->cmd_sts = + ETH_BUFFER_OWNED_BY_DMA | ETH_RX_ENABLE_INTERRUPT; + + /* Flush descriptor and CPU pipe */ + mb(); + + /* Move the used descriptor pointer to the next descriptor */ + mp->rx_used_desc_q = (used_rx_desc + 1) % MV64340_RX_QUEUE_SIZE; + + /* Any Rx return cancels the Rx resource error status */ + mp->rx_resource_err = 0; + + return ETH_OK; +} diff --git a/drivers/net/mv643xx_eth.h b/drivers/net/mv643xx_eth.h new file mode 100644 index 000000000..46a057d0c --- /dev/null +++ b/drivers/net/mv643xx_eth.h @@ -0,0 +1,601 @@ +#ifndef __MV64340_ETH_H__ +#define __MV64340_ETH_H__ + +#include +#include +#include +#include +#include + +#include + +#define BIT0 0x00000001 +#define BIT1 0x00000002 +#define BIT2 0x00000004 +#define BIT3 0x00000008 +#define BIT4 0x00000010 +#define BIT5 0x00000020 +#define BIT6 0x00000040 +#define BIT7 0x00000080 +#define BIT8 0x00000100 +#define BIT9 0x00000200 +#define BIT10 0x00000400 +#define BIT11 0x00000800 +#define BIT12 0x00001000 +#define BIT13 0x00002000 +#define BIT14 0x00004000 +#define BIT15 0x00008000 +#define BIT16 0x00010000 +#define BIT17 0x00020000 +#define BIT18 0x00040000 +#define BIT19 0x00080000 +#define BIT20 0x00100000 +#define BIT21 0x00200000 +#define BIT22 0x00400000 +#define BIT23 0x00800000 +#define BIT24 0x01000000 +#define BIT25 0x02000000 +#define BIT26 0x04000000 +#define BIT27 0x08000000 +#define BIT28 0x10000000 +#define BIT29 0x20000000 +#define BIT30 0x40000000 +#define BIT31 0x80000000 + +/* + * The first part is the high level driver of the gigE ethernet ports. + */ + +#define ETH_PORT0_IRQ_NUM 48 /* main high register, bit0 */ +#define ETH_PORT1_IRQ_NUM ETH_PORT0_IRQ_NUM+1 /* main high register, bit1 */ +#define ETH_PORT2_IRQ_NUM ETH_PORT0_IRQ_NUM+2 /* main high register, bit1 */ + +/* Checksum offload for Tx works */ +#define MV64340_CHECKSUM_OFFLOAD_TX +#define MV64340_NAPI +#define MV64340_TX_FAST_REFILL +#undef MV64340_COAL + +/* + * Number of RX / TX descriptors on RX / TX rings. + * Note that allocating RX descriptors is done by allocating the RX + * ring AND a preallocated RX buffers (skb's) for each descriptor. + * The TX descriptors only allocates the TX descriptors ring, + * with no pre allocated TX buffers (skb's are allocated by higher layers. + */ + +/* Default TX ring size is 1000 descriptors */ +#define MV64340_TX_QUEUE_SIZE 1000 + +/* Default RX ring size is 400 descriptors */ +#define MV64340_RX_QUEUE_SIZE 400 + +#define MV64340_TX_COAL 100 +#ifdef MV64340_COAL +#define MV64340_RX_COAL 100 +#endif + + +/* + * The second part is the low level driver of the gigE ethernet ports. * + */ + + +/* + * Header File for : MV-643xx network interface header + * + * DESCRIPTION: + * This header file contains macros typedefs and function declaration for + * the Marvell Gig Bit Ethernet Controller. + * + * DEPENDENCIES: + * None. + * + */ + +/* Default port configuration value */ +#define PORT_CONFIG_VALUE \ + ETH_UNICAST_NORMAL_MODE | \ + ETH_DEFAULT_RX_QUEUE_0 | \ + ETH_DEFAULT_RX_ARP_QUEUE_0 | \ + ETH_RECEIVE_BC_IF_NOT_IP_OR_ARP | \ + ETH_RECEIVE_BC_IF_IP | \ + ETH_RECEIVE_BC_IF_ARP | \ + ETH_CAPTURE_TCP_FRAMES_DIS | \ + ETH_CAPTURE_UDP_FRAMES_DIS | \ + ETH_DEFAULT_RX_TCP_QUEUE_0 | \ + ETH_DEFAULT_RX_UDP_QUEUE_0 | \ + ETH_DEFAULT_RX_BPDU_QUEUE_0 + +/* Default port extend configuration value */ +#define PORT_CONFIG_EXTEND_VALUE \ + ETH_SPAN_BPDU_PACKETS_AS_NORMAL | \ + ETH_PARTITION_DISABLE + + +/* Default sdma control value */ +#define PORT_SDMA_CONFIG_VALUE \ + ETH_RX_BURST_SIZE_16_64BIT | \ + GT_ETH_IPG_INT_RX(0) | \ + ETH_TX_BURST_SIZE_16_64BIT; + +#define GT_ETH_IPG_INT_RX(value) \ + ((value & 0x3fff) << 8) + +/* Default port serial control value */ +#define PORT_SERIAL_CONTROL_VALUE \ + ETH_FORCE_LINK_PASS | \ + ETH_ENABLE_AUTO_NEG_FOR_DUPLX | \ + ETH_DISABLE_AUTO_NEG_FOR_FLOW_CTRL | \ + ETH_ADV_SYMMETRIC_FLOW_CTRL | \ + ETH_FORCE_FC_MODE_NO_PAUSE_DIS_TX | \ + ETH_FORCE_BP_MODE_NO_JAM | \ + BIT9 | \ + ETH_DO_NOT_FORCE_LINK_FAIL | \ + ETH_RETRANSMIT_16_ATTEMPTS | \ + ETH_ENABLE_AUTO_NEG_SPEED_GMII | \ + ETH_DTE_ADV_0 | \ + ETH_DISABLE_AUTO_NEG_BYPASS | \ + ETH_AUTO_NEG_NO_CHANGE | \ + ETH_MAX_RX_PACKET_9700BYTE | \ + ETH_CLR_EXT_LOOPBACK | \ + ETH_SET_FULL_DUPLEX_MODE | \ + ETH_ENABLE_FLOW_CTRL_TX_RX_IN_FULL_DUPLEX + +#define RX_BUFFER_MAX_SIZE 0x4000000 +#define TX_BUFFER_MAX_SIZE 0x4000000 + +/* MAC accepet/reject macros */ +#define ACCEPT_MAC_ADDR 0 +#define REJECT_MAC_ADDR 1 + +/* Buffer offset from buffer pointer */ +#define RX_BUF_OFFSET 0x2 + +/* Gigabit Ethernet Unit Global Registers */ + +/* MIB Counters register definitions */ +#define ETH_MIB_GOOD_OCTETS_RECEIVED_LOW 0x0 +#define ETH_MIB_GOOD_OCTETS_RECEIVED_HIGH 0x4 +#define ETH_MIB_BAD_OCTETS_RECEIVED 0x8 +#define ETH_MIB_INTERNAL_MAC_TRANSMIT_ERR 0xc +#define ETH_MIB_GOOD_FRAMES_RECEIVED 0x10 +#define ETH_MIB_BAD_FRAMES_RECEIVED 0x14 +#define ETH_MIB_BROADCAST_FRAMES_RECEIVED 0x18 +#define ETH_MIB_MULTICAST_FRAMES_RECEIVED 0x1c +#define ETH_MIB_FRAMES_64_OCTETS 0x20 +#define ETH_MIB_FRAMES_65_TO_127_OCTETS 0x24 +#define ETH_MIB_FRAMES_128_TO_255_OCTETS 0x28 +#define ETH_MIB_FRAMES_256_TO_511_OCTETS 0x2c +#define ETH_MIB_FRAMES_512_TO_1023_OCTETS 0x30 +#define ETH_MIB_FRAMES_1024_TO_MAX_OCTETS 0x34 +#define ETH_MIB_GOOD_OCTETS_SENT_LOW 0x38 +#define ETH_MIB_GOOD_OCTETS_SENT_HIGH 0x3c +#define ETH_MIB_GOOD_FRAMES_SENT 0x40 +#define ETH_MIB_EXCESSIVE_COLLISION 0x44 +#define ETH_MIB_MULTICAST_FRAMES_SENT 0x48 +#define ETH_MIB_BROADCAST_FRAMES_SENT 0x4c +#define ETH_MIB_UNREC_MAC_CONTROL_RECEIVED 0x50 +#define ETH_MIB_FC_SENT 0x54 +#define ETH_MIB_GOOD_FC_RECEIVED 0x58 +#define ETH_MIB_BAD_FC_RECEIVED 0x5c +#define ETH_MIB_UNDERSIZE_RECEIVED 0x60 +#define ETH_MIB_FRAGMENTS_RECEIVED 0x64 +#define ETH_MIB_OVERSIZE_RECEIVED 0x68 +#define ETH_MIB_JABBER_RECEIVED 0x6c +#define ETH_MIB_MAC_RECEIVE_ERROR 0x70 +#define ETH_MIB_BAD_CRC_EVENT 0x74 +#define ETH_MIB_COLLISION 0x78 +#define ETH_MIB_LATE_COLLISION 0x7c + +/* Port serial status reg (PSR) */ +#define ETH_INTERFACE_GMII_MII 0 +#define ETH_INTERFACE_PCM BIT0 +#define ETH_LINK_IS_DOWN 0 +#define ETH_LINK_IS_UP BIT1 +#define ETH_PORT_AT_HALF_DUPLEX 0 +#define ETH_PORT_AT_FULL_DUPLEX BIT2 +#define ETH_RX_FLOW_CTRL_DISABLED 0 +#define ETH_RX_FLOW_CTRL_ENBALED BIT3 +#define ETH_GMII_SPEED_100_10 0 +#define ETH_GMII_SPEED_1000 BIT4 +#define ETH_MII_SPEED_10 0 +#define ETH_MII_SPEED_100 BIT5 +#define ETH_NO_TX 0 +#define ETH_TX_IN_PROGRESS BIT7 +#define ETH_BYPASS_NO_ACTIVE 0 +#define ETH_BYPASS_ACTIVE BIT8 +#define ETH_PORT_NOT_AT_PARTITION_STATE 0 +#define ETH_PORT_AT_PARTITION_STATE BIT9 +#define ETH_PORT_TX_FIFO_NOT_EMPTY 0 +#define ETH_PORT_TX_FIFO_EMPTY BIT10 + + +/* These macros describes the Port configuration reg (Px_cR) bits */ +#define ETH_UNICAST_NORMAL_MODE 0 +#define ETH_UNICAST_PROMISCUOUS_MODE BIT0 +#define ETH_DEFAULT_RX_QUEUE_0 0 +#define ETH_DEFAULT_RX_QUEUE_1 BIT1 +#define ETH_DEFAULT_RX_QUEUE_2 BIT2 +#define ETH_DEFAULT_RX_QUEUE_3 (BIT2 | BIT1) +#define ETH_DEFAULT_RX_QUEUE_4 BIT3 +#define ETH_DEFAULT_RX_QUEUE_5 (BIT3 | BIT1) +#define ETH_DEFAULT_RX_QUEUE_6 (BIT3 | BIT2) +#define ETH_DEFAULT_RX_QUEUE_7 (BIT3 | BIT2 | BIT1) +#define ETH_DEFAULT_RX_ARP_QUEUE_0 0 +#define ETH_DEFAULT_RX_ARP_QUEUE_1 BIT4 +#define ETH_DEFAULT_RX_ARP_QUEUE_2 BIT5 +#define ETH_DEFAULT_RX_ARP_QUEUE_3 (BIT5 | BIT4) +#define ETH_DEFAULT_RX_ARP_QUEUE_4 BIT6 +#define ETH_DEFAULT_RX_ARP_QUEUE_5 (BIT6 | BIT4) +#define ETH_DEFAULT_RX_ARP_QUEUE_6 (BIT6 | BIT5) +#define ETH_DEFAULT_RX_ARP_QUEUE_7 (BIT6 | BIT5 | BIT4) +#define ETH_RECEIVE_BC_IF_NOT_IP_OR_ARP 0 +#define ETH_REJECT_BC_IF_NOT_IP_OR_ARP BIT7 +#define ETH_RECEIVE_BC_IF_IP 0 +#define ETH_REJECT_BC_IF_IP BIT8 +#define ETH_RECEIVE_BC_IF_ARP 0 +#define ETH_REJECT_BC_IF_ARP BIT9 +#define ETH_TX_AM_NO_UPDATE_ERROR_SUMMARY BIT12 +#define ETH_CAPTURE_TCP_FRAMES_DIS 0 +#define ETH_CAPTURE_TCP_FRAMES_EN BIT14 +#define ETH_CAPTURE_UDP_FRAMES_DIS 0 +#define ETH_CAPTURE_UDP_FRAMES_EN BIT15 +#define ETH_DEFAULT_RX_TCP_QUEUE_0 0 +#define ETH_DEFAULT_RX_TCP_QUEUE_1 BIT16 +#define ETH_DEFAULT_RX_TCP_QUEUE_2 BIT17 +#define ETH_DEFAULT_RX_TCP_QUEUE_3 (BIT17 | BIT16) +#define ETH_DEFAULT_RX_TCP_QUEUE_4 BIT18 +#define ETH_DEFAULT_RX_TCP_QUEUE_5 (BIT18 | BIT16) +#define ETH_DEFAULT_RX_TCP_QUEUE_6 (BIT18 | BIT17) +#define ETH_DEFAULT_RX_TCP_QUEUE_7 (BIT18 | BIT17 | BIT16) +#define ETH_DEFAULT_RX_UDP_QUEUE_0 0 +#define ETH_DEFAULT_RX_UDP_QUEUE_1 BIT19 +#define ETH_DEFAULT_RX_UDP_QUEUE_2 BIT20 +#define ETH_DEFAULT_RX_UDP_QUEUE_3 (BIT20 | BIT19) +#define ETH_DEFAULT_RX_UDP_QUEUE_4 (BIT21 +#define ETH_DEFAULT_RX_UDP_QUEUE_5 (BIT21 | BIT19) +#define ETH_DEFAULT_RX_UDP_QUEUE_6 (BIT21 | BIT20) +#define ETH_DEFAULT_RX_UDP_QUEUE_7 (BIT21 | BIT20 | BIT19) +#define ETH_DEFAULT_RX_BPDU_QUEUE_0 0 +#define ETH_DEFAULT_RX_BPDU_QUEUE_1 BIT22 +#define ETH_DEFAULT_RX_BPDU_QUEUE_2 BIT23 +#define ETH_DEFAULT_RX_BPDU_QUEUE_3 (BIT23 | BIT22) +#define ETH_DEFAULT_RX_BPDU_QUEUE_4 BIT24 +#define ETH_DEFAULT_RX_BPDU_QUEUE_5 (BIT24 | BIT22) +#define ETH_DEFAULT_RX_BPDU_QUEUE_6 (BIT24 | BIT23) +#define ETH_DEFAULT_RX_BPDU_QUEUE_7 (BIT24 | BIT23 | BIT22) + + +/* These macros describes the Port configuration extend reg (Px_cXR) bits*/ +#define ETH_CLASSIFY_EN BIT0 +#define ETH_SPAN_BPDU_PACKETS_AS_NORMAL 0 +#define ETH_SPAN_BPDU_PACKETS_TO_RX_QUEUE_7 BIT1 +#define ETH_PARTITION_DISABLE 0 +#define ETH_PARTITION_ENABLE BIT2 + + +/* Tx/Rx queue command reg (RQCR/TQCR)*/ +#define ETH_QUEUE_0_ENABLE BIT0 +#define ETH_QUEUE_1_ENABLE BIT1 +#define ETH_QUEUE_2_ENABLE BIT2 +#define ETH_QUEUE_3_ENABLE BIT3 +#define ETH_QUEUE_4_ENABLE BIT4 +#define ETH_QUEUE_5_ENABLE BIT5 +#define ETH_QUEUE_6_ENABLE BIT6 +#define ETH_QUEUE_7_ENABLE BIT7 +#define ETH_QUEUE_0_DISABLE BIT8 +#define ETH_QUEUE_1_DISABLE BIT9 +#define ETH_QUEUE_2_DISABLE BIT10 +#define ETH_QUEUE_3_DISABLE BIT11 +#define ETH_QUEUE_4_DISABLE BIT12 +#define ETH_QUEUE_5_DISABLE BIT13 +#define ETH_QUEUE_6_DISABLE BIT14 +#define ETH_QUEUE_7_DISABLE BIT15 + + +/* These macros describes the Port Sdma configuration reg (SDCR) bits */ +#define ETH_RIFB BIT0 +#define ETH_RX_BURST_SIZE_1_64BIT 0 +#define ETH_RX_BURST_SIZE_2_64BIT BIT1 +#define ETH_RX_BURST_SIZE_4_64BIT BIT2 +#define ETH_RX_BURST_SIZE_8_64BIT (BIT2 | BIT1) +#define ETH_RX_BURST_SIZE_16_64BIT BIT3 +#define ETH_BLM_RX_NO_SWAP BIT4 +#define ETH_BLM_RX_BYTE_SWAP 0 +#define ETH_BLM_TX_NO_SWAP BIT5 +#define ETH_BLM_TX_BYTE_SWAP 0 +#define ETH_DESCRIPTORS_BYTE_SWAP BIT6 +#define ETH_DESCRIPTORS_NO_SWAP 0 +#define ETH_TX_BURST_SIZE_1_64BIT 0 +#define ETH_TX_BURST_SIZE_2_64BIT BIT22 +#define ETH_TX_BURST_SIZE_4_64BIT BIT23 +#define ETH_TX_BURST_SIZE_8_64BIT (BIT23 | BIT22) +#define ETH_TX_BURST_SIZE_16_64BIT BIT24 + + + +/* These macros describes the Port serial control reg (PSCR) bits */ +#define ETH_SERIAL_PORT_DISABLE 0 +#define ETH_SERIAL_PORT_ENABLE BIT0 +#define ETH_FORCE_LINK_PASS BIT1 +#define ETH_DO_NOT_FORCE_LINK_PASS 0 +#define ETH_ENABLE_AUTO_NEG_FOR_DUPLX 0 +#define ETH_DISABLE_AUTO_NEG_FOR_DUPLX BIT2 +#define ETH_ENABLE_AUTO_NEG_FOR_FLOW_CTRL 0 +#define ETH_DISABLE_AUTO_NEG_FOR_FLOW_CTRL BIT3 +#define ETH_ADV_NO_FLOW_CTRL 0 +#define ETH_ADV_SYMMETRIC_FLOW_CTRL BIT4 +#define ETH_FORCE_FC_MODE_NO_PAUSE_DIS_TX 0 +#define ETH_FORCE_FC_MODE_TX_PAUSE_DIS BIT5 +#define ETH_FORCE_BP_MODE_NO_JAM 0 +#define ETH_FORCE_BP_MODE_JAM_TX BIT7 +#define ETH_FORCE_BP_MODE_JAM_TX_ON_RX_ERR BIT8 +#define ETH_FORCE_LINK_FAIL 0 +#define ETH_DO_NOT_FORCE_LINK_FAIL BIT10 +#define ETH_RETRANSMIT_16_ATTEMPTS 0 +#define ETH_RETRANSMIT_FOREVER BIT11 +#define ETH_DISABLE_AUTO_NEG_SPEED_GMII BIT13 +#define ETH_ENABLE_AUTO_NEG_SPEED_GMII 0 +#define ETH_DTE_ADV_0 0 +#define ETH_DTE_ADV_1 BIT14 +#define ETH_DISABLE_AUTO_NEG_BYPASS 0 +#define ETH_ENABLE_AUTO_NEG_BYPASS BIT15 +#define ETH_AUTO_NEG_NO_CHANGE 0 +#define ETH_RESTART_AUTO_NEG BIT16 +#define ETH_MAX_RX_PACKET_1518BYTE 0 +#define ETH_MAX_RX_PACKET_1522BYTE BIT17 +#define ETH_MAX_RX_PACKET_1552BYTE BIT18 +#define ETH_MAX_RX_PACKET_9022BYTE (BIT18 | BIT17) +#define ETH_MAX_RX_PACKET_9192BYTE BIT19 +#define ETH_MAX_RX_PACKET_9700BYTE (BIT19 | BIT17) +#define ETH_SET_EXT_LOOPBACK BIT20 +#define ETH_CLR_EXT_LOOPBACK 0 +#define ETH_SET_FULL_DUPLEX_MODE BIT21 +#define ETH_SET_HALF_DUPLEX_MODE 0 +#define ETH_ENABLE_FLOW_CTRL_TX_RX_IN_FULL_DUPLEX BIT22 +#define ETH_DISABLE_FLOW_CTRL_TX_RX_IN_FULL_DUPLEX 0 +#define ETH_SET_GMII_SPEED_TO_10_100 0 +#define ETH_SET_GMII_SPEED_TO_1000 BIT23 +#define ETH_SET_MII_SPEED_TO_10 0 +#define ETH_SET_MII_SPEED_TO_100 BIT24 + + +/* SMI reg */ +#define ETH_SMI_BUSY BIT28 /* 0 - Write, 1 - Read */ +#define ETH_SMI_READ_VALID BIT27 /* 0 - Write, 1 - Read */ +#define ETH_SMI_OPCODE_WRITE 0 /* Completion of Read operation */ +#define ETH_SMI_OPCODE_READ BIT26 /* Operation is in progress */ + +/* SDMA command status fields macros */ + +/* Tx & Rx descriptors status */ +#define ETH_ERROR_SUMMARY (BIT0) + +/* Tx & Rx descriptors command */ +#define ETH_BUFFER_OWNED_BY_DMA (BIT31) + +/* Tx descriptors status */ +#define ETH_LC_ERROR (0 ) +#define ETH_UR_ERROR (BIT1 ) +#define ETH_RL_ERROR (BIT2 ) +#define ETH_LLC_SNAP_FORMAT (BIT9 ) + +/* Rx descriptors status */ +#define ETH_CRC_ERROR (0 ) +#define ETH_OVERRUN_ERROR (BIT1 ) +#define ETH_MAX_FRAME_LENGTH_ERROR (BIT2 ) +#define ETH_RESOURCE_ERROR ((BIT2 | BIT1)) +#define ETH_VLAN_TAGGED (BIT19) +#define ETH_BPDU_FRAME (BIT20) +#define ETH_TCP_FRAME_OVER_IP_V_4 (0 ) +#define ETH_UDP_FRAME_OVER_IP_V_4 (BIT21) +#define ETH_OTHER_FRAME_TYPE (BIT22) +#define ETH_LAYER_2_IS_ETH_V_2 (BIT23) +#define ETH_FRAME_TYPE_IP_V_4 (BIT24) +#define ETH_FRAME_HEADER_OK (BIT25) +#define ETH_RX_LAST_DESC (BIT26) +#define ETH_RX_FIRST_DESC (BIT27) +#define ETH_UNKNOWN_DESTINATION_ADDR (BIT28) +#define ETH_RX_ENABLE_INTERRUPT (BIT29) +#define ETH_LAYER_4_CHECKSUM_OK (BIT30) + +/* Rx descriptors byte count */ +#define ETH_FRAME_FRAGMENTED (BIT2) + +/* Tx descriptors command */ +#define ETH_LAYER_4_CHECKSUM_FIRST_DESC (BIT10) +#define ETH_FRAME_SET_TO_VLAN (BIT15) +#define ETH_TCP_FRAME (0 ) +#define ETH_UDP_FRAME (BIT16) +#define ETH_GEN_TCP_UDP_CHECKSUM (BIT17) +#define ETH_GEN_IP_V_4_CHECKSUM (BIT18) +#define ETH_ZERO_PADDING (BIT19) +#define ETH_TX_LAST_DESC (BIT20) +#define ETH_TX_FIRST_DESC (BIT21) +#define ETH_GEN_CRC (BIT22) +#define ETH_TX_ENABLE_INTERRUPT (BIT23) +#define ETH_AUTO_MODE (BIT30) + +/* typedefs */ + +typedef enum _eth_func_ret_status { + ETH_OK, /* Returned as expected. */ + ETH_ERROR, /* Fundamental error. */ + ETH_RETRY, /* Could not process request. Try later. */ + ETH_END_OF_JOB, /* Ring has nothing to process. */ + ETH_QUEUE_FULL, /* Ring resource error. */ + ETH_QUEUE_LAST_RESOURCE /* Ring resources about to exhaust. */ +} ETH_FUNC_RET_STATUS; + +typedef enum _eth_target { + ETH_TARGET_DRAM, + ETH_TARGET_DEVICE, + ETH_TARGET_CBS, + ETH_TARGET_PCI0, + ETH_TARGET_PCI1 +} ETH_TARGET; + +/* These are for big-endian machines. Little endian needs different + * definitions. + */ +#if defined(__BIG_ENDIAN) +struct eth_rx_desc { + u16 byte_cnt; /* Descriptor buffer byte count */ + u16 buf_size; /* Buffer size */ + u32 cmd_sts; /* Descriptor command status */ + u32 next_desc_ptr; /* Next descriptor pointer */ + u32 buf_ptr; /* Descriptor buffer pointer */ +}; + +struct eth_tx_desc { + u16 byte_cnt; /* buffer byte count */ + u16 l4i_chk; /* CPU provided TCP checksum */ + u32 cmd_sts; /* Command/status field */ + u32 next_desc_ptr; /* Pointer to next descriptor */ + u32 buf_ptr; /* pointer to buffer for this descriptor */ +}; + +#elif defined(__LITTLE_ENDIAN) +struct eth_rx_desc { + u32 cmd_sts; /* Descriptor command status */ + u16 buf_size; /* Buffer size */ + u16 byte_cnt; /* Descriptor buffer byte count */ + u32 buf_ptr; /* Descriptor buffer pointer */ + u32 next_desc_ptr; /* Next descriptor pointer */ +}; + +struct eth_tx_desc { + u32 cmd_sts; /* Command/status field */ + u16 l4i_chk; /* CPU provided TCP checksum */ + u16 byte_cnt; /* buffer byte count */ + u32 buf_ptr; /* pointer to buffer for this descriptor */ + u32 next_desc_ptr; /* Pointer to next descriptor */ +}; +#else +#error One of __BIG_ENDIAN or __LITTLE_ENDIAN must be defined +#endif + +/* Unified struct for Rx and Tx operations. The user is not required to */ +/* be familier with neither Tx nor Rx descriptors. */ +struct pkt_info { + unsigned short byte_cnt; /* Descriptor buffer byte count */ + unsigned short l4i_chk; /* Tx CPU provided TCP Checksum */ + unsigned int cmd_sts; /* Descriptor command status */ + dma_addr_t buf_ptr; /* Descriptor buffer pointer */ + struct sk_buff * return_info; /* User resource return information */ +}; + + +/* Ethernet port specific infomation */ + +struct mv64340_private { + int port_num; /* User Ethernet port number */ + u8 port_mac_addr[6]; /* User defined port MAC address. */ + u32 port_config; /* User port configuration value */ + u32 port_config_extend; /* User port config extend value */ + u32 port_sdma_config; /* User port SDMA config value */ + u32 port_serial_control; /* User port serial control value */ + u32 port_tx_queue_command; /* Port active Tx queues summary */ + u32 port_rx_queue_command; /* Port active Rx queues summary */ + + int rx_resource_err; /* Rx ring resource error flag */ + int tx_resource_err; /* Tx ring resource error flag */ + + /* Tx/Rx rings managment indexes fields. For driver use */ + + /* Next available and first returning Rx resource */ + int rx_curr_desc_q, rx_used_desc_q; + + /* Next available and first returning Tx resource */ + int tx_curr_desc_q, tx_used_desc_q; +#ifdef MV64340_CHECKSUM_OFFLOAD_TX + int tx_first_desc_q; +#endif + +#ifdef MV64340_TX_FAST_REFILL + u32 tx_clean_threshold; +#endif + + volatile struct eth_rx_desc * p_rx_desc_area; + dma_addr_t rx_desc_dma; + unsigned int rx_desc_area_size; + struct sk_buff * rx_skb[MV64340_RX_QUEUE_SIZE]; + + volatile struct eth_tx_desc * p_tx_desc_area; + dma_addr_t tx_desc_dma; + unsigned int tx_desc_area_size; + struct sk_buff * tx_skb[MV64340_TX_QUEUE_SIZE]; + + struct work_struct tx_timeout_task; + + /* + * Former struct mv64340_eth_priv members start here + */ + struct net_device_stats stats; + spinlock_t lock; + /* Size of Tx Ring per queue */ + unsigned int tx_ring_size; + /* Ammont of SKBs outstanding on Tx queue */ + unsigned int tx_ring_skbs; + /* Size of Rx Ring per queue */ + unsigned int rx_ring_size; + /* Ammount of SKBs allocated to Rx Ring per queue */ + unsigned int rx_ring_skbs; + + /* + * rx_task used to fill RX ring out of bottom half context + */ + struct work_struct rx_task; + + /* + * Used in case RX Ring is empty, which can be caused when + * system does not have resources (skb's) + */ + struct timer_list timeout; + long rx_task_busy __attribute__ ((aligned(SMP_CACHE_BYTES))); + unsigned rx_timer_flag; + + u32 rx_int_coal; + u32 tx_int_coal; +}; + +/* ethernet.h API list */ + +/* Port operation control routines */ +static void eth_port_init(struct mv64340_private *mp); +static void eth_port_reset(unsigned int eth_port_num); +static int eth_port_start(struct mv64340_private *mp); + +static void ethernet_set_config_reg(unsigned int eth_port_num, + unsigned int value); +static unsigned int ethernet_get_config_reg(unsigned int eth_port_num); + +/* Port MAC address routines */ +static void eth_port_uc_addr_set(unsigned int eth_port_num, + unsigned char *p_addr); + +/* PHY and MIB routines */ +static int ethernet_phy_reset(unsigned int eth_port_num); + +static int eth_port_write_smi_reg(unsigned int eth_port_num, + unsigned int phy_reg, + unsigned int value); + +static int eth_port_read_smi_reg(unsigned int eth_port_num, + unsigned int phy_reg, + unsigned int *value); + +static void eth_clear_mib_counters(unsigned int eth_port_num); + +/* Port data flow control routines */ +static ETH_FUNC_RET_STATUS eth_port_send(struct mv64340_private *mp, + struct pkt_info * p_pkt_info); +static ETH_FUNC_RET_STATUS eth_tx_return_desc(struct mv64340_private *mp, + struct pkt_info * p_pkt_info); +static ETH_FUNC_RET_STATUS eth_port_receive(struct mv64340_private *mp, + struct pkt_info * p_pkt_info); +static ETH_FUNC_RET_STATUS eth_rx_return_buff(struct mv64340_private *mp, + struct pkt_info * p_pkt_info); + +#endif /* __MV64340_ETH_H__ */ diff --git a/drivers/net/myri_sbus.c b/drivers/net/myri_sbus.c index c4c9cb22a..c5f95959c 100644 --- a/drivers/net/myri_sbus.c +++ b/drivers/net/myri_sbus.c @@ -85,7 +85,7 @@ static char version[] = static struct myri_eth *root_myri_dev; #endif -static void myri_reset_off(unsigned long lp, unsigned long cregs) +static void myri_reset_off(void __iomem *lp, void __iomem *cregs) { /* Clear IRQ mask. */ sbus_writel(0, lp + LANAI_EIMASK); @@ -94,7 +94,7 @@ static void myri_reset_off(unsigned long lp, unsigned long cregs) sbus_writel(CONTROL_ROFF, cregs + MYRICTRL_CTRL); } -static void myri_reset_on(unsigned long cregs) +static void myri_reset_on(void __iomem *cregs) { /* Enable RESET function. */ sbus_writel(CONTROL_RON, cregs + MYRICTRL_CTRL); @@ -103,14 +103,14 @@ static void myri_reset_on(unsigned long cregs) sbus_writel(CONTROL_DIRQ, cregs + MYRICTRL_CTRL); } -static void myri_disable_irq(unsigned long lp, unsigned long cregs) +static void myri_disable_irq(void __iomem *lp, void __iomem *cregs) { sbus_writel(CONTROL_DIRQ, cregs + MYRICTRL_CTRL); sbus_writel(0, lp + LANAI_EIMASK); sbus_writel(ISTAT_HOST, lp + LANAI_ISTAT); } -static void myri_enable_irq(unsigned long lp, unsigned long cregs) +static void myri_enable_irq(void __iomem *lp, void __iomem *cregs) { sbus_writel(CONTROL_EIRQ, cregs + MYRICTRL_CTRL); sbus_writel(ISTAT_HOST, lp + LANAI_EIMASK); @@ -119,7 +119,7 @@ static void myri_enable_irq(unsigned long lp, unsigned long cregs) static inline void bang_the_chip(struct myri_eth *mp) { struct myri_shmem *shmem = mp->shmem; - unsigned long cregs = mp->cregs; + void __iomem *cregs = mp->cregs; sbus_writel(1, &shmem->send); sbus_writel(CONTROL_WON, cregs + MYRICTRL_CTRL); @@ -128,7 +128,7 @@ static inline void bang_the_chip(struct myri_eth *mp) static int myri_do_handshake(struct myri_eth *mp) { struct myri_shmem *shmem = mp->shmem; - unsigned long cregs = mp->cregs; + void __iomem *cregs = mp->cregs; struct myri_channel *chan = &shmem->channel; int tick = 0; @@ -176,27 +176,27 @@ static int myri_do_handshake(struct myri_eth *mp) static int myri_load_lanai(struct myri_eth *mp) { struct net_device *dev = mp->dev; - struct myri_shmem *shmem = mp->shmem; - unsigned char *rptr; + struct myri_shmem __iomem *shmem = mp->shmem; + void __iomem *rptr; int i; myri_disable_irq(mp->lregs, mp->cregs); myri_reset_on(mp->cregs); - rptr = (unsigned char *) mp->lanai; + rptr = mp->lanai; for (i = 0; i < mp->eeprom.ramsz; i++) - sbus_writeb(0, &rptr[i]); + sbus_writeb(0, rptr + i); if (mp->eeprom.cpuvers >= CPUVERS_3_0) sbus_writel(mp->eeprom.cval, mp->lregs + LANAI_CVAL); /* Load executable code. */ for (i = 0; i < sizeof(lanai4_code); i++) - sbus_writeb(lanai4_code[i], &rptr[(lanai4_code_off * 2) + i]); + sbus_writeb(lanai4_code[i], rptr + (lanai4_code_off * 2) + i); /* Load data segment. */ for (i = 0; i < sizeof(lanai4_data); i++) - sbus_writeb(lanai4_data[i], &rptr[(lanai4_data_off * 2) + i]); + sbus_writeb(lanai4_data[i], rptr + (lanai4_data_off * 2) + i); /* Set device address. */ sbus_writeb(0, &shmem->addr[0]); @@ -237,15 +237,15 @@ static int myri_load_lanai(struct myri_eth *mp) static void myri_clean_rings(struct myri_eth *mp) { - struct sendq *sq = mp->sq; - struct recvq *rq = mp->rq; + struct sendq __iomem *sq = mp->sq; + struct recvq __iomem *rq = mp->rq; int i; sbus_writel(0, &rq->tail); sbus_writel(0, &rq->head); for (i = 0; i < (RX_RING_SIZE+1); i++) { if (mp->rx_skbs[i] != NULL) { - struct myri_rxd *rxd = &rq->myri_rxd[i]; + struct myri_rxd __iomem *rxd = &rq->myri_rxd[i]; u32 dma_addr; dma_addr = sbus_readl(&rxd->myri_scatters[0].addr); @@ -261,7 +261,7 @@ static void myri_clean_rings(struct myri_eth *mp) for (i = 0; i < TX_RING_SIZE; i++) { if (mp->tx_skbs[i] != NULL) { struct sk_buff *skb = mp->tx_skbs[i]; - struct myri_txd *txd = &sq->myri_txd[i]; + struct myri_txd __iomem *txd = &sq->myri_txd[i]; u32 dma_addr; dma_addr = sbus_readl(&txd->myri_gathers[0].addr); @@ -274,8 +274,8 @@ static void myri_clean_rings(struct myri_eth *mp) static void myri_init_rings(struct myri_eth *mp, int from_irq) { - struct recvq *rq = mp->rq; - struct myri_rxd *rxd = &rq->myri_rxd[0]; + struct recvq __iomem *rq = mp->rq; + struct myri_rxd __iomem *rxd = &rq->myri_rxd[0]; struct net_device *dev = mp->dev; int gfp_flags = GFP_KERNEL; int i; @@ -343,7 +343,7 @@ static void dump_ehdr_and_myripad(unsigned char *stuff) static void myri_tx(struct myri_eth *mp, struct net_device *dev) { - struct sendq *sq = mp->sq; + struct sendq __iomem *sq= mp->sq; int entry = mp->tx_old; int limit = sbus_readl(&sq->head); @@ -376,7 +376,7 @@ static unsigned short myri_type_trans(struct sk_buff *skb, struct net_device *de skb->mac.raw = (((unsigned char *)skb->data) + MYRI_PAD_LEN); skb_pull(skb, dev->hard_header_len); - eth = skb->mac.ethernet; + eth = eth_hdr(skb); #ifdef DEBUG_HEADER DHDR(("myri_type_trans: ")); @@ -411,8 +411,8 @@ static unsigned short myri_type_trans(struct sk_buff *skb, struct net_device *de static void myri_rx(struct myri_eth *mp, struct net_device *dev) { - struct recvq *rq = mp->rq; - struct recvq *rqa = mp->rqack; + struct recvq __iomem *rq = mp->rq; + struct recvq __iomem *rqa = mp->rqack; int entry = sbus_readl(&rqa->head); int limit = sbus_readl(&rqa->tail); int drops; @@ -423,11 +423,11 @@ static void myri_rx(struct myri_eth *mp, struct net_device *dev) drops = 0; DRX(("\n")); while (entry != limit) { - struct myri_rxd *rxdack = &rqa->myri_rxd[entry]; + struct myri_rxd __iomem *rxdack = &rqa->myri_rxd[entry]; u32 csum = sbus_readl(&rxdack->csum); int len = sbus_readl(&rxdack->myri_scatters[0].len); int index = sbus_readl(&rxdack->ctx); - struct myri_rxd *rxd = &rq->myri_rxd[rq->tail]; + struct myri_rxd __iomem *rxd = &rq->myri_rxd[rq->tail]; struct sk_buff *skb = mp->rx_skbs[index]; /* Ack it. */ @@ -545,7 +545,7 @@ static irqreturn_t myri_interrupt(int irq, void *dev_id, struct pt_regs *regs) { struct net_device *dev = (struct net_device *) dev_id; struct myri_eth *mp = (struct myri_eth *) dev->priv; - unsigned long lregs = mp->lregs; + void __iomem *lregs = mp->lregs; struct myri_channel *chan = &mp->shmem->channel; unsigned long flags; u32 status; @@ -610,8 +610,8 @@ 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 sendq *sq = mp->sq; - struct myri_txd *txd; + struct sendq __iomem *sq = mp->sq; + struct myri_txd __iomem *txd; unsigned long flags; unsigned int head, tail; int len, entry; @@ -998,22 +998,20 @@ static int __init myri_ether_init(struct sbus_dev *sdev, int num) printk("MyriCOM: Cannot map MyriCOM registers.\n"); goto err; } - mp->lanai = (unsigned short *) (mp->regs + (256 * 1024)); - mp->lanai3 = (unsigned int *) mp->lanai; - mp->lregs = (unsigned long) &mp->lanai[0x10000]; + mp->lanai = mp->regs + (256 * 1024); + mp->lregs = mp->lanai + (0x10000 * 2); } else { DET(("Mapping regs for cpuvers >= CPUVERS_4_0\n")); mp->cregs = sbus_ioremap(&sdev->resource[0], 0, PAGE_SIZE, "MyriCOM Control Regs"); mp->lregs = sbus_ioremap(&sdev->resource[0], (256 * 1024), PAGE_SIZE, "MyriCOM LANAI Regs"); - mp->lanai = (unsigned short *) + mp->lanai = sbus_ioremap(&sdev->resource[0], (512 * 1024), mp->eeprom.ramsz, "MyriCOM SRAM"); - mp->lanai3 = (unsigned int *) mp->lanai; } - DET(("Registers mapped: cregs[%lx] lregs[%lx] lanai[%p] lanai3[%p]\n", - mp->cregs, mp->lregs, mp->lanai, mp->lanai3)); + DET(("Registers mapped: cregs[%p] lregs[%p] lanai[%p]\n", + mp->cregs, mp->lregs, mp->lanai)); if (mp->eeprom.cpuvers >= CPUVERS_4_0) mp->shmem_base = 0xf000; @@ -1022,7 +1020,8 @@ static int __init myri_ether_init(struct sbus_dev *sdev, int num) DET(("Shared memory base is %04x, ", mp->shmem_base)); - mp->shmem = (struct myri_shmem *) &mp->lanai[mp->shmem_base]; + mp->shmem = (struct myri_shmem __iomem *) + (mp->lanai + (mp->shmem_base * 2)); DET(("shmem mapped at %p\n", mp->shmem)); mp->rqack = &mp->shmem->channel.recvqa; diff --git a/drivers/net/myri_sbus.h b/drivers/net/myri_sbus.h index 944ddfa65..9391e55a5 100644 --- a/drivers/net/myri_sbus.h +++ b/drivers/net/myri_sbus.h @@ -270,22 +270,21 @@ struct myri_eth { * to obtain good cache hit rates. */ spinlock_t irq_lock; - struct myri_shmem *shmem; /* Shared data structures. */ - unsigned long cregs; /* Control register space. */ - struct recvq *rqack; /* Where we ack rx's. */ - struct recvq *rq; /* Where we put buffers. */ - struct sendq *sq; /* Where we stuff tx's. */ + struct myri_shmem __iomem *shmem; /* Shared data structures. */ + void __iomem *cregs; /* Control register space. */ + struct recvq __iomem *rqack; /* Where we ack rx's. */ + struct recvq __iomem *rq; /* Where we put buffers. */ + struct sendq __iomem *sq; /* Where we stuff tx's. */ struct net_device *dev; /* Linux/NET dev struct. */ int tx_old; /* To speed up tx cleaning. */ - unsigned long lregs; /* Quick ptr to LANAI regs. */ + void __iomem *lregs; /* Quick ptr to LANAI regs. */ struct sk_buff *rx_skbs[RX_RING_SIZE+1];/* RX skb's */ struct sk_buff *tx_skbs[TX_RING_SIZE]; /* TX skb's */ struct net_device_stats enet_stats; /* Interface stats. */ /* These are less frequently accessed. */ - unsigned long regs; /* MyriCOM register space. */ - unsigned short *lanai; /* View 2 of register space. */ - unsigned int *lanai3; /* View 3 of register space. */ + void __iomem *regs; /* MyriCOM register space. */ + void __iomem *lanai; /* View 2 of register space. */ unsigned int myri_bursts; /* SBUS bursts. */ struct myri_eeprom eeprom; /* Local copy of EEPROM. */ unsigned int reg_size; /* Size of register space. */ diff --git a/drivers/net/sk98lin/skaddr.c b/drivers/net/sk98lin/skaddr.c index 5a7ce8ef8..abf857603 100644 --- a/drivers/net/sk98lin/skaddr.c +++ b/drivers/net/sk98lin/skaddr.c @@ -892,7 +892,7 @@ SK_AC *pAC, /* adapter context */ SK_IOC IoC, /* I/O context */ SK_U32 PortNumber) /* Port Number */ { - int ReturnCode; + int ReturnCode = 0; #if (!defined(SK_SLIM) || defined(DEBUG)) if (PortNumber >= (SK_U32) pAC->GIni.GIMacsFound) { return (SK_ADDR_ILLEGAL_PORT); @@ -1424,7 +1424,7 @@ SK_IOC IoC, /* I/O context */ SK_U32 PortNumber, /* port whose promiscuous mode changes */ int NewPromMode) /* new promiscuous mode */ { - int ReturnCode; + int ReturnCode = 0; #if (!defined(SK_SLIM) || defined(DEBUG)) if (PortNumber >= (SK_U32) pAC->GIni.GIMacsFound) { return (SK_ADDR_ILLEGAL_PORT); diff --git a/drivers/net/slip.c b/drivers/net/slip.c index 6209a35aa..0c1995bc2 100644 --- a/drivers/net/slip.c +++ b/drivers/net/slip.c @@ -672,7 +672,9 @@ static int slip_receive_room(struct tty_struct *tty) * Handle the 'receiver data ready' interrupt. * This function is called by the 'tty_io' module in the kernel when * a block of SLIP data has been received, which can now be decapsulated - * and sent on to some IP layer for further processing. + * and sent on to some IP layer for further processing. This will not + * be re-entered while running but other ldisc functions may be called + * in parallel */ static void slip_receive_buf(struct tty_struct *tty, const unsigned char *cp, char *fp, int count) @@ -841,9 +843,11 @@ sl_alloc(dev_t line) * SLIP line discipline is called for. Because we are * sure the tty line exists, we only have to link it to * a free SLIP channel... + * + * Called in process context serialized from other ldisc calls. */ -static int -slip_open(struct tty_struct *tty) + +static int slip_open(struct tty_struct *tty) { struct slip *sl; int err; @@ -876,11 +880,11 @@ slip_open(struct tty_struct *tty) tty->disc_data = sl; sl->line = tty_devnum(tty); sl->pid = current->pid; + + /* FIXME: already done before we were called - seems this can go */ if (tty->driver->flush_buffer) tty->driver->flush_buffer(tty); - if (tty->ldisc.flush_buffer) - tty->ldisc.flush_buffer(tty); - + if (!test_bit(SLF_INUSE, &sl->flags)) { /* Perform the low-level SLIP initialization. */ if ((err = sl_alloc_bufs(sl, SL_MTU)) != 0) @@ -923,6 +927,9 @@ err_exit: } /* + + FIXME: 1,2 are fixed 3 was never true anyway. + Let me to blame a bit. 1. TTY module calls this funstion on soft interrupt. 2. TTY module calls this function WITH MASKED INTERRUPTS! @@ -941,9 +948,8 @@ err_exit: /* * Close down a SLIP channel. - * This means flushing out any pending queues, and then restoring the - * TTY line discipline to what it was before it got hooked to SLIP - * (which usually is TTY again). + * This means flushing out any pending queues, and then returning. This + * call is serialized against other ldisc functions. */ static void slip_close(struct tty_struct *tty) diff --git a/drivers/net/smc91x.c b/drivers/net/smc91x.c index 85fae0965..5d224b642 100644 --- a/drivers/net/smc91x.c +++ b/drivers/net/smc91x.c @@ -55,6 +55,9 @@ * smc_phy_configure * - clean up (and fix stack overrun) in PHY * MII read/write functions + * 09/15/04 Hayato Fujiwara - Add m32r support. + * - Modify for SMP kernel; Change spin-locked + * regions. */ static const char version[] = "smc91x.c: v1.0, mar 07 2003 by Nicolas Pitre \n"; @@ -256,24 +259,18 @@ static void PRINT_PKT(u_char *buf, int length) /* this enables an interrupt in the interrupt mask register */ #define SMC_ENABLE_INT(x) do { \ - unsigned long flags; \ unsigned char mask; \ - spin_lock_irqsave(&lp->lock, flags); \ mask = SMC_GET_INT_MASK(); \ mask |= (x); \ SMC_SET_INT_MASK(mask); \ - spin_unlock_irqrestore(&lp->lock, flags); \ } while (0) /* this disables an interrupt from the interrupt mask register */ #define SMC_DISABLE_INT(x) do { \ - unsigned long flags; \ unsigned char mask; \ - spin_lock_irqsave(&lp->lock, flags); \ mask = SMC_GET_INT_MASK(); \ mask &= ~(x); \ SMC_SET_INT_MASK(mask); \ - spin_unlock_irqrestore(&lp->lock, flags); \ } while (0) /* @@ -580,9 +577,12 @@ static int smc_hard_start_xmit(struct sk_buff *skb, struct net_device *dev) struct smc_local *lp = netdev_priv(dev); unsigned long ioaddr = dev->base_addr; unsigned int numPages, poll_count, status, saved_bank; + unsigned long flags; DBG(3, "%s: %s\n", dev->name, __FUNCTION__); + spin_lock_irqsave(&lp->lock, flags); + BUG_ON(lp->saved_skb != NULL); lp->saved_skb = skb; @@ -604,6 +604,7 @@ static int smc_hard_start_xmit(struct sk_buff *skb, struct net_device *dev) lp->stats.tx_errors++; lp->stats.tx_dropped++; dev_kfree_skb(skb); + spin_unlock_irqrestore(&lp->lock, flags); return 0; } @@ -652,6 +653,7 @@ static int smc_hard_start_xmit(struct sk_buff *skb, struct net_device *dev) } SMC_SELECT_BANK(saved_bank); + spin_unlock_irqrestore(&lp->lock, flags); return 0; } @@ -1166,6 +1168,8 @@ static irqreturn_t smc_interrupt(int irq, void *dev_id, struct pt_regs *regs) DBG(3, "%s: %s\n", dev->name, __FUNCTION__); + spin_lock(&lp->lock); + saved_bank = SMC_CURRENT_BANK(); SMC_SELECT_BANK(2); saved_pointer = SMC_GET_PTR(); @@ -1189,8 +1193,6 @@ static irqreturn_t smc_interrupt(int irq, void *dev_id, struct pt_regs *regs) if (!status) break; - spin_lock(&lp->lock); - if (status & IM_RCV_INT) { DBG(3, "%s: RX irq\n", dev->name); smc_rcv(dev); @@ -1239,7 +1241,6 @@ static irqreturn_t smc_interrupt(int irq, void *dev_id, struct pt_regs *regs) PRINTK("%s: UNSUPPORTED: ERCV INTERRUPT \n", dev->name); } - spin_unlock(&lp->lock); } while (--timeout); /* restore register states */ @@ -1249,6 +1250,7 @@ static irqreturn_t smc_interrupt(int irq, void *dev_id, struct pt_regs *regs) DBG(3, "%s: Interrupt done (%d loops)\n", dev->name, 8-timeout); + spin_unlock(&lp->lock); /* * We return IRQ_HANDLED unconditionally here even if there was * nothing to do. There is a possibility that a packet might @@ -1264,7 +1266,9 @@ static irqreturn_t smc_interrupt(int irq, void *dev_id, struct pt_regs *regs) static void smc_timeout(struct net_device *dev) { struct smc_local *lp = netdev_priv(dev); + unsigned long flags; + spin_lock_irqsave(&lp->lock, flags); DBG(2, "%s: %s\n", dev->name, __FUNCTION__); smc_reset(dev); @@ -1298,6 +1302,9 @@ static void smc_timeout(struct net_device *dev) } /* We can accept TX packets again */ dev->trans_start = jiffies; + + spin_unlock_irqrestore(&lp->lock, flags); + netif_wake_queue(dev); } @@ -1438,7 +1445,7 @@ smc_open(struct net_device *dev) * address using ifconfig eth0 hw ether xx:xx:xx:xx:xx:xx */ if (!is_valid_ether_addr(dev->dev_addr)) { - DBG(2, (KERN_DEBUG "smc_open: no valid ethernet hw addr\n")); + DBG(2, "smc_open: no valid ethernet hw addr\n"); return -EINVAL; } @@ -1878,7 +1885,9 @@ static int __init smc_probe(struct net_device *dev, unsigned long ioaddr) if (retval) goto err_out; +#if !defined(__m32r__) set_irq_type(dev->irq, IRQT_RISING); +#endif #ifdef SMC_USE_PXA_DMA { int dma = pxa_request_dma(dev->name, DMA_PRIO_LOW, @@ -2154,10 +2163,12 @@ static struct device_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 driver_register(&smc_driver); diff --git a/drivers/net/smc91x.h b/drivers/net/smc91x.h index 7679022c0..cf74b74c3 100644 --- a/drivers/net/smc91x.h +++ b/drivers/net/smc91x.h @@ -160,6 +160,19 @@ SMC_outw(u16 val, unsigned long ioaddr, int reg) #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 +#define SMC_CAN_USE_16BIT 1 +#define SMC_CAN_USE_32BIT 0 + +#define SMC_inb(a, r) inb((a) + (r) - 0xa0000000) +#define SMC_inw(a, r) inw((a) + (r) - 0xa0000000) +#define SMC_outb(v, a, r) outb(v, (a) + (r) - 0xa0000000) +#define SMC_outw(v, a, r) outw(v, (a) + (r) - 0xa0000000) +#define SMC_insw(a, r, p, l) insw((a) + (r) - 0xa0000000, p, l) +#define SMC_outsw(a, r, p, l) outsw((a) + (r) - 0xa0000000, p, l) + #else #define SMC_CAN_USE_8BIT 1 @@ -809,9 +822,10 @@ static const char * chip_ids[ 16 ] = { do { \ char *__ptr = (p); \ int __len = (l); \ - if (__len >= 2 && (long)__ptr & 2) { \ + if (__len >= 2 && (unsigned long)__ptr & 2) { \ __len -= 2; \ - SMC_outw( *((u16 *)__ptr)++, ioaddr, DATA_REG );\ + SMC_outw( *(u16 *)__ptr, ioaddr, DATA_REG ); \ + __ptr += 2; \ } \ SMC_outsl( ioaddr, DATA_REG, __ptr, __len >> 2); \ if (__len & 2) { \ @@ -823,7 +837,7 @@ static const char * chip_ids[ 16 ] = { do { \ char *__ptr = (p); \ int __len = (l); \ - if ((long)__ptr & 2) { \ + if ((unsigned long)__ptr & 2) { \ /* \ * We want 32bit alignment here. \ * Since some buses perform a full 32bit \ @@ -831,7 +845,7 @@ static const char * chip_ids[ 16 ] = { * SMC_inw() here. Back both source (on chip \ * and destination) pointers of 2 bytes. \ */ \ - (long)__ptr &= ~2; \ + __ptr -= 2; \ __len += 2; \ SMC_SET_PTR( 2|PTR_READ|PTR_RCV|PTR_AUTOINC ); \ } \ diff --git a/drivers/net/sunbmac.c b/drivers/net/sunbmac.c index ec05d1bd1..25d35022b 100644 --- a/drivers/net/sunbmac.c +++ b/drivers/net/sunbmac.c @@ -68,7 +68,7 @@ static struct bigmac *root_bigmac_dev; #define QEC_RESET_TRIES 200 -static int qec_global_reset(unsigned long gregs) +static int qec_global_reset(void __iomem *gregs) { int tries = QEC_RESET_TRIES; @@ -88,7 +88,7 @@ static int qec_global_reset(unsigned long gregs) static void qec_init(struct bigmac *bp) { - unsigned long gregs = bp->gregs; + void __iomem *gregs = bp->gregs; struct sbus_dev *qec_sdev = bp->qec_sdev; u8 bsizes = bp->bigmac_bursts; u32 regval; @@ -117,7 +117,7 @@ static void qec_init(struct bigmac *bp) #define TX_RESET_TRIES 32 #define RX_RESET_TRIES 32 -static void bigmac_tx_reset(unsigned long bregs) +static void bigmac_tx_reset(void __iomem *bregs) { int tries = TX_RESET_TRIES; @@ -137,7 +137,7 @@ static void bigmac_tx_reset(unsigned long bregs) } } -static void bigmac_rx_reset(unsigned long bregs) +static void bigmac_rx_reset(void __iomem *bregs) { int tries = RX_RESET_TRIES; @@ -159,7 +159,7 @@ static void bigmac_stop(struct bigmac *bp) bigmac_rx_reset(bp->bregs); } -static void bigmac_get_counters(struct bigmac *bp, unsigned long bregs) +static void bigmac_get_counters(struct bigmac *bp, void __iomem *bregs) { struct net_device_stats *stats = &bp->enet_stats; @@ -244,7 +244,7 @@ static void bigmac_init_rings(struct bigmac *bp, int from_irq) #define MGMT_CLKON (MGMT_PAL_INT_MDIO|MGMT_PAL_EXT_MDIO|MGMT_PAL_OENAB|MGMT_PAL_DCLOCK) #define MGMT_CLKOFF (MGMT_PAL_INT_MDIO|MGMT_PAL_EXT_MDIO|MGMT_PAL_OENAB) -static void idle_transceiver(unsigned long tregs) +static void idle_transceiver(void __iomem *tregs) { int i = 20; @@ -256,7 +256,7 @@ static void idle_transceiver(unsigned long tregs) } } -static void write_tcvr_bit(struct bigmac *bp, unsigned long tregs, int bit) +static void write_tcvr_bit(struct bigmac *bp, void __iomem *tregs, int bit) { if (bp->tcvr_type == internal) { bit = (bit & 1) << 3; @@ -279,7 +279,7 @@ static void write_tcvr_bit(struct bigmac *bp, unsigned long tregs, int bit) } } -static int read_tcvr_bit(struct bigmac *bp, unsigned long tregs) +static int read_tcvr_bit(struct bigmac *bp, void __iomem *tregs) { int retval = 0; @@ -302,7 +302,7 @@ static int read_tcvr_bit(struct bigmac *bp, unsigned long tregs) return retval; } -static int read_tcvr_bit2(struct bigmac *bp, unsigned long tregs) +static int read_tcvr_bit2(struct bigmac *bp, void __iomem *tregs) { int retval = 0; @@ -325,7 +325,7 @@ static int read_tcvr_bit2(struct bigmac *bp, unsigned long tregs) } static void put_tcvr_byte(struct bigmac *bp, - unsigned long tregs, + void __iomem *tregs, unsigned int byte) { int shift = 4; @@ -336,7 +336,7 @@ static void put_tcvr_byte(struct bigmac *bp, } while (shift >= 0); } -static void bigmac_tcvr_write(struct bigmac *bp, unsigned long tregs, +static void bigmac_tcvr_write(struct bigmac *bp, void __iomem *tregs, int reg, unsigned short val) { int shift; @@ -376,7 +376,7 @@ static void bigmac_tcvr_write(struct bigmac *bp, unsigned long tregs, } static unsigned short bigmac_tcvr_read(struct bigmac *bp, - unsigned long tregs, + void __iomem *tregs, int reg) { unsigned short retval = 0; @@ -444,7 +444,7 @@ static unsigned short bigmac_tcvr_read(struct bigmac *bp, static void bigmac_tcvr_init(struct bigmac *bp) { - unsigned long tregs = bp->tregs; + void __iomem *tregs = bp->tregs; u32 mpal; idle_transceiver(tregs); @@ -482,7 +482,7 @@ static void bigmac_tcvr_init(struct bigmac *bp) static int bigmac_init(struct bigmac *, int); -static int try_next_permutation(struct bigmac *bp, unsigned long tregs) +static int try_next_permutation(struct bigmac *bp, void __iomem *tregs) { if (bp->sw_bmcr & BMCR_SPEED100) { int timeout; @@ -518,7 +518,7 @@ static int try_next_permutation(struct bigmac *bp, unsigned long tregs) static void bigmac_timer(unsigned long data) { struct bigmac *bp = (struct bigmac *) data; - unsigned long tregs = bp->tregs; + void __iomem *tregs = bp->tregs; int restart_timer = 0; bp->timer_ticks++; @@ -573,7 +573,7 @@ static void bigmac_timer(unsigned long data) */ static void bigmac_begin_auto_negotiation(struct bigmac *bp) { - unsigned long tregs = bp->tregs; + void __iomem *tregs = bp->tregs; int timeout; /* Grab new software copies of PHY registers. */ @@ -612,9 +612,9 @@ static void bigmac_begin_auto_negotiation(struct bigmac *bp) static int bigmac_init(struct bigmac *bp, int from_irq) { - unsigned long gregs = bp->gregs; - unsigned long cregs = bp->creg; - unsigned long bregs = bp->bregs; + void __iomem *gregs = bp->gregs; + void __iomem *cregs = bp->creg; + void __iomem *bregs = bp->bregs; unsigned char *e = &bp->dev->dev_addr[0]; /* Latch current counters into statistics. */ @@ -987,7 +987,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; - unsigned long bregs = bp->bregs; + void __iomem *bregs = bp->bregs; struct dev_mc_list *dmi = dev->mc_list; char *addrs; int i; diff --git a/drivers/net/sunbmac.h b/drivers/net/sunbmac.h index b4c5309b3..5674003fc 100644 --- a/drivers/net/sunbmac.h +++ b/drivers/net/sunbmac.h @@ -303,10 +303,10 @@ enum bigmac_timer_state { }; struct bigmac { - unsigned long gregs; /* QEC Global Registers */ - unsigned long creg; /* QEC BigMAC Channel Registers */ - unsigned long bregs; /* BigMAC Registers */ - unsigned long tregs; /* BigMAC Transceiver */ + void __iomem *gregs; /* QEC Global Registers */ + void __iomem *creg; /* QEC BigMAC Channel Registers */ + void __iomem *bregs; /* BigMAC Registers */ + void __iomem *tregs; /* BigMAC Transceiver */ struct bmac_init_block *bmac_block; /* RX and TX descriptors */ __u32 bblock_dvma; /* RX and TX descriptors */ diff --git a/drivers/net/sunhme.h b/drivers/net/sunhme.h index f2784d217..34e9f953c 100644 --- a/drivers/net/sunhme.h +++ b/drivers/net/sunhme.h @@ -397,7 +397,7 @@ struct quattro; /* Happy happy, joy joy! */ struct happy_meal { - unsigned long gregs; /* Happy meal global registers */ + void __iomem *gregs; /* Happy meal global registers */ struct hmeal_init_block *happy_block; /* RX and TX descriptors (CPU addr) */ #if defined(CONFIG_SBUS) && defined(CONFIG_PCI) @@ -423,14 +423,14 @@ struct happy_meal { struct net_device_stats net_stats; /* Statistical counters */ #if defined(CONFIG_SBUS) && defined(CONFIG_PCI) - u32 (*read32)(unsigned long); - void (*write32)(unsigned long, u32); + u32 (*read32)(void __iomem *); + void (*write32)(void __iomem *, u32); #endif - unsigned long etxregs; /* External transmitter regs */ - unsigned long erxregs; /* External receiver regs */ - unsigned long bigmacregs; /* BIGMAC core regs */ - unsigned long tcvregs; /* MIF transceiver regs */ + void __iomem *etxregs; /* External transmitter regs */ + void __iomem *erxregs; /* External receiver regs */ + void __iomem *bigmacregs; /* BIGMAC core regs */ + void __iomem *tcvregs; /* MIF transceiver regs */ dma_addr_t hblock_dvma; /* DVMA visible address happy block */ unsigned int happy_flags; /* Driver state flags */ diff --git a/drivers/net/sunlance.c b/drivers/net/sunlance.c index e80311524..6e4bb5781 100644 --- a/drivers/net/sunlance.c +++ b/drivers/net/sunlance.c @@ -230,9 +230,9 @@ struct lance_init_block { ((__u32)(((unsigned long)(&(((struct lance_init_block *)0)->rt[elem][0]))))) struct lance_private { - unsigned long lregs; /* Lance RAP/RDP regs. */ - unsigned long dregs; /* DMA controller regs. */ - volatile struct lance_init_block *init_block; + void __iomem *lregs; /* Lance RAP/RDP regs. */ + void __iomem *dregs; /* DMA controller regs. */ + struct lance_init_block *init_block; spinlock_t lock; @@ -270,7 +270,7 @@ struct lance_private { #define LANCE_REG_SIZE 0x04UL #define STOP_LANCE(__lp) \ -do { unsigned long __base = (__lp)->lregs; \ +do { void __iomem *__base = (__lp)->lregs; \ sbus_writew(LE_CSR0, __base + RAP); \ sbus_writew(LE_C0_STOP, __base + RDP); \ } while (0) @@ -314,7 +314,7 @@ static void load_csrs(struct lance_private *lp) static void lance_init_ring_dvma(struct net_device *dev) { struct lance_private *lp = netdev_priv(dev); - volatile struct lance_init_block *ib = lp->init_block; + struct lance_init_block *ib = lp->init_block; dma_addr_t aib = lp->init_block_dvma; __u32 leptr; int i; @@ -371,7 +371,7 @@ static void lance_init_ring_dvma(struct net_device *dev) static void lance_init_ring_pio(struct net_device *dev) { struct lance_private *lp = netdev_priv(dev); - volatile struct lance_init_block *ib = lp->init_block; + struct lance_init_block *ib = lp->init_block; u32 leptr; int i; @@ -501,8 +501,8 @@ static int init_restart_lance(struct lance_private *lp) static void lance_rx_dvma(struct net_device *dev) { struct lance_private *lp = netdev_priv(dev); - volatile struct lance_init_block *ib = lp->init_block; - volatile struct lance_rx_desc *rd; + struct lance_init_block *ib = lp->init_block; + struct lance_rx_desc *rd; u8 bits; int len, entry = lp->rx_new; struct sk_buff *skb; @@ -564,14 +564,14 @@ static void lance_rx_dvma(struct net_device *dev) static void lance_tx_dvma(struct net_device *dev) { struct lance_private *lp = netdev_priv(dev); - volatile struct lance_init_block *ib = lp->init_block; + struct lance_init_block *ib = lp->init_block; int i, j; spin_lock(&lp->lock); j = lp->tx_old; for (i = j; i != lp->tx_new; i = j) { - volatile struct lance_tx_desc *td = &ib->btx_ring [i]; + struct lance_tx_desc *td = &ib->btx_ring [i]; u8 bits = td->tmd1_bits; /* If we hit a packet not owned by us, stop */ @@ -641,12 +641,12 @@ out: spin_unlock(&lp->lock); } -static void lance_piocopy_to_skb(struct sk_buff *skb, volatile void *piobuf, int len) +static void lance_piocopy_to_skb(struct sk_buff *skb, void __iomem *piobuf, int len) { u16 *p16 = (u16 *) skb->data; u32 *p32; u8 *p8; - unsigned long pbuf = (unsigned long) piobuf; + void __iomem *pbuf = piobuf; /* We know here that both src and dest are on a 16bit boundary. */ *p16++ = sbus_readw(pbuf); @@ -674,8 +674,8 @@ static void lance_piocopy_to_skb(struct sk_buff *skb, volatile void *piobuf, int static void lance_rx_pio(struct net_device *dev) { struct lance_private *lp = netdev_priv(dev); - volatile struct lance_init_block *ib = lp->init_block; - volatile struct lance_rx_desc *rd; + struct lance_init_block *ib = lp->init_block; + struct lance_rx_desc *rd; unsigned char bits; int len, entry; struct sk_buff *skb; @@ -736,14 +736,14 @@ static void lance_rx_pio(struct net_device *dev) static void lance_tx_pio(struct net_device *dev) { struct lance_private *lp = netdev_priv(dev); - volatile struct lance_init_block *ib = lp->init_block; + struct lance_init_block *ib = lp->init_block; int i, j; spin_lock(&lp->lock); j = lp->tx_old; for (i = j; i != lp->tx_new; i = j) { - volatile struct lance_tx_desc *td = &ib->btx_ring [i]; + struct lance_tx_desc *td = &ib->btx_ring [i]; u8 bits = sbus_readb(&td->tmd1_bits); /* If we hit a packet not owned by us, stop */ @@ -880,7 +880,7 @@ static irqreturn_t lance_interrupt(int irq, void *dev_id, struct pt_regs *regs) static void build_fake_packet(struct lance_private *lp) { struct net_device *dev = lp->dev; - volatile struct lance_init_block *ib = lp->init_block; + struct lance_init_block *ib = lp->init_block; u16 *packet; struct ethhdr *eth; int i, entry; @@ -916,7 +916,7 @@ struct net_device *last_dev; static int lance_open(struct net_device *dev) { struct lance_private *lp = netdev_priv(dev); - volatile struct lance_init_block *ib = lp->init_block; + struct lance_init_block *ib = lp->init_block; int status = 0; last_dev = dev; @@ -1006,9 +1006,9 @@ static int lance_reset(struct net_device *dev) return status; } -static void lance_piocopy_from_skb(volatile void *dest, unsigned char *src, int len) +static void lance_piocopy_from_skb(void __iomem *dest, unsigned char *src, int len) { - unsigned long piobuf = (unsigned long) dest; + void __iomem *piobuf = dest; u32 *p32; u16 *p16; u8 *p8; @@ -1064,11 +1064,11 @@ static void lance_piocopy_from_skb(volatile void *dest, unsigned char *src, int sbus_writeb(src[0], piobuf); } -static void lance_piozero(volatile void *dest, int len) +static void lance_piozero(void __iomem *dest, int len) { - unsigned long piobuf = (unsigned long) dest; + void __iomem *piobuf = dest; - if (piobuf & 1) { + if ((unsigned long)piobuf & 1) { sbus_writeb(0, piobuf); piobuf += 1; len -= 1; @@ -1079,7 +1079,7 @@ static void lance_piozero(volatile void *dest, int len) sbus_writeb(0, piobuf); return; } - if (piobuf & 2) { + if ((unsigned long)piobuf & 2) { sbus_writew(0, piobuf); piobuf += 2; len -= 2; @@ -1113,7 +1113,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 = netdev_priv(dev); - volatile struct lance_init_block *ib = lp->init_block; + struct lance_init_block *ib = lp->init_block; int entry, skblen, len; skblen = skb->len; @@ -1174,8 +1174,8 @@ static struct net_device_stats *lance_get_stats(struct net_device *dev) static void lance_load_multicast(struct net_device *dev) { struct lance_private *lp = netdev_priv(dev); - volatile struct lance_init_block *ib = lp->init_block; - volatile u16 *mcast_table = (u16 *) &ib->filter; + struct lance_init_block *ib = lp->init_block; + u16 *mcast_table = (u16 *) &ib->filter; struct dev_mc_list *dmi = dev->mc_list; char *addrs; int i; @@ -1224,7 +1224,7 @@ static void lance_load_multicast(struct net_device *dev) static void lance_set_multicast(struct net_device *dev) { struct lance_private *lp = netdev_priv(dev); - volatile struct lance_init_block *ib = lp->init_block; + struct lance_init_block *ib = lp->init_block; u16 mode; if (!netif_running(dev)) @@ -1277,12 +1277,12 @@ static void lance_free_hwresources(struct lance_private *lp) sbus_iounmap(lp->lregs, LANCE_REG_SIZE); if (lp->init_block != NULL) { if (lp->pio_buffer) { - sbus_iounmap((unsigned long)lp->init_block, + sbus_iounmap(lp->init_block, sizeof(struct lance_init_block)); } else { sbus_free_consistent(lp->sdev, sizeof(struct lance_init_block), - (void *)lp->init_block, + lp->init_block, lp->init_block_dvma); } } @@ -1349,7 +1349,7 @@ static int __init sparc_lance_init(struct sbus_dev *sdev, lp->sdev = sdev; if (lebuffer) { - lp->init_block = (volatile struct lance_init_block *) + lp->init_block = sbus_ioremap(&lebuffer->resource[0], 0, sizeof(struct lance_init_block), "lebuffer"); if (lp->init_block == NULL) { @@ -1362,7 +1362,7 @@ static int __init sparc_lance_init(struct sbus_dev *sdev, lp->rx = lance_rx_pio; lp->tx = lance_tx_pio; } else { - lp->init_block = (volatile struct lance_init_block *) + lp->init_block = sbus_alloc_consistent(sdev, sizeof(struct lance_init_block), &lp->init_block_dvma); if (lp->init_block == NULL || diff --git a/drivers/net/sunqe.c b/drivers/net/sunqe.c index 39a4b40b4..1ed3ff5b3 100644 --- a/drivers/net/sunqe.c +++ b/drivers/net/sunqe.c @@ -49,7 +49,7 @@ static void qe_set_multicast(struct net_device *dev); #define QEC_RESET_TRIES 200 -static inline int qec_global_reset(unsigned long gregs) +static inline int qec_global_reset(void __iomem *gregs) { int tries = QEC_RESET_TRIES; @@ -73,8 +73,8 @@ static inline int qec_global_reset(unsigned long gregs) static inline int qe_stop(struct sunqe *qep) { - unsigned long cregs = qep->qcregs; - unsigned long mregs = qep->mregs; + void __iomem *cregs = qep->qcregs; + void __iomem *mregs = qep->mregs; int tries; /* Reset the MACE, then the QEC channel. */ @@ -130,9 +130,9 @@ static void qe_init_rings(struct sunqe *qep) static int qe_init(struct sunqe *qep, int from_irq) { struct sunqec *qecp = qep->parent; - unsigned long cregs = qep->qcregs; - unsigned long mregs = qep->mregs; - unsigned long gregs = qecp->gregs; + void __iomem *cregs = qep->qcregs; + void __iomem *mregs = qep->mregs; + void __iomem *gregs = qecp->gregs; unsigned char *e = &qep->dev->dev_addr[0]; u32 tmp; int i; @@ -699,7 +699,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; - unsigned long mregs = qep->mregs; + void __iomem *mregs = qep->mregs; u8 phyconfig; spin_lock_irq(&qep->lock); diff --git a/drivers/net/sunqe.h b/drivers/net/sunqe.h index b90ed5db4..af34f3611 100644 --- a/drivers/net/sunqe.h +++ b/drivers/net/sunqe.h @@ -311,7 +311,7 @@ struct qe_init_block { struct sunqe; struct sunqec { - unsigned long gregs; /* QEC Global Registers */ + void __iomem *gregs; /* QEC Global Registers */ struct sunqe *qes[4]; /* Each child MACE */ unsigned int qec_bursts; /* Support burst sizes */ struct sbus_dev *qec_sdev; /* QEC's SBUS device */ @@ -331,8 +331,8 @@ struct sunqe_buffers { ((__u32)((unsigned long)(&(((struct sunqe_buffers *)0)->mem[elem][0])))) struct sunqe { - unsigned long qcregs; /* QEC per-channel Registers */ - unsigned long mregs; /* Per-channel MACE Registers */ + void __iomem *qcregs; /* QEC per-channel Registers */ + void __iomem *mregs; /* Per-channel MACE Registers */ struct qe_init_block *qe_block; /* RX and TX descriptors */ __u32 qblock_dvma; /* RX and TX descriptors */ spinlock_t lock; /* Protects txfull state */ diff --git a/drivers/net/tokenring/olympic.h b/drivers/net/tokenring/olympic.h index e209b79d6..2fc59c997 100644 --- a/drivers/net/tokenring/olympic.h +++ b/drivers/net/tokenring/olympic.h @@ -251,8 +251,8 @@ struct olympic_private { u16 arb; /* be16 */ u16 asb; /* be16 */ - u8 *olympic_mmio; - u8 *olympic_lap; + u8 __iomem *olympic_mmio; + u8 __iomem *olympic_lap; struct pci_dev *pdev ; char *olympic_card_name ; diff --git a/drivers/net/via-velocity.c b/drivers/net/via-velocity.c index f053d2a9e..57a6d2924 100644 --- a/drivers/net/via-velocity.c +++ b/drivers/net/via-velocity.c @@ -78,6 +78,8 @@ #include #include #include +#include +#include #include "via-velocity.h" @@ -226,7 +228,10 @@ VELOCITY_PARAM(wol_opts, "Wake On Lan options"); VELOCITY_PARAM(int_works, "Number of packets per interrupt services"); -static int velocity_found1(struct pci_dev *pdev, const struct pci_device_id *ent); +static int rx_copybreak = 200; +MODULE_PARM(rx_copybreak, "i"); +MODULE_PARM_DESC(rx_copybreak, "Copy breakpoint for copy-only-tiny-frames"); + static void velocity_init_info(struct pci_dev *pdev, struct velocity_info *vptr, struct velocity_info_tbl *info); static int velocity_get_pci_info(struct velocity_info *, struct pci_dev *pdev); static void velocity_print_info(struct velocity_info *vptr); @@ -238,10 +243,8 @@ static void velocity_set_multi(struct net_device *dev); static struct net_device_stats *velocity_get_stats(struct net_device *dev); static int velocity_ioctl(struct net_device *dev, struct ifreq *rq, int cmd); static int velocity_close(struct net_device *dev); -static int velocity_rx_srv(struct velocity_info *vptr, int status); static int velocity_receive_frame(struct velocity_info *, int idx); static int velocity_alloc_rx_buf(struct velocity_info *, int idx); -static void velocity_init_registers(struct velocity_info *vptr, enum velocity_init_type type); static void velocity_free_rd_ring(struct velocity_info *vptr); static void velocity_free_tx_buf(struct velocity_info *vptr, struct velocity_td_info *); static int velocity_soft_reset(struct velocity_info *vptr); @@ -254,25 +257,40 @@ static void enable_flow_control_ability(struct velocity_info *vptr); static void enable_mii_autopoll(struct mac_regs * regs); static int velocity_mii_read(struct mac_regs *, u8 byIdx, u16 * pdata); static int velocity_mii_write(struct mac_regs *, u8 byMiiAddr, u16 data); -static int velocity_set_wol(struct velocity_info *vptr); -static void velocity_save_context(struct velocity_info *vptr, struct velocity_context *context); -static void velocity_restore_context(struct velocity_info *vptr, struct velocity_context *context); static u32 mii_check_media_mode(struct mac_regs * regs); static u32 check_connection_type(struct mac_regs * regs); -static void velocity_init_cam_filter(struct velocity_info *vptr); static int velocity_set_media_mode(struct velocity_info *vptr, u32 mii_status); #ifdef CONFIG_PM + static int velocity_suspend(struct pci_dev *pdev, u32 state); static int velocity_resume(struct pci_dev *pdev); static int velocity_netdev_event(struct notifier_block *nb, unsigned long notification, void *ptr); static struct notifier_block velocity_inetaddr_notifier = { - notifier_call:velocity_netdev_event, + .notifier_call = velocity_netdev_event, }; -#endif /* CONFIG_PM */ +static spinlock_t velocity_dev_list_lock = SPIN_LOCK_UNLOCKED; +static LIST_HEAD(velocity_dev_list); + +static void velocity_register_notifier(void) +{ + register_inetaddr_notifier(&velocity_inetaddr_notifier); +} + +static void velocity_unregister_notifier(void) +{ + unregister_inetaddr_notifier(&velocity_inetaddr_notifier); +} + +#else /* CONFIG_PM */ + +#define velocity_register_notifier() do {} while (0) +#define velocity_unregister_notifier() do {} while (0) + +#endif /* !CONFIG_PM */ /* * Internal board variants. At the moment we have only one @@ -289,8 +307,9 @@ static struct velocity_info_tbl chip_info_table[] = { */ static struct pci_device_id velocity_id_table[] __devinitdata = { - {0x1106, 0x3119, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (unsigned long) &chip_info_table[0]}, - {0,} + {PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_612X, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, (unsigned long) chip_info_table}, + {0, } }; MODULE_DEVICE_TABLE(pci, velocity_id_table); @@ -326,12 +345,22 @@ static void __devexit velocity_remove1(struct pci_dev *pdev) struct net_device *dev = pci_get_drvdata(pdev); struct velocity_info *vptr = dev->priv; +#ifdef CONFIG_PM + unsigned long flags; + + spin_lock_irqsave(&velocity_dev_list_lock, flags); + if (!list_empty(&velocity_dev_list)) + list_del(&vptr->list); + spin_unlock_irqrestore(&velocity_dev_list_lock, flags); +#endif unregister_netdev(dev); iounmap(vptr->mac_regs); pci_release_regions(pdev); pci_disable_device(pdev); pci_set_drvdata(pdev, NULL); free_netdev(dev); + + velocity_nics--; } /** @@ -435,7 +464,7 @@ static void velocity_init_cam_filter(struct velocity_info *vptr) { struct mac_regs * regs = vptr->mac_regs; - /* T urn on MCFG_PQEN, turn off MCFG_RTGOPT */ + /* Turn on MCFG_PQEN, turn off MCFG_RTGOPT */ WORD_REG_BITS_SET(MCFG_PQEN, MCFG_RTGOPT, ®s->MCFG); WORD_REG_BITS_ON(MCFG_VIDFR, ®s->MCFG); @@ -477,13 +506,13 @@ static void velocity_rx_reset(struct velocity_info *vptr) struct mac_regs * regs = vptr->mac_regs; int i; - vptr->rd_used = vptr->rd_curr = 0; + vptr->rd_dirty = vptr->rd_filled = vptr->rd_curr = 0; /* * Init state, all RD entries belong to the NIC */ for (i = 0; i < vptr->options.numrx; ++i) - vptr->rd_ring[i].rdesc0.owner = cpu_to_le32(OWNED_BY_NIC); + vptr->rd_ring[i].rdesc0.owner = OWNED_BY_NIC; writew(vptr->options.numrx, ®s->RBRDU); writel(vptr->rd_pool_dma, ®s->RDBaseLo); @@ -558,10 +587,15 @@ static void velocity_init_registers(struct velocity_info *vptr, writeb(WOLCFG_SAM | WOLCFG_SAB, ®s->WOLCFGSet); /* - * Bback off algorithm use original IEEE standard + * Back off algorithm use original IEEE standard */ BYTE_REG_BITS_SET(CFGB_OFSET, (CFGB_CRANDOM | CFGB_CAP | CFGB_MBA | CFGB_BAKOPT), ®s->CFGB); + /* + * Init CAM filter + */ + velocity_init_cam_filter(vptr); + /* * Set packet filter: Receive directed and broadcast address */ @@ -586,8 +620,6 @@ static void velocity_init_registers(struct velocity_info *vptr, mac_tx_queue_run(regs, i); } - velocity_init_cam_filter(vptr); - init_flow_control_register(vptr); writel(CR0_STOP, ®s->CR0Clr); @@ -595,7 +627,6 @@ static void velocity_init_registers(struct velocity_info *vptr, mii_status = velocity_get_opt_media_mode(vptr); netif_stop_queue(vptr->dev); - mac_clear_isr(regs); mii_init(vptr, mii_status); @@ -662,7 +693,7 @@ static int __devinit velocity_found1(struct pci_dev *pdev, const struct pci_devi struct mac_regs * regs; int ret = -ENOMEM; - if (velocity_nics++ >= MAX_UNITS) { + if (velocity_nics >= MAX_UNITS) { printk(KERN_NOTICE VELOCITY_NAME ": already found %d NICs.\n", velocity_nics); return -ENODEV; @@ -694,7 +725,6 @@ static int __devinit velocity_found1(struct pci_dev *pdev, const struct pci_devi vptr->dev = dev; - dev->priv = vptr; dev->irq = pdev->irq; ret = pci_enable_device(pdev); @@ -729,7 +759,7 @@ static int __devinit velocity_found1(struct pci_dev *pdev, const struct pci_devi dev->dev_addr[i] = readb(®s->PAR[i]); - velocity_get_options(&vptr->options, velocity_nics - 1, dev->name); + velocity_get_options(&vptr->options, velocity_nics, dev->name); /* * Mask out the options cannot be set to the chip @@ -775,6 +805,16 @@ static int __devinit velocity_found1(struct pci_dev *pdev, const struct pci_devi /* and leave the chip powered down */ pci_set_power_state(pdev, 3); +#ifdef CONFIG_PM + { + unsigned long flags; + + spin_lock_irqsave(&velocity_dev_list_lock, flags); + list_add(&vptr->list, &velocity_dev_list); + spin_unlock_irqrestore(&velocity_dev_list_lock, flags); + } +#endif + velocity_nics++; out: return ret; @@ -827,9 +867,8 @@ static void __devinit velocity_init_info(struct pci_dev *pdev, struct velocity_i vptr->io_size = info->io_size; vptr->num_txq = info->txqueue; vptr->multicast_limit = MCAM_SIZE; - spin_lock_init(&vptr->lock); - spin_lock_init(&vptr->xmit_lock); + INIT_LIST_HEAD(&vptr->list); } /** @@ -966,6 +1005,60 @@ static void velocity_free_rings(struct velocity_info *vptr) pci_free_consistent(vptr->pdev, size, vptr->tx_bufs, vptr->tx_bufs_dma); } +static inline void velocity_give_many_rx_descs(struct velocity_info *vptr) +{ + struct mac_regs *regs = vptr->mac_regs; + int avail, dirty, unusable; + + /* + * RD number must be equal to 4X per hardware spec + * (programming guide rev 1.20, p.13) + */ + if (vptr->rd_filled < 4) + return; + + wmb(); + + unusable = vptr->rd_filled & 0x0003; + dirty = vptr->rd_dirty - unusable; + for (avail = vptr->rd_filled & 0xfffc; avail; avail--) { + dirty = (dirty > 0) ? dirty - 1 : vptr->options.numrx - 1; + vptr->rd_ring[dirty].rdesc0.owner = OWNED_BY_NIC; + } + + writew(vptr->rd_filled & 0xfffc, ®s->RBRDU); + vptr->rd_filled = unusable; +} + +static int velocity_rx_refill(struct velocity_info *vptr) +{ + int dirty = vptr->rd_dirty, done = 0, ret = 0; + + do { + struct rx_desc *rd = vptr->rd_ring + dirty; + + /* Fine for an all zero Rx desc at init time as well */ + if (rd->rdesc0.owner == OWNED_BY_NIC) + break; + + if (!vptr->rd_info[dirty].skb) { + ret = velocity_alloc_rx_buf(vptr, dirty); + if (ret < 0) + break; + } + done++; + dirty = (dirty < vptr->options.numrx - 1) ? dirty + 1 : 0; + } while (dirty != vptr->rd_curr); + + if (done) { + vptr->rd_dirty = dirty; + vptr->rd_filled += done; + velocity_give_many_rx_descs(vptr); + } + + return ret; +} + /** * velocity_init_rd_ring - set up receive ring * @vptr: velocity to configure @@ -976,9 +1069,7 @@ static void velocity_free_rings(struct velocity_info *vptr) static int velocity_init_rd_ring(struct velocity_info *vptr) { - int i, ret = -ENOMEM; - struct rx_desc *rd; - struct velocity_rd_info *rd_info; + int ret = -ENOMEM; unsigned int rsize = sizeof(struct velocity_rd_info) * vptr->options.numrx; @@ -987,28 +1078,20 @@ static int velocity_init_rd_ring(struct velocity_info *vptr) goto out; memset(vptr->rd_info, 0, rsize); - /* Init the RD ring entries */ - for (i = 0; i < vptr->options.numrx; i++) { - rd = &(vptr->rd_ring[i]); - rd_info = &(vptr->rd_info[i]); + vptr->rd_filled = vptr->rd_dirty = vptr->rd_curr = 0; - ret = velocity_alloc_rx_buf(vptr, i); - if (ret < 0) { - VELOCITY_PRT(MSG_LEVEL_ERR, KERN_ERR - "%s: failed to allocate RX buffer.\n", - vptr->dev->name); - velocity_free_rd_ring(vptr); - goto out; - } - rd->rdesc0.owner = OWNED_BY_NIC; + ret = velocity_rx_refill(vptr); + if (ret < 0) { + VELOCITY_PRT(MSG_LEVEL_ERR, KERN_ERR + "%s: failed to allocate RX buffer.\n", vptr->dev->name); + velocity_free_rd_ring(vptr); } - vptr->rd_used = vptr->rd_curr = 0; out: return ret; } /** - * velocity_free_rd_ring - set up receive ring + * velocity_free_rd_ring - free receive ring * @vptr: velocity to clean up * * Free the receive buffers for each ring slot and any @@ -1025,7 +1108,7 @@ static void velocity_free_rd_ring(struct velocity_info *vptr) for (i = 0; i < vptr->options.numrx; i++) { struct velocity_rd_info *rd_info = &(vptr->rd_info[i]); - if (!rd_info->skb_dma) + if (!rd_info->skb) continue; pci_unmap_single(vptr->pdev, rd_info->skb_dma, vptr->rx_buf_sz, PCI_DMA_FROMDEVICE); @@ -1073,8 +1156,10 @@ static int velocity_init_td_ring(struct velocity_info *vptr) for (i = 0; i < vptr->options.numtx; i++, curr += sizeof(struct tx_desc)) { td = &(vptr->td_rings[j][i]); td_info = &(vptr->td_infos[j][i]); - td_info->buf = vptr->tx_bufs + (i + j) * PKT_BUF_SZ; - td_info->buf_dma = vptr->tx_bufs_dma + (i + j) * PKT_BUF_SZ; + td_info->buf = vptr->tx_bufs + + (j * vptr->options.numtx + i) * PKT_BUF_SZ; + td_info->buf_dma = vptr->tx_bufs_dma + + (j * vptr->options.numtx + i) * PKT_BUF_SZ; } vptr->td_tail[j] = vptr->td_curr[j] = vptr->td_used[j] = 0; } @@ -1146,40 +1231,27 @@ static void velocity_free_td_ring(struct velocity_info *vptr) static int velocity_rx_srv(struct velocity_info *vptr, int status) { - struct rx_desc *rd; struct net_device_stats *stats = &vptr->stats; - struct mac_regs * regs = vptr->mac_regs; int rd_curr = vptr->rd_curr; int works = 0; - while (1) { - - rd = &(vptr->rd_ring[rd_curr]); + do { + struct rx_desc *rd = vptr->rd_ring + rd_curr; - if ((vptr->rd_info[rd_curr]).skb == NULL) { - if (velocity_alloc_rx_buf(vptr, rd_curr) < 0) - break; - } - - if (works++ > 15) + if (!vptr->rd_info[rd_curr].skb) break; if (rd->rdesc0.owner == OWNED_BY_NIC) break; + rmb(); + /* * Don't drop CE or RL error frame although RXOK is off - * FIXME: need to handle copybreak */ if ((rd->rdesc0.RSR & RSR_RXOK) || (!(rd->rdesc0.RSR & RSR_RXOK) && (rd->rdesc0.RSR & (RSR_CE | RSR_RL)))) { - if (velocity_receive_frame(vptr, rd_curr) == 0) { - if (velocity_alloc_rx_buf(vptr, rd_curr) < 0) { - VELOCITY_PRT(MSG_LEVEL_ERR, KERN_ERR "%s: can not allocate rx buf\n", vptr->dev->name); - break; - } - } else { + if (velocity_receive_frame(vptr, rd_curr) < 0) stats->rx_dropped++; - } } else { if (rd->rdesc0.RSR & RSR_CRC) stats->rx_crc_errors++; @@ -1191,26 +1263,20 @@ static int velocity_rx_srv(struct velocity_info *vptr, int status) rd->inten = 1; - if (++vptr->rd_used >= 4) { - int i, rd_prev = rd_curr; - for (i = 0; i < 4; i++) { - if (--rd_prev < 0) - rd_prev = vptr->options.numrx - 1; - - rd = &(vptr->rd_ring[rd_prev]); - rd->rdesc0.owner = OWNED_BY_NIC; - } - writew(4, &(regs->RBRDU)); - vptr->rd_used -= 4; - } - vptr->dev->last_rx = jiffies; rd_curr++; if (rd_curr >= vptr->options.numrx) rd_curr = 0; - } + } while (++works <= 15); + vptr->rd_curr = rd_curr; + + if (works > 0 && velocity_rx_refill(vptr) < 0) { + VELOCITY_PRT(MSG_LEVEL_ERR, KERN_ERR + "%s: rx buf allocation failure\n", vptr->dev->name); + } + VAR_USED(stats); return works; } @@ -1241,6 +1307,65 @@ static inline void velocity_rx_csum(struct rx_desc *rd, struct sk_buff *skb) } } +/** + * velocity_rx_copy - in place Rx copy for small packets + * @rx_skb: network layer packet buffer candidate + * @pkt_size: received data size + * @rd: receive packet descriptor + * @dev: network device + * + * Replace the current skb that is scheduled for Rx processing by a + * shorter, immediatly allocated skb, if the received packet is small + * enough. This function returns a negative value if the received + * packet is too big or if memory is exhausted. + */ +static inline int velocity_rx_copy(struct sk_buff **rx_skb, int pkt_size, + struct velocity_info *vptr) +{ + int ret = -1; + + if (pkt_size < rx_copybreak) { + struct sk_buff *new_skb; + + new_skb = dev_alloc_skb(pkt_size + 2); + if (new_skb) { + new_skb->dev = vptr->dev; + new_skb->ip_summed = rx_skb[0]->ip_summed; + + if (vptr->flags & VELOCITY_FLAGS_IP_ALIGN) + skb_reserve(new_skb, 2); + + memcpy(new_skb->data, rx_skb[0]->tail, pkt_size); + *rx_skb = new_skb; + ret = 0; + } + + } + return ret; +} + +/** + * velocity_iph_realign - IP header alignment + * @vptr: velocity we are handling + * @skb: network layer packet buffer + * @pkt_size: received data size + * + * Align IP header on a 2 bytes boundary. This behavior can be + * configured by the user. + */ +static inline void velocity_iph_realign(struct velocity_info *vptr, + struct sk_buff *skb, int pkt_size) +{ + /* FIXME - memmove ? */ + if (vptr->flags & VELOCITY_FLAGS_IP_ALIGN) { + int i; + + for (i = pkt_size; i >= 0; i--) + *(skb->data + i + 2) = *(skb->data + i); + skb_reserve(skb, 2); + } +} + /** * velocity_receive_frame - received packet processor * @vptr: velocity we are handling @@ -1252,9 +1377,11 @@ static inline void velocity_rx_csum(struct rx_desc *rd, struct sk_buff *skb) static int velocity_receive_frame(struct velocity_info *vptr, int idx) { + void (*pci_action)(struct pci_dev *, dma_addr_t, size_t, int); struct net_device_stats *stats = &vptr->stats; struct velocity_rd_info *rd_info = &(vptr->rd_info[idx]); struct rx_desc *rd = &(vptr->rd_ring[idx]); + int pkt_len = rd->rdesc0.len; struct sk_buff *skb; if (rd->rdesc0.RSR & (RSR_STP | RSR_EDP)) { @@ -1269,22 +1396,8 @@ static int velocity_receive_frame(struct velocity_info *vptr, int idx) skb = rd_info->skb; skb->dev = vptr->dev; - pci_unmap_single(vptr->pdev, rd_info->skb_dma, vptr->rx_buf_sz, - PCI_DMA_FROMDEVICE); - rd_info->skb_dma = (dma_addr_t) NULL; - rd_info->skb = NULL; - - /* FIXME - memmove ? */ - if (vptr->flags & VELOCITY_FLAGS_IP_ALIGN) { - int i; - for (i = rd->rdesc0.len + 4; i >= 0; i--) - *(skb->data + i + 2) = *(skb->data + i); - skb->data += 2; - skb->tail += 2; - } - - skb_put(skb, (rd->rdesc0.len - 4)); - skb->protocol = eth_type_trans(skb, skb->dev); + pci_dma_sync_single_for_cpu(vptr->pdev, rd_info->skb_dma, + vptr->rx_buf_sz, PCI_DMA_FROMDEVICE); /* * Drop frame not meeting IEEE 802.3 @@ -1297,13 +1410,23 @@ static int velocity_receive_frame(struct velocity_info *vptr, int idx) } } + pci_action = pci_dma_sync_single_for_device; + velocity_rx_csum(rd, skb); - - /* - * FIXME: need rx_copybreak handling - */ - stats->rx_bytes += skb->len; + if (velocity_rx_copy(&skb, pkt_len, vptr) < 0) { + velocity_iph_realign(vptr, skb, pkt_len); + pci_action = pci_unmap_single; + rd_info->skb = NULL; + } + + pci_action(vptr->pdev, rd_info->skb_dma, vptr->rx_buf_sz, + PCI_DMA_FROMDEVICE); + + skb_put(skb, pkt_len - 4); + skb->protocol = eth_type_trans(skb, skb->dev); + + stats->rx_bytes += pkt_len; netif_rx(skb); return 0; @@ -1962,32 +2085,6 @@ static int velocity_intr(int irq, void *dev_instance, struct pt_regs *regs) } -/** - * ether_crc - ethernet CRC function - * - * Compute an ethernet CRC hash of the data block provided. This - * is not performance optimised but is not needed in performance - * critical code paths. - * - * FIXME: could we use shared code here ? - */ - -static inline u32 ether_crc(int length, unsigned char *data) -{ - static unsigned const ethernet_polynomial = 0x04c11db7U; - - int crc = -1; - - while (--length >= 0) { - unsigned char current_octet = *data++; - int bit; - for (bit = 0; bit < 8; bit++, current_octet >>= 1) { - crc = (crc << 1) ^ ((crc < 0) ^ (current_octet & 1) ? ethernet_polynomial : 0); - } - } - return crc; -} - /** * velocity_set_multi - filter list change callback * @dev: network device @@ -2123,13 +2220,13 @@ static int velocity_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) */ static struct pci_driver velocity_driver = { - name:VELOCITY_NAME, - id_table:velocity_id_table, - probe:velocity_found1, - remove:velocity_remove1, + .name = VELOCITY_NAME, + .id_table = velocity_id_table, + .probe = velocity_found1, + .remove = __devexit_p(velocity_remove1), #ifdef CONFIG_PM - suspend:velocity_suspend, - resume:velocity_resume, + .suspend = velocity_suspend, + .resume = velocity_resume, #endif }; @@ -2145,11 +2242,11 @@ static struct pci_driver velocity_driver = { static int __init velocity_init_module(void) { int ret; - ret = pci_module_init(&velocity_driver); -#ifdef CONFIG_PM - register_inetaddr_notifier(&velocity_inetaddr_notifier); -#endif + velocity_register_notifier(); + ret = pci_module_init(&velocity_driver); + if (ret < 0) + velocity_unregister_notifier(); return ret; } @@ -2164,9 +2261,7 @@ static int __init velocity_init_module(void) static void __exit velocity_cleanup_module(void) { -#ifdef CONFIG_PM - unregister_inetaddr_notifier(&velocity_inetaddr_notifier); -#endif + velocity_unregister_notifier(); pci_unregister_driver(&velocity_driver); } @@ -2992,172 +3087,6 @@ static void velocity_restore_context(struct velocity_info *vptr, struct velocity } -static int velocity_suspend(struct pci_dev *pdev, u32 state) -{ - struct velocity_info *vptr = pci_get_drvdata(pdev); - unsigned long flags; - - if(!netif_running(vptr->dev)) - return 0; - - netif_device_detach(vptr->dev); - - spin_lock_irqsave(&vptr->lock, flags); - pci_save_state(pdev, vptr->pci_state); -#ifdef ETHTOOL_GWOL - if (vptr->flags & VELOCITY_FLAGS_WOL_ENABLED) { - velocity_get_ip(vptr); - velocity_save_context(vptr, &vptr->context); - velocity_shutdown(vptr); - velocity_set_wol(vptr); - pci_enable_wake(pdev, 3, 1); - pci_set_power_state(pdev, 3); - } else { - velocity_save_context(vptr, &vptr->context); - velocity_shutdown(vptr); - pci_disable_device(pdev); - pci_set_power_state(pdev, state); - } -#else - pci_set_power_state(pdev, state); -#endif - spin_unlock_irqrestore(&vptr->lock, flags); - return 0; -} - -static int velocity_resume(struct pci_dev *pdev) -{ - struct velocity_info *vptr = pci_get_drvdata(pdev); - unsigned long flags; - int i; - - if(!netif_running(vptr->dev)) - return 0; - - pci_set_power_state(pdev, 0); - pci_enable_wake(pdev, 0, 0); - pci_restore_state(pdev, vptr->pci_state); - - mac_wol_reset(vptr->mac_regs); - - spin_lock_irqsave(&vptr->lock, flags); - velocity_restore_context(vptr, &vptr->context); - velocity_init_registers(vptr, VELOCITY_INIT_WOL); - mac_disable_int(vptr->mac_regs); - - velocity_tx_srv(vptr, 0); - - for (i = 0; i < vptr->num_txq; i++) { - if (vptr->td_used[i]) { - mac_tx_queue_wake(vptr->mac_regs, i); - } - } - - mac_enable_int(vptr->mac_regs); - spin_unlock_irqrestore(&vptr->lock, flags); - netif_device_attach(vptr->dev); - - return 0; -} - -static int velocity_netdev_event(struct notifier_block *nb, unsigned long notification, void *ptr) -{ - struct in_ifaddr *ifa = (struct in_ifaddr *) ptr; - struct net_device *dev; - struct velocity_info *vptr; - - if (ifa) { - dev = ifa->ifa_dev->dev; - vptr = dev->priv; - velocity_get_ip(vptr); - } - return NOTIFY_DONE; -} -#endif - -/* - * Purpose: Functions to set WOL. - */ - -const static unsigned short crc16_tab[256] = { - 0x0000, 0x1189, 0x2312, 0x329b, 0x4624, 0x57ad, 0x6536, 0x74bf, - 0x8c48, 0x9dc1, 0xaf5a, 0xbed3, 0xca6c, 0xdbe5, 0xe97e, 0xf8f7, - 0x1081, 0x0108, 0x3393, 0x221a, 0x56a5, 0x472c, 0x75b7, 0x643e, - 0x9cc9, 0x8d40, 0xbfdb, 0xae52, 0xdaed, 0xcb64, 0xf9ff, 0xe876, - 0x2102, 0x308b, 0x0210, 0x1399, 0x6726, 0x76af, 0x4434, 0x55bd, - 0xad4a, 0xbcc3, 0x8e58, 0x9fd1, 0xeb6e, 0xfae7, 0xc87c, 0xd9f5, - 0x3183, 0x200a, 0x1291, 0x0318, 0x77a7, 0x662e, 0x54b5, 0x453c, - 0xbdcb, 0xac42, 0x9ed9, 0x8f50, 0xfbef, 0xea66, 0xd8fd, 0xc974, - 0x4204, 0x538d, 0x6116, 0x709f, 0x0420, 0x15a9, 0x2732, 0x36bb, - 0xce4c, 0xdfc5, 0xed5e, 0xfcd7, 0x8868, 0x99e1, 0xab7a, 0xbaf3, - 0x5285, 0x430c, 0x7197, 0x601e, 0x14a1, 0x0528, 0x37b3, 0x263a, - 0xdecd, 0xcf44, 0xfddf, 0xec56, 0x98e9, 0x8960, 0xbbfb, 0xaa72, - 0x6306, 0x728f, 0x4014, 0x519d, 0x2522, 0x34ab, 0x0630, 0x17b9, - 0xef4e, 0xfec7, 0xcc5c, 0xddd5, 0xa96a, 0xb8e3, 0x8a78, 0x9bf1, - 0x7387, 0x620e, 0x5095, 0x411c, 0x35a3, 0x242a, 0x16b1, 0x0738, - 0xffcf, 0xee46, 0xdcdd, 0xcd54, 0xb9eb, 0xa862, 0x9af9, 0x8b70, - 0x8408, 0x9581, 0xa71a, 0xb693, 0xc22c, 0xd3a5, 0xe13e, 0xf0b7, - 0x0840, 0x19c9, 0x2b52, 0x3adb, 0x4e64, 0x5fed, 0x6d76, 0x7cff, - 0x9489, 0x8500, 0xb79b, 0xa612, 0xd2ad, 0xc324, 0xf1bf, 0xe036, - 0x18c1, 0x0948, 0x3bd3, 0x2a5a, 0x5ee5, 0x4f6c, 0x7df7, 0x6c7e, - 0xa50a, 0xb483, 0x8618, 0x9791, 0xe32e, 0xf2a7, 0xc03c, 0xd1b5, - 0x2942, 0x38cb, 0x0a50, 0x1bd9, 0x6f66, 0x7eef, 0x4c74, 0x5dfd, - 0xb58b, 0xa402, 0x9699, 0x8710, 0xf3af, 0xe226, 0xd0bd, 0xc134, - 0x39c3, 0x284a, 0x1ad1, 0x0b58, 0x7fe7, 0x6e6e, 0x5cf5, 0x4d7c, - 0xc60c, 0xd785, 0xe51e, 0xf497, 0x8028, 0x91a1, 0xa33a, 0xb2b3, - 0x4a44, 0x5bcd, 0x6956, 0x78df, 0x0c60, 0x1de9, 0x2f72, 0x3efb, - 0xd68d, 0xc704, 0xf59f, 0xe416, 0x90a9, 0x8120, 0xb3bb, 0xa232, - 0x5ac5, 0x4b4c, 0x79d7, 0x685e, 0x1ce1, 0x0d68, 0x3ff3, 0x2e7a, - 0xe70e, 0xf687, 0xc41c, 0xd595, 0xa12a, 0xb0a3, 0x8238, 0x93b1, - 0x6b46, 0x7acf, 0x4854, 0x59dd, 0x2d62, 0x3ceb, 0x0e70, 0x1ff9, - 0xf78f, 0xe606, 0xd49d, 0xc514, 0xb1ab, 0xa022, 0x92b9, 0x8330, - 0x7bc7, 0x6a4e, 0x58d5, 0x495c, 0x3de3, 0x2c6a, 0x1ef1, 0x0f78 -}; - - -static u32 mask_pattern[2][4] = { - {0x00203000, 0x000003C0, 0x00000000, 0x0000000}, /* ARP */ - {0xfffff000, 0xffffffff, 0xffffffff, 0x000ffff} /* Magic Packet */ -}; - -/** - * ether_crc16 - compute ethernet CRC - * @len: buffer length - * @cp: buffer - * @crc16: initial CRC - * - * Compute a CRC value for a block of data. - * FIXME: can we use generic functions ? - */ - -static u16 ether_crc16(int len, u8 * cp, u16 crc16) -{ - while (len--) - crc16 = (crc16 >> 8) ^ crc16_tab[(crc16 ^ *cp++) & 0xff]; - return (crc16); -} - -/** - * bit_reverse - 16bit reverse - * @data: 16bit data t reverse - * - * Reverse the order of a 16bit value and return the reversed bits - */ - -static u16 bit_reverse(u16 data) -{ - u32 new = 0x00000000; - int ii; - - - for (ii = 0; ii < 16; ii++) { - new |= ((u32) (data & 1) << (31 - ii)); - data >>= 1; - } - - return (u16) (new >> 16); -} - /** * wol_calc_crc - WOL CRC * @pattern: data pattern @@ -3166,7 +3095,7 @@ static u16 bit_reverse(u16 data) * Compute the wake on lan crc hashes for the packet header * we are interested in. */ - + u16 wol_calc_crc(int size, u8 * pattern, u8 *mask_pattern) { u16 crc = 0xFFFF; @@ -3186,12 +3115,12 @@ u16 wol_calc_crc(int size, u8 * pattern, u8 *mask_pattern) continue; } mask >>= 1; - crc = ether_crc16(1, &(pattern[i * 8 + j]), crc); + crc = crc_ccitt(crc, &(pattern[i * 8 + j]), 1); } } /* Finally, invert the result once to get the correct data */ crc = ~crc; - return bit_reverse(crc); + return bitreverse(crc) >> 16; } /** @@ -3203,13 +3132,18 @@ u16 wol_calc_crc(int size, u8 * pattern, u8 *mask_pattern) * * FIXME: check static buffer is safe here */ - + static int velocity_set_wol(struct velocity_info *vptr) { struct mac_regs * regs = vptr->mac_regs; static u8 buf[256]; int i; + static u32 mask_pattern[2][4] = { + {0x00203000, 0x000003C0, 0x00000000, 0x0000000}, /* ARP */ + {0xfffff000, 0xffffffff, 0xffffffff, 0x000ffff} /* Magic Packet */ + }; + writew(0xFFFF, ®s->WOLCRClr); writeb(WOLCFG_SAB | WOLCFG_SAM, ®s->WOLCFGSet); writew(WOLCR_MAGIC_EN, ®s->WOLCRSet); @@ -3236,7 +3170,8 @@ static int velocity_set_wol(struct velocity_info *vptr) memcpy(arp->ar_tip, vptr->ip_addr, 4); - crc = wol_calc_crc((sizeof(struct arp_packet) + 7) / 8, buf, (u8 *) & mask_pattern[0][0]); + crc = wol_calc_crc((sizeof(struct arp_packet) + 7) / 8, buf, + (u8 *) & mask_pattern[0][0]); writew(crc, ®s->PatternCRC[0]); writew(WOLCR_ARP_EN, ®s->WOLCRSet); @@ -3275,3 +3210,92 @@ static int velocity_set_wol(struct velocity_info *vptr) return 0; } +static int velocity_suspend(struct pci_dev *pdev, u32 state) +{ + struct velocity_info *vptr = pci_get_drvdata(pdev); + unsigned long flags; + + if(!netif_running(vptr->dev)) + return 0; + + netif_device_detach(vptr->dev); + + spin_lock_irqsave(&vptr->lock, flags); + pci_save_state(pdev, vptr->pci_state); +#ifdef ETHTOOL_GWOL + if (vptr->flags & VELOCITY_FLAGS_WOL_ENABLED) { + velocity_get_ip(vptr); + velocity_save_context(vptr, &vptr->context); + velocity_shutdown(vptr); + velocity_set_wol(vptr); + pci_enable_wake(pdev, 3, 1); + pci_set_power_state(pdev, 3); + } else { + velocity_save_context(vptr, &vptr->context); + velocity_shutdown(vptr); + pci_disable_device(pdev); + pci_set_power_state(pdev, state); + } +#else + pci_set_power_state(pdev, state); +#endif + spin_unlock_irqrestore(&vptr->lock, flags); + return 0; +} + +static int velocity_resume(struct pci_dev *pdev) +{ + struct velocity_info *vptr = pci_get_drvdata(pdev); + unsigned long flags; + int i; + + if(!netif_running(vptr->dev)) + return 0; + + pci_set_power_state(pdev, 0); + pci_enable_wake(pdev, 0, 0); + pci_restore_state(pdev, vptr->pci_state); + + mac_wol_reset(vptr->mac_regs); + + spin_lock_irqsave(&vptr->lock, flags); + velocity_restore_context(vptr, &vptr->context); + velocity_init_registers(vptr, VELOCITY_INIT_WOL); + mac_disable_int(vptr->mac_regs); + + velocity_tx_srv(vptr, 0); + + for (i = 0; i < vptr->num_txq; i++) { + if (vptr->td_used[i]) { + mac_tx_queue_wake(vptr->mac_regs, i); + } + } + + mac_enable_int(vptr->mac_regs); + spin_unlock_irqrestore(&vptr->lock, flags); + netif_device_attach(vptr->dev); + + return 0; +} + +static int velocity_netdev_event(struct notifier_block *nb, unsigned long notification, void *ptr) +{ + struct in_ifaddr *ifa = (struct in_ifaddr *) ptr; + + if (ifa) { + struct net_device *dev = ifa->ifa_dev->dev; + struct velocity_info *vptr; + unsigned long flags; + + spin_lock_irqsave(&velocity_dev_list_lock, flags); + list_for_each_entry(vptr, &velocity_dev_list, list) { + if (vptr->dev == dev) { + velocity_get_ip(vptr); + break; + } + } + spin_unlock_irqrestore(&velocity_dev_list_lock, flags); + } + return NOTIFY_DONE; +} +#endif diff --git a/drivers/net/via-velocity.h b/drivers/net/via-velocity.h index 2175b8696..9a187de48 100644 --- a/drivers/net/via-velocity.h +++ b/drivers/net/via-velocity.h @@ -37,7 +37,6 @@ #define OPTION_DEFAULT { [0 ... MAX_UNITS-1] = -1} #define REV_ID_VT6110 (0) -#define DEVICE_ID (0x3119) #define BYTE_REG_BITS_ON(x,p) do { writeb(readb((p))|(x),(p));} while (0) #define WORD_REG_BITS_ON(x,p) do { writew(readw((p))|(x),(p));} while (0) @@ -1320,7 +1319,7 @@ static inline void mac_set_cam_mask(struct mac_regs * regs, u8 * mask, enum velo /* disable CAMEN */ writeb(0, ®s->CAMADDR); - /* Select CAM mask */ + /* Select mar */ BYTE_REG_BITS_SET(CAMCR_PS_MAR, CAMCR_PS1 | CAMCR_PS0, ®s->CAMCR); } @@ -1361,7 +1360,7 @@ static inline void mac_set_cam(struct mac_regs * regs, int idx, u8 *addr, enum v writeb(0, ®s->CAMADDR); - /* Select CAM mask */ + /* Select mar */ BYTE_REG_BITS_SET(CAMCR_PS_MAR, CAMCR_PS1 | CAMCR_PS0, ®s->CAMCR); } @@ -1402,7 +1401,7 @@ static inline void mac_get_cam(struct mac_regs * regs, int idx, u8 *addr, enum v writeb(0, ®s->CAMADDR); - /* Select CAM mask */ + /* Select mar */ BYTE_REG_BITS_SET(CAMCR_PS_MAR, CAMCR_PS1 | CAMCR_PS0, ®s->CAMCR); } @@ -1734,14 +1733,13 @@ struct velocity_opt { }; struct velocity_info { - struct velocity_info *next; - struct velocity_info *prev; + struct list_head list; struct pci_dev *pdev; struct net_device *dev; struct net_device_stats stats; -#if CONFIG_PM +#ifdef CONFIG_PM u32 pci_state[16]; #endif @@ -1772,7 +1770,8 @@ struct velocity_info { struct velocity_td_info *td_infos[TX_QUEUE_NO]; int rd_curr; - int rd_used; + int rd_dirty; + u32 rd_filled; struct rx_desc *rd_ring; struct velocity_rd_info *rd_info; /* It's an array */ @@ -1793,7 +1792,6 @@ struct velocity_info { u8 mCAMmask[(MCAM_SIZE / 8)]; spinlock_t lock; - spinlock_t xmit_lock; int wol_opts; u8 wol_passwd[6]; diff --git a/drivers/net/wan/cycx_x25.c b/drivers/net/wan/cycx_x25.c index cd1cd8569..07bfe107a 100644 --- a/drivers/net/wan/cycx_x25.c +++ b/drivers/net/wan/cycx_x25.c @@ -186,7 +186,7 @@ static void nibble_to_byte(u8 *s, u8 *d, u8 len, u8 nibble), reset_timer(struct net_device *dev); static u8 bps_to_speed_code(u32 bps); -static u8 log2(u32 n); +static u8 cycx_log2(u32 n); static unsigned dec_to_uint(u8 *str, int len); @@ -263,7 +263,7 @@ int cycx_x25_wan_init(struct cycx_device *card, wandev_conf_t *conf) else card->wandev.mtu = 64; - cfg.pktlen = log2(card->wandev.mtu); + cfg.pktlen = cycx_log2(card->wandev.mtu); if (conf->station == WANOPT_DTE) { cfg.locaddr = 3; /* DTE */ @@ -1513,7 +1513,7 @@ static u8 bps_to_speed_code(u32 bps) } /* log base 2 */ -static u8 log2(u32 n) +static u8 cycx_log2(u32 n) { u8 log = 0; diff --git a/drivers/net/wan/lmc/lmc_debug.c b/drivers/net/wan/lmc/lmc_debug.c index 058ff8048..9dccd9546 100644 --- a/drivers/net/wan/lmc/lmc_debug.c +++ b/drivers/net/wan/lmc/lmc_debug.c @@ -64,7 +64,7 @@ void lmcEventLog (u_int32_t EventNum, u_int32_t arg2, u_int32_t arg3) #endif } -inline void lmc_trace(struct net_device *dev, char *msg){ +void lmc_trace(struct net_device *dev, char *msg){ #ifdef LMC_TRACE unsigned long j = jiffies + 3; /* Wait for 50 ms */ diff --git a/drivers/net/wan/pc300.h b/drivers/net/wan/pc300.h index e6af4921e..a4bfff8d1 100644 --- a/drivers/net/wan/pc300.h +++ b/drivers/net/wan/pc300.h @@ -164,9 +164,9 @@ typedef unsigned char ucchar; /* 8 bits, unsigned */ * (required to support Alpha systems) * ***************************************/ #ifdef __KERNEL__ -#define cpc_writeb(port,val) {writeb((ucchar)(val),(ulong)(port)); mb();} -#define cpc_writew(port,val) {writew((ushort)(val),(ulong)(port)); mb();} -#define cpc_writel(port,val) {writel((uclong)(val),(ulong)(port)); mb();} +#define cpc_writeb(port,val) {writeb((ucchar)(val),(port)); mb();} +#define cpc_writew(port,val) {writew((ushort)(val),(port)); mb();} +#define cpc_writel(port,val) {writel((uclong)(val),(port)); mb();} #define cpc_readb(port) readb(port) #define cpc_readw(port) readw(port) @@ -358,17 +358,17 @@ typedef struct pc300hw { uclong iophys; /* PLX registers I/O base */ uclong iosize; /* PLX registers I/O size */ uclong plxphys; /* PLX registers MMIO base (physical) */ - uclong plxbase; /* PLX registers MMIO base (virtual) */ + void __iomem * plxbase; /* PLX registers MMIO base (virtual) */ uclong plxsize; /* PLX registers MMIO size */ uclong scaphys; /* SCA registers MMIO base (physical) */ - uclong scabase; /* SCA registers MMIO base (virtual) */ + void __iomem * scabase; /* SCA registers MMIO base (virtual) */ uclong scasize; /* SCA registers MMIO size */ uclong ramphys; /* On-board RAM MMIO base (physical) */ - uclong rambase; /* On-board RAM MMIO base (virtual) */ + void __iomem * rambase; /* On-board RAM MMIO base (virtual) */ uclong alloc_ramsize; /* RAM MMIO size allocated by the PCI bridge */ uclong ramsize; /* On-board RAM MMIO size */ uclong falcphys; /* FALC registers MMIO base (physical) */ - uclong falcbase; /* FALC registers MMIO base (virtual) */ + void __iomem * falcbase;/* FALC registers MMIO base (virtual) */ uclong falcsize; /* FALC registers MMIO size */ } pc300hw_t; diff --git a/drivers/net/wan/pc300_drv.c b/drivers/net/wan/pc300_drv.c index 29b82687b..7a972ca25 100644 --- a/drivers/net/wan/pc300_drv.c +++ b/drivers/net/wan/pc300_drv.c @@ -307,7 +307,7 @@ static void tx_dma_buf_pt_init(pc300_t * card, int ch) { int i; int ch_factor = ch * N_DMA_TX_BUF; - volatile pcsca_bd_t *ptdescr = (pcsca_bd_t *) (card->hw.rambase + volatile pcsca_bd_t __iomem *ptdescr = (card->hw.rambase + DMA_TX_BD_BASE + ch_factor * sizeof(pcsca_bd_t)); for (i = 0; i < N_DMA_TX_BUF; i++, ptdescr++) { @@ -322,7 +322,7 @@ static void tx_dma_buf_init(pc300_t * card, int ch) { int i; int ch_factor = ch * N_DMA_TX_BUF; - volatile pcsca_bd_t *ptdescr = (pcsca_bd_t *) (card->hw.rambase + volatile pcsca_bd_t __iomem *ptdescr = (card->hw.rambase + DMA_TX_BD_BASE + ch_factor * sizeof(pcsca_bd_t)); for (i = 0; i < N_DMA_TX_BUF; i++, ptdescr++) { @@ -337,7 +337,7 @@ static void rx_dma_buf_pt_init(pc300_t * card, int ch) { int i; int ch_factor = ch * N_DMA_RX_BUF; - volatile pcsca_bd_t *ptdescr = (pcsca_bd_t *) (card->hw.rambase + volatile pcsca_bd_t __iomem *ptdescr = (card->hw.rambase + DMA_RX_BD_BASE + ch_factor * sizeof(pcsca_bd_t)); for (i = 0; i < N_DMA_RX_BUF; i++, ptdescr++) { @@ -352,7 +352,7 @@ static void rx_dma_buf_init(pc300_t * card, int ch) { int i; int ch_factor = ch * N_DMA_RX_BUF; - volatile pcsca_bd_t *ptdescr = (pcsca_bd_t *) (card->hw.rambase + volatile pcsca_bd_t __iomem *ptdescr = (card->hw.rambase + DMA_RX_BD_BASE + ch_factor * sizeof(pcsca_bd_t)); for (i = 0; i < N_DMA_RX_BUF; i++, ptdescr++) { @@ -365,7 +365,7 @@ static void rx_dma_buf_init(pc300_t * card, int ch) static void tx_dma_buf_check(pc300_t * card, int ch) { - volatile pcsca_bd_t *ptdescr; + volatile pcsca_bd_t __iomem *ptdescr; int i; ucshort first_bd = card->chan[ch].tx_first_bd; ucshort next_bd = card->chan[ch].tx_next_bd; @@ -374,10 +374,10 @@ static void tx_dma_buf_check(pc300_t * card, int ch) first_bd, TX_BD_ADDR(ch, first_bd), next_bd, TX_BD_ADDR(ch, next_bd)); for (i = first_bd, - ptdescr = (pcsca_bd_t *) (card->hw.rambase + TX_BD_ADDR(ch, first_bd)); + ptdescr = (card->hw.rambase + TX_BD_ADDR(ch, first_bd)); i != ((next_bd + 1) & (N_DMA_TX_BUF - 1)); i = (i + 1) & (N_DMA_TX_BUF - 1), - ptdescr = (pcsca_bd_t *) (card->hw.rambase + TX_BD_ADDR(ch, i))) { + ptdescr = (card->hw.rambase + TX_BD_ADDR(ch, i))) { printk("\n CH%d TX%d: next=0x%lx, ptbuf=0x%lx, ST=0x%x, len=%d", ch, i, (uclong) cpc_readl(&ptdescr->next), (uclong) cpc_readl(&ptdescr->ptbuf), @@ -390,7 +390,7 @@ static void tx_dma_buf_check(pc300_t * card, int ch) /* Show all TX buffer descriptors */ static void tx1_dma_buf_check(pc300_t * card, int ch) { - volatile pcsca_bd_t *ptdescr; + volatile pcsca_bd_t __iomem *ptdescr; int i; ucshort first_bd = card->chan[ch].tx_first_bd; ucshort next_bd = card->chan[ch].tx_next_bd; @@ -404,7 +404,7 @@ static void tx1_dma_buf_check(pc300_t * card, int ch) (uclong) cpc_readl(scabase + DTX_REG(CDAL, ch)), (uclong) cpc_readl(scabase + DTX_REG(EDAL, ch))); for (i = 0; i < N_DMA_TX_BUF; i++) { - ptdescr = (pcsca_bd_t *) (card->hw.rambase + TX_BD_ADDR(ch, i)); + ptdescr = (card->hw.rambase + TX_BD_ADDR(ch, i)); printk("\n CH%d TX%d: next=0x%lx, ptbuf=0x%lx, ST=0x%x, len=%d", ch, i, (uclong) cpc_readl(&ptdescr->next), (uclong) cpc_readl(&ptdescr->ptbuf), @@ -416,7 +416,7 @@ static void tx1_dma_buf_check(pc300_t * card, int ch) static void rx_dma_buf_check(pc300_t * card, int ch) { - volatile pcsca_bd_t *ptdescr; + volatile pcsca_bd_t __iomem *ptdescr; int i; ucshort first_bd = card->chan[ch].rx_first_bd; ucshort last_bd = card->chan[ch].rx_last_bd; @@ -424,7 +424,7 @@ static void rx_dma_buf_check(pc300_t * card, int ch) ch_factor = ch * N_DMA_RX_BUF; printk("#CH%d: f_bd = %d, l_bd = %d\n", ch, first_bd, last_bd); - for (i = 0, ptdescr = (pcsca_bd_t *) (card->hw.rambase + + for (i = 0, ptdescr = (card->hw.rambase + DMA_RX_BD_BASE + ch_factor * sizeof(pcsca_bd_t)); i < N_DMA_RX_BUF; i++, ptdescr++) { if (cpc_readb(&ptdescr->status) & DST_OSB) @@ -439,12 +439,12 @@ static void rx_dma_buf_check(pc300_t * card, int ch) int dma_get_rx_frame_size(pc300_t * card, int ch) { - volatile pcsca_bd_t *ptdescr; + volatile pcsca_bd_t __iomem *ptdescr; ucshort first_bd = card->chan[ch].rx_first_bd; int rcvd = 0; volatile ucchar status; - ptdescr = (pcsca_bd_t *)(card->hw.rambase + RX_BD_ADDR(ch, first_bd)); + ptdescr = (card->hw.rambase + RX_BD_ADDR(ch, first_bd)); while ((status = cpc_readb(&ptdescr->status)) & DST_OSB) { rcvd += cpc_readw(&ptdescr->len); first_bd = (first_bd + 1) & (N_DMA_RX_BUF - 1); @@ -453,7 +453,7 @@ int dma_get_rx_frame_size(pc300_t * card, int ch) * (dma_buf_read will clean the buffer descriptors in this case). */ return (rcvd); } - ptdescr = (pcsca_bd_t *)(card->hw.rambase + cpc_readl(&ptdescr->next)); + ptdescr = (card->hw.rambase + cpc_readl(&ptdescr->next)); } return (-1); } @@ -465,7 +465,7 @@ int dma_get_rx_frame_size(pc300_t * card, int ch) int dma_buf_write(pc300_t * card, int ch, ucchar * ptdata, int len) { int i, nchar; - volatile pcsca_bd_t *ptdescr; + volatile pcsca_bd_t __iomem *ptdescr; int tosend = len; ucchar nbuf = ((len - 1) / BD_DEF_LEN) + 1; @@ -474,11 +474,11 @@ int dma_buf_write(pc300_t * card, int ch, ucchar * ptdata, int len) } for (i = 0; i < nbuf; i++) { - ptdescr = (pcsca_bd_t *) (card->hw.rambase + + ptdescr = (card->hw.rambase + TX_BD_ADDR(ch, card->chan[ch].tx_next_bd)); nchar = cpc_min(BD_DEF_LEN, tosend); if (cpc_readb(&ptdescr->status) & DST_OSB) { - memcpy_toio((void *)(card->hw.rambase + cpc_readl(&ptdescr->ptbuf)), + memcpy_toio((card->hw.rambase + cpc_readl(&ptdescr->ptbuf)), &ptdata[len - tosend], nchar); cpc_writew(&ptdescr->len, nchar); card->chan[ch].nfree_tx_bd--; @@ -507,11 +507,11 @@ int dma_buf_read(pc300_t * card, int ch, struct sk_buff *skb) { int nchar; pc300ch_t *chan = (pc300ch_t *) & card->chan[ch]; - volatile pcsca_bd_t *ptdescr; + volatile pcsca_bd_t __iomem *ptdescr; int rcvd = 0; volatile ucchar status; - ptdescr = (pcsca_bd_t *) (card->hw.rambase + + ptdescr = (card->hw.rambase + RX_BD_ADDR(ch, chan->rx_first_bd)); while ((status = cpc_readb(&ptdescr->status)) & DST_OSB) { nchar = cpc_readw(&ptdescr->len); @@ -527,7 +527,7 @@ int dma_buf_read(pc300_t * card, int ch, struct sk_buff *skb) chan->rx_first_bd = (chan->rx_first_bd+1) & (N_DMA_RX_BUF-1); if (status & DST_EOM) break; - ptdescr = (pcsca_bd_t *) (card->hw.rambase + + ptdescr = (card->hw.rambase + cpc_readl(&ptdescr->next)); status = cpc_readb(&ptdescr->status); } @@ -536,7 +536,7 @@ int dma_buf_read(pc300_t * card, int ch, struct sk_buff *skb) if (nchar != 0) { if (skb) { memcpy_fromio(skb_put(skb, nchar), - (void *)(card->hw.rambase+cpc_readl(&ptdescr->ptbuf)),nchar); + (card->hw.rambase+cpc_readl(&ptdescr->ptbuf)),nchar); } rcvd += nchar; } @@ -547,7 +547,7 @@ int dma_buf_read(pc300_t * card, int ch, struct sk_buff *skb) if (status & DST_EOM) break; - ptdescr = (pcsca_bd_t *) (card->hw.rambase + cpc_readl(&ptdescr->next)); + ptdescr = (card->hw.rambase + cpc_readl(&ptdescr->next)); } if (rcvd != 0) { @@ -562,7 +562,7 @@ int dma_buf_read(pc300_t * card, int ch, struct sk_buff *skb) void tx_dma_stop(pc300_t * card, int ch) { - uclong scabase = card->hw.scabase; + void __iomem *scabase = card->hw.scabase; ucchar drr_ena_bit = 1 << (5 + 2 * ch); ucchar drr_rst_bit = 1 << (1 + 2 * ch); @@ -573,7 +573,7 @@ void tx_dma_stop(pc300_t * card, int ch) void rx_dma_stop(pc300_t * card, int ch) { - uclong scabase = card->hw.scabase; + void __iomem *scabase = card->hw.scabase; ucchar drr_ena_bit = 1 << (4 + 2 * ch); ucchar drr_rst_bit = 1 << (2 * ch); @@ -584,7 +584,7 @@ void rx_dma_stop(pc300_t * card, int ch) void rx_dma_start(pc300_t * card, int ch) { - uclong scabase = card->hw.scabase; + void __iomem *scabase = card->hw.scabase; pc300ch_t *chan = (pc300ch_t *) & card->chan[ch]; /* Start DMA */ @@ -609,7 +609,7 @@ void rx_dma_start(pc300_t * card, int ch) /*************************/ void falc_issue_cmd(pc300_t * card, int ch, ucchar cmd) { - uclong falcbase = card->hw.falcbase; + void __iomem *falcbase = card->hw.falcbase; unsigned long i = 0; while (cpc_readb(falcbase + F_REG(SIS, ch)) & SIS_CEC) { @@ -627,7 +627,7 @@ void falc_intr_enable(pc300_t * card, int ch) pc300ch_t *chan = (pc300ch_t *) & card->chan[ch]; pc300chconf_t *conf = (pc300chconf_t *) & chan->conf; falc_t *pfalc = (falc_t *) & chan->falc; - uclong falcbase = card->hw.falcbase; + void __iomem *falcbase = card->hw.falcbase; /* Interrupt pins are open-drain */ cpc_writeb(falcbase + F_REG(IPC, ch), @@ -674,7 +674,7 @@ void falc_intr_enable(pc300_t * card, int ch) void falc_open_timeslot(pc300_t * card, int ch, int timeslot) { - uclong falcbase = card->hw.falcbase; + void __iomem *falcbase = card->hw.falcbase; ucchar tshf = card->chan[ch].falc.offset; cpc_writeb(falcbase + F_REG((ICB1 + (timeslot - tshf) / 8), ch), @@ -690,7 +690,7 @@ void falc_open_timeslot(pc300_t * card, int ch, int timeslot) void falc_close_timeslot(pc300_t * card, int ch, int timeslot) { - uclong falcbase = card->hw.falcbase; + void __iomem *falcbase = card->hw.falcbase; ucchar tshf = card->chan[ch].falc.offset; cpc_writeb(falcbase + F_REG((ICB1 + (timeslot - tshf) / 8), ch), @@ -708,7 +708,7 @@ void falc_close_all_timeslots(pc300_t * card, int ch) { pc300ch_t *chan = (pc300ch_t *) & card->chan[ch]; pc300chconf_t *conf = (pc300chconf_t *) & chan->conf; - uclong falcbase = card->hw.falcbase; + void __iomem *falcbase = card->hw.falcbase; cpc_writeb(falcbase + F_REG(ICB1, ch), 0xff); cpc_writeb(falcbase + F_REG(TTR1, ch), 0); @@ -730,7 +730,7 @@ void falc_open_all_timeslots(pc300_t * card, int ch) { pc300ch_t *chan = (pc300ch_t *) & card->chan[ch]; pc300chconf_t *conf = (pc300chconf_t *) & chan->conf; - uclong falcbase = card->hw.falcbase; + void __iomem *falcbase = card->hw.falcbase; cpc_writeb(falcbase + F_REG(ICB1, ch), 0); if (conf->fr_mode == PC300_FR_UNFRAMED) { @@ -811,7 +811,7 @@ void falc_init_t1(pc300_t * card, int ch) pc300ch_t *chan = (pc300ch_t *) & card->chan[ch]; pc300chconf_t *conf = (pc300chconf_t *) & chan->conf; falc_t *pfalc = (falc_t *) & chan->falc; - uclong falcbase = card->hw.falcbase; + void __iomem *falcbase = card->hw.falcbase; ucchar dja = (ch ? (LIM2_DJA2 | LIM2_DJA1) : 0); /* Switch to T1 mode (PCM 24) */ @@ -980,7 +980,7 @@ void falc_init_e1(pc300_t * card, int ch) pc300ch_t *chan = (pc300ch_t *) & card->chan[ch]; pc300chconf_t *conf = (pc300chconf_t *) & chan->conf; falc_t *pfalc = (falc_t *) & chan->falc; - uclong falcbase = card->hw.falcbase; + void __iomem *falcbase = card->hw.falcbase; ucchar dja = (ch ? (LIM2_DJA2 | LIM2_DJA1) : 0); /* Switch to E1 mode (PCM 30) */ @@ -1157,7 +1157,7 @@ void falc_init_e1(pc300_t * card, int ch) void falc_init_hdlc(pc300_t * card, int ch) { - uclong falcbase = card->hw.falcbase; + void __iomem *falcbase = card->hw.falcbase; pc300ch_t *chan = (pc300ch_t *) & card->chan[ch]; pc300chconf_t *conf = (pc300chconf_t *) & chan->conf; @@ -1186,7 +1186,7 @@ void te_config(pc300_t * card, int ch) pc300ch_t *chan = (pc300ch_t *) & card->chan[ch]; pc300chconf_t *conf = (pc300chconf_t *) & chan->conf; falc_t *pfalc = (falc_t *) & chan->falc; - uclong falcbase = card->hw.falcbase; + void __iomem *falcbase = card->hw.falcbase; ucchar dummy; unsigned long flags; @@ -1246,7 +1246,7 @@ void falc_check_status(pc300_t * card, int ch, unsigned char frs0) pc300ch_t *chan = (pc300ch_t *) & card->chan[ch]; pc300chconf_t *conf = (pc300chconf_t *) & chan->conf; falc_t *pfalc = (falc_t *) & chan->falc; - uclong falcbase = card->hw.falcbase; + void __iomem *falcbase = card->hw.falcbase; /* Verify LOS */ if (frs0 & FRS0_LOS) { @@ -1402,7 +1402,7 @@ void falc_update_stats(pc300_t * card, int ch) pc300ch_t *chan = (pc300ch_t *) & card->chan[ch]; pc300chconf_t *conf = (pc300chconf_t *) & chan->conf; falc_t *pfalc = (falc_t *) & chan->falc; - uclong falcbase = card->hw.falcbase; + void __iomem *falcbase = card->hw.falcbase; ucshort counter; counter = cpc_readb(falcbase + F_REG(FECL, ch)); @@ -1455,7 +1455,7 @@ void falc_remote_loop(pc300_t * card, int ch, int loop_on) pc300ch_t *chan = (pc300ch_t *) & card->chan[ch]; pc300chconf_t *conf = (pc300chconf_t *) & chan->conf; falc_t *pfalc = (falc_t *) & chan->falc; - uclong falcbase = card->hw.falcbase; + void __iomem *falcbase = card->hw.falcbase; if (loop_on) { // EVENT_FALC_ABNORMAL @@ -1499,7 +1499,7 @@ void falc_local_loop(pc300_t * card, int ch, int loop_on) { pc300ch_t *chan = (pc300ch_t *) & card->chan[ch]; falc_t *pfalc = (falc_t *) & chan->falc; - uclong falcbase = card->hw.falcbase; + void __iomem *falcbase = card->hw.falcbase; if (loop_on) { cpc_writeb(falcbase + F_REG(LIM0, ch), @@ -1527,7 +1527,7 @@ void falc_payload_loop(pc300_t * card, int ch, int loop_on) pc300ch_t *chan = (pc300ch_t *) & card->chan[ch]; pc300chconf_t *conf = (pc300chconf_t *) & chan->conf; falc_t *pfalc = (falc_t *) & chan->falc; - uclong falcbase = card->hw.falcbase; + void __iomem *falcbase = card->hw.falcbase; if (loop_on) { // EVENT_FALC_ABNORMAL @@ -1580,7 +1580,7 @@ void turn_off_xlu(pc300_t * card, int ch) { pc300ch_t *chan = (pc300ch_t *) & card->chan[ch]; pc300chconf_t *conf = (pc300chconf_t *) & chan->conf; - uclong falcbase = card->hw.falcbase; + void __iomem *falcbase = card->hw.falcbase; if (conf->media == IF_IFACE_T1) { cpc_writeb(falcbase + F_REG(FMR5, ch), @@ -1601,7 +1601,7 @@ void turn_off_xld(pc300_t * card, int ch) { pc300ch_t *chan = (pc300ch_t *) & card->chan[ch]; pc300chconf_t *conf = (pc300chconf_t *) & chan->conf; - uclong falcbase = card->hw.falcbase; + void __iomem *falcbase = card->hw.falcbase; if (conf->media == IF_IFACE_T1) { cpc_writeb(falcbase + F_REG(FMR5, ch), @@ -1624,7 +1624,7 @@ void falc_generate_loop_up_code(pc300_t * card, int ch) pc300ch_t *chan = (pc300ch_t *) & card->chan[ch]; pc300chconf_t *conf = (pc300chconf_t *) & chan->conf; falc_t *pfalc = (falc_t *) & chan->falc; - uclong falcbase = card->hw.falcbase; + void __iomem *falcbase = card->hw.falcbase; if (conf->media == IF_IFACE_T1) { cpc_writeb(falcbase + F_REG(FMR5, ch), @@ -1657,7 +1657,7 @@ void falc_generate_loop_down_code(pc300_t * card, int ch) pc300ch_t *chan = (pc300ch_t *) & card->chan[ch]; pc300chconf_t *conf = (pc300chconf_t *) & chan->conf; falc_t *pfalc = (falc_t *) & chan->falc; - uclong falcbase = card->hw.falcbase; + void __iomem *falcbase = card->hw.falcbase; if (conf->media == IF_IFACE_T1) { cpc_writeb(falcbase + F_REG(FMR5, ch), @@ -1687,7 +1687,7 @@ void falc_pattern_test(pc300_t * card, int ch, unsigned int activate) pc300ch_t *chan = (pc300ch_t *) & card->chan[ch]; pc300chconf_t *conf = (pc300chconf_t *) & chan->conf; falc_t *pfalc = (falc_t *) & chan->falc; - uclong falcbase = card->hw.falcbase; + void __iomem *falcbase = card->hw.falcbase; if (activate) { pfalc->prbs = 1; @@ -1973,11 +1973,11 @@ static void sca_tx_intr(pc300dev_t *dev) pc300ch_t *chan = (pc300ch_t *)dev->chan; pc300_t *card = (pc300_t *)chan->card; int ch = chan->channel; - volatile pcsca_bd_t * ptdescr; + volatile pcsca_bd_t __iomem * ptdescr; struct net_device_stats *stats = hdlc_stats(dev->dev); /* Clean up descriptors from previous transmission */ - ptdescr = (pcsca_bd_t *)(card->hw.rambase + + ptdescr = (card->hw.rambase + TX_BD_ADDR(ch,chan->tx_first_bd)); while ((cpc_readl(card->hw.scabase + DTX_REG(CDAL,ch)) != TX_BD_ADDR(ch,chan->tx_first_bd)) && @@ -1988,8 +1988,7 @@ static void sca_tx_intr(pc300dev_t *dev) cpc_writew(&ptdescr->len, 0); chan->nfree_tx_bd++; chan->tx_first_bd = (chan->tx_first_bd + 1) & (N_DMA_TX_BUF - 1); - ptdescr = (pcsca_bd_t *)(card->hw.rambase + - TX_BD_ADDR(ch,chan->tx_first_bd)); + ptdescr = (card->hw.rambase + TX_BD_ADDR(ch,chan->tx_first_bd)); } #ifdef CONFIG_PC300_MLPPP @@ -2006,7 +2005,7 @@ static void sca_tx_intr(pc300dev_t *dev) static void sca_intr(pc300_t * card) { - uclong scabase = card->hw.scabase; + void __iomem *scabase = card->hw.scabase; volatile uclong status; int ch; int intr_count = 0; @@ -2187,7 +2186,7 @@ static void falc_t1_loop_detection(pc300_t * card, int ch, ucchar frs1) { pc300ch_t *chan = (pc300ch_t *) & card->chan[ch]; falc_t *pfalc = (falc_t *) & chan->falc; - uclong falcbase = card->hw.falcbase; + void __iomem *falcbase = card->hw.falcbase; if (((cpc_readb(falcbase + F_REG(LCR1, ch)) & LCR1_XPRBS) == 0) && !pfalc->loop_gen) { @@ -2212,7 +2211,7 @@ static void falc_e1_loop_detection(pc300_t * card, int ch, ucchar rsp) { pc300ch_t *chan = (pc300ch_t *) & card->chan[ch]; falc_t *pfalc = (falc_t *) & chan->falc; - uclong falcbase = card->hw.falcbase; + void __iomem *falcbase = card->hw.falcbase; if (((cpc_readb(falcbase + F_REG(LCR1, ch)) & LCR1_XPRBS) == 0) && !pfalc->loop_gen) { @@ -2237,7 +2236,7 @@ static void falc_t1_intr(pc300_t * card, int ch) { pc300ch_t *chan = (pc300ch_t *) & card->chan[ch]; falc_t *pfalc = (falc_t *) & chan->falc; - uclong falcbase = card->hw.falcbase; + void __iomem *falcbase = card->hw.falcbase; ucchar isr0, isr3, gis; ucchar dummy; @@ -2284,7 +2283,7 @@ static void falc_e1_intr(pc300_t * card, int ch) { pc300ch_t *chan = (pc300ch_t *) & card->chan[ch]; falc_t *pfalc = (falc_t *) & chan->falc; - uclong falcbase = card->hw.falcbase; + void __iomem *falcbase = card->hw.falcbase; ucchar isr1, isr2, isr3, gis, rsp; ucchar dummy; @@ -2408,7 +2407,7 @@ static irqreturn_t cpc_intr(int irq, void *dev_id, struct pt_regs *regs) void cpc_sca_status(pc300_t * card, int ch) { ucchar ilar; - uclong scabase = card->hw.scabase; + void __iomem *scabase = card->hw.scabase; uclong flags; tx_dma_buf_check(card, ch); @@ -2544,7 +2543,7 @@ int cpc_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) int ch = chan->channel; void __user *arg = ifr->ifr_data; struct if_settings *settings = &ifr->ifr_settings; - uclong scabase = card->hw.scabase; + void __iomem *scabase = card->hw.scabase; if (!capable(CAP_NET_ADMIN)) return -EPERM; @@ -2863,8 +2862,8 @@ int ch_config(pc300dev_t * d) pc300ch_t *chan = (pc300ch_t *) d->chan; pc300chconf_t *conf = (pc300chconf_t *) & chan->conf; pc300_t *card = (pc300_t *) chan->card; - uclong scabase = card->hw.scabase; - uclong plxbase = card->hw.plxbase; + void __iomem *scabase = card->hw.scabase; + void __iomem *plxbase = card->hw.plxbase; int ch = chan->channel; uclong clkrate = chan->conf.phys_settings.clock_rate; uclong clktype = chan->conf.phys_settings.clock_type; @@ -3010,7 +3009,7 @@ int rx_config(pc300dev_t * d) { pc300ch_t *chan = (pc300ch_t *) d->chan; pc300_t *card = (pc300_t *) chan->card; - uclong scabase = card->hw.scabase; + void __iomem *scabase = card->hw.scabase; int ch = chan->channel; cpc_writeb(scabase + DSR_RX(ch), 0); @@ -3041,7 +3040,7 @@ int tx_config(pc300dev_t * d) { pc300ch_t *chan = (pc300ch_t *) d->chan; pc300_t *card = (pc300_t *) chan->card; - uclong scabase = card->hw.scabase; + void __iomem *scabase = card->hw.scabase; int ch = chan->channel; cpc_writeb(scabase + DSR_TX(ch), 0); @@ -3105,7 +3104,7 @@ void cpc_opench(pc300dev_t * d) pc300ch_t *chan = (pc300ch_t *) d->chan; pc300_t *card = (pc300_t *) chan->card; int ch = chan->channel; - uclong scabase = card->hw.scabase; + void __iomem *scabase = card->hw.scabase; ch_config(d); @@ -3211,7 +3210,7 @@ static uclong detect_ram(pc300_t * card) { uclong i; ucchar data; - uclong rambase = card->hw.rambase; + void __iomem *rambase = card->hw.rambase; card->hw.ramsize = PC300_RAMSIZE; /* Let's find out how much RAM is present on this board */ @@ -3227,7 +3226,7 @@ static uclong detect_ram(pc300_t * card) static void plx_init(pc300_t * card) { - struct RUNTIME_9050 *plx_ctl = (struct RUNTIME_9050 *) card->hw.plxbase; + struct RUNTIME_9050 __iomem *plx_ctl = card->hw.plxbase; /* Reset PLX */ cpc_writel(&plx_ctl->init_ctrl, @@ -3532,10 +3531,9 @@ cpc_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) if ((err = pci_enable_device(pdev)) != 0) goto err_release_sca; - card->hw.plxbase = (uclong) ioremap(card->hw.plxphys, card->hw.plxsize); - card->hw.rambase = (uclong) ioremap(card->hw.ramphys, - card->hw.alloc_ramsize); - card->hw.scabase = (uclong) ioremap(card->hw.scaphys, card->hw.scasize); + card->hw.plxbase = ioremap(card->hw.plxphys, card->hw.plxsize); + card->hw.rambase = ioremap(card->hw.ramphys, card->hw.alloc_ramsize); + card->hw.scabase = ioremap(card->hw.scaphys, card->hw.scasize); switch (device_id) { case PCI_DEVICE_ID_PC300_TE_1: case PCI_DEVICE_ID_PC300_TE_2: @@ -3543,14 +3541,13 @@ cpc_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) case PCI_DEVICE_ID_PC300_TE_M_2: request_mem_region(card->hw.falcphys, card->hw.falcsize, "FALC Registers"); - card->hw.falcbase = (uclong) ioremap(card->hw.falcphys, - card->hw.falcsize); + card->hw.falcbase = ioremap(card->hw.falcphys, card->hw.falcsize); break; case PCI_DEVICE_ID_PC300_RX_1: case PCI_DEVICE_ID_PC300_RX_2: default: - card->hw.falcbase = 0; + card->hw.falcbase = NULL; break; } @@ -3616,11 +3613,11 @@ cpc_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) return 0; err_io_unmap: - iounmap((void *) card->hw.plxbase); - iounmap((void *) card->hw.scabase); - iounmap((void *) card->hw.rambase); + iounmap(card->hw.plxbase); + iounmap(card->hw.scabase); + iounmap(card->hw.rambase); if (card->hw.type == PC300_TE) { - iounmap((void *) card->hw.falcbase); + iounmap(card->hw.falcbase); release_mem_region(card->hw.falcphys, card->hw.falcsize); } err_release_sca: @@ -3649,15 +3646,15 @@ static void __devexit cpc_remove_one(struct pci_dev *pdev) for (i = 0; i < card->hw.nchan; i++) { unregister_hdlc_device(card->chan[i].d.dev); } - iounmap((void *) card->hw.plxbase); - iounmap((void *) card->hw.scabase); - iounmap((void *) card->hw.rambase); + iounmap(card->hw.plxbase); + iounmap(card->hw.scabase); + iounmap(card->hw.rambase); release_mem_region(card->hw.plxphys, card->hw.plxsize); release_mem_region(card->hw.ramphys, card->hw.alloc_ramsize); release_mem_region(card->hw.scaphys, card->hw.scasize); release_region(card->hw.iophys, card->hw.iosize); if (card->hw.type == PC300_TE) { - iounmap((void *) card->hw.falcbase); + iounmap(card->hw.falcbase); release_mem_region(card->hw.falcphys, card->hw.falcsize); } for (i = 0; i < card->hw.nchan; i++) diff --git a/drivers/net/wan/sdla_chdlc.c b/drivers/net/wan/sdla_chdlc.c index b00fb7837..e66c250ea 100644 --- a/drivers/net/wan/sdla_chdlc.c +++ b/drivers/net/wan/sdla_chdlc.c @@ -3628,11 +3628,7 @@ static void tty_poll_work (void* data) if ((tty=card->tty)==NULL) return; - if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && - tty->ldisc.write_wakeup){ - (tty->ldisc.write_wakeup)(tty); - } - wake_up_interruptible(&tty->write_wait); + tty_wakeup(tty); #if defined(SERIAL_HAVE_POLL_WAIT) wake_up_interruptible(&tty->poll_wait); #endif @@ -3857,6 +3853,7 @@ static void wanpipe_tty_receive(sdla_t *card, unsigned addr, unsigned int len) char fp=0; struct tty_struct *tty; int i; + struct tty_ldisc *ld; if (!card->tty_open){ dbg_printk(KERN_INFO "%s: TTY not open during receive\n", @@ -3944,8 +3941,11 @@ static void wanpipe_tty_receive(sdla_t *card, unsigned addr, unsigned int len) len -= offset; } sdla_peek(&card->hw, addr, card->tty_rx+offset, len); - if (tty->ldisc.receive_buf){ - tty->ldisc.receive_buf(tty,card->tty_rx,&fp,olen); + ld = tty_ldisc_ref(tty); + if (ld) { + if (ld->receive_buf) + ld->receive_buf(tty,card->tty_rx,&fp,olen); + tty_ldisc_deref(ld); }else{ if (net_ratelimit()){ printk(KERN_INFO @@ -4252,14 +4252,10 @@ static void wanpipe_tty_flush_buffer(struct tty_struct *tty) if (!tty) return; - wake_up_interruptible(&tty->write_wait); #if defined(SERIAL_HAVE_POLL_WAIT) wake_up_interruptible(&tty->poll_wait); #endif - if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && - tty->ldisc.write_wakeup) - (tty->ldisc.write_wakeup)(tty); - + tty_wakeup(tty); return; } diff --git a/drivers/net/wan/sdla_fr.c b/drivers/net/wan/sdla_fr.c index 52f40883d..2efccb055 100644 --- a/drivers/net/wan/sdla_fr.c +++ b/drivers/net/wan/sdla_fr.c @@ -3678,7 +3678,7 @@ static int process_udp_mgmt_pkt(sdla_t* card) break; } - (void *)ptr_trc_el = card->u.f.curr_trc_el; + ptr_trc_el = (void *)card->u.f.curr_trc_el; buffer_length = 0; fr_udp_pkt->data[0x00] = 0x00; @@ -3729,7 +3729,7 @@ static int process_udp_mgmt_pkt(sdla_t* card) ptr_trc_el ++; if((void *)ptr_trc_el > card->u.f.trc_el_last) - (void*)ptr_trc_el = card->u.f.trc_el_base; + ptr_trc_el = (void*)card->u.f.trc_el_base; buffer_length += sizeof(fpipemon_trc_hdr_t); if(fpipemon_trc->fpipemon_trc_hdr.data_passed) { diff --git a/drivers/net/wan/sdladrv.c b/drivers/net/wan/sdladrv.c index 501e9a752..c8bc6da57 100644 --- a/drivers/net/wan/sdladrv.c +++ b/drivers/net/wan/sdladrv.c @@ -937,7 +937,7 @@ int sdla_peek (sdlahw_t* hw, unsigned long addr, void* buf, unsigned len) peek_by_4 ((unsigned long)hw->dpmbase + curpos, buf, curlen); addr += curlen; - (char*)buf += curlen; + buf = (char*)buf + curlen; len -= curlen; } @@ -1019,7 +1019,7 @@ int sdla_poke (sdlahw_t* hw, unsigned long addr, void* buf, unsigned len) poke_by_4 ((unsigned long)hw->dpmbase + curpos, buf, curlen); addr += curlen; - (char*)buf += curlen; + buf = (char*)buf + curlen; len -= curlen; } @@ -2001,7 +2001,7 @@ static int detect_s514 (sdlahw_t* hw) (void *)hw->dpmbase = ioremap((unsigned long)S514_mem_base_addr, (unsigned long)MAX_SIZEOF_S514_MEMORY); /* map the physical control register memory to virtual memory */ - (void *)hw->vector = ioremap( + hw->vector = (unsigned long)ioremap( (unsigned long)(S514_mem_base_addr + S514_CTRL_REG_BYTE), (unsigned long)16); diff --git a/drivers/net/wan/sdlamain.c b/drivers/net/wan/sdlamain.c index be10f8c41..74e151ace 100644 --- a/drivers/net/wan/sdlamain.c +++ b/drivers/net/wan/sdlamain.c @@ -976,7 +976,7 @@ static int ioctl_dump (sdla_t* card, sdla_dump_t* u_dump) dump.length -= len; dump.offset += len; - (char*)dump.ptr += len; + dump.ptr = (char*)dump.ptr + len; } sdla_mapmem(&card->hw, oldvec);/* restore DPM window position */ diff --git a/drivers/net/wireless/airport.c b/drivers/net/wireless/airport.c index 20bd0dfef..51c54132c 100644 --- a/drivers/net/wireless/airport.c +++ b/drivers/net/wireless/airport.c @@ -1,4 +1,4 @@ -/* airport.c 0.13e +/* airport.c * * A driver for "Hermes" chipset based Apple Airport wireless * card. @@ -11,6 +11,9 @@ * 0.06 : fix possible hang on powerup, add sleep support */ +#define DRIVER_NAME "airport" +#define PFX DRIVER_NAME ": " + #include #include @@ -25,6 +28,7 @@ #include #include #include +#include #include #include @@ -50,7 +54,7 @@ static int airport_suspend(struct macio_dev *mdev, u32 state) { struct net_device *dev = dev_get_drvdata(&mdev->ofdev.dev); - struct orinoco_private *priv = dev->priv; + struct orinoco_private *priv = netdev_priv(dev); unsigned long flags; int err; @@ -84,15 +88,14 @@ static int airport_resume(struct macio_dev *mdev) { struct net_device *dev = dev_get_drvdata(&mdev->ofdev.dev); - struct orinoco_private *priv = dev->priv; + 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); - set_current_state(TASK_UNINTERRUPTIBLE); - schedule_timeout(HZ/5); + msleep(200); enable_irq(dev->irq); @@ -126,7 +129,7 @@ static int airport_detach(struct macio_dev *mdev) { struct net_device *dev = dev_get_drvdata(&mdev->ofdev.dev); - struct orinoco_private *priv = dev->priv; + struct orinoco_private *priv = netdev_priv(dev); struct airport *card = priv->card; if (card->ndev_registered) @@ -144,8 +147,7 @@ airport_detach(struct macio_dev *mdev) macio_release_resource(mdev, 0); pmac_call_feature(PMAC_FTR_AIRPORT_ENABLE, macio_get_of_node(mdev), 0, 0); - set_current_state(TASK_UNINTERRUPTIBLE); - schedule_timeout(HZ); + ssleep(1); macio_set_drvdata(mdev, NULL); free_netdev(dev); @@ -171,14 +173,12 @@ static int airport_hard_reset(struct orinoco_private *priv) disable_irq(dev->irq); pmac_call_feature(PMAC_FTR_AIRPORT_ENABLE, macio_get_of_node(card->mdev), 0, 0); - set_current_state(TASK_UNINTERRUPTIBLE); - schedule_timeout(HZ); + ssleep(1); pmac_call_feature(PMAC_FTR_AIRPORT_ENABLE, macio_get_of_node(card->mdev), 0, 1); - set_current_state(TASK_UNINTERRUPTIBLE); - schedule_timeout(HZ); + ssleep(1); enable_irq(dev->irq); - schedule_timeout(HZ); + ssleep(1); #endif return 0; @@ -194,24 +194,24 @@ airport_attach(struct macio_dev *mdev, const struct of_match *match) hermes_t *hw; if (macio_resource_count(mdev) < 1 || macio_irq_count(mdev) < 1) { - printk(KERN_ERR "airport: wrong interrupt/addresses in OF tree\n"); + 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), airport_hard_reset); if (! dev) { - printk(KERN_ERR "airport: can't allocate device datas\n"); + printk(KERN_ERR PFX "can't allocate device datas\n"); return -ENODEV; } - priv = dev->priv; + priv = netdev_priv(dev); card = priv->card; hw = &priv->hw; card->mdev = mdev; if (macio_request_resource(mdev, 0, "airport")) { - printk(KERN_ERR "airport: can't request IO resource !\n"); + printk(KERN_ERR PFX "can't request IO resource !\n"); free_netdev(dev); return -EBUSY; } @@ -224,11 +224,11 @@ airport_attach(struct macio_dev *mdev, const struct of_match *match) /* Setup interrupts & base address */ dev->irq = macio_irq(mdev, 0); phys_addr = macio_resource_start(mdev, 0); /* Physical address */ - printk(KERN_DEBUG "Airport at physical address %lx\n", phys_addr); + printk(KERN_DEBUG PFX "Airport at physical address %lx\n", phys_addr); dev->base_addr = phys_addr; card->vaddr = ioremap(phys_addr, AIRPORT_IO_LEN); if (!card->vaddr) { - printk("airport: ioremap() failed\n"); + printk(PFX "ioremap() failed\n"); goto failed; } @@ -237,24 +237,23 @@ airport_attach(struct macio_dev *mdev, const struct of_match *match) /* Power up card */ pmac_call_feature(PMAC_FTR_AIRPORT_ENABLE, macio_get_of_node(mdev), 0, 1); - set_current_state(TASK_UNINTERRUPTIBLE); - schedule_timeout(HZ); + ssleep(1); /* Reset it before we get the interrupt */ hermes_init(hw); if (request_irq(dev->irq, orinoco_interrupt, 0, "Airport", dev)) { - printk(KERN_ERR "airport: Couldn't get IRQ %d\n", dev->irq); + 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 "airport: register_netdev() failed\n"); + printk(KERN_ERR PFX "register_netdev() failed\n"); goto failed; } - printk(KERN_DEBUG "airport: card registered for interface %s\n", dev->name); + printk(KERN_DEBUG PFX "card registered for interface %s\n", dev->name); card->ndev_registered = 1; return 0; failed: @@ -263,7 +262,8 @@ airport_attach(struct macio_dev *mdev, const struct of_match *match) } /* airport_attach */ -static char version[] __initdata = "airport.c 0.13e (Benjamin Herrenschmidt )"; +static char version[] __initdata = DRIVER_NAME " " DRIVER_VERSION + " (Benjamin Herrenschmidt )"; MODULE_AUTHOR("Benjamin Herrenschmidt "); MODULE_DESCRIPTION("Driver for the Apple Airport wireless card."); MODULE_LICENSE("Dual MPL/GPL"); @@ -280,7 +280,7 @@ static struct of_match airport_match[] = static struct macio_driver airport_driver = { - .name = "airport", + .name = DRIVER_NAME, .match_table = airport_match, .probe = airport_attach, .remove = airport_detach, diff --git a/drivers/net/wireless/hermes.c b/drivers/net/wireless/hermes.c index d5ec3dedb..730090118 100644 --- a/drivers/net/wireless/hermes.c +++ b/drivers/net/wireless/hermes.c @@ -13,8 +13,8 @@ * (wvlan_hcf.c) library, and the NetBSD wireless driver (in no * particular order). * - * Copyright (C) 2000, David Gibson, Linuxcare Australia - * Copyright (C) 2001, David Gibson, IBM + * Copyright (C) 2000, David Gibson, Linuxcare Australia. + * (C) Copyright David Gibson, IBM Corp. 2001-2003. * * 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 @@ -53,10 +53,9 @@ #include "hermes.h" MODULE_DESCRIPTION("Low-level driver helper for Lucent Hermes chipset and Prism II HFA384x wireless MAC controller"); -MODULE_AUTHOR("David Gibson "); -#ifdef MODULE_LICENSE +MODULE_AUTHOR("Pavel Roskin " + " & David Gibson "); MODULE_LICENSE("Dual MPL/GPL"); -#endif /* These are maximum timeouts. Most often, card wil react much faster */ #define CMD_BUSY_TIMEOUT (100) /* In iterations of ~1us */ @@ -226,7 +225,7 @@ int hermes_init(hermes_t *hw) * * Callable from any context, but locking is your problem. */ int hermes_docmd_wait(hermes_t *hw, u16 cmd, u16 parm0, - hermes_response_t *resp) + struct hermes_response *resp) { int err; int k; @@ -392,7 +391,6 @@ static int hermes_bap_seek(hermes_t *hw, int bap, u16 id, u16 offset) return -EIO; } - return 0; } diff --git a/drivers/net/wireless/hermes_rid.h b/drivers/net/wireless/hermes_rid.h index 761c5422e..4f46b4809 100644 --- a/drivers/net/wireless/hermes_rid.h +++ b/drivers/net/wireless/hermes_rid.h @@ -4,21 +4,21 @@ /* * Configuration RIDs */ -#define HERMES_RID_CNFPORTTYPE 0xFC00 /* used */ -#define HERMES_RID_CNFOWNMACADDR 0xFC01 /* used */ -#define HERMES_RID_CNFDESIREDSSID 0xFC02 /* used */ -#define HERMES_RID_CNFOWNCHANNEL 0xFC03 /* used */ -#define HERMES_RID_CNFOWNSSID 0xFC04 /* used */ +#define HERMES_RID_CNFPORTTYPE 0xFC00 +#define HERMES_RID_CNFOWNMACADDR 0xFC01 +#define HERMES_RID_CNFDESIREDSSID 0xFC02 +#define HERMES_RID_CNFOWNCHANNEL 0xFC03 +#define HERMES_RID_CNFOWNSSID 0xFC04 #define HERMES_RID_CNFOWNATIMWINDOW 0xFC05 -#define HERMES_RID_CNFSYSTEMSCALE 0xFC06 /* used */ +#define HERMES_RID_CNFSYSTEMSCALE 0xFC06 #define HERMES_RID_CNFMAXDATALEN 0xFC07 #define HERMES_RID_CNFWDSADDRESS 0xFC08 -#define HERMES_RID_CNFPMENABLED 0xFC09 /* used */ +#define HERMES_RID_CNFPMENABLED 0xFC09 #define HERMES_RID_CNFPMEPS 0xFC0A -#define HERMES_RID_CNFMULTICASTRECEIVE 0xFC0B /* used */ -#define HERMES_RID_CNFMAXSLEEPDURATION 0xFC0C /* used */ -#define HERMES_RID_CNFPMHOLDOVERDURATION 0xFC0D /* used */ -#define HERMES_RID_CNFOWNNAME 0xFC0E /* used */ +#define HERMES_RID_CNFMULTICASTRECEIVE 0xFC0B +#define HERMES_RID_CNFMAXSLEEPDURATION 0xFC0C +#define HERMES_RID_CNFPMHOLDOVERDURATION 0xFC0D +#define HERMES_RID_CNFOWNNAME 0xFC0E #define HERMES_RID_CNFOWNDTIMPERIOD 0xFC10 #define HERMES_RID_CNFWDSADDRESS1 0xFC11 #define HERMES_RID_CNFWDSADDRESS2 0xFC12 @@ -27,17 +27,18 @@ #define HERMES_RID_CNFWDSADDRESS5 0xFC15 #define HERMES_RID_CNFWDSADDRESS6 0xFC16 #define HERMES_RID_CNFMULTICASTPMBUFFERING 0xFC17 -#define HERMES_RID_CNFWEPENABLED_AGERE 0xFC20 /* used */ +#define HERMES_RID_CNFWEPENABLED_AGERE 0xFC20 +#define HERMES_RID_CNFAUTHENTICATION_AGERE 0xFC21 #define HERMES_RID_CNFMANDATORYBSSID_SYMBOL 0xFC21 -#define HERMES_RID_CNFWEPDEFAULTKEYID 0xFC23 /* used */ -#define HERMES_RID_CNFDEFAULTKEY0 0xFC24 /* used */ -#define HERMES_RID_CNFDEFAULTKEY1 0xFC25 /* used */ -#define HERMES_RID_CNFMWOROBUST_AGERE 0xFC25 /* used */ -#define HERMES_RID_CNFDEFAULTKEY2 0xFC26 /* used */ -#define HERMES_RID_CNFDEFAULTKEY3 0xFC27 /* used */ -#define HERMES_RID_CNFWEPFLAGS_INTERSIL 0xFC28 /* used */ +#define HERMES_RID_CNFWEPDEFAULTKEYID 0xFC23 +#define HERMES_RID_CNFDEFAULTKEY0 0xFC24 +#define HERMES_RID_CNFDEFAULTKEY1 0xFC25 +#define HERMES_RID_CNFMWOROBUST_AGERE 0xFC25 +#define HERMES_RID_CNFDEFAULTKEY2 0xFC26 +#define HERMES_RID_CNFDEFAULTKEY3 0xFC27 +#define HERMES_RID_CNFWEPFLAGS_INTERSIL 0xFC28 #define HERMES_RID_CNFWEPKEYMAPPINGTABLE 0xFC29 -#define HERMES_RID_CNFAUTHENTICATION 0xFC2A /* used */ +#define HERMES_RID_CNFAUTHENTICATION 0xFC2A #define HERMES_RID_CNFMAXASSOCSTA 0xFC2B #define HERMES_RID_CNFKEYLENGTH_SYMBOL 0xFC2B #define HERMES_RID_CNFTXCONTROL 0xFC2C @@ -53,14 +54,14 @@ #define HERMES_RID_CNFTIMCTRL 0xFC40 #define HERMES_RID_CNFTHIRTY2TALLY 0xFC42 #define HERMES_RID_CNFENHSECURITY 0xFC43 -#define HERMES_RID_CNFGROUPADDRESSES 0xFC80 /* used */ -#define HERMES_RID_CNFCREATEIBSS 0xFC81 /* used */ -#define HERMES_RID_CNFFRAGMENTATIONTHRESHOLD 0xFC82 /* used */ -#define HERMES_RID_CNFRTSTHRESHOLD 0xFC83 /* used */ -#define HERMES_RID_CNFTXRATECONTROL 0xFC84 /* used */ -#define HERMES_RID_CNFPROMISCUOUSMODE 0xFC85 /* used */ +#define HERMES_RID_CNFGROUPADDRESSES 0xFC80 +#define HERMES_RID_CNFCREATEIBSS 0xFC81 +#define HERMES_RID_CNFFRAGMENTATIONTHRESHOLD 0xFC82 +#define HERMES_RID_CNFRTSTHRESHOLD 0xFC83 +#define HERMES_RID_CNFTXRATECONTROL 0xFC84 +#define HERMES_RID_CNFPROMISCUOUSMODE 0xFC85 #define HERMES_RID_CNFBASICRATES_SYMBOL 0xFC8A -#define HERMES_RID_CNFPREAMBLE_SYMBOL 0xFC8C /* used */ +#define HERMES_RID_CNFPREAMBLE_SYMBOL 0xFC8C #define HERMES_RID_CNFFRAGMENTATIONTHRESHOLD0 0xFC90 #define HERMES_RID_CNFFRAGMENTATIONTHRESHOLD1 0xFC91 #define HERMES_RID_CNFFRAGMENTATIONTHRESHOLD2 0xFC92 @@ -75,18 +76,21 @@ #define HERMES_RID_CNFRTSTHRESHOLD4 0xFC9B #define HERMES_RID_CNFRTSTHRESHOLD5 0xFC9C #define HERMES_RID_CNFRTSTHRESHOLD6 0xFC9D +#define HERMES_RID_CNFHOSTSCAN_SYMBOL 0xFCAB #define HERMES_RID_CNFSHORTPREAMBLE 0xFCB0 -#define HERMES_RID_CNFWEPKEYS_AGERE 0xFCB0 /* used */ +#define HERMES_RID_CNFWEPKEYS_AGERE 0xFCB0 #define HERMES_RID_CNFEXCLUDELONGPREAMBLE 0xFCB1 -#define HERMES_RID_CNFTXKEY_AGERE 0xFCB1 /* used */ +#define HERMES_RID_CNFTXKEY_AGERE 0xFCB1 #define HERMES_RID_CNFAUTHENTICATIONRSPTO 0xFCB2 +#define HERMES_RID_CNFSCANSSID_AGERE 0xFCB2 #define HERMES_RID_CNFBASICRATES 0xFCB3 #define HERMES_RID_CNFSUPPORTEDRATES 0xFCB4 -#define HERMES_RID_CNFTICKTIME 0xFCE0 /* used */ +#define HERMES_RID_CNFTICKTIME 0xFCE0 #define HERMES_RID_CNFSCANREQUEST 0xFCE1 #define HERMES_RID_CNFJOINREQUEST 0xFCE2 #define HERMES_RID_CNFAUTHENTICATESTATION 0xFCE3 #define HERMES_RID_CNFCHANNELINFOREQUEST 0xFCE4 +#define HERMES_RID_CNFHOSTSCAN 0xFCE5 /* * Information RIDs @@ -100,30 +104,31 @@ #define HERMES_RID_NICID 0xFD0B #define HERMES_RID_MFISUPRANGE 0xFD0C #define HERMES_RID_CFISUPRANGE 0xFD0D -#define HERMES_RID_CHANNELLIST 0xFD10 /* used */ +#define HERMES_RID_CHANNELLIST 0xFD10 #define HERMES_RID_REGULATORYDOMAINS 0xFD11 #define HERMES_RID_TEMPTYPE 0xFD12 #define HERMES_RID_CIS 0xFD13 -#define HERMES_RID_STAID 0xFD20 /* used */ +#define HERMES_RID_STAID 0xFD20 #define HERMES_RID_STASUPRANGE 0xFD21 #define HERMES_RID_MFIACTRANGES 0xFD22 #define HERMES_RID_CFIACTRANGES2 0xFD23 -#define HERMES_RID_SECONDARYVERSION_SYMBOL 0xFD24 /* used */ +#define HERMES_RID_SECONDARYVERSION_SYMBOL 0xFD24 #define HERMES_RID_PORTSTATUS 0xFD40 -#define HERMES_RID_CURRENTSSID 0xFD41 /* used */ -#define HERMES_RID_CURRENTBSSID 0xFD42 /* used */ -#define HERMES_RID_COMMSQUALITY 0xFD43 /* used */ -#define HERMES_RID_CURRENTTXRATE 0xFD44 /* used */ +#define HERMES_RID_CURRENTSSID 0xFD41 +#define HERMES_RID_CURRENTBSSID 0xFD42 +#define HERMES_RID_COMMSQUALITY 0xFD43 +#define HERMES_RID_CURRENTTXRATE 0xFD44 #define HERMES_RID_CURRENTBEACONINTERVAL 0xFD45 #define HERMES_RID_CURRENTSCALETHRESHOLDS 0xFD46 #define HERMES_RID_PROTOCOLRSPTIME 0xFD47 -#define HERMES_RID_SHORTRETRYLIMIT 0xFD48 /* used */ -#define HERMES_RID_LONGRETRYLIMIT 0xFD49 /* used */ -#define HERMES_RID_MAXTRANSMITLIFETIME 0xFD4A /* used */ +#define HERMES_RID_SHORTRETRYLIMIT 0xFD48 +#define HERMES_RID_LONGRETRYLIMIT 0xFD49 +#define HERMES_RID_MAXTRANSMITLIFETIME 0xFD4A #define HERMES_RID_MAXRECEIVELIFETIME 0xFD4B #define HERMES_RID_CFPOLLABLE 0xFD4C #define HERMES_RID_AUTHENTICATIONALGORITHMS 0xFD4D #define HERMES_RID_PRIVACYOPTIONIMPLEMENTED 0xFD4F +#define HERMES_RID_DBMCOMMSQUALITY_INTERSIL 0xFD51 #define HERMES_RID_CURRENTTXRATE1 0xFD80 #define HERMES_RID_CURRENTTXRATE2 0xFD81 #define HERMES_RID_CURRENTTXRATE3 0xFD82 @@ -133,21 +138,11 @@ #define HERMES_RID_OWNMACADDR 0xFD86 #define HERMES_RID_SCANRESULTSTABLE 0xFD88 #define HERMES_RID_PHYTYPE 0xFDC0 -#define HERMES_RID_CURRENTCHANNEL 0xFDC1 /* used */ +#define HERMES_RID_CURRENTCHANNEL 0xFDC1 #define HERMES_RID_CURRENTPOWERSTATE 0xFDC2 #define HERMES_RID_CCAMODE 0xFDC3 -#define HERMES_RID_SUPPORTEDDATARATES 0xFDC6 /* used */ +#define HERMES_RID_SUPPORTEDDATARATES 0xFDC6 #define HERMES_RID_BUILDSEQ 0xFFFE #define HERMES_RID_FWID 0xFFFF -/* "ID" structure - used for ESSID and station nickname */ -struct hermes_idstring { - u16 len; - u16 val[16]; -} __attribute__ ((packed)); - -typedef struct hermes_multicast { - u8 addr[HERMES_MAX_MULTICAST][ETH_ALEN]; -} __attribute__ ((packed)) hermes_multicast_t; - #endif diff --git a/drivers/net/wireless/ieee80211/LICENSE b/drivers/net/wireless/ieee80211/LICENSE new file mode 100644 index 000000000..5f297e5bb --- /dev/null +++ b/drivers/net/wireless/ieee80211/LICENSE @@ -0,0 +1,339 @@ + +"This software program is licensed subject to the GNU General Public License +(GPL). Version 2, June 1991, available at +" + +GNU General Public License + +Version 2, June 1991 + +Copyright (C) 1989, 1991 Free Software Foundation, Inc. +59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + +Everyone is permitted to copy and distribute verbatim copies of this license +document, but changing it is not allowed. + +Preamble + +The licenses for most software are designed to take away your freedom to +share and change it. By contrast, the GNU General Public License is intended +to guarantee your freedom to share and change free software--to make sure +the software is free for all its users. This General Public License applies +to most of the Free Software Foundation's software and to any other program +whose authors commit to using it. (Some other Free Software Foundation +software is covered by the GNU Library General Public License instead.) You +can apply it to your programs, too. + +When we speak of free software, we are referring to freedom, not price. Our +General Public Licenses are designed to make sure that you have the freedom +to distribute copies of free software (and charge for this service if you +wish), that you receive source code or can get it if you want it, that you +can change the software or use pieces of it in new free programs; and that +you know you can do these things. + +To protect your rights, we need to make restrictions that forbid anyone to +deny you these rights or to ask you to surrender the rights. These +restrictions translate to certain responsibilities for you if you distribute +copies of the software, or if you modify it. + +For example, if you distribute copies of such a program, whether gratis or +for a fee, you must give the recipients all the rights that you have. You +must make sure that they, too, receive or can get the source code. And you +must show them these terms so they know their rights. + +We protect your rights with two steps: (1) copyright the software, and (2) +offer you this license which gives you legal permission to copy, distribute +and/or modify the software. + +Also, for each author's protection and ours, we want to make certain that +everyone understands that there is no warranty for this free software. If +the software is modified by someone else and passed on, we want its +recipients to know that what they have is not the original, so that any +problems introduced by others will not reflect on the original authors' +reputations. + +Finally, any free program is threatened constantly by software patents. We +wish to avoid the danger that redistributors of a free program will +individually obtain patent licenses, in effect making the program +proprietary. To prevent this, we have made it clear that any patent must be +licensed for everyone's free use or not licensed at all. + +The precise terms and conditions for copying, distribution and modification +follow. + +TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + +0. This License applies to any program or other work which contains a notice + placed by the copyright holder saying it may be distributed under the + terms of this General Public License. The "Program", below, refers to any + such program or work, and a "work based on the Program" means either the + Program or any derivative work under copyright law: that is to say, a + work containing the Program or a portion of it, either verbatim or with + modifications and/or translated into another language. (Hereinafter, + translation is included without limitation in the term "modification".) + Each licensee is addressed as "you". + + Activities other than copying, distribution and modification are not + covered by this License; they are outside its scope. The act of running + the Program is not restricted, and the output from the Program is covered + only if its contents constitute a work based on the Program (independent + of having been made by running the Program). Whether that is true depends + on what the Program does. + +1. You may copy and distribute verbatim copies of the Program's source code + as you receive it, in any medium, provided that you conspicuously and + appropriately publish on each copy an appropriate copyright notice and + disclaimer of warranty; keep intact all the notices that refer to this + License and to the absence of any warranty; and give any other recipients + of the Program a copy of this License along with the Program. + + You may charge a fee for the physical act of transferring a copy, and you + may at your option offer warranty protection in exchange for a fee. + +2. You may modify your copy or copies of the Program or any portion of it, + thus forming a work based on the Program, and copy and distribute such + modifications or work under the terms of Section 1 above, provided that + you also meet all of these conditions: + + * a) You must cause the modified files to carry prominent notices stating + that you changed the files and the date of any change. + + * b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any part + thereof, to be licensed as a whole at no charge to all third parties + under the terms of this License. + + * c) If the modified program normally reads commands interactively when + run, you must cause it, when started running for such interactive + use in the most ordinary way, to print or display an announcement + including an appropriate copyright notice and a notice that there is + no warranty (or else, saying that you provide a warranty) and that + users may redistribute the program under these conditions, and + telling the user how to view a copy of this License. (Exception: if + the Program itself is interactive but does not normally print such + an announcement, your work based on the Program is not required to + print an announcement.) + + These requirements apply to the modified work as a whole. If identifiable + sections of that work are not derived from the Program, and can be + reasonably considered independent and separate works in themselves, then + this License, and its terms, do not apply to those sections when you + distribute them as separate works. But when you distribute the same + sections as part of a whole which is a work based on the Program, the + distribution of the whole must be on the terms of this License, whose + permissions for other licensees extend to the entire whole, and thus to + each and every part regardless of who wrote it. + + Thus, it is not the intent of this section to claim rights or contest + your rights to work written entirely by you; rather, the intent is to + exercise the right to control the distribution of derivative or + collective works based on the Program. + + In addition, mere aggregation of another work not based on the Program + with the Program (or with a work based on the Program) on a volume of a + storage or distribution medium does not bring the other work under the + scope of this License. + +3. You may copy and distribute the Program (or a work based on it, under + Section 2) in object code or executable form under the terms of Sections + 1 and 2 above provided that you also do one of the following: + + * a) Accompany it with the complete corresponding machine-readable source + code, which must be distributed under the terms of Sections 1 and 2 + above on a medium customarily used for software interchange; or, + + * b) Accompany it with a written offer, valid for at least three years, + to give any third party, for a charge no more than your cost of + physically performing source distribution, a complete machine- + readable copy of the corresponding source code, to be distributed + under the terms of Sections 1 and 2 above on a medium customarily + used for software interchange; or, + + * c) Accompany it with the information you received as to the offer to + distribute corresponding source code. (This alternative is allowed + only for noncommercial distribution and only if you received the + program in object code or executable form with such an offer, in + accord with Subsection b above.) + + The source code for a work means the preferred form of the work for + making modifications to it. For an executable work, complete source code + means all the source code for all modules it contains, plus any + associated interface definition files, plus the scripts used to control + compilation and installation of the executable. However, as a special + exception, the source code distributed need not include anything that is + normally distributed (in either source or binary form) with the major + components (compiler, kernel, and so on) of the operating system on which + the executable runs, unless that component itself accompanies the + executable. + + If distribution of executable or object code is made by offering access + to copy from a designated place, then offering equivalent access to copy + the source code from the same place counts as distribution of the source + code, even though third parties are not compelled to copy the source + along with the object code. + +4. You may not copy, modify, sublicense, or distribute the Program except as + expressly provided under this License. Any attempt otherwise to copy, + modify, sublicense or distribute the Program is void, and will + automatically terminate your rights under this License. However, parties + who have received copies, or rights, from you under this License will not + have their licenses terminated so long as such parties remain in full + compliance. + +5. You are not required to accept this License, since you have not signed + it. However, nothing else grants you permission to modify or distribute + the Program or its derivative works. These actions are prohibited by law + if you do not accept this License. Therefore, by modifying or + distributing the Program (or any work based on the Program), you + indicate your acceptance of this License to do so, and all its terms and + conditions for copying, distributing or modifying the Program or works + based on it. + +6. Each time you redistribute the Program (or any work based on the + Program), the recipient automatically receives a license from the + original licensor to copy, distribute or modify the Program subject to + these terms and conditions. You may not impose any further restrictions + on the recipients' exercise of the rights granted herein. You are not + responsible for enforcing compliance by third parties to this License. + +7. If, as a consequence of a court judgment or allegation of patent + infringement or for any other reason (not limited to patent issues), + conditions are imposed on you (whether by court order, agreement or + otherwise) that contradict the conditions of this License, they do not + excuse you from the conditions of this License. If you cannot distribute + so as to satisfy simultaneously your obligations under this License and + any other pertinent obligations, then as a consequence you may not + distribute the Program at all. For example, if a patent license would + not permit royalty-free redistribution of the Program by all those who + receive copies directly or indirectly through you, then the only way you + could satisfy both it and this License would be to refrain entirely from + distribution of the Program. + + If any portion of this section is held invalid or unenforceable under any + particular circumstance, the balance of the section is intended to apply + and the section as a whole is intended to apply in other circumstances. + + It is not the purpose of this section to induce you to infringe any + patents or other property right claims or to contest validity of any + such claims; this section has the sole purpose of protecting the + integrity of the free software distribution system, which is implemented + by public license practices. Many people have made generous contributions + to the wide range of software distributed through that system in + reliance on consistent application of that system; it is up to the + author/donor to decide if he or she is willing to distribute software + through any other system and a licensee cannot impose that choice. + + This section is intended to make thoroughly clear what is believed to be + a consequence of the rest of this License. + +8. If the distribution and/or use of the Program is restricted in certain + countries either by patents or by copyrighted interfaces, the original + copyright holder who places the Program under this License may add an + explicit geographical distribution limitation excluding those countries, + so that distribution is permitted only in or among countries not thus + excluded. In such case, this License incorporates the limitation as if + written in the body of this License. + +9. The Free Software Foundation may publish revised and/or new versions of + the General Public License from time to time. Such new versions will be + similar in spirit to the present version, but may differ in detail to + address new problems or concerns. + + Each version is given a distinguishing version number. If the Program + specifies a version number of this License which applies to it and "any + later version", you have the option of following the terms and + conditions either of that version or of any later version published by + the Free Software Foundation. If the Program does not specify a version + number of this License, you may choose any version ever published by the + Free Software Foundation. + +10. If you wish to incorporate parts of the Program into other free programs + whose distribution conditions are different, write to the author to ask + for permission. For software which is copyrighted by the Free Software + Foundation, write to the Free Software Foundation; we sometimes make + exceptions for this. Our decision will be guided by the two goals of + preserving the free status of all derivatives of our free software and + of promoting the sharing and reuse of software generally. + + NO WARRANTY + +11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY + FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN + OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES + PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER + EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE + ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH + YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL + NECESSARY SERVICING, REPAIR OR CORRECTION. + +12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING + WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR + REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR + DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL + DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM + (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED + INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF + THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR + OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + +END OF TERMS AND CONDITIONS + +How to Apply These Terms to Your New Programs + +If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it free +software which everyone can redistribute and change under these terms. + +To do so, attach the following notices to the program. It is safest to +attach them to the start of each source file to most effectively convey the +exclusion of warranty; and each file should have at least the "copyright" +line and a pointer to where the full notice is found. + +one line to give the program's name and an idea of what it does. +Copyright (C) yyyy name of author + +This program is free software; you can redistribute it and/or modify it +under the terms of the 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. + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this when +it starts in an interactive mode: + +Gnomovision version 69, Copyright (C) year name of author Gnomovision comes +with ABSOLUTELY NO WARRANTY; for details type 'show w'. This is free +software, and you are welcome to redistribute it under certain conditions; +type 'show c' for details. + +The hypothetical commands 'show w' and 'show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may be +called something other than 'show w' and 'show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + +Yoyodyne, Inc., hereby disclaims all copyright interest in the program +'Gnomovision' (which makes passes at compilers) written by James Hacker. + +signature of Ty Coon, 1 April 1989 +Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Library General Public +License instead of this License. diff --git a/drivers/net/wireless/ieee80211/Makefile b/drivers/net/wireless/ieee80211/Makefile new file mode 100644 index 000000000..0d0b3e01b --- /dev/null +++ b/drivers/net/wireless/ieee80211/Makefile @@ -0,0 +1,23 @@ +# +# Makefile for the Linux Wireless network device drivers. +# +# Original makefile by Peter Johanson + +list-m := + +list-$(CONFIG_IEEE80211) += ieee80211 +list-$(CONFIG_IEEE80211_CRYPT) += ieee80211_crypt +list-$(CONFIG_IEEE80211_CRYPT) += ieee80211_crypt_wep +list-$(CONFIG_IEEE80211_WPA) += ieee80211_crypt_ccmp +list-$(CONFIG_IEEE80211_WPA) += ieee80211_crypt_tkip + +obj-$(CONFIG_IEEE80211) += ieee80211.o +obj-$(CONFIG_IEEE80211_CRYPT) += ieee80211_crypt.o +obj-$(CONFIG_IEEE80211_CRYPT) += ieee80211_crypt_wep.o +obj-$(CONFIG_IEEE80211_WPA) += ieee80211_crypt_ccmp.o +obj-$(CONFIG_IEEE80211_WPA) += ieee80211_crypt_tkip.o +ieee80211-objs := \ + ieee80211_module.o \ + ieee80211_tx.o \ + ieee80211_rx.o \ + ieee80211_wx.o diff --git a/drivers/net/wireless/ieee80211/ieee80211.h b/drivers/net/wireless/ieee80211/ieee80211.h new file mode 100644 index 000000000..73104e8ae --- /dev/null +++ b/drivers/net/wireless/ieee80211/ieee80211.h @@ -0,0 +1,847 @@ +/* + * Merged with mainline ieee80211.h in Aug 2004. Original ieee802_11 + * remains copyright by the original authors + * + * Portions of the merged code are 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 + * + * Copyright (c) 2002-2003, Jouni Malinen + * + * Adaption to a generic IEEE 802.11 stack by James Ketrenos + * + * 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. + */ +#ifndef IEEE80211_H +#define IEEE80211_H +#include /* ETH_ALEN */ + +#define IEEE80211_DATA_LEN 2304 +/* Maximum size for the MA-UNITDATA primitive, 802.11 standard section + 6.2.1.1.2. + + The figure in section 7.1.2 suggests a body size of up to 2312 + bytes is allowed, which is a bit confusing, I suspect this + represents the 2304 bytes of real data, plus a possible 8 bytes of + WEP IV and ICV. (this interpretation suggested by Ramiro Barreiro) */ + + +#define IEEE80211_HLEN 30 +#define IEEE80211_FRAME_LEN (IEEE80211_DATA_LEN + IEEE80211_HLEN) + +struct ieee80211_hdr { + u16 frame_ctl; + u16 duration_id; + u8 addr1[ETH_ALEN]; + u8 addr2[ETH_ALEN]; + u8 addr3[ETH_ALEN]; + u16 seq_ctl; + u8 addr4[ETH_ALEN]; +} __attribute__ ((packed)); + +#define IEEE80211_3ADDR_SIZE (24) +#define IEEE80211_4ADDR_SIZE (30) + +#define MIN_FRAG_THRESHOLD 256U +#define MAX_FRAG_THRESHOLD 2342U + +/* Frame control field constants */ +#define IEEE80211_FCTL_VERS 0x0002 +#define IEEE80211_FCTL_FTYPE 0x000c +#define IEEE80211_FCTL_STYPE 0x00f0 +#define IEEE80211_FCTL_TODS 0x0100 +#define IEEE80211_FCTL_FROMDS 0x0200 +#define IEEE80211_FCTL_MOREFRAGS 0x0400 +#define IEEE80211_FCTL_RETRY 0x0800 +#define IEEE80211_FCTL_PM 0x1000 +#define IEEE80211_FCTL_MOREDATA 0x2000 +#define IEEE80211_FCTL_WEP 0x4000 +#define IEEE80211_FCTL_ORDER 0x8000 + +#define IEEE80211_FTYPE_MGMT 0x0000 +#define IEEE80211_FTYPE_CTL 0x0004 +#define IEEE80211_FTYPE_DATA 0x0008 + +/* management */ +#define IEEE80211_STYPE_ASSOC_REQ 0x0000 +#define IEEE80211_STYPE_ASSOC_RESP 0x0010 +#define IEEE80211_STYPE_REASSOC_REQ 0x0020 +#define IEEE80211_STYPE_REASSOC_RESP 0x0030 +#define IEEE80211_STYPE_PROBE_REQ 0x0040 +#define IEEE80211_STYPE_PROBE_RESP 0x0050 +#define IEEE80211_STYPE_BEACON 0x0080 +#define IEEE80211_STYPE_ATIM 0x0090 +#define IEEE80211_STYPE_DISASSOC 0x00A0 +#define IEEE80211_STYPE_AUTH 0x00B0 +#define IEEE80211_STYPE_DEAUTH 0x00C0 + +/* control */ +#define IEEE80211_STYPE_PSPOLL 0x00A0 +#define IEEE80211_STYPE_RTS 0x00B0 +#define IEEE80211_STYPE_CTS 0x00C0 +#define IEEE80211_STYPE_ACK 0x00D0 +#define IEEE80211_STYPE_CFEND 0x00E0 +#define IEEE80211_STYPE_CFENDACK 0x00F0 + +/* data */ +#define IEEE80211_STYPE_DATA 0x0000 +#define IEEE80211_STYPE_DATA_CFACK 0x0010 +#define IEEE80211_STYPE_DATA_CFPOLL 0x0020 +#define IEEE80211_STYPE_DATA_CFACKPOLL 0x0030 +#define IEEE80211_STYPE_NULLFUNC 0x0040 +#define IEEE80211_STYPE_CFACK 0x0050 +#define IEEE80211_STYPE_CFPOLL 0x0060 +#define IEEE80211_STYPE_CFACKPOLL 0x0070 + +#define IEEE80211_SCTL_FRAG 0x000F +#define IEEE80211_SCTL_SEQ 0xFFF0 + + +/* debug macros */ + +#ifdef CONFIG_IEEE80211_DEBUG +extern u32 ieee80211_debug_level; +#define IEEE80211_DEBUG(level, fmt, args...) \ +do { if (ieee80211_debug_level & (level)) \ + printk(KERN_DEBUG "ieee80211: %c %s " fmt, \ + in_interrupt() ? 'I' : 'U', __FUNCTION__, ## args); } while (0) +#else +#define IEEE80211_DEBUG(level, fmt, args...) do {} while (0); +#endif /* CONFIG_IEEE80211_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 IEEE80211_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 IEEE80211_xxxx_DEBUG() macro definition for your + * classification, or use IEEE80211_DEBUG(IEEE80211_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_IEEE80211_DEBUG defined in your kernel configuration + * + */ + +#define IEEE80211_DL_INFO BIT(0) +#define IEEE80211_DL_WX BIT(1) +#define IEEE80211_DL_SCAN BIT(2) +#define IEEE80211_DL_STATE BIT(3) +#define IEEE80211_DL_MGMT BIT(4) +#define IEEE80211_DL_FRAG BIT(5) +#define IEEE80211_DL_EAP BIT(6) + +#define IEEE80211_ERROR(f, a...) printk(KERN_ERR "ieee80211: " f, ## a) +#define IEEE80211_WARNING(f, a...) printk(KERN_WARNING "ieee80211: " f, ## a) +#define IEEE80211_DEBUG_INFO(f, a...) IEEE80211_DEBUG(IEEE80211_DL_INFO, f, ## a) + +#define IEEE80211_DEBUG_WX(f, a...) IEEE80211_DEBUG(IEEE80211_DL_WX, f, ## a) +#define IEEE80211_DEBUG_SCAN(f, a...) IEEE80211_DEBUG(IEEE80211_DL_SCAN, f, ## a) +#define IEEE80211_DEBUG_STATE(f, a...) IEEE80211_DEBUG(IEEE80211_DL_STATE, f, ## a) +#define IEEE80211_DEBUG_MGMT(f, a...) IEEE80211_DEBUG(IEEE80211_DL_MGMT, f, ## a) +#define IEEE80211_DEBUG_FRAG(f, a...) IEEE80211_DEBUG(IEEE80211_DL_FRAG, f, ## a) +#define IEEE80211_DEBUG_EAP(f, a...) IEEE80211_DEBUG(IEEE80211_DL_EAP, f, ## a) +#include +#include +#include /* ARPHRD_ETHER */ + +#ifndef WIRELESS_SPY +#define WIRELESS_SPY // enable iwspy support +#endif +#include // new driver API + +#define BIT(x) (1 << (x)) + +#ifndef ETH_P_PAE +#define ETH_P_PAE 0x888E /* Port Access Entity (IEEE 802.1X) */ +#endif /* ETH_P_PAE */ + +#define ETH_P_PREAUTH 0x88C7 /* IEEE 802.11i pre-authentication */ + +#ifndef ETH_P_80211_RAW +#define ETH_P_80211_RAW (ETH_P_ECONET + 1) +#endif + +/* IEEE 802.11 defines */ + +#define P80211_OUI_LEN 3 + +struct ieee80211_snap_hdr { + + u8 dsap; /* always 0xAA */ + u8 ssap; /* always 0xAA */ + u8 ctrl; /* always 0x03 */ + u8 oui[P80211_OUI_LEN]; /* organizational universal id */ + +} __attribute__ ((packed)); + +#define SNAP_SIZE sizeof(struct ieee80211_snap_hdr) + +#define WLAN_FC_GET_TYPE(fc) ((fc) & IEEE80211_FCTL_FTYPE) +#define WLAN_FC_GET_STYPE(fc) ((fc) & IEEE80211_FCTL_STYPE) + +#define WLAN_GET_SEQ_FRAG(seq) ((seq) & IEEE80211_SCTL_FRAG) +#define WLAN_GET_SEQ_SEQ(seq) ((seq) & IEEE80211_SCTL_SEQ) + +/* Authentication algorithms */ +#define WLAN_AUTH_OPEN 0 +#define WLAN_AUTH_SHARED_KEY 1 + +#define WLAN_AUTH_CHALLENGE_LEN 128 + +#define WLAN_CAPABILITY_BSS BIT(0) +#define WLAN_CAPABILITY_IBSS BIT(1) +#define WLAN_CAPABILITY_CF_POLLABLE BIT(2) +#define WLAN_CAPABILITY_CF_POLL_REQUEST BIT(3) +#define WLAN_CAPABILITY_PRIVACY BIT(4) +#define WLAN_CAPABILITY_SHORT_PREAMBLE BIT(5) +#define WLAN_CAPABILITY_PBCC BIT(6) +#define WLAN_CAPABILITY_CHANNEL_AGILITY BIT(7) + +/* Status codes */ +#define WLAN_STATUS_SUCCESS 0 +#define WLAN_STATUS_UNSPECIFIED_FAILURE 1 +#define WLAN_STATUS_CAPS_UNSUPPORTED 10 +#define WLAN_STATUS_REASSOC_NO_ASSOC 11 +#define WLAN_STATUS_ASSOC_DENIED_UNSPEC 12 +#define WLAN_STATUS_NOT_SUPPORTED_AUTH_ALG 13 +#define WLAN_STATUS_UNKNOWN_AUTH_TRANSACTION 14 +#define WLAN_STATUS_CHALLENGE_FAIL 15 +#define WLAN_STATUS_AUTH_TIMEOUT 16 +#define WLAN_STATUS_AP_UNABLE_TO_HANDLE_NEW_STA 17 +#define WLAN_STATUS_ASSOC_DENIED_RATES 18 +/* 802.11b */ +#define WLAN_STATUS_ASSOC_DENIED_NOSHORT 19 +#define WLAN_STATUS_ASSOC_DENIED_NOPBCC 20 +#define WLAN_STATUS_ASSOC_DENIED_NOAGILITY 21 + +/* Reason codes */ +#define WLAN_REASON_UNSPECIFIED 1 +#define WLAN_REASON_PREV_AUTH_NOT_VALID 2 +#define WLAN_REASON_DEAUTH_LEAVING 3 +#define WLAN_REASON_DISASSOC_DUE_TO_INACTIVITY 4 +#define WLAN_REASON_DISASSOC_AP_BUSY 5 +#define WLAN_REASON_CLASS2_FRAME_FROM_NONAUTH_STA 6 +#define WLAN_REASON_CLASS3_FRAME_FROM_NONASSOC_STA 7 +#define WLAN_REASON_DISASSOC_STA_HAS_LEFT 8 +#define WLAN_REASON_STA_REQ_ASSOC_WITHOUT_AUTH 9 + + +/* 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 + +#define IEEE80211_MGMT_HDR_LEN 24 +#define IEEE80211_DATA_HDR3_LEN 24 +#define IEEE80211_DATA_HDR4_LEN 30 + + +#define IEEE80211_STATMASK_SIGNAL BIT(0) +#define IEEE80211_STATMASK_RSSI BIT(1) +#define IEEE80211_STATMASK_NOISE BIT(2) +#define IEEE80211_STATMASK_RATE BIT(3) +#define IEEE80211_STATMASK_WEMASK 0x7 + + +#define IEEE80211_CCK_MODULATION BIT(0) +#define IEEE80211_OFDM_MODULATION BIT(1) + +#define IEEE80211_24GHZ_BAND BIT(0) +#define IEEE80211_52GHZ_BAND BIT(1) + +#define IEEE80211_CCK_RATE_1MB 0x02 +#define IEEE80211_CCK_RATE_2MB 0x04 +#define IEEE80211_CCK_RATE_5MB 0x0B +#define IEEE80211_CCK_RATE_11MB 0x16 +#define IEEE80211_OFDM_RATE_6MB 0x0C +#define IEEE80211_OFDM_RATE_9MB 0x12 +#define IEEE80211_OFDM_RATE_12MB 0x18 +#define IEEE80211_OFDM_RATE_18MB 0x24 +#define IEEE80211_OFDM_RATE_24MB 0x30 +#define IEEE80211_OFDM_RATE_36MB 0x48 +#define IEEE80211_OFDM_RATE_48MB 0x60 +#define IEEE80211_OFDM_RATE_54MB 0x6C +#define IEEE80211_BASIC_RATE_MASK 0x80 + +#define IEEE80211_CCK_RATE_1MB_MASK BIT(0) +#define IEEE80211_CCK_RATE_2MB_MASK BIT(1) +#define IEEE80211_CCK_RATE_5MB_MASK BIT(2) +#define IEEE80211_CCK_RATE_11MB_MASK BIT(3) +#define IEEE80211_OFDM_RATE_6MB_MASK BIT(4) +#define IEEE80211_OFDM_RATE_9MB_MASK BIT(5) +#define IEEE80211_OFDM_RATE_12MB_MASK BIT(6) +#define IEEE80211_OFDM_RATE_18MB_MASK BIT(7) +#define IEEE80211_OFDM_RATE_24MB_MASK BIT(8) +#define IEEE80211_OFDM_RATE_36MB_MASK BIT(9) +#define IEEE80211_OFDM_RATE_48MB_MASK BIT(10) +#define IEEE80211_OFDM_RATE_54MB_MASK BIT(11) + +#define IEEE80211_CCK_RATES_MASK 0x0000000F +#define IEEE80211_CCK_BASIC_RATES_MASK (IEEE80211_CCK_RATE_1MB_MASK | \ + IEEE80211_CCK_RATE_2MB_MASK) +#define IEEE80211_CCK_DEFAULT_RATES_MASK (IEEE80211_CCK_BASIC_RATES_MASK | \ + IEEE80211_CCK_RATE_5MB_MASK | \ + IEEE80211_CCK_RATE_11MB_MASK) + +#define IEEE80211_OFDM_RATES_MASK 0x00000FF0 +#define IEEE80211_OFDM_BASIC_RATES_MASK (IEEE80211_OFDM_RATE_6MB_MASK | \ + IEEE80211_OFDM_RATE_12MB_MASK | \ + IEEE80211_OFDM_RATE_24MB_MASK) +#define IEEE80211_OFDM_DEFAULT_RATES_MASK (IEEE80211_OFDM_BASIC_RATES_MASK | \ + IEEE80211_OFDM_RATE_9MB_MASK | \ + IEEE80211_OFDM_RATE_18MB_MASK | \ + IEEE80211_OFDM_RATE_36MB_MASK | \ + IEEE80211_OFDM_RATE_48MB_MASK | \ + IEEE80211_OFDM_RATE_54MB_MASK) +#define IEEE80211_DEFAULT_RATES_MASK (IEEE80211_OFDM_DEFAULT_RATES_MASK | \ + IEEE80211_CCK_DEFAULT_RATES_MASK) + +#define IEEE80211_NUM_OFDM_RATES 8 +#define IEEE80211_NUM_CCK_RATES 4 +#define IEEE80211_OFDM_SHIFT_MASK_A 4 + + + + +/* NOTE: This data is for statistical purposes; not all hardware provides this + * information for frames received. Not setting these will not cause + * any adverse affects. */ +struct ieee80211_rx_stats { + u32 mac_time; + u8 rssi; + u8 signal; + u8 noise; + u16 rate; /* in 100 kbps */ + u8 received_channel; + u8 control; + u8 mask; + u8 freq; + u16 len; +}; + +/* IEEE 802.11 requires that STA supports concurrent reception of at least + * three fragmented frames. This define can be increased to support more + * concurrent frames, but it should be noted that each entry can consume about + * 2 kB of RAM and increasing cache size will slow down frame reassembly. */ +#define IEEE80211_FRAG_CACHE_LEN 4 + +struct ieee80211_frag_entry { + unsigned long first_frag_time; + unsigned int seq; + unsigned int last_frag; + struct sk_buff *skb; + u8 src_addr[ETH_ALEN]; + u8 dst_addr[ETH_ALEN]; +}; + +struct ieee80211_stats { + unsigned int tx_unicast_frames; + unsigned int tx_multicast_frames; + unsigned int tx_fragments; + unsigned int tx_unicast_octets; + unsigned int tx_multicast_octets; + unsigned int tx_deferred_transmissions; + unsigned int tx_single_retry_frames; + unsigned int tx_multiple_retry_frames; + unsigned int tx_retry_limit_exceeded; + unsigned int tx_discards; + unsigned int rx_unicast_frames; + unsigned int rx_multicast_frames; + unsigned int rx_fragments; + unsigned int rx_unicast_octets; + unsigned int rx_multicast_octets; + unsigned int rx_fcs_errors; + unsigned int rx_discards_no_buffer; + unsigned int tx_discards_wrong_sa; + unsigned int rx_discards_wep_undecryptable; + unsigned int rx_message_in_msg_fragments; + unsigned int rx_message_in_bad_msg_fragments; +}; + +struct ieee80211_device; + +#include "ieee80211_crypt.h" + +#define SEC_KEY_1 BIT(0) +#define SEC_KEY_2 BIT(1) +#define SEC_KEY_3 BIT(2) +#define SEC_KEY_4 BIT(3) +#define SEC_ACTIVE_KEY BIT(4) +#define SEC_AUTH_MODE BIT(5) +#define SEC_UNICAST_GROUP BIT(6) +#define SEC_LEVEL BIT(7) +#define SEC_ENABLED BIT(8) + +#define SEC_LEVEL_0 0 /* None */ +#define SEC_LEVEL_1 1 /* WEP 40 and 104 bit */ +#define SEC_LEVEL_2 2 /* Level 1 + TKIP */ +#define SEC_LEVEL_2_CKIP 3 /* Level 1 + CKIP */ +#define SEC_LEVEL_3 4 /* Level 2 + CCMP */ + +#define WEP_KEYS 4 +#define WEP_KEY_LEN 13 + +struct ieee80211_security { + u16 active_key:1, + enabled:1, + auth_mode:2, + auth_algo:4, + unicast_uses_group:1; + u8 key_sizes[WEP_KEYS]; + u8 keys[WEP_KEYS][WEP_KEY_LEN]; + u8 level; + u16 flags; +} __attribute__ ((packed)); + + +/* + + 802.11 data frame from AP + + ,-------------------------------------------------------------------. +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-2340 bytes + +*/ + +struct ieee80211_header_data { + u16 frame_ctl; + u16 duration_id; + u8 addr1[6]; + u8 addr2[6]; + u8 addr3[6]; + u16 seq_ctrl; +}; + +#define BEACON_PROBE_SSID_ID_POSITION 12 + +/* Management Frame Information Element Types */ +#define MFIE_TYPE_SSID 0 +#define MFIE_TYPE_RATES 1 +#define MFIE_TYPE_FH_SET 2 +#define MFIE_TYPE_DS_SET 3 +#define MFIE_TYPE_CF_SET 4 +#define MFIE_TYPE_TIM 5 +#define MFIE_TYPE_IBSS_SET 6 +#define MFIE_TYPE_CHALLENGE 16 +#define MFIE_TYPE_RSN 48 +#define MFIE_TYPE_RATES_EX 50 +#define MFIE_TYPE_GENERIC 221 + +struct ieee80211_info_element_hdr { + u8 id; + u8 len; +} __attribute__ ((packed)); + +struct ieee80211_info_element { + u8 id; + u8 len; + u8 data[0]; +} __attribute__ ((packed)); + +/* + * These are the data types that can make up management packets + * + u16 auth_algorithm; + u16 auth_sequence; + u16 beacon_interval; + u16 capability; + u8 current_ap[ETH_ALEN]; + u16 listen_interval; + struct { + u16 association_id:14, reserved:2; + } __attribute__ ((packed)); + u32 time_stamp[2]; + u16 reason; + u16 status; +*/ + +struct ieee80211_authentication { + struct ieee80211_header_data header; + u16 algorithm; + u16 transaction; + u16 status; + struct ieee80211_info_element info_element; +} __attribute__ ((packed)); + + +struct ieee80211_probe_response { + struct ieee80211_header_data header; + u32 time_stamp[2]; + u16 beacon_interval; + u16 capability; + struct ieee80211_info_element info_element; +} __attribute__ ((packed)); + +struct ieee80211_assoc_request_frame { + u16 capability; + u16 listen_interval; + u8 current_ap[ETH_ALEN]; + struct ieee80211_info_element info_element; +} __attribute__ ((packed)); + +struct ieee80211_helper_functions { + void (*set_security)(struct ieee80211_device *ieee, + struct ieee80211_security *sec); + + /* these functions are defined in hardware model specific files + * (hostap_{cs,plx,pci}.c */ + int (*card_present)(struct ieee80211_device *ieee); + void (*cor_sreset)(struct ieee80211_device *ieee); + int (*dev_open)(struct ieee80211_device *ieee); + int (*dev_close)(struct ieee80211_device *ieee); + void (*genesis_reset)(struct ieee80211_device *ieee, int hcr); + + + /* Turn on unencrypted packet filtering at the HW level */ + void (*set_unencrypted_filter)(struct ieee80211_device *ieee, int flag); + + /* the following functions are from hostap_hw.c, but they may have some + * hardware model specific code */ + + int (*hw_enable)(struct net_device *dev, int initial); + int (*hw_config)(struct net_device *dev, int initial); + void (*hw_reset)(struct net_device *dev); + void (*hw_shutdown)(struct net_device *dev, int no_disable); + int (*reset_port)(struct net_device *dev); + int (*tx)(struct sk_buff *skb, struct net_device *dev); + void (*schedule_reset)(struct ieee80211_device *ieee); + int (*tx_80211)(struct sk_buff *skb, struct net_device *dev); +}; + + + +/* SWEEP TABLE ENTRIES NUMBER*/ +#define MAX_SWEEP_TAB_ENTRIES 42 +#define MAX_SWEEP_TAB_ENTRIES_PER_PACKET 7 +/* MAX_RATES_LENGTH needs to be 12. The spec says 8, and many APs + * only use 8, and then use extended rates for the remaining supported + * rates. Other APs, however, stick all of their supported rates on the + * main rates information element... */ +#define MAX_RATES_LENGTH ((u8)12) +#define MAX_RATES_EX_LENGTH ((u8)16) +#define MAX_NETWORK_COUNT 128 + +#define CRC_LENGTH 4U + +#define MAX_WPA_IE_LEN 64 + +#define NETWORK_EMPTY_ESSID BIT(0) +#define NETWORK_HAS_OFDM BIT(1) + +struct ieee80211_network { + u8 bssid[ETH_ALEN]; + u8 ssid_len; + struct ieee80211_rx_stats stats; + u16 capability; + u8 channel; + u8 rates[MAX_RATES_LENGTH]; + u8 rates_len; + u8 rates_ex[MAX_RATES_EX_LENGTH]; + u8 rates_ex_len; + unsigned long last_scanned; + u8 mode; + u8 flags; + u32 last_associate; + /* Ensure null-terminated for any debug msgs */ + u8 ssid[IW_ESSID_MAX_SIZE + 1]; + u32 time_stamp[2]; + u16 beacon_interval; + u16 listen_interval; + u16 atim_window; +#ifdef CONFIG_IEEE80211_WPA + u8 wpa_ie[MAX_WPA_IE_LEN]; + size_t wpa_ie_len; + u8 rsn_ie[MAX_WPA_IE_LEN]; + size_t rsn_ie_len; +#endif /* CONFIG_IEEE80211_WPA */ + struct list_head list; +}; + +enum ieee80211_state { + IEEE80211_UNINITIALIZED = 0, + IEEE80211_INITIALIZED, + IEEE80211_ASSOCIATING, + IEEE80211_ASSOCIATED, + IEEE80211_AUTHENTICATING, + IEEE80211_AUTHENTICATED, + IEEE80211_SHUTDOWN +}; + +#define DEFAULT_MAX_SCAN_AGE (15 * HZ) +#define DEFAULT_FTS 2342 +#define MAC_FMT "%02x:%02x:%02x:%02x:%02x:%02x" +#define MAC_ARG(x) ((u8*)(x))[0],((u8*)(x))[1],((u8*)(x))[2],((u8*)(x))[3],((u8*)(x))[4],((u8*)(x))[5] + + +extern inline int is_multicast_ether_addr(const u8 *addr) +{ + return ((addr[0] != 0xff) && (0x01 & addr[0])); +} + +extern inline int is_broadcast_ether_addr(const u8 *addr) +{ + return ((addr[0] == 0xff) && (addr[1] == 0xff) && (addr[2] == 0xff) && \ + (addr[3] == 0xff) && (addr[4] == 0xff) && (addr[5] == 0xff)); +} + + +struct ieee80211_device { + struct net_device *dev; + + /* Bookkeeping structures */ + struct net_device_stats stats; + struct ieee80211_stats ieee_stats; + void *priv; + + /* Probe / Beacon management */ + struct list_head network_free_list; + struct list_head network_list; + struct ieee80211_network *networks; + int scans; + int scan_age; + + int iw_mode; /* operating mode (IW_MODE_*) */ + + spinlock_t lock; + + int tx_payload_only; /* set to 1 if HW only expects the Tx SKB + * to contain the payload (vs. a fully configured + * 802.11 header for a data frame) */ + int tx_headroom; /* Set to size of any additional room needed at front + * of allocated Tx SKBs */ + + /* WEP and other encryption related settings at the device level */ + int open_wep; /* Set to 1 to allow unencrypted frames */ + + int reset_on_keychange; /* Set to 1 if the HW needs to be reset on + * WEP key changes */ + + /* If the host performs {en,de}cryption, then set to 1 */ + int host_encrypt; + int host_decrypt; + +#ifdef CONFIG_IEEE80211_WPA + /* WPA data */ + int wpa_enabled; + int drop_unencrypted; + int tkip_countermeasures; + int ieee_802_1x; /* is IEEE 802.1X used */ + int privacy_invoked; + size_t wpa_ie_len; + u8 *wpa_ie; +#endif /* CONFIG_IEEE80211_WPA */ + + 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 bcrx_sta_key; /* use individual keys to override default keys even + * with RX of broad/multicast frames */ + + /* Fragmentation structures */ + struct ieee80211_frag_entry frag_cache[IEEE80211_FRAG_CACHE_LEN]; + unsigned int frag_next_idx; + u16 fts; /* Fragmentation Threshold */ + + /* Association info */ + u8 bssid[ETH_ALEN]; + + /* Callback vtable */ + struct ieee80211_helper_functions *func; + + enum ieee80211_state state; + + int mode; /* A, B, G */ + int modulation; /* CCK, OFDM */ + int freq_band; /* 2.4Ghz, 5.2Ghz, Mixed */ + int abg_ture; /* ABG flag */ +}; + +#define IEEE_A 0 +#define IEEE_B 1 +#define IEEE_G 2 +#define IEEE_MASK (BIT(IEEE_A) | BIT(IEEE_B) | BIT(IEEE_G)) + +extern 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; +} + + +extern inline int ieee80211_is_valid_mode(struct ieee80211_device *ieee, int mode) +{ + int rc = 1; + switch(mode) { + case IEEE_A: + if (!(ieee->modulation & IEEE80211_OFDM_MODULATION) || + !(ieee->freq_band & IEEE80211_52GHZ_BAND)) + rc = 0; + break; + + case IEEE_B: + if (!(ieee->modulation & IEEE80211_CCK_MODULATION) || + !(ieee->freq_band & IEEE80211_24GHZ_BAND)) + rc = 0; + break; + + case IEEE_G: + if (!(ieee->modulation & IEEE80211_OFDM_MODULATION) || + !(ieee->freq_band & IEEE80211_24GHZ_BAND)) + rc = 0; + break; + } + return rc; +} + +extern inline int ieee80211_get_hdrlen(u16 fc) +{ + int hdrlen = 24; + + switch (WLAN_FC_GET_TYPE(fc)) { + case IEEE80211_FTYPE_DATA: + if ((fc & IEEE80211_FCTL_FROMDS) && (fc & IEEE80211_FCTL_TODS)) + hdrlen = 30; /* Addr4 */ + break; + case IEEE80211_FTYPE_CTL: + switch (WLAN_FC_GET_STYPE(fc)) { + case IEEE80211_STYPE_CTS: + case IEEE80211_STYPE_ACK: + hdrlen = 10; + break; + default: + hdrlen = 16; + break; + } + break; + } + + return hdrlen; +} + + + +/* ieee80211.c */ +extern struct ieee80211_device * ieee80211_alloc(struct net_device *dev, + void *priv); +extern void ieee80211_free(struct ieee80211_device *ieee); + +extern int ieee80211_set_encryption(struct ieee80211_device *ieee); + +/* ieee80211_tx.c */ + +struct ieee80211_txb { + u8 nr_frags; + u8 encrypted; + u16 reserved; + u16 frag_size; + u16 payload_size; + struct sk_buff *fragments[0]; +}; + +extern struct ieee80211_txb *ieee80211_skb_to_txb(struct ieee80211_device *ieee, + struct sk_buff *skb); +extern void ieee80211_txb_free(struct ieee80211_txb *); + + +/* ieee80211_rx.c */ +extern int ieee80211_rx(struct ieee80211_device *ieee, struct sk_buff *skb, + struct ieee80211_rx_stats *rx_stats); +extern void ieee80211_rx_mgt(struct ieee80211_device *ieee, + struct ieee80211_hdr *header, + struct ieee80211_rx_stats *stats); + +/* iee80211_wx.c */ +extern int ieee80211_wx_get_scan(struct ieee80211_device *ieee, + struct iw_request_info *info, + union iwreq_data *wrqu, char *key); +extern int ieee80211_wx_set_encode(struct ieee80211_device *ieee, + struct iw_request_info *info, + union iwreq_data *wrqu, char *key); +extern int ieee80211_wx_get_encode(struct ieee80211_device *ieee, + struct iw_request_info *info, + union iwreq_data *wrqu, char *key); + + +extern inline void ieee80211_increment_scans(struct ieee80211_device *ieee) +{ + ieee->scans++; +} + +extern inline int ieee80211_get_scans(struct ieee80211_device *ieee) +{ + return ieee->scans; +} + +static inline 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, "", sizeof("")); + 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; +} + +#ifndef offset_in_page +#define offset_in_page(p) ((unsigned long)(p) & ~PAGE_MASK) +#endif + +#endif /* IEEE80211_H */ diff --git a/drivers/net/wireless/ieee80211/ieee80211_crypt.c b/drivers/net/wireless/ieee80211/ieee80211_crypt.c new file mode 100644 index 000000000..c64c6c5e3 --- /dev/null +++ b/drivers/net/wireless/ieee80211/ieee80211_crypt.c @@ -0,0 +1,253 @@ +/* + * Host AP crypto routines + * + * Copyright (c) 2002-2003, Jouni Malinen + * Portions 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. + * + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "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; +}; + + +struct ieee80211_crypto { + struct list_head algs; + spinlock_t lock; +}; + +static struct ieee80211_crypto *hcrypt; + +void ieee80211_crypt_deinit_entries(struct ieee80211_device *ieee, + int force) +{ + struct list_head *ptr, *n; + struct ieee80211_crypt_data *entry; + + for (ptr = ieee->crypt_deinit_list.next, n = ptr->next; + ptr != &ieee->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); + module_put(entry->ops->owner); + } + kfree(entry); + } +} + +void ieee80211_crypt_deinit_handler(unsigned long data) +{ + struct ieee80211_device *ieee = (struct ieee80211_device *)data; + unsigned long flags; + + spin_lock_irqsave(&ieee->lock, flags); + ieee80211_crypt_deinit_entries(ieee, 0); + if (!list_empty(&ieee->crypt_deinit_list)) { + 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); + 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; + + if (hcrypt == NULL) + return -1; + + alg = kmalloc(sizeof(*alg), GFP_KERNEL); + if (alg == NULL) + return -ENOMEM; + + memset(alg, 0, sizeof(*alg)); + alg->ops = ops; + + spin_lock_irqsave(&hcrypt->lock, flags); + list_add(&alg->list, &hcrypt->algs); + spin_unlock_irqrestore(&hcrypt->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) +{ + unsigned long flags; + struct list_head *ptr; + struct ieee80211_crypto_alg *del_alg = NULL; + + if (hcrypt == NULL) + return -1; + + spin_lock_irqsave(&hcrypt->lock, flags); + for (ptr = hcrypt->algs.next; ptr != &hcrypt->algs; ptr = ptr->next) { + struct ieee80211_crypto_alg *alg = + (struct ieee80211_crypto_alg *) ptr; + if (alg->ops == ops) { + list_del(&alg->list); + del_alg = alg; + break; + } + } + spin_unlock_irqrestore(&hcrypt->lock, flags); + + if (del_alg) { + printk(KERN_DEBUG "ieee80211_crypt: unregistered algorithm " + "'%s'\n", ops->name); + kfree(del_alg); + } + + return del_alg ? 0 : -1; +} + + +struct ieee80211_crypto_ops * ieee80211_get_crypto_ops(const char *name) +{ + unsigned long flags; + struct list_head *ptr; + struct ieee80211_crypto_alg *found_alg = NULL; + + if (hcrypt == NULL) + return NULL; + + spin_lock_irqsave(&hcrypt->lock, flags); + for (ptr = hcrypt->algs.next; ptr != &hcrypt->algs; ptr = ptr->next) { + struct ieee80211_crypto_alg *alg = + (struct ieee80211_crypto_alg *) ptr; + if (strcmp(alg->ops->name, name) == 0) { + found_alg = alg; + break; + } + } + spin_unlock_irqrestore(&hcrypt->lock, flags); + + if (found_alg) + return found_alg->ops; + else + return NULL; +} + + +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, + .encrypt_mpdu = NULL, + .decrypt_mpdu = NULL, + .encrypt_msdu = NULL, + .decrypt_msdu = NULL, + .set_key = NULL, + .get_key = NULL, + .extra_prefix_len = 0, + .extra_postfix_len = 0, + .owner = THIS_MODULE, +}; + + +static int __init ieee80211_crypto_init(void) +{ + hcrypt = kmalloc(sizeof(*hcrypt), GFP_KERNEL); + if (hcrypt == NULL) + return -ENOMEM; + + memset(hcrypt, 0, sizeof(*hcrypt)); + INIT_LIST_HEAD(&hcrypt->algs); + spin_lock_init(&hcrypt->lock); + + (void) ieee80211_register_crypto_ops(&ieee80211_crypt_null); + + return 0; +} + + +static void __exit ieee80211_crypto_deinit(void) +{ + struct list_head *ptr, *n; + + if (hcrypt == NULL) + return; + + for (ptr = hcrypt->algs.next, n = ptr->next; ptr != &hcrypt->algs; + ptr = n, n = ptr->next) { + struct ieee80211_crypto_alg *alg = + (struct ieee80211_crypto_alg *) ptr; + list_del(ptr); + printk(KERN_DEBUG "ieee80211_crypt: unregistered algorithm " + "'%s' (deinit)\n", alg->ops->name); + kfree(alg); + } + + kfree(hcrypt); +} + +EXPORT_SYMBOL(ieee80211_crypt_deinit_entries); +EXPORT_SYMBOL(ieee80211_crypt_deinit_handler); +EXPORT_SYMBOL(ieee80211_crypt_delayed_deinit); + +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/drivers/net/wireless/ieee80211/ieee80211_crypt.h b/drivers/net/wireless/ieee80211/ieee80211_crypt.h new file mode 100644 index 000000000..3cb675fb3 --- /dev/null +++ b/drivers/net/wireless/ieee80211/ieee80211_crypt.h @@ -0,0 +1,104 @@ +/* + * 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 + * + * Copyright (c) 2002-2003, Jouni Malinen + * + * Adaption to a generic IEEE 802.11 stack by James Ketrenos + * + * + * 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 + +#ifdef CONFIG_IEEE80211_CRYPT_MODULE +#ifndef CONFIG_IEEE80211_CRYPT +#define CONFIG_IEEE80211_CRYPT +#endif +#endif + +#ifdef CONFIG_IEEE80211_CRYPT +#include + +struct ieee80211_crypto_ops { + const char *name; + + /* 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); + + /* 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); + + /* 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_prefix_len, extra_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; +}; + +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); + +#else + +struct ieee80211_crypt_data { + struct list_head list; /* delayed deletion list */ + void *ops; + void *priv; + atomic_t refcnt; +}; + +#endif /* CONFIG_IEEE80211_NOWEP */ + +#endif diff --git a/drivers/net/wireless/ieee80211/ieee80211_crypt_ccmp.c b/drivers/net/wireless/ieee80211/ieee80211_crypt_ccmp.c new file mode 100644 index 000000000..02c999776 --- /dev/null +++ b/drivers/net/wireless/ieee80211/ieee80211_crypt_ccmp.c @@ -0,0 +1,477 @@ +/* + * Host AP crypt: host-based CCMP encryption implementation for Host AP driver + * + * Copyright (c) 2003-2004, Jouni Malinen + * + * This program is free software; you can 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "ieee80211.h" + +#ifndef CONFIG_CRYPTO +#error CONFIG_CRYPTO is required to build this module. +#endif + +#include +#include + +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_tfm *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]; +}; + +void ieee80211_ccmp_aes_encrypt(struct crypto_tfm *tfm, + const u8 pt[16], u8 ct[16]) +{ + struct scatterlist src, dst; + + src.page = virt_to_page(pt); + src.offset = offset_in_page(pt); + src.length = AES_BLOCK_LEN; + + dst.page = virt_to_page(ct); + dst.offset = offset_in_page(ct); + dst.length = AES_BLOCK_LEN; + + crypto_cipher_encrypt(tfm, &dst, &src, AES_BLOCK_LEN); +} + +static void * ieee80211_ccmp_init(int key_idx) +{ + struct ieee80211_ccmp_data *priv; + + priv = kmalloc(sizeof(*priv), GFP_ATOMIC); + if (priv == NULL) { + goto fail; + } + memset(priv, 0, sizeof(*priv)); + priv->key_idx = key_idx; + + priv->tfm = crypto_alloc_tfm("aes", 0); + if (priv->tfm == NULL) { + printk(KERN_DEBUG "ieee80211_crypt_ccmp: could not allocate " + "crypto API aes\n"); + goto fail; + } + + return priv; + +fail: + if (priv) { + if (priv->tfm) + crypto_free_tfm(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_tfm(_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_tfm *tfm, + struct ieee80211_hdr *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) & 0x08)); + 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_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 *hdr; + u8 *b0 = key->tx_b0; + u8 *b = key->tx_b; + u8 *e = key->tx_e; + u8 *s0 = key->tx_s0; + + if (skb_headroom(skb) < CCMP_HDR_LEN || + skb_tailroom(skb) < CCMP_MIC_LEN || + skb->len < hdr_len) + return -1; + + data_len = skb->len - hdr_len; + pos = skb_push(skb, CCMP_HDR_LEN); + memmove(pos, pos + CCMP_HDR_LEN, hdr_len); + pos += hdr_len; + mic = skb_put(skb, CCMP_MIC_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]; + + hdr = (struct ieee80211_hdr *) skb->data; + ccmp_init_blocks(key->tfm, hdr, key->tx_pn, data_len, b0, b, s0); + + blocks = (data_len + AES_BLOCK_LEN - 1) / 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; +} + + +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 *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 " MAC_FMT "\n", MAC_ARG(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 " MAC_FMT + " with keyid=%d that does not have a configured" + " key\n", MAC_ARG(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 (memcmp(pn, key->rx_pn, CCMP_PN_LEN) <= 0) { + if (net_ratelimit()) { + printk(KERN_DEBUG "CCMP: replay detected: STA=" MAC_FMT + " previous PN %02x%02x%02x%02x%02x%02x " + "received PN %02x%02x%02x%02x%02x%02x\n", + MAC_ARG(hdr->addr2), MAC_ARG(key->rx_pn), + MAC_ARG(pn)); + } + key->dot11RSNAStatsCCMPReplays++; + return -4; + } + + ccmp_init_blocks(key->tfm, hdr, pn, data_len, b0, a, b); + xor_block(mic, b, CCMP_MIC_LEN); + + blocks = (data_len + AES_BLOCK_LEN - 1) / 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=" + MAC_FMT "\n", MAC_ARG(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_tfm *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, + MAC_ARG(ccmp->tx_pn), MAC_ARG(ccmp->rx_pn), + 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, + .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_prefix_len = CCMP_HDR_LEN, + .extra_postfix_len = CCMP_MIC_LEN, + .owner = THIS_MODULE, +}; + + +static int __init ieee80211_crypto_ccmp_init(void) +{ + if (ieee80211_register_crypto_ops(&ieee80211_crypt_ccmp) < 0) + return -1; + + return 0; +} + + +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/drivers/net/wireless/ieee80211/ieee80211_crypt_tkip.c b/drivers/net/wireless/ieee80211/ieee80211_crypt_tkip.c new file mode 100644 index 000000000..be7abd0e7 --- /dev/null +++ b/drivers/net/wireless/ieee80211/ieee80211_crypt_tkip.c @@ -0,0 +1,714 @@ +/* + * Host AP crypt: host-based TKIP encryption implementation for Host AP driver + * + * Copyright (c) 2003-2004, Jouni Malinen + * + * This program is free software; you can 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "ieee80211.h" + +#ifndef CONFIG_CRYPTO +#error CONFIG_CRYPTO is required to build this module. +#endif + +#include +#include +#include + +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_tfm *tfm_arc4; + struct crypto_tfm *tfm_michael; + + /* scratch buffers for virt_to_page() (crypto API) */ + u8 rx_hdr[16], tx_hdr[16]; +}; + +static void * ieee80211_tkip_init(int key_idx) +{ + struct ieee80211_tkip_data *priv; + + priv = kmalloc(sizeof(*priv), GFP_ATOMIC); + if (priv == NULL) + goto fail; + memset(priv, 0, sizeof(*priv)); + priv->key_idx = key_idx; + + priv->tfm_arc4 = crypto_alloc_tfm("arc4", 0); + if (priv->tfm_arc4 == NULL) { + printk(KERN_DEBUG "ieee80211_crypt_tkip: could not allocate " + "crypto API arc4\n"); + goto fail; + } + + priv->tfm_michael = crypto_alloc_tfm("michael_mic", 0); + if (priv->tfm_michael == NULL) { + printk(KERN_DEBUG "ieee80211_crypt_tkip: could not allocate " + "crypto API michael_mic\n"); + goto fail; + } + + return priv; + +fail: + if (priv) { + if (priv->tfm_michael) + crypto_free_tfm(priv->tfm_michael); + if (priv->tfm_arc4) + crypto_free_tfm(priv->tfm_arc4); + kfree(priv); + } + + return NULL; +} + + +static void ieee80211_tkip_deinit(void *priv) +{ + struct ieee80211_tkip_data *_priv = priv; + if (_priv && _priv->tfm_michael) + crypto_free_tfm(_priv->tfm_michael); + if (_priv && _priv->tfm_arc4) + crypto_free_tfm(_priv->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(u16 *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((u16 *) &TK[0])); + PPK[1] += _S_(PPK[0] ^ Mk16_le((u16 *) &TK[2])); + PPK[2] += _S_(PPK[1] ^ Mk16_le((u16 *) &TK[4])); + PPK[3] += _S_(PPK[2] ^ Mk16_le((u16 *) &TK[6])); + PPK[4] += _S_(PPK[3] ^ Mk16_le((u16 *) &TK[8])); + PPK[5] += _S_(PPK[4] ^ Mk16_le((u16 *) &TK[10])); + + PPK[0] += RotR1(PPK[5] ^ Mk16_le((u16 *) &TK[12])); + PPK[1] += RotR1(PPK[0] ^ Mk16_le((u16 *) &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((u16 *) &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_encrypt(struct sk_buff *skb, int hdr_len, void *priv) +{ + struct ieee80211_tkip_data *tkey = priv; + int len; + u8 rc4key[16], *pos, *icv; + struct ieee80211_hdr *hdr; + u32 crc; + struct scatterlist sg; + + if (skb_headroom(skb) < 8 || skb_tailroom(skb) < 4 || + skb->len < hdr_len) + return -1; + + hdr = (struct ieee80211_hdr *) skb->data; + 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; + icv = skb_put(skb, 4); + + *pos++ = rc4key[0]; + *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; + + crc = ~crc32_le(~0, pos, len); + icv[0] = crc; + icv[1] = crc >> 8; + icv[2] = crc >> 16; + icv[3] = crc >> 24; + + crypto_cipher_setkey(tkey->tfm_arc4, rc4key, 16); + sg.page = virt_to_page(pos); + sg.offset = offset_in_page(pos); + sg.length = len + 4; + crypto_cipher_encrypt(tkey->tfm_arc4, &sg, &sg, len + 4); + + tkey->tx_iv16++; + if (tkey->tx_iv16 == 0) { + tkey->tx_phase1_done = 0; + tkey->tx_iv32++; + } + + return 0; +} + +static int ieee80211_tkip_decrypt(struct sk_buff *skb, int hdr_len, void *priv) +{ + struct ieee80211_tkip_data *tkey = priv; + u8 rc4key[16]; + u8 keyidx, *pos; + u32 iv32; + u16 iv16; + struct ieee80211_hdr *hdr; + u8 icv[4]; + u32 crc; + struct scatterlist sg; + int plen; + + if (skb->len < hdr_len + 8 + 4) + 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 "TKIP: received packet without ExtIV" + " flag from " MAC_FMT "\n", MAC_ARG(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 " MAC_FMT + " with keyid=%d that does not have a configured" + " key\n", MAC_ARG(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 (iv32 < tkey->rx_iv32 || + (iv32 == tkey->rx_iv32 && iv16 <= tkey->rx_iv16)) { + if (net_ratelimit()) { + printk(KERN_DEBUG "TKIP: replay detected: STA=" MAC_FMT + " previous TSC %08x%04x received TSC " + "%08x%04x\n", MAC_ARG(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_cipher_setkey(tkey->tfm_arc4, rc4key, 16); + sg.page = virt_to_page(pos); + sg.offset = offset_in_page(pos); + sg.length = plen + 4; + crypto_cipher_decrypt(tkey->tfm_arc4, &sg, &sg, plen + 4); + + 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=" + MAC_FMT "\n", MAC_ARG(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 ieee80211_tkip_data *tkey, u8 *key, u8 *hdr, + u8 *data, size_t data_len, u8 *mic) +{ + struct scatterlist sg[2]; + + if (tkey->tfm_michael == NULL) { + printk(KERN_WARNING "michael_mic: tfm_michael == NULL\n"); + return -1; + } + sg[0].page = virt_to_page(hdr); + sg[0].offset = offset_in_page(hdr); + sg[0].length = 16; + + sg[1].page = virt_to_page(data); + sg[1].offset = offset_in_page(data); + sg[1].length = data_len; + + crypto_digest_init(tkey->tfm_michael); + crypto_digest_setkey(tkey->tfm_michael, key, 8); + crypto_digest_update(tkey->tfm_michael, sg, 2); + crypto_digest_final(tkey->tfm_michael, mic); + + return 0; +} + +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_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; + } + + 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, &tkey->key[16], tkey->tx_hdr, + skb->data + hdr_len, skb->len - 8 - hdr_len, pos)) + return -1; + + return 0; +} + + +#if WIRELESS_EXT >= 18 +static void ieee80211_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); +} +#elif WIRELESS_EXT >= 15 +static void ieee80211_michael_mic_failure(struct net_device *dev, + struct ieee80211_hdr *hdr, + int keyidx) +{ + union iwreq_data wrqu; + char buf[128]; + + /* TODO: needed parameters: count, keyid, key type, TSC */ + sprintf(buf, "MLME-MICHAELMICFAILURE.indication(keyid=%d %scast addr=" + MAC_FMT ")", keyidx, hdr->addr1[0] & 0x01 ? "broad" : "uni", + MAC_ARG(hdr->addr2)); + memset(&wrqu, 0, sizeof(wrqu)); + wrqu.data.length = strlen(buf); + wireless_send_event(dev, IWEVCUSTOM, &wrqu, buf); +} +#else /* WIRELESS_EXT >= 15 */ +static inline void ieee80211_michael_mic_failure(struct net_device *dev, + struct ieee80211_hdr *hdr, + int keyidx) +{ +} +#endif /* WIRELESS_EXT >= 15 */ + + +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]; + + if (!tkey->key_set) + return -1; + + michael_mic_hdr(skb, tkey->rx_hdr); + if (michael_mic(tkey, &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 " MAC_FMT " keyidx=%d\n", + skb->dev ? skb->dev->name : "N/A", MAC_ARG(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_tfm *tfm = tkey->tfm_michael; + struct crypto_tfm *tfm2 = tkey->tfm_arc4; + + keyidx = tkey->key_idx; + memset(tkey, 0, sizeof(*tkey)); + tkey->key_idx = keyidx; + tkey->tfm_michael = tfm; + tkey->tfm_arc4 = tfm2; + 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, + .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_prefix_len = 4 + 4, /* IV + ExtIV */ + .extra_postfix_len = 8 + 4, /* MIC + ICV */ + .owner = THIS_MODULE, +}; + + +static int __init ieee80211_crypto_tkip_init(void) +{ + if (ieee80211_register_crypto_ops(&ieee80211_crypt_tkip) < 0) + return -1; + + return 0; +} + + +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/drivers/net/wireless/ieee80211/ieee80211_crypt_wep.c b/drivers/net/wireless/ieee80211/ieee80211_crypt_wep.c new file mode 100644 index 000000000..892b5551f --- /dev/null +++ b/drivers/net/wireless/ieee80211/ieee80211_crypt_wep.c @@ -0,0 +1,277 @@ +/* + * Host AP crypt: host-based WEP encryption implementation for Host AP driver + * + * Copyright (c) 2002-2004, Jouni Malinen + * + * This program is free software; you can 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 +#include +#include +#include +#include +#include +#include +#include + +#include "ieee80211.h" + +#ifndef CONFIG_CRYPTO +#error CONFIG_CRYPTO is required to build this module. +#endif +#include +#include +#include + +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_tfm *tfm; +}; + + +static void * prism2_wep_init(int keyidx) +{ + struct prism2_wep_data *priv; + + priv = kmalloc(sizeof(*priv), GFP_ATOMIC); + if (priv == NULL) + goto fail; + memset(priv, 0, sizeof(*priv)); + priv->key_idx = keyidx; + + priv->tfm = crypto_alloc_tfm("arc4", 0); + if (priv->tfm == NULL) { + printk(KERN_DEBUG "ieee80211_crypt_wep: could not allocate " + "crypto API arc4\n"); + goto fail; + } + + /* start WEP IV from a random value */ + get_random_bytes(&priv->iv, 4); + + return priv; + +fail: + if (priv) { + if (priv->tfm) + crypto_free_tfm(priv->tfm); + kfree(priv); + } + return NULL; +} + + +static void prism2_wep_deinit(void *priv) +{ + struct prism2_wep_data *_priv = priv; + if (_priv && _priv->tfm) + crypto_free_tfm(_priv->tfm); + kfree(priv); +} + + +/* 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; + u32 crc, klen, len; + u8 key[WEP_KEY_LEN + 3]; + u8 *pos, *icv; + struct scatterlist sg; + + if (skb_headroom(skb) < 4 || skb_tailroom(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++ = key[0] = (wep->iv >> 16) & 0xff; + *pos++ = key[1] = (wep->iv >> 8) & 0xff; + *pos++ = key[2] = wep->iv & 0xff; + *pos++ = wep->key_idx << 6; + + /* Copy rest of the WEP key (the secret part) */ + memcpy(key + 3, wep->key, wep->key_len); + + /* Append little-endian CRC32 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_cipher_setkey(wep->tfm, key, klen); + sg.page = virt_to_page(pos); + sg.offset = offset_in_page(pos); + sg.length = len + 4; + crypto_cipher_encrypt(wep->tfm, &sg, &sg, len + 4); + + return 0; +} + + +/* 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; + 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_cipher_setkey(wep->tfm, key, klen); + sg.page = virt_to_page(pos); + sg.offset = offset_in_page(pos); + sg.length = plen + 4; + crypto_cipher_decrypt(wep->tfm, &sg, &sg, plen + 4); + + 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, + .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_prefix_len = 4, /* IV */ + .extra_postfix_len = 4, /* ICV */ + .owner = THIS_MODULE, +}; + + +static int __init ieee80211_crypto_wep_init(void) +{ + if (ieee80211_register_crypto_ops(&ieee80211_crypt_wep) < 0) + return -1; + + return 0; +} + + +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/drivers/net/wireless/ieee80211/ieee80211_module.c b/drivers/net/wireless/ieee80211/ieee80211_module.c new file mode 100644 index 000000000..39bc355a9 --- /dev/null +++ b/drivers/net/wireless/ieee80211/ieee80211_module.c @@ -0,0 +1,252 @@ +/******************************************************************************* + + Copyright(c) 2004 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 + + Copyright (c) 2002-2003, Jouni Malinen + + 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 + Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 + +*******************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "ieee80211.h" + +MODULE_DESCRIPTION("802.11 data/management/control stack"); +MODULE_AUTHOR("Copyright (C) 2004 Intel Corporation "); +MODULE_LICENSE("GPL"); + +#define DRV_NAME "ieee80211" + +static inline int ieee80211_networks_allocate(struct ieee80211_device *ieee) +{ + if (ieee->networks) + return 0; + + ieee->networks = kmalloc( + 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; + } + + memset(ieee->networks, 0, + MAX_NETWORK_COUNT * sizeof(struct ieee80211_network)); + + return 0; +} + +static inline void ieee80211_networks_free(struct ieee80211_device *ieee) +{ + if (!ieee->networks) + return; + kfree(ieee->networks); + ieee->networks = NULL; +} + +static inline 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); +} + +struct ieee80211_device *ieee80211_alloc(struct net_device *dev, + void *priv) +{ + struct ieee80211_device *ieee = kmalloc( + sizeof(struct ieee80211_device), GFP_KERNEL); + int err; + if (ieee == NULL) + return NULL; + memset(ieee, 0, sizeof(*ieee)); + ieee->dev = dev; + ieee->priv = priv; + + err = ieee80211_networks_allocate(ieee); + if (err) { + IEEE80211_ERROR("Unable to allocate beacon storage\n"); + goto error; + } + ieee80211_networks_initialize(ieee); + + /* Default fragmentation threshold is maximum payload size */ + ieee->fts = DEFAULT_FTS; + ieee->scan_age = DEFAULT_MAX_SCAN_AGE; + ieee->open_wep = 1; + +#ifdef CONFIG_IEEE80211_CRYPT + /* Default to enabling full open WEP with host based encrypt/decrypt */ + ieee->host_encrypt = 1; + ieee->host_decrypt = 1; + + INIT_LIST_HEAD(&ieee->crypt_deinit_list); + init_timer(&ieee->crypt_deinit_timer); + ieee->crypt_deinit_timer.data = (unsigned long)ieee; + ieee->crypt_deinit_timer.function = ieee80211_crypt_deinit_handler; +#endif + + spin_lock_init(&ieee->lock); + + return ieee; + + error: + if (ieee) + kfree(ieee); + return NULL; +} + +void ieee80211_free(struct ieee80211_device *ieee) +{ +#ifdef CONFIG_IEEE80211_CRYPT + int i; + + 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; + } + } +#endif + ieee80211_networks_free(ieee); + kfree(ieee); +} + +#ifdef CONFIG_IEEE80211_DEBUG + +static int debug = 0; +u32 ieee80211_debug_level = 0; +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 *buffer, + unsigned long count, void *data) +{ + char buf[] = "0x00000000"; + unsigned long len = min(sizeof(buf) - 1, (u32)count); + char *p = (char *)buf; + unsigned long val; + + if (copy_from_user(buf, buffer, len)) + return count; + buf[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 == buf) + printk(KERN_INFO DRV_NAME + ": %s is not in hex or decimal form.\n", buf); + else + ieee80211_debug_level = val; + + return strnlen(buf, count); +} + +static int __init ieee80211_init(void) +{ + struct proc_dir_entry *e; + + ieee80211_debug_level = debug; + ieee80211_proc = create_proc_entry(DRV_NAME, S_IFDIR, 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, proc_net); + ieee80211_proc = NULL; + return -EIO; + } + e->read_proc = show_debug_level; + e->write_proc = store_debug_level; + e->data = NULL; + + return 0; +} + +static void __exit ieee80211_exit(void) +{ + if (ieee80211_proc) { + remove_proc_entry("debug_level", ieee80211_proc); + remove_proc_entry(DRV_NAME, proc_net); + ieee80211_proc = NULL; + } +} + +#include +module_param(debug, int, 0444); +MODULE_PARM_DESC(debug, "debug output mask"); + + +module_exit(ieee80211_exit); +module_init(ieee80211_init); +#endif + +EXPORT_SYMBOL(ieee80211_alloc); +EXPORT_SYMBOL(ieee80211_free); diff --git a/drivers/net/wireless/ieee80211/ieee80211_rx.c b/drivers/net/wireless/ieee80211/ieee80211_rx.c new file mode 100644 index 000000000..fee0e5d0f --- /dev/null +++ b/drivers/net/wireless/ieee80211/ieee80211_rx.c @@ -0,0 +1,1161 @@ +/* + * 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 + * + * Copyright (c) 2002-2003, Jouni Malinen + * 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. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "ieee80211.h" + +static inline 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->mac.raw = skb->data; + skb_pull(skb, ieee80211_get_hdrlen(fc)); + skb->pkt_type = PACKET_OTHERHOST; + skb->protocol = __constant_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)) { + printk(KERN_DEBUG "%s: expiring fragment cache entry " + "seq=%u last_frag=%u\n", + ieee->dev->name, 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) && + memcmp(entry->src_addr, src, ETH_ALEN) == 0 && + memcmp(entry->dst_addr, dst, ETH_ALEN) == 0) + 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 *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) + + 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 *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) { + printk(KERN_DEBUG "%s: could not invalidate fragment cache " + "entry (seq=%u)\n", + ieee->dev->name, 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 inline 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 *) + 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) */ + +#ifdef CONFIG_IEEE80211_CRYPT +/* 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 *hdr; + u8 *pos; + + if (skb->len < 24) + return 0; + + hdr = (struct ieee80211_hdr *) 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 && + memcmp(hdr->addr1, dev->dev_addr, ETH_ALEN) == 0 && + memcmp(hdr->addr3, dev->dev_addr, ETH_ALEN) == 0) { + /* ToDS frame with own addr BSSID and DA */ + } else if ((fc & (IEEE80211_FCTL_TODS | IEEE80211_FCTL_FROMDS)) == + IEEE80211_FCTL_FROMDS && + memcmp(hdr->addr1, dev->dev_addr, ETH_ALEN) == 0) { + /* 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 inline int +ieee80211_rx_frame_decrypt(struct ieee80211_device* ieee, struct sk_buff *skb, + struct ieee80211_crypt_data *crypt) +{ + struct ieee80211_hdr *hdr; + int res, hdrlen; + + if (crypt == NULL || crypt->ops->decrypt_mpdu == NULL) + return 0; + + hdr = (struct ieee80211_hdr *) skb->data; + hdrlen = ieee80211_get_hdrlen(le16_to_cpu(hdr->frame_ctl)); + +#ifdef CONFIG_IEEE80211_WPA + if (ieee->tkip_countermeasures && + strcmp(crypt->ops->name, "TKIP") == 0) { + if (net_ratelimit()) { + printk(KERN_DEBUG "%s: TKIP countermeasures: dropped " + "received packet from " MAC_FMT "\n", + ieee->dev->name, MAC_ARG(hdr->addr2)); + } + return -1; + } +#endif + + atomic_inc(&crypt->refcnt); + 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", + ieee->dev->name, MAC_ARG(hdr->addr2), res); + if (res == -2) + printk(KERN_DEBUG "%s: WEP decryption failed ICV " + "mismatch (key %d)\n", + ieee->dev->name, skb->data[hdrlen + 3] >> 6); + ieee->ieee_stats.rx_discards_wep_undecryptable++; + return -1; + } + + return res; +} + + +/* Called only as a tasklet (software IRQ), by ieee80211_rx */ +static inline int +ieee80211_rx_frame_decrypt_msdu(struct ieee80211_device* ieee, struct sk_buff *skb, + int keyidx, struct ieee80211_crypt_data *crypt) +{ + struct ieee80211_hdr *hdr; + int res, hdrlen; + + if (crypt == NULL || crypt->ops->decrypt_msdu == NULL) + return 0; + + hdr = (struct ieee80211_hdr *) 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, MAC_ARG(hdr->addr2), keyidx); + return -1; + } + + return 0; +} +#endif /* CONFIG_IEEE80211_CRYPT */ + + +/* 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 *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]; +#ifdef CONFIG_IEEE80211_CRYPT + struct ieee80211_crypt_data *crypt = NULL; + int keyidx = 0; +#endif + + hdr = (struct ieee80211_hdr *)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); + +#ifdef NOT_YET +#if WIRELESS_EXT > 15 + /* Put this code here so that we avoid duplicating it in all + * Rx paths. - Jean II */ +#ifdef IW_WIRELESS_SPY /* defined in iw_handler.h */ + /* If spy monitoring on */ + if (iface->spy_data.spy_number > 0) { + struct iw_quality wstats; + wstats.level = rx_stats->signal; + wstats.noise = rx_stats->noise; + wstats.updated = 6; /* No qual value */ + /* Update spy records */ + wireless_spy_update(dev, hdr->addr2, &wstats); + } +#endif /* IW_WIRELESS_SPY */ +#endif /* WIRELESS_EXT > 15 */ + hostap_update_rx_stats(local->ap, hdr, rx_stats); +#endif + +#if WIRELESS_EXT > 15 + if (ieee->iw_mode == IW_MODE_MONITOR) { + ieee80211_monitor_rx(ieee, skb, rx_stats); + stats->rx_packets++; + stats->rx_bytes += skb->len; + return 1; + } +#endif + +#ifdef CONFIG_IEEE80211_CRYPT + if (ieee->host_decrypt) { + int idx = 0; + if (skb->len >= hdrlen + 3) + idx = skb->data[hdrlen + 3] >> 6; + crypt = ieee->crypt[idx]; +#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_WEP)) { +#if 0 + /* 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. */ + printk(KERN_DEBUG "%s: WEP decryption failed (not set)" + " (SA=" MAC_FMT ")\n", + ieee->dev->name, MAC_ARG(hdr->addr2)); +#endif + ieee->ieee_stats.rx_discards_wep_undecryptable++; + goto rx_dropped; + } + } +#endif /* CONFIG_IEEE80211_CRYPT */ + +#ifdef NOT_YET + if (type != WLAN_FC_TYPE_DATA) { + if (type == WLAN_FC_TYPE_MGMT && stype == WLAN_FC_STYPE_AUTH && + fc & IEEE80211_FCTL_WEP && 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, + MAC_ARG(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 + + /* Data frame - extract src/dst addresses */ + if (skb->len < IEEE80211_DATA_HDR3_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_DATA_HDR4_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 && + memcmp(hdr->addr2, ieee->assoc_ap_addr, ETH_ALEN) == 0) { + /* 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. */ + 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) + printk(KERN_DEBUG "%s: RX: dropped data frame " + "with no data (type=0x%02x, subtype=0x%02x, len=%d)\n", + dev->name, type, stype, skb->len); + goto rx_dropped; + } + + /* skb: hdr + (possibly fragmented, possibly encrypted) payload */ + +#ifdef CONFIG_IEEE80211_CRYPT + if (ieee->host_decrypt && (fc & IEEE80211_FCTL_WEP) && + (keyidx = ieee80211_rx_frame_decrypt(ieee, skb, crypt)) < 0) + goto rx_dropped; +#endif + hdr = (struct ieee80211_hdr *) skb->data; + + /* skb: hdr + (possibly fragmented) plaintext payload */ + // PR: FIXME: hostap has additional conditions in the "if" below: + // ieee->host_decrypt && (fc & IEEE80211_FCTL_WEP) && + 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) { + printk(KERN_DEBUG "%s: Rx cannot get skb from " + "fragment cache (morefrag=%d seq=%u frag=%u)\n", + dev->name, + (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 */ + memcpy(skb_put(frag_skb, flen), skb->data, flen); + } else { + /* append frame payload to the end of the fragment + * cache skb */ + memcpy(skb_put(frag_skb, flen), skb->data + hdrlen, + 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 *) skb->data; + ieee80211_frag_cache_invalidate(ieee, hdr); + } + + /* skb: hdr + (possible reassembled) full MSDU payload; possibly still + * encrypted/authenticated */ +#ifdef CONFIG_IEEE80211_CRYPT + if (ieee->host_decrypt && (fc & IEEE80211_FCTL_WEP) && + ieee80211_rx_frame_decrypt_msdu(ieee, skb, keyidx, crypt)) + goto rx_dropped; + + hdr = (struct ieee80211_hdr *) skb->data; + if (crypt && !(fc & IEEE80211_FCTL_WEP) && !ieee->open_wep) { + if (/*ieee->ieee_802_1x &&*/ + ieee80211_is_eapol_frame(ieee, skb)) { + /* pass unencrypted EAPOL frames even if encryption is + * configured */ + IEEE80211_DEBUG_EAP("RX: IEEE 802.1X - passing " + "unencrypted EAPOL frame\n"); + } else { + printk(KERN_DEBUG "%s: encryption configured, but RX " + "frame not encrypted (SA=" MAC_FMT ")\n", + ieee->dev->name, MAC_ARG(hdr->addr2)); + goto rx_dropped; + } + } + +#ifdef CONFIG_IEEE80211_DEBUG + if (crypt && !(fc & IEEE80211_FCTL_WEP) && + ieee80211_is_eapol_frame(ieee, skb)) { + IEEE80211_DEBUG_EAP("RX: IEEE 802.1X - passing " + "unencrypted EAPOL frame\n"); + } +#endif + + if (/*ieee->drop_unencrypted*/ crypt && !(fc & IEEE80211_FCTL_WEP) && !ieee->open_wep && + !ieee80211_is_eapol_frame(ieee, skb)) { + if (net_ratelimit()) { + printk(KERN_DEBUG "%s: dropped unencrypted RX data " + "frame from " MAC_FMT " (drop_unencrypted=1)\n", + dev->name, MAC_ARG(hdr->addr2)); + } + goto rx_dropped; + } +#endif /* CONFIG_IEEE80211_CRYPT */ + + /* 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->ieee_802_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 { + u16 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 */ + memcpy(skb->data + 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->protocol = __constant_htons(ETH_P_802_3); + skb2->mac.raw = skb2->nh.raw = skb2->data; + /* skb2->nh.raw = skb2->data + ETH_HLEN; */ + skb2->dev = dev; + dev_queue_xmit(skb2); + } + +#endif + + if (skb) { + skb->protocol = eth_type_trans(skb, dev); + memset(skb->cb, 0, sizeof(skb->cb)); + skb->dev = dev; + skb->ip_summed = CHECKSUM_NONE; /* 802.11 crc not sufficient */ + netif_rx(skb); + } + + 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; +} + +#define MGMT_FRAME_FIXED_PART_LENGTH 0x24 + +static int ieee80211_filter_network( + struct ieee80211_device *ieee, + struct ieee80211_network *network, + struct ieee80211_rx_stats *stats) +{ + // TODO check valid channel + if (ieee->abg_ture == 1) + return 0; + + switch (stats->freq) { + case IEEE80211_52GHZ_BAND: + if (ieee->freq_band == IEEE80211_24GHZ_BAND) + return 1; + break; + case IEEE80211_24GHZ_BAND: + default: + if (ieee->freq_band == IEEE80211_52GHZ_BAND) + return 1; + if (ieee->modulation == IEEE80211_CCK_MODULATION) { + if (network->flags & NETWORK_HAS_OFDM) + return 1; + } else if (ieee->modulation == IEEE80211_OFDM_MODULATION) { + if (!(network->flags & NETWORK_HAS_OFDM)) + return 1; + } + break; + } + + return 0; +} + +static inline int ieee80211_is_ofdm_rate(u8 rate) +{ + switch (rate & ~IEEE80211_BASIC_RATE_MASK) { + case IEEE80211_OFDM_RATE_6MB: + case IEEE80211_OFDM_RATE_9MB: + case IEEE80211_OFDM_RATE_12MB: + case IEEE80211_OFDM_RATE_18MB: + case IEEE80211_OFDM_RATE_24MB: + case IEEE80211_OFDM_RATE_36MB: + case IEEE80211_OFDM_RATE_48MB: + case IEEE80211_OFDM_RATE_54MB: + return 1; + } + return 0; +} + + +static inline int ieee80211_network_init( + struct ieee80211_device *ieee, + struct ieee80211_probe_response *beacon, + struct ieee80211_network *network, + struct ieee80211_rx_stats *stats) +{ + struct ieee80211_info_element *info_element; + u16 left; + u8 i; + int probe_response = WLAN_FC_GET_STYPE(beacon->header.frame_ctl) == + IEEE80211_STYPE_PROBE_RESP; + + if (stats->freq == IEEE80211_52GHZ_BAND) { + /* for A band (No DS info) */ + network->channel = stats->received_channel; + } + + /* Pull out fixed field data */ + memcpy(network->bssid, beacon->header.addr3, ETH_ALEN); + network->capability = beacon->capability; + network->last_scanned = jiffies; + network->time_stamp[0] = beacon->time_stamp[0]; + network->time_stamp[1] = beacon->time_stamp[1]; + network->beacon_interval = beacon->beacon_interval; + network->listen_interval = 0x0A; /* Where to pull this? beacon->listen_interval;*/ + network->flags = 0; + network->rates_len = network->rates_ex_len = 0; + network->last_associate = 0; + +#ifdef CONFIG_IEEE80211_WPA + network->wpa_ie_len = 0; + network->rsn_ie_len = 0; +#endif /* CONFIG_IEEE80211_WPA */ + + info_element = &beacon->info_element; + left = stats->len - ((void *)info_element - (void *)beacon); + while (left >= sizeof(struct ieee80211_info_element_hdr)) { + if (sizeof(struct ieee80211_info_element_hdr) + info_element->len > left) { + IEEE80211_DEBUG_SCAN("SCAN: parse failed: info_element->len + 2 > left : info_element->len+2=%d left=%d.\n", + info_element->len + sizeof(struct ieee80211_info_element), + left); + return 1; + } + + switch (info_element->id) { + case MFIE_TYPE_SSID: + if (!probe_response) { + IEEE80211_DEBUG_SCAN( + "MFIE_TYPE_SSID: " + "Ignored from BEACON FRAME.\n"); + 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_SCAN("MFIE_TYPE_SSID: '%s' len=%d.\n", + network->ssid, network->ssid_len); + break; + + case MFIE_TYPE_RATES: + 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]; + if (!(network->flags & NETWORK_HAS_OFDM)) + network->flags |= ieee80211_is_ofdm_rate(info_element->data[i]) ? NETWORK_HAS_OFDM : 0; + } + break; + + case MFIE_TYPE_RATES_EX: + 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]; + if (!(network->flags & NETWORK_HAS_OFDM)) + network->flags |= ieee80211_is_ofdm_rate(info_element->data[i]) ? NETWORK_HAS_OFDM : 0; + } + break; + + case MFIE_TYPE_DS_SET: + IEEE80211_DEBUG_SCAN("MFIE_TYPE_DS_SET: %d\n", + info_element->data[0]); + if (stats->freq == IEEE80211_24GHZ_BAND) + network->channel = info_element->data[0]; + break; + + case MFIE_TYPE_FH_SET: + IEEE80211_DEBUG_SCAN("MFIE_TYPE_FH_SET: ignored\n"); + break; + + case MFIE_TYPE_CF_SET: + IEEE80211_DEBUG_SCAN("MFIE_TYPE_CF_SET: ignored\n"); + break; + + case MFIE_TYPE_TIM: + IEEE80211_DEBUG_SCAN("MFIE_TYPE_TIM: ignored\n"); + break; + + case MFIE_TYPE_IBSS_SET: + IEEE80211_DEBUG_SCAN("MFIE_TYPE_IBSS_SET: ignored\n"); + break; + + case MFIE_TYPE_CHALLENGE: + IEEE80211_DEBUG_SCAN("MFIE_TYPE_CHALLENGE: ignored\n"); + break; + +#ifdef CONFIG_IEEE80211_WPA + case MFIE_TYPE_GENERIC: + IEEE80211_DEBUG_SCAN("MFIE_TYPE_GENERIC: %d bytes\n", + info_element->len); + 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_SCAN("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; +#endif + + default: + IEEE80211_DEBUG_SCAN("unsupported IE %d\n", + info_element->id); + break; + } + + left -= sizeof(struct ieee80211_info_element_hdr) + + info_element->len; + info_element = (struct ieee80211_info_element *) + &info_element->data[info_element->len]; + } + + if (stats->freq == IEEE80211_52GHZ_BAND) + network->mode = IEEE_A; + else { + if (network->flags & NETWORK_HAS_OFDM) + network->mode = IEEE_G; + else + network->mode = IEEE_B; + } + + if (ieee80211_is_empty_essid(network->ssid, network->ssid_len)) + network->flags |= NETWORK_EMPTY_ESSID; + + if (ieee80211_filter_network(ieee, network, stats)) { + IEEE80211_DEBUG_SCAN("Filtered out '%s (" MAC_FMT ")' " + "network.\n", + escape_essid(network->ssid, + network->ssid_len), + MAC_ARG(network->bssid)); + return 1; + } + + memcpy(&network->stats, stats, sizeof(network->stats)); + + return 0; +} + +static inline void ieee80211_process_probe_response( + struct ieee80211_device *ieee, + struct ieee80211_probe_response *beacon, + struct ieee80211_rx_stats *stats) +{ + struct ieee80211_network *network; + struct ieee80211_network *oldest_network = NULL; +#ifdef CONFIG_IEEE80211_DEBUG + struct ieee80211_info_element *ssid_ie; + u8 ssid_len = sizeof(""); + u8 ssid[IW_ESSID_MAX_SIZE]; + u8 empty_ssid; +#endif + + IEEE80211_DEBUG_SCAN( + "\n" + "Time Stamp : %08X %08X\n" + "Beacon Interval : %04X\n" + "Capabilities : %c%c%c%c-%c%c%c%c\n", + beacon->time_stamp[0], + beacon->time_stamp[1], + beacon->beacon_interval, + (beacon->capability & BIT(7)) ? '1' : '0', + (beacon->capability & BIT(6)) ? '1' : '0', + (beacon->capability & BIT(5)) ? '1' : '0', + (beacon->capability & BIT(4)) ? '1' : '0', + (beacon->capability & BIT(3)) ? '1' : '0', + (beacon->capability & BIT(2)) ? '1' : '0', + (beacon->capability & BIT(1)) ? '1' : '0', + (beacon->capability & BIT(0)) ? '1' : '0'); + + /* Search for this entry in the list and nuke it if it is + * already there. + */ + list_for_each_entry(network, &ieee->network_list, list) { + if (!memcmp(network->bssid, beacon->header.addr3, + ETH_ALEN)) + break; + if ((oldest_network == NULL) || + (network->last_scanned < oldest_network->last_scanned)) + oldest_network = network; + } + + /* If we didn't find a match, then get a new network slot to initialize + * with this beacon's information */ + if (&network->list == &ieee->network_list) { + if (list_empty(&ieee->network_free_list)) { + /* If there are no more slots, expire the oldest */ + list_del(&oldest_network->list); + network = oldest_network; + IEEE80211_DEBUG_SCAN("Expired '%s (" MAC_FMT ")' from " + "network list.\n", + escape_essid(network->ssid, + network->ssid_len), + MAC_ARG(network->bssid)); + } else { + /* Otherwise just pull from the free list */ + network = list_entry(ieee->network_free_list.next, + struct ieee80211_network, list); + list_del(ieee->network_free_list.next); + } + +#ifdef CONFIG_IEEE80211_DEBUG + ssid_ie = &beacon->info_element; + if (ssid_ie->id == MFIE_TYPE_SSID) { + ssid_len = min(ssid_ie->len, (u8)IW_ESSID_MAX_SIZE); + empty_ssid = ieee80211_is_empty_essid(ssid_ie->data, + ssid_len); + } else { + empty_ssid = 1; + } + + if (empty_ssid) + memcpy(ssid, "", sizeof("")); + else + memcpy(ssid, ssid_ie->data, ssid_len); +#endif + + IEEE80211_DEBUG_SCAN("Adding '%s (" MAC_FMT ")' to network " + "list.\n", + escape_essid(ssid, ssid_len), + MAC_ARG(beacon->header.addr3)); + list_add_tail(&network->list, &ieee->network_list); + } else { + IEEE80211_DEBUG_SCAN("Updating '%s (" MAC_FMT ")' to network " + "list.\n", + escape_essid(network->ssid, + network->ssid_len), + MAC_ARG(network->bssid)); + } + + if (ieee80211_network_init(ieee, beacon, network, stats)) { + /* If parsing of the beacon probe was not successful then + * nuke this network from the list and stick it on the free + * list for future use */ + IEEE80211_DEBUG_SCAN("Dropped '%s (" MAC_FMT ")' from network " + "list.\n", + escape_essid(ssid, ssid_len), + MAC_ARG(beacon->header.addr3)); + list_del(&network->list); + list_add_tail(&network->list, &ieee->network_free_list); + } +} + +void ieee80211_rx_mgt(struct ieee80211_device *ieee, + struct ieee80211_hdr *header, + struct ieee80211_rx_stats *stats) +{ + switch (WLAN_FC_GET_STYPE(header->frame_ctl)) { + case IEEE80211_STYPE_ASSOC_RESP: + IEEE80211_DEBUG_MGMT("received ASSOCIATION RESPONSE (%d)\n", + WLAN_FC_GET_STYPE(header->frame_ctl)); + break; + + case IEEE80211_STYPE_REASSOC_RESP: + IEEE80211_DEBUG_MGMT("received REASSOCIATION RESPONSE (%d)\n", + WLAN_FC_GET_STYPE(header->frame_ctl)); + break; + + case IEEE80211_STYPE_PROBE_RESP: + IEEE80211_DEBUG_MGMT("received PROBE RESPONSE (%d)\n", + WLAN_FC_GET_STYPE(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(header->frame_ctl)); + IEEE80211_DEBUG_SCAN("Beacon\n"); + ieee80211_process_probe_response( + ieee, (struct ieee80211_probe_response *)header, stats); + break; + + default: + IEEE80211_DEBUG_MGMT("received UNKNOWN (%d)\n", + WLAN_FC_GET_STYPE(header->frame_ctl)); + IEEE80211_WARNING("%s: Unknown management packet: %d\n", + ieee->dev->name, + WLAN_FC_GET_STYPE(header->frame_ctl)); + break; + } +} + + +EXPORT_SYMBOL(ieee80211_rx_mgt); +EXPORT_SYMBOL(ieee80211_rx); diff --git a/drivers/net/wireless/ieee80211/ieee80211_tx.c b/drivers/net/wireless/ieee80211/ieee80211_tx.c new file mode 100644 index 000000000..abf5401e4 --- /dev/null +++ b/drivers/net/wireless/ieee80211/ieee80211_tx.c @@ -0,0 +1,452 @@ +/****************************************************************************** + + Copyright(c) 2003 - 2004 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 + Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 + +******************************************************************************/ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "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 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 `----.----' + | + .- 'IP Packet' expands, if WEP enabled, to <--' + | + V + ,-----------------------. +Bytes | 4 | 0-2296 | 4 | + |-----|-----------|-----| +Desc. | IV | Encrypted | ICV | + | | IP Packet | | + `-----------------------' +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 inline int ieee80211_put_snap(u8 *data, u16 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 == 0x8137 || h_proto == 0x80f3) + oui = P802_1H_OUI; + else + oui = RFC1042_OUI; + snap->oui[0] = oui[0]; + snap->oui[1] = oui[1]; + snap->oui[2] = oui[2]; + + *(u16 *)(data + SNAP_SIZE) = htons(h_proto); + + return SNAP_SIZE + sizeof(u16); +} + +#ifdef CONFIG_IEEE80211_CRYPT +static inline 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; +#ifdef CONFIG_IEEE80211_WPA + struct ieee80211_hdr *header; + + if (ieee->tkip_countermeasures && + crypt && crypt->ops && strcmp(crypt->ops->name, "TKIP") == 0) { + header = (struct ieee80211_hdr *) frag->data; + if (net_ratelimit()) { + printk(KERN_DEBUG "%s: TKIP countermeasures: dropped " + "TX packet to " MAC_FMT "\n", + ieee->dev->name, MAC_ARG(header->addr1)); + } + return -1; + } +#endif + /* To encrypt, frame format is: + * IV (4 bytes), clear payload (including SNAP), ICV (4 bytes) */ + + // PR: FIXME: Copied from hostap. Check fragmentation/MSDU/MPDU encryption. + /* Host-based IEEE 802.11 fragmentation for TX is not yet supported, so + * call both MSDU and MPDU encryption functions from here. */ + atomic_inc(&crypt->refcnt); + res = 0; + if (crypt->ops->encrypt_msdu) + res = crypt->ops->encrypt_msdu(frag, hdr_len, crypt->priv); + if (res == 0 && 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; +} +#endif + + +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); +} + +struct ieee80211_txb *ieee80211_alloc_txb(int nr_frags, int txb_size, + int 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, sizeof(struct ieee80211_txb), 0); + 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); + if (unlikely(!txb->fragments[i])) { + i--; + break; + } + } + if (unlikely(i != nr_frags)) { + while (i >= 0) + dev_kfree_skb_any(txb->fragments[i--]); + kfree(txb); + return NULL; + } + return txb; +} + +/* SKBs are added to the ieee->tx_queue. */ +struct ieee80211_txb *ieee80211_skb_to_txb(struct ieee80211_device *ieee, + struct sk_buff *skb) +{ + struct ieee80211_txb *txb; + int i, bytes_per_frag, nr_frags, bytes_last_frag, frag_size; + unsigned long flags; + struct net_device_stats *stats = &ieee->stats; + int ether_type, encrypt; + int bytes, fc, hdr_len; + struct sk_buff *skb_frag; + struct ieee80211_hdr header; + u8 dest[ETH_ALEN], src[ETH_ALEN]; + +#ifdef CONFIG_IEEE80211_CRYPT + struct ieee80211_crypt_data* crypt; +#endif + + spin_lock_irqsave(&ieee->lock, flags); + + if (unlikely(skb->len < SNAP_SIZE + sizeof(u16))) { + printk(KERN_WARNING "%s: skb too small (%d).\n", + ieee->dev->name, skb->len); + goto failed; + } + + ether_type = ntohs(((struct ethhdr *)skb->data)->h_proto); + +#ifndef CONFIG_IEEE80211_CRYPT + encrypt = 0; +#else /* CONFIG_IEEE80211_CRYPT */ + crypt = ieee->crypt[ieee->tx_keyidx]; + +#ifndef CONFIG_IEEE80211_WPA + encrypt = (ether_type != ETH_P_PAE) && + ieee->host_encrypt && crypt && crypt->ops; + +#else /* CONFIG_IEEE80211_WPA */ + encrypt = !(ether_type == ETH_P_PAE && ieee->ieee_802_1x) && + ieee->host_encrypt && crypt && crypt->ops; + + if (!encrypt && ieee->ieee_802_1x && + ieee->drop_unencrypted && ether_type != ETH_P_PAE){ + stats->tx_dropped++; + /* FIXME: Allocate an empty txb and return it; this + * isn't the best code path since an alloc/free is + * required for no real reason except to return a + * special case success code... */ + txb = ieee80211_alloc_txb(0, ieee->fts, GFP_ATOMIC); + if (unlikely(!txb)) { + printk(KERN_WARNING + "%s: Could not allocate TXB\n", + ieee->dev->name); + goto failed; + } + + if (net_ratelimit()) { + printk(KERN_DEBUG "%s: dropped unencrypted TX data " + "frame (drop_unencrypted=1)\n", + ieee->dev->name); + } + + goto success; + } + +#endif /* CONFIG_IEEE80211_WPA */ + if (crypt && !encrypt && ether_type == ETH_P_PAE) + IEEE80211_DEBUG_EAP("TX: IEEE 802.11 - sending EAPOL frame\n"); +#endif /* CONFIG_IEEE80211_CRYPT */ + + if (encrypt) { + /* Save source and destination addresses */ + memcpy(&dest, skb->data, ETH_ALEN); + memcpy(&src, skb->data+ETH_ALEN, ETH_ALEN); + } + + /* 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); + + if (!ieee->tx_payload_only) { + if (encrypt) + fc = IEEE80211_FTYPE_DATA | IEEE80211_STYPE_DATA | + IEEE80211_FCTL_WEP; + else + fc = IEEE80211_FTYPE_DATA | IEEE80211_STYPE_DATA; + + if (ieee->iw_mode == IW_MODE_INFRA) { + fc |= IEEE80211_FCTL_TODS; + hdr_len = 24; + /* 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); + } + header.frame_ctl = cpu_to_le16(fc); + hdr_len = IEEE80211_3ADDR_SIZE; + } else + hdr_len = 0; + + /* 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. */ + if (is_multicast_ether_addr(dest) || + is_broadcast_ether_addr(dest)) + frag_size = MAX_FRAG_THRESHOLD - IEEE80211_3ADDR_SIZE; + else + frag_size = ieee->fts - IEEE80211_3ADDR_SIZE; + + bytes_per_frag = frag_size; + +#ifdef CONFIG_IEEE80211_CRYPT + /* Each fragment may need to have room for encryptiong pre/postfix */ + if (encrypt) + bytes_per_frag -= crypt->ops->extra_prefix_len + + crypt->ops->extra_postfix_len; +#endif + + /* 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; + + /* When we allocate the TXB we allocate enough space for the reserve + * and full fragment bytes (bytes_per_frag doesn't include prefix and + * postfix) */ + txb = ieee80211_alloc_txb(nr_frags, frag_size, GFP_ATOMIC); + if (unlikely(!txb)) { + printk(KERN_WARNING "%s: Could not allocate TXB\n", + ieee->dev->name); + goto failed; + } + txb->encrypted = encrypt; + txb->payload_size = bytes; + + for (i = 0; i < nr_frags; i++) { + skb_frag = txb->fragments[i]; + +#ifdef CONFIG_IEEE80211_CRYPT + if (encrypt) + skb_reserve(skb_frag, crypt->ops->extra_prefix_len); +#endif + + if (hdr_len) + memcpy(skb_put(skb_frag, hdr_len), &header, hdr_len); + + bytes = (i == nr_frags - 1) ? bytes_last_frag : bytes_per_frag; + + /* Put a SNAP header on the first fragment */ + if (i == 0) { + ieee80211_put_snap( + skb_put(skb_frag, SNAP_SIZE + sizeof(u16)), + ether_type); + bytes -= SNAP_SIZE + sizeof(u16); + } + + memcpy(skb_put(skb_frag, bytes), skb->data, bytes); + + /* Advance the SKB... */ + skb_pull(skb, bytes); + +#ifdef CONFIG_IEEE80211_CRYPT + /* Encryption routine will move the header forward in order + * to insert the IV between the header and the payload */ + if (encrypt) { + ieee80211_encrypt_fragment(ieee, skb_frag, hdr_len); + skb_pull(skb_frag, hdr_len); + } +#endif + } + + stats->tx_packets++; + stats->tx_bytes += txb->payload_size; + +#ifdef CONFIG_IEEE80211_WPA + success: +#endif + /* We are now done with the SKB provided to us */ + dev_kfree_skb_any(skb); + + spin_unlock_irqrestore(&ieee->lock, flags); + + return txb; + + failed: + stats->tx_errors++; + + return NULL; + +} + +EXPORT_SYMBOL(ieee80211_skb_to_txb); +EXPORT_SYMBOL(ieee80211_txb_free); diff --git a/drivers/net/wireless/ieee80211/ieee80211_wx.c b/drivers/net/wireless/ieee80211/ieee80211_wx.c new file mode 100644 index 000000000..ee46e9b3d --- /dev/null +++ b/drivers/net/wireless/ieee80211/ieee80211_wx.c @@ -0,0 +1,516 @@ +/****************************************************************************** + + Copyright(c) 2004 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 + + Copyright (c) 2002-2003, Jouni Malinen + + 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 + Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 + +******************************************************************************/ +#include +#include +#include +#include + +#include "ieee80211.h" +static const char ieee80211_modes[] = { + 'a', 'b', 'g', '?' +}; + +#if 0 +static u32 ieee80211_frequency(u8 channel, u8 mode) +{ + if (mode == IEEE_A) { + if (channel >= 8 && channel <= 161) + return 5000000 + 5000 * channel; + + if (channel >= 240 && channel <= 252) + return 4960000 + 5000 * (channel - 240); + } + + if (channel == 14) + return 2484000; + + if (channel >= 1 && channel <= 13) + return 2407000 + 5000 * channel; + + return 0; +} +#endif + +#define MAX_CUSTOM_LEN 64 +static inline char *ipw2100_translate_scan(struct ieee80211_device *ieee, + char *start, char *stop, + struct ieee80211_network *network) +{ + char custom[MAX_CUSTOM_LEN]; + char *p; + struct iw_event iwe; + int i, j; + u8 max_rate, 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(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(""); + start = iwe_stream_add_point(start, stop, &iwe, ""); + } else { + iwe.u.data.length = min(network->ssid_len, (u8)32); + start = iwe_stream_add_point(start, stop, &iwe, network->ssid); + } + + /* Add the protocol name */ + iwe.cmd = SIOCGIWNAME; + snprintf(iwe.u.name, IFNAMSIZ, "IEEE 802.11%c", ieee80211_modes[network->mode]); + start = iwe_stream_add_event(start, stop, &iwe, IW_EV_CHAR_LEN); + + /* Add mode */ + iwe.cmd = SIOCGIWMODE; + if (network->capability & + (WLAN_CAPABILITY_BSS | WLAN_CAPABILITY_IBSS)) { + if (network->capability & WLAN_CAPABILITY_BSS) + iwe.u.mode = IW_MODE_MASTER; + else + iwe.u.mode = IW_MODE_ADHOC; + + start = iwe_stream_add_event(start, stop, &iwe, + IW_EV_UINT_LEN); + } + + /* Add frequency/channel */ + iwe.cmd = SIOCGIWFREQ; +/* iwe.u.freq.m = ieee80211_frequency(network->channel, network->mode); + iwe.u.freq.e = 3; */ + iwe.u.freq.m = network->channel; + iwe.u.freq.e = 0; + iwe.u.freq.i = 0; + start = iwe_stream_add_event(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(start, stop, &iwe, network->ssid); + + /* Add basic and extended rates */ + max_rate = 0; + p = custom; + p += snprintf(p, MAX_CUSTOM_LEN - (p - custom), " Rates (Mb/s): "); + 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; + if (rate > max_rate) + max_rate = rate; + p += snprintf(p, MAX_CUSTOM_LEN - (p - custom), + "%d%s ", rate >> 1, (rate & 1) ? ".5" : ""); + } + for (; j < network->rates_ex_len; j++) { + rate = network->rates_ex[j] & 0x7F; + p += snprintf(p, MAX_CUSTOM_LEN - (p - custom), + "%d%s ", rate >> 1, (rate & 1) ? ".5" : ""); + if (rate > max_rate) + max_rate = rate; + } + + iwe.cmd = SIOCGIWRATE; + iwe.u.bitrate.fixed = iwe.u.bitrate.disabled = 0; + iwe.u.bitrate.value = max_rate * 500000; + start = iwe_stream_add_event(start, stop, &iwe, + IW_EV_PARAM_LEN); + + iwe.cmd = IWEVCUSTOM; + iwe.u.data.length = p - custom; + if (iwe.u.data.length) + start = iwe_stream_add_point(start, stop, &iwe, custom); + +#if 0 + /* Add quality statistics */ + /* TODO: Fix these values... */ + iwe.cmd = IWEVQUAL; + iwe.u.qual.qual = network->stats.signal; + iwe.u.qual.level = network->stats.rssi; + iwe.u.qual.noise = network->stats.noise; + iwe.u.qual.updated = network->stats.mask & IEEE80211_STATMASK_WEMASK; + + start = iwe_stream_add_event(start, stop, &iwe, IW_EV_QUAL_LEN); +#endif + iwe.cmd = IWEVCUSTOM; + p = custom; + + if (network->stats.mask & IEEE80211_STATMASK_RSSI) + p += snprintf(p, MAX_CUSTOM_LEN - (p - custom), + " RSSI: %-4d dBm ", + (s8)network->stats.rssi); + + if (network->stats.mask & IEEE80211_STATMASK_NOISE) + p += snprintf(p, MAX_CUSTOM_LEN - (p - custom), + " Noise: %-4d dBm ", + (s8)network->stats.noise); + + if (network->stats.mask & IEEE80211_STATMASK_SIGNAL) + p += snprintf(p, MAX_CUSTOM_LEN - (p - custom), + " Signal: %-4d dBm ", + (s8)network->stats.signal); + + iwe.u.data.length = p - custom; + if (iwe.u.data.length) + start = iwe_stream_add_point(start, stop, &iwe, custom); + +#ifdef CONFIG_IEEE80211_WPA + if (ieee->wpa_enabled && network->wpa_ie_len){ + char buf[MAX_WPA_IE_LEN * 2 + 30]; + + u8 *p = buf; + p += sprintf(p, "wpa_ie="); + for (i = 0; i < network->wpa_ie_len; i++) { + p += sprintf(p, "%02x", network->wpa_ie[i]); + } + + memset(&iwe, 0, sizeof(iwe)); + iwe.cmd = IWEVCUSTOM; + iwe.u.data.length = strlen(buf); + start = iwe_stream_add_point(start, stop, &iwe, buf); + } + + if (ieee->wpa_enabled && network->rsn_ie_len){ + char buf[MAX_WPA_IE_LEN * 2 + 30]; + + u8 *p = buf; + p += sprintf(p, "rsn_ie="); + for (i = 0; i < network->rsn_ie_len; i++) { + p += sprintf(p, "%02x", network->rsn_ie[i]); + } + + memset(&iwe, 0, sizeof(iwe)); + iwe.cmd = IWEVCUSTOM; + iwe.u.data.length = strlen(buf); + start = iwe_stream_add_point(start, stop, &iwe, buf); + } + +#endif /* CONFIG_IEEE80211_WPA */ + + /* 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: %lums ago", (jiffies - network->last_scanned) / (HZ / 100)); + iwe.u.data.length = p - custom; + if (iwe.u.data.length) + start = iwe_stream_add_point(start, stop, &iwe, custom); + + + return start; +} + +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; + + char *ev = extra; + char *stop = ev + IW_SCAN_MAX_DATA; + int i = 0; + + IEEE80211_DEBUG_WX("Getting scan\n"); + + spin_lock_irqsave(&ieee->lock, flags); + + list_for_each_entry(network, &ieee->network_list, list) { + i++; + if (ieee->scan_age == 0 || + (jiffies - network->last_scanned) < ieee->scan_age) + ev = ipw2100_translate_scan(ieee, ev, stop, network); + else + IEEE80211_DEBUG_SCAN( + "Not showing network '%s (" + MAC_FMT ")' due to age (%lums).\n", + escape_essid(network->ssid, + network->ssid_len), + MAC_ARG(network->bssid), + (jiffies - network->last_scanned) / (HZ / 100)); + } + + 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 0; +} + +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); +#ifndef CONFIG_IEEE80211_CRYPT + if (erq->flags & IW_ENCODE_DISABLED) + return 0; + return -EOPNOTSUPP; +#else + struct net_device *dev = ieee->dev; + struct ieee80211_security sec = { + .flags = 0 + }; + int i, key, key_provided, len; + struct ieee80211_crypt_data **crypt; + + 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.level = SEC_LEVEL_0; + sec.flags |= SEC_ENABLED | SEC_LEVEL; + } + + goto done; + } + + sec.enabled = 1; + sec.flags |= SEC_ENABLED; + + 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) { + struct ieee80211_crypt_data *new_crypt; + + /* take WEP into use */ + new_crypt = kmalloc(sizeof(struct ieee80211_crypt_data), + GFP_KERNEL); + if (new_crypt == NULL) + return -ENOMEM; + memset(new_crypt, 0, sizeof(struct ieee80211_crypt_data)); + 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.o\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; + (*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 + * explicitely set */ + if (key == sec.active_key) + sec.flags |= SEC_ACTIVE_KEY; + } else { + 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) { + ieee->tx_keyidx = key; + sec.active_key = key; + sec.flags |= SEC_ACTIVE_KEY; + } + } + + done: + 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 */ + + if (ieee->func && ieee->func->set_security) + ieee->func->set_security(ieee, &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->func->reset_port && + ieee->func->reset_port(dev)) { + printk(KERN_DEBUG "%s: reset_port failed\n", dev->name); + return -EINVAL; + } + return 0; +#endif +} + +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); +#ifndef CONFIG_IEEE80211_CRYPT + printk(KERN_WARNING "%s: Encryption requested but not enabled in " + "build.\n", ieee->dev->name); + erq->length = 0; + erq->flags = IW_ENCODE_DISABLED; + return 0; +#else + int len, key; + struct ieee80211_crypt_data *crypt; + + 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 (crypt == NULL || crypt->ops == NULL) { + erq->length = 0; + erq->flags |= IW_ENCODE_DISABLED; + return 0; + } + + if (strcmp(crypt->ops->name, "WEP") != 0) { + /* only WEP is supported with wireless extensions, so just + * report that encryption is used */ + erq->length = 0; + erq->flags |= IW_ENCODE_ENABLED; + return 0; + } + + len = crypt->ops->get_key(keybuf, WEP_KEY_LEN, NULL, crypt->priv); + erq->length = (len >= 0 ? len : 0); + + erq->flags |= IW_ENCODE_ENABLED; + + if (ieee->open_wep) + erq->flags |= IW_ENCODE_OPEN; + else + erq->flags |= IW_ENCODE_RESTRICTED; + + return 0; +#endif +} + +EXPORT_SYMBOL(ieee80211_wx_get_scan); +EXPORT_SYMBOL(ieee80211_wx_set_encode); +EXPORT_SYMBOL(ieee80211_wx_get_encode); diff --git a/drivers/net/wireless/ieee802_11.h b/drivers/net/wireless/ieee802_11.h index 07d626ea1..53dd5248f 100644 --- a/drivers/net/wireless/ieee802_11.h +++ b/drivers/net/wireless/ieee802_11.h @@ -76,4 +76,3 @@ struct ieee802_11_hdr { #define IEEE802_11_SCTL_SEQ 0xFFF0 #endif /* _IEEE802_11_H */ - diff --git a/drivers/net/wireless/ipw2100/LICENSE b/drivers/net/wireless/ipw2100/LICENSE new file mode 100644 index 000000000..5f297e5bb --- /dev/null +++ b/drivers/net/wireless/ipw2100/LICENSE @@ -0,0 +1,339 @@ + +"This software program is licensed subject to the GNU General Public License +(GPL). Version 2, June 1991, available at +" + +GNU General Public License + +Version 2, June 1991 + +Copyright (C) 1989, 1991 Free Software Foundation, Inc. +59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + +Everyone is permitted to copy and distribute verbatim copies of this license +document, but changing it is not allowed. + +Preamble + +The licenses for most software are designed to take away your freedom to +share and change it. By contrast, the GNU General Public License is intended +to guarantee your freedom to share and change free software--to make sure +the software is free for all its users. This General Public License applies +to most of the Free Software Foundation's software and to any other program +whose authors commit to using it. (Some other Free Software Foundation +software is covered by the GNU Library General Public License instead.) You +can apply it to your programs, too. + +When we speak of free software, we are referring to freedom, not price. Our +General Public Licenses are designed to make sure that you have the freedom +to distribute copies of free software (and charge for this service if you +wish), that you receive source code or can get it if you want it, that you +can change the software or use pieces of it in new free programs; and that +you know you can do these things. + +To protect your rights, we need to make restrictions that forbid anyone to +deny you these rights or to ask you to surrender the rights. These +restrictions translate to certain responsibilities for you if you distribute +copies of the software, or if you modify it. + +For example, if you distribute copies of such a program, whether gratis or +for a fee, you must give the recipients all the rights that you have. You +must make sure that they, too, receive or can get the source code. And you +must show them these terms so they know their rights. + +We protect your rights with two steps: (1) copyright the software, and (2) +offer you this license which gives you legal permission to copy, distribute +and/or modify the software. + +Also, for each author's protection and ours, we want to make certain that +everyone understands that there is no warranty for this free software. If +the software is modified by someone else and passed on, we want its +recipients to know that what they have is not the original, so that any +problems introduced by others will not reflect on the original authors' +reputations. + +Finally, any free program is threatened constantly by software patents. We +wish to avoid the danger that redistributors of a free program will +individually obtain patent licenses, in effect making the program +proprietary. To prevent this, we have made it clear that any patent must be +licensed for everyone's free use or not licensed at all. + +The precise terms and conditions for copying, distribution and modification +follow. + +TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + +0. This License applies to any program or other work which contains a notice + placed by the copyright holder saying it may be distributed under the + terms of this General Public License. The "Program", below, refers to any + such program or work, and a "work based on the Program" means either the + Program or any derivative work under copyright law: that is to say, a + work containing the Program or a portion of it, either verbatim or with + modifications and/or translated into another language. (Hereinafter, + translation is included without limitation in the term "modification".) + Each licensee is addressed as "you". + + Activities other than copying, distribution and modification are not + covered by this License; they are outside its scope. The act of running + the Program is not restricted, and the output from the Program is covered + only if its contents constitute a work based on the Program (independent + of having been made by running the Program). Whether that is true depends + on what the Program does. + +1. You may copy and distribute verbatim copies of the Program's source code + as you receive it, in any medium, provided that you conspicuously and + appropriately publish on each copy an appropriate copyright notice and + disclaimer of warranty; keep intact all the notices that refer to this + License and to the absence of any warranty; and give any other recipients + of the Program a copy of this License along with the Program. + + You may charge a fee for the physical act of transferring a copy, and you + may at your option offer warranty protection in exchange for a fee. + +2. You may modify your copy or copies of the Program or any portion of it, + thus forming a work based on the Program, and copy and distribute such + modifications or work under the terms of Section 1 above, provided that + you also meet all of these conditions: + + * a) You must cause the modified files to carry prominent notices stating + that you changed the files and the date of any change. + + * b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any part + thereof, to be licensed as a whole at no charge to all third parties + under the terms of this License. + + * c) If the modified program normally reads commands interactively when + run, you must cause it, when started running for such interactive + use in the most ordinary way, to print or display an announcement + including an appropriate copyright notice and a notice that there is + no warranty (or else, saying that you provide a warranty) and that + users may redistribute the program under these conditions, and + telling the user how to view a copy of this License. (Exception: if + the Program itself is interactive but does not normally print such + an announcement, your work based on the Program is not required to + print an announcement.) + + These requirements apply to the modified work as a whole. If identifiable + sections of that work are not derived from the Program, and can be + reasonably considered independent and separate works in themselves, then + this License, and its terms, do not apply to those sections when you + distribute them as separate works. But when you distribute the same + sections as part of a whole which is a work based on the Program, the + distribution of the whole must be on the terms of this License, whose + permissions for other licensees extend to the entire whole, and thus to + each and every part regardless of who wrote it. + + Thus, it is not the intent of this section to claim rights or contest + your rights to work written entirely by you; rather, the intent is to + exercise the right to control the distribution of derivative or + collective works based on the Program. + + In addition, mere aggregation of another work not based on the Program + with the Program (or with a work based on the Program) on a volume of a + storage or distribution medium does not bring the other work under the + scope of this License. + +3. You may copy and distribute the Program (or a work based on it, under + Section 2) in object code or executable form under the terms of Sections + 1 and 2 above provided that you also do one of the following: + + * a) Accompany it with the complete corresponding machine-readable source + code, which must be distributed under the terms of Sections 1 and 2 + above on a medium customarily used for software interchange; or, + + * b) Accompany it with a written offer, valid for at least three years, + to give any third party, for a charge no more than your cost of + physically performing source distribution, a complete machine- + readable copy of the corresponding source code, to be distributed + under the terms of Sections 1 and 2 above on a medium customarily + used for software interchange; or, + + * c) Accompany it with the information you received as to the offer to + distribute corresponding source code. (This alternative is allowed + only for noncommercial distribution and only if you received the + program in object code or executable form with such an offer, in + accord with Subsection b above.) + + The source code for a work means the preferred form of the work for + making modifications to it. For an executable work, complete source code + means all the source code for all modules it contains, plus any + associated interface definition files, plus the scripts used to control + compilation and installation of the executable. However, as a special + exception, the source code distributed need not include anything that is + normally distributed (in either source or binary form) with the major + components (compiler, kernel, and so on) of the operating system on which + the executable runs, unless that component itself accompanies the + executable. + + If distribution of executable or object code is made by offering access + to copy from a designated place, then offering equivalent access to copy + the source code from the same place counts as distribution of the source + code, even though third parties are not compelled to copy the source + along with the object code. + +4. You may not copy, modify, sublicense, or distribute the Program except as + expressly provided under this License. Any attempt otherwise to copy, + modify, sublicense or distribute the Program is void, and will + automatically terminate your rights under this License. However, parties + who have received copies, or rights, from you under this License will not + have their licenses terminated so long as such parties remain in full + compliance. + +5. You are not required to accept this License, since you have not signed + it. However, nothing else grants you permission to modify or distribute + the Program or its derivative works. These actions are prohibited by law + if you do not accept this License. Therefore, by modifying or + distributing the Program (or any work based on the Program), you + indicate your acceptance of this License to do so, and all its terms and + conditions for copying, distributing or modifying the Program or works + based on it. + +6. Each time you redistribute the Program (or any work based on the + Program), the recipient automatically receives a license from the + original licensor to copy, distribute or modify the Program subject to + these terms and conditions. You may not impose any further restrictions + on the recipients' exercise of the rights granted herein. You are not + responsible for enforcing compliance by third parties to this License. + +7. If, as a consequence of a court judgment or allegation of patent + infringement or for any other reason (not limited to patent issues), + conditions are imposed on you (whether by court order, agreement or + otherwise) that contradict the conditions of this License, they do not + excuse you from the conditions of this License. If you cannot distribute + so as to satisfy simultaneously your obligations under this License and + any other pertinent obligations, then as a consequence you may not + distribute the Program at all. For example, if a patent license would + not permit royalty-free redistribution of the Program by all those who + receive copies directly or indirectly through you, then the only way you + could satisfy both it and this License would be to refrain entirely from + distribution of the Program. + + If any portion of this section is held invalid or unenforceable under any + particular circumstance, the balance of the section is intended to apply + and the section as a whole is intended to apply in other circumstances. + + It is not the purpose of this section to induce you to infringe any + patents or other property right claims or to contest validity of any + such claims; this section has the sole purpose of protecting the + integrity of the free software distribution system, which is implemented + by public license practices. Many people have made generous contributions + to the wide range of software distributed through that system in + reliance on consistent application of that system; it is up to the + author/donor to decide if he or she is willing to distribute software + through any other system and a licensee cannot impose that choice. + + This section is intended to make thoroughly clear what is believed to be + a consequence of the rest of this License. + +8. If the distribution and/or use of the Program is restricted in certain + countries either by patents or by copyrighted interfaces, the original + copyright holder who places the Program under this License may add an + explicit geographical distribution limitation excluding those countries, + so that distribution is permitted only in or among countries not thus + excluded. In such case, this License incorporates the limitation as if + written in the body of this License. + +9. The Free Software Foundation may publish revised and/or new versions of + the General Public License from time to time. Such new versions will be + similar in spirit to the present version, but may differ in detail to + address new problems or concerns. + + Each version is given a distinguishing version number. If the Program + specifies a version number of this License which applies to it and "any + later version", you have the option of following the terms and + conditions either of that version or of any later version published by + the Free Software Foundation. If the Program does not specify a version + number of this License, you may choose any version ever published by the + Free Software Foundation. + +10. If you wish to incorporate parts of the Program into other free programs + whose distribution conditions are different, write to the author to ask + for permission. For software which is copyrighted by the Free Software + Foundation, write to the Free Software Foundation; we sometimes make + exceptions for this. Our decision will be guided by the two goals of + preserving the free status of all derivatives of our free software and + of promoting the sharing and reuse of software generally. + + NO WARRANTY + +11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY + FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN + OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES + PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER + EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE + ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH + YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL + NECESSARY SERVICING, REPAIR OR CORRECTION. + +12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING + WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR + REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR + DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL + DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM + (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED + INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF + THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR + OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + +END OF TERMS AND CONDITIONS + +How to Apply These Terms to Your New Programs + +If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it free +software which everyone can redistribute and change under these terms. + +To do so, attach the following notices to the program. It is safest to +attach them to the start of each source file to most effectively convey the +exclusion of warranty; and each file should have at least the "copyright" +line and a pointer to where the full notice is found. + +one line to give the program's name and an idea of what it does. +Copyright (C) yyyy name of author + +This program is free software; you can redistribute it and/or modify it +under the terms of the 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. + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this when +it starts in an interactive mode: + +Gnomovision version 69, Copyright (C) year name of author Gnomovision comes +with ABSOLUTELY NO WARRANTY; for details type 'show w'. This is free +software, and you are welcome to redistribute it under certain conditions; +type 'show c' for details. + +The hypothetical commands 'show w' and 'show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may be +called something other than 'show w' and 'show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + +Yoyodyne, Inc., hereby disclaims all copyright interest in the program +'Gnomovision' (which makes passes at compilers) written by James Hacker. + +signature of Ty Coon, 1 April 1989 +Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Library General Public +License instead of this License. diff --git a/drivers/net/wireless/ipw2100/Makefile b/drivers/net/wireless/ipw2100/Makefile new file mode 100644 index 000000000..34503b1ff --- /dev/null +++ b/drivers/net/wireless/ipw2100/Makefile @@ -0,0 +1,15 @@ +# +# Makefile for the Linux Wireless network device drivers. +# +# Original makefile by Peter Johanson + +EXTRA_CFLAGS += -I$(TOPDIR)/drivers/net/wireless/ieee80211 + +ifeq ($(CONFIG_IPW_DEBUG),y) + EXTRA_CFLAGS += -g -Wa,-adhlms=$@.lst +endif + +list-m := +list-$(CONFIG_IPW2100) += ipw2100 + +obj-$(CONFIG_IPW2100) += ipw2100.o diff --git a/drivers/net/wireless/ipw2100/ipw2100.c b/drivers/net/wireless/ipw2100/ipw2100.c new file mode 100644 index 000000000..0a787d7db --- /dev/null +++ b/drivers/net/wireless/ipw2100/ipw2100.c @@ -0,0 +1,8857 @@ +/****************************************************************************** + + Copyright(c) 2003 - 2004 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 + 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 + + + Portions of this file are based on the Host AP project, + Copyright (c) 2001-2002, SSH Communications Security Corp and Jouni Malinen + + Copyright (c) 2002-2003, Jouni Malinen + + 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 X__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 X__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 + +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#define __KERNEL_SYSCALLS__ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifndef CONFIG_IPW2100_LEGACY_FW_LOAD +#include +#endif +#include +#include + +#include "ipw2100.h" + +#define IPW2100_VERSION "1.0.0" + +#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-2004 Intel Corporation" + + +/* Debugging stuff */ +#ifdef CONFIG_IPW_DEBUG +#define CONFIG_IPW2100_RX_DEBUG /* Reception debugging */ +#endif + +MODULE_DESCRIPTION(DRV_DESCRIPTION); +MODULE_AUTHOR(DRV_COPYRIGHT); +MODULE_LICENSE("GPL"); + +static int debug = 0; +static char *ifname = NULL; +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 +module_param(debug, int, 0444); +module_param(ifname, charp, 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(ifname, "interface name (default 'eth%d')"); +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])"); + +u32 ipw2100_debug_level = IPW_DL_NONE; + +#ifdef CONFIG_IPW_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 X__ipw2100_tx_send_commands(struct ipw2100_priv *priv); +static void X__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 inline void read_register(struct net_device *dev, u32 reg, u32 *val) +{ + *val = readl((void *)(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 *)(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 *)(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 *)(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 *)(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 *)(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 inline 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 inline 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 *)(dev->base_addr + IPW_REG_DOA_DEBUG_AREA_START)) + == IPW_DATA_DOA_DEBUG_VALUE)); +} + +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) { + IPW_DEBUG_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; + + IPW_DEBUG_WARNING(DRV_NAME + ": ordinal buffer length too small, need %d\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; + } + + IPW_DEBUG_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 inline 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_work(priv->workqueue, &priv->reset_work); + + 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 hardware 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); + + X__ipw2100_tx_send_commands(priv); + X__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", + HOST_COMPLETE_TIMEOUT / (HZ / 100)); + priv->fatal_error = IPW2100_ERR_MSG_TIMEOUT; + priv->status &= ~STATUS_CMD_ACTIVE; + schedule_reset(priv); + return -EIO; + } + + if (priv->fatal_error) { + IPW_DEBUG_WARNING("%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 */ + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout(HZ / 100); + + 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 */ + +//#if CONFIG_IPW2100_D0ENABLED + /* 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); +//#endif + + return 0; +} + +/********************************************************************* + Procedure : ipw2100_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) { + IPW_DEBUG_ERROR("%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) { + IPW_DEBUG_ERROR("%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 inline int rf_kill_active(struct ipw2100_priv *priv) +{ +#define MAX_RF_KILL_CHECKS 5 +#define RF_KILL_CHECK_DELAY 40 +#define RF_KILL_CHECK_THRESHOLD 3 + + 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, ®); + 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) +{ +#define IPW_WAIT_FW_INIT_COMPLETE_DELAY (40 * HZ / 1000) + 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)) { + IPW_DEBUG_ERROR("%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 { + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout(IPW_WAIT_FW_INIT_COMPLETE_DELAY); + /* 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) { + IPW_DEBUG_WARNING("%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_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, ®); + + 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_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; + + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout(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; + + IPW_DEBUG_HC("HOST_COMPLETE\n"); + + if (priv->status & STATUS_ENABLED) + return 0; + + err = ipw2100_hw_send_command(priv, &cmd); + if (err) { + IPW_DEBUG_INFO("Failed to send HOST_COMPLETE command\n"); + return err; + } + + 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); + return err; + } + + if (priv->stop_hang_check) { + priv->stop_hang_check = 0; + queue_delayed_work(priv->workqueue, &priv->hang_check, 2 * HZ); + } + + return 0; +} + +static int ipw2100_hw_stop_adapter(struct ipw2100_priv *priv) +{ +#define HW_POWER_DOWN_DELAY (HZ / 10) + + 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) + IPW_DEBUG_WARNING("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-standy 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) + IPW_DEBUG_WARNING( + "%s: Power down command failed: Error %d\n", + priv->net_dev->name, err); + else { + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout(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, ®); + + if (reg & IPW_AUX_HOST_RESET_REG_MASTER_DISABLED) + break; + } + + if (i == 0) + IPW_DEBUG_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; + + IPW_DEBUG_HC("CARD_DISABLE\n"); + + if (!(priv->status & STATUS_ENABLED)) + return 0; + + /* Make sure we clear the associated state */ + priv->status &= ~STATUS_ASSOCIATED; + + if (!priv->stop_hang_check) { + priv->stop_hang_check = 1; + cancel_delayed_work(&priv->hang_check); + } + + err = ipw2100_hw_send_command(priv, &cmd); + if (err) { + IPW_DEBUG_WARNING("exit - failed to send CARD_DISABLE command\n"); + return err; + } + + err = ipw2100_wait_for_card_state(priv, IPW_HW_STATE_DISABLED); + if (err) { + IPW_DEBUG_WARNING("exit - card failed to change to DISABLED\n"); + return err; + } + + IPW_DEBUG_INFO("TODO: implement scan state machine\n"); + + + return 0; +} + +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->sec.flags & SEC_ENABLED) && priv->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; +} + +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"); + err = ipw2100_hw_send_command(priv, &cmd); + + priv->status |= STATUS_SCANNING; + IPW_DEBUG_INFO("exit\n"); + + return err; +} + +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; + } + + /* 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)) { + IPW_DEBUG_WARNING("%s: Could not cycle adapter.\n", + priv->net_dev->name); + rc = 1; + goto exit; + } + } else + priv->status |= STATUS_POWERED; + + /* Load the firmeware, start the clocks, etc. */ + if (ipw2100_start_adapter(priv)) { + IPW_DEBUG_ERROR("%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)) { + IPW_DEBUG_ERROR("%s: Failed to determine HW features.\n", + priv->net_dev->name); + rc = 1; + goto exit; + } + + lock = LOCK_NONE; + if (ipw2100_set_ordinal(priv, IPW_ORD_PERS_DB_LOCK, &lock, &ord_len)) { + IPW_DEBUG_ERROR("%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, 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)) { + IPW_DEBUG_ERROR("%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)) { + IPW_DEBUG_ERROR( + "%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 = netdev_priv(dev); + return ipw2100_up(priv, 1); +} + +static void ipw2100_down(struct ipw2100_priv *priv) +{ + unsigned long flags; + + /* 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)) + IPW_DEBUG_ERROR("%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); + +#ifdef ACPI_CSTATE_LIMIT_DEFINED + if (priv->config & CFG_C3_DISABLED) { + IPW_DEBUG_INFO(DRV_NAME ": Resetting C3 transitions.\n"); + acpi_set_cstate_limit(priv->cstate_limit); + priv->config &= ~CFG_C3_DISABLED; + } +#endif + + priv->status &= ~STATUS_ASSOCIATED; + netif_carrier_off(priv->net_dev); + netif_stop_queue(priv->net_dev); +} + +void ipw2100_reset_adapter(struct ipw2100_priv *priv) +{ + unsigned long flags; + spin_lock_irqsave(&priv->low_lock, flags); + IPW_DEBUG_INFO(DRV_NAME ": %s: Restarting adapter.\n", + priv->net_dev->name); + priv->resets++; + priv->status &= ~STATUS_ASSOCIATED; + 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); + + /* stop timed checks so that they don't interfere with reset */ + priv->stop_hang_check = 1; + cancel_delayed_work(&priv->hang_check); + + ipw2100_up(priv, 0); + +} + + +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]; + + /* + * 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=" + MAC_FMT ")\n", + priv->net_dev->name, escape_essid(essid, essid_len), + txratename, chan, MAC_ARG(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_ASSOCIATED; + priv->connect_start = get_seconds(); + + netif_carrier_on(priv->net_dev); + if (netif_queue_stopped(priv->net_dev)) { + IPW_DEBUG_INFO("Waking net queue.\n"); + netif_wake_queue(priv->net_dev); + } else { + IPW_DEBUG_INFO("Starting net queue.\n"); + netif_start_queue(priv->net_dev); + } + + queue_delayed_work(priv->workqueue, &priv->wx_event_work, HZ / 10); +} + + +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((char*)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) +{ + IPW_DEBUG(IPW_DL_NOTIF | IPW_DL_STATE | IPW_DL_ASSOC, + "disassociated: '%s' " MAC_FMT " \n", + escape_essid(priv->essid, priv->essid_len), + MAC_ARG(priv->bssid)); + + if (priv->status & STATUS_STOPPING) { + IPW_DEBUG_INFO("Card is stopping itself, discard ASSN_LOST.\n"); + return; + } + priv->status &= ~STATUS_ASSOCIATED; + + 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_work(priv->workqueue, &priv->security_work); + + queue_work(priv->workqueue, &priv->wx_event_work); +} + +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; + +#ifdef ACPI_CSTATE_LIMIT_DEFINED + if (priv->config & CFG_C3_DISABLED) { + IPW_DEBUG_INFO(DRV_NAME ": Resetting C3 transitions.\n"); + acpi_set_cstate_limit(priv->cstate_limit); + priv->config &= ~CFG_C3_DISABLED; + } +#endif + + /* If not already running, we now fire up a timer that will poll + * the state of the RF switch on the hardware so we can re-enable + * the firmware if the switch is enabled */ + if (priv->stop_rf_kill) { + priv->stop_rf_kill = 0; + queue_delayed_work(priv->workqueue, &priv->rf_kill, HZ); + } +} + +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; +} + +#ifdef CONFIG_IPW_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_IPW_DEBUG */ + +static void isr_indicate_scanning(struct ipw2100_priv *priv, u32 status) +{ + priv->status |= STATUS_SCANNING; +} + +const struct ipw2100_status_indicator status_handlers[] = { + IPW2100_HANDLER(IPW_STATE_INITIALIZED, 0), + IPW2100_HANDLER(IPW_STATE_COUNTRY_FOUND, 0), + IPW2100_HANDLER(IPW_STATE_ASSOCIATED, isr_indicate_associated), + IPW2100_HANDLER(IPW_STATE_ASSN_LOST, isr_indicate_association_lost), + IPW2100_HANDLER(IPW_STATE_ASSN_CHANGED, 0), + IPW2100_HANDLER(IPW_STATE_SCAN_COMPLETE, isr_scan_complete), + IPW2100_HANDLER(IPW_STATE_ENTERED_PSP, 0), + IPW2100_HANDLER(IPW_STATE_LEFT_PSP, 0), + IPW2100_HANDLER(IPW_STATE_RF_KILL, isr_indicate_rf_kill), + IPW2100_HANDLER(IPW_STATE_DISABLED, 0), + IPW2100_HANDLER(IPW_STATE_POWER_DOWN, 0), + IPW2100_HANDLER(IPW_STATE_SCANNING, isr_indicate_scanning), + IPW2100_HANDLER(-1, 0) +}; + + +static void isr_status_change(struct ipw2100_priv *priv, int status) +{ + int i; + + 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_IPW_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_IPW_DEBUG +const char *frame_types[] = { + "COMMAND_STATUS_VAL", + "STATUS_CHANGE_VAL", + "P80211_DATA_VAL", + "P8023_DATA_VAL", + "HOST_NOTIFICATION_VAL" +}; +#endif + + +static inline 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 inline 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] = (u8*)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 inline 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; +} + +static inline 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; +} + +/* + * + * 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 CONFIG_IPW2100_RX_DEBUG +u8 packet_data[IPW_RX_NIC_BUFFER_LENGTH]; +#endif + +static inline void ipw2100_corruption_detected(struct ipw2100_priv *priv, + int i) +{ +#ifdef CONFIG_IPW_DEBUG_C3 + struct ipw2100_status *status = &priv->status_queue.drv[i]; + u32 match, reg; + int j; +#endif +#ifdef ACPI_CSTATE_LIMIT_DEFINED + int limit; +#endif + + IPW_DEBUG_INFO(DRV_NAME ": PCI latency error detected at " + "0x%04X.\n", i * sizeof(struct ipw2100_status)); + +#ifdef ACPI_CSTATE_LIMIT_DEFINED + IPW_DEBUG_INFO(DRV_NAME ": Disabling C3 transitions.\n"); + limit = acpi_get_cstate_limit(); + if (limit > 2) { + priv->cstate_limit = limit; + acpi_set_cstate_limit(2); + priv->config |= CFG_C3_DISABLED; + } +#endif + +#ifdef CONFIG_IPW_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, ®); + + 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 inline 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"); + } + + if (unlikely(priv->ieee->iw_mode == IW_MODE_MONITOR && + 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); + + skb_put(packet->skb, status->frame_size); + +#ifdef CONFIG_IPW2100_RX_DEBUG + /* Make a copy of the frame so we can dump it to the logs if + * ieee80211_rx fails */ + memcpy(packet_data, packet->skb->data, + min(status->frame_size, IPW_RX_NIC_BUFFER_LENGTH)); +#endif + + if (!ieee80211_rx(priv->ieee, packet->skb, stats)) { +#ifdef CONFIG_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))) { + 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; +} + +static inline 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_PROMISC + return 0; +#else + switch (WLAN_FC_GET_TYPE(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 inline 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.signal = sq->drv[i].rssi + IPW2100_RSSI_TO_DBM; + stats.len = sq->drv[i].frame_size; + + stats.mask = 0; + if (stats.noise != 0) + stats.mask |= IEEE80211_STATMASK_NOISE; + if (stats.rssi != 0) + stats.mask |= IEEE80211_STATMASK_RSSI; + if (stats.signal != 0) + stats.mask |= IEEE80211_STATMASK_SIGNAL; + if (stats.rate != 0) + stats.mask |= IEEE80211_STATMASK_RATE; + 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: + 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_PROMISC + if (priv->ieee->iw_mode == IW_MODE_MONITOR) { + isr_rx(priv, i, &stats); + break; + } +#endif + if (stats.len < sizeof(u->rx_data.header)) + break; + switch (WLAN_FC_GET_TYPE(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 inline 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: + IPW_DEBUG_WARNING("%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) + IPW_DEBUG_WARNING("%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_IPW_DEBUG + { + int i = txq->oldest; + IPW_DEBUG_TX( + "TX%d V=%p P=%p T=%p L=%d\n", i, + &txq->drv[i], + (void*)txq->nic + i * sizeof(struct ipw2100_bd), + (void*)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=%p T=%p L=%d\n", i, + &txq->drv[i], + (void*)txq->nic + i * + sizeof(struct ipw2100_bd), + (void*)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) + IPW_DEBUG_WARNING("%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 */ + priv->ieee->stats.tx_packets++; + 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); + } + + priv->ieee->stats.tx_bytes += packet->info.d_struct.txb->payload_size; + 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_queue_stopped(priv->net_dev)) { + IPW_DEBUG_INFO(KERN_INFO + "%s: Waking net queue.\n", + priv->net_dev->name); + 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) + IPW_DEBUG_WARNING("%s: Queue mismatch. " + "Expecting COMMAND TBD but pulled " + "something else: ids %d=%d.\n", + priv->net_dev->name, txq->oldest, packet->index); + +#ifdef CONFIG_IPW_DEBUG + if (packet->info.c_struct.cmd->host_command_reg < + sizeof(command_types) / sizeof(*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) { + IPW_DEBUG_WARNING( + "%s: Driver is running slow (%d iters).\n", + priv->net_dev->name, i); + } +} + + +static void X__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); + } +} + + +/* + * X__ipw2100_tx_send_data + * + */ +static void X__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; + + 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; + + 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_IPW_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; + + tbd->host_addr = pci_map_single( + priv->pci_dev, + packet->info.d_struct.txb->fragments[i]->data, + 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) { + IPW_DEBUG_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) { + IPW_DEBUG_ERROR("***** 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); + X__ipw2100_tx_send_commands(priv); + X__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 pt_regs *regs) +{ + 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 */ + IPW_DEBUG_WARNING("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 sk_buff *skb, struct net_device *dev) +{ + struct ipw2100_priv *priv = netdev_priv(dev); + struct list_head *element; + struct ipw2100_data_header *header = NULL; + 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); + + header = packet->info.d_struct.data; + memcpy(header->dst_addr, skb->data, ETH_ALEN); + memcpy(header->src_addr, skb->data + ETH_ALEN, ETH_ALEN); + + packet->info.d_struct.txb = ieee80211_skb_to_txb(priv->ieee, skb); + if (packet->info.d_struct.txb == NULL) { + IPW_DEBUG_DROP("Failed to Tx packet\n"); + priv->ieee->stats.tx_errors++; + goto fail_unlock; + } + + if (packet->info.d_struct.txb->nr_frags == 0) { + /* No fragments provided; packet transform was successful, + * and then dropped */ + + /* This packet was processed successfully, even though it was + * never passed to the HW, so we reset the NIC watchdog + * timer to keep it from kicking in */ + priv->net_dev->trans_start = jiffies; + ieee80211_txb_free(packet->info.d_struct.txb); + packet->info.d_struct.txb = NULL; + goto success; + } + + header->host_command_reg = SEND; + header->host_command_reg1 = 0; + + /* For now we only support host based encryption */ + header->needs_encryption = 0; + header->encrypted = packet->info.d_struct.txb->encrypted; + if (packet->info.d_struct.txb->nr_frags > 1) + header->fragment_size = packet->info.d_struct.txb->frag_size; + else + header->fragment_size = 0; + + 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); + + X__ipw2100_tx_send_data(priv); + + success: + 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) { + IPW_DEBUG_ERROR("%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) { + IPW_DEBUG_ERROR( + "%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, 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, char *buf) +{ + struct ipw2100_priv *p = (struct ipw2100_priv *)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, char *buf) +{ + struct ipw2100_priv *p = (struct ipw2100_priv *)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, char *buf) +{ + struct ipw2100_priv *p = (struct ipw2100_priv *)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 } +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 } +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 } +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, 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 < (sizeof(hw_data) / sizeof(*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, 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 < (sizeof(nic_data) / sizeof(*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, 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, 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; + + 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, char *buf) +{ + struct ipw2100_priv *priv = dev_get_drvdata(d); + u32 val = 0; + int len = 0; + u32 val_len; + static int loop = 0; + + if (loop >= sizeof(ord_data) / sizeof(*ord_data)) + loop = 0; + + /* sysfs provides us PAGE_SIZE buffer */ + while (len < PAGE_SIZE - 128 && + loop < (sizeof(ord_data) / sizeof(*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, 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_IPW_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); + + +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) { + IPW_DEBUG_ERROR("%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_PROMISC + case IW_MODE_MONITOR: + priv->last_mode = priv->ieee->iw_mode; + priv->net_dev->type = ARPHRD_IEEE80211; + break; +#endif /* CONFIG_IPW2100_PROMISC */ + } + + priv->ieee->iw_mode = mode; + +#ifdef CONFIG_PM + /* Indicate ipw2100_download_firmware download firmware + * from disk instead of memory. */ + ipw2100_firmware.version = 0; +#endif + + priv->reset_backoff = 0; + ipw2100_reset_adapter(priv); + + return 0; +} + +static ssize_t show_internals(struct device *d, 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); + + 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, 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; + + 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: %02x:%02x:%02x:%02x:%02x:%02x\n", + bssid[0], bssid[1], bssid[2], + bssid[3], bssid[4], bssid[5]); + out += sprintf(out, "Channel: %d\n", chan); + + return out - buf; +} +static DEVICE_ATTR(bssinfo, S_IRUGO, show_bssinfo, NULL); + + + + +#ifdef CONFIG_IPW_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(DRV_NAME + ": %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_IPW_DEBUG */ + + +static ssize_t show_fatal_error(struct device *d, 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, 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, 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, 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; + + 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, 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"); + + 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 */ + cancel_delayed_work(&priv->rf_kill); + queue_delayed_work(priv->workqueue, &priv->rf_kill, + HZ); + } else + schedule_reset(priv); + } + + return 1; +} + +static ssize_t store_rf_kill(struct device *d, 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, 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); + 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) { + IPW_DEBUG_ERROR("%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) { + IPW_DEBUG_ERROR("%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 mac[ETH_ALEN]; + + int err; + + err = ipw2100_get_ordinal(priv, IPW_ORD_STAT_ADAPTER_MAC, + mac, &length); + if (err) { + IPW_DEBUG_INFO("MAC address read failed\n"); + return -EIO; + } + IPW_DEBUG_INFO("card MAC is %02X:%02X:%02X:%02X:%02X:%02X\n", + mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]); + + memcpy(priv->net_dev->dev_addr, mac, ETH_ALEN); + + return 0; +} + +/******************************************************************** + * + * Firmware Commands + * + ********************************************************************/ + +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; +} + +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) { + IPW_DEBUG_ERROR("%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; +} + + +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; +} + +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; +} + +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; +} + +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 CONFIG_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; +} + + +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 + +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; +} + +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; +} + + +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_IPW_DEBUG + if (bssid != NULL) + IPW_DEBUG_HC( + "MANDATORY_BSSID: %02X:%02X:%02X:%02X:%02X:%02X\n", + bssid[0], bssid[1], bssid[2], bssid[3], bssid[4], + bssid[5]); + else + IPW_DEBUG_HC("MANDATORY_BSSID: \n"); +#endif + /* if BSSID is empty then we disable mandatory bssid mode */ + if (bssid != NULL) + memcpy((u8 *)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; +} + +#ifdef CONFIG_IEEE80211_WPA +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; +} +#endif + +/* + * Pseudo code for setting up wpa_frame: + */ +#if 0 +void x(struct ieee80211_assoc_frame *wpa_assoc) +{ + struct ipw2100_wpa_assoc_frame frame; + frame->fixed_ie_mask = IPW_WPA_CAPABILTIES | + IPW_WPA_LISTENINTERVAL | + IPW_WPA_AP_ADDRESS; + frame->capab_info = wpa_assoc->capab_info; + frame->lisen_interval = wpa_assoc->listent_interval; + memcpy(frame->current_ap, wpa_assoc->current_ap, ETH_ALEN); + + /* UNKNOWN -- I'm not postivive about this part; don't have any WPA + * setup here to test it with. + * + * Walk the IEs in the wpa_assoc and figure out the total size of all + * that data. Stick that into frame->var_ie_len. Then memcpy() all of + * the IEs from wpa_frame into frame. + */ + frame->var_ie_len = calculate_ie_len(wpa_assoc); + memcpy(frame->var_ie, wpa_assoc->variable, frame->var_ie_len); + + ipw2100_set_wpa_ie(priv, &frame, 0); +} +#endif + + + + +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)); + +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) { + 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\n", + security->auth_mode, security->allowed_ciphers); + + 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; +} + +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; + + cmd.host_command_parameters[0] = tx_power; + + if (priv->ieee->iw_mode == IW_MODE_ADHOC) + err = ipw2100_hw_send_command(priv, &cmd); + if (!err) + priv->tx_power = tx_power; + + return 0; +} + +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) { + IPW_DEBUG_ERROR("%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 occuring. 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) { + IPW_DEBUG_ERROR("%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; +} + +#if 0 +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[0] = 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) { + IPW_DEBUG_ERROR("%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; +} +#endif + + +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->sec.enabled) { + err = ipw2100_set_security_information( + priv, IPW_AUTH_OPEN, SEC_LEVEL_0, 0, 1); + } else { + auth_mode = IPW_AUTH_OPEN; + if ((priv->sec.flags & SEC_AUTH_MODE) && + (priv->sec.auth_mode == WLAN_AUTH_SHARED_KEY)) + auth_mode = IPW_AUTH_SHARED; + + sec_level = SEC_LEVEL_0; + if (priv->sec.flags & SEC_LEVEL) + sec_level = priv->sec.level; + + use_group = 0; + if (priv->sec.flags & SEC_UNICAST_GROUP) + use_group = priv->sec.unicast_uses_group; + + err = ipw2100_set_security_information( + priv, auth_mode, sec_level, use_group, 1); + } + + if (err) + goto exit; + + if (priv->sec.enabled) { + for (i = 0; i < 4; i++) { + if (!(priv->sec.flags & (1 << i))) { + memset(priv->sec.keys[i], 0, WEP_KEY_LEN); + priv->sec.key_sizes[i] = 0; + } else { + err = ipw2100_set_key(priv, i, + priv->sec.keys[i], + priv->sec.key_sizes[i], + 1); + if (err) + goto exit; + } + } + } + + /* Always enable privacy so the Host can filter WEP packets if + * encrypted data is sent up */ + err = ipw2100_set_wep_flags( + priv, priv->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 ipw2100_priv *priv) +{ + /* 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 ieee80211_device *ieee, + struct ieee80211_security *sec) +{ + struct ipw2100_priv *priv = ieee->priv; + int i, force_update = 0; + + if (!(priv->status & STATUS_INITIALIZED)) + return; + + for (i = 0; i < 4; i++) { + if (sec->flags & (1 << i)) { + priv->sec.key_sizes[i] = sec->key_sizes[i]; + if (sec->key_sizes[i] == 0) + priv->sec.flags &= ~(1 << i); + else + memcpy(priv->sec.keys[i], sec->keys[i], + sec->key_sizes[i]); + priv->sec.flags |= (1 << i); + priv->status |= STATUS_SECURITY_UPDATED; + } + } + + if ((sec->flags & SEC_ACTIVE_KEY) && + priv->sec.active_key != sec->active_key) { + if (sec->active_key <= 3) { + priv->sec.active_key = sec->active_key; + priv->sec.flags |= SEC_ACTIVE_KEY; + } else + priv->sec.flags &= ~SEC_ACTIVE_KEY; + + priv->status |= STATUS_SECURITY_UPDATED; + } + + if ((sec->flags & SEC_AUTH_MODE) && + (priv->sec.auth_mode != sec->auth_mode)) { + priv->sec.auth_mode = sec->auth_mode; + priv->sec.flags |= SEC_AUTH_MODE; + priv->status |= STATUS_SECURITY_UPDATED; + } + + if (sec->flags & SEC_ENABLED && + priv->sec.enabled != sec->enabled) { + priv->sec.flags |= SEC_ENABLED; + priv->sec.enabled = sec->enabled; + priv->status |= STATUS_SECURITY_UPDATED; + force_update = 1; + } + + if (sec->flags & SEC_LEVEL && + priv->sec.level != sec->level) { + priv->sec.level = sec->level; + priv->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->sec.flags & BIT(8) ? '1' : '0', + priv->sec.flags & BIT(7) ? '1' : '0', + priv->sec.flags & BIT(6) ? '1' : '0', + priv->sec.flags & BIT(5) ? '1' : '0', + priv->sec.flags & BIT(4) ? '1' : '0', + priv->sec.flags & BIT(3) ? '1' : '0', + priv->sec.flags & BIT(2) ? '1' : '0', + priv->sec.flags & BIT(1) ? '1' : '0', + priv->sec.flags & BIT(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)) + ipw2100_configure_security(priv, 0); +} + +static struct ieee80211_helper_functions ipw2100_ieee_callbacks = { + .set_security = shim__set_security, +}; + +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_PROMISC + 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_PROMISC */ + + 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 = netdev_priv(dev); + struct sockaddr *addr = p; + int err = 0; + + if (!is_valid_ether_addr(addr->sa_data)) + return -EADDRNOTAVAIL; + + 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; + ipw2100_reset_adapter(priv); + done: + return err; +} + +static int ipw2100_open(struct net_device *dev) +{ + struct ipw2100_priv *priv = netdev_priv(dev); + unsigned long flags; + IPW_DEBUG_INFO("dev->open\n"); + + spin_lock_irqsave(&priv->low_lock, flags); + if (priv->status & STATUS_ASSOCIATED) + 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 = netdev_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 = netdev_priv(dev); + + priv->ieee->stats.tx_errors++; + +#ifdef CONFIG_IPW2100_PROMISC + 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); +} + + +/* + * TODO: reimplement it so that it reads statistics + * from the adapter using ordinal tables + * instead of/in addition to collecting them + * in the driver + */ +static struct net_device_stats *ipw2100_stats(struct net_device *dev) +{ + struct ipw2100_priv *priv = netdev_priv(dev); + + return &priv->ieee->stats; +} + +/* Support for wpa_supplicant. Will be replaced with WEXT once + * they get WPA support. */ +#ifdef CONFIG_IEEE80211_WPA + +/* following definitions must match definitions in driver_ipw2100.c */ + +#define IPW2100_IOCTL_WPA_SUPPLICANT SIOCIWFIRSTPRIV+30 + +#define IPW2100_CMD_SET_WPA_PARAM 1 +#define IPW2100_CMD_SET_WPA_IE 2 +#define IPW2100_CMD_SET_ENCRYPTION 3 +#define IPW2100_CMD_MLME 4 + +#define IPW2100_PARAM_WPA_ENABLED 1 +#define IPW2100_PARAM_TKIP_COUNTERMEASURES 2 +#define IPW2100_PARAM_DROP_UNENCRYPTED 3 +#define IPW2100_PARAM_PRIVACY_INVOKED 4 +#define IPW2100_PARAM_AUTH_ALGS 5 +#define IPW2100_PARAM_IEEE_802_1X 6 + +#define IPW2100_MLME_STA_DEAUTH 1 +#define IPW2100_MLME_STA_DISASSOC 2 + +#define IPW2100_CRYPT_ERR_UNKNOWN_ALG 2 +#define IPW2100_CRYPT_ERR_UNKNOWN_ADDR 3 +#define IPW2100_CRYPT_ERR_CRYPT_INIT_FAILED 4 +#define IPW2100_CRYPT_ERR_KEY_SET_FAILED 5 +#define IPW2100_CRYPT_ERR_TX_KEY_SET_FAILED 6 +#define IPW2100_CRYPT_ERR_CARD_CONF_FAILED 7 + +#define IPW2100_CRYPT_ALG_NAME_LEN 16 + +struct ipw2100_param { + u32 cmd; + u8 sta_addr[ETH_ALEN]; + union { + struct { + u8 name; + u32 value; + } wpa_param; + struct { + u32 len; + u8 *data; + } wpa_ie; + struct{ + int command; + int reason_code; + } mlme; + struct { + u8 alg[IPW2100_CRYPT_ALG_NAME_LEN]; + u8 set_tx; + u32 err; + u8 idx; + u8 seq[8]; /* sequence counter (set: RX, get: TX) */ + u16 key_len; + u8 key[0]; + } crypt; + + } u; +}; + +/* end of driver_ipw2100.c code */ + +static int ipw2100_wpa_enable(struct ipw2100_priv *priv, int value){ + + struct ieee80211_device *ieee = priv->ieee; + struct ieee80211_security sec = { + .flags = SEC_LEVEL | SEC_ENABLED, + }; + int ret = 0; + + ieee->wpa_enabled = value; + + if (value){ + sec.level = SEC_LEVEL_3; + sec.enabled = 1; + } else { + sec.level = SEC_LEVEL_0; + sec.enabled = 0; + } + + if (ieee->func && ieee->func->set_security) + ieee->func->set_security(ieee, &sec); + else + ret = -EOPNOTSUPP; + + return ret; +} + +#define AUTH_ALG_OPEN_SYSTEM 0x1 +#define AUTH_ALG_SHARED_KEY 0x2 + +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 & AUTH_ALG_SHARED_KEY){ + sec.auth_mode = WLAN_AUTH_SHARED_KEY; + ieee->open_wep = 0; + } else { + sec.auth_mode = WLAN_AUTH_OPEN; + ieee->open_wep = 1; + } + + if (ieee->func && ieee->func->set_security) + ieee->func->set_security(ieee, &sec); + else + ret = -EOPNOTSUPP; + + return ret; +} + + +static int ipw2100_wpa_set_param(struct net_device *dev, u8 name, u32 value){ + + struct ipw2100_priv *priv = netdev_priv(dev); + int ret=0; + + switch(name){ + case IPW2100_PARAM_WPA_ENABLED: + ret = ipw2100_wpa_enable(priv, value); + break; + + case IPW2100_PARAM_TKIP_COUNTERMEASURES: + priv->ieee->tkip_countermeasures=value; + break; + + case IPW2100_PARAM_DROP_UNENCRYPTED: + priv->ieee->drop_unencrypted=value; + break; + + case IPW2100_PARAM_PRIVACY_INVOKED: + priv->ieee->privacy_invoked=value; + break; + + case IPW2100_PARAM_AUTH_ALGS: + ret = ipw2100_wpa_set_auth_algs(priv, value); + break; + + case IPW2100_PARAM_IEEE_802_1X: + priv->ieee->ieee_802_1x=value; + break; + + default: + IPW_DEBUG_ERROR("%s: Unknown WPA param: %d\n", + dev->name, name); + ret = -EOPNOTSUPP; + } + + return ret; +} + +static int ipw2100_wpa_mlme(struct net_device *dev, int command, int reason){ + + struct ipw2100_priv *priv = netdev_priv(dev); + int ret=0; + + switch(command){ + case IPW2100_MLME_STA_DEAUTH: + // silently ignore + break; + + case IPW2100_MLME_STA_DISASSOC: + ipw2100_disassociate_bssid(priv); + break; + + default: + IPW_DEBUG_ERROR("%s: Unknown MLME request: %d\n", + dev->name, command); + ret = -EOPNOTSUPP; + } + + return ret; +} + + +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 int ipw2100_wpa_set_wpa_ie(struct net_device *dev, + struct ipw2100_param *param, int plen){ + + struct ipw2100_priv *priv = netdev_priv(dev); + struct ieee80211_device *ieee = priv->ieee; + u8 *buf; + + if (! ieee->wpa_enabled) + return -EOPNOTSUPP; + + if (param->u.wpa_ie.len > MAX_WPA_IE_LEN || + (param->u.wpa_ie.len && + param->u.wpa_ie.data==NULL)) + return -EINVAL; + + if (param->u.wpa_ie.len){ + buf = kmalloc(param->u.wpa_ie.len, GFP_KERNEL); + if (buf == NULL) + return -ENOMEM; + + memcpy(buf, param->u.wpa_ie.data, param->u.wpa_ie.len); + + kfree(ieee->wpa_ie); + ieee->wpa_ie = buf; + ieee->wpa_ie_len = param->u.wpa_ie.len; + + } 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; +} + +/* implementation borrowed from hostap driver */ + +static int ipw2100_wpa_set_encryption(struct net_device *dev, + struct ipw2100_param *param, int param_len){ + + int ret = 0; + struct ipw2100_priv *priv = netdev_priv(dev); + struct ieee80211_device *ieee = priv->ieee; + struct ieee80211_crypto_ops *ops; + struct ieee80211_crypt_data **crypt; + + struct ieee80211_security sec = { + .flags = 0, + }; + + param->u.crypt.err = 0; + param->u.crypt.alg[IPW2100_CRYPT_ALG_NAME_LEN - 1] = '\0'; + + if (param_len != + (int) ((char *) param->u.crypt.key - (char *) param) + + param->u.crypt.key_len){ + IPW_DEBUG_INFO("Len mismatch %d, %d\n", param_len, param->u.crypt.key_len); + return -EINVAL; + } + if (param->sta_addr[0] == 0xff && param->sta_addr[1] == 0xff && + param->sta_addr[2] == 0xff && param->sta_addr[3] == 0xff && + param->sta_addr[4] == 0xff && param->sta_addr[5] == 0xff) { + if (param->u.crypt.idx >= WEP_KEYS) + return -EINVAL; + crypt = &ieee->crypt[param->u.crypt.idx]; + } else { + return -EINVAL; + } + + if (strcmp(param->u.crypt.alg, "none") == 0) { + if (crypt){ + sec.enabled = 0; + sec.level = SEC_LEVEL_0; + sec.flags |= SEC_ENABLED | SEC_LEVEL; + ieee80211_crypt_delayed_deinit(ieee, crypt); + } + goto done; + } + sec.enabled = 1; + sec.flags |= SEC_ENABLED; + + ops = ieee80211_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); + } 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); + } 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); + } + if (ops == NULL) { + IPW_DEBUG_INFO("%s: unknown crypto alg '%s'\n", + dev->name, param->u.crypt.alg); + param->u.crypt.err = IPW2100_CRYPT_ERR_UNKNOWN_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 = (struct ieee80211_crypt_data *) + kmalloc(sizeof(struct ieee80211_crypt_data), GFP_KERNEL); + if (new_crypt == NULL) { + ret = -ENOMEM; + goto done; + } + memset(new_crypt, 0, sizeof(struct ieee80211_crypt_data)); + new_crypt->ops = ops; + if (new_crypt->ops && try_module_get(new_crypt->ops->owner)) + new_crypt->priv = new_crypt->ops->init(param->u.crypt.idx); + + if (new_crypt->priv == NULL) { + kfree(new_crypt); + param->u.crypt.err = + IPW2100_CRYPT_ERR_CRYPT_INIT_FAILED; + ret = -EINVAL; + goto done; + } + + *crypt = new_crypt; + } + + if (param->u.crypt.key_len > 0 && (*crypt)->ops->set_key && + (*crypt)->ops->set_key(param->u.crypt.key, + param->u.crypt.key_len, param->u.crypt.seq, + (*crypt)->priv) < 0) { + IPW_DEBUG_INFO("%s: key setting failed\n", + dev->name); + param->u.crypt.err = IPW2100_CRYPT_ERR_KEY_SET_FAILED; + ret = -EINVAL; + goto done; + } + + if (param->u.crypt.set_tx){ + ieee->tx_keyidx = param->u.crypt.idx; + sec.active_key = param->u.crypt.idx; + sec.flags |= SEC_ACTIVE_KEY; + } + + if (ops->name != NULL){ + + if (strcmp(ops->name, "WEP") == 0) { + memcpy(sec.keys[param->u.crypt.idx], param->u.crypt.key, param->u.crypt.key_len); + sec.key_sizes[param->u.crypt.idx] = param->u.crypt.key_len; + sec.flags |= (1 << param->u.crypt.idx); + sec.flags |= SEC_LEVEL; + sec.level = SEC_LEVEL_1; + } else if (strcmp(ops->name, "TKIP") == 0) { + sec.flags |= SEC_LEVEL; + sec.level = SEC_LEVEL_2; + } else if (strcmp(ops->name, "CCMP") == 0) { + sec.flags |= SEC_LEVEL; + sec.level = SEC_LEVEL_3; + } + } + done: + if (ieee->func && ieee->func->set_security) + ieee->func->set_security(ieee, &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->func->reset_port && + ieee->func->reset_port(dev)) { + IPW_DEBUG_INFO("%s: reset_port failed\n", dev->name); + param->u.crypt.err = IPW2100_CRYPT_ERR_CARD_CONF_FAILED; + return -EINVAL; + } + + return ret; +} + + +static int ipw2100_wpa_supplicant(struct net_device *dev, struct iw_point *p){ + + struct ipw2100_param *param; + int ret=0; + + IPW_DEBUG_IOCTL("wpa_supplicant: len=%d\n", p->length); + + if (p->length < sizeof(struct ipw2100_param) || !p->pointer) + return -EINVAL; + + param = (struct ipw2100_param *)kmalloc(p->length, GFP_KERNEL); + if (param == NULL) + return -ENOMEM; + + if (copy_from_user(param, p->pointer, p->length)){ + kfree(param); + return -EFAULT; + } + + switch (param->cmd){ + + case IPW2100_CMD_SET_WPA_PARAM: + ret = ipw2100_wpa_set_param(dev, param->u.wpa_param.name, + param->u.wpa_param.value); + break; + + case IPW2100_CMD_SET_WPA_IE: + ret = ipw2100_wpa_set_wpa_ie(dev, param, p->length); + break; + + case IPW2100_CMD_SET_ENCRYPTION: + ret = ipw2100_wpa_set_encryption(dev, param, p->length); + break; + + case IPW2100_CMD_MLME: + ret = ipw2100_wpa_mlme(dev, param->u.mlme.command, + param->u.mlme.reason_code); + break; + + default: + IPW_DEBUG_ERROR("%s: Unknown WPA supplicant request: %d\n", + dev->name, param->cmd); + ret = -EOPNOTSUPP; + + } + + if (ret == 0 && copy_to_user(p->pointer, param, p->length)) + ret = -EFAULT; + + kfree(param); + return ret; +} +#endif /* CONFIG_IEEE80211_WPA */ + +static int ipw2100_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) +{ +#ifdef CONFIG_IEEE80211_WPA + struct iwreq *wrq = (struct iwreq *) rq; + int ret=-1; + switch (cmd){ + case IPW2100_IOCTL_WPA_SUPPLICANT: + ret = ipw2100_wpa_supplicant(dev, &wrq->u.data); + return ret; + + default: + return -EOPNOTSUPP; + } + +#endif /* CONFIG_IEEE80211_WPA */ + + return -EOPNOTSUPP; +} + + +static void ipw_ethtool_get_drvinfo(struct net_device *dev, + struct ethtool_drvinfo *info) +{ + struct ipw2100_priv *priv = netdev_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 = netdev_priv(dev); + return (priv->status & STATUS_ASSOCIATED) ? 1 : 0; +} + + +static struct ethtool_ops ipw2100_ethtool_ops = { + .get_link = ipw2100_ethtool_get_link, + .get_drvinfo = ipw_ethtool_get_drvinfo, +}; + +static void ipw2100_hang_check(void *adapter) +{ + struct ipw2100_priv *priv = adapter; + 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; + } /*else { + spin_unlock_irqrestore(&priv->low_lock, flags); + if (ipw2100_set_rts_threshold(priv, priv->rts_threshold)) { + IPW_DEBUG_INFO("%s: Firmware command queue " + "stalled.\n", + priv->net_dev->name); + + restart = 1; + } + spin_lock_irqsave(&priv->low_lock, flags); + }*/ + + if (restart) { + /* Kill timer */ + priv->stop_hang_check = 1; + priv->hangs++; + + /* Restart the NIC */ + schedule_reset(priv); + } + + priv->last_rtc = rtc; + + /* Check again in two seconds */ + if (!priv->stop_hang_check) + queue_delayed_work(priv->workqueue, &priv->hang_check, HZ); + + spin_unlock_irqrestore(&priv->low_lock, flags); +} + + +static void ipw2100_rf_kill(void *adapter) +{ + struct ipw2100_priv *priv = adapter; + 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, 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); + +static struct net_device *ipw2100_alloc_device( + struct pci_dev *pci_dev, + char *base_addr, + unsigned long mem_start, + unsigned long mem_len) +{ + struct ipw2100_priv *priv; + struct net_device *dev; + + dev = alloc_etherdev(sizeof(struct ipw2100_priv)); + if (!dev) + return NULL; + + dev->type = ARPHRD_ETHER; + dev->open = ipw2100_open; + dev->stop = ipw2100_close; + dev->init = ipw2100_net_init; + dev->hard_start_xmit = ipw2100_tx; + dev->do_ioctl = ipw2100_ioctl; + dev->get_stats = ipw2100_stats; + dev->ethtool_ops = &ipw2100_ethtool_ops; + dev->tx_timeout = ipw2100_tx_timeout; + dev->wireless_handlers = &ipw2100_wx_handler_def; + dev->get_wireless_stats = ipw2100_wx_wireless_stats; + 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; + + if (ifname && ifname[0] != '\0') + strncpy(dev->name, ifname, IFNAMSIZ - 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 */ + + priv = netdev_priv(dev); + + priv->pci_dev = pci_dev; + priv->net_dev = dev; + + /* memset() puts everything to 0, so we only have explicitely 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; + + + + /* Initialize IEEE80211 stack */ + priv->ieee = ieee80211_alloc(dev, priv); + if (!priv->ieee) { + IPW_DEBUG_WARNING(DRV_NAME + ": Unable to allocate IEEE stack\n"); + free_netdev(dev); + return NULL; + } + + priv->ieee->func = &ipw2100_ieee_callbacks; + priv->ieee->tx_payload_only = 1; + +#ifdef CONFIG_IEEE80211_WPA + priv->ieee->wpa_enabled = 0; + priv->ieee->tkip_countermeasures = 0; + priv->ieee->drop_unencrypted = 0; + priv->ieee->privacy_invoked = 0; + priv->ieee->ieee_802_1x = 1; +#endif /* CONFIG_IEEE80211_WPA */ + + /* Set module parameters */ + switch (mode) { + case 1: + priv->ieee->iw_mode = IW_MODE_ADHOC; + break; +#ifdef CONFIG_IPW2100_PROMISC + 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); + + 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); + + +#ifdef CONFIG_SOFTWARE_SUSPEND2 + priv->workqueue = create_workqueue(DRV_NAME, 0); +#else + priv->workqueue = create_workqueue(DRV_NAME); +#endif + INIT_WORK(&priv->reset_work, + (void (*)(void *))ipw2100_reset_adapter, priv); + INIT_WORK(&priv->security_work, + (void (*)(void *))ipw2100_security_work, priv); + INIT_WORK(&priv->wx_event_work, + (void (*)(void *))ipw2100_wx_event_work, priv); + INIT_WORK(&priv->hang_check, ipw2100_hang_check, priv); + INIT_WORK(&priv->rf_kill, ipw2100_rf_kill, priv); + + 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; +} + + + +#define PCI_DMA_32BIT 0x00000000ffffffffULL + +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; + char *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 = netdev_priv(dev); + + pci_set_master(pci_dev); + pci_set_drvdata(pci_dev, priv); + + err = pci_set_dma_mask(pci_dev, PCI_DMA_32BIT); + 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, 0); + + 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 calilng ipw2100_queues_allocate.\n"); + err = -ENOMEM; + goto fail; + } + ipw2100_queues_initialize(priv); + + err = request_irq(pci_dev->irq, + ipw2100_interrupt, SA_SHIRQ, + 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"); + + SET_MODULE_OWNER(dev); + + 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 regiser_netdev.\n"); + goto fail_unlock; + } + 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 */ + sysfs_create_group(&pci_dev->dev.kobj, &ipw2100_attribute_group); + netif_carrier_off(dev); + + /* 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; + + return 0; + + fail_unlock: + + 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); + + if (priv->ieee) { + ieee80211_free(priv->ieee); + priv->ieee = NULL; + } + + /* 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_netdev(dev); + pci_set_drvdata(pci_dev, NULL); + } + + if (base_addr) + iounmap((char*)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) { + + 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); + + /* 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); + + ieee80211_free(priv->ieee); + priv->ieee = NULL; + + 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((unsigned char *)dev->base_addr); + + free_netdev(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, u32 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); + + 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); + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,10) + pci_save_state(pci_dev, priv->pm_state); +#endif + pci_set_power_state(pci_dev, state); + + 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; + u32 val; + + if (IPW2100_PM_DISABLED) + return 0; + + IPW_DEBUG_INFO("%s: Coming out of suspend...\n", + dev->name); + + pci_set_power_state(pci_dev, 0); +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,10) + pci_restore_state(pci_dev, priv->pm_state); +#else + pci_restore_state(pci_dev); +#endif + + /* + * 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); + + 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); + +#ifdef CONFIG_IEEE80211_NOWEP + IPW_DEBUG_INFO(DRV_NAME ": Compiled with WEP disabled.\n"); +#endif + +#ifdef CONFIG_IPW2100_LEGACY_FW_LOAD + IPW_DEBUG_INFO(DRV_NAME ": Compiled with LEGACY FW load.\n"); +#endif + + ret = pci_module_init(&ipw2100_pci_driver); + +#ifdef CONFIG_IPW_DEBUG + ipw2100_debug_level = debug; + driver_create_file(&ipw2100_pci_driver.driver, + &driver_attr_debug_level); +#endif + + 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_IPW_DEBUG + driver_remove_file(&ipw2100_pci_driver.driver, + &driver_attr_debug_level); +#endif + pci_unregister_driver(&ipw2100_pci_driver); +} + +module_init(ipw2100_init); +module_exit(ipw2100_exit); + +#define WEXT_USECHANNELS 1 + +const long ipw2100_frequencies[] = { + 2412, 2417, 2422, 2427, + 2432, 2437, 2442, 2447, + 2452, 2457, 2462, 2467, + 2472, 2484 +}; + +#define FREQ_COUNT (sizeof(ipw2100_frequencies) / \ + sizeof(ipw2100_frequencies[0])) + +const long ipw2100_rates_11b[] = { + 1000000, + 2000000, + 5500000, + 11000000 +}; + +#define RATE_COUNT (sizeof(ipw2100_rates_11b) / sizeof(ipw2100_rates_11b[0])) + +static int ipw2100_wx_get_name(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + /* + * No check of STATUS_INITIALIZED required + */ + + struct ipw2100_priv *priv = netdev_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 = netdev_priv(dev); + struct iw_freq *fwrq = &wrqu->freq; + int err = 0; + + if (priv->ieee->iw_mode == IW_MODE_INFRA) + return -EOPNOTSUPP; + + 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) + return -EOPNOTSUPP; + else { /* Set the channel */ + IPW_DEBUG_WX("SET Freq/Channel -> %d \n", fwrq->m); + err = ipw2100_set_channel(priv, fwrq->m, 0); + } + + done: + return err; +} + + +static int ipw2100_wx_get_freq(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + /* + * No check of STATUS_INITIALIZED required + */ + + struct ipw2100_priv *priv = netdev_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 = netdev_priv(dev); + int err = 0; + + IPW_DEBUG_WX("SET Mode -> %d \n", wrqu->mode); + + if (wrqu->mode == priv->ieee->iw_mode) + return 0; + + if (!(priv->status & STATUS_INITIALIZED)) { + err = -EIO; + goto done; + } + + switch (wrqu->mode) { +#ifdef CONFIG_IPW2100_PROMISC + case IW_MODE_MONITOR: + err = ipw2100_switch_mode(priv, IW_MODE_MONITOR); + break; +#endif /* CONFIG_IPW2100_PROMISC */ + 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: + return err; +} + +static int ipw2100_wx_get_mode(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + /* + * No check of STATUS_INITIALIZED required + */ + + struct ipw2100_priv *priv = netdev_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 */ +const s32 timeout_duration[POWER_MODES] = { + 350000, + 250000, + 75000, + 37000, + 25000, +}; + +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) +{ + /* + * No check of STATUS_INITIALIZED required + */ + + struct ipw2100_priv *priv = netdev_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 = 16; + +// 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; + + 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 = netdev_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; + + 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 -> %02X:%02X:%02X:%02X:%02X:%02X\n", + wrqu->ap_addr.sa_data[0] & 0xff, + wrqu->ap_addr.sa_data[1] & 0xff, + wrqu->ap_addr.sa_data[2] & 0xff, + wrqu->ap_addr.sa_data[3] & 0xff, + wrqu->ap_addr.sa_data[4] & 0xff, + wrqu->ap_addr.sa_data[5] & 0xff); + + done: + return err; +} + +static int ipw2100_wx_get_wap(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + /* + * No check of STATUS_INITIALIZED required + */ + + struct ipw2100_priv *priv = netdev_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: " MAC_FMT "\n", + MAC_ARG(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 = netdev_priv(dev); + char *essid = ""; /* ANY */ + int length = 0; + int err = 0; + + if (!(priv->status & STATUS_INITIALIZED)) { + err = -EIO; + goto done; + } + + if (wrqu->essid.flags && wrqu->essid.length) { + length = wrqu->essid.length - 1; + 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: + return err; +} + +static int ipw2100_wx_get_essid(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + /* + * No check of STATUS_INITIALIZED required + */ + + struct ipw2100_priv *priv = netdev_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) +{ + /* + * No check of STATUS_INITIALIZED required + */ + + struct ipw2100_priv *priv = netdev_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) +{ + /* + * No check of STATUS_INITIALIZED required + */ + + struct ipw2100_priv *priv = netdev_priv(dev); + + wrqu->data.length = strlen(priv->nick) + 1; + 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 = netdev_priv(dev); + u32 target_rate = wrqu->bitrate.value; + u32 rate; + int err = 0; + + 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: + 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 = netdev_priv(dev); + int val; + int len = sizeof(val); + int err; + + if (!(priv->status & STATUS_ENABLED) || + priv->status & STATUS_RF_KILL_MASK || + !(priv->status & STATUS_ASSOCIATED)) { + wrqu->bitrate.value = 0; + return 0; + } + + 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"); + return err; + } + + 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: + + return 0; +} + +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 = netdev_priv(dev); + int value, err; + + /* Auto RTS not yet supported */ + if (wrqu->rts.fixed == 0) + return -EINVAL; + + 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) + return -EINVAL; + value = wrqu->rts.value; + } + + err = ipw2100_set_rts_threshold(priv, value); + + IPW_DEBUG_WX("SET RTS Threshold -> 0x%08X \n", value); + done: + return err; +} + +static int ipw2100_wx_get_rts(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + /* + * No check of STATUS_INITIALIZED required + */ + + struct ipw2100_priv *priv = netdev_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 = netdev_priv(dev); + int err, value; + + if (priv->ieee->iw_mode != IW_MODE_ADHOC) + return -EINVAL; + + if (wrqu->txpower.disabled == 1 || 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 - IPW_TX_POWER_MIN_DBM) * 16 / + (IPW_TX_POWER_MAX_DBM - IPW_TX_POWER_MIN_DBM); + } + + 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: + + return err; +} + +static int ipw2100_wx_get_txpow(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + /* + * No check of STATUS_INITIALIZED required + */ + + struct ipw2100_priv *priv = netdev_priv(dev); + + if (priv->ieee->iw_mode != IW_MODE_ADHOC) { + wrqu->power.disabled = 1; + return 0; + } + + if (priv->tx_power == IPW_TX_POWER_DEFAULT) { + wrqu->power.fixed = 0; + wrqu->power.value = IPW_TX_POWER_MAX_DBM; + wrqu->power.disabled = 1; + } else { + wrqu->power.disabled = 0; + wrqu->power.fixed = 1; + wrqu->power.value = + (priv->tx_power * + (IPW_TX_POWER_MAX_DBM - IPW_TX_POWER_MIN_DBM)) / + (IPW_TX_POWER_MAX - IPW_TX_POWER_MIN) + + IPW_TX_POWER_MIN_DBM; + } + + wrqu->power.flags = IW_TXPOW_DBM; + + IPW_DEBUG_WX("GET TX Power -> %d \n", wrqu->power.value); + + return 0; +} + +static int ipw2100_wx_set_frag(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + /* + * No check of STATUS_INITIALIZED required + */ + + struct ipw2100_priv *priv = netdev_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) +{ + /* + * No check of STATUS_INITIALIZED required + */ + + struct ipw2100_priv *priv = netdev_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 = netdev_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; + + if (!(priv->status & STATUS_INITIALIZED)) { + err = -EIO; + goto done; + } + + if (wrqu->retry.flags & IW_RETRY_MIN) { + 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_MAX) { + 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: + return err; +} + +static int ipw2100_wx_get_retry(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + /* + * No check of STATUS_INITIALIZED required + */ + + struct ipw2100_priv *priv = netdev_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_MAX) { + wrqu->retry.flags = IW_RETRY_LIMIT & IW_RETRY_MAX; + wrqu->retry.value = priv->long_retry_limit; + } else { + wrqu->retry.flags = + (priv->short_retry_limit != + priv->long_retry_limit) ? + IW_RETRY_LIMIT & IW_RETRY_MIN : 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 = netdev_priv(dev); + int err = 0; + + if (!(priv->status & STATUS_INITIALIZED)) { + err = -EIO; + goto done; + } + + IPW_DEBUG_WX("Initiating scan...\n"); + 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: + return err; +} + +static int ipw2100_wx_get_scan(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + /* + * No check of STATUS_INITIALIZED required + */ + + struct ipw2100_priv *priv = netdev_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 = netdev_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) +{ + /* + * No check of STATUS_INITIALIZED required + */ + + struct ipw2100_priv *priv = netdev_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 = netdev_priv(dev); + int err = 0; + + 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 explicitely 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: + return err; + +} + +static int ipw2100_wx_get_power(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + /* + * No check of STATUS_INITIALIZED required + */ + + struct ipw2100_priv *priv = netdev_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; +} + + +/* + * + * IWPRIV handlers + * + */ +#ifdef CONFIG_IPW2100_PROMISC +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 = netdev_priv(dev); + int *parms = (int *)extra; + int enable = (parms[0] > 0); + int err = 0; + + 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: + 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 = netdev_priv(dev); + if (priv->status & STATUS_INITIALIZED) + ipw2100_reset_adapter(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 = netdev_priv(dev); + int err = 0, mode = *(int *)extra; + + if (!(priv->status & STATUS_INITIALIZED)) { + err = -EIO; + goto done; + } + + if ((mode < 1) || (mode > POWER_MODES)) + mode = IPW_POWER_AUTO; + + if (priv->power_mode != mode) + err = ipw2100_set_power_mode(priv, mode); + done: + 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) +{ + /* + * No check of STATUS_INITIALIZED required + */ + + struct ipw2100_priv *priv = netdev_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)", 0); + 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 = netdev_priv(dev); + int err, mode = *(int *)extra; + + 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 + return -EINVAL; + + err = ipw2100_system_config(priv, 0); + +done: + return err; +} + +static int ipw2100_wx_get_preamble(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + /* + * No check of STATUS_INITIALIZED required + */ + + struct ipw2100_priv *priv = netdev_priv(dev); + + if (priv->config & CFG_LONG_PREAMBLE) + snprintf(wrqu->name, IFNAMSIZ, "long (1)"); + else + snprintf(wrqu->name, IFNAMSIZ, "auto (0)"); + + return 0; +} + +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 */ + NULL, /* -- hole -- */ + NULL, /* SIOCGIWAPLIST -- depricated */ + 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 */ +}; + +#define IPW2100_PRIV_SET_PROMISC 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 + +static const struct iw_priv_args ipw2100_private_args[] = { + +#ifdef CONFIG_IPW2100_PROMISC + { + IPW2100_PRIV_SET_PROMISC, + 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_PROMISC */ + + { + 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" + }, +}; + +static iw_handler ipw2100_private_handler[] = { +#ifdef CONFIG_IPW2100_PROMISC + ipw2100_wx_set_promisc, + ipw2100_wx_reset, +#else /* CONFIG_IPW2100_PROMISC */ + NULL, + NULL, +#endif /* CONFIG_IPW2100_PROMISC */ + ipw2100_wx_set_powermode, + ipw2100_wx_get_powermode, + ipw2100_wx_set_preamble, + ipw2100_wx_get_preamble, +}; + +struct iw_handler_def ipw2100_wx_handler_def = +{ + .standard = ipw2100_wx_handlers, + .num_standard = sizeof(ipw2100_wx_handlers) / sizeof(iw_handler), + .num_private = sizeof(ipw2100_private_handler) / sizeof(iw_handler), + .num_private_args = sizeof(ipw2100_private_args) / + sizeof(struct iw_priv_args), + .private = (iw_handler *)ipw2100_private_handler, + .private_args = (struct iw_priv_args *)ipw2100_private_args, +}; + +/* + * Get wireless statistics. + * Called by /proc/net/wireless + * Also called by SIOCGIWSTATS + */ +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 = netdev_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_IPW_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; +} + +void ipw2100_wx_event_work(struct ipw2100_priv *priv) +{ + union iwreq_data wrqu; + int len = ETH_ALEN; + + if (priv->status & STATUS_STOPPING) + return; + + IPW_DEBUG_WX("enter\n"); + + + wrqu.ap_addr.sa_family = ARPHRD_ETHER; + + /* Fetch BSSID from the hardware */ + if (!(priv->status & 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 { + memcpy(wrqu.ap_addr.sa_data, priv->bssid, ETH_ALEN); + memcpy(&priv->ieee->bssid, priv->bssid, ETH_ALEN); + } + + if (!(priv->status & STATUS_ASSOCIATED)) { + /* 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); + } + + + 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" + +#ifdef CONFIG_IPW2100_LEGACY_FW_LOAD + +static char *firmware = NULL; + +/* Module paramter for path to the firmware*/ +#include +module_param(firmware, charp, 0); + +MODULE_PARM_DESC(firmware, "complete path to firmware file"); + +#endif /* CONFIG_IPW2100_LEGACY_FW_LOAD */ + + +/* + +BINARY FIRMWARE HEADER FORMAT + +offset length desc +0 2 version +2 2 mode == 0:BSS,1:IBSS,2:PROMISC +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)); + + +#ifdef CONFIG_IPW2100_LEGACY_FW_LOAD + +/* + * + * The following was originally based on the mod_firmware_load in + * drivers/sound/sound_firmware.c. Primary changes revolved around + * making it work for firmware images > 128k and to support having + * both a firmware and microcode image in the file loaded. + * + */ +static void ipw2100_fw_free(struct ipw2100_fw *fw) +{ + struct ipw2100_fw_chunk *c; + struct list_head *e; + + while (!list_empty(&fw->fw.chunk_list)) { + e = fw->fw.chunk_list.next; + c = list_entry(e, struct ipw2100_fw_chunk, list); + list_del(e); + vfree(c->buf); + vfree(c); + } + + while (!list_empty(&fw->uc.chunk_list)) { + e = fw->uc.chunk_list.next; + c = list_entry(e, struct ipw2100_fw_chunk, list); + list_del(e); + vfree(c->buf); + vfree(c); + } +} + + +static int ipw2100_fw_load(struct file *filp, struct ipw2100_fw_chunk_set *cs, long size) +{ + struct ipw2100_fw_chunk *c; + int i = 0; + + /* Break firmware image into chunks of 128k */ + cs->size = size; + cs->chunks = cs->size >> 17; + + if (cs->size & 0x1FFFF) + cs->chunks++; + + IPW_DEBUG_FW("Loading %ld bytes from %u chunks\n", + cs->size, cs->chunks); + + /* Load the chunks */ + while (size > 0) { + i++; + + c = (struct ipw2100_fw_chunk *)vmalloc( + sizeof(struct ipw2100_fw_chunk)); + if (c == NULL) { + IPW_DEBUG_INFO("Out of memory loading firmware " + "chunk %d.\n", i); + goto fail; + } + c->pos = 0; + + if (size >= 0x20000) + c->len = 0x20000; + else + c->len = size; + + c->buf = (unsigned char *)vmalloc(c->len); + if (c->buf == NULL) { + IPW_DEBUG_INFO("Out of memory loading firmware " + "chunk %d.\n", i); + goto fail; + + } + if (vfs_read(filp, c->buf, c->len, &filp->f_pos) != c->len) { + IPW_DEBUG_INFO("Failed to read chunk firmware " + "chunk %d.\n", i); + goto fail; + } + + list_add_tail(&c->list, &cs->chunk_list); + + IPW_DEBUG_FW("Chunk %d loaded: %lu bytes\n", + i, c->len); + size -= c->len; + } + + return 0; + + fail: + return 1; +} + +static int ipw2100_do_mod_firmware_load(const char *fn, struct ipw2100_fw *fw) +{ + struct file *filp; + long l; + struct ipw2100_fw_header h; + + /* Make sure the lists are init'd so that error paths can safely walk + * them to free potentially allocated storage */ + INIT_LIST_HEAD(&fw->fw.chunk_list); + INIT_LIST_HEAD(&fw->uc.chunk_list); + + filp = filp_open(fn, 0, 0); + if (IS_ERR(filp)) { + IPW_DEBUG_INFO("Unable to load '%s'.\n", fn); + return 1; + } + + l = i_size_read(filp->f_dentry->d_inode); + IPW_DEBUG_FW("Loading %ld bytes for firmware '%s'\n", l, fn); + + if (vfs_read(filp, (char *)&h, sizeof(h), &filp->f_pos) != sizeof(h)) { + IPW_DEBUG_INFO("Failed to read '%s'.\n", fn); + goto fail; + } + + if (IPW2100_FW_MAJOR(h.version) != IPW2100_FW_MAJOR_VERSION) { + IPW_DEBUG_WARNING("Firmware image not compatible " + "(detected version id of %u). " + "See Documentation/networking/README.ipw2100\n", + h.version); + goto fail; + } + + fw->version = h.version; + + if (ipw2100_fw_load(filp, &fw->fw, h.fw_size)) + goto fail; + + if (ipw2100_fw_load(filp, &fw->uc, h.uc_size)) + goto fail; + + filp_close(filp, current->files); + return 0; + + fail: + ipw2100_fw_free(fw); + filp_close(filp, current->files); + return 1; +} + +static int ipw2100_mod_firmware_load(const char *fn, struct ipw2100_fw *fw) +{ + int r; + mm_segment_t fs = get_fs(); + set_fs(get_ds()); + r = ipw2100_do_mod_firmware_load(fn, fw); + set_fs(fs); + return r; +} + +static inline struct list_head *ipw2100_fw_read( + struct list_head *e, struct ipw2100_fw_chunk_set *cs, + unsigned char *data, size_t len) +{ + struct ipw2100_fw_chunk *c = list_entry(e, struct ipw2100_fw_chunk, + list); + unsigned int avail = c->len - c->pos; + if (avail <= len) { + struct ipw2100_fw_chunk *tmp; + + memcpy(data, c->buf + c->pos, avail); + c->pos = 0; + + IPW_DEBUG_INFO("advancing to next chunk...\n"); + + e = e->next; + tmp = list_entry(e, struct ipw2100_fw_chunk, list); + + if (avail != len) { + memcpy(data + avail, + tmp->buf + tmp->pos, + len - avail); + tmp->pos += len - avail; + } + + return e; + } + + memcpy(data, c->buf + c->pos, len); + c->pos += len; + + return e; +} + +static inline struct list_head *ipw2100_fw_readw( + struct list_head *e, struct ipw2100_fw_chunk_set *cs, + unsigned short *data) +{ + return ipw2100_fw_read(e, cs, (unsigned char *)data, sizeof(*data)); +} + +static inline struct list_head *ipw2100_fw_readl( + struct list_head *e, struct ipw2100_fw_chunk_set *cs, + unsigned int *data) +{ + return ipw2100_fw_read(e, cs, (unsigned char *)data, sizeof(*data)); +} + +#else /* CONFIG_IPW2100_LEGACY_FW_LOAD */ + +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) { + IPW_DEBUG_WARNING("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; +} + +#endif /* CONFIG_IPW2100_LEGACY_FW_LOAD */ + + +int ipw2100_get_firmware(struct ipw2100_priv *priv, struct ipw2100_fw *fw) +{ + char *fw_name; + +#ifdef CONFIG_IPW2100_LEGACY_FW_LOAD + int err = 0; + + IPW_DEBUG_INFO("%s: Using legacy firmware load.\n", + priv->net_dev->name); + + if (!firmware) { + switch (priv->ieee->iw_mode) { + case IW_MODE_ADHOC: + fw_name = "/etc/firmware/" IPW2100_FW_NAME("-i"); + break; +#ifdef CONFIG_IPW2100_PROMISC + case IW_MODE_MONITOR: + fw_name = "/etc/firmware/" IPW2100_FW_NAME("-p"); + break; +#endif + case IW_MODE_INFRA: + default: + fw_name = "/etc/firmware/" IPW2100_FW_NAME(""); + break; + } + } else + fw_name = firmware; + + err = ipw2100_mod_firmware_load(fw_name, fw); + if (err) { + IPW_DEBUG_ERROR("%s: Firmware '%s' not available. " + "See Documentation/networking/README.ipw2100\n", + priv->net_dev->name, fw_name); + return -EIO; + } + +#else /* CONFIG_IPW2100_LEGACY_FW_LOAD */ + 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_PROMISC + 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) { + IPW_DEBUG_ERROR( + "%s: Firmware '%s' not available or load failed.\n", + priv->net_dev->name, fw_name); + return rc; + } + IPW_DEBUG_INFO("firmware data %p size %d", fw->fw_entry->data, + fw->fw_entry->size); + + ipw2100_mod_firmware_load(fw); + +#endif /* CONFIG_IPW2100_LEGACY_FW_LOAD */ + + return 0; +} + +void ipw2100_release_firmware(struct ipw2100_priv *priv, + struct ipw2100_fw *fw) +{ + fw->version = 0; +#ifdef CONFIG_IPW2100_LEGACY_FW_LOAD + + ipw2100_fw_free(fw); + +#else /* CONFIG_IPW2100_LEGACY_FW_LOAD */ + + if (fw->fw_entry) + release_firmware(fw->fw_entry); + fw->fw_entry = NULL; + +#endif /* CONFIG_IPW2100_LEGACY_FW_LOAD */ +} + + +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; +} + +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 + */ +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; + +#ifdef CONFIG_IPW2100_LEGACY_FW_LOAD + unsigned char data[32]; + + struct ipw2100_fw_chunk_set *cs = &fw->fw; + struct list_head *e = cs->chunk_list.next; + unsigned int size = cs->size; + + while (size > 0) { + e = ipw2100_fw_readl(e, cs, &addr); + size -= sizeof(addr); + + e = ipw2100_fw_readw(e, cs, &len); + size -= sizeof(len); + + if (len > 32) { + IPW_DEBUG_ERROR( + "Invalid firmware run-length of %d bytes\n", + len); + return -EINVAL; + } + + e = ipw2100_fw_read(e, cs, data, len); + size -= len; + + write_nic_memory(priv->net_dev, addr, len, data); + } + +#else /* CONFIG_IPW2100_LEGACY_FW_LOAD */ + + 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) { + IPW_DEBUG_ERROR( + "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; + } +#endif /* CONFIG_IPW2100_LEGACY_FW_LOAD */ + + 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; +}; + +int ipw2100_ucode_download(struct ipw2100_priv *priv, struct ipw2100_fw *fw) +{ + struct net_device *dev = priv->net_dev; + +#ifdef CONFIG_IPW2100_LEGACY_FW_LOAD + + struct ipw2100_fw_chunk_set *cs = &fw->uc; + struct list_head *e = cs->chunk_list.next; + unsigned int size = cs->size; + unsigned short uc_data; + +#else /* CONFIG_IPW2100_LEGACY_FW_LOAD */ + + const unsigned char *microcode_data = fw->uc.data; + unsigned int microcode_data_left = fw->uc.size; + +#endif /* CONFIG_IPW2100_LEGACY_FW_LOAD */ + + struct symbol_alive_response response; + int i, j; + u8 data; + + /* Symbol control */ + write_nic_word(dev, IPW2100_CONTROL_REG, 0x703); + readl((void *)(dev->base_addr)); + write_nic_word(dev, IPW2100_CONTROL_REG, 0x707); + readl((void *)(dev->base_addr)); + + /* HW config */ + write_nic_byte(dev, 0x210014, 0x72); /* fifo width =16 */ + readl((void *)(dev->base_addr)); + write_nic_byte(dev, 0x210014, 0x72); /* fifo width =16 */ + readl((void *)(dev->base_addr)); + + /* EN_CS_ACCESS bit to reset control store pointer */ + write_nic_byte(dev, 0x210000, 0x40); + readl((void *)(dev->base_addr)); + write_nic_byte(dev, 0x210000, 0x0); + readl((void *)(dev->base_addr)); + write_nic_byte(dev, 0x210000, 0x40); + readl((void *)(dev->base_addr)); + + /* copy microcode from buffer into Symbol */ + +#ifdef CONFIG_IPW2100_LEGACY_FW_LOAD + + while (size > 0) { + e = ipw2100_fw_readw(e, cs, &uc_data); + size -= sizeof(uc_data); + write_nic_byte(dev, 0x210010, uc_data & 0xFF); + write_nic_byte(dev, 0x210010, (uc_data >> 8) & 0xFF); + } + +#else /* CONFIG_IPW2100_LEGACY_FW_LOAD */ + + while (microcode_data_left > 0) { + write_nic_byte(dev, 0x210010, *microcode_data++); + write_nic_byte(dev, 0x210010, *microcode_data++); + microcode_data_left -= 2; + } + +#endif /* CONFIG_IPW2100_LEGACY_FW_LOAD */ + + /* EN_CS_ACCESS bit to reset the control store pointer */ + write_nic_byte(dev, 0x210000, 0x0); + readl((void *)(dev->base_addr)); + + /* Enable System (Reg 0) + * first enable causes garbage in RX FIFO */ + write_nic_byte(dev, 0x210000, 0x0); + readl((void *)(dev->base_addr)); + write_nic_byte(dev, 0x210000, 0x80); + readl((void *)(dev->base_addr)); + + /* Reset External Baseband Reg */ + write_nic_word(dev, IPW2100_CONTROL_REG, 0x703); + readl((void *)(dev->base_addr)); + write_nic_word(dev, IPW2100_CONTROL_REG, 0x707); + readl((void *)(dev->base_addr)); + + /* HW Config (Reg 5) */ + write_nic_byte(dev, 0x210014, 0x72); // fifo width =16 + readl((void *)(dev->base_addr)); + write_nic_byte(dev, 0x210014, 0x72); // fifo width =16 + readl((void *)(dev->base_addr)); + + /* Enable System (Reg 0) + * second enable should be OK */ + write_nic_byte(dev, 0x210000, 0x00); // clear enable system + readl((void *)(dev->base_addr)); + 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) { + IPW_DEBUG_ERROR("%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) { + IPW_DEBUG_ERROR("%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/ipw2100.h b/drivers/net/wireless/ipw2100/ipw2100.h new file mode 100644 index 000000000..ec2598d13 --- /dev/null +++ b/drivers/net/wireless/ipw2100/ipw2100.h @@ -0,0 +1,1282 @@ +/****************************************************************************** + + Copyright(c) 2003 - 2004 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 + Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 + +******************************************************************************/ +#ifndef _IPW2100_H +#define _IPW2100_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include // new driver API + +#include "ieee80211.h" + +#include + +#ifndef IRQ_NONE +typedef void irqreturn_t; +#define IRQ_NONE +#define IRQ_HANDLED +#define IRQ_RETVAL(x) +#endif + +#if WIRELESS_EXT < 17 +#define IW_QUAL_QUAL_INVALID 0x10 +#define IW_QUAL_LEVEL_INVALID 0x20 +#define IW_QUAL_NOISE_INVALID 0x40 +#endif + +#if ( LINUX_VERSION_CODE < KERNEL_VERSION(2,6,5) ) +#define pci_dma_sync_single_for_cpu pci_dma_sync_single +#define pci_dma_sync_single_for_device pci_dma_sync_single +#endif + +#ifndef HAVE_FREE_NETDEV +#define free_netdev(x) kfree(x) +#endif + + + +struct ipw2100_priv; +struct ipw2100_tx_packet; +struct ipw2100_rx_packet; + +#ifdef CONFIG_IPW_DEBUG +enum { IPW_DEBUG_ENABLED = 1 }; +extern u32 ipw2100_debug_level; +#define IPW_DEBUG(level, message...) \ +do { \ + if (ipw2100_debug_level & (level)) { \ + printk(KERN_DEBUG "ipw2100: %c %s ", \ + in_interrupt() ? 'I' : 'U', __FUNCTION__); \ + printk(message); \ + } \ +} while (0) +#else +enum { IPW_DEBUG_ENABLED = 0 }; +#define IPW_DEBUG(level, message...) do {} while (0) +#endif /* CONFIG_IPW_DEBUG */ + +#define IPW_DL_UNINIT 0x80000000 +#define IPW_DL_NONE 0x00000000 +#define IPW_DL_ALL 0x7FFFFFFF + +/* + * 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 IPW2100_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/ipw2100/debug_level + * + * you simply need to add your entry to the ipw2100_debug_levels array. + * + * If you do not see debug_level in /proc/net/ipw2100 then you do not have + * CONFIG_IPW_DEBUG defined in your kernel configuration + * + */ + +#define IPW_DL_ERROR BIT(0) +#define IPW_DL_WARNING BIT(1) +#define IPW_DL_INFO BIT(2) +#define IPW_DL_WX BIT(3) +#define IPW_DL_HC BIT(5) +#define IPW_DL_STATE BIT(6) + +#define IPW_DL_NOTIF BIT(10) +#define IPW_DL_SCAN BIT(11) +#define IPW_DL_ASSOC BIT(12) +#define IPW_DL_DROP BIT(13) + +#define IPW_DL_IOCTL BIT(14) +#define IPW_DL_RF_KILL BIT(17) + + +#define IPW_DL_MANAGE BIT(15) +#define IPW_DL_FW BIT(16) + +#define IPW_DL_FRAG BIT(21) +#define IPW_DL_WEP BIT(22) +#define IPW_DL_TX BIT(23) +#define IPW_DL_RX BIT(24) +#define IPW_DL_ISR BIT(25) +#define IPW_DL_IO BIT(26) +#define IPW_DL_TRACE BIT(28) + +#define IPW_DEBUG_ERROR(f, a...) printk(KERN_ERR DRV_NAME ": " f, ## a) +#define IPW_DEBUG_WARNING(f, a...) printk(KERN_WARNING DRV_NAME ": " f, ## a) +#define IPW_DEBUG_INFO(f...) IPW_DEBUG(IPW_DL_INFO, ## f) +#define IPW_DEBUG_WX(f...) IPW_DEBUG(IPW_DL_WX, ## f) +#define IPW_DEBUG_SCAN(f...) IPW_DEBUG(IPW_DL_SCAN, ## f) +#define IPW_DEBUG_NOTIF(f...) IPW_DEBUG(IPW_DL_NOTIF, ## f) +#define IPW_DEBUG_TRACE(f...) IPW_DEBUG(IPW_DL_TRACE, ## f) +#define IPW_DEBUG_RX(f...) IPW_DEBUG(IPW_DL_RX, ## f) +#define IPW_DEBUG_TX(f...) IPW_DEBUG(IPW_DL_TX, ## f) +#define IPW_DEBUG_ISR(f...) IPW_DEBUG(IPW_DL_ISR, ## f) +#define IPW_DEBUG_MANAGEMENT(f...) IPW_DEBUG(IPW_DL_MANAGE, ## f) +#define IPW_DEBUG_WEP(f...) IPW_DEBUG(IPW_DL_WEP, ## f) +#define IPW_DEBUG_HC(f...) IPW_DEBUG(IPW_DL_HC, ## f) +#define IPW_DEBUG_FRAG(f...) IPW_DEBUG(IPW_DL_FRAG, ## f) +#define IPW_DEBUG_FW(f...) IPW_DEBUG(IPW_DL_FW, ## f) +#define IPW_DEBUG_RF_KILL(f...) IPW_DEBUG(IPW_DL_RF_KILL, ## f) +#define IPW_DEBUG_DROP(f...) IPW_DEBUG(IPW_DL_DROP, ## f) +#define IPW_DEBUG_IO(f...) IPW_DEBUG(IPW_DL_IO, ## f) +#define IPW_DEBUG_IOCTL(f...) IPW_DEBUG(IPW_DL_IOCTL, ## f) +#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 VERIFY(f) \ +{ \ + int status = 0; \ + status = f; \ + if(status) \ + return status; \ +} + +enum { + IPW_HW_STATE_DISABLED = 1, + IPW_HW_STATE_ENABLED = 0 +}; + +struct ssid_context { + char ssid[IW_ESSID_MAX_SIZE + 1]; + int ssid_len; + unsigned char bssid[ETH_ALEN]; + int port_type; + int channel; + +}; + +extern const char *port_type_str[]; +extern const char *band_str[]; + +#define NUMBER_OF_BD_PER_COMMAND_PACKET 1 +#define NUMBER_OF_BD_PER_DATA_PACKET 2 + +#define IPW_MAX_BDS 6 +#define NUMBER_OF_OVERHEAD_BDS_PER_PACKETR 2 +#define NUMBER_OF_BDS_TO_LEAVE_FOR_COMMANDS 1 + +#define REQUIRED_SPACE_IN_RING_FOR_COMMAND_PACKET \ + (IPW_BD_QUEUE_W_R_MIN_SPARE + NUMBER_OF_BD_PER_COMMAND_PACKET) + +struct bd_status { + union { + struct { u8 nlf:1, txType:2, intEnabled:1, reserved:4;} fields; + u8 field; + } info; +} __attribute__ ((packed)); + +#define IPW_BUFDESC_LAST_FRAG 0 + +struct ipw2100_bd { + u32 host_addr; + u32 buf_length; + struct bd_status status; + /* number of fragments for frame (should be set only for + * 1st TBD) */ + u8 num_fragments; + u8 reserved[6]; +} __attribute__ ((packed)); + +#define IPW_BD_QUEUE_LENGTH(n) (1<value = (x)->hi = 0; \ + (x)->lo = 0x7fffffff; \ +} while (0) +#define SET_STAT(x,y) do { \ + (x)->value = y; \ + if ((x)->value > (x)->hi) (x)->hi = (x)->value; \ + if ((x)->value < (x)->lo) (x)->lo = (x)->value; \ +} while (0) +#define INC_STAT(x) do { if (++(x)->value > (x)->hi) (x)->hi = (x)->value; } \ +while (0) +#define DEC_STAT(x) do { if (--(x)->value < (x)->lo) (x)->lo = (x)->value; } \ +while (0) + +#define IPW2100_ERROR_QUEUE 5 + +/* Power management code: enable or disable? */ +enum { +#ifdef CONFIG_PM + IPW2100_PM_DISABLED = 0, + PM_STATE_SIZE = 16, +#else + IPW2100_PM_DISABLED = 1, + PM_STATE_SIZE = 0, +#endif +}; + +#define STATUS_POWERED BIT(0) +#define STATUS_CMD_ACTIVE BIT(1) /**< host command in progress */ +#define STATUS_RUNNING BIT(2) /* Card initialized, but not enabled */ +#define STATUS_ENABLED BIT(3) /* Card enabled -- can scan,Tx,Rx */ +#define STATUS_STOPPING BIT(4) /* Card is in shutdown phase */ +#define STATUS_INITIALIZED BIT(5) /* Card is ready for external calls */ +#define STATUS_ASSOCIATED BIT(9) +#define STATUS_INT_ENABLED BIT(11) +#define STATUS_RF_KILL_HW BIT(12) +#define STATUS_RF_KILL_SW BIT(13) +#define STATUS_RF_KILL_MASK (STATUS_RF_KILL_HW | STATUS_RF_KILL_SW) +#define STATUS_EXIT_PENDING BIT(14) + +#define STATUS_SCAN_PENDING BIT(23) +#define STATUS_SCANNING BIT(24) +#define STATUS_SCAN_ABORTING BIT(25) +#define STATUS_SCAN_COMPLETE BIT(26) +#define STATUS_WX_EVENT_PENDING BIT(27) +#define STATUS_RESET_PENDING BIT(29) +#define STATUS_SECURITY_UPDATED BIT(30) /* Security sync needed */ + + + +/* Internal NIC states */ +#define IPW_STATE_INITIALIZED BIT(0) +#define IPW_STATE_COUNTRY_FOUND BIT(1) +#define IPW_STATE_ASSOCIATED BIT(2) +#define IPW_STATE_ASSN_LOST BIT(3) +#define IPW_STATE_ASSN_CHANGED BIT(4) +#define IPW_STATE_SCAN_COMPLETE BIT(5) +#define IPW_STATE_ENTERED_PSP BIT(6) +#define IPW_STATE_LEFT_PSP BIT(7) +#define IPW_STATE_RF_KILL BIT(8) +#define IPW_STATE_DISABLED BIT(9) +#define IPW_STATE_POWER_DOWN BIT(10) +#define IPW_STATE_SCANNING BIT(11) + + + +#define CFG_STATIC_CHANNEL BIT(0) /* Restrict assoc. to single channel */ +#define CFG_STATIC_ESSID BIT(1) /* Restrict assoc. to single SSID */ +#define CFG_STATIC_BSSID BIT(2) /* Restrict assoc. to single BSSID */ +#define CFG_CUSTOM_MAC BIT(3) +#define CFG_LONG_PREAMBLE BIT(4) +#define CFG_ASSOCIATE BIT(6) +#define CFG_FIXED_RATE BIT(7) +#define CFG_ADHOC_CREATE BIT(8) +#define CFG_C3_DISABLED BIT(9) +#define CFG_PASSIVE_SCAN BIT(10) + +#define CAP_SHARED_KEY BIT(0) /* Off = OPEN */ +#define CAP_PRIVACY_ON BIT(1) /* Off = No privacy */ + +struct ipw2100_priv { + + int stop_hang_check; /* Set 1 when shutting down to kill hang_check */ + int stop_rf_kill; /* Set 1 when shutting down to kill rf_kill */ + + struct ieee80211_device *ieee; + unsigned long status; + unsigned long config; + unsigned long capability; + + /* Statistics */ + int resets; + int reset_backoff; + + /* Context */ + u8 essid[IW_ESSID_MAX_SIZE]; + u8 essid_len; + u8 bssid[ETH_ALEN]; + u8 channel; + int last_mode; + int cstate_limit; + + unsigned long connect_start; + unsigned long last_reset; + + u32 channel_mask; + u32 fatal_error; + u32 fatal_errors[IPW2100_ERROR_QUEUE]; + u32 fatal_index; + int eeprom_version; + int firmware_version; + unsigned long hw_features; + int hangs; + u32 last_rtc; + int dump_raw; /* 1 to dump raw bytes in /sys/.../memory */ + u8* snapshot[0x30]; + + u8 mandatory_bssid_mac[ETH_ALEN]; + u8 mac_addr[ETH_ALEN]; + + int power_mode; + + /* WEP data */ + struct ieee80211_security sec; + int messages_sent; + + + int short_retry_limit; + int long_retry_limit; + + u32 rts_threshold; + u32 frag_threshold; + + int in_isr; + + u32 tx_rates; + int tx_power; + u32 beacon_interval; + + char nick[IW_ESSID_MAX_SIZE + 1]; + + struct ipw2100_status_queue status_queue; + + struct statistic txq_stat; + struct statistic rxq_stat; + struct ipw2100_bd_queue rx_queue; + struct ipw2100_bd_queue tx_queue; + struct ipw2100_rx_packet *rx_buffers; + + struct statistic fw_pend_stat; + struct list_head fw_pend_list; + + struct statistic msg_free_stat; + struct statistic msg_pend_stat; + struct list_head msg_free_list; + struct list_head msg_pend_list; + struct ipw2100_tx_packet *msg_buffers; + + struct statistic tx_free_stat; + struct statistic tx_pend_stat; + struct list_head tx_free_list; + struct list_head tx_pend_list; + struct ipw2100_tx_packet *tx_buffers; + + struct ipw2100_ordinals ordinals; + + struct pci_dev *pci_dev; + + struct proc_dir_entry *dir_dev; + + struct net_device *net_dev; + struct iw_statistics wstats; + + struct tasklet_struct irq_tasklet; + + struct workqueue_struct *workqueue; + struct work_struct reset_work; + struct work_struct security_work; + struct work_struct wx_event_work; + struct work_struct hang_check; + struct work_struct rf_kill; + + u32 interrupts; + int tx_interrupts; + int rx_interrupts; + int inta_other; + + spinlock_t low_lock; + + wait_queue_head_t wait_command_queue; +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,10) + u32 pm_state[PM_STATE_SIZE]; +#endif +}; + + +/********************************************************* + * Host Command -> From Driver to FW + *********************************************************/ + +/** + * Host command identifiers + */ +#define HOST_COMPLETE 2 +#define SYSTEM_CONFIG 6 +#define SSID 8 +#define MANDATORY_BSSID 9 +#define AUTHENTICATION_TYPE 10 +#define ADAPTER_ADDRESS 11 +#define PORT_TYPE 12 +#define INTERNATIONAL_MODE 13 +#define CHANNEL 14 +#define RTS_THRESHOLD 15 +#define FRAG_THRESHOLD 16 +#define POWER_MODE 17 +#define TX_RATES 18 +#define BASIC_TX_RATES 19 +#define WEP_KEY_INFO 20 +#define WEP_KEY_INDEX 25 +#define WEP_FLAGS 26 +#define ADD_MULTICAST 27 +#define CLEAR_ALL_MULTICAST 28 +#define BEACON_INTERVAL 29 +#define ATIM_WINDOW 30 +#define CLEAR_STATISTICS 31 +#define SEND 33 +#define TX_POWER_INDEX 36 +#define BROADCAST_SCAN 43 +#define CARD_DISABLE 44 +#define PREFERRED_BSSID 45 +#define SET_SCAN_OPTIONS 46 +#define SCAN_DWELL_TIME 47 +#define SWEEP_TABLE 48 +#define AP_OR_STATION_TABLE 49 +#define GROUP_ORDINALS 50 +#define SHORT_RETRY_LIMIT 51 +#define LONG_RETRY_LIMIT 52 + +#define HOST_PRE_POWER_DOWN 58 +#define CARD_DISABLE_PHY_OFF 61 +#define MSDU_TX_RATES 62 + + +// Rogue AP Detection +#define SET_STATION_STAT_BITS 64 +#define CLEAR_STATIONS_STAT_BITS 65 +#define LEAP_ROGUE_MODE 66 //TODO tbw replaced by CFG_LEAP_ROGUE_AP +#define SET_SECURITY_INFORMATION 67 +#define DISASSOCIATION_BSSID 68 +#define SET_WPA_IE 69 + + + +// system configuration bit mask: +//#define IPW_CFG_ANTENNA_SETTING 0x03 +//#define IPW_CFG_ANTENNA_A 0x01 +//#define IPW_CFG_ANTENNA_B 0x02 +#define IPW_CFG_PROMISCUOUS 0x00004 +//#define IPW_CFG_TX_STATUS_ENABLE 0x00008 +#define IPW_CFG_PREAMBLE_AUTO 0x00010 +#define IPW_CFG_IBSS_AUTO_START 0x00020 +//#define IPW_CFG_KERBEROS_ENABLE 0x00040 +#define IPW_CFG_LOOPBACK 0x00100 +//#define IPW_CFG_WNMP_PING_PASS 0x00200 +//#define IPW_CFG_DEBUG_ENABLE 0x00400 +#define IPW_CFG_ANSWER_BCSSID_PROBE 0x00800 +//#define IPW_CFG_BT_PRIORITY 0x01000 +#define IPW_CFG_BT_SIDEBAND_SIGNAL 0x02000 +#define IPW_CFG_802_1x_ENABLE 0x04000 +#define IPW_CFG_BSS_MASK 0x08000 +#define IPW_CFG_IBSS_MASK 0x10000 +//#define IPW_CFG_DYNAMIC_CW 0x10000 + +#define IPW_SCAN_NOASSOCIATE BIT(0) +#define IPW_SCAN_MIXED_CELL BIT(1) +/* RESERVED BIT(2) */ +#define IPW_SCAN_PASSIVE BIT(3) + +#define IPW_NIC_FATAL_ERROR 0x2A7F0 +#define IPW_ERROR_ADDR(x) (x & 0x3FFFF) +#define IPW_ERROR_CODE(x) ((x & 0xFF000000) >> 24) +#define IPW2100_ERR_C3_CORRUPTION (0x10 << 24) +#define IPW2100_ERR_MSG_TIMEOUT (0x11 << 24) +#define IPW2100_ERR_FW_LOAD (0x12 << 24) + +#define IPW_MEM_SRAM_HOST_SHARED_LOWER_BOUND 0x200 +#define IPW_MEM_SRAM_HOST_INTERRUPT_AREA_LOWER_BOUND IPW_MEM_SRAM_HOST_SHARED_LOWER_BOUND + 0x0D80 + +#define IPW_MEM_HOST_SHARED_RX_BD_BASE (IPW_MEM_SRAM_HOST_SHARED_LOWER_BOUND + 0x40) +#define IPW_MEM_HOST_SHARED_RX_STATUS_BASE (IPW_MEM_SRAM_HOST_SHARED_LOWER_BOUND + 0x44) +#define IPW_MEM_HOST_SHARED_RX_BD_SIZE (IPW_MEM_SRAM_HOST_SHARED_LOWER_BOUND + 0x48) +#define IPW_MEM_HOST_SHARED_RX_READ_INDEX (IPW_MEM_SRAM_HOST_SHARED_LOWER_BOUND + 0xa0) + +#define IPW_MEM_HOST_SHARED_TX_QUEUE_BD_BASE (IPW_MEM_SRAM_HOST_SHARED_LOWER_BOUND + 0x00) +#define IPW_MEM_HOST_SHARED_TX_QUEUE_BD_SIZE (IPW_MEM_SRAM_HOST_SHARED_LOWER_BOUND + 0x04) +#define IPW_MEM_HOST_SHARED_TX_QUEUE_READ_INDEX (IPW_MEM_SRAM_HOST_SHARED_LOWER_BOUND + 0x80) + +#define IPW_MEM_HOST_SHARED_RX_WRITE_INDEX \ + (IPW_MEM_SRAM_HOST_INTERRUPT_AREA_LOWER_BOUND + 0x20) + +#define IPW_MEM_HOST_SHARED_TX_QUEUE_WRITE_INDEX \ + (IPW_MEM_SRAM_HOST_INTERRUPT_AREA_LOWER_BOUND) + + +#if 0 +#define IPW_MEM_HOST_SHARED_TX_QUEUE_0_BD_BASE (IPW_MEM_SRAM_HOST_SHARED_LOWER_BOUND + 0x00) +#define IPW_MEM_HOST_SHARED_TX_QUEUE_0_BD_SIZE (IPW_MEM_SRAM_HOST_SHARED_LOWER_BOUND + 0x04) +#define IPW_MEM_HOST_SHARED_TX_QUEUE_1_BD_BASE (IPW_MEM_SRAM_HOST_SHARED_LOWER_BOUND + 0x08) +#define IPW_MEM_HOST_SHARED_TX_QUEUE_1_BD_SIZE (IPW_MEM_SRAM_HOST_SHARED_LOWER_BOUND + 0x0c) +#define IPW_MEM_HOST_SHARED_TX_QUEUE_2_BD_BASE (IPW_MEM_SRAM_HOST_SHARED_LOWER_BOUND + 0x10) +#define IPW_MEM_HOST_SHARED_TX_QUEUE_2_BD_SIZE (IPW_MEM_SRAM_HOST_SHARED_LOWER_BOUND + 0x14) +#define IPW_MEM_HOST_SHARED_TX_QUEUE_3_BD_BASE (IPW_MEM_SRAM_HOST_SHARED_LOWER_BOUND + 0x18) +#define IPW_MEM_HOST_SHARED_TX_QUEUE_3_BD_SIZE (IPW_MEM_SRAM_HOST_SHARED_LOWER_BOUND + 0x1c) +#define IPW_MEM_HOST_SHARED_TX_QUEUE_0_READ_INDEX (IPW_MEM_SRAM_HOST_SHARED_LOWER_BOUND + 0x80) +#define IPW_MEM_HOST_SHARED_TX_QUEUE_1_READ_INDEX (IPW_MEM_SRAM_HOST_SHARED_LOWER_BOUND + 0x84) +#define IPW_MEM_HOST_SHARED_TX_QUEUE_2_READ_INDEX (IPW_MEM_SRAM_HOST_SHARED_LOWER_BOUND + 0x88) +#define IPW_MEM_HOST_SHARED_TX_QUEUE_3_READ_INDEX (IPW_MEM_SRAM_HOST_SHARED_LOWER_BOUND + 0x8c) + +#define IPW_MEM_HOST_SHARED_TX_QUEUE_BD_BASE(QueueNum) \ + (IPW_MEM_SRAM_HOST_SHARED_LOWER_BOUND + (QueueNum<<3)) +#define IPW_MEM_HOST_SHARED_TX_QUEUE_BD_SIZE(QueueNum) \ + (IPW_MEM_SRAM_HOST_SHARED_LOWER_BOUND + 0x0004+(QueueNum<<3)) +#define IPW_MEM_HOST_SHARED_TX_QUEUE_READ_INDEX(QueueNum) \ + (IPW_MEM_SRAM_HOST_SHARED_LOWER_BOUND + 0x0080+(QueueNum<<2)) + +#define IPW_MEM_HOST_SHARED_TX_QUEUE_0_WRITE_INDEX \ + (IPW_MEM_SRAM_HOST_INTERRUPT_AREA_LOWER_BOUND + 0x00) +#define IPW_MEM_HOST_SHARED_TX_QUEUE_1_WRITE_INDEX \ + (IPW_MEM_SRAM_HOST_INTERRUPT_AREA_LOWER_BOUND + 0x04) +#define IPW_MEM_HOST_SHARED_TX_QUEUE_2_WRITE_INDEX \ + (IPW_MEM_SRAM_HOST_INTERRUPT_AREA_LOWER_BOUND + 0x08) +#define IPW_MEM_HOST_SHARED_TX_QUEUE_3_WRITE_INDEX \ + (IPW_MEM_SRAM_HOST_INTERRUPT_AREA_LOWER_BOUND + 0x0c) +#define IPW_MEM_HOST_SHARED_SLAVE_MODE_INT_REGISTER \ + (IPW_MEM_SRAM_HOST_INTERRUPT_AREA_LOWER_BOUND + 0x78) + +#endif + +#define IPW_MEM_HOST_SHARED_ORDINALS_TABLE_1 (IPW_MEM_SRAM_HOST_SHARED_LOWER_BOUND + 0x180) +#define IPW_MEM_HOST_SHARED_ORDINALS_TABLE_2 (IPW_MEM_SRAM_HOST_SHARED_LOWER_BOUND + 0x184) + +#define IPW2100_INTA_TX_TRANSFER (0x00000001) // Bit 0 (LSB) +#define IPW2100_INTA_RX_TRANSFER (0x00000002) // Bit 1 +#define IPW2100_INTA_TX_COMPLETE (0x00000004) // Bit 2 +#define IPW2100_INTA_EVENT_INTERRUPT (0x00000008) // Bit 3 +#define IPW2100_INTA_STATUS_CHANGE (0x00000010) // Bit 4 +#define IPW2100_INTA_BEACON_PERIOD_EXPIRED (0x00000020) // Bit 5 +#define IPW2100_INTA_SLAVE_MODE_HOST_COMMAND_DONE (0x00010000) // Bit 16 +#define IPW2100_INTA_FW_INIT_DONE (0x01000000) // Bit 24 +#define IPW2100_INTA_FW_CALIBRATION_CALC (0x02000000) // Bit 25 +#define IPW2100_INTA_FATAL_ERROR (0x40000000) // Bit 30 +#define IPW2100_INTA_PARITY_ERROR (0x80000000) // Bit 31 (MSB) + +#define IPW_AUX_HOST_RESET_REG_PRINCETON_RESET (0x00000001) +#define IPW_AUX_HOST_RESET_REG_FORCE_NMI (0x00000002) +#define IPW_AUX_HOST_RESET_REG_PCI_HOST_CLUSTER_FATAL_NMI (0x00000004) +#define IPW_AUX_HOST_RESET_REG_CORE_FATAL_NMI (0x00000008) +#define IPW_AUX_HOST_RESET_REG_SW_RESET (0x00000080) +#define IPW_AUX_HOST_RESET_REG_MASTER_DISABLED (0x00000100) +#define IPW_AUX_HOST_RESET_REG_STOP_MASTER (0x00000200) + +#define IPW_AUX_HOST_GP_CNTRL_BIT_CLOCK_READY (0x00000001) // Bit 0 (LSB) +#define IPW_AUX_HOST_GP_CNTRL_BIT_HOST_ALLOWS_STANDBY (0x00000002) // Bit 1 +#define IPW_AUX_HOST_GP_CNTRL_BIT_INIT_DONE (0x00000004) // Bit 2 +#define IPW_AUX_HOST_GP_CNTRL_BITS_SYS_CONFIG (0x000007c0) // Bits 6-10 +#define IPW_AUX_HOST_GP_CNTRL_BIT_BUS_TYPE (0x00000200) // Bit 9 +#define IPW_AUX_HOST_GP_CNTRL_BIT_BAR0_BLOCK_SIZE (0x00000400) // Bit 10 +#define IPW_AUX_HOST_GP_CNTRL_BIT_USB_MODE (0x20000000) // Bit 29 +#define IPW_AUX_HOST_GP_CNTRL_BIT_HOST_FORCES_SYS_CLK (0x40000000) // Bit 30 +#define IPW_AUX_HOST_GP_CNTRL_BIT_FW_FORCES_SYS_CLK (0x80000000) // Bit 31 (MSB) + +#define IPW_BIT_GPIO_GPIO1_MASK 0x0000000C +#define IPW_BIT_GPIO_GPIO3_MASK 0x000000C0 +#define IPW_BIT_GPIO_GPIO1_ENABLE 0x00000008 +#define IPW_BIT_GPIO_RF_KILL 0x00010000 + +#define IPW_BIT_GPIO_LED_OFF 0x00002000 // Bit 13 = 1 + +#define IPW_REG_DOMAIN_0_OFFSET 0x0000 +#define IPW_REG_DOMAIN_1_OFFSET IPW_MEM_SRAM_HOST_SHARED_LOWER_BOUND + +#define IPW_REG_INTA IPW_REG_DOMAIN_0_OFFSET + 0x0008 +#define IPW_REG_INTA_MASK IPW_REG_DOMAIN_0_OFFSET + 0x000C +#define IPW_REG_INDIRECT_ACCESS_ADDRESS IPW_REG_DOMAIN_0_OFFSET + 0x0010 +#define IPW_REG_INDIRECT_ACCESS_DATA IPW_REG_DOMAIN_0_OFFSET + 0x0014 +#define IPW_REG_AUTOINCREMENT_ADDRESS IPW_REG_DOMAIN_0_OFFSET + 0x0018 +#define IPW_REG_AUTOINCREMENT_DATA IPW_REG_DOMAIN_0_OFFSET + 0x001C +#define IPW_REG_RESET_REG IPW_REG_DOMAIN_0_OFFSET + 0x0020 +#define IPW_REG_GP_CNTRL IPW_REG_DOMAIN_0_OFFSET + 0x0024 +#define IPW_REG_GPIO IPW_REG_DOMAIN_0_OFFSET + 0x0030 +#define IPW_REG_FW_TYPE IPW_REG_DOMAIN_1_OFFSET + 0x0188 +#define IPW_REG_FW_VERSION IPW_REG_DOMAIN_1_OFFSET + 0x018C +#define IPW_REG_FW_COMPATABILITY_VERSION IPW_REG_DOMAIN_1_OFFSET + 0x0190 + +#define IPW_REG_INDIRECT_ADDR_MASK 0x00FFFFFC + +#define IPW_INTERRUPT_MASK 0xC1010013 + +#define IPW2100_CONTROL_REG 0x220000 +#define IPW2100_CONTROL_PHY_OFF 0x8 + +#define IPW2100_COMMAND 0x00300004 +#define IPW2100_COMMAND_PHY_ON 0x0 +#define IPW2100_COMMAND_PHY_OFF 0x1 + +/* in DEBUG_AREA, values of memory always 0xd55555d5 */ +#define IPW_REG_DOA_DEBUG_AREA_START IPW_REG_DOMAIN_0_OFFSET + 0x0090 +#define IPW_REG_DOA_DEBUG_AREA_END IPW_REG_DOMAIN_0_OFFSET + 0x00FF +#define IPW_DATA_DOA_DEBUG_VALUE 0xd55555d5 + +#define IPW_INTERNAL_REGISTER_HALT_AND_RESET 0x003000e0 + +#define IPW_WAIT_CLOCK_STABILIZATION_DELAY 50 // micro seconds +#define IPW_WAIT_RESET_ARC_COMPLETE_DELAY 10 // micro seconds +#define IPW_WAIT_RESET_MASTER_ASSERT_COMPLETE_DELAY 10 // micro seconds + +// BD ring queue read/write difference +#define IPW_BD_QUEUE_W_R_MIN_SPARE 2 + +#define IPW_CACHE_LINE_LENGTH_DEFAULT 0x80 + +#define IPW_CARD_DISABLE_PHY_OFF_COMPLETE_WAIT 100 // 100 milli +#define IPW_PREPARE_POWER_DOWN_COMPLETE_WAIT 100 // 100 milli + + + + +#define IPW_HEADER_802_11_SIZE sizeof(struct ieee80211_header_data) +#define IPW_MAX_80211_PAYLOAD_SIZE 2304U +#define IPW_MAX_802_11_PAYLOAD_LENGTH 2312 +#define IPW_MAX_ACCEPTABLE_TX_FRAME_LENGTH 1536 +#define IPW_MIN_ACCEPTABLE_RX_FRAME_LENGTH 60 +#define IPW_MAX_ACCEPTABLE_RX_FRAME_LENGTH \ + (IPW_MAX_ACCEPTABLE_TX_FRAME_LENGTH + IPW_HEADER_802_11_SIZE - \ + sizeof(struct ethhdr)) + +#define IPW_802_11_FCS_LENGTH 4 +#define IPW_RX_NIC_BUFFER_LENGTH \ + (IPW_MAX_802_11_PAYLOAD_LENGTH + IPW_HEADER_802_11_SIZE + \ + IPW_802_11_FCS_LENGTH) + +#define IPW_802_11_PAYLOAD_OFFSET \ + (sizeof(struct ieee80211_header_data) + \ + sizeof(struct ieee80211_snap_hdr)) + +struct ipw2100_rx { + union { + unsigned char payload[IPW_RX_NIC_BUFFER_LENGTH]; + struct ieee80211_hdr header; + u32 status; + struct ipw2100_notification notification; + struct ipw2100_cmd_header command; + } rx_data; +} __attribute__ ((packed)); + +// Bit 0-7 are for 802.11b tx rates - . Bit 5-7 are reserved +#define TX_RATE_1_MBIT 0x0001 +#define TX_RATE_2_MBIT 0x0002 +#define TX_RATE_5_5_MBIT 0x0004 +#define TX_RATE_11_MBIT 0x0008 +#define TX_RATE_MASK 0x000F +#define DEFAULT_TX_RATES 0x000F + +#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_AUTO 0x06 +#define IPW_POWER_MASK 0x0F +#define IPW_POWER_ENABLED 0x10 +#define IPW_POWER_LEVEL(x) ((x) & IPW_POWER_MASK) + +#define IPW_TX_POWER_AUTO 0 +#define IPW_TX_POWER_ENHANCED 1 + +#define IPW_TX_POWER_DEFAULT 32 +#define IPW_TX_POWER_MIN 0 +#define IPW_TX_POWER_MAX 16 +#define IPW_TX_POWER_MIN_DBM (-12) +#define IPW_TX_POWER_MAX_DBM 16 + +#define FW_SCAN_DONOT_ASSOCIATE 0x0001 // Dont Attempt to Associate after Scan +#define FW_SCAN_PASSIVE 0x0008 // Force PASSSIVE Scan + +#define REG_MIN_CHANNEL 0 +#define REG_MAX_CHANNEL 14 + +#define REG_CHANNEL_MASK 0x00003FFF +#define IPW_IBSS_11B_DEFAULT_MASK 0x87ff + +#define DIVERSITY_EITHER 0 // Use both antennas +#define DIVERSITY_ANTENNA_A 1 // Use antenna A +#define DIVERSITY_ANTENNA_B 2 // Use antenna B + + +#define HOST_COMMAND_WAIT 0 +#define HOST_COMMAND_NO_WAIT 1 + +#define LOCK_NONE 0 +#define LOCK_DRIVER 1 +#define LOCK_FW 2 + +#define TYPE_SWEEP_ORD 0x000D +#define TYPE_IBSS_STTN_ORD 0x000E +#define TYPE_BSS_AP_ORD 0x000F +#define TYPE_RAW_BEACON_ENTRY 0x0010 +#define TYPE_CALIBRATION_DATA 0x0011 +#define TYPE_ROGUE_AP_DATA 0x0012 +#define TYPE_ASSOCIATION_REQUEST 0x0013 +#define TYPE_REASSOCIATION_REQUEST 0x0014 + + +#define HW_FEATURE_RFKILL (0x0001) +#define RF_KILLSWITCH_OFF (1) +#define RF_KILLSWITCH_ON (0) + +#define IPW_COMMAND_POOL_SIZE 40 + +#define IPW_START_ORD_TAB_1 1 +#define IPW_START_ORD_TAB_2 1000 + +#define IPW_ORD_TAB_1_ENTRY_SIZE sizeof(u32) + +#define IS_ORDINAL_TABLE_ONE(mgr,id) \ + ((id >= IPW_START_ORD_TAB_1) && (id < mgr->table1_size)) +#define IS_ORDINAL_TABLE_TWO(mgr,id) \ + ((id >= IPW_START_ORD_TAB_2) && (id < (mgr->table2_size + IPW_START_ORD_TAB_2))) + +#define BSS_ID_LENGTH 6 + +// Fixed size data: Ordinal Table 1 +typedef enum _ORDINAL_TABLE_1 { // NS - means Not Supported by FW +// Transmit statistics + IPW_ORD_STAT_TX_HOST_REQUESTS = 1,// # of requested Host Tx's (MSDU) + IPW_ORD_STAT_TX_HOST_COMPLETE, // # of successful Host Tx's (MSDU) + IPW_ORD_STAT_TX_DIR_DATA, // # of successful Directed Tx's (MSDU) + + IPW_ORD_STAT_TX_DIR_DATA1 = 4, // # of successful Directed Tx's (MSDU) @ 1MB + IPW_ORD_STAT_TX_DIR_DATA2, // # of successful Directed Tx's (MSDU) @ 2MB + IPW_ORD_STAT_TX_DIR_DATA5_5, // # of successful Directed Tx's (MSDU) @ 5_5MB + IPW_ORD_STAT_TX_DIR_DATA11, // # of successful Directed Tx's (MSDU) @ 11MB + IPW_ORD_STAT_TX_DIR_DATA22, // # of successful Directed Tx's (MSDU) @ 22MB + + IPW_ORD_STAT_TX_NODIR_DATA1 = 13,// # of successful Non_Directed Tx's (MSDU) @ 1MB + IPW_ORD_STAT_TX_NODIR_DATA2, // # of successful Non_Directed Tx's (MSDU) @ 2MB + IPW_ORD_STAT_TX_NODIR_DATA5_5, // # of successful Non_Directed Tx's (MSDU) @ 5.5MB + IPW_ORD_STAT_TX_NODIR_DATA11, // # of successful Non_Directed Tx's (MSDU) @ 11MB + + IPW_ORD_STAT_NULL_DATA = 21, // # of successful NULL data Tx's + IPW_ORD_STAT_TX_RTS, // # of successful Tx RTS + IPW_ORD_STAT_TX_CTS, // # of successful Tx CTS + IPW_ORD_STAT_TX_ACK, // # of successful Tx ACK + IPW_ORD_STAT_TX_ASSN, // # of successful Association Tx's + IPW_ORD_STAT_TX_ASSN_RESP, // # of successful Association response Tx's + IPW_ORD_STAT_TX_REASSN, // # of successful Reassociation Tx's + IPW_ORD_STAT_TX_REASSN_RESP, // # of successful Reassociation response Tx's + IPW_ORD_STAT_TX_PROBE, // # of probes successfully transmitted + IPW_ORD_STAT_TX_PROBE_RESP, // # of probe responses successfully transmitted + IPW_ORD_STAT_TX_BEACON, // # of tx beacon + IPW_ORD_STAT_TX_ATIM, // # of Tx ATIM + IPW_ORD_STAT_TX_DISASSN, // # of successful Disassociation TX + IPW_ORD_STAT_TX_AUTH, // # of successful Authentication Tx + IPW_ORD_STAT_TX_DEAUTH, // # of successful Deauthentication TX + + IPW_ORD_STAT_TX_TOTAL_BYTES = 41,// Total successful Tx data bytes + IPW_ORD_STAT_TX_RETRIES, // # of Tx retries + IPW_ORD_STAT_TX_RETRY1, // # of Tx retries at 1MBPS + IPW_ORD_STAT_TX_RETRY2, // # of Tx retries at 2MBPS + IPW_ORD_STAT_TX_RETRY5_5, // # of Tx retries at 5.5MBPS + IPW_ORD_STAT_TX_RETRY11, // # of Tx retries at 11MBPS + + IPW_ORD_STAT_TX_FAILURES = 51, // # of Tx Failures + IPW_ORD_STAT_TX_ABORT_AT_HOP, //NS // # of Tx's aborted at hop time + IPW_ORD_STAT_TX_MAX_TRIES_IN_HOP,// # of times max tries in a hop failed + IPW_ORD_STAT_TX_ABORT_LATE_DMA, //NS // # of times tx aborted due to late dma setup + IPW_ORD_STAT_TX_ABORT_STX, //NS // # of times backoff aborted + IPW_ORD_STAT_TX_DISASSN_FAIL, // # of times disassociation failed + IPW_ORD_STAT_TX_ERR_CTS, // # of missed/bad CTS frames + IPW_ORD_STAT_TX_BPDU, //NS // # of spanning tree BPDUs sent + IPW_ORD_STAT_TX_ERR_ACK, // # of tx err due to acks + + // Receive statistics + IPW_ORD_STAT_RX_HOST = 61, // # of packets passed to host + IPW_ORD_STAT_RX_DIR_DATA, // # of directed packets + IPW_ORD_STAT_RX_DIR_DATA1, // # of directed packets at 1MB + IPW_ORD_STAT_RX_DIR_DATA2, // # of directed packets at 2MB + IPW_ORD_STAT_RX_DIR_DATA5_5, // # of directed packets at 5.5MB + IPW_ORD_STAT_RX_DIR_DATA11, // # of directed packets at 11MB + IPW_ORD_STAT_RX_DIR_DATA22, // # of directed packets at 22MB + + IPW_ORD_STAT_RX_NODIR_DATA = 71,// # of nondirected packets + IPW_ORD_STAT_RX_NODIR_DATA1, // # of nondirected packets at 1MB + IPW_ORD_STAT_RX_NODIR_DATA2, // # of nondirected packets at 2MB + IPW_ORD_STAT_RX_NODIR_DATA5_5, // # of nondirected packets at 5.5MB + IPW_ORD_STAT_RX_NODIR_DATA11, // # of nondirected packets at 11MB + + IPW_ORD_STAT_RX_NULL_DATA = 80, // # of null data rx's + IPW_ORD_STAT_RX_POLL, //NS // # of poll rx + IPW_ORD_STAT_RX_RTS, // # of Rx RTS + IPW_ORD_STAT_RX_CTS, // # of Rx CTS + IPW_ORD_STAT_RX_ACK, // # of Rx ACK + IPW_ORD_STAT_RX_CFEND, // # of Rx CF End + IPW_ORD_STAT_RX_CFEND_ACK, // # of Rx CF End + CF Ack + IPW_ORD_STAT_RX_ASSN, // # of Association Rx's + IPW_ORD_STAT_RX_ASSN_RESP, // # of Association response Rx's + IPW_ORD_STAT_RX_REASSN, // # of Reassociation Rx's + IPW_ORD_STAT_RX_REASSN_RESP, // # of Reassociation response Rx's + IPW_ORD_STAT_RX_PROBE, // # of probe Rx's + IPW_ORD_STAT_RX_PROBE_RESP, // # of probe response Rx's + IPW_ORD_STAT_RX_BEACON, // # of Rx beacon + IPW_ORD_STAT_RX_ATIM, // # of Rx ATIM + IPW_ORD_STAT_RX_DISASSN, // # of disassociation Rx + IPW_ORD_STAT_RX_AUTH, // # of authentication Rx + IPW_ORD_STAT_RX_DEAUTH, // # of deauthentication Rx + + IPW_ORD_STAT_RX_TOTAL_BYTES = 101,// Total rx data bytes received + IPW_ORD_STAT_RX_ERR_CRC, // # of packets with Rx CRC error + IPW_ORD_STAT_RX_ERR_CRC1, // # of Rx CRC errors at 1MB + IPW_ORD_STAT_RX_ERR_CRC2, // # of Rx CRC errors at 2MB + IPW_ORD_STAT_RX_ERR_CRC5_5, // # of Rx CRC errors at 5.5MB + IPW_ORD_STAT_RX_ERR_CRC11, // # of Rx CRC errors at 11MB + + IPW_ORD_STAT_RX_DUPLICATE1 = 112, // # of duplicate rx packets at 1MB + IPW_ORD_STAT_RX_DUPLICATE2, // # of duplicate rx packets at 2MB + IPW_ORD_STAT_RX_DUPLICATE5_5, // # of duplicate rx packets at 5.5MB + IPW_ORD_STAT_RX_DUPLICATE11, // # of duplicate rx packets at 11MB + IPW_ORD_STAT_RX_DUPLICATE = 119, // # of duplicate rx packets + + IPW_ORD_PERS_DB_LOCK = 120, // # locking fw permanent db + IPW_ORD_PERS_DB_SIZE, // # size of fw permanent db + IPW_ORD_PERS_DB_ADDR, // # address of fw permanent db + IPW_ORD_STAT_RX_INVALID_PROTOCOL, // # of rx frames with invalid protocol + IPW_ORD_SYS_BOOT_TIME, // # Boot time + IPW_ORD_STAT_RX_NO_BUFFER, // # of rx frames rejected due to no buffer + IPW_ORD_STAT_RX_ABORT_LATE_DMA, //NS // # of rx frames rejected due to dma setup too late + IPW_ORD_STAT_RX_ABORT_AT_HOP, //NS // # of rx frames aborted due to hop + IPW_ORD_STAT_RX_MISSING_FRAG, // # of rx frames dropped due to missing fragment + IPW_ORD_STAT_RX_ORPHAN_FRAG, // # of rx frames dropped due to non-sequential fragment + IPW_ORD_STAT_RX_ORPHAN_FRAME, // # of rx frames dropped due to unmatched 1st frame + IPW_ORD_STAT_RX_FRAG_AGEOUT, // # of rx frames dropped due to uncompleted frame + IPW_ORD_STAT_RX_BAD_SSID, //NS // Bad SSID (unused) + IPW_ORD_STAT_RX_ICV_ERRORS, // # of ICV errors during decryption + +// PSP Statistics + IPW_ORD_STAT_PSP_SUSPENSION = 137,// # of times adapter suspended + IPW_ORD_STAT_PSP_BCN_TIMEOUT, // # of beacon timeout + IPW_ORD_STAT_PSP_POLL_TIMEOUT, // # of poll response timeouts + IPW_ORD_STAT_PSP_NONDIR_TIMEOUT,// # of timeouts waiting for last broadcast/muticast pkt + IPW_ORD_STAT_PSP_RX_DTIMS, // # of PSP DTIMs received + IPW_ORD_STAT_PSP_RX_TIMS, // # of PSP TIMs received + IPW_ORD_STAT_PSP_STATION_ID, // PSP Station ID + +// Association and roaming + IPW_ORD_LAST_ASSN_TIME = 147, // RTC time of last association + IPW_ORD_STAT_PERCENT_MISSED_BCNS,// current calculation of % missed beacons + IPW_ORD_STAT_PERCENT_RETRIES, // current calculation of % missed tx retries + IPW_ORD_ASSOCIATED_AP_PTR, // If associated, this is ptr to the associated + // AP table entry. set to 0 if not associated + IPW_ORD_AVAILABLE_AP_CNT, // # of AP's decsribed in the AP table + IPW_ORD_AP_LIST_PTR, // Ptr to list of available APs + IPW_ORD_STAT_AP_ASSNS, // # of associations + IPW_ORD_STAT_ASSN_FAIL, // # of association failures + IPW_ORD_STAT_ASSN_RESP_FAIL, // # of failuresdue to response fail + IPW_ORD_STAT_FULL_SCANS, // # of full scans + + IPW_ORD_CARD_DISABLED, // # Card Disabled + IPW_ORD_STAT_ROAM_INHIBIT, // # of times roaming was inhibited due to ongoing activity + IPW_FILLER_40, + IPW_ORD_RSSI_AT_ASSN = 160, // RSSI of associated AP at time of association + IPW_ORD_STAT_ASSN_CAUSE1, // # of reassociations due to no tx from AP in last N + // hops or no prob_ responses in last 3 minutes + IPW_ORD_STAT_ASSN_CAUSE2, // # of reassociations due to poor tx/rx quality + IPW_ORD_STAT_ASSN_CAUSE3, // # of reassociations due to tx/rx quality with excessive + // load at the AP + IPW_ORD_STAT_ASSN_CAUSE4, // # of reassociations due to AP RSSI level fell below + // eligible group + IPW_ORD_STAT_ASSN_CAUSE5, // # of reassociations due to load leveling + IPW_ORD_STAT_ASSN_CAUSE6, //NS // # of reassociations due to dropped by Ap + IPW_FILLER_41, + IPW_FILLER_42, + IPW_FILLER_43, + IPW_ORD_STAT_AUTH_FAIL, // # of times authentication failed + IPW_ORD_STAT_AUTH_RESP_FAIL, // # of times authentication response failed + IPW_ORD_STATION_TABLE_CNT, // # of entries in association table + +// Other statistics + IPW_ORD_RSSI_AVG_CURR = 173, // Current avg RSSI + IPW_ORD_STEST_RESULTS_CURR, //NS // Current self test results word + IPW_ORD_STEST_RESULTS_CUM, //NS // Cummulative self test results word + IPW_ORD_SELF_TEST_STATUS, //NS // + IPW_ORD_POWER_MGMT_MODE, // Power mode - 0=CAM, 1=PSP + IPW_ORD_POWER_MGMT_INDEX, //NS // + IPW_ORD_COUNTRY_CODE, // IEEE country code as recv'd from beacon + IPW_ORD_COUNTRY_CHANNELS, // channels suported by country +// IPW_ORD_COUNTRY_CHANNELS: +// For 11b the lower 2-byte are used for channels from 1-14 +// and the higher 2-byte are not used. + IPW_ORD_RESET_CNT, // # of adapter resets (warm) + IPW_ORD_BEACON_INTERVAL, // Beacon interval + + IPW_ORD_PRINCETON_VERSION = 184, //NS // Princeton Version + IPW_ORD_ANTENNA_DIVERSITY, // TRUE if antenna diversity is disabled + IPW_ORD_CCA_RSSI, //NS // CCA RSSI value (factory programmed) + IPW_ORD_STAT_EEPROM_UPDATE, //NS // # of times config EEPROM updated + IPW_ORD_DTIM_PERIOD, // # of beacon intervals between DTIMs + IPW_ORD_OUR_FREQ, // current radio freq lower digits - channel ID + + IPW_ORD_RTC_TIME = 190, // current RTC time + IPW_ORD_PORT_TYPE, // operating mode + IPW_ORD_CURRENT_TX_RATE, // current tx rate + IPW_ORD_SUPPORTED_RATES, // Bitmap of supported tx rates + IPW_ORD_ATIM_WINDOW, // current ATIM Window + IPW_ORD_BASIC_RATES, // bitmap of basic tx rates + IPW_ORD_NIC_HIGHEST_RATE, // bitmap of basic tx rates + IPW_ORD_AP_HIGHEST_RATE, // bitmap of basic tx rates + IPW_ORD_CAPABILITIES, // Management frame capability field + IPW_ORD_AUTH_TYPE, // Type of authentication + IPW_ORD_RADIO_TYPE, // Adapter card platform type + IPW_ORD_RTS_THRESHOLD = 201, // Min length of packet after which RTS handshaking is used + IPW_ORD_INT_MODE, // International mode + IPW_ORD_FRAGMENTATION_THRESHOLD, // protocol frag threshold + IPW_ORD_EEPROM_SRAM_DB_BLOCK_START_ADDRESS, // EEPROM offset in SRAM + IPW_ORD_EEPROM_SRAM_DB_BLOCK_SIZE, // EEPROM size in SRAM + IPW_ORD_EEPROM_SKU_CAPABILITY, // EEPROM SKU Capability 206 = + IPW_ORD_EEPROM_IBSS_11B_CHANNELS, // EEPROM IBSS 11b channel set + + IPW_ORD_MAC_VERSION = 209, // MAC Version + IPW_ORD_MAC_REVISION, // MAC Revision + IPW_ORD_RADIO_VERSION, // Radio Version + IPW_ORD_NIC_MANF_DATE_TIME, // MANF Date/Time STAMP + IPW_ORD_UCODE_VERSION, // Ucode Version + IPW_ORD_HW_RF_SWITCH_STATE = 214, // HW RF Kill Switch State +} ORDINALTABLE1; +//ENDOF TABLE1 + +// ordinal table 2 +// Variable length data: +#define IPW_FIRST_VARIABLE_LENGTH_ORDINAL 1001 + +typedef enum _ORDINAL_TABLE_2 { // NS - means Not Supported by FW + IPW_ORD_STAT_BASE = 1000, // contains number of variable ORDs + IPW_ORD_STAT_ADAPTER_MAC = 1001, // 6 bytes: our adapter MAC address + IPW_ORD_STAT_PREFERRED_BSSID = 1002, // 6 bytes: BSSID of the preferred AP + IPW_ORD_STAT_MANDATORY_BSSID = 1003, // 6 bytes: BSSID of the mandatory AP + IPW_FILL_1, //NS // + IPW_ORD_STAT_COUNTRY_TEXT = 1005, // 36 bytes: Country name text, First two bytes are Country code + IPW_ORD_STAT_ASSN_SSID = 1006, // 32 bytes: ESSID String + IPW_ORD_STATION_TABLE = 1007, // ? bytes: Station/AP table (via Direct SSID Scans) + IPW_ORD_STAT_SWEEP_TABLE = 1008, // ? bytes: Sweep/Host Table table (via Broadcast Scans) + IPW_ORD_STAT_ROAM_LOG = 1009, // ? bytes: Roaming log + IPW_ORD_STAT_RATE_LOG = 1010, //NS // 0 bytes: Rate log + IPW_ORD_STAT_FIFO = 1011, //NS // 0 bytes: Fifo buffer data structures + IPW_ORD_STAT_FW_VER_NUM = 1012, // 14 bytes: fw version ID string as in (a.bb.ccc; "0.08.011") + IPW_ORD_STAT_FW_DATE = 1013, // 14 bytes: fw date string (mmm dd yyyy; "Mar 13 2002") + IPW_ORD_STAT_ASSN_AP_BSSID = 1014, // 6 bytes: MAC address of associated AP + IPW_ORD_STAT_DEBUG = 1015, //NS // ? bytes: + IPW_ORD_STAT_NIC_BPA_NUM = 1016, // 11 bytes: NIC BPA number in ASCII + IPW_ORD_STAT_UCODE_DATE = 1017, // 5 bytes: uCode date + IPW_ORD_SECURITY_NGOTIATION_RESULT = 1018, +} ORDINALTABLE2; // NS - means Not Supported by FW + +#define IPW_LAST_VARIABLE_LENGTH_ORDINAL 1018 + +#ifndef WIRELESS_SPY +#define WIRELESS_SPY // enable iwspy support +#endif + +extern struct iw_handler_def ipw2100_wx_handler_def; +extern struct iw_statistics *ipw2100_wx_wireless_stats(struct net_device * dev); +extern void ipw2100_wx_event_work(struct ipw2100_priv *priv); + +#define IPW_HOST_FW_SHARED_AREA0 0x0002f200 +#define IPW_HOST_FW_SHARED_AREA0_END 0x0002f510 // 0x310 bytes + +#define IPW_HOST_FW_SHARED_AREA1 0x0002f610 +#define IPW_HOST_FW_SHARED_AREA1_END 0x0002f630 // 0x20 bytes + +#define IPW_HOST_FW_SHARED_AREA2 0x0002fa00 +#define IPW_HOST_FW_SHARED_AREA2_END 0x0002fa20 // 0x20 bytes + +#define IPW_HOST_FW_SHARED_AREA3 0x0002fc00 +#define IPW_HOST_FW_SHARED_AREA3_END 0x0002fc10 // 0x10 bytes + +#define IPW_HOST_FW_INTERRUPT_AREA 0x0002ff80 +#define IPW_HOST_FW_INTERRUPT_AREA_END 0x00030000 // 0x80 bytes + +struct ipw2100_fw_chunk { + unsigned char *buf; + long len; + long pos; + struct list_head list; +}; + +struct ipw2100_fw_chunk_set { +#ifdef CONFIG_IPW2100_LEGACY_FW_LOAD + struct list_head chunk_list; + unsigned int chunks; +#else + const void *data; +#endif + unsigned long size; +}; + +struct ipw2100_fw { + int version; + struct ipw2100_fw_chunk_set fw; + struct ipw2100_fw_chunk_set uc; +#ifndef CONFIG_IPW2100_LEGACY_FW_LOAD + const struct firmware *fw_entry; +#endif +}; + +int ipw2100_get_firmware(struct ipw2100_priv *priv, struct ipw2100_fw *fw); +void ipw2100_release_firmware(struct ipw2100_priv *priv, struct ipw2100_fw *fw); +int ipw2100_fw_download(struct ipw2100_priv *priv, struct ipw2100_fw *fw); +int ipw2100_ucode_download(struct ipw2100_priv *priv, struct ipw2100_fw *fw); + +#define MAX_FW_VERSION_LEN 14 + +int ipw2100_get_fwversion(struct ipw2100_priv *priv, char *buf, size_t max); +int ipw2100_get_ucodeversion(struct ipw2100_priv *priv, char *buf, size_t max); + +#endif /* _IPW2100_H */ diff --git a/drivers/net/wireless/ipw2200/LICENSE b/drivers/net/wireless/ipw2200/LICENSE new file mode 100644 index 000000000..5f297e5bb --- /dev/null +++ b/drivers/net/wireless/ipw2200/LICENSE @@ -0,0 +1,339 @@ + +"This software program is licensed subject to the GNU General Public License +(GPL). Version 2, June 1991, available at +" + +GNU General Public License + +Version 2, June 1991 + +Copyright (C) 1989, 1991 Free Software Foundation, Inc. +59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + +Everyone is permitted to copy and distribute verbatim copies of this license +document, but changing it is not allowed. + +Preamble + +The licenses for most software are designed to take away your freedom to +share and change it. By contrast, the GNU General Public License is intended +to guarantee your freedom to share and change free software--to make sure +the software is free for all its users. This General Public License applies +to most of the Free Software Foundation's software and to any other program +whose authors commit to using it. (Some other Free Software Foundation +software is covered by the GNU Library General Public License instead.) You +can apply it to your programs, too. + +When we speak of free software, we are referring to freedom, not price. Our +General Public Licenses are designed to make sure that you have the freedom +to distribute copies of free software (and charge for this service if you +wish), that you receive source code or can get it if you want it, that you +can change the software or use pieces of it in new free programs; and that +you know you can do these things. + +To protect your rights, we need to make restrictions that forbid anyone to +deny you these rights or to ask you to surrender the rights. These +restrictions translate to certain responsibilities for you if you distribute +copies of the software, or if you modify it. + +For example, if you distribute copies of such a program, whether gratis or +for a fee, you must give the recipients all the rights that you have. You +must make sure that they, too, receive or can get the source code. And you +must show them these terms so they know their rights. + +We protect your rights with two steps: (1) copyright the software, and (2) +offer you this license which gives you legal permission to copy, distribute +and/or modify the software. + +Also, for each author's protection and ours, we want to make certain that +everyone understands that there is no warranty for this free software. If +the software is modified by someone else and passed on, we want its +recipients to know that what they have is not the original, so that any +problems introduced by others will not reflect on the original authors' +reputations. + +Finally, any free program is threatened constantly by software patents. We +wish to avoid the danger that redistributors of a free program will +individually obtain patent licenses, in effect making the program +proprietary. To prevent this, we have made it clear that any patent must be +licensed for everyone's free use or not licensed at all. + +The precise terms and conditions for copying, distribution and modification +follow. + +TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + +0. This License applies to any program or other work which contains a notice + placed by the copyright holder saying it may be distributed under the + terms of this General Public License. The "Program", below, refers to any + such program or work, and a "work based on the Program" means either the + Program or any derivative work under copyright law: that is to say, a + work containing the Program or a portion of it, either verbatim or with + modifications and/or translated into another language. (Hereinafter, + translation is included without limitation in the term "modification".) + Each licensee is addressed as "you". + + Activities other than copying, distribution and modification are not + covered by this License; they are outside its scope. The act of running + the Program is not restricted, and the output from the Program is covered + only if its contents constitute a work based on the Program (independent + of having been made by running the Program). Whether that is true depends + on what the Program does. + +1. You may copy and distribute verbatim copies of the Program's source code + as you receive it, in any medium, provided that you conspicuously and + appropriately publish on each copy an appropriate copyright notice and + disclaimer of warranty; keep intact all the notices that refer to this + License and to the absence of any warranty; and give any other recipients + of the Program a copy of this License along with the Program. + + You may charge a fee for the physical act of transferring a copy, and you + may at your option offer warranty protection in exchange for a fee. + +2. You may modify your copy or copies of the Program or any portion of it, + thus forming a work based on the Program, and copy and distribute such + modifications or work under the terms of Section 1 above, provided that + you also meet all of these conditions: + + * a) You must cause the modified files to carry prominent notices stating + that you changed the files and the date of any change. + + * b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any part + thereof, to be licensed as a whole at no charge to all third parties + under the terms of this License. + + * c) If the modified program normally reads commands interactively when + run, you must cause it, when started running for such interactive + use in the most ordinary way, to print or display an announcement + including an appropriate copyright notice and a notice that there is + no warranty (or else, saying that you provide a warranty) and that + users may redistribute the program under these conditions, and + telling the user how to view a copy of this License. (Exception: if + the Program itself is interactive but does not normally print such + an announcement, your work based on the Program is not required to + print an announcement.) + + These requirements apply to the modified work as a whole. If identifiable + sections of that work are not derived from the Program, and can be + reasonably considered independent and separate works in themselves, then + this License, and its terms, do not apply to those sections when you + distribute them as separate works. But when you distribute the same + sections as part of a whole which is a work based on the Program, the + distribution of the whole must be on the terms of this License, whose + permissions for other licensees extend to the entire whole, and thus to + each and every part regardless of who wrote it. + + Thus, it is not the intent of this section to claim rights or contest + your rights to work written entirely by you; rather, the intent is to + exercise the right to control the distribution of derivative or + collective works based on the Program. + + In addition, mere aggregation of another work not based on the Program + with the Program (or with a work based on the Program) on a volume of a + storage or distribution medium does not bring the other work under the + scope of this License. + +3. You may copy and distribute the Program (or a work based on it, under + Section 2) in object code or executable form under the terms of Sections + 1 and 2 above provided that you also do one of the following: + + * a) Accompany it with the complete corresponding machine-readable source + code, which must be distributed under the terms of Sections 1 and 2 + above on a medium customarily used for software interchange; or, + + * b) Accompany it with a written offer, valid for at least three years, + to give any third party, for a charge no more than your cost of + physically performing source distribution, a complete machine- + readable copy of the corresponding source code, to be distributed + under the terms of Sections 1 and 2 above on a medium customarily + used for software interchange; or, + + * c) Accompany it with the information you received as to the offer to + distribute corresponding source code. (This alternative is allowed + only for noncommercial distribution and only if you received the + program in object code or executable form with such an offer, in + accord with Subsection b above.) + + The source code for a work means the preferred form of the work for + making modifications to it. For an executable work, complete source code + means all the source code for all modules it contains, plus any + associated interface definition files, plus the scripts used to control + compilation and installation of the executable. However, as a special + exception, the source code distributed need not include anything that is + normally distributed (in either source or binary form) with the major + components (compiler, kernel, and so on) of the operating system on which + the executable runs, unless that component itself accompanies the + executable. + + If distribution of executable or object code is made by offering access + to copy from a designated place, then offering equivalent access to copy + the source code from the same place counts as distribution of the source + code, even though third parties are not compelled to copy the source + along with the object code. + +4. You may not copy, modify, sublicense, or distribute the Program except as + expressly provided under this License. Any attempt otherwise to copy, + modify, sublicense or distribute the Program is void, and will + automatically terminate your rights under this License. However, parties + who have received copies, or rights, from you under this License will not + have their licenses terminated so long as such parties remain in full + compliance. + +5. You are not required to accept this License, since you have not signed + it. However, nothing else grants you permission to modify or distribute + the Program or its derivative works. These actions are prohibited by law + if you do not accept this License. Therefore, by modifying or + distributing the Program (or any work based on the Program), you + indicate your acceptance of this License to do so, and all its terms and + conditions for copying, distributing or modifying the Program or works + based on it. + +6. Each time you redistribute the Program (or any work based on the + Program), the recipient automatically receives a license from the + original licensor to copy, distribute or modify the Program subject to + these terms and conditions. You may not impose any further restrictions + on the recipients' exercise of the rights granted herein. You are not + responsible for enforcing compliance by third parties to this License. + +7. If, as a consequence of a court judgment or allegation of patent + infringement or for any other reason (not limited to patent issues), + conditions are imposed on you (whether by court order, agreement or + otherwise) that contradict the conditions of this License, they do not + excuse you from the conditions of this License. If you cannot distribute + so as to satisfy simultaneously your obligations under this License and + any other pertinent obligations, then as a consequence you may not + distribute the Program at all. For example, if a patent license would + not permit royalty-free redistribution of the Program by all those who + receive copies directly or indirectly through you, then the only way you + could satisfy both it and this License would be to refrain entirely from + distribution of the Program. + + If any portion of this section is held invalid or unenforceable under any + particular circumstance, the balance of the section is intended to apply + and the section as a whole is intended to apply in other circumstances. + + It is not the purpose of this section to induce you to infringe any + patents or other property right claims or to contest validity of any + such claims; this section has the sole purpose of protecting the + integrity of the free software distribution system, which is implemented + by public license practices. Many people have made generous contributions + to the wide range of software distributed through that system in + reliance on consistent application of that system; it is up to the + author/donor to decide if he or she is willing to distribute software + through any other system and a licensee cannot impose that choice. + + This section is intended to make thoroughly clear what is believed to be + a consequence of the rest of this License. + +8. If the distribution and/or use of the Program is restricted in certain + countries either by patents or by copyrighted interfaces, the original + copyright holder who places the Program under this License may add an + explicit geographical distribution limitation excluding those countries, + so that distribution is permitted only in or among countries not thus + excluded. In such case, this License incorporates the limitation as if + written in the body of this License. + +9. The Free Software Foundation may publish revised and/or new versions of + the General Public License from time to time. Such new versions will be + similar in spirit to the present version, but may differ in detail to + address new problems or concerns. + + Each version is given a distinguishing version number. If the Program + specifies a version number of this License which applies to it and "any + later version", you have the option of following the terms and + conditions either of that version or of any later version published by + the Free Software Foundation. If the Program does not specify a version + number of this License, you may choose any version ever published by the + Free Software Foundation. + +10. If you wish to incorporate parts of the Program into other free programs + whose distribution conditions are different, write to the author to ask + for permission. For software which is copyrighted by the Free Software + Foundation, write to the Free Software Foundation; we sometimes make + exceptions for this. Our decision will be guided by the two goals of + preserving the free status of all derivatives of our free software and + of promoting the sharing and reuse of software generally. + + NO WARRANTY + +11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY + FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN + OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES + PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER + EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE + ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH + YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL + NECESSARY SERVICING, REPAIR OR CORRECTION. + +12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING + WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR + REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR + DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL + DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM + (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED + INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF + THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR + OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + +END OF TERMS AND CONDITIONS + +How to Apply These Terms to Your New Programs + +If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it free +software which everyone can redistribute and change under these terms. + +To do so, attach the following notices to the program. It is safest to +attach them to the start of each source file to most effectively convey the +exclusion of warranty; and each file should have at least the "copyright" +line and a pointer to where the full notice is found. + +one line to give the program's name and an idea of what it does. +Copyright (C) yyyy name of author + +This program is free software; you can redistribute it and/or modify it +under the terms of the 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. + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this when +it starts in an interactive mode: + +Gnomovision version 69, Copyright (C) year name of author Gnomovision comes +with ABSOLUTELY NO WARRANTY; for details type 'show w'. This is free +software, and you are welcome to redistribute it under certain conditions; +type 'show c' for details. + +The hypothetical commands 'show w' and 'show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may be +called something other than 'show w' and 'show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + +Yoyodyne, Inc., hereby disclaims all copyright interest in the program +'Gnomovision' (which makes passes at compilers) written by James Hacker. + +signature of Ty Coon, 1 April 1989 +Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Library General Public +License instead of this License. diff --git a/drivers/net/wireless/ipw2200/Makefile b/drivers/net/wireless/ipw2200/Makefile new file mode 100644 index 000000000..244e265f8 --- /dev/null +++ b/drivers/net/wireless/ipw2200/Makefile @@ -0,0 +1,15 @@ +# +# Makefile for the Linux Wireless network device drivers. +# +# Original makefile by Peter Johanson + +EXTRA_CFLAGS += -I$(TOPDIR)/drivers/net/wireless/ieee80211 + +ifneq ($(CONFIG_IPW_DEBUG),) + EXTRA_CFLAGS += -g -Wa,-adhlms=$@.lst +endif + +list-m := +list-$(CONFIG_IPW2200) += ipw2200 + +obj-$(CONFIG_IPW2200) += ipw2200.o diff --git a/drivers/net/wireless/ipw2200/ipw2200.c b/drivers/net/wireless/ipw2200/ipw2200.c new file mode 100644 index 000000000..f2c8800cf --- /dev/null +++ b/drivers/net/wireless/ipw2200/ipw2200.c @@ -0,0 +1,6744 @@ +/****************************************************************************** + + Copyright(c) 2003 - 2004 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 + 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 + Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 + +******************************************************************************/ + +#include "ipw2200.h" +#include "ieee80211.h" + +#define IPW2200_VERSION "0.13" +#define DRV_DESCRIPTION "Intel(R) PRO/Wireless 2200/2915 Network Driver" +#define DRV_COPYRIGHT "Copyright(c) 2003-2004 Intel Corporation" +#define DRV_VERSION IPW2200_VERSION + +MODULE_LICENSE("GPL"); + +static int debug = 0; +static int channel = 0; +static char *ifname; +static int mode = 0; + +static u32 ipw_debug_level; +static int associate = 1; +static int adhoc_create = 1; +static int disable = 0; +static const char ipw_modes[] = { + 'a', 'b', 'g', '?' +}; + +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_down(struct ipw_priv *); +static int ipw_config(struct ipw_priv *); +static int init_supported_rates(struct ipw_priv *priv, struct ipw_supported_rates *prates); + +static u8 band_b_active_channel[MAX_B_CHANNELS] = { + 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 0 +}; +static u8 band_a_active_channel[MAX_A_CHANNELS] = { + 36, 40, 44, 48, 149, 153, 157, 161, 165, 52, 56, 60, 64, 0 +}; + +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 (!(ipw_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); + } +} + +static u32 _ipw_read_reg32(struct ipw_priv *priv, u32 reg); +#define ipw_read_reg32(a, b) _ipw_read_reg32(a, b) + +static u8 _ipw_read_reg8(struct ipw_priv *ipw, u32 reg); +#define ipw_read_reg8(a, b) _ipw_read_reg8(a, b) + +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); +} + +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); +} + +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); +} + +#define _ipw_write8(ipw, ofs, val) writeb((val), (void*)(ipw)->hw_base + (ofs)) +#define ipw_write8(ipw, ofs, val) \ + IPW_DEBUG_IO("%s %d: write_direct8(0x%08X, 0x%08X)\n", __FILE__, __LINE__, (u32)(ofs), (u32)(val)); \ + _ipw_write8(ipw, ofs, val) + +#define _ipw_write16(ipw, ofs, val) writew((val), (void*)(ipw)->hw_base + (ofs)) +#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) + +#define _ipw_write32(ipw, ofs, val) writel((val), (void*)(ipw)->hw_base + (ofs)) +#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) + +#define _ipw_read8(ipw, ofs) readb((void*)(ipw)->hw_base + (ofs)) +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); +} +#define ipw_read8(ipw, ofs) __ipw_read8(__FILE__, __LINE__, ipw, ofs) + +#define _ipw_read16(ipw, ofs) readw((void*)(ipw)->hw_base + (ofs)) +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); +} +#define ipw_read16(ipw, ofs) __ipw_read16(__FILE__, __LINE__, ipw, ofs) + +#define _ipw_read32(ipw, ofs) readl((void*)(ipw)->hw_base + (ofs)) +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); +} +#define ipw_read32(ipw, ofs) __ipw_read32(__FILE__, __LINE__, ipw, ofs) + +static void _ipw_read_indirect(struct ipw_priv *, u32, u8 *, int); +#define ipw_read_indirect(a, b, c, d) \ + IPW_DEBUG_IO("%s %d: read_inddirect(0x%08X) %d bytes\n", __FILE__, __LINE__, (u32)(b), d); \ + _ipw_read_indirect(a, b, c, d) + +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) + +/* indirect write s */ +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, CX2_INDIRECT_ADDR, reg); + _ipw_write32(priv, CX2_INDIRECT_DATA, value); +} + + +static void _ipw_write_reg8(struct ipw_priv *priv, u32 reg, u8 value) +{ + IPW_DEBUG_IO(" reg = 0x%8X : value = 0x%8X\n", reg, value); + _ipw_write32(priv, CX2_INDIRECT_ADDR, reg & CX2_INDIRECT_ADDR_MASK); + _ipw_write8(priv, CX2_INDIRECT_DATA, value); + IPW_DEBUG_IO(" reg = 0x%8X : value = 0x%8X\n", + (int)priv->hw_base + CX2_INDIRECT_DATA, + value); +} + +static void _ipw_write_reg16(struct ipw_priv *priv, u32 reg, + u16 value) +{ + IPW_DEBUG_IO(" reg = 0x%8X : value = 0x%8X\n", reg, value); + _ipw_write32(priv, CX2_INDIRECT_ADDR, reg & CX2_INDIRECT_ADDR_MASK); + _ipw_write16(priv, CX2_INDIRECT_DATA, value); +} + +/* indirect read s */ + +static u8 _ipw_read_reg8(struct ipw_priv *priv, u32 reg) +{ + u32 word; + _ipw_write32(priv, CX2_INDIRECT_ADDR, reg & CX2_INDIRECT_ADDR_MASK); + IPW_DEBUG_IO(" reg = 0x%8X : \n", reg); + word = _ipw_read32(priv, CX2_INDIRECT_DATA); + return (word >> ((reg & 0x3)*8)) & 0xff; +} + +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, CX2_INDIRECT_ADDR, reg); + value = _ipw_read32(priv, CX2_INDIRECT_DATA); + IPW_DEBUG_IO(" reg = 0x%4X : value = 0x%4x \n", reg, value); + return value; +} + +/* iterative/auto-increment 32 bit reads and writes */ +static void _ipw_read_indirect(struct ipw_priv *priv, u32 addr, u8 * buf, + int num) +{ + u32 aligned_addr = addr & CX2_INDIRECT_ADDR_MASK; + u32 dif_len = addr - aligned_addr; + u32 aligned_len; + u32 i; + + IPW_DEBUG_IO("addr = %i, buf = %p, num = %i\n", addr, buf, num); + + /* Read the first nibble byte by byte */ + if (unlikely(dif_len)) { + /* Start reading at aligned_addr + dif_len */ + _ipw_write32(priv, CX2_INDIRECT_ADDR, aligned_addr); + for (i = dif_len; i < 4; i++, buf++) + *buf = _ipw_read8(priv, CX2_INDIRECT_DATA + i); + num -= dif_len; + aligned_addr += 4; + } + + /* Read DWs through autoinc register */ + _ipw_write32(priv, CX2_AUTOINC_ADDR, aligned_addr); + aligned_len = num & CX2_INDIRECT_ADDR_MASK; + for (i = 0; i < aligned_len; i += 4, buf += 4, aligned_addr += 4) + *(u32*)buf = ipw_read32(priv, CX2_AUTOINC_DATA); + + /* Copy the last nibble */ + dif_len = num - aligned_len; + _ipw_write32(priv, CX2_INDIRECT_ADDR, aligned_addr); + for (i = 0; i < dif_len; i++, buf++) + *buf = ipw_read8(priv, CX2_INDIRECT_DATA + i); +} + +static void _ipw_write_indirect(struct ipw_priv *priv, u32 addr, u8 *buf, + int num) +{ + u32 aligned_addr = addr & CX2_INDIRECT_ADDR_MASK; + u32 dif_len = addr - aligned_addr; + u32 aligned_len; + u32 i; + + IPW_DEBUG_IO("addr = %i, buf = %p, num = %i\n", addr, buf, num); + + /* Write the first nibble byte by byte */ + if (unlikely(dif_len)) { + /* Start writing at aligned_addr + dif_len */ + _ipw_write32(priv, CX2_INDIRECT_ADDR, aligned_addr); + for (i = dif_len; i < 4; i++, buf++) + _ipw_write8(priv, CX2_INDIRECT_DATA + i, *buf); + num -= dif_len; + aligned_addr += 4; + } + + /* Write DWs through autoinc register */ + _ipw_write32(priv, CX2_AUTOINC_ADDR, aligned_addr); + aligned_len = num & CX2_INDIRECT_ADDR_MASK; + for (i = 0; i < aligned_len; i += 4, buf += 4, aligned_addr += 4) + _ipw_write32(priv, CX2_AUTOINC_DATA, *(u32*)buf); + + /* Copy the last nibble */ + dif_len = num - aligned_len; + _ipw_write32(priv, CX2_INDIRECT_ADDR, aligned_addr); + for (i = 0; i < dif_len; i++, buf++) + _ipw_write8(priv, CX2_INDIRECT_DATA + i, *buf); +} + +static void ipw_write_direct(struct ipw_priv *priv, u32 addr, void *buf, + int num) +{ + memcpy_toio((void*)(priv->hw_base + addr), buf, num); +} + +static inline void ipw_set_bit(struct ipw_priv *priv, u32 reg, u32 mask) +{ + ipw_write32(priv, reg, ipw_read32(priv, reg) | mask); +} + +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, CX2_INTA_MASK_R, CX2_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, CX2_INTA_MASK_R, ~CX2_INTA_MASK_ALL); +} + +#ifdef CONFIG_IPW_DEBUG +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 "ERROR_BAD_PARAM"; + case IPW_FW_ERROR_BAD_CHECKSUM: + return "ERROR_BAD_CHECKSUM"; + case IPW_FW_ERROR_NMI_INTERRUPT: + return "ERROR_NMI_INTERRUPT"; + case IPW_FW_ERROR_BAD_DATABASE: + return "ERROR_BAD_DATABASE"; + case IPW_FW_ERROR_ALLOC_FAIL: + return "ERROR_ALLOC_FAIL"; + case IPW_FW_ERROR_DMA_UNDERRUN: + return "ERROR_DMA_UNDERRUN"; + case IPW_FW_ERROR_DMA_STATUS: + return "ERROR_DMA_STATUS"; + case IPW_FW_ERROR_DINOSTATUS_ERROR: + return "ERROR_DINOSTATUS_ERROR"; + case IPW_FW_ERROR_EEPROMSTATUS_ERROR: + return "ERROR_EEPROMSTATUS_ERROR"; + case IPW_FW_ERROR_SYSASSERT: + return "ERROR_SYSASSERT"; + case IPW_FW_ERROR_FATAL_ERROR: + return "ERROR_FATALSTATUS_ERROR"; + default: + return "UNKNOWNSTATUS_ERROR"; + } +} +#endif /* CONFIG_IPW_DEBUG */ + +static void ipw_dump_nic_error_log(struct ipw_priv *priv) +{ + u32 desc, time, blink1, blink2, ilink1, ilink2, idata, i, count, base; + + base = ipw_read32(priv, IPWSTATUS_ERROR_LOG); + count = ipw_read_reg32(priv, base); + + if (ERROR_START_OFFSET <= count * ERROR_ELEM_SIZE) + IPW_ERROR("Start IPW Error Log Dump:\n"); + + for (i = ERROR_START_OFFSET; + i <= count * ERROR_ELEM_SIZE; + i += ERROR_ELEM_SIZE) { + desc = ipw_read_reg32(priv, base + i); + time = ipw_read_reg32(priv, base + i + 1*sizeof(u32)); + blink1 = ipw_read_reg32(priv, base + i + 2*sizeof(u32)); + blink2 = ipw_read_reg32(priv, base + i + 3*sizeof(u32)); + ilink1 = ipw_read_reg32(priv, base + i + 4*sizeof(u32)); + ilink2 = ipw_read_reg32(priv, base + i + 5*sizeof(u32)); + idata = ipw_read_reg32(priv, base + i + 6*sizeof(u32)); + +#ifdef CONFIG_IPW_DEBUG + IPW_ERROR( + "%s %i 0x%08x 0x%08x 0x%08x 0x%08x 0x%08x\n", + ipw_error_desc(desc), time, blink1, blink2, + ilink1, ilink2, idata); +#endif + } +} + +static void ipw_dump_nic_event_log(struct ipw_priv *priv) +{ + u32 ev, time, data, i, count, base; + + base = ipw_read32(priv, IPW_EVENT_LOG); + count = ipw_read_reg32(priv, base); + + if (EVENT_START_OFFSET <= count * EVENT_ELEM_SIZE) + IPW_ERROR("Start IPW Event Log Dump:\n"); + + for (i = EVENT_START_OFFSET; + i <= count * EVENT_ELEM_SIZE; + i += EVENT_ELEM_SIZE) { + ev = ipw_read_reg32(priv, base + i); + time = ipw_read_reg32(priv, base + i + 1*sizeof(u32)); + data = ipw_read_reg32(priv, base + i + 2*sizeof(u32)); + +#ifdef CONFIG_IPW_DEBUG + IPW_ERROR("%i\t0x%08x\t%i\n", time, data, ev); +#endif + } +} + +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 %d\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 %d\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); + +} + +/* + * 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 ssize_t show_status(struct device *d, char *buf) +{ + struct ipw_priv *p = (struct ipw_priv *)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, char *buf) +{ + struct ipw_priv *p = (struct ipw_priv *)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, char *buf) +{ + struct ipw_priv *p = (struct ipw_priv *)d->driver_data; + u8 type = p->eeprom[EEPROM_NIC_TYPE]; + + switch (type) { + case EEPROM_NIC_TYPE_STANDARD: + return sprintf(buf, "STANDARD\n"); + case EEPROM_NIC_TYPE_DELL: + return sprintf(buf, "DELL\n"); + case EEPROM_NIC_TYPE_FUJITSU: + return sprintf(buf, "FUJITSU\n"); + case EEPROM_NIC_TYPE_IBM: + return sprintf(buf, "IBM\n"); + case EEPROM_NIC_TYPE_HP: + return sprintf(buf, "HP\n"); + } + + return sprintf(buf, "UNKNOWN\n"); +} +static DEVICE_ATTR(nic_type, S_IRUGO, show_nic_type, NULL); + +static ssize_t dump_error_log(struct device *d, const char *buf, + size_t count) +{ + char *p = (char *)buf; + + if (p[0] == '1') + ipw_dump_nic_error_log((struct ipw_priv*)d->driver_data); + + return strnlen(buf, count); +} +static DEVICE_ATTR(dump_errors, S_IWUSR, NULL, dump_error_log); + +static ssize_t dump_event_log(struct device *d, const char *buf, + size_t count) +{ + char *p = (char *)buf; + + if (p[0] == '1') + ipw_dump_nic_event_log((struct ipw_priv*)d->driver_data); + + return strnlen(buf, count); +} +static DEVICE_ATTR(dump_events, S_IWUSR, NULL, dump_event_log); + +static ssize_t show_ucode_version(struct device *d, char *buf) +{ + u32 len = sizeof(u32), tmp = 0; + struct ipw_priv *p = (struct ipw_priv*)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, char *buf) +{ + u32 len = sizeof(u32), tmp = 0; + struct ipw_priv *p = (struct ipw_priv*)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, 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, const char *buf, + size_t count) +{ + struct ipw_priv *p = (struct ipw_priv*)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, char *buf) +{ + u32 reg = 0; + struct ipw_priv *p = (struct ipw_priv *)d->driver_data; + + reg = ipw_read_reg32(p, CX2_INTERNAL_CMD_EVENT); + return sprintf(buf, "0x%08x\n", reg); +} +static ssize_t store_command_event_reg(struct device *d, + const char *buf, + size_t count) +{ + u32 reg; + struct ipw_priv *p = (struct ipw_priv *)d->driver_data; + + sscanf(buf, "%x", ®); + ipw_write_reg32(p, CX2_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, char *buf) +{ + u32 reg = 0; + struct ipw_priv *p = (struct ipw_priv *)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, + const char *buf, + size_t count) +{ + u32 reg; + struct ipw_priv *p = (struct ipw_priv *)d->driver_data; + + sscanf(buf, "%x", ®); + 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, char *buf) +{ + u32 reg = 0; + struct ipw_priv *priv = (struct ipw_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, + const char *buf, + size_t count) +{ + struct ipw_priv *priv = (struct ipw_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, char *buf) +{ + u8 reg = 0; + struct ipw_priv *priv = (struct ipw_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, + const char *buf, + size_t count) +{ + struct ipw_priv *priv = (struct ipw_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, char *buf) +{ + u32 reg = 0; + struct ipw_priv *priv = (struct ipw_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, + const char *buf, + size_t count) +{ + struct ipw_priv *priv = (struct ipw_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 inline 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, 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 = (struct ipw_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); + } + wake_up_interruptible(&priv->wait_command_queue); + 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, + 2 * HZ); + } else + queue_work(priv->workqueue, &priv->up); + } + + return 1; +} + +static ssize_t store_rf_kill(struct device *d, const char *buf, size_t count) +{ + struct ipw_priv *priv = (struct ipw_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 void ipw_irq_tasklet(struct ipw_priv *priv) +{ + u32 inta, inta_mask, handled = 0; + unsigned long flags; + int rc = 0; + + spin_lock_irqsave(&priv->lock, flags); + + inta = ipw_read32(priv, CX2_INTA_RW); + inta_mask = ipw_read32(priv, CX2_INTA_MASK_R); + inta &= (CX2_INTA_MASK_ALL & inta_mask); + + /* Add any cached INTA values that need to be handled */ + inta |= priv->isr_inta; + + /* handle all the justifications for the interrupt */ + if (inta & CX2_INTA_BIT_RX_TRANSFER) { + ipw_rx(priv); + handled |= CX2_INTA_BIT_RX_TRANSFER; + } + + if (inta & CX2_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 |= CX2_INTA_BIT_TX_CMD_QUEUE; + } + + if (inta & CX2_INTA_BIT_TX_QUEUE_1) { + IPW_DEBUG_TX("TX_QUEUE_1\n"); + rc = ipw_queue_tx_reclaim( priv, &priv->txq[0], 0); + handled |= CX2_INTA_BIT_TX_QUEUE_1; + } + + if (inta & CX2_INTA_BIT_TX_QUEUE_2) { + IPW_DEBUG_TX("TX_QUEUE_2\n"); + rc = ipw_queue_tx_reclaim( priv, &priv->txq[1], 1); + handled |= CX2_INTA_BIT_TX_QUEUE_2; + } + + if (inta & CX2_INTA_BIT_TX_QUEUE_3) { + IPW_DEBUG_TX("TX_QUEUE_3\n"); + rc = ipw_queue_tx_reclaim( priv, &priv->txq[2], 2); + handled |= CX2_INTA_BIT_TX_QUEUE_3; + } + + if (inta & CX2_INTA_BIT_TX_QUEUE_4) { + IPW_DEBUG_TX("TX_QUEUE_4\n"); + rc = ipw_queue_tx_reclaim( priv, &priv->txq[3], 3); + handled |= CX2_INTA_BIT_TX_QUEUE_4; + } + + if (inta & CX2_INTA_BIT_STATUS_CHANGE) { + IPW_WARNING("STATUS_CHANGE\n"); + handled |= CX2_INTA_BIT_STATUS_CHANGE; + } + + if (inta & CX2_INTA_BIT_BEACON_PERIOD_EXPIRED) { + IPW_WARNING("TX_PERIOD_EXPIRED\n"); + handled |= CX2_INTA_BIT_BEACON_PERIOD_EXPIRED; + } + + if (inta & CX2_INTA_BIT_SLAVE_MODE_HOST_CMD_DONE) { + IPW_WARNING("HOST_CMD_DONE\n"); + handled |= CX2_INTA_BIT_SLAVE_MODE_HOST_CMD_DONE; + } + + if (inta & CX2_INTA_BIT_FW_INITIALIZATION_DONE) { + IPW_WARNING("FW_INITIALIZATION_DONE\n"); + priv->status |= STATUS_FW_READY; + handled |= CX2_INTA_BIT_FW_INITIALIZATION_DONE; + } + + if (inta & CX2_INTA_BIT_FW_CARD_DISABLE_PHY_OFF_DONE) { + IPW_WARNING("PHY_OFF_DONE\n"); + handled |= CX2_INTA_BIT_FW_CARD_DISABLE_PHY_OFF_DONE; + } + + if (inta & CX2_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); + netif_carrier_off(priv->net_dev); + netif_stop_queue(priv->net_dev); + cancel_delayed_work(&priv->request_scan); + queue_delayed_work(priv->workqueue, &priv->rf_kill, 2 * HZ); + handled |= CX2_INTA_BIT_RF_KILL_DONE; + } + + if (inta & CX2_INTA_BIT_FATAL_ERROR) { + IPW_ERROR("Fatal error\n"); + ipw_dump_nic_error_log(priv); + ipw_dump_nic_event_log(priv); + queue_work(priv->workqueue, &priv->adapter_restart); + handled |= CX2_INTA_BIT_FATAL_ERROR; + } + + if (inta & CX2_INTA_BIT_PARITY_ERROR) { + IPW_ERROR("Parity error\n"); + handled |= CX2_INTA_BIT_PARITY_ERROR; + } + + if (handled != inta) { + IPW_ERROR("Unhandled INTA bits 0x%08x\n", + inta & ~handled); + } + + /* enable all interrupts */ + ipw_enable_interrupts(priv); + + spin_unlock_irqrestore(&priv->lock, flags); +} + +#ifdef CONFIG_IPW_DEBUG +#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"; + } +} +#endif /* CONFIG_IPW_DEBUG */ + +#define HOST_COMPLETE_TIMEOUT HZ +static int ipw_send_cmd(struct ipw_priv *priv, struct host_cmd *cmd) +{ + int rc = 0; + + if (priv->status & STATUS_HCMD_ACTIVE) { + IPW_ERROR("Already sending a command\n"); + return -1; + } + + priv->status |= STATUS_HCMD_ACTIVE; + + IPW_DEBUG_HC("Sending %s command (#%d), %d bytes\n", + get_cmd_string(cmd->cmd), cmd->cmd, cmd->len); + 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) + return rc; + + rc = wait_event_interruptible_timeout( + priv->wait_command_queue, !(priv->status & STATUS_HCMD_ACTIVE), + HOST_COMPLETE_TIMEOUT); + if (rc == 0) { + IPW_DEBUG_INFO("Command completion failed out after %dms.\n", + HOST_COMPLETE_TIMEOUT / (HZ / 100)); + return -EIO; + } + if (priv->status & STATUS_RF_KILL_MASK) { + IPW_DEBUG_INFO("Command aborted due to RF Kill Switch\n"); + return -EIO; + } + + return 0; +} + +static int ipw_send_host_complete(struct ipw_priv *priv) +{ + struct host_cmd cmd = { + .cmd = IPW_CMD_HOST_COMPLETE, + .len = 0 + }; + + if (!priv) { + IPW_ERROR("Invalid args\n"); + return -1; + } + + if (ipw_send_cmd(priv, &cmd)) { + IPW_ERROR("failed to send HOST_COMPLETE command\n"); + return -1; + } + + return 0; +} + +static int ipw_send_system_config(struct ipw_priv *priv, + struct ipw_sys_config *config) +{ + struct host_cmd cmd = { + .cmd = IPW_CMD_SYSTEM_CONFIG, + .len = sizeof(*config) + }; + + if (!priv || !config) { + IPW_ERROR("Invalid args\n"); + return -1; + } + + memcpy(&cmd.param,config,sizeof(*config)); + if (ipw_send_cmd(priv, &cmd)) { + IPW_ERROR("failed to send SYSTEM_CONFIG command\n"); + return -1; + } + + return 0; +} + +static int ipw_send_ssid(struct ipw_priv *priv, u8 *ssid, int len) +{ + struct host_cmd cmd = { + .cmd = IPW_CMD_SSID, + .len = min(len, IW_ESSID_MAX_SIZE) + }; + + if (!priv || !ssid) { + IPW_ERROR("Invalid args\n"); + return -1; + } + + memcpy(&cmd.param, ssid, cmd.len); + if (ipw_send_cmd(priv, &cmd)) { + IPW_ERROR("failed to send SSID command\n"); + return -1; + } + + return 0; +} + +static int ipw_send_adapter_address(struct ipw_priv *priv, u8 *mac) +{ + struct host_cmd cmd = { + .cmd = IPW_CMD_ADAPTER_ADDRESS, + .len = ETH_ALEN + }; + + if (!priv || !mac) { + IPW_ERROR("Invalid args\n"); + return -1; + } + + IPW_DEBUG_INFO("%s: Setting MAC to " MAC_FMT "\n", + priv->net_dev->name, MAC_ARG(mac)); + + memcpy(&cmd.param, mac, ETH_ALEN); + + if (ipw_send_cmd(priv, &cmd)) { + IPW_ERROR("failed to send ADAPTER_ADDRESS command\n"); + return -1; + } + + return 0; +} + +static int ipw_send_scan_request_ext(struct ipw_priv *priv, + struct ipw_scan_request_ext *request) +{ + struct host_cmd cmd = { + .cmd = IPW_CMD_SCAN_REQUEST_EXT, + .len = sizeof(*request) + }; + + if (!priv || !request) { + IPW_ERROR("Invalid args\n"); + return -1; + } + + memcpy(&cmd.param,request,sizeof(*request)); + if (ipw_send_cmd(priv, &cmd)) { + IPW_ERROR("failed to send SCAN_REQUEST_EXT command\n"); + return -1; + } + + return 0; +} + +static int ipw_send_scan_abort(struct ipw_priv *priv) +{ + struct host_cmd cmd = { + .cmd = IPW_CMD_SCAN_ABORT, + .len = 0 + }; + + if (!priv) { + IPW_ERROR("Invalid args\n"); + return -1; + } + + if (ipw_send_cmd(priv, &cmd)) { + IPW_ERROR("failed to send SCAN_ABORT command\n"); + return -1; + } + + return 0; +} + +static int ipw_set_sensitivity(struct ipw_priv *priv, u16 sens) +{ + struct host_cmd cmd = { + .cmd = IPW_CMD_SENSITIVITY_CALIB, + .len = sizeof(struct ipw_sensitivity_calib) + }; + struct ipw_sensitivity_calib *calib = (struct ipw_sensitivity_calib *) + &cmd.param; + calib->beacon_rssi_raw = sens; + if (ipw_send_cmd(priv, &cmd)) { + IPW_ERROR("failed to send SENSITIVITY CALIB command\n"); + return -1; + } + + return 0; +} + +static int ipw_send_associate(struct ipw_priv *priv, + struct ipw_associate *associate) +{ + struct host_cmd cmd = { + .cmd = IPW_CMD_ASSOCIATE, + .len = sizeof(*associate) + }; + + if (!priv || !associate) { + IPW_ERROR("Invalid args\n"); + return -1; + } + + memcpy(&cmd.param,associate,sizeof(*associate)); + if (ipw_send_cmd(priv, &cmd)) { + IPW_ERROR("failed to send ASSOCIATE command\n"); + return -1; + } + + return 0; +} + +static int ipw_send_supported_rates(struct ipw_priv *priv, + struct ipw_supported_rates *rates) +{ + struct host_cmd cmd = { + .cmd = IPW_CMD_SUPPORTED_RATES, + .len = sizeof(*rates) + }; + + if (!priv || !rates) { + IPW_ERROR("Invalid args\n"); + return -1; + } + + memcpy(&cmd.param,rates,sizeof(*rates)); + if (ipw_send_cmd(priv, &cmd)) { + IPW_ERROR("failed to send SUPPORTED_RATES command\n"); + return -1; + } + + return 0; +} + +static int ipw_set_random_seed(struct ipw_priv *priv) +{ + struct host_cmd cmd = { + .cmd = IPW_CMD_SEED_NUMBER, + .len = sizeof(u32) + }; + + if (!priv) { + IPW_ERROR("Invalid args\n"); + return -1; + } + + get_random_bytes(&cmd.param, sizeof(u32)); + + if (ipw_send_cmd(priv, &cmd)) { + IPW_ERROR("failed to send SEED_NUMBER command\n"); + return -1; + } + + return 0; +} + +static int ipw_send_tx_power(struct ipw_priv *priv, + struct ipw_tx_power *power) +{ + struct host_cmd cmd = { + .cmd = IPW_CMD_TX_POWER, + .len = sizeof(*power) + }; + + if (!priv || !power) { + IPW_ERROR("Invalid args\n"); + return -1; + } + + memcpy(&cmd.param,power,sizeof(*power)); + if (ipw_send_cmd(priv, &cmd)) { + IPW_ERROR("failed to send TX_POWER command\n"); + return -1; + } + + return 0; +} + +static int ipw_send_rts_threshold(struct ipw_priv *priv, u16 rts) +{ + struct ipw_rts_threshold rts_threshold = { + .rts_threshold = rts, + }; + struct host_cmd cmd = { + .cmd = IPW_CMD_RTS_THRESHOLD, + .len = sizeof(rts_threshold) + }; + + if (!priv) { + IPW_ERROR("Invalid args\n"); + return -1; + } + + memcpy(&cmd.param, &rts_threshold, sizeof(rts_threshold)); + if (ipw_send_cmd(priv, &cmd)) { + IPW_ERROR("failed to send RTS_THRESHOLD command\n"); + return -1; + } + + return 0; +} + +static int ipw_send_frag_threshold(struct ipw_priv *priv, u16 frag) +{ + struct ipw_frag_threshold frag_threshold = { + .frag_threshold = frag, + }; + struct host_cmd cmd = { + .cmd = IPW_CMD_FRAG_THRESHOLD, + .len = sizeof(frag_threshold) + }; + + if (!priv) { + IPW_ERROR("Invalid args\n"); + return -1; + } + + memcpy(&cmd.param, &frag_threshold, sizeof(frag_threshold)); + if (ipw_send_cmd(priv, &cmd)) { + IPW_ERROR("failed to send FRAG_THRESHOLD command\n"); + return -1; + } + + return 0; +} + +static int ipw_send_power_mode(struct ipw_priv *priv, u32 mode) +{ + struct host_cmd cmd = { + .cmd = IPW_CMD_POWER_MODE, + .len = sizeof(u32) + }; + u32 *param = (u32*)(&cmd.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 = IPW_POWER_INDEX_3; + break; + case IPW_POWER_AC: + *param = IPW_POWER_MODE_CAM; + break; + default: + *param = mode; + break; + } + + if (ipw_send_cmd(priv, &cmd)) { + IPW_ERROR("failed to send POWER_MODE command\n"); + return -1; + } + + return 0; +} + +/* + * 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 inline 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 inline 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<eeprom; + memcpy(mac, &ee[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; + u16 *eeprom = (u16 *)priv->eeprom; + + IPW_DEBUG_TRACE(">>\n"); + + /* read entire contents of eeprom into private buffer */ + for ( i=0; i<128; i++ ) + eeprom[i] = 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 it's 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; ieeprom[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 inline void ipw_zero_memory(struct ipw_priv *priv, u32 start, u32 count) +{ + count >>= 2; + if (!count) return; + _ipw_write32(priv, CX2_AUTOINC_ADDR, start); + while (count--) + _ipw_write32(priv, CX2_AUTOINC_DATA, 0); +} + +static inline void ipw_fw_dma_reset_command_blocks(struct ipw_priv *priv) +{ + ipw_zero_memory(priv, CX2_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, CX2_DMA_I_CB_BASE, CX2_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, CX2_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 = CX2_SHARED_SRAM_DMA_CONTROL + (sizeof(struct command_block) * index); + IPW_DEBUG_FW(">> :\n"); + + ipw_write_indirect(priv, address, (u8*)cb, 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, CX2_RESET_REG,CX2_RESET_REG_MASTER_DISABLED | CX2_RESET_REG_STOP_MASTER); + + /* Set the Start bit. */ + control = DMA_CONTROL_SMALL_CB_CONST_VALUE | DMA_CB_START; + ipw_write_reg32(priv, CX2_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,CX2_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, CX2_DMA_I_DMA_CONTROL); + IPW_DEBUG_FW_INFO("CX2_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, CX2_DMA_I_CURRENT_CB); + + current_cb_index = (current_cb_address - CX2_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 = (void *)dest_address; + cb->source_addr = (void *)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; + 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%8X\n", + (int) priv->sram_desc.last_cb_index); + + while (current_index < priv->sram_desc.last_cb_index) { + udelay(50); + current_index = ipw_fw_dma_command_block_index(priv); + + watchdog++; + + 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, CX2_RESET_REG, + CX2_RESET_REG_MASTER_DISABLED | CX2_RESET_REG_STOP_MASTER); + + IPW_DEBUG_FW("<< dmaWaitSync \n"); + return 0; +} + +/** + * 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; +} + +static inline 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, CX2_RESET_REG, CX2_RESET_REG_STOP_MASTER); + + rc = ipw_poll_bit(priv, CX2_RESET_REG, + CX2_RESET_REG_MASTER_DISABLED, 100); + if (rc < 0) { + IPW_ERROR("stop master failed in 10ms\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, CX2_RESET_REG, CBD_RESET_REG_PRINCETON_RESET); + + /* no one knows timing, for safety add some delay */ + mdelay(5); +} + +struct fw_header { + u32 version; + u32 mode; +}; + +struct fw_chunk { + u32 address; + u32 length; +}; + +#define IPW_FW_MAJOR_VERSION 2 +#define IPW_FW_MINOR_VERSION 0 + +#define IPW_FW_MINOR(x) ((x & 0xff) >> 8) +#define IPW_FW_MAJOR(x) (x & 0xff) + +#define IPW_FW_VERSION ((IPW_FW_MINOR_VERSION << 8) | \ + IPW_FW_MAJOR_VERSION) + +#define IPW_FW_PREFIX "ipw-" __stringify(IPW_FW_MAJOR_VERSION) \ +"." __stringify(IPW_FW_MINOR_VERSION) "-" + +#if 0 +#define IPW_FW_NAME(x) IPW_FW_PREFIX "" x ".fw" +#else +#define IPW_FW_NAME(x) "ipw2200_" x ".fw" +#endif + +static int ipw_load_ucode(struct ipw_priv *priv, u8 * data, + size_t len) +{ + int rc = 0, i, addr; + u8 cr = 0; + u16 *image; + + image = (u16 *)data; + + IPW_DEBUG_TRACE(">> \n"); + + rc = ipw_stop_master(priv); + + if (rc < 0) + return rc; + +// spin_lock_irqsave(&priv->lock, flags); + + for (addr = CX2_SHARED_LOWER_BOUND; + addr < CX2_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, CX2_MEM_HALT_AND_RESET ,CX2_BIT_HALT_RESET_ON); + ipw_arc_release(priv); + ipw_write_reg32(priv, CX2_MEM_HALT_AND_RESET, CX2_BIT_HALT_RESET_OFF); + mdelay(1); + + /* reset PHY */ + ipw_write_reg32(priv, CX2_INTERNAL_CMD_EVENT, CX2_BASEBAND_POWER_DOWN); + mdelay(1); + + ipw_write_reg32(priv, CX2_INTERNAL_CMD_EVENT, 0); + mdelay(1); + + /* enable ucode store */ + ipw_write_reg8(priv, DINO_CONTROL_REG, 0x0); + ipw_write_reg8(priv, DINO_CONTROL_REG, 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, CX2_BASEBAND_CONTROL_STORE, image[i]); + + + /* enable DINO */ + ipw_write_reg8(priv, CX2_BASEBAND_CONTROL_STATUS, 0); + ipw_write_reg8(priv, CX2_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, CX2_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 */ + u32 response_buffer[(sizeof(priv->dino_alive) + 3) / 4]; + + for (i = 0; i < ARRAY_SIZE(response_buffer); i++) + response_buffer[i] = + ipw_read_reg32(priv, + CX2_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, CX2_BASEBAND_CONTROL_STATUS, 0); + +// spin_unlock_irqrestore(&priv->lock, flags); + + 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, + chunk->address, chunk->length); + if (rc) { + IPW_DEBUG_INFO("dmaAddBuffer Failed\n"); + goto out; + } + + offset += 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, CX2_RESET_REG, CX2_RESET_REG_STOP_MASTER); + + rc = ipw_poll_bit(priv, CX2_RESET_REG, + CX2_RESET_REG_MASTER_DISABLED, 500); + if (rc < 0) { + IPW_ERROR("wait for reg master disabled failed\n"); + return rc; + } + + ipw_set_bit(priv, CX2_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, CX2_RESET_REG, + CX2_RESET_REG_MASTER_DISABLED | + CX2_RESET_REG_STOP_MASTER | + CBD_RESET_REG_PRINCETON_RESET); + + /* enable power management */ + ipw_set_bit(priv, CX2_GP_CNTRL_RW, CX2_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, CX2_GP_CNTRL_RW, CX2_GP_CNTRL_BIT_INIT_DONE); + + /* low-level PLL activation */ + ipw_write32(priv, CX2_READ_INT_REGISTER, CX2_BIT_INT_HOST_SRAM_READ_INT_REGISTER); + + /* wait for clock stabilization */ + rc = ipw_poll_bit(priv, CX2_GP_CNTRL_RW, + CX2_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, CX2_RESET_REG, CX2_RESET_REG_SW_RESET); + + udelay(10); + + /* set "initialization complete" bit to move adapter to D0 state */ + ipw_set_bit(priv, CX2_GP_CNTRL_RW, CX2_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; + + IPW_DEBUG_TRACE(">>\n"); + + rc = ipw_init_nic(priv); + + IPW_DEBUG_TRACE("<<\n"); + return rc; +} + +static int ipw_get_fw(struct ipw_priv *priv, + const struct firmware **fw, const char *name) +{ + struct fw_header *header; + int rc; + + /* ask firmware_class module to get the boot firmware off disk */ + rc = request_firmware(fw, name, &priv->pci_dev->dev); + if (rc < 0) { + IPW_ERROR("%s load failed\n", name); + return rc; + } + + header = (struct fw_header *)(*fw)->data; + if (IPW_FW_MAJOR(header->version) != IPW_FW_MAJOR_VERSION) { + IPW_ERROR("'%s' firmware version not compatible (%d != %d)\n", + name, + IPW_FW_MAJOR(header->version), IPW_FW_MAJOR_VERSION); + return rc; + } + + IPW_DEBUG_INFO("Loading firmware '%s' file v%d.%d (%d bytes)\n", + name, + IPW_FW_MAJOR(header->version), + IPW_FW_MINOR(header->version), + (*fw)->size - sizeof(struct fw_header)); + return 0; +} + +#define CX2_RX_BUF_SIZE (3000) + +static inline void ipw_rx_queue_reset(struct ipw_priv *priv, + struct ipw_rx_queue *rxq) +{ + int i; + + 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, + CX2_RX_BUF_SIZE, + PCI_DMA_FROMDEVICE); + dev_kfree_skb(rxq->pool[i].skb); + } + 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->processed = RX_QUEUE_SIZE - 1; + rxq->free_count = 0; +} + +#ifdef CONFIG_PM +static int fw_loaded = 0; +static const struct firmware *bootfw = NULL; +static const struct firmware *firmware = NULL; +static const struct firmware *ucode = NULL; +#endif + +static int ipw_load(struct ipw_priv *priv) +{ +#ifndef CONFIG_PM + const struct firmware *bootfw = NULL; + const struct firmware *firmware = NULL; + const struct firmware *ucode = NULL; +#endif + int rc = 0, retries = 3; + +#ifdef CONFIG_PM + if (!fw_loaded) { +#endif + rc = ipw_get_fw(priv, &bootfw, IPW_FW_NAME("boot")); + if (rc) + goto error; + + rc = ipw_get_fw(priv, &ucode, IPW_FW_NAME("ucode")); + if (rc) + goto error; + + switch (priv->ieee->iw_mode) { + case IW_MODE_ADHOC: + rc = ipw_get_fw(priv, &firmware, IPW_FW_NAME("ibss")); + break; + +#ifdef CONFIG_IPW_PROMISC + case IW_MODE_MONITOR: + rc = ipw_get_fw(priv, &firmware, IPW_FW_NAME("sniffer")); + break; +#endif + case IW_MODE_INFRA: + rc = ipw_get_fw(priv, &firmware, IPW_FW_NAME("bss")); + break; + + default: + rc = -EINVAL; + } + + if (rc) + goto error; + +#ifdef CONFIG_PM + fw_loaded = 1; + } +#endif + + 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, CX2_INTA_MASK_R, ~CX2_INTA_MASK_ALL); + priv->status &= ~STATUS_INT_ENABLED; + + /* ack pending interrupts */ + ipw_write32(priv, CX2_INTA_RW, CX2_INTA_MASK_ALL); + + ipw_stop_nic(priv); + + rc = ipw_reset_nic(priv); + if (rc) { + IPW_ERROR("Unable to reset NIC\n"); + goto error; + } + + ipw_zero_memory(priv, CX2_NIC_SRAM_LOWER_BOUND, + CX2_NIC_SRAM_UPPER_BOUND - CX2_NIC_SRAM_LOWER_BOUND); + + /* DMA the initial boot firmware into the device */ + rc = ipw_load_firmware(priv, bootfw->data + sizeof(struct fw_header), + bootfw->size - sizeof(struct fw_header)); + if (rc < 0) { + IPW_ERROR("Unable to load boot firmware\n"); + goto error; + } + + /* kick start the device */ + ipw_start_nic(priv); + + /* wait for the device to finish it's initial startup sequence */ + rc = ipw_poll_bit(priv, CX2_INTA_RW, + CX2_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, CX2_INTA_RW, CX2_INTA_BIT_FW_INITIALIZATION_DONE); + + /* DMA the ucode into the device */ + rc = ipw_load_ucode(priv, ucode->data + sizeof(struct fw_header), + ucode->size - sizeof(struct fw_header)); + if (rc < 0) { + IPW_ERROR("Unable to load ucode\n"); + goto error; + } + + /* stop nic */ + ipw_stop_nic(priv); + + /* DMA bss firmware into the device */ + rc = ipw_load_firmware(priv, firmware->data + + sizeof(struct fw_header), + firmware->size - sizeof(struct fw_header)); + if (rc < 0 ) { + IPW_ERROR("Unable to load firmware\n"); + goto error; + } + + ipw_write32(priv, IPW_EEPROM_LOAD_DISABLE, 0); + + rc = ipw_queue_reset(priv); + if (rc) { + IPW_ERROR("Unable to initialize queues\n"); + goto error; + } + + /* Ensure interrupts are disabled */ + ipw_write32(priv, CX2_INTA_MASK_R, ~CX2_INTA_MASK_ALL); + + /* kick start the device */ + ipw_start_nic(priv); + + if (ipw_read32(priv, CX2_INTA_RW) & CX2_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, CX2_INTA_RW, + CX2_INTA_BIT_FW_INITIALIZATION_DONE, 500); + if (rc < 0) { + IPW_ERROR("device failed to start after 500ms\n"); + goto error; + } + IPW_DEBUG_INFO("device response after %dms\n", rc); + + /* ack fw init done interrupt */ + ipw_write32(priv, CX2_INTA_RW, CX2_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); + + ipw_rx_queue_replenish(priv); + ipw_write32(priv, CX2_RX_READ_INDEX, priv->rxq->read); + + /* ack pending interrupts */ + ipw_write32(priv, CX2_INTA_RW, CX2_INTA_MASK_ALL); + +#ifndef CONFIG_PM + release_firmware(bootfw); + release_firmware(ucode); + release_firmware(firmware); +#endif + return 0; + + error: + if (priv->rxq) { + ipw_rx_queue_free(priv, priv->rxq); + priv->rxq = NULL; + } + ipw_tx_queue_free(priv); + if (bootfw) + release_firmware(bootfw); + if (ucode) + release_firmware(ucode); + if (firmware) + release_firmware(firmware); +#ifdef CONFIG_PM + fw_loaded = 0; +#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 + */ + +static inline int ipw_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(%d) 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 (bd->u.data.num_chunks > NUM_TFD_CHUNKS) { + IPW_ERROR("Too many chunks: %i\n", bd->u.data.num_chunks); + /** @todo issue fatal error, it is quite serious situation */ + return; + } + + /* unmap chunks if any */ + for (i = 0; i < bd->u.data.num_chunks; i++) { + pci_unmap_single(dev, bd->u.data.chunk_ptr[i], + 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 inline __maybe_wake_tx(struct ipw_priv *priv) +{ + if (netif_running(priv->net_dev)) { + switch (priv->port_type) { + case DCR_TYPE_MU_BSS: + case DCR_TYPE_MU_IBSS: + if (!(priv->status & STATUS_ASSOCIATED)) { + return; + } + } + netif_wake_queue(priv->net_dev); + } + +} + +static inline 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 inline 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)) + return i; + + if (i == MAX_STATIONS) + return IPW_INVALID_STATION; + + 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 inline 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 int ipw_tx_skb(struct ipw_priv *priv, struct sk_buff *skb) +{ + int rc = 0, i = 0; + struct tfd_frame *tfd; + struct clx2_tx_queue *txq = &priv->txq[0]; + struct clx2_queue *q = &txq->q; + struct ieee80211_txb *txb; + u16 frame_ctl; + u8 dst[ETH_ALEN]; + u8 src[ETH_ALEN]; + + memcpy(dst, skb->data, ETH_ALEN); + memcpy(src, skb->data + ETH_ALEN, ETH_ALEN); + + txb = ieee80211_skb_to_txb(priv->ieee, skb); + if (txb == NULL) { + IPW_DEBUG_DROP("Failed to Tx packet\n"); + + /* + this is the one and only error path that should free + the skb since ieee80211_skb_to_txb will free the skb + when there are no errors + */ + dev_kfree_skb(skb); + return -EIO; + } + + /* If the call succeeded, but packet was dropped (valid scenario + * in WPA configurations) */ + if (txb->nr_frags == 0) { + rc = 0; + goto out_free_txb; + } + + if (txb->nr_frags > NUM_TFD_CHUNKS - 1) { + /* TODO: merge fragments back into a single buffer */ + IPW_ERROR("Too many 80211 fragments, lower the " + "device FTS\n"); + goto out_free_txb; + } + + tfd = &txq->bd[q->first_empty]; + txq->txb[q->first_empty] = txb; + memset(tfd, 0, sizeof(*tfd)); + + if (priv->ieee->iw_mode == IW_MODE_ADHOC) { + u8 id = ipw_find_station(priv, dst); + if (id == IPW_INVALID_STATION) { + if (is_broadcast_ether_addr(dst) || + is_multicast_ether_addr(dst)) { + id = ipw_add_station(priv, dst); + } else { + IPW_ERROR("Attempt to send data to " + "non-existent cell: " MAC_FMT "\n", + MAC_ARG(dst)); + rc = -EIO; + goto out_free_txb; + } + } + 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 = txb->payload_size; + tfd->u.data.tx_flags = DCT_FLAG_NO_WEP | DCT_FLAG_ACK_REQD; + + if (priv->ieee->modulation & IEEE80211_OFDM_MODULATION) + tfd->u.data.tx_flags_ext = DCT_FLAG_EXT_MODE_OFDM; + else + tfd->u.data.tx_flags_ext = DCT_FLAG_EXT_MODE_CCK; + if (priv->config & CFG_PREAMBLE) + tfd->u.data.tx_flags |= DCT_FLAG_SHORT_PREMBL; + frame_ctl = IEEE80211_FTYPE_DATA | IEEE80211_STYPE_DATA; + if (priv->ieee->iw_mode == IW_MODE_INFRA) { + frame_ctl |= IEEE80211_FCTL_TODS; + /* To DS: Addr1 = BSSID, Addr2 = SA, Addr3 = DA */ + memcpy(tfd->u.data.tfd.tfd_24.mchdr.addr1, priv->ieee->bssid, + ETH_ALEN); + memcpy(tfd->u.data.tfd.tfd_24.mchdr.addr2, src, ETH_ALEN); + memcpy(tfd->u.data.tfd.tfd_24.mchdr.addr3, dst, ETH_ALEN); + } else if (priv->ieee->iw_mode == IW_MODE_ADHOC) { + /* not From/To DS: Addr1 = DA, Addr2 = SA, Addr3 = BSSID */ + memcpy(tfd->u.data.tfd.tfd_24.mchdr.addr1, dst, ETH_ALEN); + memcpy(tfd->u.data.tfd.tfd_24.mchdr.addr2, src, ETH_ALEN); + memcpy(tfd->u.data.tfd.tfd_24.mchdr.addr3, priv->ieee->bssid, + ETH_ALEN); + } else { + IPW_ERROR("Unknown ieee->iw_mode in Tx: %d\n", + priv->ieee->iw_mode); + } + if (txb->encrypted) + frame_ctl |= IEEE80211_FCTL_WEP; + + tfd->u.data.tfd.tfd_24.mchdr.ctrl1 = frame_ctl & 0xff; + tfd->u.data.tfd.tfd_24.mchdr.ctrl2 = (frame_ctl >> 8) & 0xff; + + /* payload */ + tfd->u.data.num_chunks = txb->nr_frags; + for (i = 0; i < tfd->u.data.num_chunks; i++) { + IPW_DEBUG_TX("Dumping TX packet frag %i of %i (%d bytes):\n", + i, tfd->u.data.num_chunks, + txb->fragments[i]->len); + printk_buf(IPW_DL_TX, txb->fragments[i]->data, + txb->fragments[i]->len); + + tfd->u.data.chunk_ptr[i] = pci_map_single( + priv->pci_dev, txb->fragments[i]->data, + txb->fragments[i]->len, PCI_DMA_TODEVICE); + tfd->u.data.chunk_len[i] = txb->fragments[i]->len; + } + + /* kick DMA */ + q->first_empty = ipw_queue_inc_wrap(q->first_empty, q->n_bd); + ipw_write32(priv, q->reg_w, q->first_empty); + + /* Keep TIMEOUT from happening while we're still developing... */ + priv->net_dev->trans_start = jiffies; + + if (ipw_queue_space(q) < q->high_mark) { + IPW_DEBUG_INFO("Stopping network queue\n"); + netif_stop_queue(priv->net_dev); + } + + goto done; + + out_free_txb: + priv->net_dev->trans_start = jiffies; + ieee80211_txb_free(txb); + txq->txb[q->first_empty] = NULL; + done: + return rc; +} + +static void ipw_disassociate(void *data) +{ + struct ipw_priv *priv = data; + int err; + + if (!(priv->status & (STATUS_ASSOCIATING | STATUS_ASSOCIATED))) { + IPW_DEBUG_ASSOC("Disassociating while not associated.\n"); + return; + } + + IPW_DEBUG_ASSOC("Disassocation attempt from " MAC_FMT " " + "on channel %d.\n", + MAC_ARG(priv->assoc_request.bssid), + priv->assoc_request.channel); + 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; + } + + priv->status &= ~(STATUS_ASSOCIATING | STATUS_ASSOCIATED); + priv->status |= STATUS_DISASSOCIATING; +} + +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 inline void ipw_reset_counters(struct ipw_priv *priv) +{ + priv->missed_beacons = 0; + priv->tx_packets = 0; +} + +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) + return ipw_status_codes[i].reason; + return "Unknown status value."; +} + +/** + * Handle host notification packet. + * Called from interrupt routine + */ +static inline void ipw_rx_notification(struct ipw_priv* priv, + struct ipw_rx_notification *notif) +{ + IPW_DEBUG_NOTIF("type = %i (%d bytes)\n", + notif->subtype, notif->size); + + switch (notif->subtype) { + case HOST_NOTIFICATION_STATUS_ASSOCIATED: { + struct notif_association *assoc = ¬if->u.assoc; + + switch (assoc->state) { + case CMAS_ASSOCIATED: { + IPW_DEBUG(IPW_DL_NOTIF | IPW_DL_STATE | IPW_DL_ASSOC, + "associated: '%s' " MAC_FMT " \n", + escape_essid(priv->essid, priv->essid_len), + MAC_ARG(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); + break; + } + + ipw_reset_counters(priv); + + priv->status &= ~STATUS_ASSOCIATING; + priv->status |= STATUS_ASSOCIATED; + + netif_carrier_on(priv->net_dev); + if (netif_queue_stopped(priv->net_dev)) { + IPW_DEBUG_NOTIF("waking queue\n"); + netif_wake_queue(priv->net_dev); + } else { + IPW_DEBUG_NOTIF("starting queue\n"); + netif_start_queue(priv->net_dev); + } + + notify_wx_assoc_event(priv); + break; + } + + case CMAS_AUTHENTICATED: { + IPW_DEBUG(IPW_DL_NOTIF | IPW_DL_STATE | IPW_DL_ASSOC, + "authenticated: '%s' " MAC_FMT "\n", + escape_essid(priv->essid, priv->essid_len), + MAC_ARG(priv->bssid)); + break; + } + + case CMAS_INIT: { + IPW_DEBUG(IPW_DL_NOTIF | IPW_DL_STATE | IPW_DL_ASSOC, + "disassociated: '%s' " MAC_FMT " \n", + escape_essid(priv->essid, priv->essid_len), + MAC_ARG(priv->bssid)); + + priv->status &= ~( + STATUS_DISASSOCIATING | + STATUS_ASSOCIATING | + STATUS_ASSOCIATED | + STATUS_AUTH); + + netif_carrier_off(priv->net_dev); + netif_stop_queue(priv->net_dev); + notify_wx_assoc_event(priv); + + /* Cancel any queued work ... */ + cancel_delayed_work(&priv->request_scan); + + /* Queue up another scan... */ + queue_work(priv->workqueue, &priv->request_scan); + break; + } + + default: + IPW_ERROR("assoc: unknown (%d)\n", + assoc->state); + break; + } + + break; + } + + case HOST_NOTIFICATION_STATUS_AUTHENTICATE: { + struct notif_authenticate *auth = ¬if->u.auth; + switch (auth->state) { + case CMAS_AUTHENTICATED: + IPW_DEBUG(IPW_DL_NOTIF | IPW_DL_STATE, + "authenticated: '%s' " MAC_FMT " \n", + escape_essid(priv->essid, priv->essid_len), + MAC_ARG(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", + ntohs(auth->status), + ipw_get_status_code(ntohs(auth->status))); + } + IPW_DEBUG(IPW_DL_NOTIF | IPW_DL_STATE | IPW_DL_ASSOC, + "deauthenticated: '%s' " MAC_FMT "\n", + escape_essid(priv->essid, priv->essid_len), + MAC_ARG(priv->bssid)); + + priv->status &= ~(STATUS_ASSOCIATING | + STATUS_ASSOCIATED | + STATUS_AUTH); + + netif_carrier_off(priv->net_dev); + netif_stop_queue(priv->net_dev); + notify_wx_assoc_event(priv); + queue_work(priv->workqueue, &priv->request_scan); + 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 = ¬if->u.channel_result; + + if (notif->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 %d)\n", + notif->size,sizeof(*x)); + } + break; + } + + case HOST_NOTIFICATION_STATUS_SCAN_COMPLETED: { + struct notif_scan_complete* x = ¬if->u.scan_complete; + if (notif->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 %d)\n", + notif->size,sizeof(*x)); + } + + priv->status &= ~(STATUS_SCANNING | STATUS_SCAN_ABORTING); + if (priv->status & STATUS_SCAN_PENDING) + queue_work(priv->workqueue, &priv->request_scan); + else if (!(priv->status & (STATUS_ASSOCIATED | + STATUS_ASSOCIATING))) + queue_work(priv->workqueue, &priv->associate); + else if (priv->config & CFG_ASSOCIATE) + queue_delayed_work( + priv->workqueue, &priv->request_scan, + SCAN_INTERVAL); + priv->ieee->scans++; + break; + } + + case HOST_NOTIFICATION_STATUS_FRAG_LENGTH: { + struct notif_frag_length *x = ¬if->u.frag_len; + + if (notif->size == sizeof(*x)) { + IPW_ERROR("Frag length: %d\n", x->frag_length); + } else { + IPW_ERROR("Frag length of wrong size %d " + "(should be %d)\n", + notif->size, sizeof(*x)); + } + break; + } + + case HOST_NOTIFICATION_STATUS_LINK_DETERIORATION: { + struct notif_link_deterioration *x = + ¬if->u.link_deterioration; + if (notif->size==sizeof(*x)) { + IPW_DEBUG(IPW_DL_NOTIF | IPW_DL_STATE, + "link deterioration: '%s' " MAC_FMT " \n", + escape_essid(priv->essid, priv->essid_len), + MAC_ARG(priv->bssid)); + memcpy(&priv->last_link_deterioration, x, sizeof(*x)); + } else { + IPW_ERROR("Link Deterioration of wrong size %d " + "(should be %d)\n", + notif->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) { + /* TODO: Do anything special? */ + } else { + IPW_ERROR("Unexpected DINO_CONFIG_RESPONSE\n"); + } + break; + } + + case HOST_NOTIFICATION_STATUS_BEACON_STATE: { + struct notif_beacon_state *x = ¬if->u.beacon_state; + if (notif->size != sizeof(*x)) { + IPW_ERROR("Beacon state of wrong size %d (should " + "be %d)\n", notif->size, sizeof(*x)); + break; + } + + if (x->state == HOST_NOTIFICATION_STATUS_BEACON_MISSING) { + if (priv->status & STATUS_SCANNING) { + /* TODO: Stop scan to keep fw from getting + * stuck... */ + IPW_WARNING("TODO: Stop scan\n"); + } + + if (x->number - priv->missed_beacons > + priv->missed_beacon_threshold && + priv->status & STATUS_ASSOCIATED) { + IPW_DEBUG(IPW_DL_INFO | IPW_DL_NOTIF, + "Missed beacon: %d - disassociate\n", + x->number); + priv->missed_beacons = x->number; + queue_work(priv->workqueue, + &priv->disassociate); + } else if (x->number - priv->missed_beacons > + priv->roaming_threshold) { + IPW_DEBUG_NOTIF("Missed beacon: %d - initiate " + "roaming\n", + x->number); + /* TODO: Roaming... */ + priv->missed_beacons = x->number; + } else { + IPW_DEBUG_NOTIF("Missed beacon: %d\n", + x->number); + } + } + + + break; + } + + case HOST_NOTIFICATION_STATUS_TGI_TX_KEY: { + struct notif_tgi_tx_key *x = ¬if->u.tgi_tx_key; + if (notif->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 %d)\n", + notif->size,sizeof(*x)); + break; + } + + case HOST_NOTIFICATION_CALIB_KEEP_RESULTS: { + struct notif_calibration *x = ¬if->u.calibration; + + if (notif->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 %d)\n", + notif->size,sizeof(*x)); + break; + } + + default: + IPW_ERROR("Unknown notification: " + "subtype=%d,flags=0x%2x,size=%d\n", + notif->subtype, notif->flags, notif->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, + CX2_TX_CMD_QUEUE_READ_INDEX, + CX2_TX_CMD_QUEUE_WRITE_INDEX, + CX2_TX_CMD_QUEUE_BD_BASE, + CX2_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, + CX2_TX_QUEUE_0_READ_INDEX, + CX2_TX_QUEUE_0_WRITE_INDEX, + CX2_TX_QUEUE_0_BD_BASE, + CX2_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, + CX2_TX_QUEUE_1_READ_INDEX, + CX2_TX_QUEUE_1_WRITE_INDEX, + CX2_TX_QUEUE_1_BD_BASE, + CX2_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, + CX2_TX_QUEUE_2_READ_INDEX, + CX2_TX_QUEUE_2_WRITE_INDEX, + CX2_TX_QUEUE_2_BD_BASE, + CX2_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, + CX2_TX_QUEUE_3_READ_INDEX, + CX2_TX_QUEUE_3_WRITE_INDEX, + CX2_TX_QUEUE_3_BD_BASE, + CX2_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 adwances '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_queue_space(q) > q->low_mark && qindex >= 0) { + __maybe_wake_tx(priv); + } + 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_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 CX2_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; + + int write = rxq->write; + + while ((rxq->write != rxq->processed) && (rxq->free_count)) { + element = rxq->rx_free.next; + rxb = list_entry(element, struct ipw_rx_mem_buffer, list); + list_del(element); + + ipw_write32(priv, CX2_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--; + } + + /* 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, CX2_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; + + 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(CX2_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->rxb = (struct ipw_rx_buffer *)rxb->skb->data; + rxb->dma_addr = pci_map_single( + priv->pci_dev, rxb->skb->data, CX2_RX_BUF_SIZE, + PCI_DMA_FROMDEVICE); + + list_add_tail(&rxb->list, &rxq->rx_free); + rxq->free_count++; + } + + ipw_rx_queue_restock(priv); +} + +/* Assumes that the skb field of the buffers in 'pool' is kept accurate. + * If an SKB has been detached, the POOL needs to have it's 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, + CX2_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 = (struct ipw_rx_queue *)kmalloc(sizeof(*rxq), GFP_KERNEL); + memset(rxq, 0, sizeof(*rxq)); + 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->processed = RX_QUEUE_SIZE - 1; + 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])) { + 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])) { + 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 rates->num_rates; +} + +static inline 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_best_network( + struct ipw_priv *priv, + struct ipw_network_match *match, + struct ieee80211_network *network) +{ + struct ipw_supported_rates rates; + + /* 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_BSS)) || + (priv->ieee->iw_mode == IW_MODE_ADHOC && + !(network->capability & WLAN_CAPABILITY_IBSS))) { + IPW_DEBUG_ASSOC("Network '%s (" MAC_FMT ")' excluded due to " + "capability mismatch.\n", + escape_essid(network->ssid, network->ssid_len), + MAC_ARG(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 (" MAC_FMT ")' excluded " + "because of hidden ESSID.\n", + escape_essid(network->ssid, network->ssid_len), + MAC_ARG(network->bssid)); + 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 (" MAC_FMT ")' excluded because " + "'%s (" MAC_FMT ")' has a stronger signal.\n", + escaped, MAC_ARG(network->bssid), + escape_essid(match->network->ssid, + match->network->ssid_len), + MAC_ARG(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 + HZ * 3 > jiffies) { + IPW_DEBUG_ASSOC("Network '%s (" MAC_FMT ")' excluded " + "because of storming.\n", + escape_essid(network->ssid, network->ssid_len), + MAC_ARG(network->bssid)); + return 0; + } + + /* Now go through and see if the requested network is valid... */ + if (priv->ieee->scan_age != 0 && + jiffies - network->last_scanned > priv->ieee->scan_age) { + IPW_DEBUG_ASSOC("Network '%s (" MAC_FMT ")' excluded " + "because of age: %lums.\n", + escape_essid(network->ssid, network->ssid_len), + MAC_ARG(network->bssid), + (jiffies - network->last_scanned) / (HZ / 100)); + return 0; + } + + if ((priv->config & CFG_STATIC_CHANNEL) && + (network->channel != priv->channel)) { + IPW_DEBUG_ASSOC("Network '%s (" MAC_FMT ")' excluded " + "because of channel mismatch: %d != %d.\n", + escape_essid(network->ssid, network->ssid_len), + MAC_ARG(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 (" MAC_FMT ")' excluded " + "because of privacy mismatch: %s != %s.\n", + escape_essid(network->ssid, network->ssid_len), + MAC_ARG(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 (" MAC_FMT ")' excluded " + "because of BSSID mismatch: " MAC_FMT ".\n", + escape_essid(network->ssid, network->ssid_len), + MAC_ARG(network->bssid), + MAC_ARG(priv->bssid)); + return 0; + } + + /* If an ESSID has been configured but this AP did not + * broadcast its ESSID, then it is a valid match. Otherwise, + * compare the broadcast ESSID to ours */ + if ((priv->config & CFG_STATIC_ESSID) && + !(network->flags & NETWORK_EMPTY_ESSID) && + 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 (" MAC_FMT ")' excluded " + "because of ESSID mismatch: '%s'.\n", + escaped, MAC_ARG(network->bssid), + escape_essid(priv->essid, priv->essid_len)); + return 0; + } + + /* Filter out any incompatible freq / mode combinations */ + if (!ieee80211_is_valid_mode(priv->ieee, network->mode)) { + IPW_DEBUG_ASSOC("Network '%s (" MAC_FMT ")' excluded " + "because of invalid frequency/mode " + "combination.\n", + escape_essid(network->ssid, network->ssid_len), + MAC_ARG(network->bssid)); + return 0; + } + + ipw_compatible_rates(priv, network, &rates); + if (rates.num_rates == 0) { + IPW_DEBUG_ASSOC("Network '%s (" MAC_FMT ")' excluded " + "because of no compatible rates.\n", + escape_essid(network->ssid, network->ssid_len), + MAC_ARG(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 (" MAC_FMT ")' is a viable match.\n", + escape_essid(network->ssid, network->ssid_len), + MAC_ARG(network->bssid)); + + return 1; +} + +static u8 ipw_calc_rssi_dbm(u8 rssi, u8 agc) +{ + u16 tmp1 = (agc & BIT(6)) ? 16 : 0; + u16 tmp2 = (agc & BIT(5)) ? 16 : 0; + u16 tmp3 = agc & 0xF; + tmp3 += tmp3 >> 1; + return rssi - (tmp1 + tmp2 + tmp3 + 35); +} + +static void ipw_adhoc_create(struct ipw_priv *priv, + struct ieee80211_network *network) +{ + 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; + network->channel = priv->channel; + 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->mode = priv->ieee->mode; + 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 */ +#ifdef CONFIG_IEEE80211_WPA + network->wpa_ie_len = 0; + network->rsn_ie_len = 0; +#endif /* CONFIG_IEEE80211_WPA */ +} + +static void ipw_send_wep_keys(struct ipw_priv *priv) +{ + struct ipw_wep_key *key; + int i; + struct host_cmd cmd = { + .cmd = IPW_CMD_WEP_KEY, + .len = sizeof(*key) + }; + + key = (struct ipw_wep_key *)&cmd.param; + key->cmd_id = DINO_CMD_WEP_KEY; + key->seq_num = 0; + + for (i = 0; i < 4; i++) { + key->key_index = i; + if (!(priv->sec.flags & (1 << i))) { + key->key_size = 0; + } else { + key->key_size = priv->sec.key_sizes[i]; + memcpy(key->key, priv->sec.keys[i], key->key_size); + } + + if (ipw_send_cmd(priv, &cmd)) { + IPW_ERROR("failed to send WEP_KEY command\n"); + return; + } + } +} + +static void 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; + int err; + + list_for_each_entry(network, &priv->ieee->network_list, list) + ipw_best_network(priv, &match, network); + + 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 && + !list_empty(&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); + } + + /* If we reached the end of the list, then we don't have any valid + * matching APs */ + if (!network) { +#ifdef CONFIG_IPW_DEBUG + IPW_DEBUG_INFO("Scan completed, no valid APs matched " + "[CFG 0x%08lX]\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 %d\n", priv->channel); + 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); +#endif + + queue_delayed_work(priv->workqueue, &priv->request_scan, + SCAN_INTERVAL); + + return; + } + + if (priv->config & CFG_FIXED_RATE) { + /* 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 */ + fr.tx_rates = 0; + break; + } + + fr.tx_rates >>= IEEE80211_OFDM_SHIFT_MASK_A; + break; + + default: /* 2.4Ghz or Mixed */ + /* IEEE_B */ + if (network->mode == IEEE_B) { + if (fr.tx_rates & ~IEEE80211_CCK_RATES_MASK) { + /* Invalid fixed rate mask */ + fr.tx_rates = 0; + } + break; + } + + /* IEEE_G */ + if (fr.tx_rates & ~(IEEE80211_CCK_RATES_MASK | + IEEE80211_OFDM_RATES_MASK)) { + /* Invalid fixed rate mask */ + 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); + } + + 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); + } + + IPW_DEBUG_ASSOC("Assocation attempt: '%s', channel %d, " + "802.11%c [%d], enc=%s%s.\n", + escape_essid(priv->essid, priv->essid_len), + network->channel, ipw_modes[network->mode], + rates->num_rates, + priv->capability & CAP_PRIVACY_ON ? "on " : "off", + priv->capability & CAP_PRIVACY_ON ? + (priv->capability & CAP_SHARED_KEY ? "(shared)" : + "(open)") : ""); + + network->last_associate = jiffies; + priv->ieee->mode = network->mode; + + memset(&priv->assoc_request, 0, sizeof(priv->assoc_request)); + priv->assoc_request.channel = network->channel; + if ((priv->capability & CAP_PRIVACY_ON) && + (priv->capability & CAP_SHARED_KEY)) { + priv->assoc_request.auth_type = AUTH_SHARED_KEY; + priv->assoc_request.auth_key = priv->sec.active_key; + } else { + priv->assoc_request.auth_type = AUTH_OPEN; + priv->assoc_request.auth_key = 0; + } + + if (priv->capability & CAP_PRIVACY_ON) + ipw_send_wep_keys(priv); + + priv->assoc_request.ieee_mode = priv->ieee->mode; + + priv->assoc_request.beacon_interval = 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 { + priv->assoc_request.assoc_type = HC_ASSOCIATE; + priv->assoc_request.assoc_tsf_msw = network->time_stamp[1]; + priv->assoc_request.assoc_tsf_lsw = 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 = network->atim_window; + } else { + memcpy(&priv->assoc_request.dest, network->bssid, ETH_ALEN); + priv->assoc_request.atim_window = 0; + } + + priv->assoc_request.capability = network->capability; + priv->assoc_request.listen_interval = 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; + } + + rates->ieee_mode = priv->ieee->mode; + rates->purpose = IPW_RATE_CONNECT; + ipw_send_supported_rates(priv, rates); + + if (network->mode == IEEE_G) + priv->sys_config.dot11g_auto_detection = 1; + else + priv->sys_config.dot11g_auto_detection = 0; + err = ipw_send_system_config(priv, &priv->sys_config); + if (err) { + IPW_DEBUG_HC("Attempt to send sys config command failed.\n"); + return; + } + + err = ipw_set_sensitivity(priv, network->stats.rssi ? + network->stats.rssi + 112 : 0); + if (err) { + IPW_DEBUG_HC("Attempt to send associate command failed.\n"); + return; + } + + err = ipw_send_associate(priv, &priv->assoc_request); + if (err) { + IPW_DEBUG_HC("Attempt to send associate command failed.\n"); + return; + } + + priv->channel = network->channel; + memcpy(priv->bssid, network->bssid, ETH_ALEN); + + priv->status |= STATUS_ASSOCIATING; + + IPW_DEBUG(IPW_DL_STATE, "associating: '%s' " MAC_FMT " \n", + escape_essid(priv->essid, priv->essid_len), + MAC_ARG(priv->bssid)); +} + +static inline void ipw_handle_data_packet(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; + + /* We only process data packets if the + * interface is open */ + if (unlikely((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, (u32)&pkt->u.frame.data[0] - (u32)pkt); + + /* Set the size of the skb to the size of the frame */ + skb_put(rxb->skb, pkt->u.frame.length); + + 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; +} + + +/* + * 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 *header; + u32 r, w, i; + + r = ipw_read32(priv, CX2_RX_READ_INDEX); + w = ipw_read32(priv, CX2_RX_WRITE_INDEX); + i = (priv->rxq->processed + 1) % RX_QUEUE_SIZE; + + while (i != r) { + rxb = priv->rxq->queue[i]; +#ifdef CONFIG_IPW_DEBUG + if (unlikely(rxb == NULL)) { + printk(KERN_CRIT "Queue not allocated!\n"); + break; + } +#endif + priv->rxq->queue[i] = NULL; + + pci_dma_sync_single_for_cpu(priv->pci_dev, rxb->dma_addr, + CX2_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 = ipw_calc_rssi_dbm( + pkt->u.frame.rssi, pkt->u.frame.agc), + .signal = pkt->u.frame.signal, + .rate = pkt->u.frame.rate, + .mac_time = jiffies, + .received_channel = pkt->u.frame.received_channel, + .freq = (pkt->u.frame.control & BIT(0)) ? IEEE80211_24GHZ_BAND : IEEE80211_52GHZ_BAND, + .len = pkt->u.frame.length, + }; + + if (stats.rssi != 0) + stats.mask |= IEEE80211_STATMASK_RSSI; + if (stats.signal != 0) + stats.mask |= IEEE80211_STATMASK_SIGNAL; + if (stats.rate != 0) + stats.mask |= IEEE80211_STATMASK_RATE; + + priv->last_rx_rssi = stats.rssi; + priv->last_rx_rate = stats.rate; + + header = (struct ieee80211_hdr *)(rxb->skb->data + + IPW_RX_FRAME_SIZE); + + IPW_DEBUG_RX("Frame: len=%u\n", pkt->u.frame.length); + +#ifdef CONFIG_IPW_PROMISC + if (priv->ieee->iw_mode == IW_MODE_MONITOR) { + ipw_handle_data_packet(priv, rxb, &stats); + break; + } +#endif + + if (pkt->u.frame.length < frame_hdr_len(header)) { + IPW_DEBUG_DROP("Received packet is too small. " + "Dropping.\n"); + priv->ieee->stats.rx_errors++; + priv->wstats.discard.misc++; + break; + } + + header = (struct ieee80211_hdr *)(rxb->skb->data + + IPW_RX_FRAME_SIZE); + switch (WLAN_FC_GET_TYPE(header->frame_ctl)) { + case IEEE80211_FTYPE_MGMT: + ieee80211_rx_mgt(priv->ieee, header, &stats); + if (priv->ieee->iw_mode == IW_MODE_ADHOC && + ((WLAN_FC_GET_STYPE(header->frame_ctl) == + IEEE80211_STYPE_PROBE_RESP) || + (WLAN_FC_GET_STYPE(header->frame_ctl) == + IEEE80211_STYPE_BEACON)) && + !memcmp(header->addr3, priv->bssid, ETH_ALEN)) { + IPW_DEBUG_SCAN("Adding AdHoc station: " + MAC_FMT "\n", + MAC_ARG(header->addr2)); + ipw_add_station(priv, header->addr2); + } + break; + + case IEEE80211_FTYPE_CTL: + break; + + case IEEE80211_FTYPE_DATA: + 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, + 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, + CX2_RX_BUF_SIZE, PCI_DMA_FROMDEVICE); + list_add_tail(&rxb->list, &priv->rxq->rx_used); + + i = (i + 1) % RX_QUEUE_SIZE; + } + + /* Backtrack one entry */ + priv->rxq->processed = (i ? i : RX_QUEUE_SIZE) - 1; + + ipw_rx_queue_restock(priv); +} + +static void ipw_adapter_restart(void *adapter) +{ + struct ipw_priv *priv = adapter; + + if (priv->status & STATUS_RF_KILL_MASK) + return; + + ipw_down(priv); + if (ipw_up(priv)) { + IPW_ERROR("Failed to up device\n"); + return; + } +} + + + +static inline int 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 0; + } + priv->status |= STATUS_SCAN_ABORTING; + + err = ipw_send_scan_abort(priv); + if (err) { + IPW_DEBUG_HC("Request to abort scan failed.\n"); + return err; + } + + return 0; +} + +static int ipw_request_scan(struct ipw_priv *priv) +{ + struct ipw_scan_request_ext scan; + int number_of_channel = 1; + int number_of_a_channel = 1; + int i, err, scan_type; + + if (priv->status & STATUS_EXIT_PENDING) { + IPW_DEBUG_SCAN("Aborting scan due to device shutdown\n"); + priv->status |= STATUS_SCAN_PENDING; + return 0; + } + + if (priv->status & STATUS_SCANNING) { + IPW_DEBUG_HC("Concurrent scan requested. Aborting first.\n"); + priv->status |= STATUS_SCAN_PENDING; + ipw_abort_scan(priv); + return 0; + } + + if (priv->status & STATUS_SCAN_ABORTING) { + IPW_DEBUG_HC("Scan request while abort pending. Queuing.\n"); + priv->status |= STATUS_SCAN_PENDING; + return 0; + } + + if (priv->status & STATUS_RF_KILL_MASK) { + IPW_DEBUG_HC("Aborting scan due to RF Kill activation\n"); + priv->status |= STATUS_SCAN_PENDING; + return 0; + } + + memset(&scan, 0, sizeof(scan)); + + scan.dwell_time[IPW_SCAN_ACTIVE_BROADCAST_SCAN] = 20; + scan.dwell_time[IPW_SCAN_ACTIVE_BROADCAST_AND_DIRECT_SCAN] = 100; + scan.dwell_time[IPW_SCAN_PASSIVE_FULL_DWELL_SCAN] = 120; + + scan.full_scan_index = ieee80211_get_scans(priv->ieee); + /* Ensure that every other scan is a fast channel hop scan */ + if (priv->config & CFG_STATIC_ESSID && (priv->ieee->scans % 2)) { + err = ipw_send_ssid(priv, priv->essid, priv->essid_len); + if (err) { + IPW_DEBUG_HC("Attempt to send SSID command failed.\n"); + return err; + } + + scan_type = IPW_SCAN_ACTIVE_BROADCAST_AND_DIRECT_SCAN; + } else { + scan_type = IPW_SCAN_ACTIVE_BROADCAST_SCAN; + } + + if (priv->ieee->freq_band & IEEE80211_52GHZ_BAND) { + for (i = 0; i < MAX_A_CHANNELS; i++) { + if (band_a_active_channel[i] == 0) + break; + scan.channels_list[number_of_a_channel] = band_a_active_channel[i]; + ipw_set_scan_type(&scan, number_of_a_channel, scan_type); + number_of_a_channel++; + } + scan.channels_list[0] = (u8)(IEEE_A << 6) | (number_of_a_channel - 1); + number_of_channel = number_of_a_channel + 1; + } + + if (number_of_a_channel == 1) { + number_of_a_channel = 0; + number_of_channel = 1; + } + + if (priv->ieee->freq_band & IEEE80211_24GHZ_BAND) { + for (i = 0; i <= MAX_B_CHANNELS; i++) { + if (band_b_active_channel[i] == 0) + break; + scan.channels_list[number_of_channel] = band_b_active_channel[i]; + ipw_set_scan_type(&scan, number_of_channel, scan_type); + number_of_channel++; + } + + scan.channels_list[number_of_a_channel] = (u8)(IEEE_B << 6) | (number_of_channel - number_of_a_channel - 1); + } + + /* TODO -- Remove the currently active channel from the list of + * channels set for active scan */ + + err = ipw_send_scan_request_ext(priv, &scan); + if (err) { + IPW_DEBUG_HC("Sending scan command failed: %08X\n", + err); + return -EIO; + } + + priv->status |= STATUS_SCANNING; + priv->status &= ~STATUS_SCAN_PENDING; + + return 0; +} + +/* + * 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 = netdev_priv(dev); + if (!(priv->status & STATUS_ASSOCIATED)) + strcpy(wrqu->name, "unassociated"); + else + snprintf(wrqu->name, IFNAMSIZ, "IEEE 802.11%c", + ipw_modes[priv->ieee->mode]); + IPW_DEBUG_WX("Name: %s\n", wrqu->name); + 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; + if (!(priv->status & (STATUS_SCANNING | STATUS_ASSOCIATED | + STATUS_ASSOCIATING))) { + 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; + + /* If we are currently associated, or trying to associate + * then see if this is a new channel (causing us to disassociate) */ + if (priv->status & (STATUS_ASSOCIATED | STATUS_ASSOCIATING)) { + IPW_DEBUG_ASSOC("Disassociating due to channel change.\n"); + 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 = netdev_priv(dev); + struct iw_freq *fwrq = &wrqu->freq; + + /* 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 != ipw_frequencies[c])) + c++; + + /* hack to fall through */ + fwrq->e = 0; + fwrq->m = c + 1; + } + } + + if (fwrq->e > 0 || fwrq->m > 1000) + return -EOPNOTSUPP; + + IPW_DEBUG_WX("SET Freq/Channel -> %d \n", fwrq->m); + return ipw_set_channel(priv, (u8)fwrq->m); + + return 0; +} + + +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 = netdev_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_ASSOCIATING | 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 ipw_wx_set_mode(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + struct ipw_priv *priv = netdev_priv(dev); + int err = 0; + + IPW_DEBUG_WX("Set MODE: %d\n", wrqu->mode); + + if (wrqu->mode == priv->ieee->iw_mode) + return 0; + + switch (wrqu->mode) { +#ifdef CONFIG_IPW_PROMISC + 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; + } + +#ifdef CONFIG_IPW_PROMISC + if (priv->ieee->iw_mode == IW_MODE_MONITOR) + priv->net_dev->type = ARPHRD_ETHER; + + if (wrqu->mode == IW_MODE_MONITOR) + priv->net_dev->type = ARPHRD_IEEE80211; +#endif /* CONFIG_IPW_PROMISC */ + +#ifdef CONFIG_PM + /* Free the existing firmware and reset the fw_loaded + * flag so ipw_load() will bring in the new firmawre */ + if (fw_loaded) { + fw_loaded = 0; + } + + release_firmware(bootfw); + release_firmware(ucode); + release_firmware(firmware); +#endif + + priv->ieee->iw_mode = wrqu->mode; + ipw_adapter_restart(priv); + + 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 = netdev_priv(dev); + + wrqu->mode = priv->ieee->iw_mode; + IPW_DEBUG_WX("Get MODE -> %d\n", wrqu->mode); + + return 0; +} + + +#define DEFAULT_FRAG_THRESHOLD 2342U +#define MIN_FRAG_THRESHOLD 256U +#define MAX_FRAG_THRESHOLD 2342U +#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 + +/* 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 = netdev_priv(dev); + struct iw_range *range = (struct iw_range *)extra; + u16 val; + int i; + + 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; /* > 8% missed beacons is 'bad' */ + /* 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 */ + + 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; /* Different token sizes */ + range->num_encoding_sizes = 2; /* Number of entry in the list */ + range->max_encoding_tokens = WEP_KEYS; /* Max number of tokens */ + + /* Set the Wireless Extension versions */ + range->we_version_compiled = WIRELESS_EXT; + range->we_version_source = 16; + + range->num_channels = FREQ_COUNT; + + val = 0; + for (i = 0; i < FREQ_COUNT; i++) { + range->freq[val].i = i + 1; + range->freq[val].m = ipw_frequencies[i] * 100000; + range->freq[val].e = 1; + val++; + + if (val == IW_MAX_FREQUENCIES) + break; + } + range->num_frequency = val; + + 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 = netdev_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; + + 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; + if (!(priv->status & (STATUS_SCANNING | STATUS_ASSOCIATED | + STATUS_ASSOCIATING))) { + IPW_DEBUG_ASSOC("Attempting to associate with new " + "parameters.\n"); + ipw_associate(priv); + } + + 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"); + return 0; + } + + IPW_DEBUG_WX("Setting mandatory BSSID to " MAC_FMT "\n", + MAC_ARG(wrqu->ap_addr.sa_data)); + + memcpy(priv->bssid, wrqu->ap_addr.sa_data, ETH_ALEN); + + /* If we are currently associated, or trying to associate + * then see if this is a new BSSID (causing us to disassociate) */ + if (priv->status & (STATUS_ASSOCIATED | STATUS_ASSOCIATING)) { + IPW_DEBUG_ASSOC("Disassociating due to BSSID change.\n"); + ipw_disassociate(priv); + } + + 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 = netdev_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 | 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: " MAC_FMT "\n", + MAC_ARG(wrqu->ap_addr.sa_data)); + 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 = netdev_priv(dev); + char *essid = ""; /* ANY */ + int length = 0; + + if (wrqu->essid.flags && wrqu->essid.length) { + length = wrqu->essid.length - 1; + essid = extra; + } + if (length == 0) { + IPW_DEBUG_WX("Setting ESSID to ANY\n"); + priv->config &= ~CFG_STATIC_ESSID; + if (!(priv->status & (STATUS_SCANNING | STATUS_ASSOCIATED | + STATUS_ASSOCIATING))) { + IPW_DEBUG_ASSOC("Attempting to associate with new " + "parameters.\n"); + ipw_associate(priv); + } + + return 0; + } + + 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"); + return 0; + } + + IPW_DEBUG_WX("Setting ESSID: '%s' (%d)\n", escape_essid(essid, length), + length); + + priv->essid_len = length; + memcpy(priv->essid, essid, priv->essid_len); + + /* If we are currently associated, or trying to associate + * then see if this is a new ESSID (causing us to disassociate) */ + if (priv->status & (STATUS_ASSOCIATED | STATUS_ASSOCIATING)) { + IPW_DEBUG_ASSOC("Disassociating due to ESSID change.\n"); + ipw_disassociate(priv); + } + + 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 = netdev_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 | 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 */ + } + + 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 = netdev_priv(dev); + + IPW_DEBUG_WX("Setting nick to '%s'\n", extra); + 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_TRACE("<<\n"); + 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 = netdev_priv(dev); + IPW_DEBUG_WX("Getting nick\n"); + wrqu->data.length = strlen(priv->nick) + 1; + memcpy(extra, priv->nick, wrqu->data.length); + wrqu->data.flags = 1; /* active */ + return 0; +} + + +static int ipw_wx_set_rate(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + IPW_DEBUG_WX("0x%p, 0x%p, 0x%p\n", dev, info, wrqu); + return -EOPNOTSUPP; +} + +static inline 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->ieee->mode == IEEE_B) + mask &= IEEE80211_CCK_RATES_MASK; + while (i && !(mask & i)) i >>= 1; + switch (i) { + case IEEE80211_CCK_RATE_1MB_MASK: return IPW_TX_RATE_1MB; + case IEEE80211_CCK_RATE_2MB_MASK: return IPW_TX_RATE_2MB; + case IEEE80211_CCK_RATE_5MB_MASK: return IPW_TX_RATE_5MB; + case IEEE80211_OFDM_RATE_6MB_MASK: return IPW_TX_RATE_6MB; + case IEEE80211_OFDM_RATE_9MB_MASK: return IPW_TX_RATE_9MB; + case IEEE80211_CCK_RATE_11MB_MASK: return IPW_TX_RATE_11MB; + case IEEE80211_OFDM_RATE_12MB_MASK: return IPW_TX_RATE_12MB; + case IEEE80211_OFDM_RATE_18MB_MASK: return IPW_TX_RATE_18MB; + case IEEE80211_OFDM_RATE_24MB_MASK: return IPW_TX_RATE_24MB; + case IEEE80211_OFDM_RATE_36MB_MASK: return IPW_TX_RATE_36MB; + case IEEE80211_OFDM_RATE_48MB_MASK: return IPW_TX_RATE_48MB; + case IEEE80211_OFDM_RATE_54MB_MASK: return IPW_TX_RATE_54MB; + } + + if (priv->ieee->mode == IEEE_B) + return IPW_TX_RATE_11MB; + else + return IPW_TX_RATE_54MB; +} + +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 { + IPW_DEBUG_WX("Defaulting to MAX rate\n"); + rate = 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; +} + +static int ipw_wx_get_rate(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + wrqu->bitrate.value = ipw_get_current_rate(netdev_priv(dev)); + + 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 = netdev_priv(dev); + + if (wrqu->rts.disabled) + priv->rts_threshold = DEFAULT_RTS_THRESHOLD; + else { + if (wrqu->rts.value < MIN_RTS_THRESHOLD || + wrqu->rts.value > MAX_RTS_THRESHOLD) + return -EINVAL; + + priv->rts_threshold = wrqu->rts.value; + } + + ipw_send_rts_threshold(priv, priv->rts_threshold); + 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 = netdev_priv(dev); + wrqu->rts.value = priv->rts_threshold; + wrqu->rts.fixed = 0; /* no auto select */ + wrqu->rts.disabled = + (wrqu->rts.value == DEFAULT_RTS_THRESHOLD); + + 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 = netdev_priv(dev); + struct ipw_tx_power tx_power; + int i; + + if (ipw_radio_kill_sw(priv, wrqu->power.disabled)) + return -EINPROGRESS; + + if (wrqu->power.flags != IW_TXPOW_DBM) + return -EINVAL; + + if ((wrqu->power.value > 20) || + (wrqu->power.value < -12)) + return -EINVAL; + + priv->tx_power = wrqu->power.value; + + memset(&tx_power, 0, sizeof(tx_power)); + + /* configure device for 'G' band */ + tx_power.ieee_mode = IEEE_G; + tx_power.num_channels = 11; + for (i = 0; i < 11; i++) { + tx_power.channels_tx_power[i].channel_number = i + 1; + tx_power.channels_tx_power[i].tx_power = priv->tx_power; + } + if (ipw_send_tx_power(priv, &tx_power)) + goto error; + + /* configure device to also handle 'B' band */ + tx_power.ieee_mode = IEEE_B; + if (ipw_send_tx_power(priv, &tx_power)) + goto error; + + return 0; + + error: + return -EIO; +} + + +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 = netdev_priv(dev); + + wrqu->power.value = priv->tx_power; + wrqu->power.fixed = 1; + wrqu->power.flags = IW_TXPOW_DBM; + wrqu->power.disabled = 0; + + IPW_DEBUG_WX("GET TX Power -> %d \n", 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 = netdev_priv(dev); + + if (wrqu->frag.disabled) + priv->ieee->fts = DEFAULT_FRAG_THRESHOLD; + else { + if (wrqu->frag.value < MIN_FRAG_THRESHOLD || + wrqu->frag.value > MAX_FRAG_THRESHOLD) + return -EINVAL; + + priv->ieee->fts = wrqu->frag.value & ~0x1; + } + + ipw_send_frag_threshold(priv, wrqu->frag.value); + 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 = netdev_priv(dev); + wrqu->frag.value = priv->ieee->fts; + wrqu->frag.fixed = 0; /* no auto select */ + wrqu->frag.disabled = + (wrqu->frag.value == DEFAULT_FRAG_THRESHOLD); + + 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) +{ + IPW_DEBUG_WX("0x%p, 0x%p, 0x%p\n", dev, info, wrqu); + return -EOPNOTSUPP; +} + + +static int ipw_wx_get_retry(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + IPW_DEBUG_WX("0x%p, 0x%p, 0x%p\n", dev, info, wrqu); + return -EOPNOTSUPP; +} + + +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 = netdev_priv(dev); + IPW_DEBUG_WX("Start scan\n"); + if (ipw_request_scan(priv)) + return -EIO; + 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 = netdev_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 = netdev_priv(dev); + return ieee80211_wx_set_encode(priv->ieee, info, wrqu, key); +} + +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 = netdev_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 = netdev_priv(dev); + int err; + + 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"); + return err; + } + + IPW_DEBUG_WX("SET Power Management Mode -> off\n"); + + 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 explicitely state all */ + break; + default: /* Otherwise we don't support it */ + IPW_DEBUG_WX("SET PM Mode: %X not supported.\n", + wrqu->power.flags); + 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"); + return err; + } + + IPW_DEBUG_WX("SET Power Management Mode -> 0x%02X\n", + priv->power_mode); + + 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 = netdev_priv(dev); + + if (!(priv->power_mode & IPW_POWER_ENABLED)) { + wrqu->power.disabled = 1; + } else { + wrqu->power.disabled = 0; + } + + 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 = netdev_priv(dev); + int mode = *(int *)extra; + int err; + + if ((mode < 1) || (mode > IPW_POWER_LIMIT)) { + mode = IPW_POWER_AC; + priv->power_mode = mode; + } else { + priv->power_mode = IPW_POWER_ENABLED | mode; + } + + if (priv->power_mode != mode) { + err = ipw_send_power_mode(priv, mode); + + if (err) { + IPW_DEBUG_WX("failed setting power mode.\n"); + return err; + } + } + + 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 = netdev_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 = netdev_priv(dev); + int mode = *(int *)extra; + u8 band = 0, modulation = 0; + + if (mode == 0 || mode & ~IEEE_MASK) { + IPW_WARNING("Attempt to set invalid wireless mode: %d\n", + mode); + return -EINVAL; + } + + if (priv->adapter == IPW_2915ABG) { + priv->ieee->abg_ture = 1; + if (mode & BIT(IEEE_A)) { + band |= IEEE80211_52GHZ_BAND; + modulation |= IEEE80211_OFDM_MODULATION; + } else + priv->ieee->abg_ture = 0; + } else { + if (mode & BIT(IEEE_A)) { + IPW_WARNING("Attempt to set 2200BG into " + "802.11a mode\n"); + return -EINVAL; + } + + priv->ieee->abg_ture = 0; + } + + if (mode & BIT(IEEE_B)) { + band |= IEEE80211_24GHZ_BAND; + modulation |= IEEE80211_CCK_MODULATION; + } else + priv->ieee->abg_ture = 0; + + if (mode & BIT(IEEE_G)) { + band |= IEEE80211_24GHZ_BAND; + modulation |= IEEE80211_OFDM_MODULATION; + } else + priv->ieee->abg_ture = 0; + + priv->ieee->freq_band = band; + priv->ieee->modulation = modulation; + init_supported_rates(priv, &priv->rates); + + /* If we are currently associated, or trying to associate + * then see if this is a new configuration (causing us to + * disassociate) */ + if (priv->status & (STATUS_ASSOCIATED | STATUS_ASSOCIATING)) { + /* The resulting association will trigger + * the new rates to be sent to the device */ + IPW_DEBUG_ASSOC("Disassociating due to mode change.\n"); + ipw_disassociate(priv); + } else + ipw_send_supported_rates(priv, &priv->rates); + + IPW_DEBUG_WX("PRIV SET MODE: %c%c%c\n", + mode & BIT(IEEE_A) ? 'a' : '.', + mode & BIT(IEEE_B) ? 'b' : '.', + mode & BIT(IEEE_G) ? 'g' : '.'); + 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 = netdev_priv(dev); + + switch (priv->ieee->freq_band) { + case IEEE80211_24GHZ_BAND: + switch (priv->ieee->modulation) { + case IEEE80211_CCK_MODULATION: + strncpy(extra, "802.11b (2)", MAX_WX_STRING); + break; + case IEEE80211_OFDM_MODULATION: + strncpy(extra, "802.11g (4)", MAX_WX_STRING); + break; + default: + strncpy(extra, "802.11bg (6)", MAX_WX_STRING); + break; + } + break; + + case IEEE80211_52GHZ_BAND: + strncpy(extra, "802.11a (1)", MAX_WX_STRING); + break; + + default: /* Mixed Band */ + switch (priv->ieee->modulation) { + case IEEE80211_CCK_MODULATION: + strncpy(extra, "802.11ab (3)", MAX_WX_STRING); + break; + case IEEE80211_OFDM_MODULATION: + strncpy(extra, "802.11ag (5)", MAX_WX_STRING); + break; + default: + strncpy(extra, "802.11abg (7)", MAX_WX_STRING); + break; + } + break; + } + + IPW_DEBUG_WX("PRIV GET MODE: %s\n", extra); + + wrqu->data.length = strlen(extra) + 1; + + return 0; +} + +#ifdef CONFIG_IPW_PROMISC +static int ipw_wx_set_promisc(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + struct ipw_priv *priv = netdev_priv(dev); + int *parms = (int *)extra; + int enable = (parms[0] > 0); + + IPW_DEBUG_WX("SET PROMISC: %d %d\n", enable, parms[1]); + if (enable) { + if (priv->ieee->iw_mode != IW_MODE_MONITOR) { + priv->net_dev->type = ARPHRD_IEEE80211; + ipw_adapter_restart(priv); + } + + ipw_set_channel(priv, parms[1]); + } else { + if (priv->ieee->iw_mode != IW_MODE_MONITOR) + return 0; + priv->net_dev->type = ARPHRD_ETHER; + ipw_adapter_restart(priv); + } + return 0; +} + + +static int ipw_wx_reset(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + struct ipw_priv *priv = netdev_priv(dev); + IPW_DEBUG_WX("RESET\n"); + ipw_adapter_restart(priv); + return 0; +} +#endif // CONFIG_IPW_PROMISC + +/* 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(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, +}; + +#define IPW_PRIV_SET_POWER SIOCIWFIRSTPRIV +#define IPW_PRIV_GET_POWER SIOCIWFIRSTPRIV+1 +#define IPW_PRIV_SET_MODE SIOCIWFIRSTPRIV+2 +#define IPW_PRIV_GET_MODE SIOCIWFIRSTPRIV+3 +#define IPW_PRIV_SET_PROMISC SIOCIWFIRSTPRIV+4 +#define IPW_PRIV_RESET SIOCIWFIRSTPRIV+5 + + +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" + }, +#ifdef CONFIG_IPW_PROMISC + { + IPW_PRIV_SET_PROMISC, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 2, 0, "monitor" + }, + { + IPW_PRIV_RESET, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 0, 0, "reset" + }, +#endif /* CONFIG_IPW_PROMISC */ +}; + +static iw_handler ipw_priv_handler[] = { + ipw_wx_set_powermode, + ipw_wx_get_powermode, + ipw_wx_set_wireless_mode, + ipw_wx_get_wireless_mode, +#ifdef CONFIG_IPW_PROMISC + ipw_wx_set_promisc, + ipw_wx_reset, +#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 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 = netdev_priv(dev); + struct iw_statistics *wstats; + u32 rssi, quality, missed_beacons, tx_failures;/*, tx_retry;*/ + u32 len = sizeof(u32); + + u32 rate, max_rate, rx_err; + + /* If we don't have a connection the quality and level is 0*/ + if (!(priv->status & STATUS_ASSOCIATED)) + return (struct iw_statistics *) NULL; + + wstats = &priv->wstats; + +/* +quality = (current rate / max band rate) * 0.4 + + 1 - (Rx error / Rx packets) * 0.6 +*/ + rate = ipw_get_current_rate(priv); + max_rate = (priv->assoc_request.ieee_mode == IEEE_B) ? + 11000000 : 54000000; + + ipw_get_ordinal(priv, IPW_ORD_STAT_RX_ERR_CRC, &rx_err, &len); + quality = (rate * 40) / max_rate; + /* TODO: Use a cached copy of the Rx stats that can be periodically + * reset -- otherwise the quality level will turn static */ + if (priv->ieee->stats.rx_packets + rx_err) + quality += 60 - (rx_err * 60) / + (priv->ieee->stats.rx_packets + rx_err); + else + quality += 60; + + wstats->qual.qual = quality; + + ipw_get_ordinal(priv, IPW_ORD_STAT_CURR_RSSI_RAW, &rssi, &len); + wstats->qual.level = priv->last_rx_rssi; /*rssi + IPW_RSSI_TO_DBM;*/ + wstats->qual.updated = IW_QUAL_QUAL_UPDATED|IW_QUAL_LEVEL_UPDATED; + + /* current fw does not support noise statistics */ + wstats->qual.updated |= IW_QUAL_NOISE_INVALID; + + if (ipw_get_ordinal(priv, IPW_ORD_STAT_MISSED_BEACONS, + &missed_beacons, &len)) + goto fail_get_ordinal; + + wstats->miss.beacon = missed_beacons; + + if (ipw_get_ordinal(priv, IPW_ORD_STAT_TX_FAILURE, + &tx_failures, &len)) + goto fail_get_ordinal; + wstats->discard.retries = tx_failures; + +/* if (ipw_get_ordinal(priv, IPW_ORD_STAT_TX_RETRY, &tx_retry, &len)) + goto fail_get_ordinal; + wstats->discard.retries += tx_retry; */ + + return wstats; + + fail_get_ordinal: + IPW_DEBUG_WX("failed querying ordinals.\n"); + + return NULL; +} + + +/* net device stuff */ + +static inline void init_sys_config(struct ipw_sys_config *sys_config) +{ + memset(sys_config, 0, sizeof(struct ipw_sys_config)); + sys_config->bt_coexistence = 1; /* We may need to look into prvStaBtConfig */ + 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; + sys_config->antenna_diversity = CFG_SYS_ANTENNA_BOTH; + 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; +} + +static int ipw_net_open(struct net_device *dev) +{ + struct ipw_priv *priv = netdev_priv(dev); + IPW_DEBUG_INFO("dev->open\n"); + /* we should be verifying the device is ready to be opened */ + if (!(priv->status & STATUS_RF_KILL_MASK) && + (priv->status & STATUS_ASSOCIATED)) + 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; +} + +static int ipw_net_hard_start_xmit(struct sk_buff *skb, + struct net_device *dev) +{ + struct ipw_priv *priv = netdev_priv(dev); + unsigned long flags; + int err; + + IPW_DEBUG_TX("dev->xmit(%d bytes)\n", skb->len); + + spin_lock_irqsave(&priv->lock, flags); + + if (!(priv->status & STATUS_ASSOCIATED)) { + IPW_DEBUG_INFO("Tx attempt while not associated.\n"); + priv->ieee->stats.tx_carrier_errors++; + netif_stop_queue(dev); + goto fail_unlock; + } + + err = ipw_tx_skb(priv, skb); + if (err) { + priv->ieee->stats.tx_errors++; + goto fail_unlock; + } + + spin_unlock_irqrestore(&priv->lock, flags); + return 0; + + fail_unlock: + spin_unlock_irqrestore(&priv->lock, flags); + return 1; +} + +static struct net_device_stats *ipw_net_get_stats(struct net_device *dev) +{ + struct ipw_priv *priv = netdev_priv(dev); + + 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 = netdev_priv(dev); + struct sockaddr *addr = p; + if (!is_valid_ether_addr(addr->sa_data)) + return -EADDRNOTAVAIL; + priv->config |= CFG_CUSTOM_MAC; + memcpy(priv->mac_addr, addr->sa_data, ETH_ALEN); + printk(KERN_INFO "%s: Setting MAC to " MAC_FMT "\n", + priv->net_dev->name, MAC_ARG(priv->mac_addr)); + ipw_adapter_restart(priv); + return 0; +} + +static void ipw_ethtool_get_drvinfo(struct net_device *dev, + struct ethtool_drvinfo *info) +{ + struct ipw_priv *p = netdev_priv(dev); + char vers[64]; + char date[32]; + u32 len; + + strcpy(info->driver, DRV_NAME); + strcpy(info->version, DRV_VERSION); + + ipw_get_ordinal(p, IPW_ORD_STAT_FW_VERSION, vers, &len); + 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 = CX2_EEPROM_IMAGE_SIZE; +} + +static u32 ipw_ethtool_get_link(struct net_device *dev) +{ + struct ipw_priv *priv = dev->priv; + return (priv->status & STATUS_ASSOCIATED) != 0; +} + +static int ipw_ethtool_get_eeprom_len(struct net_device *dev) +{ + return CX2_EEPROM_IMAGE_SIZE; +} + +static int ipw_ethtool_get_eeprom(struct net_device *dev, + struct ethtool_eeprom *eeprom, u8 *bytes) +{ + struct ipw_priv *p = netdev_priv(dev); + + if (eeprom->offset + eeprom->len > CX2_EEPROM_IMAGE_SIZE) + return -EINVAL; + + memcpy(bytes, &((u8 *)p->eeprom)[eeprom->offset], eeprom->len); + return 0; +} + +static int ipw_ethtool_set_eeprom(struct net_device *dev, + struct ethtool_eeprom *eeprom, u8 *bytes) +{ + struct ipw_priv *p = netdev_priv(dev); + int i; + + if (eeprom->offset + eeprom->len > CX2_EEPROM_IMAGE_SIZE) + return -EINVAL; + + memcpy(&((u8 *)p->eeprom)[eeprom->offset], bytes, eeprom->len); + for (i = IPW_EEPROM_DATA; + i < IPW_EEPROM_DATA + CX2_EEPROM_IMAGE_SIZE; + i++) + ipw_write8(p, i, p->eeprom[i]); + + return 0; +} + +static 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 pt_regs *regs) +{ + struct ipw_priv *priv = data; + u32 inta, inta_mask; + + if (!priv) + return IRQ_NONE; + + spin_lock(&priv->lock); + + if (!(priv->status & STATUS_INT_ENABLED)) { + /* Shared IRQ */ + goto none; + } + + inta = ipw_read32(priv, CX2_INTA_RW); + inta_mask = ipw_read32(priv, CX2_INTA_MASK_R); + + if (inta == 0xFFFFFFFF) { + /* Hardware disappeared */ + IPW_WARNING("IRQ INTA == 0xFFFFFFFF\n"); + goto none; + } + + if (!(inta & (CX2_INTA_MASK_ALL & inta_mask))) { + /* Shared interrupt */ + goto none; + } + + /* tell the device to stop sending interrupts */ + ipw_disable_interrupts(priv); + + /* ack current interrupts */ + inta &= (CX2_INTA_MASK_ALL & inta_mask); + ipw_write32(priv, CX2_INTA_RW, inta); + + /* Cache INTA value for our tasklet */ + priv->isr_inta = inta; + + tasklet_schedule(&priv->irq_tasklet); + + spin_unlock(&priv->lock); + + return IRQ_HANDLED; + none: + spin_unlock(&priv->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"); + ipw_adapter_restart(priv); + } else + IPW_DEBUG_RF_KILL("HW RF Kill deactivated. SW RF Kill still " + "enabled\n"); + + exit_unlock: + spin_unlock_irqrestore(&priv->lock, flags); +} + +static int ipw_setup_deferred_work(struct ipw_priv *priv) +{ + int ret = 0; + +#ifdef CONFIG_SOFTWARE_SUSPEND2 + priv->workqueue = create_workqueue(DRV_NAME, 0); +#else + priv->workqueue = create_workqueue(DRV_NAME); +#endif + init_waitqueue_head(&priv->wait_command_queue); + + INIT_WORK(&priv->associate, ipw_associate, priv); + INIT_WORK(&priv->disassociate, ipw_disassociate, priv); + INIT_WORK(&priv->rx_replenish, ipw_rx_queue_replenish, priv); + INIT_WORK(&priv->adapter_restart, ipw_adapter_restart, priv); + INIT_WORK(&priv->rf_kill, ipw_rf_kill, priv); + INIT_WORK(&priv->up, (void (*)(void *))ipw_up, priv); + INIT_WORK(&priv->down, (void (*)(void *))ipw_down, priv); + INIT_WORK(&priv->request_scan, + (void (*)(void *))ipw_request_scan, priv); + + tasklet_init(&priv->irq_tasklet, (void (*)(unsigned long)) + ipw_irq_tasklet, (unsigned long)priv); + + return ret; +} + + +static void shim__set_security(struct ieee80211_device *ieee, + struct ieee80211_security *sec) +{ + struct ipw_priv *priv = ieee->priv; + int i; + + for (i = 0; i < 4; i++) { + if (sec->flags & (1 << i)) { + priv->sec.key_sizes[i] = sec->key_sizes[i]; + if (sec->key_sizes[i] == 0) + priv->sec.flags &= ~(1 << i); + else + memcpy(priv->sec.keys[i], sec->keys[i], + sec->key_sizes[i]); + priv->sec.flags |= (1 << i); + priv->status |= STATUS_SECURITY_UPDATED; + } + } + + if ((sec->flags & SEC_ACTIVE_KEY) && + priv->sec.active_key != sec->active_key) { + if (sec->active_key <= 3) { + priv->sec.active_key = sec->active_key; + priv->sec.flags |= SEC_ACTIVE_KEY; + } else + priv->sec.flags &= ~SEC_ACTIVE_KEY; + priv->status |= STATUS_SECURITY_UPDATED; + } + + if ((sec->flags & SEC_AUTH_MODE) && + (priv->sec.auth_mode != sec->auth_mode)) { + priv->sec.auth_mode = sec->auth_mode; + priv->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->sec.enabled != sec->enabled) { + priv->sec.flags |= SEC_ENABLED; + priv->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_LEVEL && + priv->sec.level != sec->level) { + priv->sec.level = sec->level; + priv->sec.flags |= SEC_LEVEL; + priv->status |= STATUS_SECURITY_UPDATED; + } + + /* If the privacy state has switched from what we are + * associated/ing with, then disassociate */ + if ((priv->status & (STATUS_ASSOCIATED | STATUS_ASSOCIATING)) && + (((priv->assoc_request.capability & + WLAN_CAPABILITY_PRIVACY) && !sec->enabled) || + (!((priv->assoc_request.capability & + WLAN_CAPABILITY_PRIVACY) && sec->enabled)))) { + IPW_DEBUG_ASSOC("Disassociating due to capability " + "change.\n"); + ipw_disassociate(priv); + } +} + +static struct ieee80211_helper_functions ipw_ieee_callbacks = { + .set_security = shim__set_security, + .card_present = NULL, + .cor_sreset = NULL, + .dev_open = NULL, + .dev_close = NULL, + .genesis_reset = NULL, + .set_unencrypted_filter = NULL, + .hw_enable = NULL, + .hw_config = NULL, + .hw_reset = NULL, + .hw_shutdown = NULL, + .reset_port = NULL, + .tx = NULL, + .schedule_reset = NULL, + .tx_80211 = NULL +}; + +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 = IEEE_A; + 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 = IEEE_G; + 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) +{ + int i; + struct ipw_tx_power tx_power; + + memset(&priv->sys_config, 0, sizeof(priv->sys_config)); + memset(&tx_power, 0, sizeof(tx_power)); + + /* 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 */ + + /* configure device for 'G' band */ + tx_power.ieee_mode = IEEE_G; + tx_power.num_channels = 11; + for (i = 0; i < 11; i++) { + tx_power.channels_tx_power[i].channel_number = i + 1; + tx_power.channels_tx_power[i].tx_power = priv->tx_power; + } + if (ipw_send_tx_power(priv, &tx_power)) + goto error; + + /* configure device to also handle 'B' band */ + tx_power.ieee_mode = IEEE_B; + if (ipw_send_tx_power(priv, &tx_power)) + 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); + if (ipw_send_system_config(priv, &priv->sys_config)) + 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; + } + + if (ipw_set_random_seed(priv)) + goto error; + + /* final state transition to the RUN state */ + if (ipw_send_host_complete(priv)) + goto error; + + /* kick off an active scan */ + if (ipw_request_scan(priv)) + goto error; + + return 0; + + error: + return -EIO; +} + +#define MAX_HW_RESTARTS 5 +static int ipw_up(struct ipw_priv *priv) +{ + int rc, i; + + if (priv->status & STATUS_EXIT_PENDING) + return -EIO; + + 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: 0x%08X\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); + + if (priv->status & STATUS_RF_KILL_MASK) + return 0; + + rc = ipw_config(priv); + if (!rc) { + IPW_DEBUG_INFO("Configured device on count %i\n", i); + + netif_start_queue(priv->net_dev); + return 0; + } else { + 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_down(struct ipw_priv *priv) +{ + /* tell the device to stop sending interrupts */ + ipw_disable_interrupts(priv); + + /* Clear all bits but the RF Kill */ + priv->status &= STATUS_RF_KILL_MASK; + + netif_carrier_off(priv->net_dev); + netif_stop_queue(priv->net_dev); + + ipw_stop_nic(priv); +} + +/* Called by register_netdev() */ +static int ipw_net_init(struct net_device *dev) +{ + struct ipw_priv *priv = netdev_priv(dev); + + 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; + } + + if (ipw_up(priv)) + return -EIO; + + 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, 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_dump_errors.attr, + &dev_attr_dump_events.attr, + &dev_attr_eeprom_delay.attr, + &dev_attr_ucode_version.attr, + &dev_attr_rtc.attr, + NULL +}; + +static struct attribute_group ipw_attribute_group = { + .name = NULL, /* put in device directory */ + .attrs = ipw_sysfs_entries, +}; + +static int ipw_pci_probe(struct pci_dev *pdev, + const struct pci_device_id *ent) +{ + int err = 0; + struct net_device *net_dev; + void *base; + u32 length, val; + struct ipw_priv *priv; + int band, modulation; + + net_dev = alloc_etherdev(sizeof(struct ipw_priv)); + if (net_dev == NULL) { + err = -ENOMEM; + goto out; + } + + priv = netdev_priv(net_dev); + priv->net_dev = net_dev; + priv->pci_dev = pdev; +#ifdef CONFIG_IPW_DEBUG + ipw_debug_level = debug; +#endif + spin_lock_init(&priv->lock); + + if (pci_enable_device(pdev)) { + err = -ENODEV; + goto out_free_netdev; + } + + pci_set_master(pdev); + 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 = (unsigned long)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; + } + + /* Initialize WEP */ + priv->ieee = ieee80211_alloc(priv->net_dev, priv); + if (!priv->ieee) { + IPW_ERROR("Unable to allocate ieee80211 data\n"); + err = -ENOMEM; + goto out_destroy_workqueue; + } + + /* Initialize module parameter values here */ + if (ifname) + strncpy(net_dev->name, ifname, IFNAMSIZ); + + if (associate) + priv->config |= CFG_ASSOCIATE; + else + IPW_DEBUG_INFO("Auto associate disabled.\n"); + + if (adhoc_create) + priv->config |= CFG_ADHOC_CREATE; + else + IPW_DEBUG_INFO("Auto adhoc creation disabled.\n"); + + if (disable) { + 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); + IPW_DEBUG_INFO("Bind to static channel %d\n", channel); + /* TODO: Validate that provided channel is in range */ + } + + switch (mode) { + case 1: + priv->ieee->iw_mode = IW_MODE_ADHOC; + break; +#ifdef CONFIG_IPW_PROMISC + case 2: + priv->ieee->iw_mode = IW_MODE_MONITOR; + break; +#endif + default: + case 0: + priv->ieee->iw_mode = IW_MODE_INFRA; + break; + } + + if ((priv->pci_dev->device == 0x4223) || + (priv->pci_dev->device == 0x4224)) { + printk(KERN_INFO DRV_NAME + ": Detected Intel PRO/Wireless 2915ABG Network " + "Connection\n"); + priv->ieee->abg_ture = 1; + band = IEEE80211_52GHZ_BAND | IEEE80211_24GHZ_BAND; + modulation = IEEE80211_OFDM_MODULATION | + IEEE80211_CCK_MODULATION; + priv->adapter = IPW_2915ABG; + priv->ieee->mode = IEEE_A; + } else { + printk(KERN_INFO DRV_NAME + ": Detected Intel PRO/Wireless 2200BG Network " + "Connection\n"); + priv->ieee->abg_ture = 0; + band = IEEE80211_24GHZ_BAND; + modulation = IEEE80211_OFDM_MODULATION | + IEEE80211_CCK_MODULATION; + priv->adapter = IPW_2200BG; + priv->ieee->mode = IEEE_G; + } + + priv->ieee->freq_band = band; + priv->ieee->modulation = modulation; + + priv->rates_mask = IEEE80211_DEFAULT_RATES_MASK; + + priv->missed_beacon_threshold = IPW_MB_DISASSOCIATE_THRESHOLD_DEFAULT; + priv->roaming_threshold = IPW_MB_ROAMING_THRESHOLD_DEFAULT; + + priv->ieee->func = &ipw_ieee_callbacks; + priv->ieee->tx_payload_only = 1; + + priv->rts_threshold = DEFAULT_RTS_THRESHOLD; + + /* If power management is turned on, default to AC mode */ + priv->power_mode = IPW_POWER_AC; + priv->tx_power = IPW_DEFAULT_TX_POWER; + + err = request_irq(pdev->irq, ipw_isr, SA_SHIRQ, DRV_NAME, + priv); + if (err) { + IPW_ERROR("Error allocating IRQ %d\n", pdev->irq); + goto out_free_ieee80211; + } + + SET_MODULE_OWNER(net_dev); + SET_NETDEV_DEV(net_dev, &pdev->dev); + + net_dev->open = ipw_net_open; + net_dev->stop = ipw_net_stop; + net_dev->init = ipw_net_init; + net_dev->hard_start_xmit = ipw_net_hard_start_xmit; + 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; + net_dev->get_wireless_stats = ipw_get_wireless_stats; + net_dev->wireless_handlers = &ipw_wx_handler_def; + net_dev->ethtool_ops = &ipw_ethtool_ops; + net_dev->irq = pdev->irq; + net_dev->base_addr = 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"); + goto out_release_irq; + } + + err = register_netdev(net_dev); + if (err) { + IPW_ERROR("failed to register network device\n"); + goto out_remove_group; + } + + return 0; + + out_remove_group: + sysfs_remove_group(&pdev->dev.kobj, &ipw_attribute_group); + out_release_irq: + free_irq(pdev->irq, priv); + out_free_ieee80211: + ieee80211_free(priv->ieee); + priv->ieee = NULL; + out_destroy_workqueue: + destroy_workqueue(priv->workqueue); + priv->workqueue = NULL; + out_iounmap: + iounmap((void *)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_netdev: + free_netdev(priv->net_dev); + out: + return err; +} + +static void ipw_pci_remove(struct pci_dev *pdev) +{ + struct ipw_priv *priv = pci_get_drvdata(pdev); + if (!priv) + return; + + priv->status |= STATUS_EXIT_PENDING; + + sysfs_remove_group(&pdev->dev.kobj, &ipw_attribute_group); + + ipw_down(priv); + + unregister_netdev(priv->net_dev); + + if (priv->rxq) { + ipw_rx_queue_free(priv, priv->rxq); + priv->rxq = NULL; + } + ipw_tx_queue_free(priv); + ieee80211_free(priv->ieee); + + /* ipw_down will ensure that there is no more pending work + * in the workqueue's, so we can safely remove them now. */ + if (priv->workqueue) { + cancel_delayed_work(&priv->request_scan); + cancel_delayed_work(&priv->rf_kill); + destroy_workqueue(priv->workqueue); + priv->workqueue = NULL; + } + + free_irq(pdev->irq, priv); + iounmap((void *)priv->hw_base); + pci_release_regions(pdev); + pci_disable_device(pdev); + pci_set_drvdata(pdev, NULL); + free_netdev(priv->net_dev); + +#ifdef CONFIG_PM + if (fw_loaded) { + release_firmware(bootfw); + release_firmware(ucode); + release_firmware(firmware); + fw_loaded = 0; + } +#endif +} + + +#ifdef CONFIG_PM +static int ipw_pci_suspend(struct pci_dev *pdev, u32 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); + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,10) + pci_save_state(pdev, priv->pm_state); +#else + pci_save_state(pdev); +#endif + pci_disable_device(pdev); // needed? + pci_set_power_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; + u32 val; + + printk(KERN_INFO "%s: Coming out of suspend...\n", dev->name); + + pci_set_power_state(pdev, 0); +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,10) + pci_restore_state(pdev, priv->pm_state); +#else + pci_restore_state(pdev); +#endif + /* + * 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 */ + ipw_up(priv); + + return 0; +} +#endif + +/* 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 +}; + +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_module_init(&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(adhoc_create, int, 0444); +MODULE_PARM_DESC(adhoc_create, "auto create adhoc network (default on)"); + +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])"); + +module_param(ifname, charp, 0444); +MODULE_PARM_DESC(ifname, "network device name (default eth%d)"); + +module_param(mode, int, 0444); +MODULE_PARM_DESC(mode, "network mode (0=BSS,1=IBSS,2=Monitor)"); + +module_exit(ipw_exit); +module_init(ipw_init); diff --git a/drivers/net/wireless/ipw2200/ipw2200.h b/drivers/net/wireless/ipw2200/ipw2200.h new file mode 100644 index 000000000..78234efea --- /dev/null +++ b/drivers/net/wireless/ipw2200/ipw2200.h @@ -0,0 +1,1722 @@ +/****************************************************************************** + + Copyright(c) 2003 - 2004 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 + Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 + +******************************************************************************/ + +#ifndef __ipw2200_h__ +#define __ipw2200_h__ + +#define WEXT_USECHANNELS 1 + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "ieee80211.h" + +#define DRV_NAME "ipw2200" + +#include + +#ifndef IRQ_NONE +typedef void irqreturn_t; +#define IRQ_NONE +#define IRQ_HANDLED +#define IRQ_RETVAL(x) +#endif + +#if ( LINUX_VERSION_CODE < KERNEL_VERSION(2,6,5) ) +#define pci_dma_sync_single_for_cpu pci_dma_sync_single +#define pci_dma_sync_single_for_device pci_dma_sync_single +#endif + +#ifndef HAVE_FREE_NETDEV +#define free_netdev(x) kfree(x) +#endif + +#if WIRELESS_EXT < 17 +#define IW_QUAL_QUAL_UPDATED 0x1 +#define IW_QUAL_LEVEL_UPDATED 0x2 +#define IW_QUAL_NOISE_INVALID 0x40 +#endif + +/* 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_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 +/* + * TX Queue Flag Definitions + */ + +/* 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_SHORT_PREMBL 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 +#define IPW_ +/* overwrite TSF field */ +#define DCT_FLAG_TSF_REQD 0x40 + +/* ACK rx is expected to follow */ +#define DCT_FLAG_ACK_REQD 0x80 + +#define DCT_FLAG_EXT_MODE_CCK 0x01 +#define DCT_FLAG_EXT_MODE_OFDM 0x00 + + +#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_DISASSOCIATE_THRESHOLD_DEFAULT 24 +#define IPW_MB_ROAMING_THRESHOLD_DEFAULT 8 +#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 + +/** + * 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)); + +struct machdr32 +{ + u8 ctrl1; + u8 ctrl2; + u16 duration; // watch out for endians! + u8 addr1[ MACADRR_BYTE_LEN ]; + u8 addr2[ MACADRR_BYTE_LEN ]; + u8 addr3[ MACADRR_BYTE_LEN ]; + u16 seq_ctrl; // more endians! + u8 addr4[ MACADRR_BYTE_LEN ]; + u16 qos_ctrl; +} __attribute__ ((packed)) ; + +struct machdr30 +{ + u8 ctrl1; + u8 ctrl2; + u16 duration; // watch out for endians! + u8 addr1[ MACADRR_BYTE_LEN ]; + u8 addr2[ MACADRR_BYTE_LEN ]; + u8 addr3[ MACADRR_BYTE_LEN ]; + u16 seq_ctrl; // more endians! + u8 addr4[ MACADRR_BYTE_LEN ]; +} __attribute__ ((packed)) ; + +struct machdr26 +{ + u8 ctrl1; + u8 ctrl2; + u16 duration; // watch out for endians! + u8 addr1[ MACADRR_BYTE_LEN ]; + u8 addr2[ MACADRR_BYTE_LEN ]; + u8 addr3[ MACADRR_BYTE_LEN ]; + u16 seq_ctrl; // more endians! + u16 qos_ctrl; +} __attribute__ ((packed)) ; + +struct machdr24 +{ + u8 ctrl1; + u8 ctrl2; + u16 duration; // watch out for endians! + u8 addr1[ MACADRR_BYTE_LEN ]; + u8 addr2[ MACADRR_BYTE_LEN ]; + u8 addr3[ MACADRR_BYTE_LEN ]; + u16 seq_ctrl; // more endians! +} __attribute__ ((packed)) ; + +// TX TFD with 32 byte MAC Header +struct tx_tfd_32 +{ + struct machdr32 mchdr; // 32 + u32 uivplaceholder[2]; // 8 +} __attribute__ ((packed)) ; + +// TX TFD with 30 byte MAC Header +struct tx_tfd_30 +{ + struct machdr30 mchdr; // 30 + u8 reserved[2]; // 2 + u32 uivplaceholder[2]; // 8 +} __attribute__ ((packed)) ; + +// tx tfd with 26 byte mac header +struct tx_tfd_26 +{ + struct machdr26 mchdr; // 26 + u8 reserved1[2]; // 2 + u32 uivplaceholder[2]; // 8 + u8 reserved2[4]; // 4 +} __attribute__ ((packed)) ; + +// tx tfd with 24 byte mac header +struct tx_tfd_24 +{ + struct machdr24 mchdr; // 24 + u32 uivplaceholder[2]; // 8 + u8 reserved[8]; // 8 +} __attribute__ ((packed)) ; + + +#define DCT_WEP_KEY_FIELD_LENGTH 16 + +struct tfd_command +{ + u8 index; + u8 length; + u16 reserved; + u8 payload[0]; +} __attribute__ ((packed)) ; + +struct tfd_data { + /* Header */ + u32 work_area_ptr; + u8 station_number; /* 0 for BSS */ + u8 reserved1; + u16 reserved2; + + /* Tx Parameters */ + u8 cmd_id; + u8 seq_num; + u16 len; + u8 priority; + u8 tx_flags; + u8 tx_flags_ext; + u8 key_index; + u8 wepkey[DCT_WEP_KEY_FIELD_LENGTH]; + u8 rate; + u8 antenna; + u16 next_packet_duration; + u16 next_frag_len; + u16 back_off_counter; //////txop; + u8 retrylimit; + u16 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 */ + u32 num_chunks; + u32 chunk_ptr[NUM_TFD_CHUNKS]; + u16 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_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 { + u32 b[SUP_RATE_11B_MAX_NUM_CHANNELS]; + u32 g[SUP_RATE_11G_MAX_NUM_CHANNELS]; + } success; + union { + u32 b[SUP_RATE_11B_MAX_NUM_CHANNELS]; + u32 g[SUP_RATE_11G_MAX_NUM_CHANNELS]; + } failed; +} __attribute__ ((packed)); + +/* statistics command response */ +struct ipw_cmd_stats { + u8 cmd_id; + u8 seq_num; + u16 good_sfd; + u16 bad_plcp; + u16 wrong_bssid; + u16 valid_mpdu; + u16 bad_mac_header; + u16 reserved_frame_types; + u16 rx_ina; + u16 bad_crc32; + u16 invalid_cts; + u16 invalid_acks; + u16 long_distance_ina_fina; + u16 dsp_silence_unreachable; + u16 accumulated_rssi; + u16 rx_ovfl_frame_tossed; + u16 rssi_silence_threshold; + u16 rx_ovfl_frame_supplied; + u16 last_rx_frame_signal; + u16 last_rx_frame_noise; + u16 rx_autodetec_no_ofdm; + u16 rx_autodetec_no_barker; + u16 reserved; +} __attribute__ ((packed)); + +struct notif_channel_result { + u8 channel_num; + struct ipw_cmd_stats stats; + u8 uReserved; +} __attribute__ ((packed)); + +struct notif_scan_complete { + u8 scan_type; + u8 num_channels; + u8 status; + u8 reserved; +} __attribute__ ((packed)); + +struct notif_frag_length { + u16 frag_length; + u16 reserved; +} __attribute__ ((packed)); + +struct notif_beacon_state { + u32 state; + u32 number; +} __attribute__ ((packed)); + +struct notif_tgi_tx_key { + u8 key_state; + u8 security_type; + u8 station_index; + u8 reserved; +} __attribute__ ((packed)); + +struct notif_link_deterioration { + struct ipw_cmd_stats stats; + u8 rate; + u8 modulation; + struct rate_histogram histogram; + u8 reserved1; + u16 reserved2; +} __attribute__ ((packed)); + +struct notif_association { + u8 state; +} __attribute__ ((packed)); + +struct notif_authenticate { + u8 state; + struct machdr24 addr; + u16 status; +} __attribute__ ((packed)); + +struct temperature +{ + s32 measured; + s32 active; +} __attribute__ ((packed)); + +struct notif_calibration { + u8 data[104]; +} __attribute__ ((packed)); + +struct ipw_rx_notification { + u8 reserved[8]; + u8 subtype; + u8 flags; + u16 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; + u8 raw[0]; + } u; +} __attribute__ ((packed)); + +struct ipw_rx_frame { + u32 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 reserved2; + u16 signal; + u16 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 + u16 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 sizeof(struct ipw_rx_header) + \ + sizeof(struct ipw_rx_frame) + +struct ipw_rx_mem_buffer { + dma_addr_t dma_addr; + struct ipw_rx_buffer *rxb; + 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 */ +}; /* Not transferred over network, so not __attribute__ ((packed)) */ + +struct alive_command_responce { + u8 alive_command; + u8 sequence_number; + u16 software_revision; + u8 device_identifier; + u8 reserved1[5]; + u16 reserved2; + u16 reserved3; + u16 clock_settle_time; + u16 powerup_settle_time; + u16 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; + void *source_addr; + void *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 reserved2; + u8 accept_all_mgmt_bcpr; + u8 accept_all_mgtm_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)); + +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]; + u32 tx_counter[2]; +} __attribute__ ((packed)); + +#define IPW_SCAN_CHANNELS 54 + +struct ipw_scan_request +{ + u8 scan_type; + u16 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 +{ + u32 full_scan_index; + u8 channels_list[IPW_SCAN_CHANNELS]; + u8 scan_type[IPW_SCAN_CHANNELS / 2]; + u8 reserved; + u16 dwell_time[IPW_SCAN_TYPES]; +} __attribute__ ((packed)); + +extern 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; +} + +extern 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; + u8 auth_type:4, + auth_key:4; + u8 assoc_type; + u8 reserved; + u16 policy_support; + u8 preamble_length; + u8 ieee_mode; + u8 bssid[ETH_ALEN]; + u32 assoc_tsf_msw; + u32 assoc_tsf_lsw; + u16 capability; + u16 listen_interval; + u16 beacon_interval; + u8 dest[ETH_ALEN]; + u16 atim_window; + u8 smr; + u8 reserved1; + u16 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 +{ + u16 rts_threshold; + u16 reserved; +} __attribute__ ((packed)); + +struct ipw_frag_threshold +{ + u16 frag_threshold; + u16 reserved; +} __attribute__ ((packed)); + +struct ipw_retry_limit +{ + u8 short_retry_limit; + u8 long_retry_limit; + u16 reserved; +} __attribute__ ((packed)); + +struct ipw_dino_config +{ + u32 dino_config_addr; + u16 dino_config_size; + u8 dino_response; + u8 reserved; +} __attribute__ ((packed)); + +struct ipw_aironet_info +{ + u8 id; + u8 length; + u16 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_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_qos_parameters +{ + u16 cw_min[4]; + u16 cw_max[4]; + u8 aifs[4]; + u8 flag[4]; + u16 tx_op_limit[4]; +} __attribute__ ((packed)); + +struct ipw_rsn_capabilities +{ + u8 id; + u8 length; + u16 version; +} __attribute__ ((packed)); + +struct ipw_sensitivity_calib +{ + u16 beacon_rssi_raw; + u16 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 { + 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_FW_DOWNLOAD BIT(0) /**< fw download in progress */ +#define STATUS_HCMD_ACTIVE BIT(1) /**< host command in progress */ +#define STATUS_HCMD_DONE BIT(2) /**< host command reply received */ +#define STATUS_HCMD_TIMEOUT BIT(3) /**< host command timed out */ +#define STATUS_FW_READY BIT(4) /**< FW is ready (got INIT_DONE IRQ ) */ +#define STATUS_HOST_COMPLETE BIT(5) /**< Ready to Tx/Rx (HostComplete) */ +#define STATUS_WEP BIT(7) /**< use WEP */ +#define STATUS_ERROR BIT(8) /**< Error state. Needs restart. */ +#define STATUS_SNIF_DINO BIT(9) /**< Pass DINO header to sniffer */ + +#define STATUS_INT_ENABLED BIT(11) +#define STATUS_RF_KILL_HW BIT(12) +#define STATUS_RF_KILL_SW BIT(13) +#define STATUS_RF_KILL_MASK (STATUS_RF_KILL_HW | STATUS_RF_KILL_SW) +#define STATUS_EXIT_PENDING BIT(14) + +#define STATUS_SCAN_PENDING BIT(20) +#define STATUS_SCANNING BIT(21) +#define STATUS_SCAN_ABORTING BIT(22) +#define STATUS_AUTH BIT(23) /**< Authenticated */ +#define STATUS_ASSOCIATING BIT(24) +#define STATUS_ASSOCIATED BIT(25) /**< Associated */ +#define STATUS_DISASSOCIATING BIT(26) + +#define STATUS_INDIRECT_BYTE BIT(27) /* sysfs entry configured for access */ +#define STATUS_INDIRECT_DWORD BIT(28) /* sysfs entry configured for access */ +#define STATUS_DIRECT_DWORD BIT(29) /* sysfs entry configured for access */ + +#define STATUS_SECURITY_UPDATED BIT(30) /* Security sync needed */ + +#define CFG_STATIC_CHANNEL BIT(0) /* Restrict assoc. to single channel */ +#define CFG_STATIC_ESSID BIT(1) /* Restrict assoc. to single SSID */ +#define CFG_STATIC_BSSID BIT(2) /* Restrict assoc. to single BSSID */ +#define CFG_CUSTOM_MAC BIT(3) +#define CFG_PREAMBLE BIT(4) +/* free bit */ +#define CFG_ASSOCIATE BIT(6) +#define CFG_FIXED_RATE BIT(7) +#define CFG_ADHOC_CREATE BIT(8) + +#define CAP_SHARED_KEY BIT(0) /* Off = OPEN */ +#define CAP_PRIVACY_ON BIT(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; +}; + +struct ipw_priv { + /* ieee device used by generic ieee processing code */ + struct ieee80211_device *ieee; + struct ieee80211_security sec; + + /* spinlock */ + spinlock_t lock; + + /* basic pci-network driver stuff */ + struct pci_dev *pci_dev; + struct net_device *net_dev; + + /* pci harware address support */ + unsigned long hw_base; /* (virt) */ + 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; + + /* Rx and Tx DMA processing queues */ + struct ipw_rx_queue *rxq; + struct clx2_tx_queue txq_cmd; + struct clx2_tx_queue txq[4]; + unsigned long status; + unsigned long config; + unsigned long capability; + + u8 last_rx_rate; + u8 last_rx_rssi; + 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 missed_beacon_threshold; + u32 roaming_threshold; + + struct ipw_associate assoc_request; + + 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]; + + + /* Statistics and counters reset with each association */ + u32 missed_beacons; + u32 tx_packets; + + /* eeprom */ + u8 eeprom[0x100]; /* 256 bytes of eeprom */ + int eeprom_delay; + + struct iw_statistics wstats; + + struct workqueue_struct *workqueue; + + struct work_struct associate; + struct work_struct disassociate; + struct work_struct rx_replenish; + struct work_struct request_scan; + struct work_struct adapter_restart; + struct work_struct rf_kill; + struct work_struct up; + struct work_struct down; + struct tasklet_struct irq_tasklet; + + +#define IPW_2200BG 1 +#define IPW_2915ABG 2 + u8 adapter; + +#define IPW_DEFAULT_TX_POWER 0x14 + u8 tx_power; + +#ifdef CONFIG_PM + u32 pm_state[16]; +#endif + + /* network state */ + + /* Used to pass the current INTA value from ISR to Tasklet */ + u32 isr_inta; + + /* debugging info */ + u32 indirect_dword; + u32 direct_dword; + u32 indirect_byte; +}; /*ipw_priv */ + + +/* debug macros */ + +#ifdef CONFIG_IPW_DEBUG +#define IPW_DEBUG(level, fmt, args...) \ +do { if (ipw_debug_level & (level)) \ + printk(KERN_DEBUG DRV_NAME": %c %s " fmt, \ + in_interrupt() ? 'I' : 'U', __FUNCTION__, ## args); } while (0) +#else +#define IPW_DEBUG(level, fmt, args...) do {} while (0) +#endif /* CONFIG_IPW_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_IPW_DEBUG defined in your kernel configuration + * + */ + +#define IPW_DL_ERROR BIT(0) +#define IPW_DL_WARNING BIT(1) +#define IPW_DL_INFO BIT(2) +#define IPW_DL_WX BIT(3) +#define IPW_DL_HOST_COMMAND BIT(5) +#define IPW_DL_STATE BIT(6) + +#define IPW_DL_NOTIF BIT(10) +#define IPW_DL_SCAN BIT(11) +#define IPW_DL_ASSOC BIT(12) +#define IPW_DL_DROP BIT(13) +#define IPW_DL_IOCTL BIT(14) + +#define IPW_DL_MANAGE BIT(15) +#define IPW_DL_FW BIT(16) +#define IPW_DL_RF_KILL BIT(17) + + +#define IPW_DL_ORD BIT(20) + +#define IPW_DL_FRAG BIT(21) +#define IPW_DL_WEP BIT(22) +#define IPW_DL_TX BIT(23) +#define IPW_DL_RX BIT(24) +#define IPW_DL_ISR BIT(25) +#define IPW_DL_FW_INFO BIT(26) +#define IPW_DL_IO BIT(27) +#define IPW_DL_TRACE BIT(28) + + +#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_STATUS(f, a...) IPW_DEBUG(IPW_DL_STATUS, f, ## a) +#define IPW_DEBUG_TRACE(f, a...) IPW_DEBUG(IPW_DL_TRACE, f, ## a) +#define IPW_DEBUG_RX(f, a...) IPW_DEBUG(IPW_DL_RX, f, ## a) +#define IPW_DEBUG_TX(f, a...) IPW_DEBUG(IPW_DL_TX, f, ## a) +#define IPW_DEBUG_ISR(f, a...) IPW_DEBUG(IPW_DL_ISR, f, ## a) +#define IPW_DEBUG_MANAGEMENT(f, a...) IPW_DEBUG(IPW_DL_MANAGE, f, ## a) +#define IPW_DEBUG_WEP(f, a...) IPW_DEBUG(IPW_DL_WEP, f, ## a) +#define IPW_DEBUG_HC(f, a...) IPW_DEBUG(IPW_DL_HOST_COMMAND, f, ## a) +#define IPW_DEBUG_FRAG(f, a...) IPW_DEBUG(IPW_DL_FRAG, f, ## a) +#define IPW_DEBUG_FW(f, a...) IPW_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_DEBUG(IPW_DL_IO, f, ## a) +#define IPW_DEBUG_ORD(f, a...) IPW_DEBUG(IPW_DL_ORD, f, ## a) +#define IPW_DEBUG_FW_INFO(f, a...) IPW_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) +#include + +/* +* Register bit definitions +*/ + +/* Dino control registers bits */ + +#define DINO_ENABLE_SYSTEM 0x80 +#define DINO_ENABLE_CS 0x40 +#define DINO_RXFIFO_DATA 0x01 +#define DINO_CONTROL_REG 0x00200000 + +#define CX2_INTA_RW 0x00000008 +#define CX2_INTA_MASK_R 0x0000000C +#define CX2_INDIRECT_ADDR 0x00000010 +#define CX2_INDIRECT_DATA 0x00000014 +#define CX2_AUTOINC_ADDR 0x00000018 +#define CX2_AUTOINC_DATA 0x0000001C +#define CX2_RESET_REG 0x00000020 +#define CX2_GP_CNTRL_RW 0x00000024 + +#define CX2_READ_INT_REGISTER 0xFF4 + +#define CX2_GP_CNTRL_BIT_INIT_DONE 0x00000004 + +#define CX2_REGISTER_DOMAIN1_END 0x00001000 +#define CX2_SRAM_READ_INT_REGISTER 0x00000ff4 + +#define CX2_SHARED_LOWER_BOUND 0x00000200 +#define CX2_INTERRUPT_AREA_LOWER_BOUND 0x00000f80 + +#define CX2_NIC_SRAM_LOWER_BOUND 0x00000000 +#define CX2_NIC_SRAM_UPPER_BOUND 0x00030000 + +#define CX2_BIT_INT_HOST_SRAM_READ_INT_REGISTER (1 << 29) +#define CX2_GP_CNTRL_BIT_CLOCK_READY 0x00000001 +#define CX2_GP_CNTRL_BIT_HOST_ALLOWS_STANDBY 0x00000002 + +/* + * RESET Register Bit Indexes + */ +#define CBD_RESET_REG_PRINCETON_RESET 0x00000001 /* Bit 0 (LSB) */ +#define CX2_RESET_REG_SW_RESET 0x00000080 /* Bit 7 */ +#define CX2_RESET_REG_MASTER_DISABLED 0x00000100 /* Bit 8 */ +#define CX2_RESET_REG_STOP_MASTER 0x00000200 /* Bit 9 */ +#define CX2_ARC_KESHET_CONFIG 0x08000000 /* Bit 27 */ +#define CX2_START_STANDBY 0x00000004 /* Bit 2 */ + +#define CX2_CSR_CIS_UPPER_BOUND 0x00000200 +#define CX2_DOMAIN_0_END 0x1000 +#define CLX_MEM_BAR_SIZE 0x1000 + +#define CX2_BASEBAND_CONTROL_STATUS 0X00200000 +#define CX2_BASEBAND_TX_FIFO_WRITE 0X00200004 +#define CX2_BASEBAND_RX_FIFO_READ 0X00200004 +#define CX2_BASEBAND_CONTROL_STORE 0X00200010 + +#define CX2_INTERNAL_CMD_EVENT 0X00300004 +#define CX2_BASEBAND_POWER_DOWN 0x00000001 + +#define CX2_MEM_HALT_AND_RESET 0x003000e0 + +/* defgroup bits_halt_reset MEM_HALT_AND_RESET register bits */ +#define CX2_BIT_HALT_RESET_ON 0x80000000 +#define CX2_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 CX2_SHARED_SRAM_SIZE 0x00030000 +#define CX2_SHARED_SRAM_DMA_CONTROL 0x00027000 +#define CB_MAX_LENGTH 0x1FFF + +#define CX2_HOST_EEPROM_DATA_SRAM_SIZE 0xA18 +#define CX2_EEPROM_IMAGE_SIZE 0x100 + + +/* DMA defs */ +#define CX2_DMA_I_CURRENT_CB 0x003000D0 +#define CX2_DMA_O_CURRENT_CB 0x003000D4 +#define CX2_DMA_I_DMA_CONTROL 0x003000A4 +#define CX2_DMA_I_CB_BASE 0x003000A0 + +#define CX2_TX_CMD_QUEUE_BD_BASE (0x00000200) +#define CX2_TX_CMD_QUEUE_BD_SIZE (0x00000204) +#define CX2_TX_QUEUE_0_BD_BASE (0x00000208) +#define CX2_TX_QUEUE_0_BD_SIZE (0x0000020C) +#define CX2_TX_QUEUE_1_BD_BASE (0x00000210) +#define CX2_TX_QUEUE_1_BD_SIZE (0x00000214) +#define CX2_TX_QUEUE_2_BD_BASE (0x00000218) +#define CX2_TX_QUEUE_2_BD_SIZE (0x0000021C) +#define CX2_TX_QUEUE_3_BD_BASE (0x00000220) +#define CX2_TX_QUEUE_3_BD_SIZE (0x00000224) +#define CX2_RX_BD_BASE (0x00000240) +#define CX2_RX_BD_SIZE (0x00000244) +#define CX2_RFDS_TABLE_LOWER (0x00000500) + +#define CX2_TX_CMD_QUEUE_READ_INDEX (0x00000280) +#define CX2_TX_QUEUE_0_READ_INDEX (0x00000284) +#define CX2_TX_QUEUE_1_READ_INDEX (0x00000288) +#define CX2_TX_QUEUE_2_READ_INDEX (0x0000028C) +#define CX2_TX_QUEUE_3_READ_INDEX (0x00000290) +#define CX2_RX_READ_INDEX (0x000002A0) + +#define CX2_TX_CMD_QUEUE_WRITE_INDEX (0x00000F80) +#define CX2_TX_QUEUE_0_WRITE_INDEX (0x00000F84) +#define CX2_TX_QUEUE_1_WRITE_INDEX (0x00000F88) +#define CX2_TX_QUEUE_2_WRITE_INDEX (0x00000F8C) +#define CX2_TX_QUEUE_3_WRITE_INDEX (0x00000F90) +#define CX2_RX_WRITE_INDEX (0x00000FA0) + +/* + * EEPROM Related Definitions + */ + +#define IPW_EEPROM_DATA_SRAM_ADDRESS (CX2_SHARED_LOWER_BOUND + 0x814) +#define IPW_EEPROM_DATA_SRAM_SIZE (CX2_SHARED_LOWER_BOUND + 0x818) +#define IPW_EEPROM_LOAD_DISABLE (CX2_SHARED_LOWER_BOUND + 0x81C) +#define IPW_EEPROM_DATA (CX2_SHARED_LOWER_BOUND + 0x820) +#define IPW_EEPROM_UPPER_ADDRESS (CX2_SHARED_LOWER_BOUND + 0x9E0) + +#define IPW_STATION_TABLE_LOWER (CX2_SHARED_LOWER_BOUND + 0xA0C) +#define IPW_STATION_TABLE_UPPER (CX2_SHARED_LOWER_BOUND + 0xB0C) +#define IPW_REQUEST_ATIM (CX2_SHARED_LOWER_BOUND + 0xB0C) +#define IPW_ATIM_SENT (CX2_SHARED_LOWER_BOUND + 0xB10) +#define IPW_WHO_IS_AWAKE (CX2_SHARED_LOWER_BOUND + 0xB14) +#define IPW_DURING_ATIM_WINDOW (CX2_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_STANDARD 0 +#define EEPROM_NIC_TYPE_DELL 1 +#define EEPROM_NIC_TYPE_FUJITSU 2 +#define EEPROM_NIC_TYPE_IBM 3 +#define EEPROM_NIC_TYPE_HP 4 + +#define FW_MEM_REG_LOWER_BOUND 0x00300000 +#define FW_MEM_REG_EEPROM_ACCESS (FW_MEM_REG_LOWER_BOUND + 0x40) + +#define EEPROM_BIT_SK __BIT(0) +#define EEPROM_BIT_CS __BIT(1) +#define EEPROM_BIT_DI __BIT(2) +#define EEPROM_BIT_DO __BIT(4) + +#define EEPROM_CMD_READ 0x2 + +/* Defines a single bit in a by bit number (0-31) */ +#define __BIT(x) (1UL << (x)) + +/* Interrupts masks */ +#define CX2_INTA_NONE 0x00000000 + +#define CX2_INTA_BIT_RX_TRANSFER 0x00000002 +#define CX2_INTA_BIT_STATUS_CHANGE 0x00000010 +#define CX2_INTA_BIT_BEACON_PERIOD_EXPIRED 0x00000020 + +//Inta Bits for CF +#define CX2_INTA_BIT_TX_CMD_QUEUE 0x00000800 +#define CX2_INTA_BIT_TX_QUEUE_1 0x00001000 +#define CX2_INTA_BIT_TX_QUEUE_2 0x00002000 +#define CX2_INTA_BIT_TX_QUEUE_3 0x00004000 +#define CX2_INTA_BIT_TX_QUEUE_4 0x00008000 + +#define CX2_INTA_BIT_SLAVE_MODE_HOST_CMD_DONE 0x00010000 + +#define CX2_INTA_BIT_PREPARE_FOR_POWER_DOWN 0x00100000 +#define CX2_INTA_BIT_POWER_DOWN 0x00200000 + +#define CX2_INTA_BIT_FW_INITIALIZATION_DONE 0x01000000 +#define CX2_INTA_BIT_FW_CARD_DISABLE_PHY_OFF_DONE 0x02000000 +#define CX2_INTA_BIT_RF_KILL_DONE 0x04000000 +#define CX2_INTA_BIT_FATAL_ERROR 0x40000000 +#define CX2_INTA_BIT_PARITY_ERROR 0x80000000 + +/* Interrupts enabled at init time. */ +#define CX2_INTA_MASK_ALL \ + (CX2_INTA_BIT_TX_QUEUE_1 | \ + CX2_INTA_BIT_TX_QUEUE_2 | \ + CX2_INTA_BIT_TX_QUEUE_3 | \ + CX2_INTA_BIT_TX_QUEUE_4 | \ + CX2_INTA_BIT_TX_CMD_QUEUE | \ + CX2_INTA_BIT_RX_TRANSFER | \ + CX2_INTA_BIT_FATAL_ERROR | \ + CX2_INTA_BIT_PARITY_ERROR | \ + CX2_INTA_BIT_STATUS_CHANGE | \ + CX2_INTA_BIT_FW_INITIALIZATION_DONE | \ + CX2_INTA_BIT_BEACON_PERIOD_EXPIRED | \ + CX2_INTA_BIT_SLAVE_MODE_HOST_CMD_DONE | \ + CX2_INTA_BIT_PREPARE_FOR_POWER_DOWN | \ + CX2_INTA_BIT_POWER_DOWN | \ + CX2_INTA_BIT_RF_KILL_DONE ) + +#define IPWSTATUS_ERROR_LOG (CX2_SHARED_LOWER_BOUND + 0x410) +#define IPW_EVENT_LOG (CX2_SHARED_LOWER_BOUND + 0x414) + +/* 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)) + +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_DINOSTATUS_ERROR, + IPW_FW_ERROR_EEPROMSTATUS_ERROR, + IPW_FW_ERROR_SYSASSERT, + IPW_FW_ERROR_FATAL_ERROR +}; + +#define AUTH_OPEN 0 +#define AUTH_SHARED_KEY 1 +#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 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_ORDINALS_TABLE_LOWER (CX2_SHARED_LOWER_BOUND + 0x500) +#define IPW_ORDINALS_TABLE_0 (CX2_SHARED_LOWER_BOUND + 0x180) +#define IPW_ORDINALS_TABLE_1 (CX2_SHARED_LOWER_BOUND + 0x184) +#define IPW_ORDINALS_TABLE_2 (CX2_SHARED_LOWER_BOUND + 0x188) +#define IPW_MEM_FIXED_OVERRIDE (CX2_SHARED_LOWER_BOUND + 0x41C) + +struct ipw_fixed_rate { + u16 tx_rates; + u16 reserved; +} __attribute__ ((packed)); + +#define CX2_INDIRECT_ADDR_MASK (~0x3ul) + +struct host_cmd { + u8 cmd; + u8 len; + u16 reserved; + u32 param[TFD_CMD_IMMEDIATE_PAYLOAD_LENGTH]; +} __attribute__ ((packed)); + +#define CFG_BT_COEXISTENCE_MIN 0x00 +#define CFG_BT_COEXISTENCE_DEFER 0x02 +#define CFG_BT_COEXISTENCE_KILL 0x04 +#define CFG_BT_COEXISTENCE_WME_OVER_BT 0x08 +#define CFG_BT_COEXISTENCE_OOB 0x10 +#define CFG_BT_COEXISTENCE_MAX 0xFF +#define CFG_BT_COEXISTENCE_DEF 0x80 /* read Bt from EEPROM*/ + +#define CFG_CTS_TO_ITSELF_ENABLED_MIN 0x0 +#define CFG_CTS_TO_ITSELF_ENABLED_MAX 0x1 +#define CFG_CTS_TO_ITSELF_ENABLED_DEF CFG_CTS_TO_ITSELF_ENABLED_MIN + +#define CFG_SYS_ANTENNA_BOTH 0x000 +#define CFG_SYS_ANTENNA_A 0x001 +#define CFG_SYS_ANTENNA_B 0x003 + +/* + * 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 + +static const long ipw_frequencies[] = { + 2412, 2417, 2422, 2427, + 2432, 2437, 2442, 2447, + 2452, 2457, 2462, 2467, + 2472, 2484 +}; + +#define FREQ_COUNT ARRAY_SIZE(ipw_frequencies) + +#define IPW_MAX_CONFIG_RETRIES 10 + +static inline u32 frame_hdr_len(struct ieee80211_hdr *hdr) +{ + u32 retval; + u16 fc; + + retval = sizeof(struct ieee80211_hdr); + fc = le16_to_cpu(hdr->frame_ctl); + + /* + * Function ToDS FromDS + * IBSS 0 0 + * To AP 1 0 + * From AP 0 1 + * WDS (bridge) 1 1 + * + * Only WDS frames use Address4 among them. --YZ + */ + if (!(fc & IEEE80211_FCTL_TODS) || !(fc & IEEE80211_FCTL_FROMDS)) + retval -= ETH_ALEN; + + return retval; +} + +#endif /* __ipw2200_h__ */ diff --git a/drivers/net/wireless/orinoco.h b/drivers/net/wireless/orinoco.h index 972a8510f..c2f5f78f5 100644 --- a/drivers/net/wireless/orinoco.h +++ b/drivers/net/wireless/orinoco.h @@ -7,39 +7,19 @@ #ifndef _ORINOCO_H #define _ORINOCO_H +#define DRIVER_VERSION "0.13e" + #include #include #include #include #include -#include "hermes.h" - -/* Workqueue / task queue backwards compatibility stuff */ - -#if LINUX_VERSION_CODE > KERNEL_VERSION(2,5,41) -#include -#else -#include -#define work_struct tq_struct -#define INIT_WORK INIT_TQUEUE -#define schedule_work schedule_task -#endif - -/* Interrupt handler backwards compatibility stuff */ -#ifndef IRQ_NONE - -#define IRQ_NONE -#define IRQ_HANDLED -typedef void irqreturn_t; -#endif +#include "hermes.h" /* To enable debug messages */ //#define ORINOCO_DEBUG 3 -#if (! defined (WIRELESS_EXT)) || (WIRELESS_EXT < 10) -#error "orinoco driver requires Wireless extensions v10 or later." -#endif /* (! defined (WIRELESS_EXT)) || (WIRELESS_EXT < 10) */ #define WIRELESS_SPY // enable iwspy support #define ORINOCO_MAX_KEY_SIZE 14 @@ -50,11 +30,6 @@ struct orinoco_key { char data[ORINOCO_MAX_KEY_SIZE]; } __attribute__ ((packed)); -#define ORINOCO_INTEN ( HERMES_EV_RX | HERMES_EV_ALLOC | HERMES_EV_TX | \ - HERMES_EV_TXEXC | HERMES_EV_WTERR | HERMES_EV_INFO | \ - HERMES_EV_INFDROP ) - - struct orinoco_private { void *card; /* Pointer to card dependent structure */ int (*hard_reset)(struct orinoco_private *); @@ -78,7 +53,6 @@ struct orinoco_private { hermes_t hw; u16 txfid; - /* Capabilities of the hardware/firmware */ int firmware_type; #define FIRMWARE_TYPE_AGERE 1 @@ -128,6 +102,10 @@ extern int orinoco_debug; #define TRACE_ENTER(devname) DEBUG(2, "%s: -> %s()\n", devname, __FUNCTION__); #define TRACE_EXIT(devname) DEBUG(2, "%s: <- %s()\n", devname, __FUNCTION__); +/********************************************************************/ +/* Exported prototypes */ +/********************************************************************/ + extern struct net_device *alloc_orinocodev(int sizeof_card, int (*hard_reset)(struct orinoco_private *)); extern int __orinoco_up(struct net_device *dev); diff --git a/drivers/net/wireless/orinoco_cs.c b/drivers/net/wireless/orinoco_cs.c index 292eeffb6..4eb92ac6a 100644 --- a/drivers/net/wireless/orinoco_cs.c +++ b/drivers/net/wireless/orinoco_cs.c @@ -1,4 +1,4 @@ -/* orinoco_cs.c 0.13e - (formerly known as dldwd_cs.c) +/* 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/ @@ -10,6 +10,9 @@ * Copyright notice & release notes in file orinoco.c */ +#define DRIVER_NAME "orinoco_cs" +#define PFX DRIVER_NAME ": " + #include #ifdef __IN_PCMCIA_PACKAGE__ #include @@ -47,9 +50,7 @@ MODULE_AUTHOR("David Gibson "); MODULE_DESCRIPTION("Driver for PCMCIA Lucent Orinoco, Prism II based and similar wireless cards"); -#ifdef MODULE_LICENSE MODULE_LICENSE("Dual MPL/GPL"); -#endif /* Module parameters */ @@ -76,7 +77,7 @@ MODULE_PARM(ignore_cis_vcc, "i"); * device driver with appropriate cards, through the card * configuration database. */ -static dev_info_t dev_info = "orinoco_cs"; +static dev_info_t dev_info = DRIVER_NAME; /********************************************************************/ /* Data structures */ @@ -144,15 +145,6 @@ orinoco_cs_hard_reset(struct orinoco_private *priv) /* PCMCIA stuff */ /********************************************************************/ -/* In 2.5 (as of 2.5.69 at least) there is a cs_error exported which - * does this, but it's not in 2.4 so we do our own for now. */ -static void -orinoco_cs_error(client_handle_t handle, int func, int ret) -{ - error_info_t err = { func, ret }; - pcmcia_report_error(handle, &err); -} - /* * This creates an "instance" of the driver, allocating local data * structures for one device. The device is registered with Card @@ -174,7 +166,7 @@ orinoco_cs_attach(void) dev = alloc_orinocodev(sizeof(*card), orinoco_cs_hard_reset); if (! dev) return NULL; - priv = dev->priv; + priv = netdev_priv(dev); card = priv->card; /* Link both structures together */ @@ -216,7 +208,7 @@ orinoco_cs_attach(void) ret = pcmcia_register_client(&link->handle, &client_reg); if (ret != CS_SUCCESS) { - orinoco_cs_error(link->handle, RegisterClient, ret); + cs_error(link->handle, RegisterClient, ret); orinoco_cs_detach(link); return NULL; } @@ -230,8 +222,7 @@ orinoco_cs_attach(void) * are freed. Otherwise, the structures will be freed when the device * is released. */ -static void -orinoco_cs_detach(dev_link_t * link) +static void orinoco_cs_detach(dev_link_t *link) { dev_link_t **linkp; struct net_device *dev = link->priv; @@ -240,10 +231,8 @@ orinoco_cs_detach(dev_link_t * link) for (linkp = &dev_list; *linkp; linkp = &(*linkp)->next) if (*linkp == link) break; - if (*linkp == NULL) { - BUG(); - return; - } + + BUG_ON(*linkp == NULL); if (link->state & DEV_CONFIG) orinoco_cs_release(link); @@ -254,9 +243,9 @@ orinoco_cs_detach(dev_link_t * link) /* Unlink device structure, and free it */ *linkp = link->next; - DEBUG(0, "orinoco_cs: detach: link=%p link->dev=%p\n", link, link->dev); + DEBUG(0, PFX "detach: link=%p link->dev=%p\n", link, link->dev); if (link->dev) { - DEBUG(0, "orinoco_cs: About to unregister net device %p\n", + DEBUG(0, PFX "About to unregister net device %p\n", dev); unregister_netdev(dev); } @@ -269,15 +258,16 @@ orinoco_cs_detach(dev_link_t * link) * device available to the system. */ -#define CS_CHECK(fn, ret) \ -do { last_fn = (fn); if ((last_ret = (ret)) != 0) goto cs_failed; } while (0) +#define CS_CHECK(fn, ret) do { \ + last_fn = (fn); if ((last_ret = (ret)) != 0) goto cs_failed; \ + } while (0) static void orinoco_cs_config(dev_link_t *link) { struct net_device *dev = link->priv; client_handle_t handle = link->handle; - struct orinoco_private *priv = dev->priv; + struct orinoco_private *priv = netdev_priv(dev); struct orinoco_pccard *card = priv->card; hermes_t *hw = &priv->hw; int last_fn, last_ret; @@ -308,7 +298,8 @@ orinoco_cs_config(dev_link_t *link) link->state |= DEV_CONFIG; /* Look up the current Vcc */ - CS_CHECK(GetConfigurationInfo, pcmcia_get_configuration_info(handle, &conf)); + CS_CHECK(GetConfigurationInfo, + pcmcia_get_configuration_info(handle, &conf)); link->conf.Vcc = conf.Vcc; /* @@ -412,8 +403,9 @@ orinoco_cs_config(dev_link_t *link) pcmcia_release_io(link->handle, &link->io); last_ret = pcmcia_get_next_tuple(handle, &tuple); if (last_ret == CS_NO_MORE_ITEMS) { - printk(KERN_ERR "GetNextTuple(). No matching CIS configuration, " - "maybe you need the ignore_cis_vcc=1 parameter.\n"); + printk(KERN_ERR PFX "GetNextTuple(): No matching " + "CIS configuration, maybe you need the " + "ignore_cis_vcc=1 parameter.\n"); goto cs_failed; } } @@ -451,7 +443,8 @@ orinoco_cs_config(dev_link_t *link) * 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->handle, &link->conf)); + CS_CHECK(RequestConfiguration, + pcmcia_request_configuration(link->handle, &link->conf)); /* Ok, we have the configuration, prepare to register the netdev */ dev->base_addr = link->io.BasePort1; @@ -463,7 +456,7 @@ orinoco_cs_config(dev_link_t *link) dev->name[0] = '\0'; /* Tell the stack we exist */ if (register_netdev(dev) != 0) { - printk(KERN_ERR "orinoco_cs: register_netdev() failed\n"); + printk(KERN_ERR PFX "register_netdev() failed\n"); goto failed; } @@ -495,7 +488,7 @@ orinoco_cs_config(dev_link_t *link) return; cs_failed: - orinoco_cs_error(link->handle, last_fn, last_ret); + cs_error(link->handle, last_fn, last_ret); failed: orinoco_cs_release(link); @@ -510,7 +503,7 @@ static void orinoco_cs_release(dev_link_t *link) { struct net_device *dev = link->priv; - struct orinoco_private *priv = dev->priv; + struct orinoco_private *priv = netdev_priv(dev); unsigned long flags; /* We're committed to taking the device away now, so mark the @@ -538,7 +531,7 @@ orinoco_cs_event(event_t event, int priority, { dev_link_t *link = args->client_data; struct net_device *dev = link->priv; - struct orinoco_private *priv = dev->priv; + struct orinoco_private *priv = netdev_priv(dev); struct orinoco_pccard *card = priv->card; int err = 0; unsigned long flags; @@ -635,12 +628,14 @@ orinoco_cs_event(event_t event, int priority, /* Can't be declared "const" or the whole __initdata section will * become const */ -static char version[] __initdata = "orinoco_cs.c 0.13e (David Gibson and others)"; +static char version[] __initdata = DRIVER_NAME " " DRIVER_VERSION + " (David Gibson , " + "Pavel Roskin , et al)"; static struct pcmcia_driver orinoco_driver = { .owner = THIS_MODULE, .drv = { - .name = "orinoco_cs", + .name = DRIVER_NAME, }, .attach = orinoco_cs_attach, .detach = orinoco_cs_detach, @@ -660,7 +655,7 @@ exit_orinoco_cs(void) pcmcia_unregister_driver(&orinoco_driver); if (dev_list) - DEBUG(0, "orinoco_cs: Removing leftover devices.\n"); + DEBUG(0, PFX "Removing leftover devices.\n"); while (dev_list != NULL) { if (dev_list->state & DEV_CONFIG) orinoco_cs_release(dev_list); @@ -670,4 +665,3 @@ exit_orinoco_cs(void) module_init(init_orinoco_cs); module_exit(exit_orinoco_cs); - diff --git a/drivers/net/wireless/ray_cs.h b/drivers/net/wireless/ray_cs.h index 6d4985061..c77afa14f 100644 --- a/drivers/net/wireless/ray_cs.h +++ b/drivers/net/wireless/ray_cs.h @@ -28,9 +28,9 @@ typedef struct ray_dev_t { dev_node_t node; window_handle_t amem_handle; /* handle to window for attribute memory */ window_handle_t rmem_handle; /* handle to window for rx buffer on card */ - UCHAR *sram; /* pointer to beginning of shared RAM */ - UCHAR *amem; /* pointer to attribute mem window */ - UCHAR *rmem; /* pointer to receive buffer window */ + void __iomem *sram; /* pointer to beginning of shared RAM */ + void __iomem *amem; /* pointer to attribute mem window */ + void __iomem *rmem; /* pointer to receive buffer window */ dev_link_t *finder; /* pointer back to dev_link_t for card */ struct timer_list timer; long tx_ccs_lock; diff --git a/drivers/net/wireless/wavelan.c b/drivers/net/wireless/wavelan.c index 1cbd34ba5..3047d8cfa 100644 --- a/drivers/net/wireless/wavelan.c +++ b/drivers/net/wireless/wavelan.c @@ -3822,17 +3822,18 @@ static irqreturn_t wavelan_interrupt(int irq, void *dev_id, struct pt_regs *regs if ((hasr & HASR_MMC_INTR) && (lp->hacr & HACR_MMC_INT_ENABLE)) { u8 dce_status; -#ifdef DEBUG_INTERRUPT_ERROR - printk(KERN_INFO - "%s: wavelan_interrupt(): unexpected mmc interrupt: status 0x%04x.\n", - dev->name, dce_status); -#endif /* * Interrupt from the modem management controller. * This will clear it -- ignored for now. */ mmc_read(ioaddr, mmroff(0, mmr_dce_status), &dce_status, sizeof(dce_status)); + +#ifdef DEBUG_INTERRUPT_ERROR + printk(KERN_INFO + "%s: wavelan_interrupt(): unexpected mmc interrupt: status 0x%04x.\n", + dev->name, dce_status); +#endif } /* Check if not controller interrupt */ diff --git a/drivers/oprofile/buffer_sync.h b/drivers/oprofile/buffer_sync.h index a8def27d8..08866f6a9 100644 --- a/drivers/oprofile/buffer_sync.h +++ b/drivers/oprofile/buffer_sync.h @@ -16,4 +16,7 @@ int sync_start(void); /* remove the hooks */ void sync_stop(void); +/* sync the given CPU's buffer */ +void sync_buffer(int cpu); + #endif /* OPROFILE_BUFFER_SYNC_H */ diff --git a/drivers/oprofile/cpu_buffer.h b/drivers/oprofile/cpu_buffer.h index 66eafcdb1..23be2013a 100644 --- a/drivers/oprofile/cpu_buffer.h +++ b/drivers/oprofile/cpu_buffer.h @@ -12,15 +12,17 @@ #include #include +#include #include struct task_struct; -/* allocate a sample buffer for each CPU */ int alloc_cpu_buffers(void); - void free_cpu_buffers(void); +void start_cpu_work(void); +void end_cpu_work(void); + /* CPU buffer is composed of such entries (which are * also used for context switch notes) */ @@ -38,11 +40,12 @@ struct oprofile_cpu_buffer { struct op_sample * buffer; unsigned long sample_received; unsigned long sample_lost_overflow; - unsigned long sample_lost_task_exit; + int cpu; + struct work_struct work; } ____cacheline_aligned; extern struct oprofile_cpu_buffer cpu_buffer[]; -void cpu_buffer_reset(struct oprofile_cpu_buffer *cpu_buf); +void cpu_buffer_reset(struct oprofile_cpu_buffer * cpu_buf); #endif /* OPROFILE_CPU_BUFFER_H */ diff --git a/drivers/oprofile/oprofile_stats.c b/drivers/oprofile/oprofile_stats.c index 8e92b0388..3ec23d8a6 100644 --- a/drivers/oprofile/oprofile_stats.c +++ b/drivers/oprofile/oprofile_stats.c @@ -22,14 +22,10 @@ void oprofile_reset_stats(void) struct oprofile_cpu_buffer * cpu_buf; int i; - for (i = 0; i < NR_CPUS; ++i) { - if (!cpu_possible(i)) - continue; - + for_each_cpu(i) { cpu_buf = &cpu_buffer[i]; cpu_buf->sample_received = 0; cpu_buf->sample_lost_overflow = 0; - cpu_buf->sample_lost_task_exit = 0; } atomic_set(&oprofile_stats.sample_lost_no_mm, 0); @@ -50,10 +46,7 @@ void oprofile_create_stats_files(struct super_block * sb, struct dentry * root) if (!dir) return; - for (i = 0; i < NR_CPUS; ++i) { - if (!cpu_possible(i)) - continue; - + for_each_cpu(i) { cpu_buf = &cpu_buffer[i]; snprintf(buf, 10, "cpu%d", i); cpudir = oprofilefs_mkdir(sb, dir, buf); @@ -66,8 +59,6 @@ void oprofile_create_stats_files(struct super_block * sb, struct dentry * root) &cpu_buf->sample_received); oprofilefs_create_ro_ulong(sb, cpudir, "sample_lost_overflow", &cpu_buf->sample_lost_overflow); - oprofilefs_create_ro_ulong(sb, cpudir, "sample_lost_task_exit", - &cpu_buf->sample_lost_task_exit); } oprofilefs_create_ro_atomic(sb, dir, "sample_lost_no_mm", diff --git a/drivers/oprofile/timer_int.c b/drivers/oprofile/timer_int.c index 3fae2c772..5f0318908 100644 --- a/drivers/oprofile/timer_int.c +++ b/drivers/oprofile/timer_int.c @@ -19,7 +19,7 @@ static int timer_notify(struct notifier_block * self, unsigned long val, void * { struct pt_regs * regs = (struct pt_regs *)data; int cpu = smp_processor_id(); - unsigned long eip = instruction_pointer(regs); + unsigned long eip = profile_pc(regs); oprofile_add_sample(eip, !user_mode(regs), 0, cpu); return 0; diff --git a/drivers/parisc/lasi.c b/drivers/parisc/lasi.c index 6e67ea060..45f2be4cc 100644 --- a/drivers/parisc/lasi.c +++ b/drivers/parisc/lasi.c @@ -11,7 +11,7 @@ * (at your option) any later version. * * by Alan Cox and - * Alex deVries + * Alex deVries */ #include diff --git a/drivers/pci/.cvsignore b/drivers/pci/.cvsignore new file mode 100644 index 000000000..d5b21d9ee --- /dev/null +++ b/drivers/pci/.cvsignore @@ -0,0 +1,3 @@ +classlist.h +devlist.h +gen-devlist diff --git a/drivers/pci/Makefile b/drivers/pci/Makefile index 377c7238c..474eacd34 100644 --- a/drivers/pci/Makefile +++ b/drivers/pci/Makefile @@ -35,7 +35,7 @@ ifndef CONFIG_X86 obj-y += syscall.o endif -host-progs := gen-devlist +hostprogs-y := gen-devlist # Dependencies on generated files need to be listed explicitly $(obj)/names.o: $(obj)/devlist.h $(obj)/classlist.h diff --git a/drivers/pci/hotplug/Kconfig b/drivers/pci/hotplug/Kconfig index 3dc99c18a..9117a95a3 100644 --- a/drivers/pci/hotplug/Kconfig +++ b/drivers/pci/hotplug/Kconfig @@ -88,6 +88,18 @@ config HOTPLUG_PCI_ACPI When in doubt, say N. +config HOTPLUG_PCI_ACPI_IBM + tristate "ACPI PCI Hotplug driver IBM extensions" + depends on HOTPLUG_PCI_ACPI + help + Say Y here if you have an IBM system that supports PCI Hotplug using + ACPI. + + To compile this driver as a module, choose M here: the + module will be called acpiphp_ibm. + + When in doubt, say N. + config HOTPLUG_PCI_CPCI bool "CompactPCI Hotplug driver" depends on HOTPLUG_PCI diff --git a/drivers/pci/hotplug/Makefile b/drivers/pci/hotplug/Makefile index a972757a0..4fc3ed9b8 100644 --- a/drivers/pci/hotplug/Makefile +++ b/drivers/pci/hotplug/Makefile @@ -7,6 +7,7 @@ obj-$(CONFIG_HOTPLUG_PCI_FAKE) += fakephp.o obj-$(CONFIG_HOTPLUG_PCI_COMPAQ) += cpqphp.o obj-$(CONFIG_HOTPLUG_PCI_IBM) += ibmphp.o obj-$(CONFIG_HOTPLUG_PCI_ACPI) += acpiphp.o +obj-$(CONFIG_HOTPLUG_PCI_ACPI_IBM) += acpiphp_ibm.o obj-$(CONFIG_HOTPLUG_PCI_CPCI_ZT5550) += cpcihp_zt5550.o obj-$(CONFIG_HOTPLUG_PCI_CPCI_GENERIC) += cpcihp_generic.o obj-$(CONFIG_HOTPLUG_PCI_PCIE) += pciehp.o diff --git a/drivers/pci/hotplug/acpiphp_ibm.c b/drivers/pci/hotplug/acpiphp_ibm.c new file mode 100644 index 000000000..fe7866c1c --- /dev/null +++ b/drivers/pci/hotplug/acpiphp_ibm.c @@ -0,0 +1,474 @@ +/* + * ACPI PCI Hot Plug IBM Extension + * + * Copyright (C) 2004 Vernon Mauery + * Copyright (C) 2004 IBM Corp. + * + * All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or (at + * your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or + * NON INFRINGEMENT. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * Send feedback to + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "acpiphp.h" +#include "pci_hotplug.h" + +#define DRIVER_VERSION "1.0.1" +#define DRIVER_AUTHOR "Irene Zubarev , Vernon Mauery " +#define DRIVER_DESC "ACPI Hot Plug PCI Controller Driver IBM extension" + +static int debug; + +MODULE_AUTHOR(DRIVER_AUTHOR); +MODULE_DESCRIPTION(DRIVER_DESC); +MODULE_LICENSE("GPL"); +MODULE_VERSION(DRIVER_VERSION); +module_param(debug, bool, 644); +MODULE_PARM_DESC(debug, " Debugging mode enabled or not"); +#define MY_NAME "acpiphp_ibm" + +#undef dbg +#define dbg(format, arg...) \ +do { \ + if (debug) \ + printk(KERN_DEBUG "%s: " format, \ + MY_NAME , ## arg); \ +} while (0) + +#define FOUND_APCI 0x61504349 +/* these are the names for the IBM ACPI pseudo-device */ +#define IBM_HARDWARE_ID1 "IBM37D0" +#define IBM_HARDWARE_ID2 "IBM37D4" + +/* union apci_descriptor - allows access to the + * various device descriptors that are embedded in the + * aPCI table + */ +union apci_descriptor { + struct { + char sig[4]; + u8 len; + } header; + struct { + u8 type; + u8 len; + u16 slot_id; + u8 bus_id; + u8 dev_num; + u8 slot_num; + u8 slot_attr[2]; + u8 attn; + u8 status[2]; + u8 sun; + } slot; + struct { + u8 type; + u8 len; + } generic; +}; + +/* struct notification - keeps info about the device + * that cause the ACPI notification event + */ +struct notification { + struct acpi_device *device; + u8 event; +}; + +static int ibm_set_attention_status(struct hotplug_slot *slot, u8 status); +static int ibm_get_attention_status(struct hotplug_slot *slot, u8 *status); +static void ibm_handle_events(acpi_handle handle, u32 event, void *context); +static int ibm_get_table_from_acpi(char **bufp); +static ssize_t ibm_read_apci_table(struct kobject *kobj, + char *buffer, loff_t pos, size_t size); +static acpi_status __init ibm_find_acpi_device(acpi_handle handle, + u32 lvl, void *context, void **rv); +static int __init ibm_acpiphp_init(void); +static void __exit ibm_acpiphp_exit(void); + +static acpi_handle ibm_acpi_handle; +static struct notification ibm_note; +static struct bin_attribute ibm_apci_table_attr = { + .attr = { + .name = "apci_table", + .owner = THIS_MODULE, + .mode = S_IRUGO, + }, + .read = ibm_read_apci_table, + .write = NULL, +}; +static struct acpiphp_attention_info ibm_attention_info = +{ + .set_attn = ibm_set_attention_status, + .get_attn = ibm_get_attention_status, + .owner = THIS_MODULE, +}; + + +/** + * ibm_set_attention_status - callback method to set the attention LED + * @slot: the hotplug_slot to work with + * @status: what to set the LED to (0 or 1) + * + * Description: this method is registered with the acpiphp module as a + * callback to do the device specific task of setting the LED status + **/ +static int ibm_set_attention_status(struct hotplug_slot *slot, u8 status) +{ + int retval = 0; + union acpi_object args[2]; + struct acpi_object_list params = { .pointer = args, .count = 2 }; + acpi_status stat; + unsigned long rc = 0; + struct acpiphp_slot *acpi_slot; + + acpi_slot = ((struct slot *)(slot->private))->acpi_slot; + + dbg("%s: set slot %d attention status to %d\n", __FUNCTION__, + acpi_slot->sun, (status ? 1 : 0)); + + args[0].type = ACPI_TYPE_INTEGER; + args[0].integer.value = acpi_slot->sun; + args[1].type = ACPI_TYPE_INTEGER; + args[1].integer.value = (status) ? 1 : 0; + + stat = acpi_evaluate_integer(ibm_acpi_handle, "APLS", ¶ms, &rc); + if (ACPI_FAILURE(stat)) { + retval = -ENODEV; + err("APLS evaluation failed: 0x%08x\n", stat); + } else if (!rc) { + retval = -ERANGE; + err("APLS method failed: 0x%08lx\n", rc); + } + return retval; +} + +/** + * ibm_get_attention_status - callback method to get attention LED status + * @slot: the hotplug_slot to work with + * @status: returns what the LED is set to (0 or 1) + * + * Description: this method is registered with the acpiphp module as a + * callback to do the device specific task of getting the LED status + * + * Because there is no direct method of getting the LED status directly + * from an ACPI call, we read the aPCI table and parse out our + * slot descriptor to read the status from that. + **/ +static int ibm_get_attention_status(struct hotplug_slot *slot, u8 *status) +{ + int retval = -EINVAL, ind = 0, size; + char *table = NULL; + struct acpiphp_slot *acpi_slot; + union apci_descriptor *des; + + acpi_slot = ((struct slot *)(slot->private))->acpi_slot; + + size = ibm_get_table_from_acpi(&table); + if (size <= 0 || !table) + goto get_attn_done; + // read the header + des = (union apci_descriptor *)&table[ind]; + if (memcmp(des->header.sig, "aPCI", 4) != 0) + goto get_attn_done; + des = (union apci_descriptor *)&table[ind += des->header.len]; + while (ind < size && (des->generic.type != 0x82 || + des->slot.slot_id != acpi_slot->sun)) + des = (union apci_descriptor *)&table[ind += des->generic.len]; + if (ind < size && des->slot.slot_id == acpi_slot->sun) { + retval = 0; + if (des->slot.attn & 0xa0 || des->slot.status[1] & 0x08) + *status = 1; + else + *status = 0; + } + + dbg("%s: get slot %d attention status is %d retval=%x\n", + __FUNCTION__, acpi_slot->sun, *status, retval); + +get_attn_done: + kfree(table); + return retval; +} + +/** + * ibm_handle_events - listens for ACPI events for the IBM37D0 device + * @handle: an ACPI handle to the device that caused the event + * @event: the event info (device specific) + * @context: passed context (our notification struct) + * + * Description: this method is registered as a callback with the ACPI + * subsystem it is called when this device has an event to notify the OS of + * + * The events actually come from the device as two events that get + * synthesized into one event with data by this function. The event + * ID comes first and then the slot number that caused it. We report + * this as one event to the OS. + * + * From section 5.6.2.2 of the ACPI 2.0 spec, I understand that the OSPM will + * only re-enable the interrupt that causes this event AFTER this method + * has returned, thereby enforcing serial access for the notification struct. + **/ +static void ibm_handle_events(acpi_handle handle, u32 event, void *context) +{ + u8 detail = event & 0x0f; + u8 subevent = event & 0xf0; + struct notification *note = context; + + dbg("%s: Received notification %02x\n", __FUNCTION__, event); + + if (subevent == 0x80) { + dbg("%s: generationg bus event\n", __FUNCTION__); + acpi_bus_generate_event(note->device, note->event, detail); + } else + note->event = event; +} + +/** + * ibm_get_table_from_acpi - reads the APLS buffer from ACPI + * @bufp: address to pointer to allocate for the table + * + * Description: this method reads the APLS buffer in from ACPI and + * stores the "stripped" table into a single buffer + * it allocates and passes the address back in bufp + * + * If NULL is passed in as buffer, this method only calculates + * the size of the table and returns that without filling + * in the buffer + * + * returns < 0 on error or the size of the table on success + **/ +static int ibm_get_table_from_acpi(char **bufp) +{ + union acpi_object *package; + struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; + acpi_status status; + char *lbuf = NULL; + int i, size = -EIO; + + status = acpi_evaluate_object(ibm_acpi_handle, "APCI", NULL, &buffer); + if (ACPI_FAILURE(status)) { + err("%s: APCI evaluation failed\n", __FUNCTION__); + return -ENODEV; + } + + package = (union acpi_object *) buffer.pointer; + if(!(package) || + (package->type != ACPI_TYPE_PACKAGE) || + !(package->package.elements)) { + err("%s: Invalid APCI object\n", __FUNCTION__); + goto read_table_done; + } + + for(size = 0, i = 0; i < package->package.count; i++) { + if (package->package.elements[i].type != ACPI_TYPE_BUFFER) { + err("%s: Invalid APCI element %d\n", __FUNCTION__, i); + goto read_table_done; + } + size += package->package.elements[i].buffer.length; + } + + if (bufp == NULL) + goto read_table_done; + + lbuf = kmalloc(size, GFP_KERNEL); + dbg("%s: element count: %i, ASL table size: %i, &table = 0x%p\n", + __FUNCTION__, package->package.count, size, lbuf); + + if (lbuf) { + *bufp = lbuf; + memset(lbuf, 0, size); + } else { + size = -ENOMEM; + goto read_table_done; + } + + size = 0; + for (i=0; ipackage.count; i++) { + memcpy(&lbuf[size], + package->package.elements[i].buffer.pointer, + package->package.elements[i].buffer.length); + size += package->package.elements[i].buffer.length; + } + +read_table_done: + kfree(buffer.pointer); + return size; +} + +/** + * ibm_read_apci_table - callback for the sysfs apci_table file + * @kobj: the kobject this binary attribute is a part of + * @buffer: the kernel space buffer to fill + * @pos: the offset into the file + * @size: the number of bytes requested + * + * Description: gets registered with sysfs as the reader callback + * to be executed when /sys/bus/pci/slots/apci_table gets read + * + * Since we don't get notified on open and close for this file, + * things get really tricky here... + * our solution is to only allow reading the table in all at once + **/ +static ssize_t ibm_read_apci_table(struct kobject *kobj, + char *buffer, loff_t pos, size_t size) +{ + int bytes_read = -EINVAL; + char *table = NULL; + + dbg("%s: pos = %d, size = %zd\n", __FUNCTION__, (int)pos, size); + + if (pos == 0) { + bytes_read = ibm_get_table_from_acpi(&table); + if (bytes_read > 0 && bytes_read <= size) + memcpy(buffer, table, bytes_read); + kfree(table); + } + return bytes_read; +} + +/** + * ibm_find_acpi_device - callback to find our ACPI device + * @handle: the ACPI handle of the device we are inspecting + * @lvl: depth into the namespace tree + * @context: a pointer to our handle to fill when we find the device + * @rv: a return value to fill if desired + * + * Description: used as a callback when calling acpi_walk_namespace + * to find our device. When this method returns non-zero + * acpi_walk_namespace quits its search and returns our value + **/ +static acpi_status __init ibm_find_acpi_device(acpi_handle handle, + u32 lvl, void *context, void **rv) +{ + acpi_handle *phandle = (acpi_handle *)context; + acpi_status status; + struct acpi_device_info info; + struct acpi_buffer info_buffer = { + .length = sizeof(struct acpi_device_info), + .pointer = &info, + }; + + status = acpi_get_object_info(handle, &info_buffer); + if (ACPI_FAILURE(status)) { + err("%s: Failed to get device information", __FUNCTION__); + return 0; + } + info.hardware_id.value[sizeof(info.hardware_id.value) - 1] = '\0'; + + if(info.current_status && (info.valid & ACPI_VALID_HID) && + (!strcmp(info.hardware_id.value, IBM_HARDWARE_ID1) || + !strcmp(info.hardware_id.value, IBM_HARDWARE_ID2))) { + dbg("found hardware: %s, handle: %p\n", info.hardware_id.value, + handle); + *phandle = handle; + /* returning non-zero causes the search to stop + * and returns this value to the caller of + * acpi_walk_namespace, but it also causes some warnings + * in the acpi debug code to print... + */ + return FOUND_APCI; + } + return 0; +} + +static int __init ibm_acpiphp_init(void) +{ + int retval = 0; + acpi_status status; + struct acpi_device *device; + struct kobject *sysdir = &pci_hotplug_slots_subsys.kset.kobj; + + dbg("%s\n", __FUNCTION__); + + if (acpi_walk_namespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT, + ACPI_UINT32_MAX, ibm_find_acpi_device, + &ibm_acpi_handle, NULL) != FOUND_APCI) { + err("%s: acpi_walk_namespace failed\n", __FUNCTION__); + retval = -ENODEV; + goto init_return; + } + dbg("%s: found IBM aPCI device\n", __FUNCTION__); + if (acpi_bus_get_device(ibm_acpi_handle, &device)) { + err("%s: acpi_bus_get_device failed\n", __FUNCTION__); + retval = -ENODEV; + goto init_return; + } + if (acpiphp_register_attention(&ibm_attention_info)) { + retval = -ENODEV; + goto init_return; + } + + ibm_note.device = device; + status = acpi_install_notify_handler( + ibm_acpi_handle, + ACPI_DEVICE_NOTIFY, + ibm_handle_events, + &ibm_note); + if (ACPI_FAILURE(status)) { + err("%s: Failed to register notification handler\n", + __FUNCTION__); + retval = -EBUSY; + goto init_cleanup; + } + + ibm_apci_table_attr.size = ibm_get_table_from_acpi(NULL); + retval = sysfs_create_bin_file(sysdir, &ibm_apci_table_attr); + + return retval; + +init_cleanup: + acpiphp_unregister_attention(&ibm_attention_info); +init_return: + return retval; +} + +static void __exit ibm_acpiphp_exit(void) +{ + acpi_status status; + struct kobject *sysdir = &pci_hotplug_slots_subsys.kset.kobj; + + dbg("%s\n", __FUNCTION__); + + if (acpiphp_unregister_attention(&ibm_attention_info)) + err("%s: attention info deregistration failed", __FUNCTION__); + + status = acpi_remove_notify_handler( + ibm_acpi_handle, + ACPI_DEVICE_NOTIFY, + ibm_handle_events); + if (ACPI_FAILURE(status)) + err("%s: Notification handler removal failed\n", + __FUNCTION__); + // remove the /sys entries + if (sysfs_remove_bin_file(sysdir, &ibm_apci_table_attr)) + err("%s: removal of sysfs file apci_table failed\n", + __FUNCTION__); +} + +module_init(ibm_acpiphp_init); +module_exit(ibm_acpiphp_exit); diff --git a/drivers/pci/pci-acpi.c b/drivers/pci/pci-acpi.c new file mode 100644 index 000000000..968eb32f2 --- /dev/null +++ b/drivers/pci/pci-acpi.c @@ -0,0 +1,209 @@ +/* + * File: pci-acpi.c + * Purpose: Provide PCI supports in ACPI + * + * Copyright (C) 2004 Intel + * Copyright (C) Tom Long Nguyen (tom.l.nguyen@intel.com) + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +static u32 ctrlset_buf[3] = {0, 0, 0}; +static u32 global_ctrlsets = 0; +u8 OSC_UUID[16] = {0x5B, 0x4D, 0xDB, 0x33, 0xF7, 0x1F, 0x1C, 0x40, 0x96, 0x57, 0x74, 0x41, 0xC0, 0x3D, 0xD7, 0x66}; + +static acpi_status +acpi_query_osc ( + acpi_handle handle, + u32 level, + void *context, + void **retval ) +{ + acpi_status status; + struct acpi_object_list input; + union acpi_object in_params[4]; + struct acpi_buffer output; + union acpi_object out_obj; + u32 osc_dw0; + + /* Setting up output buffer */ + output.length = sizeof(out_obj) + 3*sizeof(u32); + output.pointer = &out_obj; + + /* Setting up input parameters */ + input.count = 4; + input.pointer = in_params; + in_params[0].type = ACPI_TYPE_BUFFER; + in_params[0].buffer.length = 16; + in_params[0].buffer.pointer = OSC_UUID; + in_params[1].type = ACPI_TYPE_INTEGER; + in_params[1].integer.value = 1; + in_params[2].type = ACPI_TYPE_INTEGER; + in_params[2].integer.value = 3; + in_params[3].type = ACPI_TYPE_BUFFER; + in_params[3].buffer.length = 12; + in_params[3].buffer.pointer = (u8 *)context; + + status = acpi_evaluate_object(handle, "_OSC", &input, &output); + if (ACPI_FAILURE (status)) { + printk(KERN_DEBUG + "Evaluate _OSC Set fails. Status = 0x%04x\n", status); + return status; + } + if (out_obj.type != ACPI_TYPE_BUFFER) { + printk(KERN_DEBUG + "Evaluate _OSC returns wrong type\n"); + return AE_TYPE; + } + osc_dw0 = *((u32 *) out_obj.buffer.pointer); + if (osc_dw0) { + if (osc_dw0 & OSC_REQUEST_ERROR) + printk(KERN_DEBUG "_OSC request fails\n"); + if (osc_dw0 & OSC_INVALID_UUID_ERROR) + printk(KERN_DEBUG "_OSC invalid UUID\n"); + if (osc_dw0 & OSC_INVALID_REVISION_ERROR) + printk(KERN_DEBUG "_OSC invalid revision\n"); + if (osc_dw0 & OSC_CAPABILITIES_MASK_ERROR) { + /* Update Global Control Set */ + global_ctrlsets = *((u32 *)(out_obj.buffer.pointer+8)); + return AE_OK; + } + return AE_ERROR; + } + + /* Update Global Control Set */ + global_ctrlsets = *((u32 *)(out_obj.buffer.pointer + 8)); + return AE_OK; +} + + +static acpi_status +acpi_run_osc ( + acpi_handle handle, + u32 level, + void *context, + void **retval ) +{ + acpi_status status; + struct acpi_object_list input; + union acpi_object in_params[4]; + struct acpi_buffer output; + union acpi_object out_obj; + u32 osc_dw0; + + /* Setting up output buffer */ + output.length = sizeof(out_obj) + 3*sizeof(u32); + output.pointer = &out_obj; + + /* Setting up input parameters */ + input.count = 4; + input.pointer = in_params; + in_params[0].type = ACPI_TYPE_BUFFER; + in_params[0].buffer.length = 16; + in_params[0].buffer.pointer = OSC_UUID; + in_params[1].type = ACPI_TYPE_INTEGER; + in_params[1].integer.value = 1; + in_params[2].type = ACPI_TYPE_INTEGER; + in_params[2].integer.value = 3; + in_params[3].type = ACPI_TYPE_BUFFER; + in_params[3].buffer.length = 12; + in_params[3].buffer.pointer = (u8 *)context; + + status = acpi_evaluate_object(handle, "_OSC", &input, &output); + if (ACPI_FAILURE (status)) { + printk(KERN_DEBUG + "Evaluate _OSC Set fails. Status = 0x%04x\n", status); + return status; + } + if (out_obj.type != ACPI_TYPE_BUFFER) { + printk(KERN_DEBUG + "Evaluate _OSC returns wrong type\n"); + return AE_TYPE; + } + osc_dw0 = *((u32 *) out_obj.buffer.pointer); + if (osc_dw0) { + if (osc_dw0 & OSC_REQUEST_ERROR) + printk(KERN_DEBUG "_OSC request fails\n"); + if (osc_dw0 & OSC_INVALID_UUID_ERROR) + printk(KERN_DEBUG "_OSC invalid UUID\n"); + if (osc_dw0 & OSC_INVALID_REVISION_ERROR) + printk(KERN_DEBUG "_OSC invalid revision\n"); + if (osc_dw0 & OSC_CAPABILITIES_MASK_ERROR) { + printk(KERN_DEBUG "_OSC FW not grant req. control\n"); + return AE_SUPPORT; + } + return AE_ERROR; + } + return AE_OK; +} + +/** + * pci_osc_support_set - register OS support to Firmware + * @flags: OS support bits + * + * Update OS support fields and doing a _OSC Query to obtain an update + * from Firmware on supported control bits. + **/ +acpi_status pci_osc_support_set(u32 flags) +{ + u32 temp; + + if (!(flags & OSC_SUPPORT_MASKS)) { + return AE_TYPE; + } + ctrlset_buf[OSC_SUPPORT_TYPE] |= (flags & OSC_SUPPORT_MASKS); + + /* do _OSC query for all possible controls */ + temp = ctrlset_buf[OSC_CONTROL_TYPE]; + ctrlset_buf[OSC_QUERY_TYPE] = OSC_QUERY_ENABLE; + ctrlset_buf[OSC_CONTROL_TYPE] = OSC_CONTROL_MASKS; + acpi_get_devices ( PCI_ROOT_HID_STRING, + acpi_query_osc, + ctrlset_buf, + NULL ); + ctrlset_buf[OSC_QUERY_TYPE] = !OSC_QUERY_ENABLE; + ctrlset_buf[OSC_CONTROL_TYPE] = temp; + return AE_OK; +} +EXPORT_SYMBOL(pci_osc_support_set); + +/** + * pci_osc_control_set - commit requested control to Firmware + * @flags: driver's requested control bits + * + * Attempt to take control from Firmware on requested control bits. + **/ +acpi_status pci_osc_control_set(u32 flags) +{ + acpi_status status; + u32 ctrlset; + + ctrlset = (flags & OSC_CONTROL_MASKS); + if (!ctrlset) { + return AE_TYPE; + } + if (ctrlset_buf[OSC_SUPPORT_TYPE] && + ((global_ctrlsets & ctrlset) != ctrlset)) { + return AE_SUPPORT; + } + ctrlset_buf[OSC_CONTROL_TYPE] |= ctrlset; + status = acpi_get_devices ( PCI_ROOT_HID_STRING, + acpi_run_osc, + ctrlset_buf, + NULL ); + if (ACPI_FAILURE (status)) { + ctrlset_buf[OSC_CONTROL_TYPE] &= ~ctrlset; + } + + return status; +} +EXPORT_SYMBOL(pci_osc_control_set); diff --git a/drivers/pci/remove.c b/drivers/pci/remove.c index 5ef19b175..889b2e3ac 100644 --- a/drivers/pci/remove.c +++ b/drivers/pci/remove.c @@ -59,6 +59,18 @@ int pci_remove_device_safe(struct pci_dev *dev) } EXPORT_SYMBOL(pci_remove_device_safe); +void pci_remove_bus(struct pci_bus *b) +{ + pci_proc_detach_bus(b); + + spin_lock(&pci_bus_lock); + list_del(&b->node); + spin_unlock(&pci_bus_lock); + + class_device_unregister(&b->class_dev); +} +EXPORT_SYMBOL(pci_remove_bus); + /** * pci_remove_bus_device - remove a PCI device and any children * @dev: the device to remove @@ -77,13 +89,7 @@ void pci_remove_bus_device(struct pci_dev *dev) struct pci_bus *b = dev->subordinate; pci_remove_behind_bridge(dev); - pci_proc_detach_bus(b); - - spin_lock(&pci_bus_lock); - list_del(&b->node); - spin_unlock(&pci_bus_lock); - - class_device_unregister(&b->class_dev); + pci_remove_bus(b); dev->subordinate = NULL; } diff --git a/drivers/pci/setup-bus.c b/drivers/pci/setup-bus.c index 7f0f6c209..e21250c39 100644 --- a/drivers/pci/setup-bus.c +++ b/drivers/pci/setup-bus.c @@ -537,10 +537,11 @@ pci_assign_unassigned_resources(void) /* Depth first, calculate sizes and alignments of all subordinate buses. */ - for(ln=pci_root_buses.next; ln != &pci_root_buses; ln=ln->next) + list_for_each(ln, &pci_root_buses) { pci_bus_size_bridges(pci_bus_b(ln)); + } /* Depth last, allocate resources and update the hardware. */ - for(ln=pci_root_buses.next; ln != &pci_root_buses; ln=ln->next) { + list_for_each(ln, &pci_root_buses) { pci_bus_assign_resources(pci_bus_b(ln)); pci_enable_bridges(pci_bus_b(ln)); } diff --git a/drivers/pcmcia/au1000_generic.c b/drivers/pcmcia/au1000_generic.c index bd79f3bd1..124225edc 100644 --- a/drivers/pcmcia/au1000_generic.c +++ b/drivers/pcmcia/au1000_generic.c @@ -566,7 +566,6 @@ static int au1000_pcmcia_set_mem_map(unsigned int sock, struct pccard_mem_map *map) { unsigned int speed; - unsigned long start; u_long flags; if(map->map>=MAX_WIN){ @@ -588,25 +587,19 @@ au1000_pcmcia_set_mem_map(unsigned int sock, struct pccard_mem_map *map) } spin_lock_irqsave(&pcmcia_lock, flags); - start=map->sys_start; - - if(map->sys_stop==0) - map->sys_stop=MAP_SIZE-1; - if (map->flags & MAP_ATTRIB) { - map->sys_start = pcmcia_socket[sock].phys_attr + + map->static_start = pcmcia_socket[sock].phys_attr + map->card_start; } else { - map->sys_start = pcmcia_socket[sock].phys_mem + + map->static_start = pcmcia_socket[sock].phys_mem + map->card_start; } - map->sys_stop=map->sys_start+(map->sys_stop-start); pcmcia_socket[sock].mem_map[map->map]=*map; spin_unlock_irqrestore(&pcmcia_lock, flags); - debug(3, "set_mem_map %d start %x stop %x card_start %x\n", - map->map, map->sys_start, map->sys_stop, + debug(3, "set_mem_map %d start %x card_start %x\n", + map->map, map->static_start, map->card_start); return 0; diff --git a/drivers/pcmcia/hd64465_ss.c b/drivers/pcmcia/hd64465_ss.c index fd08c3a5b..875c9cfae 100644 --- a/drivers/pcmcia/hd64465_ss.c +++ b/drivers/pcmcia/hd64465_ss.c @@ -636,19 +636,17 @@ static int hs_set_mem_map(struct pcmcia_socket *s, struct pccard_mem_map *mem) hs_socket_t *sp = container_of(s, struct hs_socket_t, socket); struct pccard_mem_map *smem; int map = mem->map; - unsigned long paddr, size; + unsigned long paddr; #if 0 - DPRINTK("hs_set_mem_map(sock=%d, map=%d, flags=0x%x, sys_start=0x%08lx, sys_end=0x%08lx, card_start=0x%08x)\n", - sock, map, mem->flags, mem->sys_start, mem->sys_stop, mem->card_start); + DPRINTK("hs_set_mem_map(sock=%d, map=%d, flags=0x%x, card_start=0x%08x)\n", + sock, map, mem->flags, mem->card_start); #endif if (map >= MAX_WIN) return -EINVAL; smem = &sp->mem_maps[map]; - size = mem->sys_stop - mem->sys_start + 1; - paddr = sp->mem_base; /* base of Attribute mapping */ if (!(mem->flags & MAP_ATTRIB)) paddr += HD64465_PCC_WINDOW; /* base of Common mapping */ @@ -660,8 +658,7 @@ static int hs_set_mem_map(struct pcmcia_socket *s, struct pccard_mem_map *mem) * queries our fixed mapping. I wish this fact had been * documented - Greg Banks. */ - mem->sys_start = paddr; - mem->sys_stop = paddr + size - 1; + mem->static_start = paddr; *smem = *mem; diff --git a/drivers/pcmcia/pd6729.c b/drivers/pcmcia/pd6729.c index 57b1bca00..4f00c9dda 100644 --- a/drivers/pcmcia/pd6729.c +++ b/drivers/pcmcia/pd6729.c @@ -502,10 +502,10 @@ static int pd6729_set_mem_map(struct pcmcia_socket *sock, struct pccard_mem_map return -EINVAL; } - if ((mem->sys_start > mem->sys_stop) || (mem->speed > 1000)) { + if ((mem->res->start > mem->res->end) || (mem->speed > 1000)) { printk("pd6729_set_mem_map: invalid address / speed"); /* printk("invalid mem map for socket %i : %lx to %lx with a start of %x\n", - sock, mem->sys_start, mem->sys_stop, mem->card_start); */ + sock, mem->res->start, mem->res->end, mem->card_start); */ return -EINVAL; } @@ -515,7 +515,7 @@ static int pd6729_set_mem_map(struct pcmcia_socket *sock, struct pccard_mem_map /* write the start address */ base = I365_MEM(map); - i = (mem->sys_start >> 12) & 0x0fff; + i = (mem->res->start >> 12) & 0x0fff; if (mem->flags & MAP_16BIT) i |= I365_MEM_16BIT; if (mem->flags & MAP_0WS) @@ -524,7 +524,7 @@ static int pd6729_set_mem_map(struct pcmcia_socket *sock, struct pccard_mem_map /* write the stop address */ - i= (mem->sys_stop >> 12) & 0x0fff; + i= (mem->res->end >> 12) & 0x0fff; switch (to_cycles(mem->speed)) { case 0: break; @@ -543,11 +543,11 @@ static int pd6729_set_mem_map(struct pcmcia_socket *sock, struct pccard_mem_map /* Take care of high byte */ indirect_write(socket, PD67_EXT_INDEX, PD67_MEM_PAGE(map)); - indirect_write(socket, PD67_EXT_DATA, mem->sys_start >> 24); + indirect_write(socket, PD67_EXT_DATA, mem->res->start >> 24); /* card start */ - i = ((mem->card_start - mem->sys_start) >> 12) & 0x3fff; + i = ((mem->card_start - mem->res->start) >> 12) & 0x3fff; if (mem->flags & MAP_WRPROT) i |= I365_MEM_WRPROT; if (mem->flags & MAP_ATTRIB) { @@ -577,7 +577,7 @@ static int pd6729_init(struct pcmcia_socket *sock) int i; struct resource res = { .end = 0x0fff }; pccard_io_map io = { 0, 0, 0, 0, 1 }; - pccard_mem_map mem = { .res = &res, .sys_stop = 0x0fff }; + pccard_mem_map mem = { .res = &res, }; pd6729_set_socket(sock, &dead_socket); for (i = 0; i < 2; i++) { diff --git a/drivers/pcmcia/pxa2xx_base.c b/drivers/pcmcia/pxa2xx_base.c index 491d11bc8..2bdf9a004 100644 --- a/drivers/pcmcia/pxa2xx_base.c +++ b/drivers/pcmcia/pxa2xx_base.c @@ -28,6 +28,7 @@ #include #include #include +#include #include #include @@ -178,39 +179,6 @@ int pxa2xx_drv_pcmcia_probe(struct device *dev) first = ops->first; nr = ops->nr; - /* Setup GPIOs for PCMCIA/CF alternate function mode. - * - * It would be nice if set_GPIO_mode included support - * for driving GPIO outputs to default high/low state - * before programming GPIOs as outputs. Setting GPIO - * outputs to default high/low state via GPSR/GPCR - * before defining them as outputs should reduce - * the possibility of glitching outputs during GPIO - * setup. This of course assumes external terminators - * are present to hold GPIOs in a defined state. - * - * In the meantime, setup default state of GPIO - * outputs before we enable them as outputs. - */ - - GPSR(GPIO48_nPOE) = GPIO_bit(GPIO48_nPOE) | - GPIO_bit(GPIO49_nPWE) | - GPIO_bit(GPIO50_nPIOR) | - GPIO_bit(GPIO51_nPIOW) | - GPIO_bit(GPIO52_nPCE_1) | - GPIO_bit(GPIO53_nPCE_2); - - pxa_gpio_mode(GPIO48_nPOE_MD); - pxa_gpio_mode(GPIO49_nPWE_MD); - pxa_gpio_mode(GPIO50_nPIOR_MD); - pxa_gpio_mode(GPIO51_nPIOW_MD); - pxa_gpio_mode(GPIO52_nPCE_1_MD); - pxa_gpio_mode(GPIO53_nPCE_2_MD); - pxa_gpio_mode(GPIO54_pSKTSEL_MD); /* REVISIT: s/b dependent on num sockets */ - pxa_gpio_mode(GPIO55_nPREG_MD); - pxa_gpio_mode(GPIO56_nPWAIT_MD); - pxa_gpio_mode(GPIO57_nIOIS16_MD); - /* Provide our PXA2xx specific timing routines. */ ops->set_timing = pxa2xx_pcmcia_set_timing; #ifdef CONFIG_CPU_FREQ diff --git a/drivers/pcmcia/pxa2xx_lubbock.c b/drivers/pcmcia/pxa2xx_lubbock.c index e22a57a4a..48726e739 100644 --- a/drivers/pcmcia/pxa2xx_lubbock.c +++ b/drivers/pcmcia/pxa2xx_lubbock.c @@ -25,10 +25,40 @@ #include #include #include +#include #include #include "sa1111_generic.h" +static int +lubbock_pcmcia_hw_init(struct soc_pcmcia_socket *skt) +{ + /* + * Setup default state of GPIO outputs + * before we enable them as outputs. + */ + GPSR(GPIO48_nPOE) = + GPIO_bit(GPIO48_nPOE) | + GPIO_bit(GPIO49_nPWE) | + GPIO_bit(GPIO50_nPIOR) | + GPIO_bit(GPIO51_nPIOW) | + GPIO_bit(GPIO52_nPCE_1) | + GPIO_bit(GPIO53_nPCE_2); + + pxa_gpio_mode(GPIO48_nPOE_MD); + pxa_gpio_mode(GPIO49_nPWE_MD); + pxa_gpio_mode(GPIO50_nPIOR_MD); + pxa_gpio_mode(GPIO51_nPIOW_MD); + pxa_gpio_mode(GPIO52_nPCE_1_MD); + pxa_gpio_mode(GPIO53_nPCE_2_MD); + pxa_gpio_mode(GPIO54_pSKTSEL_MD); + pxa_gpio_mode(GPIO55_nPREG_MD); + pxa_gpio_mode(GPIO56_nPWAIT_MD); + pxa_gpio_mode(GPIO57_nIOIS16_MD); + + return sa1111_pcmcia_hw_init(skt); +} + static int lubbock_pcmcia_configure_socket(struct soc_pcmcia_socket *skt, const socket_state_t *state) @@ -201,7 +231,7 @@ lubbock_pcmcia_configure_socket(struct soc_pcmcia_socket *skt, static struct pcmcia_low_level lubbock_pcmcia_ops = { .owner = THIS_MODULE, - .hw_init = sa1111_pcmcia_hw_init, + .hw_init = lubbock_pcmcia_hw_init, .hw_shutdown = sa1111_pcmcia_hw_shutdown, .socket_state = sa1111_pcmcia_socket_state, .configure_socket = lubbock_pcmcia_configure_socket, diff --git a/drivers/pcmcia/pxa2xx_mainstone.c b/drivers/pcmcia/pxa2xx_mainstone.c index fe62a597d..5309734e1 100644 --- a/drivers/pcmcia/pxa2xx_mainstone.c +++ b/drivers/pcmcia/pxa2xx_mainstone.c @@ -24,6 +24,7 @@ #include #include +#include #include #include "soc_common.h" @@ -38,6 +39,29 @@ static struct pcmcia_irqs irqs[] = { static int mst_pcmcia_hw_init(struct soc_pcmcia_socket *skt) { + /* + * Setup default state of GPIO outputs + * before we enable them as outputs. + */ + GPSR(GPIO48_nPOE) = + GPIO_bit(GPIO48_nPOE) | + GPIO_bit(GPIO49_nPWE) | + GPIO_bit(GPIO50_nPIOR) | + GPIO_bit(GPIO51_nPIOW) | + GPIO_bit(GPIO85_nPCE_1) | + GPIO_bit(GPIO54_nPCE_2); + + pxa_gpio_mode(GPIO48_nPOE_MD); + pxa_gpio_mode(GPIO49_nPWE_MD); + pxa_gpio_mode(GPIO50_nPIOR_MD); + pxa_gpio_mode(GPIO51_nPIOW_MD); + pxa_gpio_mode(GPIO85_nPCE_1_MD); + pxa_gpio_mode(GPIO54_nPCE_2_MD); + pxa_gpio_mode(GPIO79_pSKTSEL_MD); + pxa_gpio_mode(GPIO55_nPREG_MD); + pxa_gpio_mode(GPIO56_nPWAIT_MD); + pxa_gpio_mode(GPIO57_nIOIS16_MD); + skt->irq = (skt->nr == 0) ? MAINSTONE_S0_IRQ : MAINSTONE_S1_IRQ; return soc_pcmcia_request_irqs(skt, irqs, ARRAY_SIZE(irqs)); } diff --git a/drivers/pcmcia/soc_common.c b/drivers/pcmcia/soc_common.c index 6d393e739..a45fee3f4 100644 --- a/drivers/pcmcia/soc_common.c +++ b/drivers/pcmcia/soc_common.c @@ -49,6 +49,11 @@ #include "soc_common.h" +/* FIXME: platform dependent resource declaration has to move out of this file */ +#ifdef CONFIG_ARCH_PXA +#include +#endif + #ifdef DEBUG static int pc_debug; @@ -448,9 +453,7 @@ soc_common_pcmcia_set_mem_map(struct pcmcia_socket *sock, struct pccard_mem_map skt->ops->set_timing(skt); - map->sys_stop -= map->sys_start; - map->sys_stop += res->start + map->card_start; - map->sys_start = res->start + map->card_start; + map->static_start = res->start + map->card_start; return 0; } @@ -662,6 +665,7 @@ static void soc_pcmcia_cpufreq_unregister(void) int soc_common_drv_pcmcia_probe(struct device *dev, struct pcmcia_low_level *ops, int first, int nr) { struct skt_dev_info *sinfo; + struct soc_pcmcia_socket *skt; int ret, i; down(&soc_pcmcia_sockets_lock); @@ -679,7 +683,7 @@ int soc_common_drv_pcmcia_probe(struct device *dev, struct pcmcia_low_level *ops * Initialise the per-socket structure. */ for (i = 0; i < nr; i++) { - struct soc_pcmcia_socket *skt = &sinfo->skt[i]; + skt = &sinfo->skt[i]; skt->socket.ops = &soc_common_pcmcia_operations; skt->socket.owner = ops->owner; @@ -777,7 +781,7 @@ int soc_common_drv_pcmcia_probe(struct device *dev, struct pcmcia_low_level *ops goto out; do { - struct soc_pcmcia_socket *skt = &sinfo->skt[i]; + skt = &sinfo->skt[i]; del_timer_sync(&skt->poll_timer); pcmcia_unregister_socket(&skt->socket); diff --git a/drivers/pcmcia/yenta_socket.h b/drivers/pcmcia/yenta_socket.h index 7c3bfbfaa..7f04a6b73 100644 --- a/drivers/pcmcia/yenta_socket.h +++ b/drivers/pcmcia/yenta_socket.h @@ -107,7 +107,7 @@ struct cardbus_type { struct yenta_socket { struct pci_dev *dev; int cb_irq, io_irq; - void *base; + void __iomem *base; struct timer_list poll_timer; struct pcmcia_socket socket; diff --git a/drivers/pnp/interface.c b/drivers/pnp/interface.c index 781a4c064..f7548c410 100644 --- a/drivers/pnp/interface.c +++ b/drivers/pnp/interface.c @@ -240,12 +240,14 @@ static ssize_t pnp_show_current_resources(struct device *dmdev, char *buf) { struct pnp_dev *dev = to_pnp_dev(dmdev); int i, ret; - pnp_info_buffer_t *buffer = (pnp_info_buffer_t *) - pnp_alloc(sizeof(pnp_info_buffer_t)); - if (!buffer) - return -ENOMEM; + pnp_info_buffer_t *buffer; + if (!dev) return -EINVAL; + + buffer = (pnp_info_buffer_t *) pnp_alloc(sizeof(pnp_info_buffer_t)); + if (!buffer) + return -ENOMEM; buffer->len = PAGE_SIZE; buffer->buffer = buf; buffer->curr = buffer->buffer; diff --git a/drivers/pnp/pnpbios/proc.c b/drivers/pnp/pnpbios/proc.c index 88fcd1668..2fa4d21c7 100644 --- a/drivers/pnp/pnpbios/proc.c +++ b/drivers/pnp/pnpbios/proc.c @@ -90,8 +90,10 @@ static int proc_read_escd(char *buf, char **start, off_t pos, tmpbuf = pnpbios_kmalloc(escd.escd_size, GFP_KERNEL); if (!tmpbuf) return -ENOMEM; - if (pnp_bios_read_escd(tmpbuf, escd.nv_storage_base)) + if (pnp_bios_read_escd(tmpbuf, escd.nv_storage_base)) { + kfree(tmpbuf); return -EIO; + } escd_size = (unsigned char)(tmpbuf[0]) + (unsigned char)(tmpbuf[1])*256; @@ -168,8 +170,10 @@ static int proc_read_node(char *buf, char **start, off_t pos, node = pnpbios_kmalloc(node_info.max_node_size, GFP_KERNEL); if (!node) return -ENOMEM; - if (pnp_bios_get_dev_node(&nodenum, boot, node)) + if (pnp_bios_get_dev_node(&nodenum, boot, node)) { + kfree(node); return -EIO; + } len = node->size - sizeof(struct pnp_bios_node); memcpy(buf, node->data, len); kfree(node); diff --git a/drivers/pnp/pnpbios/rsparser.c b/drivers/pnp/pnpbios/rsparser.c index f990065e1..7aafbf80d 100644 --- a/drivers/pnp/pnpbios/rsparser.c +++ b/drivers/pnp/pnpbios/rsparser.c @@ -346,12 +346,12 @@ pnpbios_parse_resource_option_data(unsigned char * p, unsigned char * end, struc { unsigned int len, tag; int priority = 0; - struct pnp_option *option; + struct pnp_option *option, *option_independent; if (!p) return NULL; - option = pnp_register_independent_option(dev); + option_independent = option = pnp_register_independent_option(dev); if (!option) return NULL; @@ -428,9 +428,14 @@ pnpbios_parse_resource_option_data(unsigned char * p, unsigned char * end, struc case SMALL_TAG_ENDDEP: if (len != 0) goto len_err; + if (option_independent == option) + printk(KERN_WARNING "PnPBIOS: Missing SMALL_TAG_STARTDEP tag\n"); + option = option_independent; break; case SMALL_TAG_END: + if (option_independent != option) + printk(KERN_WARNING "PnPBIOS: Missing SMALL_TAG_ENDDEP tag\n"); p = p + 2; return (unsigned char *)p; break; diff --git a/drivers/pnp/system.c b/drivers/pnp/system.c index da52c86dc..d42015c38 100644 --- a/drivers/pnp/system.c +++ b/drivers/pnp/system.c @@ -104,4 +104,8 @@ static int __init pnp_system_init(void) return pnp_register_driver(&system_pnp_driver); } -subsys_initcall(pnp_system_init); +/** + * Reserve motherboard resources after PCI claim BARs, + * but before PCI assign resources for uninitialized PCI devices + */ +fs_initcall(pnp_system_init); diff --git a/drivers/s390/block/dcssblk.c b/drivers/s390/block/dcssblk.c index ca83f9b0a..b7a764ca3 100644 --- a/drivers/s390/block/dcssblk.c +++ b/drivers/s390/block/dcssblk.c @@ -5,6 +5,7 @@ */ #include +#include #include #include #include @@ -19,6 +20,7 @@ //#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) @@ -34,6 +36,8 @@ static int dcssblk_open(struct inode *inode, struct file *filp); static int dcssblk_release(struct inode *inode, struct file *filp); static int dcssblk_make_request(struct request_queue *q, struct bio *bio); +static char dcssblk_segments[DCSSBLK_PARM_LEN] = "\0"; + static int dcssblk_major; static struct block_device_operations dcssblk_devops = { .owner = THIS_MODULE, @@ -641,6 +645,47 @@ fail: return 0; } +static void +dcssblk_check_params(void) +{ + int rc, i, j, k; + char buf[9]; + struct dcssblk_dev_info *dev_info; + + for (i = 0; (i < DCSSBLK_PARM_LEN) && (dcssblk_segments[i] != '\0'); + i++) { + for (j = i; (dcssblk_segments[j] != ',') && + (dcssblk_segments[j] != '\0') && + (dcssblk_segments[j] != '(') && + (j - i) < 8; j++) + { + buf[j-i] = dcssblk_segments[j]; + } + buf[j-i] = '\0'; + rc = dcssblk_add_store(dcssblk_root_dev, buf, j-i); + if ((rc >= 0) && (dcssblk_segments[j] == '(')) { + for (k = 0; buf[k] != '\0'; k++) + buf[k] = toupper(buf[k]); + if (!strncmp(&dcssblk_segments[j], "(local)", 7)) { + down_read(&dcssblk_devices_sem); + dev_info = dcssblk_get_device_by_name(buf); + up_read(&dcssblk_devices_sem); + if (dev_info) + dcssblk_shared_store(&dev_info->dev, + "0\n", 2); + } + } + while ((dcssblk_segments[j] != ',') && + (dcssblk_segments[j] != '\0')) + { + j++; + } + if (dcssblk_segments[j] == '\0') + break; + i = j; + } +} + /* * The init/exit functions. */ @@ -689,6 +734,9 @@ dcssblk_init(void) } dcssblk_major = rc; init_rwsem(&dcssblk_devices_sem); + + dcssblk_check_params(); + PRINT_DEBUG("...finished!\n"); return 0; } @@ -696,4 +744,12 @@ dcssblk_init(void) module_init(dcssblk_init); module_exit(dcssblk_exit); +module_param_string(segments, dcssblk_segments, DCSSBLK_PARM_LEN, 0444); +MODULE_PARM_DESC(segments, "Name of DCSS segment(s) to be loaded, " + "comma-separated list, each name max. 8 chars.\n" + "Adding \"(local)\" to segment name equals echoing 0 to " + "/sys/devices/dcssblk//shared after loading " + "the segment - \n" + "e.g. segments=\"mydcss1,mydcss2,mydcss3(local)\""); + MODULE_LICENSE("GPL"); diff --git a/drivers/s390/char/con3215.c b/drivers/s390/char/con3215.c index e92bb9473..9c72ffb65 100644 --- a/drivers/s390/char/con3215.c +++ b/drivers/s390/char/con3215.c @@ -366,10 +366,7 @@ raw3215_tasklet(void *data) tty = raw->tty; if (tty != NULL && RAW3215_BUFFER_SIZE - raw->count >= RAW3215_MIN_SPACE) { - if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && - tty->ldisc.write_wakeup) - (tty->ldisc.write_wakeup)(tty); - wake_up_interruptible(&tty->write_wait); + tty_wakeup(tty); } } @@ -1055,10 +1052,7 @@ tty3215_flush_buffer(struct tty_struct *tty) raw = (struct raw3215_info *) tty->driver_data; raw3215_flush_buffer(raw); - wake_up_interruptible(&tty->write_wait); - if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && - tty->ldisc.write_wakeup) - (tty->ldisc.write_wakeup)(tty); + tty_wakeup(tty); } /* diff --git a/drivers/s390/char/sclp_tty.c b/drivers/s390/char/sclp_tty.c index a1ad37580..cd11cb171 100644 --- a/drivers/s390/char/sclp_tty.c +++ b/drivers/s390/char/sclp_tty.c @@ -277,10 +277,7 @@ sclp_ttybuf_callback(struct sclp_buffer *buffer, int rc) wake_up(&sclp_tty_waitq); /* check if the tty needs a wake up call */ if (sclp_tty != NULL) { - if ((sclp_tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && - sclp_tty->ldisc.write_wakeup) - (sclp_tty->ldisc.write_wakeup)(sclp_tty); - wake_up_interruptible(&sclp_tty->write_wait); + tty_wakeup(sclp_tty); } } diff --git a/drivers/s390/char/sclp_vt220.c b/drivers/s390/char/sclp_vt220.c index 22760a120..cc143fa9f 100644 --- a/drivers/s390/char/sclp_vt220.c +++ b/drivers/s390/char/sclp_vt220.c @@ -139,10 +139,7 @@ sclp_vt220_process_queue(struct sclp_vt220_request *request) wake_up(&sclp_vt220_waitq); /* Check if the tty needs a wake up call */ if (sclp_vt220_tty != NULL) { - if ((sclp_vt220_tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && - (sclp_vt220_tty->ldisc.write_wakeup != NULL)) - (sclp_vt220_tty->ldisc.write_wakeup)(sclp_vt220_tty); - wake_up_interruptible(&sclp_vt220_tty->write_wait); + tty_wakeup(sclp_vt220_tty); } } diff --git a/drivers/s390/cio/blacklist.c b/drivers/s390/cio/blacklist.c index 29000d5e7..4a06c7d0e 100644 --- a/drivers/s390/cio/blacklist.c +++ b/drivers/s390/cio/blacklist.c @@ -1,7 +1,7 @@ /* * drivers/s390/cio/blacklist.c * S/390 common I/O routines -- blacklisting of specific devices - * $Revision: 1.31 $ + * $Revision: 1.33 $ * * Copyright (C) 1999-2002 IBM Deutschland Entwicklung GmbH, * IBM Corporation @@ -229,7 +229,7 @@ s390_redo_validation (void) unsigned int irq; CIO_TRACE_EVENT (0, "redoval"); - for (irq = 0; irq <= __MAX_SUBCHANNELS; irq++) { + for (irq = 0; irq < __MAX_SUBCHANNELS; irq++) { int ret; struct subchannel *sch; diff --git a/drivers/s390/cio/ccwgroup.c b/drivers/s390/cio/ccwgroup.c index 9b86522fb..21a75ee28 100644 --- a/drivers/s390/cio/ccwgroup.c +++ b/drivers/s390/cio/ccwgroup.c @@ -1,7 +1,7 @@ /* * drivers/s390/cio/ccwgroup.c * bus driver for ccwgroup - * $Revision: 1.28 $ + * $Revision: 1.29 $ * * Copyright (C) 2002 IBM Deutschland Entwicklung GmbH, * IBM Corporation @@ -191,14 +191,12 @@ ccwgroup_create(struct device *root, gdev->cdev[i]->dev.driver_data = gdev; del_drvdata = 1; - *gdev = (struct ccwgroup_device) { - .creator_id = creator_id, - .count = argc, - .dev = { - .bus = &ccwgroup_bus_type, - .parent = root, - .release = ccwgroup_release, - }, + gdev->creator_id = creator_id; + gdev->count = argc; + gdev->dev = (struct device ) { + .bus = &ccwgroup_bus_type, + .parent = root, + .release = ccwgroup_release, }; snprintf (gdev->dev.bus_id, BUS_ID_SIZE, "%s", diff --git a/drivers/s390/s390mach.h b/drivers/s390/s390mach.h index 167043492..7e26f0f1b 100644 --- a/drivers/s390/s390mach.h +++ b/drivers/s390/s390mach.h @@ -63,10 +63,6 @@ struct crw { #define CRW_ERC_PERRI 0x07 /* perm. error, facility init */ #define CRW_ERC_PMOD 0x08 /* installed parameters modified */ -#define MCHCHK_STATUS_TO_PROCESS 0x00000001 -#define MCHCHK_STATUS_IN_PROGRESS 0x00000002 -#define MCHCHK_STATUS_WAITING 0x00000004 - extern __inline__ int stcrw(struct crw *pcrw ) { int ccode; diff --git a/drivers/s390/scsi/zfcp_fsf.h b/drivers/s390/scsi/zfcp_fsf.h index 879313864..3ad0ace82 100644 --- a/drivers/s390/scsi/zfcp_fsf.h +++ b/drivers/s390/scsi/zfcp_fsf.h @@ -84,19 +84,12 @@ #define FSF_SERVICE_CLASS_NOT_SUPPORTED 0x00000006 #define FSF_FCPLUN_NOT_VALID 0x00000009 #define FSF_ACCESS_DENIED 0x00000010 -#define FSF_ACCESS_TYPE_NOT_VALID 0x00000011 #define FSF_LUN_SHARING_VIOLATION 0x00000012 -#define FSF_COMMAND_ABORTED_ULP 0x00000020 -#define FSF_COMMAND_ABORTED_ADAPTER 0x00000021 #define FSF_FCP_COMMAND_DOES_NOT_EXIST 0x00000022 #define FSF_DIRECTION_INDICATOR_NOT_VALID 0x00000030 -#define FSF_INBOUND_DATA_LENGTH_NOT_VALID 0x00000031 /* FIX: obsolete? */ -#define FSF_OUTBOUND_DATA_LENGTH_NOT_VALID 0x00000032 /* FIX: obsolete? */ #define FSF_CMND_LENGTH_NOT_VALID 0x00000033 #define FSF_MAXIMUM_NUMBER_OF_PORTS_EXCEEDED 0x00000040 #define FSF_MAXIMUM_NUMBER_OF_LUNS_EXCEEDED 0x00000041 -#define FSF_REQUEST_BUF_NOT_VALID 0x00000042 -#define FSF_RESPONSE_BUF_NOT_VALID 0x00000043 #define FSF_ELS_COMMAND_REJECTED 0x00000050 #define FSF_GENERIC_COMMAND_REJECTED 0x00000051 #define FSF_OPERATION_PARTIALLY_SUCCESSFUL 0x00000052 @@ -227,6 +220,10 @@ #define FSF_HBA_PORTSTATE_LINKDOWN 0x00000006 #define FSF_HBA_PORTSTATE_ERROR 0x00000007 +/* IO states of adapter */ +#define FSF_IOSTAT_NPORT_RJT 0x00000004 +#define FSF_IOSTAT_FABRIC_RJT 0x00000005 +#define FSF_IOSTAT_LS_RJT 0x00000009 struct fsf_queue_designator; struct fsf_status_read_buffer; diff --git a/drivers/sbus/char/aurora.c b/drivers/sbus/char/aurora.c index d45619f3b..1ff81bec8 100644 --- a/drivers/sbus/char/aurora.c +++ b/drivers/sbus/char/aurora.c @@ -1531,8 +1531,7 @@ static void aurora_close(struct tty_struct * tty, struct file * filp) aurora_shutdown_port(bp, port); if (tty->driver->flush_buffer) tty->driver->flush_buffer(tty); - if (tty->ldisc.flush_buffer) - tty->ldisc.flush_buffer(tty); + tty_ldisc_flush(tty); tty->closing = 0; port->event = 0; port->tty = 0; @@ -1743,10 +1742,7 @@ static void aurora_flush_buffer(struct tty_struct *tty) port->xmit_cnt = port->xmit_head = port->xmit_tail = 0; restore_flags(flags); - wake_up_interruptible(&tty->write_wait); - if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && - tty->ldisc.write_wakeup) - (tty->ldisc.write_wakeup)(tty); + tty_wakeup(tty); #ifdef AURORA_DEBUG printk("aurora_flush_buffer: end\n"); #endif @@ -2223,10 +2219,7 @@ static void do_softint(void *private_) return; if (test_and_clear_bit(RS_EVENT_WRITE_WAKEUP, &port->event)) { - if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && - tty->ldisc.write_wakeup) - (tty->ldisc.write_wakeup)(tty); - wake_up_interruptible(&tty->write_wait); + tty_wakeup(tty); } #ifdef AURORA_DEBUG printk("do_softint: end\n"); diff --git a/drivers/sbus/char/bbc_envctrl.c b/drivers/sbus/char/bbc_envctrl.c index 37189ef06..d5259f7fe 100644 --- a/drivers/sbus/char/bbc_envctrl.c +++ b/drivers/sbus/char/bbc_envctrl.c @@ -452,7 +452,7 @@ static void fans_full_blast(void) } } -#define POLL_INTERVAL (5 * HZ) +#define POLL_INTERVAL (5 * 1000) static unsigned long last_warning_jiffies; static struct task_struct *kenvctrld_task; @@ -468,8 +468,7 @@ static int kenvctrld(void *__unused) struct bbc_cpu_temperature *tp; struct bbc_fan_control *fp; - current->state = TASK_INTERRUPTIBLE; - schedule_timeout(POLL_INTERVAL); + msleep_interruptible(POLL_INTERVAL); if (signal_pending(current)) break; diff --git a/drivers/sbus/char/bbc_i2c.c b/drivers/sbus/char/bbc_i2c.c index a489c544f..2dcd3cd87 100644 --- a/drivers/sbus/char/bbc_i2c.c +++ b/drivers/sbus/char/bbc_i2c.c @@ -189,13 +189,13 @@ static int wait_for_pin(struct bbc_i2c_bus *bp, u8 *status) while (limit-- > 0) { u8 val; - current->state = TASK_INTERRUPTIBLE; + set_current_state(TASK_INTERRUPTIBLE); *status = val = readb(bp->i2c_control_regs + 0); if ((val & I2C_PCF_PIN) == 0) { ret = 0; break; } - schedule_timeout(HZ/4); + msleep_interruptible(250); } remove_wait_queue(&bp->wq, &wait); bp->waiting = 0; diff --git a/drivers/scsi/3w-9xxx.c b/drivers/scsi/3w-9xxx.c index 2bac40c0e..0d78a7dee 100644 --- a/drivers/scsi/3w-9xxx.c +++ b/drivers/scsi/3w-9xxx.c @@ -73,7 +73,7 @@ #include "3w-9xxx.h" /* Globals */ -static const char *twa_driver_version="2.26.02.001"; +#define TWA_DRIVER_VERSION "2.26.02.001" static TW_Device_Extension *twa_device_extension_list[TW_MAX_SLOT]; static unsigned int twa_device_extension_count; static int twa_major = -1; @@ -83,6 +83,7 @@ extern struct timezone sys_tz; MODULE_AUTHOR ("AMCC"); MODULE_DESCRIPTION ("3ware 9000 Storage Controller Linux Driver"); MODULE_LICENSE("GPL"); +MODULE_VERSION(TWA_DRIVER_VERSION); /* Function prototypes */ static void twa_aen_queue_event(TW_Device_Extension *tw_dev, TW_Command_Apache_Header *header); @@ -138,7 +139,7 @@ static ssize_t twa_show_stats(struct class_device *class_dev, char *buf) "SCSI Host Resets: %4d\n" "SCSI Aborts/Timeouts: %4d\n" "AEN's: %4d\n", - twa_driver_version, + TWA_DRIVER_VERSION, tw_dev->posted_request_count, tw_dev->max_posted_request_count, tw_dev->pending_request_count, @@ -716,7 +717,7 @@ static int twa_chrdev_ioctl(struct inode *inode, struct file *file, unsigned int tw_ioctl->driver_command.status = 0; /* Copy compatiblity struct into ioctl data buffer */ tw_compat_info = (TW_Compatibility_Info *)tw_ioctl->data_buffer; - strncpy(tw_compat_info->driver_version, twa_driver_version, strlen(twa_driver_version)); + strncpy(tw_compat_info->driver_version, TWA_DRIVER_VERSION, strlen(TWA_DRIVER_VERSION)); tw_compat_info->working_srl = tw_dev->working_srl; tw_compat_info->working_branch = tw_dev->working_branch; tw_compat_info->working_build = tw_dev->working_build; @@ -1746,6 +1747,7 @@ static int twa_scsi_queue(struct scsi_cmnd *SCpnt, void (*done)(struct scsi_cmnd twa_free_request_id(tw_dev, request_id); SCpnt->result = (DID_ERROR << 16); done(SCpnt); + retval = 0; } return retval; @@ -2138,7 +2140,7 @@ static struct pci_driver twa_driver = { /* This function is called on driver initialization */ static int __init twa_init(void) { - printk(KERN_WARNING "3ware 9000 Storage Controller device driver for Linux v%s.\n", twa_driver_version); + printk(KERN_WARNING "3ware 9000 Storage Controller device driver for Linux v%s.\n", TWA_DRIVER_VERSION); return pci_module_init(&twa_driver); } /* End twa_init() */ diff --git a/drivers/scsi/BusLogic.h b/drivers/scsi/BusLogic.h index 2dd778f57..facbce6da 100644 --- a/drivers/scsi/BusLogic.h +++ b/drivers/scsi/BusLogic.h @@ -128,17 +128,15 @@ Define the Driver Message Levels. */ -enum BusLogic_MessageLevel -{ - BusLogic_AnnounceLevel = 0, - BusLogic_InfoLevel = 1, - BusLogic_NoticeLevel = 2, - BusLogic_WarningLevel = 3, - BusLogic_ErrorLevel = 4 +enum BusLogic_MessageLevel { + BusLogic_AnnounceLevel = 0, + BusLogic_InfoLevel = 1, + BusLogic_NoticeLevel = 2, + BusLogic_WarningLevel = 3, + BusLogic_ErrorLevel = 4 }; -static char *BusLogic_MessageLevelMap[] = - { KERN_NOTICE, KERN_NOTICE, KERN_NOTICE, KERN_WARNING, KERN_ERR }; +static char *BusLogic_MessageLevelMap[] = { KERN_NOTICE, KERN_NOTICE, KERN_NOTICE, KERN_WARNING, KERN_ERR }; /* @@ -166,17 +164,15 @@ static char *BusLogic_MessageLevelMap[] = of I/O Addresses required by each type. */ -enum BusLogic_HostAdapterType -{ - BusLogic_MultiMaster = 1, - BusLogic_FlashPoint = 2 +enum BusLogic_HostAdapterType { + BusLogic_MultiMaster = 1, + BusLogic_FlashPoint = 2 } PACKED; #define BusLogic_MultiMasterAddressCount 4 #define BusLogic_FlashPointAddressCount 256 -static int BusLogic_HostAdapterAddressCount[3] = - { 0, BusLogic_MultiMasterAddressCount, BusLogic_FlashPointAddressCount }; +static int BusLogic_HostAdapterAddressCount[3] = { 0, BusLogic_MultiMasterAddressCount, BusLogic_FlashPointAddressCount }; /* @@ -206,39 +202,35 @@ static int BusLogic_HostAdapterAddressCount[3] = Define the possible Host Adapter Bus Types. */ -enum BusLogic_HostAdapterBusType -{ - BusLogic_Unknown_Bus = 0, - BusLogic_ISA_Bus = 1, - BusLogic_EISA_Bus = 2, - BusLogic_PCI_Bus = 3, - BusLogic_VESA_Bus = 4, - BusLogic_MCA_Bus = 5 +enum BusLogic_HostAdapterBusType { + BusLogic_Unknown_Bus = 0, + BusLogic_ISA_Bus = 1, + BusLogic_EISA_Bus = 2, + BusLogic_PCI_Bus = 3, + BusLogic_VESA_Bus = 4, + BusLogic_MCA_Bus = 5 } PACKED; -static char *BusLogic_HostAdapterBusNames[] = - { "Unknown", "ISA", "EISA", "PCI", "VESA", "MCA" }; +static char *BusLogic_HostAdapterBusNames[] = { "Unknown", "ISA", "EISA", "PCI", "VESA", "MCA" }; -static enum BusLogic_HostAdapterBusType BusLogic_HostAdapterBusTypes[] = -{ - BusLogic_VESA_Bus, /* BT-4xx */ - BusLogic_ISA_Bus, /* BT-5xx */ - BusLogic_MCA_Bus, /* BT-6xx */ - BusLogic_EISA_Bus, /* BT-7xx */ - BusLogic_Unknown_Bus, /* BT-8xx */ - BusLogic_PCI_Bus /* BT-9xx */ +static enum BusLogic_HostAdapterBusType BusLogic_HostAdapterBusTypes[] = { + BusLogic_VESA_Bus, /* BT-4xx */ + BusLogic_ISA_Bus, /* BT-5xx */ + BusLogic_MCA_Bus, /* BT-6xx */ + BusLogic_EISA_Bus, /* BT-7xx */ + BusLogic_Unknown_Bus, /* BT-8xx */ + BusLogic_PCI_Bus /* BT-9xx */ }; /* Define the possible Host Adapter BIOS Disk Geometry Translations. */ -enum BusLogic_BIOS_DiskGeometryTranslation -{ - BusLogic_BIOS_Disk_Not_Installed = 0, - BusLogic_BIOS_Disk_Installed_64x32 = 1, - BusLogic_BIOS_Disk_Installed_128x32 = 2, - BusLogic_BIOS_Disk_Installed_255x63 = 3 +enum BusLogic_BIOS_DiskGeometryTranslation { + BusLogic_BIOS_Disk_Not_Installed = 0, + BusLogic_BIOS_Disk_Installed_64x32 = 1, + BusLogic_BIOS_Disk_Installed_128x32 = 2, + BusLogic_BIOS_Disk_Installed_255x63 = 3 } PACKED; @@ -247,18 +239,17 @@ enum BusLogic_BIOS_DiskGeometryTranslation */ typedef enum { - false, - true + false, + true } PACKED boolean; /* Define a 10^18 Statistics Byte Counter data type. */ -struct BusLogic_ByteCounter -{ - unsigned int Units; - unsigned int Billions; +struct BusLogic_ByteCounter { + unsigned int Units; + unsigned int Billions; }; @@ -266,58 +257,54 @@ struct BusLogic_ByteCounter Define the structure for I/O Address and Bus Probing Information. */ -struct BusLogic_ProbeInfo -{ - enum BusLogic_HostAdapterType HostAdapterType; - enum BusLogic_HostAdapterBusType HostAdapterBusType; - unsigned long IO_Address; - unsigned long PCI_Address; - struct pci_dev *PCI_Device; - unsigned char Bus; - unsigned char Device; - unsigned char IRQ_Channel; +struct BusLogic_ProbeInfo { + enum BusLogic_HostAdapterType HostAdapterType; + enum BusLogic_HostAdapterBusType HostAdapterBusType; + unsigned long IO_Address; + unsigned long PCI_Address; + struct pci_dev *PCI_Device; + unsigned char Bus; + unsigned char Device; + unsigned char IRQ_Channel; }; /* Define the Probe Options. */ -struct BusLogic_ProbeOptions -{ - boolean NoProbe:1; /* Bit 0 */ - boolean NoProbeISA:1; /* Bit 1 */ - boolean NoProbePCI:1; /* Bit 2 */ - boolean NoSortPCI:1; /* Bit 3 */ - boolean MultiMasterFirst:1; /* Bit 4 */ - boolean FlashPointFirst:1; /* Bit 5 */ - boolean LimitedProbeISA:1; /* Bit 6 */ - boolean Probe330:1; /* Bit 7 */ - boolean Probe334:1; /* Bit 8 */ - boolean Probe230:1; /* Bit 9 */ - boolean Probe234:1; /* Bit 10 */ - boolean Probe130:1; /* Bit 11 */ - boolean Probe134:1; /* Bit 12 */ +struct BusLogic_ProbeOptions { + boolean NoProbe:1; /* Bit 0 */ + boolean NoProbeISA:1; /* Bit 1 */ + boolean NoProbePCI:1; /* Bit 2 */ + boolean NoSortPCI:1; /* Bit 3 */ + boolean MultiMasterFirst:1; /* Bit 4 */ + boolean FlashPointFirst:1; /* Bit 5 */ + boolean LimitedProbeISA:1; /* Bit 6 */ + boolean Probe330:1; /* Bit 7 */ + boolean Probe334:1; /* Bit 8 */ + boolean Probe230:1; /* Bit 9 */ + boolean Probe234:1; /* Bit 10 */ + boolean Probe130:1; /* Bit 11 */ + boolean Probe134:1; /* Bit 12 */ }; /* Define the Global Options. */ -struct BusLogic_GlobalOptions -{ - boolean TraceProbe:1; /* Bit 0 */ - boolean TraceHardwareReset:1; /* Bit 1 */ - boolean TraceConfiguration:1; /* Bit 2 */ - boolean TraceErrors:1; /* Bit 3 */ +struct BusLogic_GlobalOptions { + boolean TraceProbe:1; /* Bit 0 */ + boolean TraceHardwareReset:1; /* Bit 1 */ + boolean TraceConfiguration:1; /* Bit 2 */ + boolean TraceErrors:1; /* Bit 3 */ }; /* Define the Local Options. */ -struct BusLogic_LocalOptions -{ - boolean InhibitTargetInquiry:1; /* Bit 0 */ +struct BusLogic_LocalOptions { + boolean InhibitTargetInquiry:1; /* Bit 0 */ }; /* @@ -335,198 +322,188 @@ struct BusLogic_LocalOptions Define the structure of the write-only Control Register. */ -union BusLogic_ControlRegister -{ - unsigned char All; - struct { - unsigned char :4; /* Bits 0-3 */ - boolean SCSIBusReset:1; /* Bit 4 */ - boolean InterruptReset:1; /* Bit 5 */ - boolean SoftReset:1; /* Bit 6 */ - boolean HardReset:1; /* Bit 7 */ - } cr; +union BusLogic_ControlRegister { + unsigned char All; + struct { + unsigned char:4; /* Bits 0-3 */ + boolean SCSIBusReset:1; /* Bit 4 */ + boolean InterruptReset:1; /* Bit 5 */ + boolean SoftReset:1; /* Bit 6 */ + boolean HardReset:1; /* Bit 7 */ + } cr; }; /* Define the structure of the read-only Status Register. */ -union BusLogic_StatusRegister -{ - unsigned char All; - struct { - boolean CommandInvalid:1; /* Bit 0 */ - boolean Reserved:1; /* Bit 1 */ - boolean DataInRegisterReady:1; /* Bit 2 */ - boolean CommandParameterRegisterBusy:1; /* Bit 3 */ - boolean HostAdapterReady:1; /* Bit 4 */ - boolean InitializationRequired:1; /* Bit 5 */ - boolean DiagnosticFailure:1; /* Bit 6 */ - boolean DiagnosticActive:1; /* Bit 7 */ - } sr; +union BusLogic_StatusRegister { + unsigned char All; + struct { + boolean CommandInvalid:1; /* Bit 0 */ + boolean Reserved:1; /* Bit 1 */ + boolean DataInRegisterReady:1; /* Bit 2 */ + boolean CommandParameterRegisterBusy:1; /* Bit 3 */ + boolean HostAdapterReady:1; /* Bit 4 */ + boolean InitializationRequired:1; /* Bit 5 */ + boolean DiagnosticFailure:1; /* Bit 6 */ + boolean DiagnosticActive:1; /* Bit 7 */ + } sr; }; /* Define the structure of the read-only Interrupt Register. */ -union BusLogic_InterruptRegister -{ - unsigned char All; - struct { - boolean IncomingMailboxLoaded:1; /* Bit 0 */ - boolean OutgoingMailboxAvailable:1; /* Bit 1 */ - boolean CommandComplete:1; /* Bit 2 */ - boolean ExternalBusReset:1; /* Bit 3 */ - unsigned char Reserved:3; /* Bits 4-6 */ - boolean InterruptValid:1; /* Bit 7 */ - } ir; +union BusLogic_InterruptRegister { + unsigned char All; + struct { + boolean IncomingMailboxLoaded:1; /* Bit 0 */ + boolean OutgoingMailboxAvailable:1; /* Bit 1 */ + boolean CommandComplete:1; /* Bit 2 */ + boolean ExternalBusReset:1; /* Bit 3 */ + unsigned char Reserved:3; /* Bits 4-6 */ + boolean InterruptValid:1; /* Bit 7 */ + } ir; }; /* Define the structure of the read-only Geometry Register. */ -union BusLogic_GeometryRegister -{ - unsigned char All; - struct { - enum BusLogic_BIOS_DiskGeometryTranslation Drive0Geometry:2;/* Bits 0-1 */ - enum BusLogic_BIOS_DiskGeometryTranslation Drive1Geometry:2;/* Bits 2-3 */ - unsigned char :3; /* Bits 4-6 */ - boolean ExtendedTranslationEnabled:1; /* Bit 7 */ - } gr; +union BusLogic_GeometryRegister { + unsigned char All; + struct { + enum BusLogic_BIOS_DiskGeometryTranslation Drive0Geometry:2; /* Bits 0-1 */ + enum BusLogic_BIOS_DiskGeometryTranslation Drive1Geometry:2; /* Bits 2-3 */ + unsigned char:3; /* Bits 4-6 */ + boolean ExtendedTranslationEnabled:1; /* Bit 7 */ + } gr; }; /* Define the BusLogic SCSI Host Adapter Command Register Operation Codes. */ -enum BusLogic_OperationCode -{ - BusLogic_TestCommandCompleteInterrupt = 0x00, - BusLogic_InitializeMailbox = 0x01, - BusLogic_ExecuteMailboxCommand = 0x02, - BusLogic_ExecuteBIOSCommand = 0x03, - BusLogic_InquireBoardID = 0x04, - BusLogic_EnableOutgoingMailboxAvailableInt = 0x05, - BusLogic_SetSCSISelectionTimeout = 0x06, - BusLogic_SetPreemptTimeOnBus = 0x07, - BusLogic_SetTimeOffBus = 0x08, - BusLogic_SetBusTransferRate = 0x09, - BusLogic_InquireInstalledDevicesID0to7 = 0x0A, - BusLogic_InquireConfiguration = 0x0B, - BusLogic_EnableTargetMode = 0x0C, - BusLogic_InquireSetupInformation = 0x0D, - BusLogic_WriteAdapterLocalRAM = 0x1A, - BusLogic_ReadAdapterLocalRAM = 0x1B, - BusLogic_WriteBusMasterChipFIFO = 0x1C, - BusLogic_ReadBusMasterChipFIFO = 0x1D, - BusLogic_EchoCommandData = 0x1F, - BusLogic_HostAdapterDiagnostic = 0x20, - BusLogic_SetAdapterOptions = 0x21, - BusLogic_InquireInstalledDevicesID8to15 = 0x23, - BusLogic_InquireTargetDevices = 0x24, - BusLogic_DisableHostAdapterInterrupt = 0x25, - BusLogic_InitializeExtendedMailbox = 0x81, - BusLogic_ExecuteSCSICommand = 0x83, - BusLogic_InquireFirmwareVersion3rdDigit = 0x84, - BusLogic_InquireFirmwareVersionLetter = 0x85, - BusLogic_InquirePCIHostAdapterInformation = 0x86, - BusLogic_InquireHostAdapterModelNumber = 0x8B, - BusLogic_InquireSynchronousPeriod = 0x8C, - BusLogic_InquireExtendedSetupInformation = 0x8D, - BusLogic_EnableStrictRoundRobinMode = 0x8F, - BusLogic_StoreHostAdapterLocalRAM = 0x90, - BusLogic_FetchHostAdapterLocalRAM = 0x91, - BusLogic_StoreLocalDataInEEPROM = 0x92, - BusLogic_UploadAutoSCSICode = 0x94, - BusLogic_ModifyIOAddress = 0x95, - BusLogic_SetCCBFormat = 0x96, - BusLogic_WriteInquiryBuffer = 0x9A, - BusLogic_ReadInquiryBuffer = 0x9B, - BusLogic_FlashROMUploadDownload = 0xA7, - BusLogic_ReadSCAMData = 0xA8, - BusLogic_WriteSCAMData = 0xA9 +enum BusLogic_OperationCode { + BusLogic_TestCommandCompleteInterrupt = 0x00, + BusLogic_InitializeMailbox = 0x01, + BusLogic_ExecuteMailboxCommand = 0x02, + BusLogic_ExecuteBIOSCommand = 0x03, + BusLogic_InquireBoardID = 0x04, + BusLogic_EnableOutgoingMailboxAvailableInt = 0x05, + BusLogic_SetSCSISelectionTimeout = 0x06, + BusLogic_SetPreemptTimeOnBus = 0x07, + BusLogic_SetTimeOffBus = 0x08, + BusLogic_SetBusTransferRate = 0x09, + BusLogic_InquireInstalledDevicesID0to7 = 0x0A, + BusLogic_InquireConfiguration = 0x0B, + BusLogic_EnableTargetMode = 0x0C, + BusLogic_InquireSetupInformation = 0x0D, + BusLogic_WriteAdapterLocalRAM = 0x1A, + BusLogic_ReadAdapterLocalRAM = 0x1B, + BusLogic_WriteBusMasterChipFIFO = 0x1C, + BusLogic_ReadBusMasterChipFIFO = 0x1D, + BusLogic_EchoCommandData = 0x1F, + BusLogic_HostAdapterDiagnostic = 0x20, + BusLogic_SetAdapterOptions = 0x21, + BusLogic_InquireInstalledDevicesID8to15 = 0x23, + BusLogic_InquireTargetDevices = 0x24, + BusLogic_DisableHostAdapterInterrupt = 0x25, + BusLogic_InitializeExtendedMailbox = 0x81, + BusLogic_ExecuteSCSICommand = 0x83, + BusLogic_InquireFirmwareVersion3rdDigit = 0x84, + BusLogic_InquireFirmwareVersionLetter = 0x85, + BusLogic_InquirePCIHostAdapterInformation = 0x86, + BusLogic_InquireHostAdapterModelNumber = 0x8B, + BusLogic_InquireSynchronousPeriod = 0x8C, + BusLogic_InquireExtendedSetupInformation = 0x8D, + BusLogic_EnableStrictRoundRobinMode = 0x8F, + BusLogic_StoreHostAdapterLocalRAM = 0x90, + BusLogic_FetchHostAdapterLocalRAM = 0x91, + BusLogic_StoreLocalDataInEEPROM = 0x92, + BusLogic_UploadAutoSCSICode = 0x94, + BusLogic_ModifyIOAddress = 0x95, + BusLogic_SetCCBFormat = 0x96, + BusLogic_WriteInquiryBuffer = 0x9A, + BusLogic_ReadInquiryBuffer = 0x9B, + BusLogic_FlashROMUploadDownload = 0xA7, + BusLogic_ReadSCAMData = 0xA8, + BusLogic_WriteSCAMData = 0xA9 }; /* Define the Inquire Board ID reply structure. */ -struct BusLogic_BoardID -{ - unsigned char BoardType; /* Byte 0 */ - unsigned char CustomFeatures; /* Byte 1 */ - unsigned char FirmwareVersion1stDigit; /* Byte 2 */ - unsigned char FirmwareVersion2ndDigit; /* Byte 3 */ +struct BusLogic_BoardID { + unsigned char BoardType; /* Byte 0 */ + unsigned char CustomFeatures; /* Byte 1 */ + unsigned char FirmwareVersion1stDigit; /* Byte 2 */ + unsigned char FirmwareVersion2ndDigit; /* Byte 3 */ }; /* Define the Inquire Configuration reply structure. */ -struct BusLogic_Configuration -{ - unsigned char :5; /* Byte 0 Bits 0-4 */ - boolean DMA_Channel5:1; /* Byte 0 Bit 5 */ - boolean DMA_Channel6:1; /* Byte 0 Bit 6 */ - boolean DMA_Channel7:1; /* Byte 0 Bit 7 */ - boolean IRQ_Channel9:1; /* Byte 1 Bit 0 */ - boolean IRQ_Channel10:1; /* Byte 1 Bit 1 */ - boolean IRQ_Channel11:1; /* Byte 1 Bit 2 */ - boolean IRQ_Channel12:1; /* Byte 1 Bit 3 */ - unsigned char :1; /* Byte 1 Bit 4 */ - boolean IRQ_Channel14:1; /* Byte 1 Bit 5 */ - boolean IRQ_Channel15:1; /* Byte 1 Bit 6 */ - unsigned char :1; /* Byte 1 Bit 7 */ - unsigned char HostAdapterID:4; /* Byte 2 Bits 0-3 */ - unsigned char :4; /* Byte 2 Bits 4-7 */ +struct BusLogic_Configuration { + unsigned char:5; /* Byte 0 Bits 0-4 */ + boolean DMA_Channel5:1; /* Byte 0 Bit 5 */ + boolean DMA_Channel6:1; /* Byte 0 Bit 6 */ + boolean DMA_Channel7:1; /* Byte 0 Bit 7 */ + boolean IRQ_Channel9:1; /* Byte 1 Bit 0 */ + boolean IRQ_Channel10:1; /* Byte 1 Bit 1 */ + boolean IRQ_Channel11:1; /* Byte 1 Bit 2 */ + boolean IRQ_Channel12:1; /* Byte 1 Bit 3 */ + unsigned char:1; /* Byte 1 Bit 4 */ + boolean IRQ_Channel14:1; /* Byte 1 Bit 5 */ + boolean IRQ_Channel15:1; /* Byte 1 Bit 6 */ + unsigned char:1; /* Byte 1 Bit 7 */ + unsigned char HostAdapterID:4; /* Byte 2 Bits 0-3 */ + unsigned char:4; /* Byte 2 Bits 4-7 */ }; /* Define the Inquire Setup Information reply structure. */ -struct BusLogic_SynchronousValue -{ - unsigned char Offset:4; /* Bits 0-3 */ - unsigned char TransferPeriod:3; /* Bits 4-6 */ - boolean Synchronous:1; /* Bit 7 */ +struct BusLogic_SynchronousValue { + unsigned char Offset:4; /* Bits 0-3 */ + unsigned char TransferPeriod:3; /* Bits 4-6 */ + boolean Synchronous:1; /* Bit 7 */ }; -struct BusLogic_SetupInformation -{ - boolean SynchronousInitiationEnabled:1; /* Byte 0 Bit 0 */ - boolean ParityCheckingEnabled:1; /* Byte 0 Bit 1 */ - unsigned char :6; /* Byte 0 Bits 2-7 */ - unsigned char BusTransferRate; /* Byte 1 */ - unsigned char PreemptTimeOnBus; /* Byte 2 */ - unsigned char TimeOffBus; /* Byte 3 */ - unsigned char MailboxCount; /* Byte 4 */ - unsigned char MailboxAddress[3]; /* Bytes 5-7 */ - struct BusLogic_SynchronousValue SynchronousValuesID0to7[8];/* Bytes 8-15 */ - unsigned char DisconnectPermittedID0to7; /* Byte 16 */ - unsigned char Signature; /* Byte 17 */ - unsigned char CharacterD; /* Byte 18 */ - unsigned char HostBusType; /* Byte 19 */ - unsigned char WideTransfersPermittedID0to7; /* Byte 20 */ - unsigned char WideTransfersActiveID0to7; /* Byte 21 */ - struct BusLogic_SynchronousValue SynchronousValuesID8to15[8]; /* Bytes 22-29 */ - unsigned char DisconnectPermittedID8to15; /* Byte 30 */ - unsigned char :8; /* Byte 31 */ - unsigned char WideTransfersPermittedID8to15; /* Byte 32 */ - unsigned char WideTransfersActiveID8to15; /* Byte 33 */ +struct BusLogic_SetupInformation { + boolean SynchronousInitiationEnabled:1; /* Byte 0 Bit 0 */ + boolean ParityCheckingEnabled:1; /* Byte 0 Bit 1 */ + unsigned char:6; /* Byte 0 Bits 2-7 */ + unsigned char BusTransferRate; /* Byte 1 */ + unsigned char PreemptTimeOnBus; /* Byte 2 */ + unsigned char TimeOffBus; /* Byte 3 */ + unsigned char MailboxCount; /* Byte 4 */ + unsigned char MailboxAddress[3]; /* Bytes 5-7 */ + struct BusLogic_SynchronousValue SynchronousValuesID0to7[8]; /* Bytes 8-15 */ + unsigned char DisconnectPermittedID0to7; /* Byte 16 */ + unsigned char Signature; /* Byte 17 */ + unsigned char CharacterD; /* Byte 18 */ + unsigned char HostBusType; /* Byte 19 */ + unsigned char WideTransfersPermittedID0to7; /* Byte 20 */ + unsigned char WideTransfersActiveID0to7; /* Byte 21 */ + struct BusLogic_SynchronousValue SynchronousValuesID8to15[8]; /* Bytes 22-29 */ + unsigned char DisconnectPermittedID8to15; /* Byte 30 */ + unsigned char:8; /* Byte 31 */ + unsigned char WideTransfersPermittedID8to15; /* Byte 32 */ + unsigned char WideTransfersActiveID8to15; /* Byte 33 */ }; /* Define the Initialize Extended Mailbox request structure. */ -struct BusLogic_ExtendedMailboxRequest -{ - unsigned char MailboxCount; /* Byte 0 */ - u32 BaseMailboxAddress; /* Bytes 1-4 */ +struct BusLogic_ExtendedMailboxRequest { + unsigned char MailboxCount; /* Byte 0 */ + u32 BaseMailboxAddress; /* Bytes 1-4 */ } PACKED; @@ -536,67 +513,63 @@ struct BusLogic_ExtendedMailboxRequest the Modify I/O Address command. */ -enum BusLogic_ISACompatibleIOPort -{ - BusLogic_IO_330 = 0, - BusLogic_IO_334 = 1, - BusLogic_IO_230 = 2, - BusLogic_IO_234 = 3, - BusLogic_IO_130 = 4, - BusLogic_IO_134 = 5, - BusLogic_IO_Disable = 6, - BusLogic_IO_Disable2 = 7 +enum BusLogic_ISACompatibleIOPort { + BusLogic_IO_330 = 0, + BusLogic_IO_334 = 1, + BusLogic_IO_230 = 2, + BusLogic_IO_234 = 3, + BusLogic_IO_130 = 4, + BusLogic_IO_134 = 5, + BusLogic_IO_Disable = 6, + BusLogic_IO_Disable2 = 7 } PACKED; -struct BusLogic_PCIHostAdapterInformation -{ - enum BusLogic_ISACompatibleIOPort ISACompatibleIOPort;/* Byte 0 */ - unsigned char PCIAssignedIRQChannel; /* Byte 1 */ - boolean LowByteTerminated:1; /* Byte 2 Bit 0 */ - boolean HighByteTerminated:1; /* Byte 2 Bit 1 */ - unsigned char :2; /* Byte 2 Bits 2-3 */ - boolean JP1:1; /* Byte 2 Bit 4 */ - boolean JP2:1; /* Byte 2 Bit 5 */ - boolean JP3:1; /* Byte 2 Bit 6 */ - boolean GenericInfoValid:1; /* Byte 2 Bit 7 */ - unsigned char :8; /* Byte 3 */ +struct BusLogic_PCIHostAdapterInformation { + enum BusLogic_ISACompatibleIOPort ISACompatibleIOPort; /* Byte 0 */ + unsigned char PCIAssignedIRQChannel; /* Byte 1 */ + boolean LowByteTerminated:1; /* Byte 2 Bit 0 */ + boolean HighByteTerminated:1; /* Byte 2 Bit 1 */ + unsigned char:2; /* Byte 2 Bits 2-3 */ + boolean JP1:1; /* Byte 2 Bit 4 */ + boolean JP2:1; /* Byte 2 Bit 5 */ + boolean JP3:1; /* Byte 2 Bit 6 */ + boolean GenericInfoValid:1; /* Byte 2 Bit 7 */ + unsigned char:8; /* Byte 3 */ }; /* Define the Inquire Extended Setup Information reply structure. */ -struct BusLogic_ExtendedSetupInformation -{ - unsigned char BusType; /* Byte 0 */ - unsigned char BIOS_Address; /* Byte 1 */ - unsigned short ScatterGatherLimit; /* Bytes 2-3 */ - unsigned char MailboxCount; /* Byte 4 */ - u32 BaseMailboxAddress; /* Bytes 5-8 */ - struct { - unsigned char :2; /* Byte 9 Bits 0-1 */ - boolean FastOnEISA:1; /* Byte 9 Bit 2 */ - unsigned char :3; /* Byte 9 Bits 3-5 */ - boolean LevelSensitiveInterrupt:1; /* Byte 9 Bit 6 */ - unsigned char :1; /* Byte 9 Bit 7 */ - } Misc; - unsigned char FirmwareRevision[3]; /* Bytes 10-12 */ - boolean HostWideSCSI:1; /* Byte 13 Bit 0 */ - boolean HostDifferentialSCSI:1; /* Byte 13 Bit 1 */ - boolean HostSupportsSCAM:1; /* Byte 13 Bit 2 */ - boolean HostUltraSCSI:1; /* Byte 13 Bit 3 */ - boolean HostSmartTermination:1; /* Byte 13 Bit 4 */ - unsigned char :3; /* Byte 13 Bits 5-7 */ +struct BusLogic_ExtendedSetupInformation { + unsigned char BusType; /* Byte 0 */ + unsigned char BIOS_Address; /* Byte 1 */ + unsigned short ScatterGatherLimit; /* Bytes 2-3 */ + unsigned char MailboxCount; /* Byte 4 */ + u32 BaseMailboxAddress; /* Bytes 5-8 */ + struct { + unsigned char:2; /* Byte 9 Bits 0-1 */ + boolean FastOnEISA:1; /* Byte 9 Bit 2 */ + unsigned char:3; /* Byte 9 Bits 3-5 */ + boolean LevelSensitiveInterrupt:1; /* Byte 9 Bit 6 */ + unsigned char:1; /* Byte 9 Bit 7 */ + } Misc; + unsigned char FirmwareRevision[3]; /* Bytes 10-12 */ + boolean HostWideSCSI:1; /* Byte 13 Bit 0 */ + boolean HostDifferentialSCSI:1; /* Byte 13 Bit 1 */ + boolean HostSupportsSCAM:1; /* Byte 13 Bit 2 */ + boolean HostUltraSCSI:1; /* Byte 13 Bit 3 */ + boolean HostSmartTermination:1; /* Byte 13 Bit 4 */ + unsigned char:3; /* Byte 13 Bits 5-7 */ } PACKED; /* Define the Enable Strict Round Robin Mode request type. */ -enum BusLogic_RoundRobinModeRequest -{ - BusLogic_AggressiveRoundRobinMode = 0, - BusLogic_StrictRoundRobinMode = 1 +enum BusLogic_RoundRobinModeRequest { + BusLogic_AggressiveRoundRobinMode = 0, + BusLogic_StrictRoundRobinMode = 1 } PACKED; @@ -607,96 +580,93 @@ enum BusLogic_RoundRobinModeRequest #define BusLogic_BIOS_BaseOffset 0 #define BusLogic_AutoSCSI_BaseOffset 64 -struct BusLogic_FetchHostAdapterLocalRAMRequest -{ - unsigned char ByteOffset; /* Byte 0 */ - unsigned char ByteCount; /* Byte 1 */ +struct BusLogic_FetchHostAdapterLocalRAMRequest { + unsigned char ByteOffset; /* Byte 0 */ + unsigned char ByteCount; /* Byte 1 */ }; /* Define the Host Adapter Local RAM AutoSCSI structure. */ -struct BusLogic_AutoSCSIData -{ - unsigned char InternalFactorySignature[2]; /* Bytes 0-1 */ - unsigned char InformationByteCount; /* Byte 2 */ - unsigned char HostAdapterType[6]; /* Bytes 3-8 */ - unsigned char :8; /* Byte 9 */ - boolean FloppyEnabled:1; /* Byte 10 Bit 0 */ - boolean FloppySecondary:1; /* Byte 10 Bit 1 */ - boolean LevelSensitiveInterrupt:1; /* Byte 10 Bit 2 */ - unsigned char :2; /* Byte 10 Bits 3-4 */ - unsigned char SystemRAMAreaForBIOS:3; /* Byte 10 Bits 5-7 */ - unsigned char DMA_Channel:7; /* Byte 11 Bits 0-6 */ - boolean DMA_AutoConfiguration:1; /* Byte 11 Bit 7 */ - unsigned char IRQ_Channel:7; /* Byte 12 Bits 0-6 */ - boolean IRQ_AutoConfiguration:1; /* Byte 12 Bit 7 */ - unsigned char DMA_TransferRate; /* Byte 13 */ - unsigned char SCSI_ID; /* Byte 14 */ - boolean LowByteTerminated:1; /* Byte 15 Bit 0 */ - boolean ParityCheckingEnabled:1; /* Byte 15 Bit 1 */ - boolean HighByteTerminated:1; /* Byte 15 Bit 2 */ - boolean NoisyCablingEnvironment:1; /* Byte 15 Bit 3 */ - boolean FastSynchronousNegotiation:1; /* Byte 15 Bit 4 */ - boolean BusResetEnabled:1; /* Byte 15 Bit 5 */ - boolean :1; /* Byte 15 Bit 6 */ - boolean ActiveNegationEnabled:1; /* Byte 15 Bit 7 */ - unsigned char BusOnDelay; /* Byte 16 */ - unsigned char BusOffDelay; /* Byte 17 */ - boolean HostAdapterBIOSEnabled:1; /* Byte 18 Bit 0 */ - boolean BIOSRedirectionOfINT19Enabled:1; /* Byte 18 Bit 1 */ - boolean ExtendedTranslationEnabled:1; /* Byte 18 Bit 2 */ - boolean MapRemovableAsFixedEnabled:1; /* Byte 18 Bit 3 */ - boolean :1; /* Byte 18 Bit 4 */ - boolean BIOSSupportsMoreThan2DrivesEnabled:1; /* Byte 18 Bit 5 */ - boolean BIOSInterruptModeEnabled:1; /* Byte 18 Bit 6 */ - boolean FlopticalSupportEnabled:1; /* Byte 19 Bit 7 */ - unsigned short DeviceEnabled; /* Bytes 19-20 */ - unsigned short WidePermitted; /* Bytes 21-22 */ - unsigned short FastPermitted; /* Bytes 23-24 */ - unsigned short SynchronousPermitted; /* Bytes 25-26 */ - unsigned short DisconnectPermitted; /* Bytes 27-28 */ - unsigned short SendStartUnitCommand; /* Bytes 29-30 */ - unsigned short IgnoreInBIOSScan; /* Bytes 31-32 */ - unsigned char PCIInterruptPin:2; /* Byte 33 Bits 0-1 */ - unsigned char HostAdapterIOPortAddress:2; /* Byte 33 Bits 2-3 */ - boolean StrictRoundRobinModeEnabled:1; /* Byte 33 Bit 4 */ - boolean VESABusSpeedGreaterThan33MHz:1; /* Byte 33 Bit 5 */ - boolean VESABurstWriteEnabled:1; /* Byte 33 Bit 6 */ - boolean VESABurstReadEnabled:1; /* Byte 33 Bit 7 */ - unsigned short UltraPermitted; /* Bytes 34-35 */ - unsigned int :32; /* Bytes 36-39 */ - unsigned char :8; /* Byte 40 */ - unsigned char AutoSCSIMaximumLUN; /* Byte 41 */ - boolean :1; /* Byte 42 Bit 0 */ - boolean SCAM_Dominant:1; /* Byte 42 Bit 1 */ - boolean SCAM_Enabled:1; /* Byte 42 Bit 2 */ - boolean SCAM_Level2:1; /* Byte 42 Bit 3 */ - unsigned char :4; /* Byte 42 Bits 4-7 */ - boolean INT13ExtensionEnabled:1; /* Byte 43 Bit 0 */ - boolean :1; /* Byte 43 Bit 1 */ - boolean CDROMBootEnabled:1; /* Byte 43 Bit 2 */ - unsigned char :5; /* Byte 43 Bits 3-7 */ - unsigned char BootTargetID:4; /* Byte 44 Bits 0-3 */ - unsigned char BootChannel:4; /* Byte 44 Bits 4-7 */ - unsigned char ForceBusDeviceScanningOrder:1; /* Byte 45 Bit 0 */ - unsigned char :7; /* Byte 45 Bits 1-7 */ - unsigned short NonTaggedToAlternateLUNPermitted; /* Bytes 46-47 */ - unsigned short RenegotiateSyncAfterCheckCondition; /* Bytes 48-49 */ - unsigned char Reserved[10]; /* Bytes 50-59 */ - unsigned char ManufacturingDiagnostic[2]; /* Bytes 60-61 */ - unsigned short Checksum; /* Bytes 62-63 */ +struct BusLogic_AutoSCSIData { + unsigned char InternalFactorySignature[2]; /* Bytes 0-1 */ + unsigned char InformationByteCount; /* Byte 2 */ + unsigned char HostAdapterType[6]; /* Bytes 3-8 */ + unsigned char:8; /* Byte 9 */ + boolean FloppyEnabled:1; /* Byte 10 Bit 0 */ + boolean FloppySecondary:1; /* Byte 10 Bit 1 */ + boolean LevelSensitiveInterrupt:1; /* Byte 10 Bit 2 */ + unsigned char:2; /* Byte 10 Bits 3-4 */ + unsigned char SystemRAMAreaForBIOS:3; /* Byte 10 Bits 5-7 */ + unsigned char DMA_Channel:7; /* Byte 11 Bits 0-6 */ + boolean DMA_AutoConfiguration:1; /* Byte 11 Bit 7 */ + unsigned char IRQ_Channel:7; /* Byte 12 Bits 0-6 */ + boolean IRQ_AutoConfiguration:1; /* Byte 12 Bit 7 */ + unsigned char DMA_TransferRate; /* Byte 13 */ + unsigned char SCSI_ID; /* Byte 14 */ + boolean LowByteTerminated:1; /* Byte 15 Bit 0 */ + boolean ParityCheckingEnabled:1; /* Byte 15 Bit 1 */ + boolean HighByteTerminated:1; /* Byte 15 Bit 2 */ + boolean NoisyCablingEnvironment:1; /* Byte 15 Bit 3 */ + boolean FastSynchronousNegotiation:1; /* Byte 15 Bit 4 */ + boolean BusResetEnabled:1; /* Byte 15 Bit 5 */ + boolean:1; /* Byte 15 Bit 6 */ + boolean ActiveNegationEnabled:1; /* Byte 15 Bit 7 */ + unsigned char BusOnDelay; /* Byte 16 */ + unsigned char BusOffDelay; /* Byte 17 */ + boolean HostAdapterBIOSEnabled:1; /* Byte 18 Bit 0 */ + boolean BIOSRedirectionOfINT19Enabled:1; /* Byte 18 Bit 1 */ + boolean ExtendedTranslationEnabled:1; /* Byte 18 Bit 2 */ + boolean MapRemovableAsFixedEnabled:1; /* Byte 18 Bit 3 */ + boolean:1; /* Byte 18 Bit 4 */ + boolean BIOSSupportsMoreThan2DrivesEnabled:1; /* Byte 18 Bit 5 */ + boolean BIOSInterruptModeEnabled:1; /* Byte 18 Bit 6 */ + boolean FlopticalSupportEnabled:1; /* Byte 19 Bit 7 */ + unsigned short DeviceEnabled; /* Bytes 19-20 */ + unsigned short WidePermitted; /* Bytes 21-22 */ + unsigned short FastPermitted; /* Bytes 23-24 */ + unsigned short SynchronousPermitted; /* Bytes 25-26 */ + unsigned short DisconnectPermitted; /* Bytes 27-28 */ + unsigned short SendStartUnitCommand; /* Bytes 29-30 */ + unsigned short IgnoreInBIOSScan; /* Bytes 31-32 */ + unsigned char PCIInterruptPin:2; /* Byte 33 Bits 0-1 */ + unsigned char HostAdapterIOPortAddress:2; /* Byte 33 Bits 2-3 */ + boolean StrictRoundRobinModeEnabled:1; /* Byte 33 Bit 4 */ + boolean VESABusSpeedGreaterThan33MHz:1; /* Byte 33 Bit 5 */ + boolean VESABurstWriteEnabled:1; /* Byte 33 Bit 6 */ + boolean VESABurstReadEnabled:1; /* Byte 33 Bit 7 */ + unsigned short UltraPermitted; /* Bytes 34-35 */ + unsigned int:32; /* Bytes 36-39 */ + unsigned char:8; /* Byte 40 */ + unsigned char AutoSCSIMaximumLUN; /* Byte 41 */ + boolean:1; /* Byte 42 Bit 0 */ + boolean SCAM_Dominant:1; /* Byte 42 Bit 1 */ + boolean SCAM_Enabled:1; /* Byte 42 Bit 2 */ + boolean SCAM_Level2:1; /* Byte 42 Bit 3 */ + unsigned char:4; /* Byte 42 Bits 4-7 */ + boolean INT13ExtensionEnabled:1; /* Byte 43 Bit 0 */ + boolean:1; /* Byte 43 Bit 1 */ + boolean CDROMBootEnabled:1; /* Byte 43 Bit 2 */ + unsigned char:5; /* Byte 43 Bits 3-7 */ + unsigned char BootTargetID:4; /* Byte 44 Bits 0-3 */ + unsigned char BootChannel:4; /* Byte 44 Bits 4-7 */ + unsigned char ForceBusDeviceScanningOrder:1; /* Byte 45 Bit 0 */ + unsigned char:7; /* Byte 45 Bits 1-7 */ + unsigned short NonTaggedToAlternateLUNPermitted; /* Bytes 46-47 */ + unsigned short RenegotiateSyncAfterCheckCondition; /* Bytes 48-49 */ + unsigned char Reserved[10]; /* Bytes 50-59 */ + unsigned char ManufacturingDiagnostic[2]; /* Bytes 60-61 */ + unsigned short Checksum; /* Bytes 62-63 */ } PACKED; /* Define the Host Adapter Local RAM Auto SCSI Byte 45 structure. */ -struct BusLogic_AutoSCSIByte45 -{ - unsigned char ForceBusDeviceScanningOrder:1; /* Bit 0 */ - unsigned char :7; /* Bits 1-7 */ +struct BusLogic_AutoSCSIByte45 { + unsigned char ForceBusDeviceScanningOrder:1; /* Bit 0 */ + unsigned char:7; /* Bits 1-7 */ }; /* @@ -705,12 +675,11 @@ struct BusLogic_AutoSCSIByte45 #define BusLogic_BIOS_DriveMapOffset 17 -struct BusLogic_BIOSDriveMapByte -{ - unsigned char TargetIDBit3:1; /* Bit 0 */ - unsigned char :2; /* Bits 1-2 */ - enum BusLogic_BIOS_DiskGeometryTranslation DiskGeometry:2; /* Bits 3-4 */ - unsigned char TargetID:3; /* Bits 5-7 */ +struct BusLogic_BIOSDriveMapByte { + unsigned char TargetIDBit3:1; /* Bit 0 */ + unsigned char:2; /* Bits 1-2 */ + enum BusLogic_BIOS_DiskGeometryTranslation DiskGeometry:2; /* Bits 3-4 */ + unsigned char TargetID:3; /* Bits 5-7 */ }; /* @@ -718,21 +687,19 @@ struct BusLogic_BIOSDriveMapByte necessary to support more than 8 Logical Units per Target Device. */ -enum BusLogic_SetCCBFormatRequest -{ - BusLogic_LegacyLUNFormatCCB = 0, - BusLogic_ExtendedLUNFormatCCB = 1 +enum BusLogic_SetCCBFormatRequest { + BusLogic_LegacyLUNFormatCCB = 0, + BusLogic_ExtendedLUNFormatCCB = 1 } PACKED; /* Define the Outgoing Mailbox Action Codes. */ -enum BusLogic_ActionCode -{ - BusLogic_OutgoingMailboxFree = 0x00, - BusLogic_MailboxStartCommand = 0x01, - BusLogic_MailboxAbortCommand = 0x02 +enum BusLogic_ActionCode { + BusLogic_OutgoingMailboxFree = 0x00, + BusLogic_MailboxStartCommand = 0x01, + BusLogic_MailboxAbortCommand = 0x02 } PACKED; @@ -742,28 +709,26 @@ enum BusLogic_ActionCode completion codes are stored in the CCB; it only uses codes 1, 2, 4, and 5. */ -enum BusLogic_CompletionCode -{ - BusLogic_IncomingMailboxFree = 0x00, - BusLogic_CommandCompletedWithoutError = 0x01, - BusLogic_CommandAbortedAtHostRequest = 0x02, - BusLogic_AbortedCommandNotFound = 0x03, - BusLogic_CommandCompletedWithError = 0x04, - BusLogic_InvalidCCB = 0x05 +enum BusLogic_CompletionCode { + BusLogic_IncomingMailboxFree = 0x00, + BusLogic_CommandCompletedWithoutError = 0x01, + BusLogic_CommandAbortedAtHostRequest = 0x02, + BusLogic_AbortedCommandNotFound = 0x03, + BusLogic_CommandCompletedWithError = 0x04, + BusLogic_InvalidCCB = 0x05 } PACKED; /* Define the Command Control Block (CCB) Opcodes. */ -enum BusLogic_CCB_Opcode -{ - BusLogic_InitiatorCCB = 0x00, - BusLogic_TargetCCB = 0x01, - BusLogic_InitiatorCCB_ScatterGather = 0x02, - BusLogic_InitiatorCCB_ResidualDataLength = 0x03, - BusLogic_InitiatorCCB_ScatterGatherResidual = 0x04, - BusLogic_BusDeviceReset = 0x81 +enum BusLogic_CCB_Opcode { + BusLogic_InitiatorCCB = 0x00, + BusLogic_TargetCCB = 0x01, + BusLogic_InitiatorCCB_ScatterGather = 0x02, + BusLogic_InitiatorCCB_ResidualDataLength = 0x03, + BusLogic_InitiatorCCB_ScatterGatherResidual = 0x04, + BusLogic_BusDeviceReset = 0x81 } PACKED; @@ -771,12 +736,11 @@ enum BusLogic_CCB_Opcode Define the CCB Data Direction Codes. */ -enum BusLogic_DataDirection -{ - BusLogic_UncheckedDataTransfer = 0, - BusLogic_DataInLengthChecked = 1, - BusLogic_DataOutLengthChecked = 2, - BusLogic_NoDataTransfer = 3 +enum BusLogic_DataDirection { + BusLogic_UncheckedDataTransfer = 0, + BusLogic_DataInLengthChecked = 1, + BusLogic_DataOutLengthChecked = 2, + BusLogic_NoDataTransfer = 3 }; @@ -785,33 +749,32 @@ enum BusLogic_DataDirection return status code 0x0C; it uses 0x12 for both overruns and underruns. */ -enum BusLogic_HostAdapterStatus -{ - BusLogic_CommandCompletedNormally = 0x00, - BusLogic_LinkedCommandCompleted = 0x0A, - BusLogic_LinkedCommandCompletedWithFlag = 0x0B, - BusLogic_DataUnderRun = 0x0C, - BusLogic_SCSISelectionTimeout = 0x11, - BusLogic_DataOverRun = 0x12, - BusLogic_UnexpectedBusFree = 0x13, - BusLogic_InvalidBusPhaseRequested = 0x14, - BusLogic_InvalidOutgoingMailboxActionCode = 0x15, - BusLogic_InvalidCommandOperationCode = 0x16, - BusLogic_LinkedCCBhasInvalidLUN = 0x17, - BusLogic_InvalidCommandParameter = 0x1A, - BusLogic_AutoRequestSenseFailed = 0x1B, - BusLogic_TaggedQueuingMessageRejected = 0x1C, - BusLogic_UnsupportedMessageReceived = 0x1D, - BusLogic_HostAdapterHardwareFailed = 0x20, - BusLogic_TargetFailedResponseToATN = 0x21, - BusLogic_HostAdapterAssertedRST = 0x22, - BusLogic_OtherDeviceAssertedRST = 0x23, - BusLogic_TargetDeviceReconnectedImproperly = 0x24, - BusLogic_HostAdapterAssertedBusDeviceReset = 0x25, - BusLogic_AbortQueueGenerated = 0x26, - BusLogic_HostAdapterSoftwareError = 0x27, - BusLogic_HostAdapterHardwareTimeoutError = 0x30, - BusLogic_SCSIParityErrorDetected = 0x34 +enum BusLogic_HostAdapterStatus { + BusLogic_CommandCompletedNormally = 0x00, + BusLogic_LinkedCommandCompleted = 0x0A, + BusLogic_LinkedCommandCompletedWithFlag = 0x0B, + BusLogic_DataUnderRun = 0x0C, + BusLogic_SCSISelectionTimeout = 0x11, + BusLogic_DataOverRun = 0x12, + BusLogic_UnexpectedBusFree = 0x13, + BusLogic_InvalidBusPhaseRequested = 0x14, + BusLogic_InvalidOutgoingMailboxActionCode = 0x15, + BusLogic_InvalidCommandOperationCode = 0x16, + BusLogic_LinkedCCBhasInvalidLUN = 0x17, + BusLogic_InvalidCommandParameter = 0x1A, + BusLogic_AutoRequestSenseFailed = 0x1B, + BusLogic_TaggedQueuingMessageRejected = 0x1C, + BusLogic_UnsupportedMessageReceived = 0x1D, + BusLogic_HostAdapterHardwareFailed = 0x20, + BusLogic_TargetFailedResponseToATN = 0x21, + BusLogic_HostAdapterAssertedRST = 0x22, + BusLogic_OtherDeviceAssertedRST = 0x23, + BusLogic_TargetDeviceReconnectedImproperly = 0x24, + BusLogic_HostAdapterAssertedBusDeviceReset = 0x25, + BusLogic_AbortQueueGenerated = 0x26, + BusLogic_HostAdapterSoftwareError = 0x27, + BusLogic_HostAdapterHardwareTimeoutError = 0x30, + BusLogic_SCSIParityErrorDetected = 0x34 } PACKED; @@ -819,23 +782,21 @@ enum BusLogic_HostAdapterStatus Define the SCSI Target Device Status Codes. */ -enum BusLogic_TargetDeviceStatus -{ - BusLogic_OperationGood = 0x00, - BusLogic_CheckCondition = 0x02, - BusLogic_DeviceBusy = 0x08 +enum BusLogic_TargetDeviceStatus { + BusLogic_OperationGood = 0x00, + BusLogic_CheckCondition = 0x02, + BusLogic_DeviceBusy = 0x08 } PACKED; /* Define the Queue Tag Codes. */ -enum BusLogic_QueueTag -{ - BusLogic_SimpleQueueTag = 0, - BusLogic_HeadOfQueueTag = 1, - BusLogic_OrderedQueueTag = 2, - BusLogic_ReservedQT = 3 +enum BusLogic_QueueTag { + BusLogic_SimpleQueueTag = 0, + BusLogic_HeadOfQueueTag = 1, + BusLogic_OrderedQueueTag = 2, + BusLogic_ReservedQT = 3 }; /* @@ -852,22 +813,20 @@ typedef unsigned char SCSI_CDB_T[BusLogic_CDB_MaxLength]; Firmware Interface and the FlashPoint SCCB Manager. */ -struct BusLogic_ScatterGatherSegment -{ - u32 SegmentByteCount; /* Bytes 0-3 */ - u32 SegmentDataPointer; /* Bytes 4-7 */ +struct BusLogic_ScatterGatherSegment { + u32 SegmentByteCount; /* Bytes 0-3 */ + u32 SegmentDataPointer; /* Bytes 4-7 */ }; /* Define the Driver CCB Status Codes. */ -enum BusLogic_CCB_Status -{ - BusLogic_CCB_Free = 0, - BusLogic_CCB_Active = 1, - BusLogic_CCB_Completed = 2, - BusLogic_CCB_Reset = 3 +enum BusLogic_CCB_Status { + BusLogic_CCB_Free = 0, + BusLogic_CCB_Active = 1, + BusLogic_CCB_Completed = 2, + BusLogic_CCB_Reset = 3 } PACKED; @@ -890,82 +849,79 @@ enum BusLogic_CCB_Status 32 Logical Units per Target Device. */ -struct BusLogic_CCB -{ - /* - MultiMaster Firmware and FlashPoint SCCB Manager Common Portion. - */ - enum BusLogic_CCB_Opcode Opcode; /* Byte 0 */ - unsigned char :3; /* Byte 1 Bits 0-2 */ - enum BusLogic_DataDirection DataDirection:2; /* Byte 1 Bits 3-4 */ - boolean TagEnable:1; /* Byte 1 Bit 5 */ - enum BusLogic_QueueTag QueueTag:2; /* Byte 1 Bits 6-7 */ - unsigned char CDB_Length; /* Byte 2 */ - unsigned char SenseDataLength; /* Byte 3 */ - u32 DataLength; /* Bytes 4-7 */ - u32 DataPointer; /* Bytes 8-11 */ - unsigned char :8; /* Byte 12 */ - unsigned char :8; /* Byte 13 */ - enum BusLogic_HostAdapterStatus HostAdapterStatus; /* Byte 14 */ - enum BusLogic_TargetDeviceStatus TargetDeviceStatus; /* Byte 15 */ - unsigned char TargetID; /* Byte 16 */ - unsigned char LogicalUnit:5; /* Byte 17 Bits 0-4 */ - boolean LegacyTagEnable:1; /* Byte 17 Bit 5 */ - enum BusLogic_QueueTag LegacyQueueTag:2; /* Byte 17 Bits 6-7 */ - SCSI_CDB_T CDB; /* Bytes 18-29 */ - unsigned char :8; /* Byte 30 */ - unsigned char :8; /* Byte 31 */ - unsigned int :32; /* Bytes 32-35 */ - u32 SenseDataPointer; /* Bytes 36-39 */ - /* - FlashPoint SCCB Manager Defined Portion. - */ - void (*CallbackFunction)(struct BusLogic_CCB *); /* Bytes 40-43 */ - u32 BaseAddress; /* Bytes 44-47 */ - enum BusLogic_CompletionCode CompletionCode; /* Byte 48 */ +struct BusLogic_CCB { + /* + MultiMaster Firmware and FlashPoint SCCB Manager Common Portion. + */ + enum BusLogic_CCB_Opcode Opcode; /* Byte 0 */ + unsigned char:3; /* Byte 1 Bits 0-2 */ + enum BusLogic_DataDirection DataDirection:2; /* Byte 1 Bits 3-4 */ + boolean TagEnable:1; /* Byte 1 Bit 5 */ + enum BusLogic_QueueTag QueueTag:2; /* Byte 1 Bits 6-7 */ + unsigned char CDB_Length; /* Byte 2 */ + unsigned char SenseDataLength; /* Byte 3 */ + u32 DataLength; /* Bytes 4-7 */ + u32 DataPointer; /* Bytes 8-11 */ + unsigned char:8; /* Byte 12 */ + unsigned char:8; /* Byte 13 */ + enum BusLogic_HostAdapterStatus HostAdapterStatus; /* Byte 14 */ + enum BusLogic_TargetDeviceStatus TargetDeviceStatus; /* Byte 15 */ + unsigned char TargetID; /* Byte 16 */ + unsigned char LogicalUnit:5; /* Byte 17 Bits 0-4 */ + boolean LegacyTagEnable:1; /* Byte 17 Bit 5 */ + enum BusLogic_QueueTag LegacyQueueTag:2; /* Byte 17 Bits 6-7 */ + SCSI_CDB_T CDB; /* Bytes 18-29 */ + unsigned char:8; /* Byte 30 */ + unsigned char:8; /* Byte 31 */ + unsigned int:32; /* Bytes 32-35 */ + u32 SenseDataPointer; /* Bytes 36-39 */ + /* + FlashPoint SCCB Manager Defined Portion. + */ + void (*CallbackFunction) (struct BusLogic_CCB *); /* Bytes 40-43 */ + u32 BaseAddress; /* Bytes 44-47 */ + enum BusLogic_CompletionCode CompletionCode; /* Byte 48 */ #ifndef CONFIG_SCSI_OMIT_FLASHPOINT - unsigned char :8; /* Byte 49 */ - unsigned short OS_Flags; /* Bytes 50-51 */ - unsigned char Private[48]; /* Bytes 52-99 */ + unsigned char:8; /* Byte 49 */ + unsigned short OS_Flags; /* Bytes 50-51 */ + unsigned char Private[48]; /* Bytes 52-99 */ #endif - /* - BusLogic Linux Driver Defined Portion. - */ - dma_addr_t AllocationGroupHead; - unsigned int AllocationGroupSize; - u32 DMA_Handle; - enum BusLogic_CCB_Status Status; - unsigned long SerialNumber; - struct scsi_cmnd *Command; - struct BusLogic_HostAdapter *HostAdapter; - struct BusLogic_CCB *Next; - struct BusLogic_CCB *NextAll; - struct BusLogic_ScatterGatherSegment - ScatterGatherList[BusLogic_ScatterGatherLimit]; + /* + BusLogic Linux Driver Defined Portion. + */ + dma_addr_t AllocationGroupHead; + unsigned int AllocationGroupSize; + u32 DMA_Handle; + enum BusLogic_CCB_Status Status; + unsigned long SerialNumber; + struct scsi_cmnd *Command; + struct BusLogic_HostAdapter *HostAdapter; + struct BusLogic_CCB *Next; + struct BusLogic_CCB *NextAll; + struct BusLogic_ScatterGatherSegment + ScatterGatherList[BusLogic_ScatterGatherLimit]; }; /* Define the 32 Bit Mode Outgoing Mailbox structure. */ -struct BusLogic_OutgoingMailbox -{ - u32 CCB; /* Bytes 0-3 */ - unsigned int :24; /* Bytes 4-6 */ - enum BusLogic_ActionCode ActionCode; /* Byte 7 */ +struct BusLogic_OutgoingMailbox { + u32 CCB; /* Bytes 0-3 */ + unsigned int:24; /* Bytes 4-6 */ + enum BusLogic_ActionCode ActionCode; /* Byte 7 */ }; /* Define the 32 Bit Mode Incoming Mailbox structure. */ -struct BusLogic_IncomingMailbox -{ - u32 CCB; /* Bytes 0-3 */ - enum BusLogic_HostAdapterStatus HostAdapterStatus; /* Byte 4 */ - enum BusLogic_TargetDeviceStatus TargetDeviceStatus; /* Byte 5 */ - unsigned char :8; /* Byte 6 */ - enum BusLogic_CompletionCode CompletionCode; /* Byte 7 */ +struct BusLogic_IncomingMailbox { + u32 CCB; /* Bytes 0-3 */ + enum BusLogic_HostAdapterStatus HostAdapterStatus; /* Byte 4 */ + enum BusLogic_TargetDeviceStatus TargetDeviceStatus; /* Byte 5 */ + unsigned char:8; /* Byte 6 */ + enum BusLogic_CompletionCode CompletionCode; /* Byte 7 */ }; @@ -973,29 +929,27 @@ struct BusLogic_IncomingMailbox Define the BusLogic Driver Options structure. */ -struct BusLogic_DriverOptions -{ - unsigned short TaggedQueuingPermitted; - unsigned short TaggedQueuingPermittedMask; - unsigned short BusSettleTime; - struct BusLogic_LocalOptions LocalOptions; - unsigned char CommonQueueDepth; - unsigned char QueueDepth[BusLogic_MaxTargetDevices]; +struct BusLogic_DriverOptions { + unsigned short TaggedQueuingPermitted; + unsigned short TaggedQueuingPermittedMask; + unsigned short BusSettleTime; + struct BusLogic_LocalOptions LocalOptions; + unsigned char CommonQueueDepth; + unsigned char QueueDepth[BusLogic_MaxTargetDevices]; }; /* Define the Host Adapter Target Flags structure. */ -struct BusLogic_TargetFlags -{ - boolean TargetExists:1; - boolean TaggedQueuingSupported:1; - boolean WideTransfersSupported:1; - boolean TaggedQueuingActive:1; - boolean WideTransfersActive:1; - boolean CommandSuccessfulFlag:1; - boolean TargetInfoReported:1; +struct BusLogic_TargetFlags { + boolean TargetExists:1; + boolean TaggedQueuingSupported:1; + boolean WideTransfersSupported:1; + boolean TaggedQueuingActive:1; + boolean WideTransfersActive:1; + boolean CommandSuccessfulFlag:1; + boolean TargetInfoReported:1; }; /* @@ -1006,25 +960,24 @@ struct BusLogic_TargetFlags typedef unsigned int BusLogic_CommandSizeBuckets_T[BusLogic_SizeBuckets]; -struct BusLogic_TargetStatistics -{ - unsigned int CommandsAttempted; - unsigned int CommandsCompleted; - unsigned int ReadCommands; - unsigned int WriteCommands; - struct BusLogic_ByteCounter TotalBytesRead; - struct BusLogic_ByteCounter TotalBytesWritten; - BusLogic_CommandSizeBuckets_T ReadCommandSizeBuckets; - BusLogic_CommandSizeBuckets_T WriteCommandSizeBuckets; - unsigned short CommandAbortsRequested; - unsigned short CommandAbortsAttempted; - unsigned short CommandAbortsCompleted; - unsigned short BusDeviceResetsRequested; - unsigned short BusDeviceResetsAttempted; - unsigned short BusDeviceResetsCompleted; - unsigned short HostAdapterResetsRequested; - unsigned short HostAdapterResetsAttempted; - unsigned short HostAdapterResetsCompleted; +struct BusLogic_TargetStatistics { + unsigned int CommandsAttempted; + unsigned int CommandsCompleted; + unsigned int ReadCommands; + unsigned int WriteCommands; + struct BusLogic_ByteCounter TotalBytesRead; + struct BusLogic_ByteCounter TotalBytesWritten; + BusLogic_CommandSizeBuckets_T ReadCommandSizeBuckets; + BusLogic_CommandSizeBuckets_T WriteCommandSizeBuckets; + unsigned short CommandAbortsRequested; + unsigned short CommandAbortsAttempted; + unsigned short CommandAbortsCompleted; + unsigned short BusDeviceResetsRequested; + unsigned short BusDeviceResetsAttempted; + unsigned short BusDeviceResetsCompleted; + unsigned short HostAdapterResetsRequested; + unsigned short HostAdapterResetsAttempted; + unsigned short HostAdapterResetsCompleted; }; /* @@ -1041,183 +994,179 @@ typedef unsigned int FlashPoint_CardHandle_T; by the FlashPoint SCCB Manager. */ -struct FlashPoint_Info -{ - u32 BaseAddress; /* Bytes 0-3 */ - boolean Present; /* Byte 4 */ - unsigned char IRQ_Channel; /* Byte 5 */ - unsigned char SCSI_ID; /* Byte 6 */ - unsigned char SCSI_LUN; /* Byte 7 */ - unsigned short FirmwareRevision; /* Bytes 8-9 */ - unsigned short SynchronousPermitted; /* Bytes 10-11 */ - unsigned short FastPermitted; /* Bytes 12-13 */ - unsigned short UltraPermitted; /* Bytes 14-15 */ - unsigned short DisconnectPermitted; /* Bytes 16-17 */ - unsigned short WidePermitted; /* Bytes 18-19 */ - boolean ParityCheckingEnabled:1; /* Byte 20 Bit 0 */ - boolean HostWideSCSI:1; /* Byte 20 Bit 1 */ - boolean HostSoftReset:1; /* Byte 20 Bit 2 */ - boolean ExtendedTranslationEnabled:1; /* Byte 20 Bit 3 */ - boolean LowByteTerminated:1; /* Byte 20 Bit 4 */ - boolean HighByteTerminated:1; /* Byte 20 Bit 5 */ - boolean ReportDataUnderrun:1; /* Byte 20 Bit 6 */ - boolean SCAM_Enabled:1; /* Byte 20 Bit 7 */ - boolean SCAM_Level2:1; /* Byte 21 Bit 0 */ - unsigned char :7; /* Byte 21 Bits 1-7 */ - unsigned char Family; /* Byte 22 */ - unsigned char BusType; /* Byte 23 */ - unsigned char ModelNumber[3]; /* Bytes 24-26 */ - unsigned char RelativeCardNumber; /* Byte 27 */ - unsigned char Reserved[4]; /* Bytes 28-31 */ - unsigned int OS_Reserved; /* Bytes 32-35 */ - unsigned char TranslationInfo[4]; /* Bytes 36-39 */ - unsigned int Reserved2[5]; /* Bytes 40-59 */ - unsigned int SecondaryRange; /* Bytes 60-63 */ +struct FlashPoint_Info { + u32 BaseAddress; /* Bytes 0-3 */ + boolean Present; /* Byte 4 */ + unsigned char IRQ_Channel; /* Byte 5 */ + unsigned char SCSI_ID; /* Byte 6 */ + unsigned char SCSI_LUN; /* Byte 7 */ + unsigned short FirmwareRevision; /* Bytes 8-9 */ + unsigned short SynchronousPermitted; /* Bytes 10-11 */ + unsigned short FastPermitted; /* Bytes 12-13 */ + unsigned short UltraPermitted; /* Bytes 14-15 */ + unsigned short DisconnectPermitted; /* Bytes 16-17 */ + unsigned short WidePermitted; /* Bytes 18-19 */ + boolean ParityCheckingEnabled:1; /* Byte 20 Bit 0 */ + boolean HostWideSCSI:1; /* Byte 20 Bit 1 */ + boolean HostSoftReset:1; /* Byte 20 Bit 2 */ + boolean ExtendedTranslationEnabled:1; /* Byte 20 Bit 3 */ + boolean LowByteTerminated:1; /* Byte 20 Bit 4 */ + boolean HighByteTerminated:1; /* Byte 20 Bit 5 */ + boolean ReportDataUnderrun:1; /* Byte 20 Bit 6 */ + boolean SCAM_Enabled:1; /* Byte 20 Bit 7 */ + boolean SCAM_Level2:1; /* Byte 21 Bit 0 */ + unsigned char:7; /* Byte 21 Bits 1-7 */ + unsigned char Family; /* Byte 22 */ + unsigned char BusType; /* Byte 23 */ + unsigned char ModelNumber[3]; /* Bytes 24-26 */ + unsigned char RelativeCardNumber; /* Byte 27 */ + unsigned char Reserved[4]; /* Bytes 28-31 */ + unsigned int OS_Reserved; /* Bytes 32-35 */ + unsigned char TranslationInfo[4]; /* Bytes 36-39 */ + unsigned int Reserved2[5]; /* Bytes 40-59 */ + unsigned int SecondaryRange; /* Bytes 60-63 */ }; /* Define the BusLogic Driver Host Adapter structure. */ -struct BusLogic_HostAdapter -{ - struct Scsi_Host *SCSI_Host; - struct pci_dev *PCI_Device; - enum BusLogic_HostAdapterType HostAdapterType; - enum BusLogic_HostAdapterBusType HostAdapterBusType; - unsigned long IO_Address; - unsigned long PCI_Address; - unsigned short AddressCount; - unsigned char HostNumber; - unsigned char ModelName[9]; - unsigned char FirmwareVersion[6]; - unsigned char FullModelName[18]; - unsigned char Bus; - unsigned char Device; - unsigned char IRQ_Channel; - unsigned char DMA_Channel; - unsigned char SCSI_ID; - boolean IRQ_ChannelAcquired:1; - boolean DMA_ChannelAcquired:1; - boolean ExtendedTranslationEnabled:1; - boolean ParityCheckingEnabled:1; - boolean BusResetEnabled:1; - boolean LevelSensitiveInterrupt:1; - boolean HostWideSCSI:1; - boolean HostDifferentialSCSI:1; - boolean HostSupportsSCAM:1; - boolean HostUltraSCSI:1; - boolean ExtendedLUNSupport:1; - boolean TerminationInfoValid:1; - boolean LowByteTerminated:1; - boolean HighByteTerminated:1; - boolean BounceBuffersRequired:1; - boolean StrictRoundRobinModeSupport:1; - boolean SCAM_Enabled:1; - boolean SCAM_Level2:1; - boolean HostAdapterInitialized:1; - boolean HostAdapterExternalReset:1; - boolean HostAdapterInternalError:1; - boolean ProcessCompletedCCBsActive; - volatile boolean HostAdapterCommandCompleted; - unsigned short HostAdapterScatterGatherLimit; - unsigned short DriverScatterGatherLimit; - unsigned short MaxTargetDevices; - unsigned short MaxLogicalUnits; - unsigned short MailboxCount; - unsigned short InitialCCBs; - unsigned short IncrementalCCBs; - unsigned short AllocatedCCBs; - unsigned short DriverQueueDepth; - unsigned short HostAdapterQueueDepth; - unsigned short UntaggedQueueDepth; - unsigned short CommonQueueDepth; - unsigned short BusSettleTime; - unsigned short SynchronousPermitted; - unsigned short FastPermitted; - unsigned short UltraPermitted; - unsigned short WidePermitted; - unsigned short DisconnectPermitted; - unsigned short TaggedQueuingPermitted; - unsigned short ExternalHostAdapterResets; - unsigned short HostAdapterInternalErrors; - unsigned short TargetDeviceCount; - unsigned short MessageBufferLength; - u32 BIOS_Address; - struct BusLogic_DriverOptions *DriverOptions; - struct FlashPoint_Info FlashPointInfo; - FlashPoint_CardHandle_T CardHandle; - struct BusLogic_HostAdapter *Next; - struct BusLogic_CCB *All_CCBs; - struct BusLogic_CCB *Free_CCBs; - struct BusLogic_CCB *FirstCompletedCCB; - struct BusLogic_CCB *LastCompletedCCB; - struct BusLogic_CCB *BusDeviceResetPendingCCB[BusLogic_MaxTargetDevices]; - struct BusLogic_TargetFlags TargetFlags[BusLogic_MaxTargetDevices]; - unsigned char QueueDepth[BusLogic_MaxTargetDevices]; - unsigned char SynchronousPeriod[BusLogic_MaxTargetDevices]; - unsigned char SynchronousOffset[BusLogic_MaxTargetDevices]; - unsigned char ActiveCommands[BusLogic_MaxTargetDevices]; - unsigned int CommandsSinceReset[BusLogic_MaxTargetDevices]; - unsigned long LastSequencePoint[BusLogic_MaxTargetDevices]; - unsigned long LastResetAttempted[BusLogic_MaxTargetDevices]; - unsigned long LastResetCompleted[BusLogic_MaxTargetDevices]; - struct BusLogic_OutgoingMailbox *FirstOutgoingMailbox; - struct BusLogic_OutgoingMailbox *LastOutgoingMailbox; - struct BusLogic_OutgoingMailbox *NextOutgoingMailbox; - struct BusLogic_IncomingMailbox *FirstIncomingMailbox; - struct BusLogic_IncomingMailbox *LastIncomingMailbox; - struct BusLogic_IncomingMailbox *NextIncomingMailbox; - struct BusLogic_TargetStatistics TargetStatistics[BusLogic_MaxTargetDevices]; - unsigned char *MailboxSpace; - dma_addr_t MailboxSpaceHandle; - unsigned int MailboxSize; - unsigned long CCB_Offset; - char MessageBuffer[BusLogic_MessageBufferSize]; +struct BusLogic_HostAdapter { + struct Scsi_Host *SCSI_Host; + struct pci_dev *PCI_Device; + enum BusLogic_HostAdapterType HostAdapterType; + enum BusLogic_HostAdapterBusType HostAdapterBusType; + unsigned long IO_Address; + unsigned long PCI_Address; + unsigned short AddressCount; + unsigned char HostNumber; + unsigned char ModelName[9]; + unsigned char FirmwareVersion[6]; + unsigned char FullModelName[18]; + unsigned char Bus; + unsigned char Device; + unsigned char IRQ_Channel; + unsigned char DMA_Channel; + unsigned char SCSI_ID; + boolean IRQ_ChannelAcquired:1; + boolean DMA_ChannelAcquired:1; + boolean ExtendedTranslationEnabled:1; + boolean ParityCheckingEnabled:1; + boolean BusResetEnabled:1; + boolean LevelSensitiveInterrupt:1; + boolean HostWideSCSI:1; + boolean HostDifferentialSCSI:1; + boolean HostSupportsSCAM:1; + boolean HostUltraSCSI:1; + boolean ExtendedLUNSupport:1; + boolean TerminationInfoValid:1; + boolean LowByteTerminated:1; + boolean HighByteTerminated:1; + boolean BounceBuffersRequired:1; + boolean StrictRoundRobinModeSupport:1; + boolean SCAM_Enabled:1; + boolean SCAM_Level2:1; + boolean HostAdapterInitialized:1; + boolean HostAdapterExternalReset:1; + boolean HostAdapterInternalError:1; + boolean ProcessCompletedCCBsActive; + volatile boolean HostAdapterCommandCompleted; + unsigned short HostAdapterScatterGatherLimit; + unsigned short DriverScatterGatherLimit; + unsigned short MaxTargetDevices; + unsigned short MaxLogicalUnits; + unsigned short MailboxCount; + unsigned short InitialCCBs; + unsigned short IncrementalCCBs; + unsigned short AllocatedCCBs; + unsigned short DriverQueueDepth; + unsigned short HostAdapterQueueDepth; + unsigned short UntaggedQueueDepth; + unsigned short CommonQueueDepth; + unsigned short BusSettleTime; + unsigned short SynchronousPermitted; + unsigned short FastPermitted; + unsigned short UltraPermitted; + unsigned short WidePermitted; + unsigned short DisconnectPermitted; + unsigned short TaggedQueuingPermitted; + unsigned short ExternalHostAdapterResets; + unsigned short HostAdapterInternalErrors; + unsigned short TargetDeviceCount; + unsigned short MessageBufferLength; + u32 BIOS_Address; + struct BusLogic_DriverOptions *DriverOptions; + struct FlashPoint_Info FlashPointInfo; + FlashPoint_CardHandle_T CardHandle; + struct BusLogic_HostAdapter *Next; + struct BusLogic_CCB *All_CCBs; + struct BusLogic_CCB *Free_CCBs; + struct BusLogic_CCB *FirstCompletedCCB; + struct BusLogic_CCB *LastCompletedCCB; + struct BusLogic_CCB *BusDeviceResetPendingCCB[BusLogic_MaxTargetDevices]; + struct BusLogic_TargetFlags TargetFlags[BusLogic_MaxTargetDevices]; + unsigned char QueueDepth[BusLogic_MaxTargetDevices]; + unsigned char SynchronousPeriod[BusLogic_MaxTargetDevices]; + unsigned char SynchronousOffset[BusLogic_MaxTargetDevices]; + unsigned char ActiveCommands[BusLogic_MaxTargetDevices]; + unsigned int CommandsSinceReset[BusLogic_MaxTargetDevices]; + unsigned long LastSequencePoint[BusLogic_MaxTargetDevices]; + unsigned long LastResetAttempted[BusLogic_MaxTargetDevices]; + unsigned long LastResetCompleted[BusLogic_MaxTargetDevices]; + struct BusLogic_OutgoingMailbox *FirstOutgoingMailbox; + struct BusLogic_OutgoingMailbox *LastOutgoingMailbox; + struct BusLogic_OutgoingMailbox *NextOutgoingMailbox; + struct BusLogic_IncomingMailbox *FirstIncomingMailbox; + struct BusLogic_IncomingMailbox *LastIncomingMailbox; + struct BusLogic_IncomingMailbox *NextIncomingMailbox; + struct BusLogic_TargetStatistics TargetStatistics[BusLogic_MaxTargetDevices]; + unsigned char *MailboxSpace; + dma_addr_t MailboxSpaceHandle; + unsigned int MailboxSize; + unsigned long CCB_Offset; + char MessageBuffer[BusLogic_MessageBufferSize]; }; /* Define a structure for the BIOS Disk Parameters. */ -struct BIOS_DiskParameters -{ - int Heads; - int Sectors; - int Cylinders; +struct BIOS_DiskParameters { + int Heads; + int Sectors; + int Cylinders; }; /* Define a structure for the SCSI Inquiry command results. */ -struct SCSI_Inquiry -{ - unsigned char PeripheralDeviceType:5; /* Byte 0 Bits 0-4 */ - unsigned char PeripheralQualifier:3; /* Byte 0 Bits 5-7 */ - unsigned char DeviceTypeModifier:7; /* Byte 1 Bits 0-6 */ - boolean RMB:1; /* Byte 1 Bit 7 */ - unsigned char ANSI_ApprovedVersion:3; /* Byte 2 Bits 0-2 */ - unsigned char ECMA_Version:3; /* Byte 2 Bits 3-5 */ - unsigned char ISO_Version:2; /* Byte 2 Bits 6-7 */ - unsigned char ResponseDataFormat:4; /* Byte 3 Bits 0-3 */ - unsigned char :2; /* Byte 3 Bits 4-5 */ - boolean TrmIOP:1; /* Byte 3 Bit 6 */ - boolean AENC:1; /* Byte 3 Bit 7 */ - unsigned char AdditionalLength; /* Byte 4 */ - unsigned char :8; /* Byte 5 */ - unsigned char :8; /* Byte 6 */ - boolean SftRe:1; /* Byte 7 Bit 0 */ - boolean CmdQue:1; /* Byte 7 Bit 1 */ - boolean :1; /* Byte 7 Bit 2 */ - boolean Linked:1; /* Byte 7 Bit 3 */ - boolean Sync:1; /* Byte 7 Bit 4 */ - boolean WBus16:1; /* Byte 7 Bit 5 */ - boolean WBus32:1; /* Byte 7 Bit 6 */ - boolean RelAdr:1; /* Byte 7 Bit 7 */ - unsigned char VendorIdentification[8]; /* Bytes 8-15 */ - unsigned char ProductIdentification[16]; /* Bytes 16-31 */ - unsigned char ProductRevisionLevel[4]; /* Bytes 32-35 */ +struct SCSI_Inquiry { + unsigned char PeripheralDeviceType:5; /* Byte 0 Bits 0-4 */ + unsigned char PeripheralQualifier:3; /* Byte 0 Bits 5-7 */ + unsigned char DeviceTypeModifier:7; /* Byte 1 Bits 0-6 */ + boolean RMB:1; /* Byte 1 Bit 7 */ + unsigned char ANSI_ApprovedVersion:3; /* Byte 2 Bits 0-2 */ + unsigned char ECMA_Version:3; /* Byte 2 Bits 3-5 */ + unsigned char ISO_Version:2; /* Byte 2 Bits 6-7 */ + unsigned char ResponseDataFormat:4; /* Byte 3 Bits 0-3 */ + unsigned char:2; /* Byte 3 Bits 4-5 */ + boolean TrmIOP:1; /* Byte 3 Bit 6 */ + boolean AENC:1; /* Byte 3 Bit 7 */ + unsigned char AdditionalLength; /* Byte 4 */ + unsigned char:8; /* Byte 5 */ + unsigned char:8; /* Byte 6 */ + boolean SftRe:1; /* Byte 7 Bit 0 */ + boolean CmdQue:1; /* Byte 7 Bit 1 */ + boolean:1; /* Byte 7 Bit 2 */ + boolean Linked:1; /* Byte 7 Bit 3 */ + boolean Sync:1; /* Byte 7 Bit 4 */ + boolean WBus16:1; /* Byte 7 Bit 5 */ + boolean WBus32:1; /* Byte 7 Bit 6 */ + boolean RelAdr:1; /* Byte 7 Bit 7 */ + unsigned char VendorIdentification[8]; /* Bytes 8-15 */ + unsigned char ProductIdentification[16]; /* Bytes 16-31 */ + unsigned char ProductRevisionLevel[4]; /* Bytes 32-35 */ }; /* @@ -1226,7 +1175,7 @@ struct SCSI_Inquiry static inline void BusLogic_AcquireHostAdapterLock(struct BusLogic_HostAdapter *HostAdapter) { - spin_lock_irq(HostAdapter->SCSI_Host->host_lock); + spin_lock_irq(HostAdapter->SCSI_Host->host_lock); } /* @@ -1235,7 +1184,7 @@ static inline void BusLogic_AcquireHostAdapterLock(struct BusLogic_HostAdapter * static inline void BusLogic_ReleaseHostAdapterLock(struct BusLogic_HostAdapter *HostAdapter) { - spin_unlock_irq(HostAdapter->SCSI_Host->host_lock); + spin_unlock_irq(HostAdapter->SCSI_Host->host_lock); } @@ -1244,10 +1193,9 @@ static inline void BusLogic_ReleaseHostAdapterLock(struct BusLogic_HostAdapter * but is only called from the interrupt handler. */ -static inline void BusLogic_AcquireHostAdapterLockIH(struct BusLogic_HostAdapter *HostAdapter, - unsigned long *ProcessorFlags) +static inline void BusLogic_AcquireHostAdapterLockIH(struct BusLogic_HostAdapter *HostAdapter, unsigned long *ProcessorFlags) { - spin_lock_irqsave(HostAdapter->SCSI_Host->host_lock, *ProcessorFlags); + spin_lock_irqsave(HostAdapter->SCSI_Host->host_lock, *ProcessorFlags); } @@ -1256,10 +1204,9 @@ static inline void BusLogic_AcquireHostAdapterLockIH(struct BusLogic_HostAdapter but is only called from the interrupt handler. */ -static inline void BusLogic_ReleaseHostAdapterLockIH(struct BusLogic_HostAdapter *HostAdapter, - unsigned long *ProcessorFlags) +static inline void BusLogic_ReleaseHostAdapterLockIH(struct BusLogic_HostAdapter *HostAdapter, unsigned long *ProcessorFlags) { - spin_unlock_irqrestore(HostAdapter->SCSI_Host->host_lock, *ProcessorFlags); + spin_unlock_irqrestore(HostAdapter->SCSI_Host->host_lock, *ProcessorFlags); } @@ -1270,65 +1217,60 @@ static inline void BusLogic_ReleaseHostAdapterLockIH(struct BusLogic_HostAdapter static inline void BusLogic_SCSIBusReset(struct BusLogic_HostAdapter *HostAdapter) { - union BusLogic_ControlRegister ControlRegister; - ControlRegister.All = 0; - ControlRegister.cr.SCSIBusReset = true; - outb(ControlRegister.All, - HostAdapter->IO_Address + BusLogic_ControlRegisterOffset); + union BusLogic_ControlRegister ControlRegister; + ControlRegister.All = 0; + ControlRegister.cr.SCSIBusReset = true; + outb(ControlRegister.All, HostAdapter->IO_Address + BusLogic_ControlRegisterOffset); } static inline void BusLogic_InterruptReset(struct BusLogic_HostAdapter *HostAdapter) { - union BusLogic_ControlRegister ControlRegister; - ControlRegister.All = 0; - ControlRegister.cr.InterruptReset = true; - outb(ControlRegister.All, - HostAdapter->IO_Address + BusLogic_ControlRegisterOffset); + union BusLogic_ControlRegister ControlRegister; + ControlRegister.All = 0; + ControlRegister.cr.InterruptReset = true; + outb(ControlRegister.All, HostAdapter->IO_Address + BusLogic_ControlRegisterOffset); } static inline void BusLogic_SoftReset(struct BusLogic_HostAdapter *HostAdapter) { - union BusLogic_ControlRegister ControlRegister; - ControlRegister.All = 0; - ControlRegister.cr.SoftReset = true; - outb(ControlRegister.All, - HostAdapter->IO_Address + BusLogic_ControlRegisterOffset); + union BusLogic_ControlRegister ControlRegister; + ControlRegister.All = 0; + ControlRegister.cr.SoftReset = true; + outb(ControlRegister.All, HostAdapter->IO_Address + BusLogic_ControlRegisterOffset); } static inline void BusLogic_HardReset(struct BusLogic_HostAdapter *HostAdapter) { - union BusLogic_ControlRegister ControlRegister; - ControlRegister.All = 0; - ControlRegister.cr.HardReset = true; - outb(ControlRegister.All, - HostAdapter->IO_Address + BusLogic_ControlRegisterOffset); + union BusLogic_ControlRegister ControlRegister; + ControlRegister.All = 0; + ControlRegister.cr.HardReset = true; + outb(ControlRegister.All, HostAdapter->IO_Address + BusLogic_ControlRegisterOffset); } static inline unsigned char BusLogic_ReadStatusRegister(struct BusLogic_HostAdapter *HostAdapter) { - return inb(HostAdapter->IO_Address + BusLogic_StatusRegisterOffset); + return inb(HostAdapter->IO_Address + BusLogic_StatusRegisterOffset); } static inline void BusLogic_WriteCommandParameterRegister(struct BusLogic_HostAdapter - *HostAdapter, - unsigned char Value) + *HostAdapter, unsigned char Value) { - outb(Value, HostAdapter->IO_Address + BusLogic_CommandParameterRegisterOffset); + outb(Value, HostAdapter->IO_Address + BusLogic_CommandParameterRegisterOffset); } static inline unsigned char BusLogic_ReadDataInRegister(struct BusLogic_HostAdapter *HostAdapter) { - return inb(HostAdapter->IO_Address + BusLogic_DataInRegisterOffset); + return inb(HostAdapter->IO_Address + BusLogic_DataInRegisterOffset); } static inline unsigned char BusLogic_ReadInterruptRegister(struct BusLogic_HostAdapter *HostAdapter) { - return inb(HostAdapter->IO_Address + BusLogic_InterruptRegisterOffset); + return inb(HostAdapter->IO_Address + BusLogic_InterruptRegisterOffset); } static inline unsigned char BusLogic_ReadGeometryRegister(struct BusLogic_HostAdapter *HostAdapter) { - return inb(HostAdapter->IO_Address + BusLogic_GeometryRegisterOffset); + return inb(HostAdapter->IO_Address + BusLogic_GeometryRegisterOffset); } /* @@ -1339,7 +1281,7 @@ static inline unsigned char BusLogic_ReadGeometryRegister(struct BusLogic_HostAd static inline void BusLogic_StartMailboxCommand(struct BusLogic_HostAdapter *HostAdapter) { - BusLogic_WriteCommandParameterRegister(HostAdapter, BusLogic_ExecuteMailboxCommand); + BusLogic_WriteCommandParameterRegister(HostAdapter, BusLogic_ExecuteMailboxCommand); } /* @@ -1348,7 +1290,7 @@ static inline void BusLogic_StartMailboxCommand(struct BusLogic_HostAdapter *Hos static inline void BusLogic_Delay(int Seconds) { - mdelay(1000 * Seconds); + mdelay(1000 * Seconds); } /* @@ -1358,12 +1300,12 @@ static inline void BusLogic_Delay(int Seconds) static inline u32 Virtual_to_Bus(void *VirtualAddress) { - return (u32) virt_to_bus(VirtualAddress); + return (u32) virt_to_bus(VirtualAddress); } static inline void *Bus_to_Virtual(u32 BusAddress) { - return (void *) bus_to_virt(BusAddress); + return (void *) bus_to_virt(BusAddress); } /* @@ -1374,7 +1316,7 @@ static inline void *Bus_to_Virtual(u32 BusAddress) static inline u32 Virtual_to_32Bit_Virtual(void *VirtualAddress) { - return (u32) (unsigned long) VirtualAddress; + return (u32) (unsigned long) VirtualAddress; } /* @@ -1384,7 +1326,8 @@ static inline u32 Virtual_to_32Bit_Virtual(void *VirtualAddress) static inline void BusLogic_IncrementErrorCounter(unsigned short *ErrorCounter) { - if (*ErrorCounter < 65535) (*ErrorCounter)++; + if (*ErrorCounter < 65535) + (*ErrorCounter)++; } /* @@ -1392,40 +1335,35 @@ static inline void BusLogic_IncrementErrorCounter(unsigned short *ErrorCounter) */ static inline void BusLogic_IncrementByteCounter(struct BusLogic_ByteCounter - *ByteCounter, - unsigned int Amount) + *ByteCounter, unsigned int Amount) { - ByteCounter->Units += Amount; - if (ByteCounter->Units > 999999999) - { - ByteCounter->Units -= 1000000000; - ByteCounter->Billions++; - } + ByteCounter->Units += Amount; + if (ByteCounter->Units > 999999999) { + ByteCounter->Units -= 1000000000; + ByteCounter->Billions++; + } } /* BusLogic_IncrementSizeBucket increments the Bucket for Amount. */ -static inline void BusLogic_IncrementSizeBucket(BusLogic_CommandSizeBuckets_T - CommandSizeBuckets, - unsigned int Amount) -{ - int Index = 0; - if (Amount < 8*1024) - { - if (Amount < 2*1024) - Index = (Amount < 1*1024 ? 0 : 1); - else Index = (Amount < 4*1024 ? 2 : 3); - } - else if (Amount < 128*1024) - { - if (Amount < 32*1024) - Index = (Amount < 16*1024 ? 4 : 5); - else Index = (Amount < 64*1024 ? 6 : 7); - } - else Index = (Amount < 256*1024 ? 8 : 9); - CommandSizeBuckets[Index]++; +static inline void BusLogic_IncrementSizeBucket(BusLogic_CommandSizeBuckets_T CommandSizeBuckets, unsigned int Amount) +{ + int Index = 0; + if (Amount < 8 * 1024) { + if (Amount < 2 * 1024) + Index = (Amount < 1 * 1024 ? 0 : 1); + else + Index = (Amount < 4 * 1024 ? 2 : 3); + } else if (Amount < 128 * 1024) { + if (Amount < 32 * 1024) + Index = (Amount < 16 * 1024 ? 4 : 5); + else + Index = (Amount < 64 * 1024 ? 6 : 7); + } else + Index = (Amount < 256 * 1024 ? 8 : 9); + CommandSizeBuckets[Index]++; } /* @@ -1450,20 +1388,14 @@ static inline void BusLogic_IncrementSizeBucket(BusLogic_CommandSizeBuckets_T static const char *BusLogic_DriverInfo(struct Scsi_Host *); static int BusLogic_DetectHostAdapter(struct scsi_host_template *); static int BusLogic_ReleaseHostAdapter(struct Scsi_Host *); -static int BusLogic_QueueCommand(struct scsi_cmnd *, - void (*CompletionRoutine)(struct scsi_cmnd *)); -static int BusLogic_BIOSDiskParameters(struct scsi_device *, - struct block_device *, - sector_t, int *); -static int BusLogic_ProcDirectoryInfo(struct Scsi_Host *, char *, - char **, off_t, int, int); +static int BusLogic_QueueCommand(struct scsi_cmnd *, void (*CompletionRoutine) (struct scsi_cmnd *)); +static int BusLogic_BIOSDiskParameters(struct scsi_device *, struct block_device *, sector_t, int *); +static int BusLogic_ProcDirectoryInfo(struct Scsi_Host *, char *, char **, off_t, int, int); static int BusLogic_SlaveConfigure(struct scsi_device *); static void BusLogic_QueueCompletedCCB(struct BusLogic_CCB *); static irqreturn_t BusLogic_InterruptHandler(int, void *, struct pt_regs *); -static int BusLogic_ResetHostAdapter(struct BusLogic_HostAdapter *, - boolean HardReset); -static void BusLogic_Message(enum BusLogic_MessageLevel, char *, - struct BusLogic_HostAdapter *, ...); +static int BusLogic_ResetHostAdapter(struct BusLogic_HostAdapter *, boolean HardReset); +static void BusLogic_Message(enum BusLogic_MessageLevel, char *, struct BusLogic_HostAdapter *, ...); static int __init BusLogic_Setup(char *); -#endif /* _BUSLOGIC_H */ +#endif /* _BUSLOGIC_H */ diff --git a/drivers/scsi/NCR5380.c b/drivers/scsi/NCR5380.c index e3bb9dde6..2032b6230 100644 --- a/drivers/scsi/NCR5380.c +++ b/drivers/scsi/NCR5380.c @@ -310,12 +310,8 @@ * possible) function may be used. Before the specific driver initialization * code finishes, NCR5380_print_options should be called. */ - static int do_abort(struct Scsi_Host *host); static void do_reset(struct Scsi_Host *host); -static struct NCR5380_hostdata *first_host = NULL; -static struct NCR5380_hostdata *last_host = NULL; -static struct timer_list usleep_timer; /* * initialize_SCp - init the scsi pointer field @@ -533,9 +529,6 @@ static void NCR5380_print_phase(struct Scsi_Host *instance) #define USLEEP_WAITLONG USLEEP_SLEEP #endif -static struct Scsi_Host *expires_first = NULL; -static spinlock_t timer_lock; /* Guards expires list */ - /* * Function : int should_disconnect (unsigned char cmd) * @@ -578,90 +571,10 @@ static int should_disconnect(unsigned char cmd) } } -/* - * Assumes instance->time_expires has been set in higher level code. - * We should move to a timer per host - * - * Locks: Takes the timer queue lock - */ - -static int NCR5380_set_timer(struct Scsi_Host *instance) -{ - struct Scsi_Host *tmp, **prev; - unsigned long flags; - - if (((struct NCR5380_hostdata *) (instance->hostdata))->next_timer) { - return -1; - } - - spin_lock_irqsave(&timer_lock, flags); - for (prev = &expires_first, tmp = expires_first; tmp; prev = &(((struct NCR5380_hostdata *) tmp->hostdata)->next_timer), tmp = ((struct NCR5380_hostdata *) tmp->hostdata)->next_timer) - if (((struct NCR5380_hostdata *) instance->hostdata)->time_expires < ((struct NCR5380_hostdata *) tmp->hostdata)->time_expires) - break; - - ((struct NCR5380_hostdata *) instance->hostdata)->next_timer = tmp; - *prev = instance; - - mod_timer(&usleep_timer, ((struct NCR5380_hostdata *) expires_first->hostdata)->time_expires); - - spin_unlock_irqrestore(&timer_lock, flags); - return 0; -} - -/** - * NCR5380_timer_fn - handle polled timeouts - * @unused: unused - * - * Walk the list of controllers, find which controllers have exceeded - * their expiry timeout and then schedule the processing co-routine to - * do the real work. - * - * Doing something about unwanted reentrancy here might be useful - * - * Locks: disables irqs, takes and frees the timer lock - */ - -static void NCR5380_timer_fn(unsigned long unused) -{ - struct Scsi_Host *instance; - struct NCR5380_hostdata *hostdata; - unsigned long flags; - - spin_lock_irqsave(&timer_lock, flags); - for (; expires_first && time_before_eq(((struct NCR5380_hostdata *) expires_first->hostdata)->time_expires, jiffies);) - { - hostdata = (struct NCR5380_hostdata *) expires_first->hostdata; - schedule_work(&hostdata->coroutine); - instance = hostdata->next_timer; - hostdata->next_timer = NULL; - hostdata->time_expires = 0; - expires_first = instance; - } - - del_timer(&usleep_timer); - if (expires_first) { - usleep_timer.expires = ((struct NCR5380_hostdata *) expires_first->hostdata)->time_expires; - add_timer(&usleep_timer); - } - spin_unlock_irqrestore(&timer_lock, flags); -} - -/** - * NCR5380_all_init - global setup - * - * Set up the global values and timers needed by the NCR5380 driver - */ - -static inline void NCR5380_all_init(void) +static void NCR5380_set_timer(struct NCR5380_hostdata *hostdata, unsigned long timeout) { - static int done = 0; - if (!done) { - dprintk(NDEBUG_INIT, ("scsi : NCR5380_all_init()\n")); - done = 1; - init_timer(&usleep_timer); - spin_lock_init(&timer_lock); - usleep_timer.function = NCR5380_timer_fn; - } + hostdata->time_expires = jiffies + timeout; + schedule_delayed_work(&hostdata->coroutine, hostdata->time_expires); } @@ -787,22 +700,6 @@ static void __init NCR5380_print_options(struct Scsi_Host *instance) } } -/** - * NCR5380_coroutine_running - coroutine status - * @instance: controller to check - * - * Return true if the co-routine for this controller is running - * or scheduled to run - * - * FIXME: this test function belongs in the workqueue code! - */ - -static int NCR5380_coroutine_running(struct Scsi_Host *instance) -{ - struct NCR5380_hostdata *hostdata = (struct NCR5380_hostdata *)instance->hostdata; - return test_bit(0, &hostdata->coroutine.pending); -} - /** * NCR5380_print_status - dump controller info * @instance: controller to dump @@ -819,8 +716,6 @@ static void NCR5380_print_status(struct Scsi_Host *instance) char *start; int len; - printk("NCR5380 : coroutine is%s running.\n", NCR5380_coroutine_running(instance)? "" : "n't"); - NCR5380_dprint(NDEBUG_ANY, instance); NCR5380_dprint_phase(NDEBUG_ANY, instance); @@ -900,7 +795,6 @@ int NCR5380_proc_info(struct Scsi_Host *instance, char *buffer, char **start, of SPRINTF("Highwater I/O busy_spin_counts -- write: %d read: %d\n", pas_wmaxi, pas_maxi); #endif spin_lock_irq(instance->host_lock); - SPRINTF("NCR5380 : coroutine is%s running.\n", NCR5380_coroutine_running(instance) ? "" : "n't"); if (!hostdata->connected) SPRINTF("scsi%d: no currently connected command\n", instance->host_no); else @@ -912,7 +806,6 @@ int NCR5380_proc_info(struct Scsi_Host *instance, char *buffer, char **start, of SPRINTF("scsi%d: disconnected_queue\n", instance->host_no); for (ptr = (Scsi_Cmnd *) hostdata->disconnected_queue; ptr; ptr = (Scsi_Cmnd *) ptr->host_scribble) pos = lprint_Scsi_Cmnd(ptr, pos, buffer, length); - spin_unlock_irq(instance->host_lock); *start = buffer; @@ -964,7 +857,7 @@ static char *lprint_opcode(int opcode, char *pos, char *buffer, int length) * Locks: interrupts must be enabled when we are called */ -static int __init NCR5380_init(struct Scsi_Host *instance, int flags) +static int __devinit NCR5380_init(struct Scsi_Host *instance, int flags) { NCR5380_local_declare(); int i, pass; @@ -984,7 +877,6 @@ static int __init NCR5380_init(struct Scsi_Host *instance, int flags) #endif NCR5380_setup(instance); - NCR5380_all_init(); hostdata->aborted = 0; hostdata->id_mask = 1 << instance->this_id; @@ -1021,18 +913,8 @@ static int __init NCR5380_init(struct Scsi_Host *instance, int flags) else hostdata->flags = FLAG_CHECK_LAST_BYTE_SENT | flags; - hostdata->next = NULL; - - if (!first_host) - first_host = hostdata; - else - last_host->next = hostdata; - - last_host = hostdata; - hostdata->host = instance; hostdata->time_expires = 0; - hostdata->next_timer = NULL; #ifndef AUTOSENSE if ((instance->cmd_per_lun > 1) || instance->can_queue > 1) @@ -1088,6 +970,19 @@ static int __init NCR5380_init(struct Scsi_Host *instance, int flags) return 0; } +/** + * NCR5380_exit - remove an NCR5380 + * @instance: adapter to remove + */ + +static void __devexit NCR5380_exit(struct Scsi_Host *instance) +{ + struct NCR5380_hostdata *hostdata = (struct NCR5380_hostdata *) instance->hostdata; + + cancel_delayed_work(&hostdata->coroutine); + flush_scheduled_work(); +} + /** * NCR5380_queue_command - queue a command * @cmd: SCSI command @@ -1169,6 +1064,7 @@ static int NCR5380_queue_command(Scsi_Cmnd * cmd, void (*done) (Scsi_Cmnd *)) return 0; } + /** * NCR5380_main - NCR state machines * @@ -1184,28 +1080,11 @@ static int NCR5380_queue_command(Scsi_Cmnd * cmd, void (*done) (Scsi_Cmnd *)) static void NCR5380_main(void *p) { struct NCR5380_hostdata *hostdata = p; + struct Scsi_Host *instance = hostdata->host; Scsi_Cmnd *tmp, *prev; - struct Scsi_Host *instance; int done; - unsigned long flags = 0; - /* - * We run (with interrupts disabled) until we're sure that none of - * the host adapters have anything that can be done, at which point - * we can exit - * - * Interrupts are enabled before doing various other internal - * instructions, after we've decided that we need to run through - * the loop again. - * - * this should prevent any race conditions. - */ - - instance = hostdata->host; - - if(instance->irq != SCSI_IRQ_NONE) - spin_lock_irqsave(instance->host_lock, flags); - + spin_lock_irq(instance->host_lock); do { /* Lock held here */ done = 1; @@ -1286,8 +1165,7 @@ static void NCR5380_main(void *p) LIST(tmp, hostdata->issue_queue); tmp->host_scribble = (unsigned char *) hostdata->issue_queue; hostdata->issue_queue = tmp; - hostdata->time_expires = jiffies + USLEEP_WAITLONG; - NCR5380_set_timer(instance); + NCR5380_set_timer(hostdata, USLEEP_WAITLONG); } } /* if hostdata->selecting */ if (hostdata->connected @@ -1304,8 +1182,7 @@ static void NCR5380_main(void *p) break; } while (!done); - if(instance->irq != SCSI_IRQ_NONE) - spin_unlock_irqrestore(instance->host_lock, flags); + spin_unlock_irq(instance->host_lock); } #ifndef DONT_USE_INTR @@ -1330,12 +1207,13 @@ static irqreturn_t NCR5380_intr(int irq, void *dev_id, struct pt_regs *regs) struct NCR5380_hostdata *hostdata = (struct NCR5380_hostdata *) instance->hostdata; int done; unsigned char basr; + unsigned long flags; dprintk(NDEBUG_INTR, ("scsi : NCR5380 irq %d triggered\n", irq)); do { done = 1; - spin_lock_irq(instance->host_lock); + spin_lock_irqsave(instance->host_lock, flags); /* Look for pending interrupts */ NCR5380_setup(instance); basr = NCR5380_read(BUS_AND_STATUS_REG); @@ -1386,7 +1264,7 @@ static irqreturn_t NCR5380_intr(int irq, void *dev_id, struct pt_regs *regs) #endif } } /* if BASR_IRQ */ - spin_unlock_irq(instance->host_lock); + spin_unlock_irqrestore(instance->host_lock, flags); if(!done) schedule_work(&hostdata->coroutine); } while (!done); @@ -1469,12 +1347,8 @@ static int NCR5380_select(struct Scsi_Host *instance, Scsi_Cmnd * cmd, int tag) int err; NCR5380_setup(instance); - if (hostdata->selecting) { - if(instance->irq != SCSI_IRQ_NONE) - spin_unlock_irq(instance->host_lock); - goto part2; /* RvC: sorry prof. Dijkstra, but it keeps the - rest of the code nearly the same */ - } + if (hostdata->selecting) + goto part2; hostdata->restart_select = 0; @@ -1495,16 +1369,12 @@ static int NCR5380_select(struct Scsi_Host *instance, Scsi_Cmnd * cmd, int tag) NCR5380_write(OUTPUT_DATA_REG, hostdata->id_mask); NCR5380_write(MODE_REG, MR_ARBITRATE); - if(instance->irq != SCSI_IRQ_NONE) - spin_unlock_irq(instance->host_lock); /* We can be relaxed here, interrupts are on, we are in workqueue context, the birds are singing in the trees */ - + spin_unlock_irq(instance->host_lock); err = NCR5380_poll_politely(instance, INITIATOR_COMMAND_REG, ICR_ARBITRATION_PROGRESS, ICR_ARBITRATION_PROGRESS, 5*HZ); - if(instance->irq != SCSI_IRQ_NONE) - spin_lock_irq(instance->host_lock); - + spin_lock_irq(instance->host_lock); if (err < 0) { printk(KERN_DEBUG "scsi: arbitration timeout at %d\n", __LINE__); NCR5380_write(MODE_REG, MR_BASE); @@ -1628,8 +1498,7 @@ part2: if (!value && (hostdata->select_time < HZ/4)) { /* RvC: we still must wait for a device response */ hostdata->select_time++; /* after 25 ticks the device has failed */ - hostdata->time_expires = jiffies + 1; - NCR5380_set_timer(instance); + NCR5380_set_timer(hostdata, 1); return 0; /* RvC: we return here with hostdata->selecting set, to go to sleep */ } @@ -1638,8 +1507,6 @@ part2: waiting period */ if ((NCR5380_read(STATUS_REG) & (SR_SEL | SR_IO)) == (SR_SEL | SR_IO)) { NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE); - if(instance->irq != SCSI_IRQ_NONE) - spin_lock_irq(instance->host_lock); NCR5380_reselect(instance); printk("scsi%d : reselection after won arbitration?\n", instance->host_no); NCR5380_write(SELECT_ENABLE_REG, hostdata->id_mask); @@ -1665,8 +1532,6 @@ part2: NCR5380_write(SELECT_ENABLE_REG, hostdata->id_mask); return -1; } - if(instance->irq != SCSI_IRQ_NONE) - spin_lock_irq(instance->host_lock); cmd->result = DID_BAD_TARGET << 16; collect_stats(hostdata, cmd); cmd->scsi_done(cmd); @@ -1693,11 +1558,13 @@ part2: */ /* Wait for start of REQ/ACK handshake */ - + + spin_unlock_irq(instance->host_lock); err = NCR5380_poll_politely(instance, STATUS_REG, SR_REQ, SR_REQ, HZ); + spin_lock_irq(instance->host_lock); - if(err) - { printk(KERN_ERR "scsi%d: timeout at NCR5380.c:%d\n", instance->host_no, __LINE__); + if(err) { + printk(KERN_ERR "scsi%d: timeout at NCR5380.c:%d\n", instance->host_no, __LINE__); NCR5380_write(SELECT_ENABLE_REG, hostdata->id_mask); goto failed; } @@ -1705,9 +1572,6 @@ part2: dprintk(NDEBUG_SELECTION, ("scsi%d : target %d selected, going into MESSAGE OUT phase.\n", instance->host_no, cmd->device->id)); tmp[0] = IDENTIFY(((instance->irq == SCSI_IRQ_NONE) ? 0 : 1), cmd->device->lun); - if(instance->irq != SCSI_IRQ_NONE) - spin_lock_irq(instance->host_lock); - len = 1; cmd->tag = 0; @@ -1720,15 +1584,14 @@ part2: hostdata->connected = cmd; hostdata->busy[cmd->device->id] |= (1 << cmd->device->lun); - initialize_SCp(cmd); - + if (cmd->SCp.ptr != (char *)cmd->sense_buffer) { + initialize_SCp(cmd); + } return 0; /* Selection failed */ failed: - if(instance->irq != SCSI_IRQ_NONE) - spin_lock_irq(instance->host_lock); return -1; } @@ -1804,8 +1667,7 @@ static int NCR5380_transfer_pio(struct Scsi_Host *instance, unsigned char *phase while (!((tmp = NCR5380_read(STATUS_REG)) & SR_REQ) && !break_allowed); if (!(tmp & SR_REQ)) { /* timeout condition */ - hostdata->time_expires = jiffies + USLEEP_SLEEP; - NCR5380_set_timer(instance); + NCR5380_set_timer(hostdata, USLEEP_SLEEP); break; } @@ -2643,9 +2505,8 @@ static void NCR5380_information_transfer(struct Scsi_Host *instance) { */ NCR5380_transfer_pio(instance, &phase, &len, &data); if (!cmd->device->disconnect && should_disconnect(cmd->cmnd[0])) { - hostdata->time_expires = jiffies + USLEEP_SLEEP; + NCR5380_set_timer(hostdata, USLEEP_SLEEP); dprintk(NDEBUG_USLEEP, ("scsi%d : issued command, sleeping until %ul\n", instance->host_no, hostdata->time_expires)); - NCR5380_set_timer(instance); return; } break; @@ -2664,9 +2525,8 @@ static void NCR5380_information_transfer(struct Scsi_Host *instance) { /* RvC: go to sleep if polling time expired */ if (!cmd->device->disconnect && time_after_eq(jiffies, poll_time)) { - hostdata->time_expires = jiffies + USLEEP_SLEEP; + NCR5380_set_timer(hostdata, USLEEP_SLEEP); dprintk(NDEBUG_USLEEP, ("scsi%d : poll timed out, sleeping until %ul\n", instance->host_no, hostdata->time_expires)); - NCR5380_set_timer(instance); return; } } diff --git a/drivers/scsi/NCR5380.h b/drivers/scsi/NCR5380.h index 321540cb4..b5103f94d 100644 --- a/drivers/scsi/NCR5380.h +++ b/drivers/scsi/NCR5380.h @@ -251,7 +251,6 @@ struct NCR5380_hostdata { NCR5380_implementation_fields; /* implementation specific */ struct Scsi_Host *host; /* Host backpointer */ - struct NCR5380_hostdata *next; /* Next in our hot chain */ unsigned char id_mask, id_higher_mask; /* 1 << id, all bits greater */ unsigned char targets_present; /* targets we have connected to, so we can call a select @@ -270,7 +269,6 @@ struct NCR5380_hostdata { volatile unsigned aborted:1; /* flag, says aborted */ int flags; unsigned long time_expires; /* in jiffies, set prior to sleeping */ - struct Scsi_Host *next_timer; int select_time; /* timer in select for target response */ volatile Scsi_Cmnd *selecting; struct work_struct coroutine; /* our co-routine */ @@ -295,6 +293,7 @@ struct NCR5380_hostdata { static int NCR5380_probe_irq(struct Scsi_Host *instance, int possible); #endif static int NCR5380_init(struct Scsi_Host *instance, int flags); +static void NCR5380_exit(struct Scsi_Host *instance); static void NCR5380_information_transfer(struct Scsi_Host *instance); #ifndef DONT_USE_INTR static irqreturn_t NCR5380_intr(int irq, void *dev_id, struct pt_regs *regs); diff --git a/drivers/scsi/aha1542.h b/drivers/scsi/aha1542.h index cba290ce9..c402351dc 100644 --- a/drivers/scsi/aha1542.h +++ b/drivers/scsi/aha1542.h @@ -91,10 +91,12 @@ struct chain { }; /* These belong in scsi.h also */ -#define any2scsi(up, p) \ -(up)[0] = (((unsigned long)(p)) >> 16) ; \ -(up)[1] = (((unsigned long)(p)) >> 8); \ -(up)[2] = ((unsigned long)(p)); +static inline void any2scsi(u8 *p, u32 v) +{ + p[0] = v >> 16; + p[1] = v >> 8; + p[2] = v; +} #define scsi2int(up) ( (((long)*(up)) << 16) + (((long)(up)[1]) << 8) + ((long)(up)[2]) ) diff --git a/drivers/scsi/ahci.c b/drivers/scsi/ahci.c new file mode 100644 index 000000000..8dd5d1f09 --- /dev/null +++ b/drivers/scsi/ahci.c @@ -0,0 +1,1045 @@ +/* + * ahci.c - AHCI SATA support + * + * Copyright 2004 Red Hat, Inc. + * + * The contents of this file are subject to the Open + * Software License version 1.1 that can be found at + * http://www.opensource.org/licenses/osl-1.1.txt and is included herein + * by reference. + * + * Alternatively, the contents of this file may be used under the terms + * of the GNU General Public License version 2 (the "GPL") as distributed + * in the kernel source COPYING file, 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 OSL, 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 OSL or the GPL. + * + * Version 1.0 of the AHCI specification: + * http://www.intel.com/technology/serialata/pdf/rev1_0.pdf + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include "scsi.h" +#include +#include +#include + +#define DRV_NAME "ahci" +#define DRV_VERSION "1.00" + + +enum { + AHCI_PCI_BAR = 5, + AHCI_MAX_SG = 168, /* hardware max is 64K */ + AHCI_DMA_BOUNDARY = 0xffffffff, + AHCI_USE_CLUSTERING = 0, + AHCI_CMD_SLOT_SZ = 32 * 32, + AHCI_RX_FIS_SZ = 256, + AHCI_CMD_TBL_HDR = 0x80, + AHCI_CMD_TBL_SZ = AHCI_CMD_TBL_HDR + (AHCI_MAX_SG * 16), + AHCI_PORT_PRIV_DMA_SZ = AHCI_CMD_SLOT_SZ + AHCI_CMD_TBL_SZ + + AHCI_RX_FIS_SZ, + AHCI_IRQ_ON_SG = (1 << 31), + AHCI_CMD_ATAPI = (1 << 5), + AHCI_CMD_WRITE = (1 << 6), + + RX_FIS_D2H_REG = 0x40, /* offset of D2H Register FIS data */ + + board_ahci = 0, + + /* global controller registers */ + HOST_CAP = 0x00, /* host capabilities */ + HOST_CTL = 0x04, /* global host control */ + HOST_IRQ_STAT = 0x08, /* interrupt status */ + HOST_PORTS_IMPL = 0x0c, /* bitmap of implemented ports */ + HOST_VERSION = 0x10, /* AHCI spec. version compliancy */ + + /* HOST_CTL bits */ + HOST_RESET = (1 << 0), /* reset controller; self-clear */ + HOST_IRQ_EN = (1 << 1), /* global IRQ enable */ + HOST_AHCI_EN = (1 << 31), /* AHCI enabled */ + + /* HOST_CAP bits */ + HOST_CAP_64 = (1 << 31), /* PCI DAC (64-bit DMA) support */ + + /* registers for each SATA port */ + PORT_LST_ADDR = 0x00, /* command list DMA addr */ + PORT_LST_ADDR_HI = 0x04, /* command list DMA addr hi */ + PORT_FIS_ADDR = 0x08, /* FIS rx buf addr */ + PORT_FIS_ADDR_HI = 0x0c, /* FIS rx buf addr hi */ + PORT_IRQ_STAT = 0x10, /* interrupt status */ + PORT_IRQ_MASK = 0x14, /* interrupt enable/disable mask */ + PORT_CMD = 0x18, /* port command */ + PORT_TFDATA = 0x20, /* taskfile data */ + PORT_SIG = 0x24, /* device TF signature */ + PORT_CMD_ISSUE = 0x38, /* command issue */ + PORT_SCR = 0x28, /* SATA phy register block */ + PORT_SCR_STAT = 0x28, /* SATA phy register: SStatus */ + PORT_SCR_CTL = 0x2c, /* SATA phy register: SControl */ + PORT_SCR_ERR = 0x30, /* SATA phy register: SError */ + PORT_SCR_ACT = 0x34, /* SATA phy register: SActive */ + + /* PORT_IRQ_{STAT,MASK} bits */ + PORT_IRQ_COLD_PRES = (1 << 31), /* cold presence detect */ + PORT_IRQ_TF_ERR = (1 << 30), /* task file error */ + PORT_IRQ_HBUS_ERR = (1 << 29), /* host bus fatal error */ + PORT_IRQ_HBUS_DATA_ERR = (1 << 28), /* host bus data error */ + PORT_IRQ_IF_ERR = (1 << 27), /* interface fatal error */ + PORT_IRQ_IF_NONFATAL = (1 << 26), /* interface non-fatal error */ + PORT_IRQ_OVERFLOW = (1 << 24), /* xfer exhausted available S/G */ + PORT_IRQ_BAD_PMP = (1 << 23), /* incorrect port multiplier */ + + PORT_IRQ_PHYRDY = (1 << 22), /* PhyRdy changed */ + PORT_IRQ_DEV_ILCK = (1 << 7), /* device interlock */ + PORT_IRQ_CONNECT = (1 << 6), /* port connect change status */ + PORT_IRQ_SG_DONE = (1 << 5), /* descriptor processed */ + PORT_IRQ_UNK_FIS = (1 << 4), /* unknown FIS rx'd */ + PORT_IRQ_SDB_FIS = (1 << 3), /* Set Device Bits FIS rx'd */ + PORT_IRQ_DMAS_FIS = (1 << 2), /* DMA Setup FIS rx'd */ + PORT_IRQ_PIOS_FIS = (1 << 1), /* PIO Setup FIS rx'd */ + PORT_IRQ_D2H_REG_FIS = (1 << 0), /* D2H Register FIS rx'd */ + + PORT_IRQ_FATAL = PORT_IRQ_TF_ERR | + PORT_IRQ_HBUS_ERR | + PORT_IRQ_HBUS_DATA_ERR | + PORT_IRQ_IF_ERR, + DEF_PORT_IRQ = PORT_IRQ_FATAL | PORT_IRQ_PHYRDY | + PORT_IRQ_CONNECT | PORT_IRQ_SG_DONE | + PORT_IRQ_UNK_FIS | PORT_IRQ_SDB_FIS | + PORT_IRQ_DMAS_FIS | PORT_IRQ_PIOS_FIS | + PORT_IRQ_D2H_REG_FIS, + + /* PORT_CMD bits */ + PORT_CMD_LIST_ON = (1 << 15), /* cmd list DMA engine running */ + PORT_CMD_FIS_ON = (1 << 14), /* FIS DMA engine running */ + PORT_CMD_FIS_RX = (1 << 4), /* Enable FIS receive DMA engine */ + PORT_CMD_POWER_ON = (1 << 2), /* Power up device */ + PORT_CMD_SPIN_UP = (1 << 1), /* Spin up device */ + PORT_CMD_START = (1 << 0), /* Enable port DMA engine */ + + PORT_CMD_ICC_ACTIVE = (0x1 << 28), /* Put i/f in active state */ + PORT_CMD_ICC_PARTIAL = (0x2 << 28), /* Put i/f in partial state */ + PORT_CMD_ICC_SLUMBER = (0x6 << 28), /* Put i/f in slumber state */ +}; + +struct ahci_cmd_hdr { + u32 opts; + u32 status; + u32 tbl_addr; + u32 tbl_addr_hi; + u32 reserved[4]; +}; + +struct ahci_sg { + u32 addr; + u32 addr_hi; + u32 reserved; + u32 flags_size; +}; + +struct ahci_host_priv { + unsigned long flags; + u32 cap; /* cache of HOST_CAP register */ + u32 port_map; /* cache of HOST_PORTS_IMPL reg */ +}; + +struct ahci_port_priv { + struct ahci_cmd_hdr *cmd_slot; + dma_addr_t cmd_slot_dma; + void *cmd_tbl; + dma_addr_t cmd_tbl_dma; + struct ahci_sg *cmd_tbl_sg; + void *rx_fis; + dma_addr_t rx_fis_dma; +}; + +static u32 ahci_scr_read (struct ata_port *ap, unsigned int sc_reg); +static void ahci_scr_write (struct ata_port *ap, unsigned int sc_reg, u32 val); +static int ahci_init_one (struct pci_dev *pdev, const struct pci_device_id *ent); +static int ahci_qc_issue(struct ata_queued_cmd *qc); +static irqreturn_t ahci_interrupt (int irq, void *dev_instance, struct pt_regs *regs); +static void ahci_phy_reset(struct ata_port *ap); +static void ahci_irq_clear(struct ata_port *ap); +static void ahci_eng_timeout(struct ata_port *ap); +static int ahci_port_start(struct ata_port *ap); +static void ahci_port_stop(struct ata_port *ap); +static void ahci_host_stop(struct ata_host_set *host_set); +static void ahci_qc_prep(struct ata_queued_cmd *qc); +static u8 ahci_check_status(struct ata_port *ap); +static inline int ahci_host_intr(struct ata_port *ap, struct ata_queued_cmd *qc); + +static Scsi_Host_Template ahci_sht = { + .module = THIS_MODULE, + .name = DRV_NAME, + .ioctl = ata_scsi_ioctl, + .queuecommand = ata_scsi_queuecmd, + .eh_strategy_handler = ata_scsi_error, + .can_queue = ATA_DEF_QUEUE, + .this_id = ATA_SHT_THIS_ID, + .sg_tablesize = AHCI_MAX_SG, + .max_sectors = ATA_MAX_SECTORS, + .cmd_per_lun = ATA_SHT_CMD_PER_LUN, + .emulated = ATA_SHT_EMULATED, + .use_clustering = AHCI_USE_CLUSTERING, + .proc_name = DRV_NAME, + .dma_boundary = AHCI_DMA_BOUNDARY, + .slave_configure = ata_scsi_slave_config, + .bios_param = ata_std_bios_param, +}; + +static struct ata_port_operations ahci_ops = { + .port_disable = ata_port_disable, + + .check_status = ahci_check_status, + .dev_select = ata_noop_dev_select, + + .phy_reset = ahci_phy_reset, + + .qc_prep = ahci_qc_prep, + .qc_issue = ahci_qc_issue, + + .eng_timeout = ahci_eng_timeout, + + .irq_handler = ahci_interrupt, + .irq_clear = ahci_irq_clear, + + .scr_read = ahci_scr_read, + .scr_write = ahci_scr_write, + + .port_start = ahci_port_start, + .port_stop = ahci_port_stop, + .host_stop = ahci_host_stop, +}; + +static struct ata_port_info ahci_port_info[] = { + /* board_ahci */ + { + .sht = &ahci_sht, + .host_flags = ATA_FLAG_SATA | ATA_FLAG_NO_LEGACY | + ATA_FLAG_SATA_RESET | ATA_FLAG_MMIO | + ATA_FLAG_PIO_DMA, + .pio_mask = 0x03, /* pio3-4 */ + .udma_mask = 0x7f, /* udma0-6 ; FIXME */ + .port_ops = &ahci_ops, + }, +}; + +static struct pci_device_id ahci_pci_tbl[] = { + { PCI_VENDOR_ID_INTEL, 0x2652, PCI_ANY_ID, PCI_ANY_ID, 0, 0, + board_ahci }, + { PCI_VENDOR_ID_INTEL, 0x2653, PCI_ANY_ID, PCI_ANY_ID, 0, 0, + board_ahci }, + { } /* terminate list */ +}; + + +static struct pci_driver ahci_pci_driver = { + .name = DRV_NAME, + .id_table = ahci_pci_tbl, + .probe = ahci_init_one, + .remove = ata_pci_remove_one, +}; + + +static inline unsigned long ahci_port_base_ul (unsigned long base, unsigned int port) +{ + return base + 0x100 + (port * 0x80); +} + +static inline void *ahci_port_base (void *base, unsigned int port) +{ + return (void *) ahci_port_base_ul((unsigned long)base, port); +} + +static void ahci_host_stop(struct ata_host_set *host_set) +{ + struct ahci_host_priv *hpriv = host_set->private_data; + kfree(hpriv); +} + +static int ahci_port_start(struct ata_port *ap) +{ + struct pci_dev *pdev = ap->host_set->pdev; + struct ahci_host_priv *hpriv = ap->host_set->private_data; + struct ahci_port_priv *pp; + int rc; + void *mem, *mmio = ap->host_set->mmio_base; + void *port_mmio = ahci_port_base(mmio, ap->port_no); + dma_addr_t mem_dma; + + rc = ata_port_start(ap); + if (rc) + return rc; + + pp = kmalloc(sizeof(*pp), GFP_KERNEL); + if (!pp) { + rc = -ENOMEM; + goto err_out; + } + memset(pp, 0, sizeof(*pp)); + + mem = pci_alloc_consistent(pdev, AHCI_PORT_PRIV_DMA_SZ, &mem_dma); + if (!mem) { + rc = -ENOMEM; + goto err_out_kfree; + } + memset(mem, 0, AHCI_PORT_PRIV_DMA_SZ); + + /* + * First item in chunk of DMA memory: 32-slot command table, + * 32 bytes each in size + */ + pp->cmd_slot = mem; + pp->cmd_slot_dma = mem_dma; + + mem += AHCI_CMD_SLOT_SZ; + mem_dma += AHCI_CMD_SLOT_SZ; + + /* + * Second item: Received-FIS area + */ + pp->rx_fis = mem; + pp->rx_fis_dma = mem_dma; + + mem += AHCI_RX_FIS_SZ; + mem_dma += AHCI_RX_FIS_SZ; + + /* + * Third item: data area for storing a single command + * and its scatter-gather table + */ + pp->cmd_tbl = mem; + pp->cmd_tbl_dma = mem_dma; + + pp->cmd_tbl_sg = mem + AHCI_CMD_TBL_HDR; + + ap->private_data = pp; + + if (hpriv->cap & HOST_CAP_64) + writel((pp->cmd_slot_dma >> 16) >> 16, port_mmio + PORT_LST_ADDR_HI); + writel(pp->cmd_slot_dma & 0xffffffff, port_mmio + PORT_LST_ADDR); + readl(port_mmio + PORT_LST_ADDR); /* flush */ + + if (hpriv->cap & HOST_CAP_64) + writel((pp->rx_fis_dma >> 16) >> 16, port_mmio + PORT_FIS_ADDR_HI); + writel(pp->rx_fis_dma & 0xffffffff, port_mmio + PORT_FIS_ADDR); + readl(port_mmio + PORT_FIS_ADDR); /* flush */ + + writel(PORT_CMD_ICC_ACTIVE | PORT_CMD_FIS_RX | + PORT_CMD_POWER_ON | PORT_CMD_SPIN_UP | + PORT_CMD_START, port_mmio + PORT_CMD); + readl(port_mmio + PORT_CMD); /* flush */ + + return 0; + +err_out_kfree: + kfree(pp); +err_out: + ata_port_stop(ap); + return rc; +} + + +static void ahci_port_stop(struct ata_port *ap) +{ + struct pci_dev *pdev = ap->host_set->pdev; + struct ahci_port_priv *pp = ap->private_data; + void *mmio = ap->host_set->mmio_base; + void *port_mmio = ahci_port_base(mmio, ap->port_no); + u32 tmp; + + tmp = readl(port_mmio + PORT_CMD); + tmp &= ~(PORT_CMD_START | PORT_CMD_FIS_RX); + writel(tmp, port_mmio + PORT_CMD); + readl(port_mmio + PORT_CMD); /* flush */ + + /* spec says 500 msecs for each PORT_CMD_{START,FIS_RX} bit, so + * this is slightly incorrect. + */ + msleep(500); + + ap->private_data = NULL; + pci_free_consistent(pdev, AHCI_PORT_PRIV_DMA_SZ, + pp->cmd_slot, pp->cmd_slot_dma); + kfree(pp); + ata_port_stop(ap); +} + +static u32 ahci_scr_read (struct ata_port *ap, unsigned int sc_reg_in) +{ + unsigned int sc_reg; + + switch (sc_reg_in) { + case SCR_STATUS: sc_reg = 0; break; + case SCR_CONTROL: sc_reg = 1; break; + case SCR_ERROR: sc_reg = 2; break; + case SCR_ACTIVE: sc_reg = 3; break; + default: + return 0xffffffffU; + } + + return readl((void *) ap->ioaddr.scr_addr + (sc_reg * 4)); +} + + +static void ahci_scr_write (struct ata_port *ap, unsigned int sc_reg_in, + u32 val) +{ + unsigned int sc_reg; + + switch (sc_reg_in) { + case SCR_STATUS: sc_reg = 0; break; + case SCR_CONTROL: sc_reg = 1; break; + case SCR_ERROR: sc_reg = 2; break; + case SCR_ACTIVE: sc_reg = 3; break; + default: + return; + } + + writel(val, (void *) ap->ioaddr.scr_addr + (sc_reg * 4)); +} + +static void ahci_phy_reset(struct ata_port *ap) +{ + void __iomem *port_mmio = (void __iomem *) ap->ioaddr.cmd_addr; + struct ata_taskfile tf; + struct ata_device *dev = &ap->device[0]; + u32 tmp; + + __sata_phy_reset(ap); + + if (ap->flags & ATA_FLAG_PORT_DISABLED) + return; + + tmp = readl(port_mmio + PORT_SIG); + tf.lbah = (tmp >> 24) & 0xff; + tf.lbam = (tmp >> 16) & 0xff; + tf.lbal = (tmp >> 8) & 0xff; + tf.nsect = (tmp) & 0xff; + + dev->class = ata_dev_classify(&tf); + if (!ata_dev_present(dev)) + ata_port_disable(ap); +} + +static u8 ahci_check_status(struct ata_port *ap) +{ + void *mmio = (void *) ap->ioaddr.cmd_addr; + + return readl(mmio + PORT_TFDATA) & 0xFF; +} + +static void ahci_fill_sg(struct ata_queued_cmd *qc) +{ + struct ahci_port_priv *pp = qc->ap->private_data; + unsigned int i; + + VPRINTK("ENTER\n"); + + /* + * Next, the S/G list. + */ + for (i = 0; i < qc->n_elem; i++) { + u32 sg_len; + dma_addr_t addr; + + addr = sg_dma_address(&qc->sg[i]); + sg_len = sg_dma_len(&qc->sg[i]); + + pp->cmd_tbl_sg[i].addr = cpu_to_le32(addr & 0xffffffff); + pp->cmd_tbl_sg[i].addr_hi = cpu_to_le32((addr >> 16) >> 16); + pp->cmd_tbl_sg[i].flags_size = cpu_to_le32(sg_len - 1); + } +} + +static void ahci_qc_prep(struct ata_queued_cmd *qc) +{ + struct ahci_port_priv *pp = qc->ap->private_data; + u32 opts; + const u32 cmd_fis_len = 5; /* five dwords */ + + /* + * Fill in command slot information (currently only one slot, + * slot 0, is currently since we don't do queueing) + */ + + opts = (qc->n_elem << 16) | cmd_fis_len; + if (qc->tf.flags & ATA_TFLAG_WRITE) + opts |= AHCI_CMD_WRITE; + + switch (qc->tf.protocol) { + case ATA_PROT_ATAPI: + case ATA_PROT_ATAPI_NODATA: + case ATA_PROT_ATAPI_DMA: + opts |= AHCI_CMD_ATAPI; + break; + + default: + /* do nothing */ + break; + } + + pp->cmd_slot[0].opts = cpu_to_le32(opts); + pp->cmd_slot[0].status = 0; + pp->cmd_slot[0].tbl_addr = cpu_to_le32(pp->cmd_tbl_dma & 0xffffffff); + pp->cmd_slot[0].tbl_addr_hi = cpu_to_le32((pp->cmd_tbl_dma >> 16) >> 16); + + /* + * Fill in command table information. First, the header, + * a SATA Register - Host to Device command FIS. + */ + ata_tf_to_fis(&qc->tf, pp->cmd_tbl, 0); + + if (!(qc->flags & ATA_QCFLAG_DMAMAP)) + return; + + ahci_fill_sg(qc); +} + +static inline void ahci_dma_complete (struct ata_port *ap, + struct ata_queued_cmd *qc, + int have_err) +{ + /* get drive status; clear intr; complete txn */ + ata_qc_complete(ata_qc_from_tag(ap, ap->active_tag), + have_err ? ATA_ERR : 0); +} + +static void ahci_intr_error(struct ata_port *ap, u32 irq_stat) +{ + void *mmio = ap->host_set->mmio_base; + void *port_mmio = ahci_port_base(mmio, ap->port_no); + u32 tmp; + int work; + + /* stop DMA */ + tmp = readl(port_mmio + PORT_CMD); + tmp &= PORT_CMD_START | PORT_CMD_FIS_RX; + writel(tmp, port_mmio + PORT_CMD); + + /* wait for engine to stop. TODO: this could be + * as long as 500 msec + */ + work = 1000; + while (work-- > 0) { + tmp = readl(port_mmio + PORT_CMD); + if ((tmp & PORT_CMD_LIST_ON) == 0) + break; + udelay(10); + } + + /* clear SATA phy error, if any */ + tmp = readl(port_mmio + PORT_SCR_ERR); + writel(tmp, port_mmio + PORT_SCR_ERR); + + /* if DRQ/BSY is set, device needs to be reset. + * if so, issue COMRESET + */ + tmp = readl(port_mmio + PORT_TFDATA); + if (tmp & (ATA_BUSY | ATA_DRQ)) { + writel(0x301, port_mmio + PORT_SCR_CTL); + readl(port_mmio + PORT_SCR_CTL); /* flush */ + udelay(10); + writel(0x300, port_mmio + PORT_SCR_CTL); + readl(port_mmio + PORT_SCR_CTL); /* flush */ + } + + /* re-start DMA */ + tmp = readl(port_mmio + PORT_CMD); + tmp |= PORT_CMD_START | PORT_CMD_FIS_RX; + writel(tmp, port_mmio + PORT_CMD); + readl(port_mmio + PORT_CMD); /* flush */ + + printk(KERN_WARNING "ata%u: error occurred, port reset\n", ap->port_no); +} + +static void ahci_eng_timeout(struct ata_port *ap) +{ + void *mmio = ap->host_set->mmio_base; + void *port_mmio = ahci_port_base(mmio, ap->port_no); + struct ata_queued_cmd *qc; + + DPRINTK("ENTER\n"); + + ahci_intr_error(ap, readl(port_mmio + PORT_IRQ_STAT)); + + qc = ata_qc_from_tag(ap, ap->active_tag); + if (!qc) { + printk(KERN_ERR "ata%u: BUG: timeout without command\n", + ap->id); + } else { + /* hack alert! We cannot use the supplied completion + * function from inside the ->eh_strategy_handler() thread. + * libata is the only user of ->eh_strategy_handler() in + * any kernel, so the default scsi_done() assumes it is + * not being called from the SCSI EH. + */ + qc->scsidone = scsi_finish_command; + ata_qc_complete(qc, ATA_ERR); + } + +} + +static inline int ahci_host_intr(struct ata_port *ap, struct ata_queued_cmd *qc) +{ + void *mmio = ap->host_set->mmio_base; + void *port_mmio = ahci_port_base(mmio, ap->port_no); + u32 status, serr, ci; + + serr = readl(port_mmio + PORT_SCR_ERR); + writel(serr, port_mmio + PORT_SCR_ERR); + + status = readl(port_mmio + PORT_IRQ_STAT); + writel(status, port_mmio + PORT_IRQ_STAT); + + ci = readl(port_mmio + PORT_CMD_ISSUE); + if (likely((ci & 0x1) == 0)) { + if (qc) { + ata_qc_complete(qc, 0); + qc = NULL; + } + } + + if (status & PORT_IRQ_FATAL) { + ahci_intr_error(ap, status); + if (qc) + ata_qc_complete(qc, ATA_ERR); + } + + return 1; +} + +static void ahci_irq_clear(struct ata_port *ap) +{ + /* TODO */ +} + +static irqreturn_t ahci_interrupt (int irq, void *dev_instance, struct pt_regs *regs) +{ + struct ata_host_set *host_set = dev_instance; + struct ahci_host_priv *hpriv; + unsigned int i, handled = 0; + void *mmio; + u32 irq_stat, irq_ack = 0; + + VPRINTK("ENTER\n"); + + hpriv = host_set->private_data; + mmio = host_set->mmio_base; + + /* sigh. 0xffffffff is a valid return from h/w */ + irq_stat = readl(mmio + HOST_IRQ_STAT); + irq_stat &= hpriv->port_map; + if (!irq_stat) + return IRQ_NONE; + + spin_lock(&host_set->lock); + + for (i = 0; i < host_set->n_ports; i++) { + struct ata_port *ap; + u32 tmp; + + VPRINTK("port %u\n", i); + ap = host_set->ports[i]; + tmp = irq_stat & (1 << i); + if (tmp && ap) { + struct ata_queued_cmd *qc; + qc = ata_qc_from_tag(ap, ap->active_tag); + if (ahci_host_intr(ap, qc)) + irq_ack |= (1 << i); + } + } + + if (irq_ack) { + writel(irq_ack, mmio + HOST_IRQ_STAT); + handled = 1; + } + + spin_unlock(&host_set->lock); + + VPRINTK("EXIT\n"); + + return IRQ_RETVAL(handled); +} + +static int ahci_qc_issue(struct ata_queued_cmd *qc) +{ + struct ata_port *ap = qc->ap; + void *port_mmio = (void *) ap->ioaddr.cmd_addr; + + writel(1, port_mmio + PORT_SCR_ACT); + readl(port_mmio + PORT_SCR_ACT); /* flush */ + + writel(1, port_mmio + PORT_CMD_ISSUE); + readl(port_mmio + PORT_CMD_ISSUE); /* flush */ + + return 0; +} + +static void ahci_setup_port(struct ata_ioports *port, unsigned long base, + unsigned int port_idx) +{ + VPRINTK("ENTER, base==0x%lx, port_idx %u\n", base, port_idx); + base = ahci_port_base_ul(base, port_idx); + VPRINTK("base now==0x%lx\n", base); + + port->cmd_addr = base; + port->scr_addr = base + PORT_SCR; + + VPRINTK("EXIT\n"); +} + +static int ahci_host_init(struct ata_probe_ent *probe_ent) +{ + struct ahci_host_priv *hpriv = probe_ent->private_data; + struct pci_dev *pdev = probe_ent->pdev; + void __iomem *mmio = probe_ent->mmio_base; + u32 tmp, cap_save; + u16 tmp16; + unsigned int i, j, using_dac; + int rc; + void __iomem *port_mmio; + + cap_save = readl(mmio + HOST_CAP); + cap_save &= ( (1<<28) | (1<<17) ); + cap_save |= (1 << 27); + + /* global controller reset */ + tmp = readl(mmio + HOST_CTL); + if ((tmp & HOST_RESET) == 0) { + writel(tmp | HOST_RESET, mmio + HOST_CTL); + readl(mmio + HOST_CTL); /* flush */ + } + + /* reset must complete within 1 second, or + * the hardware should be considered fried. + */ + ssleep(1); + + tmp = readl(mmio + HOST_CTL); + if (tmp & HOST_RESET) { + printk(KERN_ERR DRV_NAME "(%s): controller reset failed (0x%x)\n", + pci_name(pdev), tmp); + return -EIO; + } + + writel(HOST_AHCI_EN, mmio + HOST_CTL); + (void) readl(mmio + HOST_CTL); /* flush */ + writel(cap_save, mmio + HOST_CAP); + writel(0xf, mmio + HOST_PORTS_IMPL); + (void) readl(mmio + HOST_PORTS_IMPL); /* flush */ + + pci_read_config_word(pdev, 0x92, &tmp16); + tmp16 |= 0xf; + pci_write_config_word(pdev, 0x92, tmp16); + + hpriv->cap = readl(mmio + HOST_CAP); + hpriv->port_map = readl(mmio + HOST_PORTS_IMPL); + probe_ent->n_ports = (hpriv->cap & 0x1f) + 1; + + VPRINTK("cap 0x%x port_map 0x%x n_ports %d\n", + hpriv->cap, hpriv->port_map, probe_ent->n_ports); + + using_dac = hpriv->cap & HOST_CAP_64; + if (using_dac && + !pci_set_dma_mask(pdev, 0xffffffffffffffffULL)) { + rc = pci_set_consistent_dma_mask(pdev, 0xffffffffffffffffULL); + if (rc) { + rc = pci_set_consistent_dma_mask(pdev, 0xffffffffULL); + if (rc) { + printk(KERN_ERR DRV_NAME "(%s): 64-bit DMA enable failed\n", + pci_name(pdev)); + return rc; + } + } + + hpriv->flags |= HOST_CAP_64; + } else { + rc = pci_set_dma_mask(pdev, 0xffffffffULL); + if (rc) { + printk(KERN_ERR DRV_NAME "(%s): 32-bit DMA enable failed\n", + pci_name(pdev)); + return rc; + } + rc = pci_set_consistent_dma_mask(pdev, 0xffffffffULL); + if (rc) { + printk(KERN_ERR DRV_NAME "(%s): 32-bit consistent DMA enable failed\n", + pci_name(pdev)); + return rc; + } + } + + for (i = 0; i < probe_ent->n_ports; i++) { +#if 0 /* BIOSen initialize this incorrectly */ + if (!(hpriv->port_map & (1 << i))) + continue; +#endif + + port_mmio = ahci_port_base(mmio, i); + VPRINTK("mmio %p port_mmio %p\n", mmio, port_mmio); + + ahci_setup_port(&probe_ent->port[i], + (unsigned long) mmio, i); + + /* make sure port is not active */ + tmp = readl(port_mmio + PORT_CMD); + VPRINTK("PORT_CMD 0x%x\n", tmp); + if (tmp & (PORT_CMD_LIST_ON | PORT_CMD_FIS_ON | + PORT_CMD_FIS_RX | PORT_CMD_START)) { + tmp &= ~(PORT_CMD_LIST_ON | PORT_CMD_FIS_ON | + PORT_CMD_FIS_RX | PORT_CMD_START); + writel(tmp, port_mmio + PORT_CMD); + readl(port_mmio + PORT_CMD); /* flush */ + + /* spec says 500 msecs for each bit, so + * this is slightly incorrect. + */ + msleep(500); + } + + writel(PORT_CMD_SPIN_UP, port_mmio + PORT_CMD); + + j = 0; + while (j < 100) { + msleep(10); + tmp = readl(port_mmio + PORT_SCR_STAT); + if ((tmp & 0xf) == 0x3) + break; + j++; + } + + tmp = readl(port_mmio + PORT_SCR_ERR); + VPRINTK("PORT_SCR_ERR 0x%x\n", tmp); + writel(tmp, port_mmio + PORT_SCR_ERR); + + /* ack any pending irq events for this port */ + tmp = readl(port_mmio + PORT_IRQ_STAT); + VPRINTK("PORT_IRQ_STAT 0x%x\n", tmp); + if (tmp) + writel(tmp, port_mmio + PORT_IRQ_STAT); + + writel(1 << i, mmio + HOST_IRQ_STAT); + + /* set irq mask (enables interrupts) */ + writel(DEF_PORT_IRQ, port_mmio + PORT_IRQ_MASK); + } + + tmp = readl(mmio + HOST_CTL); + VPRINTK("HOST_CTL 0x%x\n", tmp); + writel(tmp | HOST_IRQ_EN, mmio + HOST_CTL); + tmp = readl(mmio + HOST_CTL); + VPRINTK("HOST_CTL 0x%x\n", tmp); + + pci_set_master(pdev); + + return 0; +} + +/* move to PCI layer, integrate w/ MSI stuff */ +static void pci_enable_intx(struct pci_dev *pdev) +{ + u16 pci_command; + + pci_read_config_word(pdev, PCI_COMMAND, &pci_command); + if (pci_command & PCI_COMMAND_INTX_DISABLE) { + pci_command &= ~PCI_COMMAND_INTX_DISABLE; + pci_write_config_word(pdev, PCI_COMMAND, pci_command); + } +} + +static void ahci_print_info(struct ata_probe_ent *probe_ent) +{ + struct ahci_host_priv *hpriv = probe_ent->private_data; + struct pci_dev *pdev = probe_ent->pdev; + void *mmio = probe_ent->mmio_base; + u32 vers, cap, impl, speed; + const char *speed_s; + u16 cc; + const char *scc_s; + + vers = readl(mmio + HOST_VERSION); + cap = hpriv->cap; + impl = hpriv->port_map; + + speed = (cap >> 20) & 0xf; + if (speed == 1) + speed_s = "1.5"; + else if (speed == 2) + speed_s = "3"; + else + speed_s = "?"; + + pci_read_config_word(pdev, 0x0a, &cc); + if (cc == 0x0101) + scc_s = "IDE"; + else if (cc == 0x0106) + scc_s = "SATA"; + else if (cc == 0x0104) + scc_s = "RAID"; + else + scc_s = "unknown"; + + printk(KERN_INFO DRV_NAME "(%s) AHCI %02x%02x.%02x%02x " + "%u slots %u ports %s Gbps 0x%x impl %s mode\n" + , + pci_name(pdev), + + (vers >> 24) & 0xff, + (vers >> 16) & 0xff, + (vers >> 8) & 0xff, + vers & 0xff, + + ((cap >> 8) & 0x1f) + 1, + (cap & 0x1f) + 1, + speed_s, + impl, + scc_s); + + printk(KERN_INFO DRV_NAME "(%s) flags: " + "%s%s%s%s%s%s" + "%s%s%s%s%s%s%s\n" + , + pci_name(pdev), + + cap & (1 << 31) ? "64bit " : "", + cap & (1 << 30) ? "ncq " : "", + cap & (1 << 28) ? "ilck " : "", + cap & (1 << 27) ? "stag " : "", + cap & (1 << 26) ? "pm " : "", + cap & (1 << 25) ? "led " : "", + + cap & (1 << 24) ? "clo " : "", + cap & (1 << 19) ? "nz " : "", + cap & (1 << 18) ? "only " : "", + cap & (1 << 17) ? "pmp " : "", + cap & (1 << 15) ? "pio " : "", + cap & (1 << 14) ? "slum " : "", + cap & (1 << 13) ? "part " : "" + ); +} + +static int ahci_init_one (struct pci_dev *pdev, const struct pci_device_id *ent) +{ + static int printed_version; + struct ata_probe_ent *probe_ent = NULL; + struct ahci_host_priv *hpriv; + unsigned long base; + void *mmio_base; + unsigned int board_idx = (unsigned int) ent->driver_data; + int rc; + + VPRINTK("ENTER\n"); + + if (!printed_version++) + printk(KERN_DEBUG DRV_NAME " version " DRV_VERSION "\n"); + + /* + * If this driver happens to only be useful on Apple's K2, then + * we should check that here as it has a normal Serverworks ID + */ + rc = pci_enable_device(pdev); + if (rc) + return rc; + + rc = pci_request_regions(pdev, DRV_NAME); + if (rc) + goto err_out; + + pci_enable_intx(pdev); + + probe_ent = kmalloc(sizeof(*probe_ent), GFP_KERNEL); + if (probe_ent == NULL) { + rc = -ENOMEM; + goto err_out_regions; + } + + memset(probe_ent, 0, sizeof(*probe_ent)); + probe_ent->pdev = pdev; + INIT_LIST_HEAD(&probe_ent->node); + + mmio_base = ioremap(pci_resource_start(pdev, AHCI_PCI_BAR), + pci_resource_len(pdev, AHCI_PCI_BAR)); + if (mmio_base == NULL) { + rc = -ENOMEM; + goto err_out_free_ent; + } + base = (unsigned long) mmio_base; + + hpriv = kmalloc(sizeof(*hpriv), GFP_KERNEL); + if (!hpriv) { + rc = -ENOMEM; + goto err_out_iounmap; + } + memset(hpriv, 0, sizeof(*hpriv)); + + probe_ent->sht = ahci_port_info[board_idx].sht; + probe_ent->host_flags = ahci_port_info[board_idx].host_flags; + probe_ent->pio_mask = ahci_port_info[board_idx].pio_mask; + probe_ent->udma_mask = ahci_port_info[board_idx].udma_mask; + probe_ent->port_ops = ahci_port_info[board_idx].port_ops; + + probe_ent->irq = pdev->irq; + probe_ent->irq_flags = SA_SHIRQ; + probe_ent->mmio_base = mmio_base; + probe_ent->private_data = hpriv; + + /* initialize adapter */ + rc = ahci_host_init(probe_ent); + if (rc) + goto err_out_hpriv; + + ahci_print_info(probe_ent); + + /* FIXME: check ata_device_add return value */ + ata_device_add(probe_ent); + kfree(probe_ent); + + return 0; + +err_out_hpriv: + kfree(hpriv); +err_out_iounmap: + iounmap(mmio_base); +err_out_free_ent: + kfree(probe_ent); +err_out_regions: + pci_release_regions(pdev); +err_out: + pci_disable_device(pdev); + return rc; +} + + +static int __init ahci_init(void) +{ + return pci_module_init(&ahci_pci_driver); +} + + +static void __exit ahci_exit(void) +{ + pci_unregister_driver(&ahci_pci_driver); +} + + +MODULE_AUTHOR("Jeff Garzik"); +MODULE_DESCRIPTION("AHCI SATA low-level driver"); +MODULE_LICENSE("GPL"); +MODULE_DEVICE_TABLE(pci, ahci_pci_tbl); +MODULE_VERSION(DRV_VERSION); + +module_init(ahci_init); +module_exit(ahci_exit); diff --git a/drivers/scsi/aic7xxx/.cvsignore b/drivers/scsi/aic7xxx/.cvsignore new file mode 100644 index 000000000..a1a7fcd04 --- /dev/null +++ b/drivers/scsi/aic7xxx/.cvsignore @@ -0,0 +1,4 @@ +aic79xx_reg.h +aic79xx_seq.h +aic7xxx_reg.h +aic7xxx_seq.h diff --git a/drivers/scsi/aic7xxx/Kconfig.aic79xx b/drivers/scsi/aic7xxx/Kconfig.aic79xx index b29981521..c2523a30a 100644 --- a/drivers/scsi/aic7xxx/Kconfig.aic79xx +++ b/drivers/scsi/aic7xxx/Kconfig.aic79xx @@ -46,7 +46,7 @@ config AIC79XX_RESET_DELAY_MS config AIC79XX_BUILD_FIRMWARE bool "Build Adapter Firmware with Kernel Build" - depends on SCSI_AIC79XX + depends on SCSI_AIC79XX && !PREVENT_FIRMWARE_BUILD help This option should only be enabled if you are modifying the firmware source to the aic79xx driver and wish to have the generated firmware diff --git a/drivers/scsi/aic7xxx/Kconfig.aic7xxx b/drivers/scsi/aic7xxx/Kconfig.aic7xxx index 473a658d2..8398e0dd4 100644 --- a/drivers/scsi/aic7xxx/Kconfig.aic7xxx +++ b/drivers/scsi/aic7xxx/Kconfig.aic7xxx @@ -61,7 +61,7 @@ config AIC7XXX_PROBE_EISA_VL config AIC7XXX_BUILD_FIRMWARE bool "Build Adapter Firmware with Kernel Build" - depends on SCSI_AIC7XXX + depends on SCSI_AIC7XXX && !PREVENT_FIRMWARE_BUILD help This option should only be enabled if you are modifying the firmware source to the aic7xxx driver and wish to have the generated firmware diff --git a/drivers/scsi/aic7xxx/aic79xx_pci.c b/drivers/scsi/aic7xxx/aic79xx_pci.c index 913984539..7cc83dafa 100644 --- a/drivers/scsi/aic7xxx/aic79xx_pci.c +++ b/drivers/scsi/aic7xxx/aic79xx_pci.c @@ -452,8 +452,10 @@ ahd_pci_test_register_access(struct ahd_softc *ahd) * or read prefetching could be initiated by the * CPU or host bridge. Our device does not support * either, so look for data corruption and/or flaged - * PCI errors. + * PCI errors. First pause without causing another + * chip reset. */ + hcntrl &= ~CHIPRST; ahd_outb(ahd, HCNTRL, hcntrl|PAUSE); while (ahd_is_paused(ahd) == 0) ; diff --git a/drivers/scsi/aic7xxx/aic7xxx_pci.c b/drivers/scsi/aic7xxx/aic7xxx_pci.c index 01cfab0e3..476780bae 100644 --- a/drivers/scsi/aic7xxx/aic7xxx_pci.c +++ b/drivers/scsi/aic7xxx/aic7xxx_pci.c @@ -1284,8 +1284,10 @@ ahc_pci_test_register_access(struct ahc_softc *ahc) * or read prefetching could be initiated by the * CPU or host bridge. Our device does not support * either, so look for data corruption and/or flagged - * PCI errors. + * PCI errors. First pause without causing another + * chip reset. */ + hcntrl &= ~CHIPRST; ahc_outb(ahc, HCNTRL, hcntrl|PAUSE); while (ahc_is_paused(ahc) == 0) ; diff --git a/drivers/scsi/aic7xxx/aicasm/Makefile b/drivers/scsi/aic7xxx/aicasm/Makefile index e8c345b8e..8c91fda64 100644 --- a/drivers/scsi/aic7xxx/aicasm/Makefile +++ b/drivers/scsi/aic7xxx/aicasm/Makefile @@ -34,10 +34,14 @@ $(PROG): ${GENHDRS} $(SRCS) $(AICASM_CC) $(AICASM_CFLAGS) $(SRCS) -o $(PROG) $(LIBS) aicdb.h: - @if [ -e "/usr/include/db3/db_185.h" ]; then \ + @if [ -e "/usr/include/db4/db_185.h" ]; then \ + echo "#include " > aicdb.h; \ + elif [ -e "/usr/include/db3/db_185.h" ]; then \ echo "#include " > aicdb.h; \ elif [ -e "/usr/include/db2/db_185.h" ]; then \ echo "#include " > aicdb.h; \ + elif [ -e "/usr/include/db1/db_185.h" ]; then \ + echo "#include " > aicdb.h; \ elif [ -e "/usr/include/db/db_185.h" ]; then \ echo "#include " > aicdb.h; \ elif [ -e "/usr/include/db_185.h" ]; then \ diff --git a/drivers/scsi/ibmvscsi/Makefile b/drivers/scsi/ibmvscsi/Makefile new file mode 100644 index 000000000..4e247b6b8 --- /dev/null +++ b/drivers/scsi/ibmvscsi/Makefile @@ -0,0 +1,5 @@ +obj-$(CONFIG_SCSI_IBMVSCSI) += ibmvscsic.o + +ibmvscsic-y += ibmvscsi.o +ibmvscsic-$(CONFIG_PPC_ISERIES) += iseries_vscsi.o +ibmvscsic-$(CONFIG_PPC_PSERIES) += rpa_vscsi.o diff --git a/drivers/scsi/ibmvscsi/ibmvscsi.c b/drivers/scsi/ibmvscsi/ibmvscsi.c new file mode 100644 index 000000000..210ee7d0d --- /dev/null +++ b/drivers/scsi/ibmvscsi/ibmvscsi.c @@ -0,0 +1,1393 @@ +/* ------------------------------------------------------------ + * ibmvscsi.c + * (C) Copyright IBM Corporation 1994, 2004 + * Authors: Colin DeVilbiss (devilbis@us.ibm.com) + * Santiago Leon (santil@us.ibm.com) + * Dave Boutcher (sleddog@us.ibm.com) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * 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 + * + * ------------------------------------------------------------ + * Emulation of a SCSI host adapter for Virtual I/O devices + * + * This driver supports the SCSI adapter implemented by the IBM + * Power5 firmware. That SCSI adapter is not a physical adapter, + * but allows Linux SCSI peripheral drivers to directly + * access devices in another logical partition on the physical system. + * + * The virtual adapter(s) are present in the open firmware device + * tree just like real adapters. + * + * One of the capabilities provided on these systems is the ability + * to DMA between partitions. The architecture states that for VSCSI, + * the server side is allowed to DMA to and from the client. The client + * is never trusted to DMA to or from the server directly. + * + * Messages are sent between partitions on a "Command/Response Queue" + * (CRQ), which is just a buffer of 16 byte entries in the receiver's + * Senders cannot access the buffer directly, but send messages by + * making a hypervisor call and passing in the 16 bytes. The hypervisor + * puts the message in the next 16 byte space in round-robbin fashion, + * turns on the high order bit of the message (the valid bit), and + * generates an interrupt to the receiver (if interrupts are turned on.) + * The receiver just turns off the valid bit when they have copied out + * the message. + * + * The VSCSI client builds a SCSI Remote Protocol (SRP) Information Unit + * (IU) (as defined in the T10 standard available at www.t10.org), gets + * a DMA address for the message, and sends it to the server as the + * payload of a CRQ message. The server DMAs the SRP IU and processes it, + * including doing any additional data transfers. When it is done, it + * DMAs the SRP response back to the same address as the request came from, + * and sends a CRQ message back to inform the client that the request has + * completed. + * + * Note that some of the underlying infrastructure is different between + * machines conforming to the "RS/6000 Platform Architecture" (RPA) and + * the older iSeries hypervisor models. To support both, some low level + * routines have been broken out into rpa_vscsi.c and iseries_vscsi.c. + * The Makefile should pick one, not two, not zero, of these. + * + * TODO: This is currently pretty tied to the IBM i/pSeries hypervisor + * interfaces. It would be really nice to abstract this above an RDMA + * layer. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include "ibmvscsi.h" + +/* The values below are somewhat arbitrary default values, but + * OS/400 will use 3 busses (disks, CDs, tapes, I think.) + * Note that there are 3 bits of channel value, 6 bits of id, and + * 5 bits of LUN. + */ +static int max_id = 64; +static int max_channel = 3; +static int init_timeout = 5; +static int max_requests = 50; + +#define IBMVSCSI_VERSION "1.5.1" + +MODULE_DESCRIPTION("IBM Virtual SCSI"); +MODULE_AUTHOR("Dave Boutcher"); +MODULE_LICENSE("GPL"); +MODULE_VERSION(IBMVSCSI_VERSION); + +module_param_named(max_id, max_id, int, S_IRUGO | S_IWUSR); +MODULE_PARM_DESC(max_id, "Largest ID value for each channel"); +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_PARM_DESC(max_requests, "Maximum requests for this adapter"); + +/* ------------------------------------------------------------ + * Routines for the event pool and event structs + */ +/** + * initialize_event_pool: - Allocates and initializes the event pool for a host + * @pool: event_pool to be initialized + * @size: Number of events in pool + * @hostdata: ibmvscsi_host_data who owns the event pool + * + * Returns zero on success. +*/ +static int initialize_event_pool(struct event_pool *pool, + int size, struct ibmvscsi_host_data *hostdata) +{ + int i; + + pool->size = size; + pool->next = 0; + pool->events = kmalloc(pool->size * sizeof(*pool->events), GFP_KERNEL); + if (!pool->events) + return -ENOMEM; + memset(pool->events, 0x00, pool->size * sizeof(*pool->events)); + + pool->iu_storage = + dma_alloc_coherent(hostdata->dev, + pool->size * sizeof(*pool->iu_storage), + &pool->iu_token, 0); + if (!pool->iu_storage) { + kfree(pool->events); + return -ENOMEM; + } + + for (i = 0; i < pool->size; ++i) { + struct srp_event_struct *evt = &pool->events[i]; + memset(&evt->crq, 0x00, sizeof(evt->crq)); + atomic_set(&evt->free, 1); + evt->crq.valid = 0x80; + evt->crq.IU_length = sizeof(*evt->xfer_iu); + evt->crq.IU_data_ptr = pool->iu_token + + sizeof(*evt->xfer_iu) * i; + evt->xfer_iu = pool->iu_storage + i; + evt->hostdata = hostdata; + } + + return 0; +} + +/** + * release_event_pool: - Frees memory of an event pool of a host + * @pool: event_pool to be released + * @hostdata: ibmvscsi_host_data who owns the even pool + * + * Returns zero on success. +*/ +static void release_event_pool(struct event_pool *pool, + struct ibmvscsi_host_data *hostdata) +{ + int i, in_use = 0; + for (i = 0; i < pool->size; ++i) + if (atomic_read(&pool->events[i].free) != 1) + ++in_use; + if (in_use) + printk(KERN_WARNING + "ibmvscsi: releasing event pool with %d " + "events still in use?\n", in_use); + kfree(pool->events); + dma_free_coherent(hostdata->dev, + pool->size * sizeof(*pool->iu_storage), + pool->iu_storage, pool->iu_token); +} + +/** + * valid_event_struct: - Determines if event is valid. + * @pool: event_pool that contains the event + * @evt: srp_event_struct to be checked for validity + * + * Returns zero if event is invalid, one otherwise. +*/ +static int valid_event_struct(struct event_pool *pool, + struct srp_event_struct *evt) +{ + int index = evt - pool->events; + if (index < 0 || index >= pool->size) /* outside of bounds */ + return 0; + if (evt != pool->events + index) /* unaligned */ + return 0; + return 1; +} + +/** + * ibmvscsi_free-event_struct: - Changes status of event to "free" + * @pool: event_pool that contains the event + * @evt: srp_event_struct to be modified + * +*/ +static void free_event_struct(struct event_pool *pool, + struct srp_event_struct *evt) +{ + if (!valid_event_struct(pool, evt)) { + printk(KERN_ERR + "ibmvscsi: Freeing invalid event_struct %p " + "(not in pool %p)\n", evt, pool->events); + return; + } + if (atomic_inc_return(&evt->free) != 1) { + printk(KERN_ERR + "ibmvscsi: Freeing event_struct %p " + "which is not in use!\n", evt); + return; + } +} + +/** + * get_evt_struct: - Gets the next free event in pool + * @pool: event_pool that contains the events to be searched + * + * Returns the next event in "free" state, and NULL if none are free. + * Note that no synchronization is done here, we assume the host_lock + * will syncrhonze things. +*/ +static struct srp_event_struct *get_event_struct(struct event_pool *pool) +{ + int i; + int poolsize = pool->size; + int offset = pool->next; + + for (i = 0; i < poolsize; i++) { + offset = (offset + 1) % poolsize; + if (!atomic_dec_if_positive(&pool->events[offset].free)) { + pool->next = offset; + return &pool->events[offset]; + } + } + + printk(KERN_ERR "ibmvscsi: found no event struct in pool!\n"); + return NULL; +} + +/** + * init_event_struct: Initialize fields in an event struct that are always + * required. + * @evt: The event + * @done: Routine to call when the event is responded to + * @format: SRP or MAD format + * @timeout: timeout value set in the CRQ + */ +static void init_event_struct(struct srp_event_struct *evt_struct, + void (*done) (struct srp_event_struct *), + u8 format, + int timeout) +{ + evt_struct->cmnd = NULL; + evt_struct->cmnd_done = NULL; + evt_struct->crq.format = format; + evt_struct->crq.timeout = timeout; + evt_struct->done = done; +} + +/* ------------------------------------------------------------ + * Routines for receiving SCSI responses from the hosting partition + */ + +/** + * set_srp_direction: Set the fields in the srp related to data + * direction and number of buffers based on the direction in + * the scsi_cmnd and the number of buffers + */ +static void set_srp_direction(struct scsi_cmnd *cmd, + struct srp_cmd *srp_cmd, + int numbuf) +{ + if (numbuf == 0) + return; + + if (numbuf == 1) { + if (cmd->sc_data_direction == DMA_TO_DEVICE) + srp_cmd->data_out_format = SRP_DIRECT_BUFFER; + else + srp_cmd->data_in_format = SRP_DIRECT_BUFFER; + } else { + if (cmd->sc_data_direction == DMA_TO_DEVICE) { + srp_cmd->data_out_format = SRP_INDIRECT_BUFFER; + srp_cmd->data_out_count = numbuf; + } else { + srp_cmd->data_in_format = SRP_INDIRECT_BUFFER; + srp_cmd->data_in_count = numbuf; + } + } +} + +/** + * unmap_cmd_data: - Unmap data pointed in srp_cmd based on the format + * @cmd: srp_cmd whose additional_data member will be unmapped + * @dev: device for which the memory is mapped + * +*/ +static void unmap_cmd_data(struct srp_cmd *cmd, struct device *dev) +{ + int i; + + if ((cmd->data_out_format == SRP_NO_BUFFER) && + (cmd->data_in_format == SRP_NO_BUFFER)) + return; + else if ((cmd->data_out_format == SRP_DIRECT_BUFFER) || + (cmd->data_in_format == SRP_DIRECT_BUFFER)) { + struct memory_descriptor *data = + (struct memory_descriptor *)cmd->additional_data; + dma_unmap_single(dev, data->virtual_address, data->length, + DMA_BIDIRECTIONAL); + } else { + struct indirect_descriptor *indirect = + (struct indirect_descriptor *)cmd->additional_data; + int num_mapped = indirect->head.length / + sizeof(indirect->list[0]); + for (i = 0; i < num_mapped; ++i) { + struct memory_descriptor *data = &indirect->list[i]; + dma_unmap_single(dev, + data->virtual_address, + data->length, DMA_BIDIRECTIONAL); + } + } +} + +/** + * map_sg_data: - Maps dma for a scatterlist and initializes decriptor fields + * @cmd: Scsi_Cmnd with the scatterlist + * @srp_cmd: srp_cmd that contains the memory descriptor + * @dev: device for which to map dma memory + * + * Called by map_data_for_srp_cmd() when building srp cmd from scsi cmd. + * Returns 1 on success. +*/ +static int map_sg_data(struct scsi_cmnd *cmd, + struct srp_cmd *srp_cmd, struct device *dev) +{ + + int i, sg_mapped; + u64 total_length = 0; + struct scatterlist *sg = cmd->request_buffer; + struct memory_descriptor *data = + (struct memory_descriptor *)srp_cmd->additional_data; + struct indirect_descriptor *indirect = + (struct indirect_descriptor *)data; + + sg_mapped = dma_map_sg(dev, sg, cmd->use_sg, DMA_BIDIRECTIONAL); + + if (sg_mapped == 0) + return 0; + + set_srp_direction(cmd, srp_cmd, sg_mapped); + + /* special case; we can use a single direct descriptor */ + if (sg_mapped == 1) { + data->virtual_address = sg_dma_address(&sg[0]); + data->length = sg_dma_len(&sg[0]); + data->memory_handle = 0; + return 1; + } + + if (sg_mapped > MAX_INDIRECT_BUFS) { + printk(KERN_ERR + "ibmvscsi: More than %d mapped sg entries, got %d\n", + MAX_INDIRECT_BUFS, sg_mapped); + return 0; + } + + indirect->head.virtual_address = 0; + indirect->head.length = sg_mapped * sizeof(indirect->list[0]); + indirect->head.memory_handle = 0; + for (i = 0; i < sg_mapped; ++i) { + struct memory_descriptor *descr = &indirect->list[i]; + struct scatterlist *sg_entry = &sg[i]; + descr->virtual_address = sg_dma_address(sg_entry); + descr->length = sg_dma_len(sg_entry); + descr->memory_handle = 0; + total_length += sg_dma_len(sg_entry); + } + indirect->total_length = total_length; + + return 1; +} + +/** + * map_single_data: - Maps memory and initializes memory decriptor fields + * @cmd: struct scsi_cmnd with the memory to be mapped + * @srp_cmd: srp_cmd that contains the memory descriptor + * @dev: device for which to map dma memory + * + * Called by map_data_for_srp_cmd() when building srp cmd from scsi cmd. + * Returns 1 on success. +*/ +static int map_single_data(struct scsi_cmnd *cmd, + struct srp_cmd *srp_cmd, struct device *dev) +{ + struct memory_descriptor *data = + (struct memory_descriptor *)srp_cmd->additional_data; + + data->virtual_address = + dma_map_single(dev, cmd->request_buffer, + cmd->request_bufflen, + DMA_BIDIRECTIONAL); + if (dma_mapping_error(data->virtual_address)) { + printk(KERN_ERR + "ibmvscsi: Unable to map request_buffer for command!\n"); + return 0; + } + data->length = cmd->request_bufflen; + data->memory_handle = 0; + + set_srp_direction(cmd, srp_cmd, 1); + + return 1; +} + +/** + * map_data_for_srp_cmd: - Calls functions to map data for srp cmds + * @cmd: struct scsi_cmnd with the memory to be mapped + * @srp_cmd: srp_cmd that contains the memory descriptor + * @dev: dma device for which to map dma memory + * + * Called by scsi_cmd_to_srp_cmd() when converting scsi cmds to srp cmds + * Returns 1 on success. +*/ +static int map_data_for_srp_cmd(struct scsi_cmnd *cmd, + struct srp_cmd *srp_cmd, struct device *dev) +{ + switch (cmd->sc_data_direction) { + case DMA_FROM_DEVICE: + case DMA_TO_DEVICE: + break; + case DMA_NONE: + return 1; + case DMA_BIDIRECTIONAL: + printk(KERN_ERR + "ibmvscsi: Can't map DMA_BIDIRECTIONAL to read/write\n"); + return 0; + default: + printk(KERN_ERR + "ibmvscsi: Unknown data direction 0x%02x; can't map!\n", + cmd->sc_data_direction); + return 0; + } + + if (!cmd->request_buffer) + return 1; + if (cmd->use_sg) + return map_sg_data(cmd, srp_cmd, dev); + return map_single_data(cmd, srp_cmd, dev); +} + +/* ------------------------------------------------------------ + * Routines for sending and receiving SRPs + */ +/** + * ibmvscsi_send_srp_event: - Transforms event to u64 array and calls send_crq() + * @evt_struct: evt_struct to be sent + * @hostdata: ibmvscsi_host_data of host + * + * Returns the value returned from ibmvscsi_send_crq(). (Zero for success) + * Note that this routine assumes that host_lock is held for synchronization +*/ +static int ibmvscsi_send_srp_event(struct srp_event_struct *evt_struct, + struct ibmvscsi_host_data *hostdata) +{ + struct scsi_cmnd *cmnd = evt_struct->cmnd; + u64 *crq_as_u64 = (u64 *) &evt_struct->crq; + int rc; + + /* If we have exhausted our request limit, just fail this request. + * Note that there are rare cases involving driver generated requests + * (such as task management requests) that the mid layer may think we + * can handle more requests (can_queue) when we actually can't + */ + if ((evt_struct->crq.format == VIOSRP_SRP_FORMAT) && + (atomic_dec_if_positive(&hostdata->request_limit) < 0)) { + /* See if the adapter is disabled */ + if (atomic_read(&hostdata->request_limit) < 0) { + if (cmnd) + cmnd->result = DID_ERROR << 16; + if (evt_struct->cmnd_done) + evt_struct->cmnd_done(cmnd); + unmap_cmd_data(&evt_struct->iu.srp.cmd, + hostdata->dev); + free_event_struct(&hostdata->pool, evt_struct); + return 0; + } else { + printk("ibmvscsi: Warning, request_limit exceeded\n"); + unmap_cmd_data(&evt_struct->iu.srp.cmd, + hostdata->dev); + free_event_struct(&hostdata->pool, evt_struct); + return SCSI_MLQUEUE_HOST_BUSY; + } + } + + /* Copy the IU into the transfer area */ + *evt_struct->xfer_iu = evt_struct->iu; + evt_struct->xfer_iu->srp.generic.tag = (u64)evt_struct; + + /* Add this to the sent list. We need to do this + * before we actually send + * in case it comes back REALLY fast + */ + list_add_tail(&evt_struct->list, &hostdata->sent); + + if ((rc = + ibmvscsi_send_crq(hostdata, crq_as_u64[0], crq_as_u64[1])) != 0) { + list_del(&evt_struct->list); + + cmnd = evt_struct->cmnd; + printk(KERN_ERR "ibmvscsi: failed to send event struct rc %d\n", + rc); + unmap_cmd_data(&evt_struct->iu.srp.cmd, hostdata->dev); + free_event_struct(&hostdata->pool, evt_struct); + if (cmnd) + cmnd->result = DID_ERROR << 16; + if (evt_struct->cmnd_done) + evt_struct->cmnd_done(cmnd); + } + + return 0; +} + +/** + * handle_cmd_rsp: - Handle responses from commands + * @evt_struct: srp_event_struct to be handled + * + * Used as a callback by when sending scsi cmds. + * Gets called by ibmvscsi_handle_crq() +*/ +static void handle_cmd_rsp(struct srp_event_struct *evt_struct) +{ + struct srp_rsp *rsp = &evt_struct->xfer_iu->srp.rsp; + struct scsi_cmnd *cmnd = evt_struct->cmnd; + + if (cmnd) { + cmnd->result = rsp->status; + if (((cmnd->result >> 1) & 0x1f) == CHECK_CONDITION) + memcpy(cmnd->sense_buffer, + rsp->sense_and_response_data, + rsp->sense_data_list_length); + unmap_cmd_data(&evt_struct->iu.srp.cmd, + evt_struct->hostdata->dev); + + if (rsp->doover) + cmnd->resid = rsp->data_out_residual_count; + else if (rsp->diover) + cmnd->resid = rsp->data_in_residual_count; + } + + if (evt_struct->cmnd_done) + evt_struct->cmnd_done(cmnd); +} + +/** + * lun_from_dev: - Returns the lun of the scsi device + * @dev: struct scsi_device + * +*/ +static inline u16 lun_from_dev(struct scsi_device *dev) +{ + return (0x2 << 14) | (dev->id << 8) | (dev->channel << 5) | dev->lun; +} + +/** + * ibmvscsi_queue: - The queuecommand function of the scsi template + * @cmd: struct scsi_cmnd to be executed + * @done: Callback function to be called when cmd is completed +*/ +static int ibmvscsi_queuecommand(struct scsi_cmnd *cmnd, + void (*done) (struct scsi_cmnd *)) +{ + struct srp_cmd *srp_cmd; + struct srp_event_struct *evt_struct; + struct ibmvscsi_host_data *hostdata = + (struct ibmvscsi_host_data *)&cmnd->device->host->hostdata; + u16 lun = lun_from_dev(cmnd->device); + + evt_struct = get_event_struct(&hostdata->pool); + if (!evt_struct) + return SCSI_MLQUEUE_HOST_BUSY; + + init_event_struct(evt_struct, + handle_cmd_rsp, + VIOSRP_SRP_FORMAT, + cmnd->timeout); + + evt_struct->cmnd = cmnd; + evt_struct->cmnd_done = done; + + /* Set up the actual SRP IU */ + srp_cmd = &evt_struct->iu.srp.cmd; + memset(srp_cmd, 0x00, sizeof(*srp_cmd)); + srp_cmd->type = SRP_CMD_TYPE; + memcpy(srp_cmd->cdb, cmnd->cmnd, sizeof(cmnd->cmnd)); + srp_cmd->lun = ((u64) lun) << 48; + + if (!map_data_for_srp_cmd(cmnd, srp_cmd, hostdata->dev)) { + printk(KERN_ERR "ibmvscsi: couldn't convert cmd to srp_cmd\n"); + free_event_struct(&hostdata->pool, evt_struct); + return SCSI_MLQUEUE_HOST_BUSY; + } + + /* Fix up dma address of the buffer itself */ + if ((srp_cmd->data_out_format == SRP_INDIRECT_BUFFER) || + (srp_cmd->data_in_format == SRP_INDIRECT_BUFFER)) { + struct indirect_descriptor *indirect = + (struct indirect_descriptor *)srp_cmd->additional_data; + indirect->head.virtual_address = evt_struct->crq.IU_data_ptr + + offsetof(struct srp_cmd, additional_data) + + offsetof(struct indirect_descriptor, list); + } + + return ibmvscsi_send_srp_event(evt_struct, hostdata); +} + +/* ------------------------------------------------------------ + * Routines for driver initialization + */ +/** + * adapter_info_rsp: - Handle response to MAD adapter info request + * @evt_struct: srp_event_struct with the response + * + * Used as a "done" callback by when sending adapter_info. Gets called + * by ibmvscsi_handle_crq() +*/ +static void adapter_info_rsp(struct srp_event_struct *evt_struct) +{ + struct ibmvscsi_host_data *hostdata = evt_struct->hostdata; + dma_unmap_single(hostdata->dev, + evt_struct->iu.mad.adapter_info.buffer, + evt_struct->iu.mad.adapter_info.common.length, + DMA_BIDIRECTIONAL); + + if (evt_struct->xfer_iu->mad.adapter_info.common.status) { + printk("ibmvscsi: error %d getting adapter info\n", + evt_struct->xfer_iu->mad.adapter_info.common.status); + } else { + printk("ibmvscsi: host srp version: %s, " + "host partition %s (%d), OS %d\n", + hostdata->madapter_info.srp_version, + hostdata->madapter_info.partition_name, + hostdata->madapter_info.partition_number, + hostdata->madapter_info.os_type); + } +} + +/** + * send_mad_adapter_info: - Sends the mad adapter info request + * and stores the result so it can be retrieved with + * sysfs. We COULD consider causing a failure if the + * returned SRP version doesn't match ours. + * @hostdata: ibmvscsi_host_data of host + * + * Returns zero if successful. +*/ +static void send_mad_adapter_info(struct ibmvscsi_host_data *hostdata) +{ + struct viosrp_adapter_info *req; + struct srp_event_struct *evt_struct; + + memset(&hostdata->madapter_info, 0x00, sizeof(hostdata->madapter_info)); + + evt_struct = get_event_struct(&hostdata->pool); + if (!evt_struct) { + printk(KERN_ERR "ibmvscsi: couldn't allocate an event " + "for ADAPTER_INFO_REQ!\n"); + return; + } + + init_event_struct(evt_struct, + adapter_info_rsp, + VIOSRP_MAD_FORMAT, + init_timeout * HZ); + + req = &evt_struct->iu.mad.adapter_info; + memset(req, 0x00, sizeof(*req)); + + req->common.type = VIOSRP_ADAPTER_INFO_TYPE; + req->common.length = sizeof(hostdata->madapter_info); + req->buffer = dma_map_single(hostdata->dev, + &hostdata->madapter_info, + sizeof(hostdata->madapter_info), + DMA_BIDIRECTIONAL); + + if (dma_mapping_error(req->buffer)) { + printk(KERN_ERR + "ibmvscsi: Unable to map request_buffer " + "for adapter_info!\n"); + free_event_struct(&hostdata->pool, evt_struct); + return; + } + + if (ibmvscsi_send_srp_event(evt_struct, hostdata)) + printk(KERN_ERR "ibmvscsi: couldn't send ADAPTER_INFO_REQ!\n"); +}; + +/** + * login_rsp: - Handle response to SRP login request + * @evt_struct: srp_event_struct with the response + * + * Used as a "done" callback by when sending srp_login. Gets called + * by ibmvscsi_handle_crq() +*/ +static void login_rsp(struct srp_event_struct *evt_struct) +{ + struct ibmvscsi_host_data *hostdata = evt_struct->hostdata; + switch (evt_struct->xfer_iu->srp.generic.type) { + case SRP_LOGIN_RSP_TYPE: /* it worked! */ + break; + case SRP_LOGIN_REJ_TYPE: /* refused! */ + printk(KERN_INFO "ibmvscsi: SRP_LOGIN_REQ rejected\n"); + /* Login failed. */ + atomic_set(&hostdata->request_limit, -1); + return; + default: + printk(KERN_ERR + "ibmvscsi: Invalid login response typecode 0x%02x!\n", + evt_struct->xfer_iu->srp.generic.type); + /* Login failed. */ + atomic_set(&hostdata->request_limit, -1); + return; + } + + printk(KERN_INFO "ibmvscsi: SRP_LOGIN succeeded\n"); + + if (evt_struct->xfer_iu->srp.login_rsp.request_limit_delta > + (max_requests - 2)) + evt_struct->xfer_iu->srp.login_rsp.request_limit_delta = + max_requests - 2; + + /* Now we know what the real request-limit is */ + atomic_set(&hostdata->request_limit, + evt_struct->xfer_iu->srp.login_rsp.request_limit_delta); + + hostdata->host->can_queue = + evt_struct->xfer_iu->srp.login_rsp.request_limit_delta - 2; + + if (hostdata->host->can_queue < 1) { + printk(KERN_ERR "ibmvscsi: Invalid request_limit_delta\n"); + return; + } + + send_mad_adapter_info(hostdata); + return; +} + +/** + * send_srp_login: - Sends the srp login + * @hostdata: ibmvscsi_host_data of host + * + * Returns zero if successful. +*/ +static int send_srp_login(struct ibmvscsi_host_data *hostdata) +{ + int rc; + unsigned long flags; + struct srp_login_req *login; + struct srp_event_struct *evt_struct = get_event_struct(&hostdata->pool); + if (!evt_struct) { + printk(KERN_ERR + "ibmvscsi: couldn't allocate an event for login req!\n"); + return FAILED; + } + + init_event_struct(evt_struct, + login_rsp, + VIOSRP_SRP_FORMAT, + init_timeout * HZ); + + login = &evt_struct->iu.srp.login_req; + login->type = SRP_LOGIN_REQ_TYPE; + login->max_requested_initiator_to_target_iulen = sizeof(union srp_iu); + login->required_buffer_formats = 0x0006; + + /* Start out with a request limit of 1, since this is negotiated in + * the login request we are just sending + */ + atomic_set(&hostdata->request_limit, 1); + + spin_lock_irqsave(hostdata->host->host_lock, flags); + rc = ibmvscsi_send_srp_event(evt_struct, hostdata); + spin_unlock_irqrestore(hostdata->host->host_lock, flags); + return rc; +}; + +/** + * sync_completion: Signal that a synchronous command has completed + * Note that after returning from this call, the evt_struct is freed. + * the caller waiting on this completion shouldn't touch the evt_struct + * again. + */ +static void sync_completion(struct srp_event_struct *evt_struct) +{ + complete(&evt_struct->comp); +} + +/** + * ibmvscsi_abort: Abort a command...from scsi host template + * send this over to the server and wait synchronously for the response + */ +static int ibmvscsi_eh_abort_handler(struct scsi_cmnd *cmd) +{ + struct ibmvscsi_host_data *hostdata = + (struct ibmvscsi_host_data *)cmd->device->host->hostdata; + struct srp_tsk_mgmt *tsk_mgmt; + struct srp_event_struct *evt; + struct srp_event_struct *tmp_evt, *found_evt; + u16 lun = lun_from_dev(cmd->device); + + /* First, find this command in our sent list so we can figure + * out the correct tag + */ + found_evt = NULL; + list_for_each_entry(tmp_evt, &hostdata->sent, list) { + if (tmp_evt->cmnd == cmd) { + found_evt = tmp_evt; + break; + } + } + + if (!found_evt) + return FAILED; + + evt = get_event_struct(&hostdata->pool); + if (evt == NULL) { + printk(KERN_ERR "ibmvscsi: failed to allocate abort event\n"); + return FAILED; + } + + init_event_struct(evt, + sync_completion, + VIOSRP_SRP_FORMAT, + init_timeout * HZ); + + tsk_mgmt = &evt->iu.srp.tsk_mgmt; + + /* Set up an abort SRP command */ + memset(tsk_mgmt, 0x00, sizeof(*tsk_mgmt)); + tsk_mgmt->type = SRP_TSK_MGMT_TYPE; + tsk_mgmt->lun = ((u64) lun) << 48; + tsk_mgmt->task_mgmt_flags = 0x01; /* ABORT TASK */ + tsk_mgmt->managed_task_tag = (u64) found_evt; + + printk(KERN_INFO "ibmvscsi: aborting command. lun 0x%lx, tag 0x%lx\n", + tsk_mgmt->lun, tsk_mgmt->managed_task_tag); + + init_completion(&evt->comp); + if (ibmvscsi_send_srp_event(evt, hostdata) != 0) { + printk(KERN_ERR "ibmvscsi: failed to send abort() event\n"); + return FAILED; + } + + spin_unlock_irq(hostdata->host->host_lock); + wait_for_completion(&evt->comp); + spin_lock_irq(hostdata->host->host_lock); + + /* Because we dropped the spinlock above, it's possible + * The event is no longer in our list. Make sure it didn't + * complete while we were aborting + */ + found_evt = NULL; + list_for_each_entry(tmp_evt, &hostdata->sent, list) { + if (tmp_evt->cmnd == cmd) { + found_evt = tmp_evt; + break; + } + } + + printk(KERN_INFO + "ibmvscsi: successfully aborted task tag 0x%lx\n", + tsk_mgmt->managed_task_tag); + + if (found_evt == NULL) + return SUCCESS; + + cmd->result = (DID_ABORT << 16); + list_del(&found_evt->list); + unmap_cmd_data(&found_evt->iu.srp.cmd, found_evt->hostdata->dev); + free_event_struct(&found_evt->hostdata->pool, found_evt); + atomic_inc(&hostdata->request_limit); + return SUCCESS; +} + +/** + * ibmvscsi_eh_device_reset_handler: Reset a single LUN...from scsi host + * template send this over to the server and wait synchronously for the + * response + */ +static int ibmvscsi_eh_device_reset_handler(struct scsi_cmnd *cmd) +{ + struct ibmvscsi_host_data *hostdata = + (struct ibmvscsi_host_data *)cmd->device->host->hostdata; + + struct srp_tsk_mgmt *tsk_mgmt; + struct srp_event_struct *evt; + struct srp_event_struct *tmp_evt, *pos; + u16 lun = lun_from_dev(cmd->device); + + evt = get_event_struct(&hostdata->pool); + if (evt == NULL) { + printk(KERN_ERR "ibmvscsi: failed to allocate reset event\n"); + return FAILED; + } + + init_event_struct(evt, + sync_completion, + VIOSRP_SRP_FORMAT, + init_timeout * HZ); + + tsk_mgmt = &evt->iu.srp.tsk_mgmt; + + /* Set up a lun reset SRP command */ + memset(tsk_mgmt, 0x00, sizeof(*tsk_mgmt)); + tsk_mgmt->type = SRP_TSK_MGMT_TYPE; + tsk_mgmt->lun = ((u64) lun) << 48; + tsk_mgmt->task_mgmt_flags = 0x08; /* LUN RESET */ + + printk(KERN_INFO "ibmvscsi: resetting device. lun 0x%lx\n", + tsk_mgmt->lun); + + init_completion(&evt->comp); + if (ibmvscsi_send_srp_event(evt, hostdata) != 0) { + printk(KERN_ERR "ibmvscsi: failed to send reset event\n"); + return FAILED; + } + + spin_unlock_irq(hostdata->host->host_lock); + wait_for_completion(&evt->comp); + spin_lock_irq(hostdata->host->host_lock); + + /* We need to find all commands for this LUN that have not yet been + * responded to, and fail them with DID_RESET + */ + list_for_each_entry_safe(tmp_evt, pos, &hostdata->sent, list) { + if ((tmp_evt->cmnd) && (tmp_evt->cmnd->device == cmd->device)) { + if (tmp_evt->cmnd) + tmp_evt->cmnd->result = (DID_RESET << 16); + list_del(&tmp_evt->list); + unmap_cmd_data(&tmp_evt->iu.srp.cmd, tmp_evt->hostdata->dev); + free_event_struct(&tmp_evt->hostdata->pool, + tmp_evt); + atomic_inc(&hostdata->request_limit); + if (tmp_evt->cmnd_done) + tmp_evt->cmnd_done(tmp_evt->cmnd); + else if (tmp_evt->done) + tmp_evt->done(tmp_evt); + } + } + return SUCCESS; +} + +/** + * purge_requests: Our virtual adapter just shut down. purge any sent requests + * @hostdata: the adapter + */ +static void purge_requests(struct ibmvscsi_host_data *hostdata) +{ + struct srp_event_struct *tmp_evt, *pos; + unsigned long flags; + + spin_lock_irqsave(hostdata->host->host_lock, flags); + list_for_each_entry_safe(tmp_evt, pos, &hostdata->sent, list) { + list_del(&tmp_evt->list); + if (tmp_evt->cmnd) { + tmp_evt->cmnd->result = (DID_ERROR << 16); + unmap_cmd_data(&tmp_evt->iu.srp.cmd, + tmp_evt->hostdata->dev); + if (tmp_evt->cmnd_done) + tmp_evt->cmnd_done(tmp_evt->cmnd); + } else { + if (tmp_evt->done) { + tmp_evt->done(tmp_evt); + } + } + free_event_struct(&tmp_evt->hostdata->pool, tmp_evt); + } + spin_unlock_irqrestore(hostdata->host->host_lock, flags); +} + +/** + * ibmvscsi_handle_crq: - Handles and frees received events in the CRQ + * @crq: Command/Response queue + * @hostdata: ibmvscsi_host_data of host + * +*/ +void ibmvscsi_handle_crq(struct viosrp_crq *crq, + struct ibmvscsi_host_data *hostdata) +{ + unsigned long flags; + struct srp_event_struct *evt_struct = + (struct srp_event_struct *)crq->IU_data_ptr; + switch (crq->valid) { + case 0xC0: /* initialization */ + switch (crq->format) { + case 0x01: /* Initialization message */ + printk(KERN_INFO "ibmvscsi: partner initialized\n"); + /* Send back a response */ + if (ibmvscsi_send_crq(hostdata, + 0xC002000000000000LL, 0) == 0) { + /* Now login */ + send_srp_login(hostdata); + } else { + printk(KERN_ERR + "ibmvscsi: Unable to send init rsp\n"); + } + + break; + case 0x02: /* Initialization response */ + printk(KERN_INFO + "ibmvscsi: partner initialization complete\n"); + + /* Now login */ + send_srp_login(hostdata); + break; + default: + printk(KERN_ERR "ibmvscsi: unknown crq message type\n"); + } + return; + case 0xFF: /* Hypervisor telling us the connection is closed */ + printk(KERN_INFO "ibmvscsi: Virtual adapter failed!\n"); + + atomic_set(&hostdata->request_limit, -1); + purge_requests(hostdata); + ibmvscsi_reset_crq_queue(&hostdata->queue, hostdata); + return; + case 0x80: /* real payload */ + break; + default: + printk(KERN_ERR + "ibmvscsi: got an invalid message type 0x%02x\n", + crq->valid); + return; + } + + /* The only kind of payload CRQs we should get are responses to + * things we send. Make sure this response is to something we + * actually sent + */ + if (!valid_event_struct(&hostdata->pool, evt_struct)) { + printk(KERN_ERR + "ibmvscsi: returned correlation_token 0x%p is invalid!\n", + (void *)crq->IU_data_ptr); + return; + } + + if (crq->format == VIOSRP_SRP_FORMAT) + atomic_add(evt_struct->xfer_iu->srp.rsp.request_limit_delta, + &hostdata->request_limit); + + if (evt_struct->done) + evt_struct->done(evt_struct); + else + printk(KERN_ERR + "ibmvscsi: returned done() is NULL; not running it!\n"); + + /* + * Lock the host_lock before messing with these structures, since we + * are running in a task context + */ + spin_lock_irqsave(evt_struct->hostdata->host->host_lock, flags); + list_del(&evt_struct->list); + free_event_struct(&evt_struct->hostdata->pool, evt_struct); + spin_unlock_irqrestore(evt_struct->hostdata->host->host_lock, flags); +} + +/** + * ibmvscsi_get_host_config: Send the command to the server to get host + * configuration data. The data is opaque to us. + */ +static int ibmvscsi_do_host_config(struct ibmvscsi_host_data *hostdata, + unsigned char *buffer, int length) +{ + struct viosrp_host_config *host_config; + struct srp_event_struct *evt_struct; + int rc; + + evt_struct = get_event_struct(&hostdata->pool); + if (!evt_struct) { + printk(KERN_ERR + "ibmvscsi: could't allocate event for HOST_CONFIG!\n"); + return -1; + } + + init_event_struct(evt_struct, + sync_completion, + VIOSRP_MAD_FORMAT, + init_timeout * HZ); + + host_config = &evt_struct->iu.mad.host_config; + + /* Set up a lun reset SRP command */ + memset(host_config, 0x00, sizeof(*host_config)); + host_config->common.type = VIOSRP_HOST_CONFIG_TYPE; + host_config->common.length = length; + host_config->buffer = dma_map_single(hostdata->dev, buffer, length, + DMA_BIDIRECTIONAL); + + if (dma_mapping_error(host_config->buffer)) { + printk(KERN_ERR + "ibmvscsi: dma_mapping error " "getting host config\n"); + free_event_struct(&hostdata->pool, evt_struct); + return -1; + } + + init_completion(&evt_struct->comp); + rc = ibmvscsi_send_srp_event(evt_struct, hostdata); + if (rc == 0) { + wait_for_completion(&evt_struct->comp); + dma_unmap_single(hostdata->dev, host_config->buffer, + length, DMA_BIDIRECTIONAL); + } + + return rc; +} + +/* ------------------------------------------------------------ + * sysfs attributes + */ +static ssize_t show_host_srp_version(struct class_device *class_dev, char *buf) +{ + struct Scsi_Host *shost = class_to_shost(class_dev); + struct ibmvscsi_host_data *hostdata = + (struct ibmvscsi_host_data *)shost->hostdata; + int len; + + len = snprintf(buf, PAGE_SIZE, "%s\n", + hostdata->madapter_info.srp_version); + return len; +} + +static struct class_device_attribute ibmvscsi_host_srp_version = { + .attr = { + .name = "srp_version", + .mode = S_IRUGO, + }, + .show = show_host_srp_version, +}; + +static ssize_t show_host_partition_name(struct class_device *class_dev, + char *buf) +{ + struct Scsi_Host *shost = class_to_shost(class_dev); + struct ibmvscsi_host_data *hostdata = + (struct ibmvscsi_host_data *)shost->hostdata; + int len; + + len = snprintf(buf, PAGE_SIZE, "%s\n", + hostdata->madapter_info.partition_name); + return len; +} + +static struct class_device_attribute ibmvscsi_host_partition_name = { + .attr = { + .name = "partition_name", + .mode = S_IRUGO, + }, + .show = show_host_partition_name, +}; + +static ssize_t show_host_partition_number(struct class_device *class_dev, + char *buf) +{ + struct Scsi_Host *shost = class_to_shost(class_dev); + struct ibmvscsi_host_data *hostdata = + (struct ibmvscsi_host_data *)shost->hostdata; + int len; + + len = snprintf(buf, PAGE_SIZE, "%d\n", + hostdata->madapter_info.partition_number); + return len; +} + +static struct class_device_attribute ibmvscsi_host_partition_number = { + .attr = { + .name = "partition_number", + .mode = S_IRUGO, + }, + .show = show_host_partition_number, +}; + +static ssize_t show_host_mad_version(struct class_device *class_dev, char *buf) +{ + struct Scsi_Host *shost = class_to_shost(class_dev); + struct ibmvscsi_host_data *hostdata = + (struct ibmvscsi_host_data *)shost->hostdata; + int len; + + len = snprintf(buf, PAGE_SIZE, "%d\n", + hostdata->madapter_info.mad_version); + return len; +} + +static struct class_device_attribute ibmvscsi_host_mad_version = { + .attr = { + .name = "mad_version", + .mode = S_IRUGO, + }, + .show = show_host_mad_version, +}; + +static ssize_t show_host_os_type(struct class_device *class_dev, char *buf) +{ + struct Scsi_Host *shost = class_to_shost(class_dev); + struct ibmvscsi_host_data *hostdata = + (struct ibmvscsi_host_data *)shost->hostdata; + int len; + + len = snprintf(buf, PAGE_SIZE, "%d\n", hostdata->madapter_info.os_type); + return len; +} + +static struct class_device_attribute ibmvscsi_host_os_type = { + .attr = { + .name = "os_type", + .mode = S_IRUGO, + }, + .show = show_host_os_type, +}; + +static ssize_t show_host_config(struct class_device *class_dev, char *buf) +{ + struct Scsi_Host *shost = class_to_shost(class_dev); + struct ibmvscsi_host_data *hostdata = + (struct ibmvscsi_host_data *)shost->hostdata; + + /* returns null-terminated host config data */ + if (ibmvscsi_do_host_config(hostdata, buf, PAGE_SIZE) == 0) + return strlen(buf); + else + return 0; +} + +static struct class_device_attribute ibmvscsi_host_config = { + .attr = { + .name = "config", + .mode = S_IRUGO, + }, + .show = show_host_config, +}; + +static struct class_device_attribute *ibmvscsi_attrs[] = { + &ibmvscsi_host_srp_version, + &ibmvscsi_host_partition_name, + &ibmvscsi_host_partition_number, + &ibmvscsi_host_mad_version, + &ibmvscsi_host_os_type, + &ibmvscsi_host_config, + NULL +}; + +/* ------------------------------------------------------------ + * SCSI driver registration + */ +static struct scsi_host_template driver_template = { + .module = THIS_MODULE, + .name = "IBM POWER Virtual SCSI Adapter " IBMVSCSI_VERSION, + .proc_name = "ibmvscsi", + .queuecommand = ibmvscsi_queuecommand, + .eh_abort_handler = ibmvscsi_eh_abort_handler, + .eh_device_reset_handler = ibmvscsi_eh_device_reset_handler, + .cmd_per_lun = 16, + .can_queue = 1, /* Updated after SRP_LOGIN */ + .this_id = -1, + .sg_tablesize = MAX_INDIRECT_BUFS, + .use_clustering = ENABLE_CLUSTERING, + .shost_attrs = ibmvscsi_attrs, +}; + +/** + * Called by bus code for each adapter + */ +static int ibmvscsi_probe(struct vio_dev *vdev, const struct vio_device_id *id) +{ + struct ibmvscsi_host_data *hostdata; + struct Scsi_Host *host; + struct device *dev = &vdev->dev; + unsigned long wait_switch = 0; + + vdev->dev.driver_data = NULL; + + host = scsi_host_alloc(&driver_template, sizeof(*hostdata)); + if (!host) { + printk(KERN_ERR "ibmvscsi: couldn't allocate host data\n"); + goto scsi_host_alloc_failed; + } + + hostdata = (struct ibmvscsi_host_data *)host->hostdata; + memset(hostdata, 0x00, sizeof(*hostdata)); + INIT_LIST_HEAD(&hostdata->sent); + hostdata->host = host; + hostdata->dev = dev; + atomic_set(&hostdata->request_limit, -1); + + if (ibmvscsi_init_crq_queue(&hostdata->queue, hostdata, + max_requests) != 0) { + printk(KERN_ERR "ibmvscsi: couldn't initialize crq\n"); + goto init_crq_failed; + } + if (initialize_event_pool(&hostdata->pool, max_requests, hostdata) != 0) { + printk(KERN_ERR "ibmvscsi: couldn't initialize event pool\n"); + goto init_pool_failed; + } + + host->max_lun = 8; + host->max_id = max_id; + host->max_channel = max_channel; + + if (scsi_add_host(hostdata->host, hostdata->dev)) + goto add_host_failed; + + /* Try to send an initialization message. Note that this is allowed + * to fail if the other end is not acive. In that case we don't + * want to scan + */ + if (ibmvscsi_send_crq(hostdata, 0xC001000000000000LL, 0) == 0) { + /* + * Wait around max init_timeout secs for the adapter to finish + * initializing. When we are done initializing, we will have a + * valid request_limit. We don't want Linux scanning before + * we are ready. + */ + for (wait_switch = jiffies + (init_timeout * HZ); + time_before(jiffies, wait_switch) && + atomic_read(&hostdata->request_limit) < 0;) { + + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout(HZ / 100); + } + + /* if we now have a valid request_limit, initiate a scan */ + if (atomic_read(&hostdata->request_limit) > 0) + scsi_scan_host(host); + } + + vdev->dev.driver_data = hostdata; + return 0; + + add_host_failed: + release_event_pool(&hostdata->pool, hostdata); + init_pool_failed: + ibmvscsi_release_crq_queue(&hostdata->queue, hostdata, max_requests); + init_crq_failed: + scsi_host_put(host); + scsi_host_alloc_failed: + return -1; +} + +static int ibmvscsi_remove(struct vio_dev *vdev) +{ + struct ibmvscsi_host_data *hostdata = vdev->dev.driver_data; + release_event_pool(&hostdata->pool, hostdata); + ibmvscsi_release_crq_queue(&hostdata->queue, hostdata, + max_requests); + + scsi_remove_host(hostdata->host); + scsi_host_put(hostdata->host); + + return 0; +} + +/** + * ibmvscsi_device_table: Used by vio.c to match devices in the device tree we + * support. + */ +static struct vio_device_id ibmvscsi_device_table[] __devinitdata = { + {"vscsi", "IBM,v-scsi"}, + {0,} +}; + +MODULE_DEVICE_TABLE(vio, ibmvscsi_device_table); +static struct vio_driver ibmvscsi_driver = { + .name = "ibmvscsi", + .id_table = ibmvscsi_device_table, + .probe = ibmvscsi_probe, + .remove = ibmvscsi_remove +}; + +int __init ibmvscsi_module_init(void) +{ + return vio_register_driver(&ibmvscsi_driver); +} + +void __exit ibmvscsi_module_exit(void) +{ + vio_unregister_driver(&ibmvscsi_driver); +} + +module_init(ibmvscsi_module_init); +module_exit(ibmvscsi_module_exit); diff --git a/drivers/scsi/ibmvscsi/ibmvscsi.h b/drivers/scsi/ibmvscsi/ibmvscsi.h new file mode 100644 index 000000000..dbe1735e5 --- /dev/null +++ b/drivers/scsi/ibmvscsi/ibmvscsi.h @@ -0,0 +1,108 @@ +/* ------------------------------------------------------------ + * ibmvscsi.h + * (C) Copyright IBM Corporation 1994, 2003 + * Authors: Colin DeVilbiss (devilbis@us.ibm.com) + * Santiago Leon (santil@us.ibm.com) + * Dave Boutcher (sleddog@us.ibm.com) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * 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 + * + * ------------------------------------------------------------ + * Emulation of a SCSI host adapter for Virtual I/O devices + * + * This driver allows the Linux SCSI peripheral drivers to directly + * access devices in the hosting partition, either on an iSeries + * hypervisor system or a converged hypervisor system. + */ +#ifndef IBMVSCSI_H +#define IBMVSCSI_H +#include +#include +#include +#include +#include "viosrp.h" + +struct scsi_cmnd; +struct Scsi_Host; + +/* Number of indirect bufs...the list of these has to fit in the + * additional data of the srp_cmd struct along with the indirect + * descriptor + */ +#define MAX_INDIRECT_BUFS 10 + +/* ------------------------------------------------------------ + * Data Structures + */ +/* an RPA command/response transport queue */ +struct crq_queue { + struct viosrp_crq *msgs; + int size, cur; + dma_addr_t msg_token; + spinlock_t lock; +}; + +/* a unit of work for the hosting partition */ +struct srp_event_struct { + union viosrp_iu *xfer_iu; + struct scsi_cmnd *cmnd; + struct list_head list; + void (*done) (struct srp_event_struct *); + struct viosrp_crq crq; + struct ibmvscsi_host_data *hostdata; + atomic_t free; + union viosrp_iu iu; + void (*cmnd_done) (struct scsi_cmnd *); + struct completion comp; +}; + +/* a pool of event structs for use */ +struct event_pool { + struct srp_event_struct *events; + u32 size; + int next; + union viosrp_iu *iu_storage; + dma_addr_t iu_token; +}; + +/* all driver data associated with a host adapter */ +struct ibmvscsi_host_data { + atomic_t request_limit; + struct device *dev; + struct event_pool pool; + struct crq_queue queue; + struct tasklet_struct srp_task; + struct list_head sent; + struct Scsi_Host *host; + struct mad_adapter_info_data madapter_info; +}; + +/* routines for managing a command/response queue */ +int ibmvscsi_init_crq_queue(struct crq_queue *queue, + struct ibmvscsi_host_data *hostdata, + int max_requests); +void ibmvscsi_release_crq_queue(struct crq_queue *queue, + struct ibmvscsi_host_data *hostdata, + int max_requests); +void ibmvscsi_reset_crq_queue(struct crq_queue *queue, + struct ibmvscsi_host_data *hostdata); + +void ibmvscsi_handle_crq(struct viosrp_crq *crq, + struct ibmvscsi_host_data *hostdata); +int ibmvscsi_send_crq(struct ibmvscsi_host_data *hostdata, + u64 word1, u64 word2); + +#endif /* IBMVSCSI_H */ diff --git a/drivers/scsi/ibmvscsi/iseries_vscsi.c b/drivers/scsi/ibmvscsi/iseries_vscsi.c new file mode 100644 index 000000000..e9202f2a8 --- /dev/null +++ b/drivers/scsi/ibmvscsi/iseries_vscsi.c @@ -0,0 +1,144 @@ +/* ------------------------------------------------------------ + * iSeries_vscsi.c + * (C) Copyright IBM Corporation 1994, 2003 + * Authors: Colin DeVilbiss (devilbis@us.ibm.com) + * Santiago Leon (santil@us.ibm.com) + * Dave Boutcher (sleddog@us.ibm.com) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * 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 + * + * ------------------------------------------------------------ + * iSeries-specific functions of the SCSI host adapter for Virtual I/O devices + * + * This driver allows the Linux SCSI peripheral drivers to directly + * access devices in the hosting partition, either on an iSeries + * hypervisor system or a converged hypervisor system. + */ + +#include +#include +#include +#include +#include +#include +#include "ibmvscsi.h" + +/* global variables */ +static struct ibmvscsi_host_data *single_host_data; + +/* ------------------------------------------------------------ + * Routines for direct interpartition interaction + */ +struct srp_lp_event { + struct HvLpEvent lpevt; /* 0x00-0x17 */ + u32 reserved1; /* 0x18-0x1B; unused */ + u16 version; /* 0x1C-0x1D; unused */ + u16 subtype_rc; /* 0x1E-0x1F; unused */ + struct viosrp_crq crq; /* 0x20-0x3F */ +}; + +/** + * standard interface for handling logical partition events. + */ +static void ibmvscsi_handle_event(struct HvLpEvent *lpevt) +{ + struct srp_lp_event *evt = (struct srp_lp_event *)lpevt; + + if (!evt) { + printk(KERN_ERR "ibmvscsi: received null event\n"); + return; + } + + if (single_host_data == NULL) { + printk(KERN_ERR + "ibmvscsi: received event, no adapter present\n"); + return; + } + + ibmvscsi_handle_crq(&evt->crq, single_host_data); +} + +/* ------------------------------------------------------------ + * Routines for driver initialization + */ +int ibmvscsi_init_crq_queue(struct crq_queue *queue, + struct ibmvscsi_host_data *hostdata, + int max_requests) +{ + int rc; + + single_host_data = hostdata; + rc = viopath_open(viopath_hostLp, viomajorsubtype_scsi, 0); + if (rc < 0) { + printk("viopath_open failed with rc %d in open_event_path\n", + rc); + goto viopath_open_failed; + } + + rc = vio_setHandler(viomajorsubtype_scsi, ibmvscsi_handle_event); + if (rc < 0) { + printk("vio_setHandler failed with rc %d in open_event_path\n", + rc); + goto vio_setHandler_failed; + } + return 0; + + vio_setHandler_failed: + viopath_close(viopath_hostLp, viomajorsubtype_scsi, max_requests); + viopath_open_failed: + return -1; +} + +void ibmvscsi_release_crq_queue(struct crq_queue *queue, + struct ibmvscsi_host_data *hostdata, + int max_requests) +{ + vio_clearHandler(viomajorsubtype_scsi); + viopath_close(viopath_hostLp, viomajorsubtype_scsi, max_requests); +} + +/** + * reset_crq_queue: - resets a crq after a failure + * @queue: crq_queue to initialize and register + * @hostdata: ibmvscsi_host_data of host + * + * no-op for iSeries + */ +void ibmvscsi_reset_crq_queue(struct crq_queue *queue, + struct ibmvscsi_host_data *hostdata) +{ +} + +/** + * ibmvscsi_send_crq: - Send a CRQ + * @hostdata: the adapter + * @word1: the first 64 bits of the data + * @word2: the second 64 bits of the data + */ +int ibmvscsi_send_crq(struct ibmvscsi_host_data *hostdata, u64 word1, u64 word2) +{ + single_host_data = hostdata; + return HvCallEvent_signalLpEventFast(viopath_hostLp, + HvLpEvent_Type_VirtualIo, + viomajorsubtype_scsi, + HvLpEvent_AckInd_NoAck, + HvLpEvent_AckType_ImmediateAck, + viopath_sourceinst(viopath_hostLp), + viopath_targetinst(viopath_hostLp), + 0, + VIOVERSION << 16, word1, word2, 0, + 0); +} diff --git a/drivers/scsi/ibmvscsi/rpa_vscsi.c b/drivers/scsi/ibmvscsi/rpa_vscsi.c new file mode 100644 index 000000000..b4e82654e --- /dev/null +++ b/drivers/scsi/ibmvscsi/rpa_vscsi.c @@ -0,0 +1,260 @@ +/* ------------------------------------------------------------ + * rpa_vscsi.c + * (C) Copyright IBM Corporation 1994, 2003 + * Authors: Colin DeVilbiss (devilbis@us.ibm.com) + * Santiago Leon (santil@us.ibm.com) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * 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 + * + * ------------------------------------------------------------ + * RPA-specific functions of the SCSI host adapter for Virtual I/O devices + * + * This driver allows the Linux SCSI peripheral drivers to directly + * access devices in the hosting partition, either on an iSeries + * hypervisor system or a converged hypervisor system. + */ + +#include +#include +#include +#include +#include +#include "ibmvscsi.h" + +/* ------------------------------------------------------------ + * Routines for managing the command/response queue + */ +/** + * ibmvscsi_handle_event: - Interrupt handler for crq events + * @irq: number of irq to handle, not used + * @dev_instance: ibmvscsi_host_data of host that received interrupt + * @regs: pt_regs with registers + * + * Disables interrupts and schedules srp_task + * Always returns IRQ_HANDLED + */ +static irqreturn_t ibmvscsi_handle_event(int irq, + void *dev_instance, + struct pt_regs *regs) +{ + struct ibmvscsi_host_data *hostdata = + (struct ibmvscsi_host_data *)dev_instance; + vio_disable_interrupts(to_vio_dev(hostdata->dev)); + tasklet_schedule(&hostdata->srp_task); + return IRQ_HANDLED; +} + +/** + * release_crq_queue: - Deallocates data and unregisters CRQ + * @queue: crq_queue to initialize and register + * @host_data: ibmvscsi_host_data of host + * + * Frees irq, deallocates a page for messages, unmaps dma, and unregisters + * the crq with the hypervisor. + */ +void ibmvscsi_release_crq_queue(struct crq_queue *queue, + struct ibmvscsi_host_data *hostdata, + int max_requests) +{ + long rc; + struct vio_dev *vdev = to_vio_dev(hostdata->dev); + free_irq(vdev->irq, (void *)hostdata); + tasklet_kill(&hostdata->srp_task); + do { + rc = plpar_hcall_norets(H_FREE_CRQ, vdev->unit_address); + } while ((rc == H_Busy) || (H_isLongBusy(rc))); + dma_unmap_single(hostdata->dev, + queue->msg_token, + queue->size * sizeof(*queue->msgs), DMA_BIDIRECTIONAL); + free_page((unsigned long)queue->msgs); +} + +/** + * crq_queue_next_crq: - Returns the next entry in message queue + * @queue: crq_queue to use + * + * Returns pointer to next entry in queue, or NULL if there are no new + * entried in the CRQ. + */ +static struct viosrp_crq *crq_queue_next_crq(struct crq_queue *queue) +{ + struct viosrp_crq *crq; + unsigned long flags; + + spin_lock_irqsave(&queue->lock, flags); + crq = &queue->msgs[queue->cur]; + if (crq->valid & 0x80) { + if (++queue->cur == queue->size) + queue->cur = 0; + } else + crq = NULL; + spin_unlock_irqrestore(&queue->lock, flags); + + return crq; +} + +/** + * ibmvscsi_send_crq: - Send a CRQ + * @hostdata: the adapter + * @word1: the first 64 bits of the data + * @word2: the second 64 bits of the data + */ +int ibmvscsi_send_crq(struct ibmvscsi_host_data *hostdata, u64 word1, u64 word2) +{ + struct vio_dev *vdev = to_vio_dev(hostdata->dev); + + return plpar_hcall_norets(H_SEND_CRQ, vdev->unit_address, word1, word2); +} + +/** + * ibmvscsi_task: - Process srps asynchronously + * @data: ibmvscsi_host_data of host + */ +static void ibmvscsi_task(void *data) +{ + struct ibmvscsi_host_data *hostdata = (struct ibmvscsi_host_data *)data; + struct vio_dev *vdev = to_vio_dev(hostdata->dev); + struct viosrp_crq *crq; + int done = 0; + + while (!done) { + /* Pull all the valid messages off the CRQ */ + while ((crq = crq_queue_next_crq(&hostdata->queue)) != NULL) { + ibmvscsi_handle_crq(crq, hostdata); + crq->valid = 0x00; + } + + vio_enable_interrupts(vdev); + if ((crq = crq_queue_next_crq(&hostdata->queue)) != NULL) { + vio_disable_interrupts(vdev); + ibmvscsi_handle_crq(crq, hostdata); + crq->valid = 0x00; + } else { + done = 1; + } + } +} + +/** + * initialize_crq_queue: - Initializes and registers CRQ with hypervisor + * @queue: crq_queue to initialize and register + * @hostdata: ibmvscsi_host_data of host + * + * Allocates a page for messages, maps it for dma, and registers + * the crq with the hypervisor. + * Returns zero on success. + */ +int ibmvscsi_init_crq_queue(struct crq_queue *queue, + struct ibmvscsi_host_data *hostdata, + int max_requests) +{ + int rc; + struct vio_dev *vdev = to_vio_dev(hostdata->dev); + + queue->msgs = (struct viosrp_crq *)get_zeroed_page(GFP_KERNEL); + + if (!queue->msgs) + goto malloc_failed; + queue->size = PAGE_SIZE / sizeof(*queue->msgs); + + queue->msg_token = dma_map_single(hostdata->dev, queue->msgs, + queue->size * sizeof(*queue->msgs), + DMA_BIDIRECTIONAL); + + if (dma_mapping_error(queue->msg_token)) + goto map_failed; + + rc = plpar_hcall_norets(H_REG_CRQ, + vdev->unit_address, + queue->msg_token, PAGE_SIZE); + if (rc == 2) { + /* Adapter is good, but other end is not ready */ + printk(KERN_WARNING "ibmvscsi: Partner adapter not ready\n"); + } else if (rc != 0) { + printk(KERN_WARNING "ibmvscsi: Error %d opening adapter\n", rc); + goto reg_crq_failed; + } + + if (request_irq(vdev->irq, + ibmvscsi_handle_event, + 0, "ibmvscsi", (void *)hostdata) != 0) { + printk(KERN_ERR "ibmvscsi: couldn't register irq 0x%x\n", + vdev->irq); + goto req_irq_failed; + } + + rc = vio_enable_interrupts(vdev); + if (rc != 0) { + printk(KERN_ERR "ibmvscsi: Error %d enabling interrupts!!!\n", + rc); + goto req_irq_failed; + } + + queue->cur = 0; + queue->lock = SPIN_LOCK_UNLOCKED; + + tasklet_init(&hostdata->srp_task, (void *)ibmvscsi_task, + (unsigned long)hostdata); + + return 0; + + req_irq_failed: + do { + rc = plpar_hcall_norets(H_FREE_CRQ, vdev->unit_address); + } while ((rc == H_Busy) || (H_isLongBusy(rc))); + reg_crq_failed: + dma_unmap_single(hostdata->dev, + queue->msg_token, + queue->size * sizeof(*queue->msgs), DMA_BIDIRECTIONAL); + map_failed: + free_page((unsigned long)queue->msgs); + malloc_failed: + return -1; +} + +/** + * reset_crq_queue: - resets a crq after a failure + * @queue: crq_queue to initialize and register + * @hostdata: ibmvscsi_host_data of host + * + */ +void ibmvscsi_reset_crq_queue(struct crq_queue *queue, + struct ibmvscsi_host_data *hostdata) +{ + int rc; + struct vio_dev *vdev = to_vio_dev(hostdata->dev); + + /* Close the CRQ */ + do { + rc = plpar_hcall_norets(H_FREE_CRQ, vdev->unit_address); + } while ((rc == H_Busy) || (H_isLongBusy(rc))); + + /* Clean out the queue */ + memset(queue->msgs, 0x00, PAGE_SIZE); + queue->cur = 0; + + /* And re-open it again */ + rc = plpar_hcall_norets(H_REG_CRQ, + vdev->unit_address, + queue->msg_token, PAGE_SIZE); + if (rc == 2) { + /* Adapter is good, but other end is not ready */ + printk(KERN_WARNING "ibmvscsi: Partner adapter not ready\n"); + } else if (rc != 0) { + printk(KERN_WARNING + "ibmvscsi: couldn't register crq--rc 0x%x\n", rc); + } +} diff --git a/drivers/scsi/ibmvscsi/srp.h b/drivers/scsi/ibmvscsi/srp.h new file mode 100644 index 000000000..e952c1cd9 --- /dev/null +++ b/drivers/scsi/ibmvscsi/srp.h @@ -0,0 +1,225 @@ +/*****************************************************************************/ +/* srp.h -- SCSI RDMA Protocol definitions */ +/* */ +/* Written By: Colin Devilbis, IBM Corporation */ +/* */ +/* Copyright (C) 2003 IBM Corporation */ +/* */ +/* This program is free software; you can redistribute it and/or modify */ +/* it under the terms of the GNU General Public License as published by */ +/* the Free Software Foundation; either version 2 of the License, or */ +/* (at your option) any later version. */ +/* */ +/* This program is distributed in the hope that it will be useful, */ +/* but WITHOUT ANY WARRANTY; without even the implied warranty of */ +/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */ +/* GNU General Public License for more details. */ +/* */ +/* You should have received a copy of the GNU General Public License */ +/* along with this program; if not, write to the Free Software */ +/* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ +/* */ +/* */ +/* This file contains structures and definitions for the SCSI RDMA Protocol */ +/* (SRP) as defined in the T10 standard available at www.t10.org. This */ +/* file was based on the 16a version of the standard */ +/* */ +/*****************************************************************************/ +#ifndef SRP_H +#define SRP_H + +#define PACKED __attribute__((packed)) + +enum srp_types { + SRP_LOGIN_REQ_TYPE = 0x00, + SRP_LOGIN_RSP_TYPE = 0xC0, + SRP_LOGIN_REJ_TYPE = 0x80, + SRP_I_LOGOUT_TYPE = 0x03, + SRP_T_LOGOUT_TYPE = 0x80, + SRP_TSK_MGMT_TYPE = 0x01, + SRP_CMD_TYPE = 0x02, + SRP_RSP_TYPE = 0xC1, + SRP_CRED_REQ_TYPE = 0x81, + SRP_CRED_RSP_TYPE = 0x41, + SRP_AER_REQ_TYPE = 0x82, + SRP_AER_RSP_TYPE = 0x42 +}; + +enum srp_descriptor_formats { + SRP_NO_BUFFER = 0x00, + SRP_DIRECT_BUFFER = 0x01, + SRP_INDIRECT_BUFFER = 0x02 +}; + +struct memory_descriptor { + u64 virtual_address; + u32 memory_handle; + u32 length; +}; + +struct indirect_descriptor { + struct memory_descriptor head; + u32 total_length; + struct memory_descriptor list[1] PACKED; +}; + +struct srp_generic { + u8 type; + u8 reserved1[7]; + u64 tag; +}; + +struct srp_login_req { + u8 type; + u8 reserved1[7]; + u64 tag; + u32 max_requested_initiator_to_target_iulen; + u32 reserved2; + u16 required_buffer_formats; + u8 reserved3:6; + u8 multi_channel_action:2; + u8 reserved4; + u32 reserved5; + u8 initiator_port_identifier[16]; + u8 target_port_identifier[16]; +}; + +struct srp_login_rsp { + u8 type; + u8 reserved1[3]; + u32 request_limit_delta; + u64 tag; + u32 max_initiator_to_target_iulen; + u32 max_target_to_initiator_iulen; + u16 supported_buffer_formats; + u8 reserved2:6; + u8 multi_channel_result:2; + u8 reserved3; + u8 reserved4[24]; +}; + +struct srp_login_rej { + u8 type; + u8 reserved1[3]; + u32 reason; + u64 tag; + u64 reserved2; + u16 supported_buffer_formats; + u8 reserved3[6]; +}; + +struct srp_i_logout { + u8 type; + u8 reserved1[7]; + u64 tag; +}; + +struct srp_t_logout { + u8 type; + u8 reserved1[3]; + u32 reason; + u64 tag; +}; + +struct srp_tsk_mgmt { + u8 type; + u8 reserved1[7]; + u64 tag; + u32 reserved2; + u64 lun PACKED; + u8 reserved3; + u8 reserved4; + u8 task_mgmt_flags; + u8 reserved5; + u64 managed_task_tag; + u64 reserved6; +}; + +struct srp_cmd { + u8 type; + u32 reserved1 PACKED; + u8 data_out_format:4; + u8 data_in_format:4; + u8 data_out_count; + u8 data_in_count; + u64 tag; + u32 reserved2; + u64 lun PACKED; + u8 reserved3; + u8 reserved4:5; + u8 task_attribute:3; + u8 reserved5; + u8 additional_cdb_len; + u8 cdb[16]; + u8 additional_data[0x100 - 0x30]; +}; + +struct srp_rsp { + u8 type; + u8 reserved1[3]; + u32 request_limit_delta; + u64 tag; + u16 reserved2; + u8 reserved3:2; + u8 diunder:1; + u8 diover:1; + u8 dounder:1; + u8 doover:1; + u8 snsvalid:1; + u8 rspvalid:1; + u8 status; + u32 data_in_residual_count; + u32 data_out_residual_count; + u32 sense_data_list_length; + u32 response_data_list_length; + u8 sense_and_response_data[18]; +}; + +struct srp_cred_req { + u8 type; + u8 reserved1[3]; + u32 request_limit_delta; + u64 tag; +}; + +struct srp_cred_rsp { + u8 type; + u8 reserved1[7]; + u64 tag; +}; + +struct srp_aer_req { + u8 type; + u8 reserved1[3]; + u32 request_limit_delta; + u64 tag; + u32 reserved2; + u64 lun; + u32 sense_data_list_length; + u32 reserved3; + u8 sense_data[20]; +}; + +struct srp_aer_rsp { + u8 type; + u8 reserved1[7]; + u64 tag; +}; + +union srp_iu { + struct srp_generic generic; + struct srp_login_req login_req; + struct srp_login_rsp login_rsp; + struct srp_login_rej login_rej; + struct srp_i_logout i_logout; + struct srp_t_logout t_logout; + struct srp_tsk_mgmt tsk_mgmt; + struct srp_cmd cmd; + struct srp_rsp rsp; + struct srp_cred_req cred_req; + struct srp_cred_rsp cred_rsp; + struct srp_aer_req aer_req; + struct srp_aer_rsp aer_rsp; +}; + +#endif diff --git a/drivers/scsi/ibmvscsi/viosrp.h b/drivers/scsi/ibmvscsi/viosrp.h new file mode 100644 index 000000000..6a6bba8a2 --- /dev/null +++ b/drivers/scsi/ibmvscsi/viosrp.h @@ -0,0 +1,126 @@ +/*****************************************************************************/ +/* srp.h -- SCSI RDMA Protocol definitions */ +/* */ +/* Written By: Colin Devilbis, IBM Corporation */ +/* */ +/* Copyright (C) 2003 IBM Corporation */ +/* */ +/* This program is free software; you can redistribute it and/or modify */ +/* it under the terms of the GNU General Public License as published by */ +/* the Free Software Foundation; either version 2 of the License, or */ +/* (at your option) any later version. */ +/* */ +/* This program is distributed in the hope that it will be useful, */ +/* but WITHOUT ANY WARRANTY; without even the implied warranty of */ +/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */ +/* GNU General Public License for more details. */ +/* */ +/* You should have received a copy of the GNU General Public License */ +/* along with this program; if not, write to the Free Software */ +/* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ +/* */ +/* */ +/* This file contains structures and definitions for IBM RPA (RS/6000 */ +/* platform architecture) implementation of the SRP (SCSI RDMA Protocol) */ +/* standard. SRP is used on IBM iSeries and pSeries platforms to send SCSI */ +/* commands between logical partitions. */ +/* */ +/* SRP Information Units (IUs) are sent on a "Command/Response Queue" (CRQ) */ +/* between partitions. The definitions in this file are architected, */ +/* and cannot be changed without breaking compatibility with other versions */ +/* of Linux and other operating systems (AIX, OS/400) that talk this protocol*/ +/* between logical partitions */ +/*****************************************************************************/ +#ifndef VIOSRP_H +#define VIOSRP_H +#include "srp.h" + +enum viosrp_crq_formats { + VIOSRP_SRP_FORMAT = 0x01, + VIOSRP_MAD_FORMAT = 0x02, + VIOSRP_OS400_FORMAT = 0x03, + VIOSRP_AIX_FORMAT = 0x04, + VIOSRP_LINUX_FORMAT = 0x06, + VIOSRP_INLINE_FORMAT = 0x07 +}; + +struct viosrp_crq { + u8 valid; /* used by RPA */ + u8 format; /* SCSI vs out-of-band */ + u8 reserved; + u8 status; /* non-scsi failure? (e.g. DMA failure) */ + u16 timeout; /* in seconds */ + u16 IU_length; /* in bytes */ + u64 IU_data_ptr; /* the TCE for transferring data */ +}; + +/* MADs are Management requests above and beyond the IUs defined in the SRP + * standard. + */ +enum viosrp_mad_types { + VIOSRP_EMPTY_IU_TYPE = 0x01, + VIOSRP_ERROR_LOG_TYPE = 0x02, + VIOSRP_ADAPTER_INFO_TYPE = 0x03, + VIOSRP_HOST_CONFIG_TYPE = 0x04 +}; + +/* + * Common MAD header + */ +struct mad_common { + u32 type; + u16 status; + u16 length; + u64 tag; +}; + +/* + * All SRP (and MAD) requests normally flow from the + * client to the server. There is no way for the server to send + * an asynchronous message back to the client. The Empty IU is used + * to hang out a meaningless request to the server so that it can respond + * asynchrouously with something like a SCSI AER + */ +struct viosrp_empty_iu { + struct mad_common common; + u64 buffer; + u32 port; +}; + +struct viosrp_error_log { + struct mad_common common; + u64 buffer; +}; + +struct viosrp_adapter_info { + struct mad_common common; + u64 buffer; +}; + +struct viosrp_host_config { + struct mad_common common; + u64 buffer; +}; + +union mad_iu { + struct viosrp_empty_iu empty_iu; + struct viosrp_error_log error_log; + struct viosrp_adapter_info adapter_info; + struct viosrp_host_config host_config; +}; + +union viosrp_iu { + union srp_iu srp; + union mad_iu mad; +}; + +struct mad_adapter_info_data { + char srp_version[8]; + char partition_name[96]; + u32 partition_number; + u32 mad_version; + u32 os_type; + u32 port_max_txu[8]; /* per-port maximum transfer */ +}; + +#endif diff --git a/drivers/scsi/ipr.c b/drivers/scsi/ipr.c index bf3942769..40bfff7f2 100644 --- a/drivers/scsi/ipr.c +++ b/drivers/scsi/ipr.c @@ -1,7 +1,7 @@ /* * ipr.c -- driver for IBM Power Linux RAID adapters * - * Written By: Brian King, IBM Corporation + * Written By: Brian King , IBM Corporation * * Copyright (C) 2003, 2004 IBM Corporation * @@ -93,7 +93,7 @@ static spinlock_t ipr_driver_lock = SPIN_LOCK_UNLOCKED; /* This table describes the differences between DMA controller chips */ static const struct ipr_chip_cfg_t ipr_chip_cfg[] = { - { /* Gemstone */ + { /* Gemstone and Citrine */ .mailbox = 0x0042C, .cache_line_size = 0x20, { @@ -208,6 +208,8 @@ struct ipr_error_table_t ipr_error_table[] = { "Synchronization required"}, {0x024E0000, 0, 0, "No ready, IOA shutdown"}, + {0x025A0000, 0, 0, + "Not ready, IOA has been shutdown"}, {0x02670100, 0, 1, "3020: Storage subsystem configuration error"}, {0x03110B00, 0, 0, @@ -880,11 +882,13 @@ static void ipr_process_ccn(struct ipr_cmnd *ipr_cmd) **/ static void ipr_log_vpd(struct ipr_std_inq_vpids *vpids, u8 *serial_num) { - char buffer[max_t(int, sizeof(struct ipr_std_inq_vpids), - IPR_SERIAL_NUM_LEN) + 1]; + char buffer[IPR_VENDOR_ID_LEN + IPR_PROD_ID_LEN + + IPR_SERIAL_NUM_LEN]; - memcpy(buffer, vpids, sizeof(struct ipr_std_inq_vpids)); - buffer[sizeof(struct ipr_std_inq_vpids)] = '\0'; + memcpy(buffer, vpids->vendor_id, IPR_VENDOR_ID_LEN); + memcpy(buffer + IPR_VENDOR_ID_LEN, vpids->product_id, + IPR_PROD_ID_LEN); + buffer[IPR_VENDOR_ID_LEN + IPR_PROD_ID_LEN] = '\0'; ipr_err("Vendor/Product ID: %s\n", buffer); memcpy(buffer, serial_num, IPR_SERIAL_NUM_LEN); @@ -1765,6 +1769,33 @@ static void ipr_get_ioa_dump(struct ipr_ioa_cfg *ioa_cfg, struct ipr_dump *dump) #define ipr_get_ioa_dump(ioa_cfg, dump) do { } while(0) #endif +/** + * ipr_release_dump - Free adapter dump memory + * @kref: kref struct + * + * Return value: + * nothing + **/ +static void ipr_release_dump(struct kref *kref) +{ + struct ipr_dump *dump = container_of(kref,struct ipr_dump,kref); + struct ipr_ioa_cfg *ioa_cfg = dump->ioa_cfg; + unsigned long lock_flags = 0; + int i; + + ENTER; + spin_lock_irqsave(ioa_cfg->host->host_lock, lock_flags); + ioa_cfg->dump = NULL; + ioa_cfg->sdt_state = INACTIVE; + spin_unlock_irqrestore(ioa_cfg->host->host_lock, lock_flags); + + for (i = 0; i < dump->ioa_dump.next_page_index; i++) + free_page((unsigned long) dump->ioa_dump.ioa_data[i]); + + kfree(dump); + LEAVE; +} + /** * ipr_worker_thread - Worker thread * @data: ioa config struct @@ -1791,13 +1822,14 @@ static void ipr_worker_thread(void *data) if (ioa_cfg->sdt_state == GET_DUMP) { dump = ioa_cfg->dump; - if (!dump || !kobject_get(&dump->kobj)) { + if (!dump) { spin_unlock_irqrestore(ioa_cfg->host->host_lock, lock_flags); return; } + kref_get(&dump->kref); spin_unlock_irqrestore(ioa_cfg->host->host_lock, lock_flags); ipr_get_ioa_dump(ioa_cfg, dump); - kobject_put(&dump->kobj); + kref_put(&dump->kref, ipr_release_dump); spin_lock_irqsave(ioa_cfg->host->host_lock, lock_flags); if (ioa_cfg->sdt_state == DUMP_OBTAINED) @@ -2008,7 +2040,7 @@ static ssize_t ipr_store_diagnostics(struct class_device *class_dev, wait_event(ioa_cfg->reset_wait_q, !ioa_cfg->in_reset_reload); /* Wait for a second for any errors to be logged */ - schedule_timeout(HZ); + msleep(1000); } else { spin_unlock_irqrestore(ioa_cfg->host->host_lock, lock_flags); return -EIO; @@ -2392,15 +2424,15 @@ static ssize_t ipr_read_dump(struct kobject *kobj, char *buf, spin_lock_irqsave(ioa_cfg->host->host_lock, lock_flags); dump = ioa_cfg->dump; - if (ioa_cfg->sdt_state != DUMP_OBTAINED || !dump || !kobject_get(&dump->kobj)) { + if (ioa_cfg->sdt_state != DUMP_OBTAINED || !dump) { spin_unlock_irqrestore(ioa_cfg->host->host_lock, lock_flags); return 0; } - + kref_get(&dump->kref); spin_unlock_irqrestore(ioa_cfg->host->host_lock, lock_flags); if (off > dump->driver_dump.hdr.len) { - kobject_put(&dump->kobj); + kref_put(&dump->kref, ipr_release_dump); return 0; } @@ -2450,41 +2482,10 @@ static ssize_t ipr_read_dump(struct kobject *kobj, char *buf, count -= len; } - kobject_put(&dump->kobj); + kref_put(&dump->kref, ipr_release_dump); return rc; } -/** - * ipr_release_dump - Free adapter dump memory - * @kobj: kobject struct - * - * Return value: - * nothing - **/ -static void ipr_release_dump(struct kobject *kobj) -{ - struct ipr_dump *dump = container_of(kobj,struct ipr_dump,kobj); - struct ipr_ioa_cfg *ioa_cfg = dump->ioa_cfg; - unsigned long lock_flags = 0; - int i; - - ENTER; - spin_lock_irqsave(ioa_cfg->host->host_lock, lock_flags); - ioa_cfg->dump = NULL; - ioa_cfg->sdt_state = INACTIVE; - spin_unlock_irqrestore(ioa_cfg->host->host_lock, lock_flags); - - for (i = 0; i < dump->ioa_dump.next_page_index; i++) - free_page((unsigned long) dump->ioa_dump.ioa_data[i]); - - kfree(dump); - LEAVE; -} - -static struct kobj_type ipr_dump_kobj_type = { - .release = ipr_release_dump, -}; - /** * ipr_alloc_dump - Prepare for adapter dump * @ioa_cfg: ioa config struct @@ -2506,8 +2507,7 @@ static int ipr_alloc_dump(struct ipr_ioa_cfg *ioa_cfg) } memset(dump, 0, sizeof(struct ipr_dump)); - kobject_init(&dump->kobj); - dump->kobj.ktype = &ipr_dump_kobj_type; + kref_init(&dump->kref); dump->ioa_cfg = ioa_cfg; spin_lock_irqsave(ioa_cfg->host->host_lock, lock_flags); @@ -2554,7 +2554,7 @@ static int ipr_free_dump(struct ipr_ioa_cfg *ioa_cfg) ioa_cfg->dump = NULL; spin_unlock_irqrestore(ioa_cfg->host->host_lock, lock_flags); - kobject_put(&dump->kobj); + kref_put(&dump->kref, ipr_release_dump); LEAVE; return 0; @@ -2690,8 +2690,6 @@ static ssize_t ipr_store_tcq_enable(struct device *dev, struct ipr_resource_entry *res; unsigned long lock_flags = 0; int tcq_active = simple_strtoul(buf, NULL, 10); - int qdepth = IPR_MAX_CMD_PER_LUN; - int tagged = 0; ssize_t len = -ENXIO; spin_lock_irqsave(ioa_cfg->host->host_lock, lock_flags); @@ -2699,13 +2697,13 @@ static ssize_t ipr_store_tcq_enable(struct device *dev, res = (struct ipr_resource_entry *)sdev->hostdata; if (res) { - res->tcq_active = 0; - qdepth = res->qdepth; - if (ipr_is_gscsi(res) && sdev->tagged_supported) { if (tcq_active) { - tagged = MSG_ORDERED_TAG; res->tcq_active = 1; + scsi_activate_tcq(sdev, res->qdepth); + } else { + res->tcq_active = 0; + scsi_deactivate_tcq(sdev, res->qdepth); } len = strlen(buf); @@ -2715,7 +2713,6 @@ static ssize_t ipr_store_tcq_enable(struct device *dev, } spin_unlock_irqrestore(ioa_cfg->host->host_lock, lock_flags); - scsi_adjust_queue_depth(sdev, tagged, qdepth); return len; } @@ -2785,7 +2782,8 @@ static int ipr_biosparam(struct scsi_device *sdev, struct block_device *block_device, sector_t capacity, int *parm) { - int heads, sectors, cylinders; + int heads, sectors; + sector_t cylinders; heads = 128; sectors = 32; @@ -2849,8 +2847,8 @@ static int ipr_slave_configure(struct scsi_device *sdev) sdev->scsi_level = 4; if (ipr_is_vset_device(res)) sdev->timeout = IPR_VSET_RW_TIMEOUT; - - sdev->allow_restart = 1; + if (IPR_IS_DASD_DEVICE(res->cfgte.std_inq_data)) + sdev->allow_restart = 1; scsi_adjust_queue_depth(sdev, 0, res->qdepth); } spin_unlock_irqrestore(ioa_cfg->host->host_lock, lock_flags); @@ -3080,7 +3078,7 @@ static int ipr_cancel_op(struct scsi_cmnd * scsi_cmd) struct ipr_ioa_cfg *ioa_cfg; struct ipr_resource_entry *res; struct ipr_cmd_pkt *cmd_pkt; - u32 ioasc, ioarcb_addr; + u32 ioasc; int op_found = 0; ENTER; @@ -3101,21 +3099,15 @@ static int ipr_cancel_op(struct scsi_cmnd * scsi_cmd) if (!op_found) return SUCCESS; - ioarcb_addr = be32_to_cpu(ipr_cmd->ioarcb.ioarcb_host_pci_addr); - ipr_cmd = ipr_get_free_ipr_cmnd(ioa_cfg); ipr_cmd->ioarcb.res_handle = res->cfgte.res_handle; cmd_pkt = &ipr_cmd->ioarcb.cmd_pkt; cmd_pkt->request_type = IPR_RQTYPE_IOACMD; - cmd_pkt->cdb[0] = IPR_ABORT_TASK; - cmd_pkt->cdb[2] = (ioarcb_addr >> 24) & 0xff; - cmd_pkt->cdb[3] = (ioarcb_addr >> 16) & 0xff; - cmd_pkt->cdb[4] = (ioarcb_addr >> 8) & 0xff; - cmd_pkt->cdb[5] = ioarcb_addr & 0xff; + cmd_pkt->cdb[0] = IPR_CANCEL_ALL_REQUESTS; ipr_cmd->u.sdev = scsi_cmd->device; ipr_sdev_err(scsi_cmd->device, "Aborting command: %02X\n", scsi_cmd->cmnd[0]); - ipr_send_blocking_cmd(ipr_cmd, ipr_abort_timeout, IPR_ABORT_TASK_TIMEOUT); + ipr_send_blocking_cmd(ipr_cmd, ipr_abort_timeout, IPR_CANCEL_ALL_TIMEOUT); ioasc = be32_to_cpu(ipr_cmd->ioasa.ioasc); /* @@ -3737,7 +3729,7 @@ static void ipr_erp_start(struct ipr_ioa_cfg *ioa_cfg, switch (ioasc & IPR_IOASC_IOASC_MASK) { case IPR_IOASC_ABORTED_CMD_TERM_BY_HOST: - scsi_cmd->result |= (DID_ERROR << 16); + scsi_cmd->result |= (DID_IMM_RETRY << 16); break; case IPR_IOASC_IR_RESOURCE_HANDLE: scsi_cmd->result |= (DID_NO_CONNECT << 16); @@ -3873,7 +3865,7 @@ static int ipr_queuecommand(struct scsi_cmnd *scsi_cmd, * We have told the host to stop giving us new requests, but * ERP ops don't count. FIXME */ - if (unlikely(!ioa_cfg->allow_cmds)) + if (unlikely(!ioa_cfg->allow_cmds && !ioa_cfg->ioa_is_dead)) return SCSI_MLQUEUE_HOST_BUSY; /* @@ -5437,13 +5429,15 @@ static void ipr_free_mem(struct ipr_ioa_cfg *ioa_cfg) **/ static void ipr_free_all_resources(struct ipr_ioa_cfg *ioa_cfg) { + struct pci_dev *pdev = ioa_cfg->pdev; + ENTER; - free_irq(ioa_cfg->pdev->irq, ioa_cfg); + free_irq(pdev->irq, ioa_cfg); iounmap((void *) ioa_cfg->hdw_dma_regs); - release_mem_region(ioa_cfg->hdw_dma_regs_pci, - pci_resource_len(ioa_cfg->pdev, 0)); + pci_release_regions(pdev); ipr_free_mem(ioa_cfg); scsi_host_put(ioa_cfg->host); + pci_disable_device(pdev); LEAVE; } @@ -5458,7 +5452,7 @@ static int __devinit ipr_alloc_cmd_blks(struct ipr_ioa_cfg *ioa_cfg) { struct ipr_cmnd *ipr_cmd; struct ipr_ioarcb *ioarcb; - u32 dma_addr; + dma_addr_t dma_addr; int i; ioa_cfg->ipr_cmd_pool = pci_pool_create (IPR_NAME, ioa_cfg->pdev, @@ -5508,14 +5502,15 @@ static int __devinit ipr_alloc_cmd_blks(struct ipr_ioa_cfg *ioa_cfg) **/ static int __devinit ipr_alloc_mem(struct ipr_ioa_cfg *ioa_cfg) { - int i; + struct pci_dev *pdev = ioa_cfg->pdev; + int i, rc = -ENOMEM; ENTER; ioa_cfg->res_entries = kmalloc(sizeof(struct ipr_resource_entry) * IPR_MAX_PHYSICAL_DEVS, GFP_KERNEL); if (!ioa_cfg->res_entries) - goto cleanup; + goto out; memset(ioa_cfg->res_entries, 0, sizeof(struct ipr_resource_entry) * IPR_MAX_PHYSICAL_DEVS); @@ -5528,24 +5523,24 @@ static int __devinit ipr_alloc_mem(struct ipr_ioa_cfg *ioa_cfg) &ioa_cfg->vpd_cbs_dma); if (!ioa_cfg->vpd_cbs) - goto cleanup; + goto out_free_res_entries; if (ipr_alloc_cmd_blks(ioa_cfg)) - goto cleanup; + goto out_free_vpd_cbs; ioa_cfg->host_rrq = pci_alloc_consistent(ioa_cfg->pdev, sizeof(u32) * IPR_NUM_CMD_BLKS, &ioa_cfg->host_rrq_dma); if (!ioa_cfg->host_rrq) - goto cleanup; + goto out_ipr_free_cmd_blocks; ioa_cfg->cfg_table = pci_alloc_consistent(ioa_cfg->pdev, sizeof(struct ipr_config_table), &ioa_cfg->cfg_table_dma); if (!ioa_cfg->cfg_table) - goto cleanup; + goto out_free_host_rrq; for (i = 0; i < IPR_NUM_HCAMS; i++) { ioa_cfg->hostrcb[i] = pci_alloc_consistent(ioa_cfg->pdev, @@ -5553,9 +5548,8 @@ static int __devinit ipr_alloc_mem(struct ipr_ioa_cfg *ioa_cfg) &ioa_cfg->hostrcb_dma[i]); if (!ioa_cfg->hostrcb[i]) - goto cleanup; + goto out_free_hostrcb_dma; - memset(ioa_cfg->hostrcb[i], 0, sizeof(struct ipr_hostrcb)); ioa_cfg->hostrcb[i]->hostrcb_dma = ioa_cfg->hostrcb_dma[i] + offsetof(struct ipr_hostrcb, hcam); list_add_tail(&ioa_cfg->hostrcb[i]->queue, &ioa_cfg->hostrcb_free_q); @@ -5565,19 +5559,35 @@ static int __devinit ipr_alloc_mem(struct ipr_ioa_cfg *ioa_cfg) IPR_NUM_TRACE_ENTRIES, GFP_KERNEL); if (!ioa_cfg->trace) - goto cleanup; + goto out_free_hostrcb_dma; memset(ioa_cfg->trace, 0, sizeof(struct ipr_trace_entry) * IPR_NUM_TRACE_ENTRIES); + rc = 0; +out: LEAVE; - return 0; - -cleanup: - ipr_free_mem(ioa_cfg); + return rc; - LEAVE; - return -ENOMEM; +out_free_hostrcb_dma: + while (i-- > 0) { + pci_free_consistent(pdev, sizeof(struct ipr_hostrcb), + ioa_cfg->hostrcb[i], + ioa_cfg->hostrcb_dma[i]); + } + pci_free_consistent(pdev, sizeof(struct ipr_config_table), + ioa_cfg->cfg_table, ioa_cfg->cfg_table_dma); +out_free_host_rrq: + pci_free_consistent(pdev, sizeof(u32) * IPR_NUM_CMD_BLKS, + ioa_cfg->host_rrq, ioa_cfg->host_rrq_dma); +out_ipr_free_cmd_blocks: + ipr_free_cmd_blks(ioa_cfg); +out_free_vpd_cbs: + pci_free_consistent(pdev, sizeof(struct ipr_misc_cbs), + ioa_cfg->vpd_cbs, ioa_cfg->vpd_cbs_dma); +out_free_res_entries: + kfree(ioa_cfg->res_entries); + goto out; } /** @@ -5678,7 +5688,7 @@ static int __devinit ipr_probe_ioa(struct pci_dev *pdev, if ((rc = pci_enable_device(pdev))) { dev_err(&pdev->dev, "Cannot enable adapter\n"); - return rc; + goto out; } dev_info(&pdev->dev, "Found IOA with IRQ: %d\n", pdev->irq); @@ -5687,7 +5697,8 @@ static int __devinit ipr_probe_ioa(struct pci_dev *pdev, if (!host) { dev_err(&pdev->dev, "call to scsi_host_alloc failed!\n"); - return -ENOMEM; + rc = -ENOMEM; + goto out_disable; } ioa_cfg = (struct ipr_ioa_cfg *)host->hostdata; @@ -5697,12 +5708,11 @@ static int __devinit ipr_probe_ioa(struct pci_dev *pdev, ipr_regs_pci = pci_resource_start(pdev, 0); - if (!request_mem_region(ipr_regs_pci, - pci_resource_len(pdev, 0), IPR_NAME)) { + rc = pci_request_regions(pdev, IPR_NAME); + if (rc < 0) { dev_err(&pdev->dev, "Couldn't register memory range of registers\n"); - scsi_host_put(host); - return -ENOMEM; + goto out_scsi_host_put; } ipr_regs = (unsigned long)ioremap(ipr_regs_pci, @@ -5711,9 +5721,8 @@ static int __devinit ipr_probe_ioa(struct pci_dev *pdev, if (!ipr_regs) { dev_err(&pdev->dev, "Couldn't map memory range of registers\n"); - release_mem_region(ipr_regs_pci, pci_resource_len(pdev, 0)); - scsi_host_put(host); - return -ENOMEM; + rc = -ENOMEM; + goto out_release_regions; } ioa_cfg->hdw_dma_regs = ipr_regs; @@ -5723,11 +5732,10 @@ static int __devinit ipr_probe_ioa(struct pci_dev *pdev, ipr_init_ioa_cfg(ioa_cfg, host, pdev); pci_set_master(pdev); - rc = pci_set_dma_mask(pdev, 0xffffffff); - if (rc != PCIBIOS_SUCCESSFUL) { + rc = pci_set_dma_mask(pdev, DMA_32BIT_MASK); + if (rc < 0) { dev_err(&pdev->dev, "Failed to set PCI DMA mask\n"); - rc = -EIO; goto cleanup_nomem; } @@ -5755,8 +5763,12 @@ static int __devinit ipr_probe_ioa(struct pci_dev *pdev, if ((rc = ipr_set_pcix_cmd_reg(ioa_cfg))) goto cleanup_nomem; - if ((rc = ipr_alloc_mem(ioa_cfg))) - goto cleanup; + rc = ipr_alloc_mem(ioa_cfg); + if (rc < 0) { + dev_err(&pdev->dev, + "Couldn't allocate enough memory for device driver!\n"); + goto cleanup_nomem; + } ipr_mask_and_clear_interrupts(ioa_cfg, ~IPR_PCII_IOA_TRANS_TO_OPER); rc = request_irq(pdev->irq, ipr_isr, SA_SHIRQ, IPR_NAME, ioa_cfg); @@ -5772,18 +5784,20 @@ static int __devinit ipr_probe_ioa(struct pci_dev *pdev, spin_unlock(&ipr_driver_lock); LEAVE; - return 0; +out: + return rc; -cleanup: - dev_err(&pdev->dev, "Couldn't allocate enough memory for device driver!\n"); cleanup_nolog: ipr_free_mem(ioa_cfg); cleanup_nomem: iounmap((void *) ipr_regs); - release_mem_region(ipr_regs_pci, pci_resource_len(pdev, 0)); +out_release_regions: + pci_release_regions(pdev); +out_scsi_host_put: scsi_host_put(host); - - return rc; +out_disable: + pci_disable_device(pdev); + goto out; } /** @@ -5988,9 +6002,15 @@ static struct pci_device_id ipr_pci_table[] __devinitdata = { { PCI_VENDOR_ID_MYLEX, PCI_DEVICE_ID_IBM_GEMSTONE, PCI_VENDOR_ID_IBM, IPR_SUBS_DEV_ID_573D, 0, 0, (kernel_ulong_t)&ipr_chip_cfg[0] }, + { PCI_VENDOR_ID_IBM, PCI_DEVICE_ID_IBM_CITRINE, + PCI_VENDOR_ID_IBM, IPR_SUBS_DEV_ID_571B, + 0, 0, (kernel_ulong_t)&ipr_chip_cfg[0] }, { PCI_VENDOR_ID_IBM, PCI_DEVICE_ID_IBM_SNIPE, PCI_VENDOR_ID_IBM, IPR_SUBS_DEV_ID_2780, 0, 0, (kernel_ulong_t)&ipr_chip_cfg[1] }, + { PCI_VENDOR_ID_IBM, PCI_DEVICE_ID_IBM_SNIPE, + PCI_VENDOR_ID_IBM, IPR_SUBS_DEV_ID_570F, + 0, 0, (kernel_ulong_t)&ipr_chip_cfg[1] }, { } }; MODULE_DEVICE_TABLE(pci, ipr_pci_table); @@ -6009,16 +6029,14 @@ static struct pci_driver ipr_driver = { * ipr_init - Module entry point * * Return value: - * 0 on success / non-zero on failure + * 0 on success / negative value on failure **/ static int __init ipr_init(void) { ipr_info("IBM Power RAID SCSI Device Driver version: %s %s\n", IPR_DRIVER_VERSION, IPR_DRIVER_DATE); - pci_register_driver(&ipr_driver); - - return 0; + return pci_module_init(&ipr_driver); } /** diff --git a/drivers/scsi/ipr.h b/drivers/scsi/ipr.h index 6d616ac42..e91afc446 100644 --- a/drivers/scsi/ipr.h +++ b/drivers/scsi/ipr.h @@ -1,7 +1,7 @@ /* * ipr.h -- driver for IBM Power Linux RAID adapters * - * Written By: Brian King, IBM Corporation + * Written By: Brian King , IBM Corporation * * Copyright (C) 2003, 2004 IBM Corporation * @@ -19,6 +19,8 @@ * 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 - Removed several careless u32/dma_addr_t errors + * that broke 64bit platforms. */ #ifndef _IPR_H @@ -27,6 +29,7 @@ #include #include #include +#include #include #include #ifdef CONFIG_KDB @@ -36,8 +39,8 @@ /* * Literals */ -#define IPR_DRIVER_VERSION "2.0.10" -#define IPR_DRIVER_DATE "(June 7, 2004)" +#define IPR_DRIVER_VERSION "2.0.11" +#define IPR_DRIVER_DATE "(August 3, 2004)" /* * IPR_DBG_TRACE: Setting this to 1 will turn on some general function tracing @@ -72,6 +75,8 @@ #define IPR_SUBS_DEV_ID_5703 0x0278 #define IPR_SUBS_DEV_ID_572E 0x02D3 #define IPR_SUBS_DEV_ID_573D 0x02D4 +#define IPR_SUBS_DEV_ID_570F 0x02BD +#define IPR_SUBS_DEV_ID_571B 0x02BE #define IPR_NAME "ipr" @@ -148,7 +153,6 @@ #define IPR_BUS_RESET 0x10 #define IPR_ID_HOST_RR_Q 0xC4 #define IPR_QUERY_IOA_CONFIG 0xC5 -#define IPR_ABORT_TASK 0xC7 #define IPR_CANCEL_ALL_REQUESTS 0xCE #define IPR_HOST_CONTROLLED_ASYNC 0xCF #define IPR_HCAM_CDB_OP_CODE_CONFIG_CHANGE 0x01 @@ -667,7 +671,7 @@ struct ipr_hcam { struct ipr_hostrcb { struct ipr_hcam hcam; - u32 hostrcb_dma; + dma_addr_t hostrcb_dma; struct list_head queue; }; @@ -850,7 +854,7 @@ struct ipr_ioa_cfg { char cfg_table_start[8]; #define IPR_CFG_TBL_START "cfg" struct ipr_config_table *cfg_table; - u32 cfg_table_dma; + dma_addr_t cfg_table_dma; char resource_table_label[8]; #define IPR_RES_TABLE_LABEL "res_tbl" @@ -861,12 +865,12 @@ struct ipr_ioa_cfg { char ipr_hcam_label[8]; #define IPR_HCAM_LABEL "hcams" struct ipr_hostrcb *hostrcb[IPR_NUM_HCAMS]; - u32 hostrcb_dma[IPR_NUM_HCAMS]; + dma_addr_t hostrcb_dma[IPR_NUM_HCAMS]; struct list_head hostrcb_free_q; struct list_head hostrcb_pending_q; u32 *host_rrq; - u32 host_rrq_dma; + dma_addr_t host_rrq_dma; #define IPR_HRRQ_REQ_RESP_HANDLE_MASK 0xfffffffc #define IPR_HRRQ_RESP_BIT_SET 0x00000002 #define IPR_HRRQ_TOGGLE_BIT 0x00000001 @@ -905,7 +909,7 @@ struct ipr_ioa_cfg { enum ipr_sdt_state sdt_state; struct ipr_misc_cbs *vpd_cbs; - u32 vpd_cbs_dma; + dma_addr_t vpd_cbs_dma; struct pci_pool *ipr_cmd_pool; @@ -1029,7 +1033,7 @@ struct ipr_ioa_dump { }__attribute__((packed, aligned (4))); struct ipr_dump { - struct kobject kobj; + struct kref kref; struct ipr_ioa_cfg *ioa_cfg; struct ipr_driver_dump driver_dump; struct ipr_ioa_dump ioa_dump; diff --git a/drivers/scsi/lpfc/Makefile b/drivers/scsi/lpfc/Makefile new file mode 100644 index 000000000..5f06e633c --- /dev/null +++ b/drivers/scsi/lpfc/Makefile @@ -0,0 +1,7 @@ +# Driver for Emulex LightPulse fibre channel host bus adapters. +EXTRA_CFLAGS += -DFC_TRANS_VER1 +obj-$(CONFIG_SCSI_LPFC) := lpfc.o + +lpfc-objs := lpfc_mem.o lpfc_sli.o lpfc_ct.o lpfc_els.o \ +lpfc_hbadisc.o lpfc_init.o lpfc_mbox.o lpfc_nportdisc.o lpfc_scsiport.o \ +lpfc_fcp.o diff --git a/drivers/scsi/lpfc/lpfc.h b/drivers/scsi/lpfc/lpfc.h new file mode 100644 index 000000000..4fea35cc1 --- /dev/null +++ b/drivers/scsi/lpfc/lpfc.h @@ -0,0 +1,449 @@ +/******************************************************************* + * This file is part of the Emulex Linux Device Driver for * + * Enterprise Fibre Channel Host Bus Adapters. * + * Refer to the README file included with this package for * + * driver version and adapter support. * + * Copyright (C) 2004 Emulex Corporation. * + * www.emulex.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, a copy of which * + * can be found in the file COPYING included with this package. * + *******************************************************************/ + +/* + * $Id: lpfc.h 1.134 2004/11/18 14:53:45EST sf_support Exp $ + */ + +#ifndef _H_LPFC +#define _H_LPFC + +struct lpfc_sli2_slim; + +#define LPFC_MAX_TARGET 256 /* max nunber of targets + supported */ +#define LPFC_MAX_DISC_THREADS 64 /* max outstanding discovery els + requests */ +#define LPFC_MAX_NS_RETRY 3 /* Try to get to the NameServer + 3 times and then give up. */ +#define LPFC_DFT_HBA_Q_DEPTH 2048 /* max cmds per hba */ +#define LPFC_LC_HBA_Q_DEPTH 1024 /* max cmds per low cost hba */ +#define LPFC_LP101_HBA_Q_DEPTH 128 /* max cmds per low cost hba */ + +/* Define the SLIM2 page size. */ +#define LPFC_SLIM2_PAGE_AREA 8192 + +/* Define macros for 64 bit support */ +#define putPaddrLow(addr) ((uint32_t) (0xffffffff & (u64)(addr))) +#define putPaddrHigh(addr) ((uint32_t) (0xffffffff & (((u64)(addr))>>32))) +#define getPaddr(high, low) ((dma_addr_t)( \ + (( (u64)(high)<<16 ) << 16)|( (u64)(low)))) +/* Provide maximum configuration definitions. */ +#define LPFC_DRVR_TIMEOUT 16 /* driver iocb timeout value in sec */ +#define MAX_FCP_TARGET 256 /* max num of FCP targets supported */ +#define FC_MAX_ADPTMSG 64 + +#define MAX_HBAEVT 32 + +#if __LITTLE_ENDIAN + +#define putLunLow(lunlow, lun) \ + { \ + lunlow = 0; \ + } + +#define putLunHigh(lunhigh, lun) \ + { \ + lunhigh = swab16(lun); \ + } + +#else /* BIG_ENDIAN_HOST */ + +#define putLunLow(lunlow, lun) \ + { \ + lunlow = 0; \ + } + +#define putLunHigh(lunhigh, lun) \ + { \ + lunhigh = (uint32_t)(lun << 16); \ + } +#endif + +/****************************************************************************/ +/* Device VPD save area */ +/****************************************************************************/ +typedef struct lpfc_vpd { + uint32_t status; /* vpd status value */ + uint32_t length; /* number of bytes actually returned */ + struct { + uint32_t rsvd1; /* Revision numbers */ + uint32_t biuRev; + uint32_t smRev; + uint32_t smFwRev; + uint32_t endecRev; + uint16_t rBit; + uint8_t fcphHigh; + uint8_t fcphLow; + uint8_t feaLevelHigh; + uint8_t feaLevelLow; + uint32_t postKernRev; + uint32_t opFwRev; + uint8_t opFwName[16]; + uint32_t sli1FwRev; + uint8_t sli1FwName[16]; + uint32_t sli2FwRev; + uint8_t sli2FwName[16]; + } rev; +} lpfc_vpd_t; + +struct lpfc_scsi_buf; + +struct lpfc_hba_event { + uint32_t fc_eventcode; + uint32_t fc_evdata1; + uint32_t fc_evdata2; + uint32_t fc_evdata3; + uint32_t fc_evdata4; +}; + +/* + * lpfc stat counters + */ +struct lpfc_stats { + /* Statistics for ELS commands */ + uint32_t elsLogiCol; + uint32_t elsRetryExceeded; + uint32_t elsXmitRetry; + uint32_t elsDelayRetry; + uint32_t elsRcvDrop; + uint32_t elsRcvFrame; + uint32_t elsRcvRSCN; + uint32_t elsRcvRNID; + uint32_t elsRcvFARP; + uint32_t elsRcvFARPR; + uint32_t elsRcvFLOGI; + uint32_t elsRcvPLOGI; + uint32_t elsRcvADISC; + uint32_t elsRcvPDISC; + uint32_t elsRcvFAN; + uint32_t elsRcvLOGO; + uint32_t elsRcvPRLO; + uint32_t elsRcvPRLI; + uint32_t elsRcvRRQ; + uint32_t elsXmitFLOGI; + uint32_t elsXmitPLOGI; + uint32_t elsXmitPRLI; + uint32_t elsXmitADISC; + uint32_t elsXmitLOGO; + uint32_t elsXmitSCR; + uint32_t elsXmitRNID; + uint32_t elsXmitFARP; + uint32_t elsXmitFARPR; + uint32_t elsXmitACC; + uint32_t elsXmitLSRJT; + + uint32_t frameRcvBcast; + uint32_t frameRcvMulti; + uint32_t strayXmitCmpl; + uint32_t frameXmitDelay; + uint32_t xriCmdCmpl; + uint32_t xriStatErr; + uint32_t LinkUp; + uint32_t LinkDown; + uint32_t LinkMultiEvent; + uint32_t NoRcvBuf; + uint32_t fcpCmd; + uint32_t fcpCmpl; + uint32_t fcpRspErr; + uint32_t fcpRemoteStop; + uint32_t fcpPortRjt; + uint32_t fcpPortBusy; + uint32_t fcpError; + uint32_t fcpLocalErr; +}; + +enum sysfs_mbox_state { + SMBOX_IDLE, + SMBOX_WRITING, + SMBOX_READING +}; + +struct lpfc_sysfs_mbox { + enum sysfs_mbox_state state; + size_t offset; + struct lpfcMboxq * mbox; +}; + +struct lpfc_hba { + uint32_t intr_inited; /* flag for interrupt registration */ + struct list_head hba_list; /* List of hbas/ports */ + struct lpfc_sli sli; + struct lpfc_sli2_slim *slim2p; + dma_addr_t slim2p_mapping; + + uint32_t hba_state; + +#define LPFC_INIT_START 1 /* Initial state after board reset */ +#define LPFC_INIT_MBX_CMDS 2 /* Initialize HBA with mbox commands */ +#define LPFC_LINK_DOWN 3 /* HBA initialized, link is down */ +#define LPFC_LINK_UP 4 /* Link is up - issue READ_LA */ +#define LPFC_LOCAL_CFG_LINK 5 /* local NPORT Id configured */ +#define LPFC_FLOGI 6 /* FLOGI sent to Fabric */ +#define LPFC_FABRIC_CFG_LINK 7 /* Fabric assigned NPORT Id + configured */ +#define LPFC_NS_REG 8 /* Register with NameServer */ +#define LPFC_NS_QRY 9 /* Query NameServer for NPort ID list */ +#define LPFC_BUILD_DISC_LIST 10 /* Build ADISC and PLOGI lists for + * device authentication / discovery */ +#define LPFC_DISC_AUTH 11 /* Processing ADISC list */ +#define LPFC_CLEAR_LA 12 /* authentication cmplt - issue + CLEAR_LA */ +#define LPFC_HBA_READY 32 +#define LPFC_HBA_ERROR 0xff + + uint8_t fc_linkspeed; /* Link speed after last READ_LA */ + + uint32_t fc_eventTag; /* event tag for link attention */ + uint32_t fc_prli_sent; /* cntr for outstanding PRLIs */ + + uint32_t num_disc_nodes; /*in addition to hba_state */ + + uint8_t fcp_mapping; /* Map FCP devices based on WWNN WWPN or DID */ +#define FCP_SEED_WWNN 0x1 +#define FCP_SEED_WWPN 0x2 +#define FCP_SEED_DID 0x4 +#define FCP_SEED_MASK 0x7 +#define FCP_SEED_AUTO 0x8 /* binding was created by auto mapping */ + + struct timer_list fc_estabtmo; /* link establishment timer */ + struct timer_list fc_disctmo; /* Discovery rescue timer */ + struct timer_list fc_fdmitmo; /* fdmi timer */ + struct timer_list fc_scantmo; /* scsi scan host timer */ + + + void *fc_evt_head; /* waiting for event queue */ + void *fc_evt_tail; /* waiting for event queue */ + + uint16_t hba_event_put; /* hbaevent event put word anchor */ + uint16_t hba_event_get; /* hbaevent event get word anchor */ + uint32_t hba_event_missed; /* hbaevent missed event word anchor */ + uint32_t sid_cnt; /* SCSI ID counter */ + + struct lpfc_hba_event hbaevt[MAX_HBAEVT]; + + /* These fields used to be binfo */ + struct lpfc_name fc_nodename; /* fc nodename */ + struct lpfc_name fc_portname; /* fc portname */ + uint32_t fc_pref_DID; /* preferred D_ID */ + uint8_t fc_pref_ALPA; /* preferred AL_PA */ + uint32_t fc_edtov; /* E_D_TOV timer value */ + uint32_t fc_arbtov; /* ARB_TOV timer value */ + uint32_t fc_ratov; /* R_A_TOV timer value */ + uint32_t fc_rttov; /* R_T_TOV timer value */ + uint32_t fc_altov; /* AL_TOV timer value */ + uint32_t fc_crtov; /* C_R_TOV timer value */ + uint32_t fc_citov; /* C_I_TOV timer value */ + uint32_t fc_myDID; /* fibre channel S_ID */ + uint32_t fc_prevDID; /* previous fibre channel S_ID */ + + struct serv_parm fc_sparam; /* buffer for our service parameters */ + struct serv_parm fc_fabparam; /* fabric service parameters buffer */ + uint8_t alpa_map[128]; /* AL_PA map from READ_LA */ + + uint8_t fc_ns_retry; /* retries for fabric nameserver */ + uint32_t fc_nlp_cnt; /* outstanding NODELIST requests */ + uint32_t fc_rscn_id_cnt; /* count of RSCNs payloads in list */ + struct lpfc_dmabuf *fc_rscn_id_list[FC_MAX_HOLD_RSCN]; + uint32_t lmt; + uint32_t fc_flag; /* FC flags */ +#define FC_PT2PT 0x1 /* pt2pt with no fabric */ +#define FC_PT2PT_PLOGI 0x2 /* pt2pt initiate PLOGI */ +#define FC_DISC_TMO 0x4 /* Discovery timer running */ +#define FC_PUBLIC_LOOP 0x8 /* Public loop */ +#define FC_LBIT 0x10 /* LOGIN bit in loopinit set */ +#define FC_RSCN_MODE 0x20 /* RSCN cmd rcv'ed */ +#define FC_NLP_MORE 0x40 /* More node to process in node tbl */ +#define FC_OFFLINE_MODE 0x80 /* Interface is offline for diag */ +#define FC_FABRIC 0x100 /* We are fabric attached */ +#define FC_ESTABLISH_LINK 0x200 /* Reestablish Link */ +#define FC_RSCN_DISCOVERY 0x400 /* Authenticate all devices after RSCN*/ +#define FC_LOADING 0x1000 /* HBA in process of loading drvr */ +#define FC_UNLOADING 0x2000 /* HBA in process of unloading drvr */ +#define FC_SCSI_SCAN_TMO 0x4000 /* scsi scan timer running */ +#define FC_ABORT_DISCOVERY 0x8000 /* we want to abort discovery */ + + uint32_t fc_topology; /* link topology, from LINK INIT */ + + struct lpfc_stats fc_stat; + + /* These are the head/tail pointers for the bind, plogi, adisc, unmap, + * and map lists. Their counters are immediately following. + */ + struct list_head fc_nlpbind_list; + struct list_head fc_plogi_list; + struct list_head fc_adisc_list; + struct list_head fc_reglogin_list; + struct list_head fc_prli_list; + struct list_head fc_nlpunmap_list; + struct list_head fc_nlpmap_list; + struct list_head fc_npr_list; + struct list_head fc_unused_list; + + /* Keep counters for the number of entries in each list. */ + uint16_t fc_bind_cnt; + uint16_t fc_plogi_cnt; + uint16_t fc_adisc_cnt; + uint16_t fc_reglogin_cnt; + uint16_t fc_prli_cnt; + uint16_t fc_unmap_cnt; + uint16_t fc_map_cnt; + uint16_t fc_npr_cnt; + uint16_t fc_unused_cnt; + struct lpfc_nodelist fc_fcpnodev; /* nodelist entry for no device */ + uint32_t nport_event_cnt; /* timestamp for nlplist entry */ + + struct lpfc_target *device_queue_hash[MAX_FCP_TARGET]; +#define LPFC_RPI_HASH_SIZE 64 +#define LPFC_RPI_HASH_FUNC(x) ((x) & (0x3f)) + /* ptr to active D_ID / RPIs */ + struct lpfc_nodelist *fc_nlplookup[LPFC_RPI_HASH_SIZE]; + uint32_t wwnn[2]; + uint32_t RandomData[7]; + + uint32_t cfg_log_verbose; + uint32_t cfg_lun_queue_depth; + uint32_t cfg_nodev_tmo; + uint32_t cfg_hba_queue_depth; + uint32_t cfg_automap; + uint32_t cfg_fcp_class; + uint32_t cfg_use_adisc; + uint32_t cfg_ack0; + uint32_t cfg_topology; + uint32_t cfg_scan_down; + uint32_t cfg_link_speed; + uint32_t cfg_cr_delay; + uint32_t cfg_cr_count; + uint32_t cfg_fdmi_on; + uint32_t cfg_fcp_bind_method; + uint32_t cfg_discovery_threads; + uint32_t cfg_max_luns; + uint32_t cfg_scsi_hotplug; + + lpfc_vpd_t vpd; /* vital product data */ + +#if defined(FC_TRANS_265_BLKPATCH) + /* + * Provide a per-HBA timer for 2.6.5 kernels patched with the + * block/unblock FC transport patch. + */ + struct timer_list dev_loss_timer; +#endif + + struct Scsi_Host *host; + struct pci_dev *pcidev; + struct list_head dpc_disc; + + pid_t dpc_pid; + int dpc_kill; + struct completion dpc_startup; + struct completion dpc_exiting; + struct semaphore *dpc_wait; + + unsigned long pci_bar0_map; /* Physical address for PCI BAR0 */ + unsigned long pci_bar2_map; /* Physical address for PCI BAR2 */ + void *slim_memmap_p; /* Kernel memory mapped address for PCI + BAR0 */ + void *ctrl_regs_memmap_p; /* Kernel memory mapped address for PCI + BAR2 */ + + void *MBslimaddr; /* virtual address for mbox cmds */ + void *HAregaddr; /* virtual address for host attn reg */ + void *CAregaddr; /* virtual address for chip attn reg */ + void *HSregaddr; /* virtual address for host status reg */ + void *HCregaddr; /* virtual address for host ctl reg */ + wait_queue_head_t linkevtwq; + wait_queue_head_t rscnevtwq; + wait_queue_head_t ctevtwq; + + uint8_t brd_no; /* FC board number */ + + char SerialNumber[32]; /* adapter Serial Number */ + char OptionROMVersion[32]; /* adapter BIOS / Fcode version */ + + struct timer_list els_tmofunc; + + void *link_stats; + + /* + * stat counters + */ + uint64_t fc4InputRequests; + uint64_t fc4OutputRequests; + uint64_t fc4ControlRequests; + + struct lpfc_sysfs_mbox sysfs_mbox; +; + /* pci_mem_pools */ + struct pci_pool *lpfc_scsi_dma_ext_pool; + struct pci_pool *lpfc_mbuf_pool; + struct lpfc_dma_pool lpfc_mbuf_safety_pool; + mempool_t *scsibuf_mem_pool; + + mempool_t *iocb_mem_pool; + mempool_t *mbox_mem_pool; + mempool_t *nlp_mem_pool; + mempool_t *bind_mem_pool; + struct list_head freebufList; + struct list_head ctrspbuflist; + struct list_head rnidrspbuflist; +}; + +/* event mask definitions */ +#define FC_REG_LINK_EVENT 0x1 /* Register for link up / down events */ +#define FC_REG_RSCN_EVENT 0x2 /* Register for RSCN events */ +#define FC_REG_CT_EVENT 0x4 /* Register for CT request events */ + +#define FC_FSTYPE_ALL 0xffff /* match on all fsTypes */ + +typedef struct fcEVT { /* Kernel level Event structure */ + uint32_t evt_handle; + uint32_t evt_mask; + uint32_t evt_data0; + uint16_t evt_sleep; + uint16_t evt_flags; + void *evt_type; + void *evt_next; + void *evt_data1; + uint32_t evt_data2; +} fcEVT_t; + +typedef struct fcEVTHDR { /* Kernel level Event Header */ + uint32_t e_handle; + uint32_t e_mask; + uint16_t e_mode; +#define E_SLEEPING_MODE 0x0001 + uint16_t e_refcnt; + uint16_t e_flag; +#define E_GET_EVENT_ACTIVE 0x0001 + fcEVT_t *e_head; + fcEVT_t *e_tail; + void *e_next_header; + void *e_type; +} fcEVTHDR_t; + +struct rnidrsp { + void *buf; + uint32_t uniqueid; + struct list_head list; + uint32_t data; +}; + +#endif /* _H_LPFC */ diff --git a/drivers/scsi/lpfc/lpfc_compat.h b/drivers/scsi/lpfc/lpfc_compat.h new file mode 100644 index 000000000..43531b50c --- /dev/null +++ b/drivers/scsi/lpfc/lpfc_compat.h @@ -0,0 +1,120 @@ +/******************************************************************* + * This file is part of the Emulex Linux Device Driver for * + * Enterprise Fibre Channel Host Bus Adapters. * + * Refer to the README file included with this package for * + * driver version and adapter support. * + * Copyright (C) 2004 Emulex Corporation. * + * www.emulex.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, a copy of which * + * can be found in the file COPYING included with this package. * + *******************************************************************/ + +/* + * $Id: lpfc_compat.h 1.28 2004/11/09 14:49:24EST sf_support Exp $ + * + * This file provides macros to aid compilation in the Linux 2.4 kernel + * over various platform architectures. + */ + +#ifndef _H_LPFC_COMPAT +#define _H_LPFC_COMPAT + + +/******************************************************************* +Note: HBA's SLI memory contains little-endian LW. +Thus to access it from a little-endian host, +memcpy_toio() and memcpy_fromio() can be used. +However on a big-endian host, copy 4 bytes at a time, +using writel() and readl(). + *******************************************************************/ + +#if __BIG_ENDIAN + +static inline void +lpfc_memcpy_to_slim( void *dest, void *src, unsigned int bytes) +{ + uint32_t *dest32; + uint32_t *src32; + unsigned int four_bytes; + + + dest32 = (uint32_t *) dest; + src32 = (uint32_t *) src; + + /* write input bytes, 4 bytes at a time */ + for (four_bytes = bytes /4; four_bytes > 0; four_bytes--) { + writel( *src32, dest32); + readl(dest32); /* flush */ + dest32++; + src32++; + } + + return; +} + +static inline void +lpfc_memcpy_from_slim( void *dest, void *src, unsigned int bytes) +{ + uint32_t *dest32; + uint32_t *src32; + unsigned int four_bytes; + + + dest32 = (uint32_t *) dest; + src32 = (uint32_t *) src; + + /* read input bytes, 4 bytes at a time */ + for (four_bytes = bytes /4; four_bytes > 0; four_bytes--) { + *dest32 = readl( src32); + dest32++; + src32++; + } + + return; +} + +#else + +static inline void +lpfc_memcpy_to_slim( void *dest, void *src, unsigned int bytes) +{ + /* actually returns 1 byte past dest */ + memcpy_toio( dest, src, bytes); +} + +static inline void +lpfc_memcpy_from_slim( void *dest, void *src, unsigned int bytes) +{ + /* actually returns 1 byte past dest */ + memcpy_fromio( dest, src, bytes); +} + +#endif /* __BIG_ENDIAN */ + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,6) +#define msleep(x) do { \ + set_current_state(TASK_UNINTERRUPTIBLE); \ + schedule_timeout((x)); \ + } while (0); +/* Provide local msecs_to_jiffies call for earlier kernels */ +static inline unsigned long msecs_to_jiffies(const unsigned int m) +{ +#if HZ <= 1000 && !(1000 % HZ) + return (m + (1000 / HZ) - 1) / (1000 / HZ); +#elif HZ > 1000 && !(HZ % 1000) + return m * (HZ / 1000); +#else + return (m * HZ + 999) / 1000; +#endif +} +#endif +#endif /* _H_LPFC_COMPAT */ diff --git a/drivers/scsi/lpfc/lpfc_crtn.h b/drivers/scsi/lpfc/lpfc_crtn.h new file mode 100644 index 000000000..ac414f82d --- /dev/null +++ b/drivers/scsi/lpfc/lpfc_crtn.h @@ -0,0 +1,265 @@ +/******************************************************************* + * This file is part of the Emulex Linux Device Driver for * + * Enterprise Fibre Channel Host Bus Adapters. * + * Refer to the README file included with this package for * + * driver version and adapter support. * + * Copyright (C) 2004 Emulex Corporation. * + * www.emulex.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, a copy of which * + * can be found in the file COPYING included with this package. * + *******************************************************************/ + +/* + * $Id: lpfc_crtn.h 1.140 2004/11/17 19:00:57EST sf_support Exp $ + */ + +#ifndef _H_LPFC_CRTN +#define _H_LPFC_CRTN + +#include +#include +#include + +#include "lpfc_disc.h" +#include "lpfc_logmsg.h" +#include "lpfc_scsi.h" +#include "lpfc_sli.h" + + +void lpfc_dump_mem(struct lpfc_hba *, LPFC_MBOXQ_t *); +void lpfc_read_nv(struct lpfc_hba *, LPFC_MBOXQ_t *); +int lpfc_read_la(struct lpfc_hba *, LPFC_MBOXQ_t *); +void lpfc_clear_la(struct lpfc_hba *, LPFC_MBOXQ_t *); +void lpfc_config_link(struct lpfc_hba *, LPFC_MBOXQ_t *); +int lpfc_read_sparam(struct lpfc_hba *, LPFC_MBOXQ_t *); +void lpfc_read_config(struct lpfc_hba *, LPFC_MBOXQ_t *); +void lpfc_set_slim(struct lpfc_hba *, LPFC_MBOXQ_t *, uint32_t, uint32_t); +int lpfc_reg_login(struct lpfc_hba *, uint32_t, uint8_t *, LPFC_MBOXQ_t *, + uint32_t); +void lpfc_unreg_login(struct lpfc_hba *, uint32_t, LPFC_MBOXQ_t *); +void lpfc_unreg_did(struct lpfc_hba *, uint32_t, LPFC_MBOXQ_t *); +void lpfc_init_link(struct lpfc_hba *, LPFC_MBOXQ_t *, uint32_t, uint32_t); + + +int lpfc_linkdown(struct lpfc_hba *); +void lpfc_mbx_cmpl_read_la(struct lpfc_hba *, LPFC_MBOXQ_t *); + +void lpfc_mbx_cmpl_clear_la(struct lpfc_hba *, LPFC_MBOXQ_t *); +void lpfc_mbx_cmpl_reg_login(struct lpfc_hba *, LPFC_MBOXQ_t *); +void lpfc_mbx_cmpl_fabric_reg_login(struct lpfc_hba *, LPFC_MBOXQ_t *); +void lpfc_mbx_cmpl_ns_reg_login(struct lpfc_hba *, LPFC_MBOXQ_t *); +void lpfc_mbx_cmpl_fdmi_reg_login(struct lpfc_hba *, LPFC_MBOXQ_t *); +int lpfc_consistent_bind_save(struct lpfc_hba *, struct lpfc_bindlist *); +int lpfc_nlp_plogi(struct lpfc_hba *, struct lpfc_nodelist *); +int lpfc_nlp_adisc(struct lpfc_hba *, struct lpfc_nodelist *); +int lpfc_nlp_unmapped(struct lpfc_hba *, struct lpfc_nodelist *); +int lpfc_nlp_mapped(struct lpfc_hba *, struct lpfc_nodelist *, + struct lpfc_bindlist *); +int lpfc_nlp_list(struct lpfc_hba *, struct lpfc_nodelist *, int); +void lpfc_set_disctmo(struct lpfc_hba *); +int lpfc_can_disctmo(struct lpfc_hba *); +int lpfc_unreg_rpi(struct lpfc_hba *, struct lpfc_nodelist *); +int lpfc_check_sli_ndlp(struct lpfc_hba *, struct lpfc_sli_ring *, + struct lpfc_iocbq *, struct lpfc_nodelist *); +int lpfc_nlp_remove(struct lpfc_hba *, struct lpfc_nodelist *); +void lpfc_nlp_init(struct lpfc_hba *, struct lpfc_nodelist *, uint32_t); +struct lpfc_nodelist *lpfc_setup_disc_node(struct lpfc_hba *, uint32_t); +struct lpfc_nodelist *lpfc_setup_rscn_node(struct lpfc_hba *, uint32_t); +void lpfc_disc_list_loopmap(struct lpfc_hba *); +void lpfc_disc_start(struct lpfc_hba *); +void lpfc_disc_flush_list(struct lpfc_hba *); +void lpfc_establish_link_tmo(unsigned long); +void lpfc_disc_timeout(unsigned long); +void lpfc_scan_timeout(unsigned long); +struct lpfc_target *lpfc_find_target(struct lpfc_hba *, uint32_t, + struct lpfc_nodelist *); +void lpfc_set_failmask(struct lpfc_hba *, struct lpfc_nodelist *, uint32_t, + uint32_t); +void lpfc_process_nodev_timeout(struct lpfc_hba *, struct lpfc_nodelist *); + +struct lpfc_nodelist *lpfc_findnode_rpi(struct lpfc_hba * phba, uint16_t rpi); +struct lpfc_nodelist *lpfc_findnode_remove_rpi(struct lpfc_hba * phba, + uint16_t rpi); +void lpfc_addnode_rpi(struct lpfc_hba * phba, struct lpfc_nodelist * ndlp, + uint16_t rpi); + +int lpfc_discq_post_event(struct lpfc_hba *, void *, void *, uint32_t); +int lpfc_do_dpc(void *); +int lpfc_disc_state_machine(struct lpfc_hba *, struct lpfc_nodelist *, void *, + uint32_t); + +uint32_t lpfc_cmpl_prli_reglogin_issue(struct lpfc_hba *, + struct lpfc_nodelist *, void *, + uint32_t); +uint32_t lpfc_cmpl_plogi_prli_issue(struct lpfc_hba *, struct lpfc_nodelist *, + void *, uint32_t); + +int lpfc_check_sparm(struct lpfc_hba *, struct lpfc_nodelist *, + struct serv_parm *, uint32_t); +int lpfc_els_abort_flogi(struct lpfc_hba *); +int lpfc_initial_flogi(struct lpfc_hba *); +int lpfc_issue_els_plogi(struct lpfc_hba *, struct lpfc_nodelist *, uint8_t); +int lpfc_issue_els_prli(struct lpfc_hba *, struct lpfc_nodelist *, uint8_t); +int lpfc_issue_els_adisc(struct lpfc_hba *, struct lpfc_nodelist *, uint8_t); +int lpfc_issue_els_logo(struct lpfc_hba *, struct lpfc_nodelist *, uint8_t); +int lpfc_issue_els_scr(struct lpfc_hba *, uint32_t, uint8_t); +int lpfc_els_free_iocb(struct lpfc_hba *, struct lpfc_iocbq *); +int lpfc_els_rsp_acc(struct lpfc_hba *, uint32_t, struct lpfc_iocbq *, + struct lpfc_nodelist *, LPFC_MBOXQ_t *, uint8_t); +int lpfc_els_rsp_reject(struct lpfc_hba *, uint32_t, struct lpfc_iocbq *, + struct lpfc_nodelist *); +int lpfc_els_rsp_adisc_acc(struct lpfc_hba *, struct lpfc_iocbq *, + struct lpfc_nodelist *); +int lpfc_els_rsp_prli_acc(struct lpfc_hba *, struct lpfc_iocbq *, + struct lpfc_nodelist *); +void lpfc_els_retry_delay(unsigned long); +void lpfc_els_unsol_event(struct lpfc_hba *, struct lpfc_sli_ring *, + struct lpfc_iocbq *); +int lpfc_els_handle_rscn(struct lpfc_hba *); +int lpfc_els_flush_rscn(struct lpfc_hba *); +int lpfc_rscn_payload_check(struct lpfc_hba *, uint32_t); +void lpfc_els_flush_cmd(struct lpfc_hba *); +int lpfc_els_disc_adisc(struct lpfc_hba *); +int lpfc_els_disc_plogi(struct lpfc_hba *); +void lpfc_els_timeout_handler(unsigned long ptr); + +void lpfc_ct_unsol_event(struct lpfc_hba *, struct lpfc_sli_ring *, + struct lpfc_iocbq *); +int lpfc_ns_cmd(struct lpfc_hba *, struct lpfc_nodelist *, int); +int lpfc_fdmi_cmd(struct lpfc_hba *, struct lpfc_nodelist *, int); +void lpfc_fdmi_tmo(unsigned long); + +int lpfc_config_port_prep(struct lpfc_hba *); +int lpfc_config_port_post(struct lpfc_hba *); +int lpfc_hba_down_prep(struct lpfc_hba *); +void lpfc_handle_eratt(struct lpfc_hba *, uint32_t); +void lpfc_handle_latt(struct lpfc_hba *); +void lpfc_hba_init(struct lpfc_hba *, uint32_t *); +int lpfc_post_buffer(struct lpfc_hba *, struct lpfc_sli_ring *, int, int); +void lpfc_cleanup(struct lpfc_hba *, uint32_t); +int lpfc_scsi_free(struct lpfc_hba *); +void lpfc_decode_firmware_rev(struct lpfc_hba *, char *, int); +uint8_t *lpfc_get_lpfchba_info(struct lpfc_hba *, uint8_t *); +int lpfc_fcp_abort(struct lpfc_hba *, int, int, int); +int lpfc_put_event(struct lpfc_hba *, uint32_t, uint32_t, void *, + uint32_t, uint32_t); +void lpfc_get_hba_model_desc(struct lpfc_hba *, uint8_t *, uint8_t *); +int lpfc_online(struct lpfc_hba *); +int lpfc_offline(struct lpfc_hba *); + + + +int lpfc_sli_queue_setup(struct lpfc_hba *); +void lpfc_slim_access(struct lpfc_hba *); + +void lpfc_handle_eratt(struct lpfc_hba *, uint32_t); +void lpfc_handle_latt(struct lpfc_hba *); +irqreturn_t lpfc_intr_handler(int, void *, struct pt_regs *); + +void lpfc_read_rev(struct lpfc_hba *, LPFC_MBOXQ_t *); +void lpfc_config_ring(struct lpfc_hba *, int, LPFC_MBOXQ_t *); +void lpfc_config_port(struct lpfc_hba *, LPFC_MBOXQ_t *); +void lpfc_mbox_put(struct lpfc_hba *, LPFC_MBOXQ_t *); +LPFC_MBOXQ_t *lpfc_mbox_get(struct lpfc_hba *); + +int lpfc_mem_alloc(struct lpfc_hba *); +void lpfc_mem_free(struct lpfc_hba *); + +struct lpfc_iocbq * +lpfc_prep_els_iocb(struct lpfc_hba * phba, + uint8_t expectRsp, + uint16_t cmdSize, + uint8_t retry, struct lpfc_nodelist * ndlp, uint32_t elscmd); + +int lpfc_sli_hba_setup(struct lpfc_hba *); +int lpfc_sli_hba_down(struct lpfc_hba *); +int lpfc_sli_intr(struct lpfc_hba *); +int lpfc_sli_issue_mbox(struct lpfc_hba *, LPFC_MBOXQ_t *, uint32_t); +int lpfc_sli_issue_iocb(struct lpfc_hba *, struct lpfc_sli_ring *, + struct lpfc_iocbq *, uint32_t); +void lpfc_sli_pcimem_bcopy(uint32_t *, uint32_t *, uint32_t); +int lpfc_sli_ringpostbuf_put(struct lpfc_hba *, struct lpfc_sli_ring *, + struct lpfc_dmabuf *); +struct lpfc_dmabuf *lpfc_sli_ringpostbuf_get(struct lpfc_hba *, + struct lpfc_sli_ring *, + dma_addr_t); +uint32_t lpfc_sli_next_iotag(struct lpfc_hba *, struct lpfc_sli_ring *); +int lpfc_sli_issue_abort_iotag32(struct lpfc_hba *, struct lpfc_sli_ring *, + struct lpfc_iocbq *); +int lpfc_sli_abort_iocb_ctx(struct lpfc_hba *, struct lpfc_sli_ring *, + uint32_t); +int lpfc_sli_sum_iocb_host(struct lpfc_hba *, struct lpfc_sli_ring *); +int lpfc_sli_abort_iocb_host(struct lpfc_hba *, struct lpfc_sli_ring *, int); +int lpfc_sli_sum_iocb_lun(struct lpfc_hba *, struct lpfc_sli_ring *, uint16_t, + uint64_t); +int lpfc_sli_abort_iocb_lun(struct lpfc_hba *, struct lpfc_sli_ring *, uint16_t, + uint64_t, int); +int lpfc_sli_abort_iocb_tgt(struct lpfc_hba *, struct lpfc_sli_ring *, + uint16_t, int); +void lpfc_mbox_timeout(unsigned long); + +void lpfc_map_fcp_cmnd_to_bpl(struct lpfc_hba *, struct lpfc_scsi_buf *); +void lpfc_free_scsi_cmd(struct lpfc_scsi_buf *); +uint32_t lpfc_os_timeout_transform(struct lpfc_hba *, uint32_t); + +struct lpfc_nodelist * +lpfc_findnode_wwpn(struct lpfc_hba * phba, uint32_t order, + struct lpfc_name * wwpn); +struct lpfc_nodelist * +lpfc_findnode_wwnn(struct lpfc_hba * phba, uint32_t order, + struct lpfc_name * wwnn); +struct lpfc_nodelist *lpfc_findnode_did(struct lpfc_hba * phba, uint32_t order, + uint32_t did); + +void lpfc_get_hba_sym_node_name(struct lpfc_hba * phba, uint8_t * symbp); + +int lpfc_sli_issue_mbox_wait(struct lpfc_hba * phba, LPFC_MBOXQ_t * pmboxq, + uint32_t timeout); + +int +lpfc_sli_issue_iocb_wait(struct lpfc_hba * phba, + struct lpfc_sli_ring * pring, + struct lpfc_iocbq * piocb, + struct lpfc_iocbq * prspiocbq, uint32_t timeout); +int lpfc_sli_issue_iocb_wait_high_priority(struct lpfc_hba * phba, + struct lpfc_sli_ring * pring, + struct lpfc_iocbq * piocb, + uint32_t flag, + struct lpfc_iocbq * prspiocbq, + uint32_t timeout); +void lpfc_sli_wake_iocb_high_priority(struct lpfc_hba * phba, + struct lpfc_iocbq * queue1, + struct lpfc_iocbq * queue2); +void *lpfc_mbuf_alloc(struct lpfc_hba *, int, dma_addr_t *); +void lpfc_mbuf_free(struct lpfc_hba *, void *, dma_addr_t); + +int lpfc_stop_timer(struct lpfc_hba *); + + +/* Function prototypes. */ +int lpfc_queuecommand(struct scsi_cmnd *, void (*done) (struct scsi_cmnd *)); +int lpfc_abort_handler(struct scsi_cmnd *); +int lpfc_reset_bus_handler(struct scsi_cmnd *); +int lpfc_reset_lun_handler(struct scsi_cmnd *); + +#if defined(FC_TRANS_VER1) || defined(FC_TRANS_265_BLKPATCH) +void lpfc_target_unblock(struct lpfc_hba *, struct lpfc_target *); +void lpfc_target_block(struct lpfc_hba *, struct lpfc_target *); +int lpfc_target_remove(struct lpfc_hba *, struct lpfc_target *); +int lpfc_target_add(struct lpfc_hba *, struct lpfc_target *); +#endif + +#define ScsiResult(host_code, scsi_code) (((host_code) << 16) | scsi_code) +#define HBA_EVENT_RSCN 5 +#define HBA_EVENT_LINK_UP 2 +#define HBA_EVENT_LINK_DOWN 3 +#endif /* _H_LPFC_CRTN */ diff --git a/drivers/scsi/lpfc/lpfc_ct.c b/drivers/scsi/lpfc/lpfc_ct.c new file mode 100644 index 000000000..83342c469 --- /dev/null +++ b/drivers/scsi/lpfc/lpfc_ct.c @@ -0,0 +1,1288 @@ +/******************************************************************* + * This file is part of the Emulex Linux Device Driver for * + * Enterprise Fibre Channel Host Bus Adapters. * + * Refer to the README file included with this package for * + * driver version and adapter support. * + * Copyright (C) 2004 Emulex Corporation. * + * www.emulex.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, a copy of which * + * can be found in the file COPYING included with this package. * + *******************************************************************/ + +/* + * $Id: lpfc_ct.c 1.143 2004/11/17 14:50:38EST sf_support Exp $ + * + * Fibre Channel SCSI LAN Device Driver CT support + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include "lpfc_sli.h" +#include "lpfc_disc.h" +#include "lpfc_scsi.h" +#include "lpfc.h" +#include "lpfc_crtn.h" +#include "lpfc_hw.h" +#include "lpfc_logmsg.h" +#include "lpfc_mem.h" +#include "lpfc_version.h" + + +#define HBA_PORTSPEED_UNKNOWN 0 /* Unknown - transceiver + * incapable of reporting */ +#define HBA_PORTSPEED_1GBIT 1 /* 1 GBit/sec */ +#define HBA_PORTSPEED_2GBIT 2 /* 2 GBit/sec */ +#define HBA_PORTSPEED_4GBIT 8 /* 4 GBit/sec */ +#define HBA_PORTSPEED_8GBIT 16 /* 8 GBit/sec */ +#define HBA_PORTSPEED_10GBIT 4 /* 10 GBit/sec */ +#define HBA_PORTSPEED_NOT_NEGOTIATED 5 /* Speed not established */ + +#define FOURBYTES 4 + + +static char *lpfc_release_version = LPFC_DRIVER_VERSION; + +/* + * lpfc_ct_unsol_event + */ +void +lpfc_ct_unsol_event(struct lpfc_hba * phba, + struct lpfc_sli_ring * pring, struct lpfc_iocbq * piocbq) +{ + + struct lpfc_iocbq *next_piocbq; + struct lpfc_dmabuf *pmbuf = NULL; + struct lpfc_dmabuf *matp, *next_matp; + uint32_t ctx = 0, count = 0; + IOCB_t *icmd = &piocbq->iocb; + int i, status, go_exit = 0; + struct list_head head; + + if (icmd->ulpStatus) + return; + + list_add_tail(&head, &piocbq->list); + list_for_each_entry_safe(piocbq, next_piocbq, &head, list) { + icmd = &piocbq->iocb; + if (ctx == 0) + ctx = (uint32_t) (icmd->ulpContext); + if (icmd->ulpStatus) { + if ((icmd->ulpStatus == IOSTAT_LOCAL_REJECT) && + ((icmd->un.ulpWord[4] & 0xff) + == IOERR_RCV_BUFFER_WAITING)) { + phba->fc_stat.NoRcvBuf++; + lpfc_post_buffer(phba, pring, 0, 1); + } + go_exit = 1; + goto ct_unsol_event_exit_piocbq; + } + + if (icmd->ulpBdeCount == 0) + continue; + + for (i = 0; i < icmd->ulpBdeCount; i++) { + matp = lpfc_sli_ringpostbuf_get(phba, pring, + getPaddr(icmd->un. + cont64[i]. + addrHigh, + icmd->un. + cont64[i]. + addrLow)); + if (!matp) { + /* Insert lpfc log message here */ + go_exit = 1; + goto ct_unsol_event_exit_piocbq; + } + + /* Typically for Unsolicited CT requests */ + if (!pmbuf) { + pmbuf = matp; + INIT_LIST_HEAD(&pmbuf->list); + } else + list_add_tail(&matp->list, &pmbuf->list); + + count += icmd->un.cont64[i].tus.f.bdeSize; + } + + lpfc_post_buffer(phba, pring, i, 1); + icmd->ulpBdeCount = 0; + } +ct_unsol_event_exit_piocbq: + list_del(&head); + /* + * if not early-exiting and there is pmbuf, + * then do FC_REG_CT_EVENT for libdfc + */ + if (!go_exit && pmbuf) { + status = lpfc_put_event(phba, FC_REG_CT_EVENT, ctx, + (void *)pmbuf, count, 0); + if (status) + /* Need to free IOCB buffer ? */ + return; + } + if (pmbuf) { + list_for_each_entry_safe(matp, next_matp, &pmbuf->list, list) { + lpfc_mbuf_free(phba, matp->virt, matp->phys); + list_del(&matp->list); + kfree(matp); + } + lpfc_mbuf_free(phba, pmbuf->virt, pmbuf->phys); + kfree(pmbuf); + } + return; +} + +static void +lpfc_free_ct_rsp(struct lpfc_hba * phba, struct lpfc_dmabuf * mlist) +{ + struct lpfc_dmabuf *mlast, *next_mlast; + + list_for_each_entry_safe(mlast, next_mlast, &mlist->list, list) { + lpfc_mbuf_free(phba, mlast->virt, mlast->phys); + list_del(&mlast->list); + kfree(mlast); + } + lpfc_mbuf_free(phba, mlist->virt, mlist->phys); + kfree(mlist); + return; +} + +static struct lpfc_dmabuf * +lpfc_alloc_ct_rsp(struct lpfc_hba * phba, int cmdcode, struct ulp_bde64 * bpl, + uint32_t size, int *entries) +{ + struct lpfc_dmabuf *mlist = NULL; + struct lpfc_dmabuf *mp; + int cnt, i = 0; + + /* We get chucks of FCELSSIZE */ + cnt = size > FCELSSIZE ? FCELSSIZE: size; + + while (size) { + /* Allocate buffer for rsp payload */ + mp = kmalloc(sizeof(struct lpfc_dmabuf), GFP_ATOMIC); + if (!mp) { + if (mlist) + lpfc_free_ct_rsp(phba, mlist); + return NULL; + } + + INIT_LIST_HEAD(&mp->list); + + if (cmdcode == be16_to_cpu(SLI_CTNS_GID_FT)) + mp->virt = lpfc_mbuf_alloc(phba, MEM_PRI, &(mp->phys)); + else + mp->virt = lpfc_mbuf_alloc(phba, 0, &(mp->phys)); + + if (!mp->virt) { + kfree(mp); + lpfc_free_ct_rsp(phba, mlist); + return NULL; + } + + /* Queue it to a linked list */ + if (!mlist) + mlist = mp; + else + list_add_tail(&mp->list, &mlist->list); + + bpl->tus.f.bdeFlags = BUFF_USE_RCV; + /* build buffer ptr list for IOCB */ + bpl->addrLow = le32_to_cpu( putPaddrLow(mp->phys) ); + bpl->addrHigh = le32_to_cpu( putPaddrHigh(mp->phys) ); + bpl->tus.f.bdeSize = (uint16_t) cnt; + bpl->tus.w = le32_to_cpu(bpl->tus.w); + bpl++; + + i++; + size -= cnt; + } + + *entries = i; + return mlist; +} + +static int +lpfc_gen_req(struct lpfc_hba *phba, struct lpfc_dmabuf *bmp, + struct lpfc_dmabuf *inp, struct lpfc_dmabuf *outp, + void (*cmpl) (struct lpfc_hba *, struct lpfc_iocbq *, + struct lpfc_iocbq *), + struct lpfc_nodelist *ndlp, uint32_t usr_flg, uint32_t num_entry, + uint32_t tmo) +{ + + struct lpfc_sli *psli = &phba->sli; + struct lpfc_sli_ring *pring = &psli->ring[LPFC_ELS_RING]; + IOCB_t *icmd; + struct lpfc_iocbq *geniocb; + + /* Allocate buffer for command iocb */ + geniocb = mempool_alloc(phba->iocb_mem_pool, GFP_ATOMIC); + if (!geniocb) { + return 1; + } + memset(geniocb, 0, sizeof (struct lpfc_iocbq)); + icmd = &geniocb->iocb; + + icmd->un.genreq64.bdl.ulpIoTag32 = 0; + icmd->un.genreq64.bdl.addrHigh = putPaddrHigh(bmp->phys); + icmd->un.genreq64.bdl.addrLow = putPaddrLow(bmp->phys); + icmd->un.genreq64.bdl.bdeFlags = BUFF_TYPE_BDL; + icmd->un.genreq64.bdl.bdeSize = (num_entry * sizeof (struct ulp_bde64)); + + if (usr_flg) + geniocb->context3 = NULL; + else + geniocb->context3 = (uint8_t *) bmp; + + /* Save for completion so we can release these resources */ + geniocb->context1 = (uint8_t *) inp; + geniocb->context2 = (uint8_t *) outp; + + /* Fill in payload, bp points to frame payload */ + icmd->ulpCommand = CMD_GEN_REQUEST64_CR; + + pci_dma_sync_single_for_device(phba->pcidev, bmp->phys, + LPFC_BPL_SIZE, PCI_DMA_TODEVICE); + + icmd->ulpIoTag = lpfc_sli_next_iotag(phba, pring); + + /* Fill in rest of iocb */ + icmd->un.genreq64.w5.hcsw.Fctl = (SI | LA); + icmd->un.genreq64.w5.hcsw.Dfctl = 0; + icmd->un.genreq64.w5.hcsw.Rctl = FC_UNSOL_CTL; + icmd->un.genreq64.w5.hcsw.Type = FC_COMMON_TRANSPORT_ULP; + + if (!tmo) + tmo = (2 * phba->fc_ratov) + 1; + icmd->ulpTimeout = tmo; + icmd->ulpBdeCount = 1; + icmd->ulpLe = 1; + icmd->ulpClass = CLASS3; + icmd->ulpContext = ndlp->nlp_rpi; + + /* Issue GEN REQ IOCB for NPORT */ + lpfc_printf_log(phba, KERN_INFO, LOG_ELS, + "%d:0119 Issue GEN REQ IOCB for NPORT x%x " + "Data: x%x x%x\n", phba->brd_no, icmd->un.ulpWord[5], + icmd->ulpIoTag, phba->hba_state); + geniocb->iocb_cmpl = cmpl; + geniocb->drvrTimeout = icmd->ulpTimeout + LPFC_DRVR_TIMEOUT; + if (lpfc_sli_issue_iocb(phba, pring, geniocb, 0) == IOCB_ERROR) { + mempool_free( geniocb, phba->iocb_mem_pool); + return 1; + } + + return 0; +} + +static int +lpfc_ct_cmd(struct lpfc_hba *phba, struct lpfc_dmabuf *inmp, + struct lpfc_dmabuf *bmp, struct lpfc_nodelist *ndlp, + void (*cmpl) (struct lpfc_hba *, struct lpfc_iocbq *, + struct lpfc_iocbq *), + uint32_t rsp_size) +{ + struct ulp_bde64 *bpl = (struct ulp_bde64 *) bmp->virt; + struct lpfc_dmabuf *outmp; + int cnt = 0, status; + int cmdcode = ((struct lpfc_sli_ct_request *) inmp->virt)-> + CommandResponse.bits.CmdRsp; + + bpl++; /* Skip past ct request */ + + /* Put buffer(s) for ct rsp in bpl */ + outmp = lpfc_alloc_ct_rsp(phba, cmdcode, bpl, rsp_size, &cnt); + if (!outmp) + return -ENOMEM; + + status = lpfc_gen_req(phba, bmp, inmp, outmp, cmpl, ndlp, 0, + cnt+1, 0); + if (status) { + lpfc_free_ct_rsp(phba, outmp); + return -ENOMEM; + } + return 0; +} + +static int +lpfc_ns_rsp(struct lpfc_hba * phba, struct lpfc_dmabuf * mp, uint32_t Size) +{ + struct lpfc_sli_ct_request *Response = + (struct lpfc_sli_ct_request *) mp->virt; + struct lpfc_nodelist *ndlp = NULL; + struct lpfc_dmabuf *mlast, *next_mp; + uint32_t *ctptr = (uint32_t *) & Response->un.gid.PortType; + uint32_t Did; + uint32_t CTentry; + int Cnt; + struct list_head head; + + lpfc_set_disctmo(phba); + + Cnt = Size > FCELSSIZE ? FCELSSIZE : Size; + + list_add_tail(&head, &mp->list); + list_for_each_entry_safe(mp, next_mp, &head, list) { + mlast = mp; + pci_dma_sync_single_for_cpu(phba->pcidev, mp->phys, + LPFC_BPL_SIZE, PCI_DMA_FROMDEVICE); + + Size -= Cnt; + + if (!ctptr) + ctptr = (uint32_t *) mlast->virt; + else + Cnt -= 16; /* subtract length of CT header */ + + /* Loop through entire NameServer list of DIDs */ + while (Cnt) { + + /* Get next DID from NameServer List */ + CTentry = *ctptr++; + Did = ((be32_to_cpu(CTentry)) & Mask_DID); + + ndlp = NULL; + if (Did != phba->fc_myDID) { + /* Check for rscn processing or not */ + ndlp = lpfc_setup_disc_node(phba, Did); + } + /* Mark all node table entries that are in the + Nameserver */ + if (ndlp) { + /* NameServer Rsp */ + lpfc_printf_log(phba, KERN_INFO, LOG_DISCOVERY, + "%d:0238 Process x%x NameServer" + " Rsp Data: x%x x%x x%x\n", + phba->brd_no, + Did, ndlp->nlp_flag, + phba->fc_flag, + phba->fc_rscn_id_cnt); + } else { + /* NameServer Rsp */ + lpfc_printf_log(phba, + KERN_INFO, + LOG_DISCOVERY, + "%d:0239 Skip x%x NameServer " + "Rsp Data: x%x x%x x%x\n", + phba->brd_no, + Did, Size, phba->fc_flag, + phba->fc_rscn_id_cnt); + } + + if (CTentry & (be32_to_cpu(SLI_CT_LAST_ENTRY))) + goto nsout1; + Cnt -= sizeof (uint32_t); + } + ctptr = NULL; + + } + +nsout1: + list_del(&head); + + /* Here we are finished in the case RSCN */ + if (phba->hba_state == LPFC_HBA_READY) { + lpfc_els_flush_rscn(phba); + phba->fc_flag |= FC_RSCN_MODE; /* we are still in RSCN mode */ + } + return 0; +} + + + + +static void +lpfc_cmpl_ct_cmd_gid_ft(struct lpfc_hba * phba, struct lpfc_iocbq * cmdiocb, + struct lpfc_iocbq * rspiocb) +{ + IOCB_t *irsp; + struct lpfc_sli *psli; + struct lpfc_dmabuf *bmp; + struct lpfc_dmabuf *inp; + struct lpfc_dmabuf *outp; + struct lpfc_nodelist *ndlp; + struct lpfc_sli_ct_request *CTrsp; + + psli = &phba->sli; + /* we pass cmdiocb to state machine which needs rspiocb as well */ + cmdiocb->context_un.rsp_iocb = rspiocb; + + inp = (struct lpfc_dmabuf *) cmdiocb->context1; + outp = (struct lpfc_dmabuf *) cmdiocb->context2; + bmp = (struct lpfc_dmabuf *) cmdiocb->context3; + + irsp = &rspiocb->iocb; + if (irsp->ulpStatus) { + if((irsp->ulpStatus == IOSTAT_LOCAL_REJECT) && + ((irsp->un.ulpWord[4] == IOERR_SLI_DOWN) || + (irsp->un.ulpWord[4] == IOERR_SLI_ABORTED))) { + goto out; + } + + /* Check for retry */ + if (phba->fc_ns_retry < LPFC_MAX_NS_RETRY) { + phba->fc_ns_retry++; + /* CT command is being retried */ + ndlp = + lpfc_findnode_did(phba, NLP_SEARCH_UNMAPPED, + NameServer_DID); + if (ndlp) { + if (lpfc_ns_cmd(phba, ndlp, SLI_CTNS_GID_FT) == + 0) { + goto out; + } + } + } + } else { + /* Good status, continue checking */ + CTrsp = (struct lpfc_sli_ct_request *) outp->virt; + if (CTrsp->CommandResponse.bits.CmdRsp == + be16_to_cpu(SLI_CT_RESPONSE_FS_ACC)) { + lpfc_ns_rsp(phba, outp, + (uint32_t) (irsp->un.genreq64.bdl.bdeSize)); + } else if (CTrsp->CommandResponse.bits.CmdRsp == + be16_to_cpu(SLI_CT_RESPONSE_FS_RJT)) { + /* NameServer Rsp Error */ + lpfc_printf_log(phba, KERN_INFO, LOG_DISCOVERY, + "%d:0240 NameServer Rsp Error " + "Data: x%x x%x x%x x%x\n", + phba->brd_no, + CTrsp->CommandResponse.bits.CmdRsp, + (uint32_t) CTrsp->ReasonCode, + (uint32_t) CTrsp->Explanation, + phba->fc_flag); + } else { + /* NameServer Rsp Error */ + lpfc_printf_log(phba, + KERN_INFO, + LOG_DISCOVERY, + "%d:0241 NameServer Rsp Error " + "Data: x%x x%x x%x x%x\n", + phba->brd_no, + CTrsp->CommandResponse.bits.CmdRsp, + (uint32_t) CTrsp->ReasonCode, + (uint32_t) CTrsp->Explanation, + phba->fc_flag); + } + } + /* Link up / RSCN discovery */ + lpfc_disc_start(phba); +out: + lpfc_free_ct_rsp(phba, outp); + lpfc_mbuf_free(phba, inp->virt, inp->phys); + lpfc_mbuf_free(phba, bmp->virt, bmp->phys); + kfree(inp); + kfree(bmp); + mempool_free( cmdiocb, phba->iocb_mem_pool); + return; +} + +static void +lpfc_cmpl_ct_cmd_rft_id(struct lpfc_hba * phba, struct lpfc_iocbq * cmdiocb, + struct lpfc_iocbq * rspiocb) +{ + struct lpfc_sli *psli; + struct lpfc_dmabuf *bmp; + struct lpfc_dmabuf *inp; + struct lpfc_dmabuf *outp; + IOCB_t *irsp; + struct lpfc_sli_ct_request *CTrsp; + + psli = &phba->sli; + /* we pass cmdiocb to state machine which needs rspiocb as well */ + cmdiocb->context_un.rsp_iocb = rspiocb; + + inp = (struct lpfc_dmabuf *) cmdiocb->context1; + outp = (struct lpfc_dmabuf *) cmdiocb->context2; + bmp = (struct lpfc_dmabuf *) cmdiocb->context3; + irsp = &rspiocb->iocb; + + CTrsp = (struct lpfc_sli_ct_request *) outp->virt; + + /* RFT request completes status CmdRsp */ + lpfc_printf_log(phba, KERN_INFO, LOG_DISCOVERY, + "%d:0209 RFT request completes ulpStatus x%x " + "CmdRsp x%x\n", phba->brd_no, irsp->ulpStatus, + CTrsp->CommandResponse.bits.CmdRsp); + + lpfc_free_ct_rsp(phba, outp); + lpfc_mbuf_free(phba, inp->virt, inp->phys); + lpfc_mbuf_free(phba, bmp->virt, bmp->phys); + kfree(inp); + kfree(bmp); + mempool_free( cmdiocb, phba->iocb_mem_pool); + return; +} + +static void +lpfc_cmpl_ct_cmd_rnn_id(struct lpfc_hba * phba, struct lpfc_iocbq * cmdiocb, + struct lpfc_iocbq * rspiocb) +{ + lpfc_cmpl_ct_cmd_rft_id(phba, cmdiocb, rspiocb); + return; +} + +static void +lpfc_cmpl_ct_cmd_rsnn_nn(struct lpfc_hba * phba, struct lpfc_iocbq * cmdiocb, + struct lpfc_iocbq * rspiocb) +{ + lpfc_cmpl_ct_cmd_rft_id(phba, cmdiocb, rspiocb); + return; +} + +void +lpfc_get_hba_sym_node_name(struct lpfc_hba * phba, uint8_t * symbp) +{ + uint8_t buf[16]; + char fwrev[16]; + + lpfc_decode_firmware_rev(phba, fwrev, 0); + lpfc_get_hba_model_desc(phba, buf, NULL); + sprintf(symbp, "Emulex %s FV%s DV%s", buf, fwrev, lpfc_release_version); +} + +/* + * lpfc_ns_cmd + * Description: + * Issue Cmd to NameServer + * SLI_CTNS_GID_FT + * LI_CTNS_RFT_ID + */ +int +lpfc_ns_cmd(struct lpfc_hba * phba, struct lpfc_nodelist * ndlp, int cmdcode) +{ + struct lpfc_dmabuf *mp, *bmp; + struct lpfc_sli_ct_request *CtReq; + struct ulp_bde64 *bpl; + void (*cmpl) (struct lpfc_hba *, struct lpfc_iocbq *, + struct lpfc_iocbq *) = NULL; + uint32_t rsp_size = 1024; + + /* fill in BDEs for command */ + /* Allocate buffer for command payload */ + mp = kmalloc(sizeof (struct lpfc_dmabuf), GFP_ATOMIC); + if (!mp) + goto ns_cmd_exit; + + INIT_LIST_HEAD(&mp->list); + mp->virt = lpfc_mbuf_alloc(phba, MEM_PRI, &(mp->phys)); + if (!mp->virt) + goto ns_cmd_free_mp; + + /* Allocate buffer for Buffer ptr list */ + bmp = kmalloc(sizeof (struct lpfc_dmabuf), GFP_ATOMIC); + if (!bmp) + goto ns_cmd_free_mpvirt; + + INIT_LIST_HEAD(&bmp->list); + bmp->virt = lpfc_mbuf_alloc(phba, MEM_PRI, &(bmp->phys)); + if (!bmp->virt) + goto ns_cmd_free_bmp; + + /* NameServer Req */ + lpfc_printf_log(phba, + KERN_INFO, + LOG_DISCOVERY, + "%d:0236 NameServer Req Data: x%x x%x x%x\n", + phba->brd_no, cmdcode, phba->fc_flag, + phba->fc_rscn_id_cnt); + + bpl = (struct ulp_bde64 *) bmp->virt; + memset(bpl, 0, sizeof(struct ulp_bde64)); + bpl->addrHigh = le32_to_cpu( putPaddrHigh(mp->phys) ); + bpl->addrLow = le32_to_cpu( putPaddrLow(mp->phys) ); + bpl->tus.f.bdeFlags = 0; + if (cmdcode == SLI_CTNS_GID_FT) + bpl->tus.f.bdeSize = GID_REQUEST_SZ; + else if (cmdcode == SLI_CTNS_RFT_ID) + bpl->tus.f.bdeSize = RFT_REQUEST_SZ; + else if (cmdcode == SLI_CTNS_RNN_ID) + bpl->tus.f.bdeSize = RNN_REQUEST_SZ; + else if (cmdcode == SLI_CTNS_RSNN_NN) + bpl->tus.f.bdeSize = RSNN_REQUEST_SZ; + else + bpl->tus.f.bdeSize = 0; + bpl->tus.w = le32_to_cpu(bpl->tus.w); + + CtReq = (struct lpfc_sli_ct_request *) mp->virt; + memset(CtReq, 0, sizeof (struct lpfc_sli_ct_request)); + CtReq->RevisionId.bits.Revision = SLI_CT_REVISION; + CtReq->RevisionId.bits.InId = 0; + CtReq->FsType = SLI_CT_DIRECTORY_SERVICE; + CtReq->FsSubType = SLI_CT_DIRECTORY_NAME_SERVER; + CtReq->CommandResponse.bits.Size = 0; + switch (cmdcode) { + case SLI_CTNS_GID_FT: + CtReq->CommandResponse.bits.CmdRsp = + be16_to_cpu(SLI_CTNS_GID_FT); + CtReq->un.gid.Fc4Type = SLI_CTPT_FCP; + if (phba->hba_state < LPFC_HBA_READY) + phba->hba_state = LPFC_NS_QRY; + lpfc_set_disctmo(phba); + cmpl = lpfc_cmpl_ct_cmd_gid_ft; + rsp_size = FC_MAX_NS_RSP; + break; + + case SLI_CTNS_RFT_ID: + CtReq->CommandResponse.bits.CmdRsp = + be16_to_cpu(SLI_CTNS_RFT_ID); + CtReq->un.rft.PortId = be32_to_cpu(phba->fc_myDID); + CtReq->un.rft.fcpReg = 1; + cmpl = lpfc_cmpl_ct_cmd_rft_id; + break; + + case SLI_CTNS_RNN_ID: + CtReq->CommandResponse.bits.CmdRsp = + be16_to_cpu(SLI_CTNS_RNN_ID); + CtReq->un.rnn.PortId = be32_to_cpu(phba->fc_myDID); + memcpy(CtReq->un.rnn.wwnn, &phba->fc_nodename, + sizeof (struct lpfc_name)); + cmpl = lpfc_cmpl_ct_cmd_rnn_id; + break; + + case SLI_CTNS_RSNN_NN: + CtReq->CommandResponse.bits.CmdRsp = + be16_to_cpu(SLI_CTNS_RSNN_NN); + memcpy(CtReq->un.rsnn.wwnn, &phba->fc_nodename, + sizeof (struct lpfc_name)); + lpfc_get_hba_sym_node_name(phba, CtReq->un.rsnn.symbname); + CtReq->un.rsnn.len = strlen(CtReq->un.rsnn.symbname); + cmpl = lpfc_cmpl_ct_cmd_rsnn_nn; + break; + } + + if (!lpfc_ct_cmd(phba, mp, bmp, ndlp, cmpl, rsp_size)) + /* On success, The cmpl function will free the buffers */ + return 0; + + lpfc_mbuf_free(phba, bmp->virt, bmp->phys); +ns_cmd_free_bmp: + kfree(bmp); +ns_cmd_free_mpvirt: + lpfc_mbuf_free(phba, mp->virt, mp->phys); +ns_cmd_free_mp: + kfree(mp); +ns_cmd_exit: + return 1; +} + +static void +lpfc_cmpl_ct_cmd_fdmi(struct lpfc_hba * phba, + struct lpfc_iocbq * cmdiocb, struct lpfc_iocbq * rspiocb) +{ + struct lpfc_dmabuf *bmp = cmdiocb->context3; + struct lpfc_dmabuf *inp = cmdiocb->context1; + struct lpfc_dmabuf *outp = cmdiocb->context2; + struct lpfc_sli_ct_request *CTrsp = outp->virt; + struct lpfc_sli_ct_request *CTcmd = inp->virt; + struct lpfc_nodelist *ndlp; + uint16_t fdmi_cmd = CTcmd->CommandResponse.bits.CmdRsp; + uint16_t fdmi_rsp = CTrsp->CommandResponse.bits.CmdRsp; + + ndlp = lpfc_findnode_did(phba, NLP_SEARCH_ALL, FDMI_DID); + if (fdmi_rsp == be16_to_cpu(SLI_CT_RESPONSE_FS_RJT)) { + /* FDMI rsp failed */ + lpfc_printf_log(phba, + KERN_INFO, + LOG_DISCOVERY, + "%d:0220 FDMI rsp failed Data: x%x\n", + phba->brd_no, + be16_to_cpu(fdmi_cmd)); + } + + switch (be16_to_cpu(fdmi_cmd)) { + case SLI_MGMT_RHBA: + lpfc_fdmi_cmd(phba, ndlp, SLI_MGMT_RPA); + break; + + case SLI_MGMT_RPA: + break; + + case SLI_MGMT_DHBA: + lpfc_fdmi_cmd(phba, ndlp, SLI_MGMT_DPRT); + break; + + case SLI_MGMT_DPRT: + lpfc_fdmi_cmd(phba, ndlp, SLI_MGMT_RHBA); + break; + } + + lpfc_free_ct_rsp(phba, outp); + lpfc_mbuf_free(phba, inp->virt, inp->phys); + lpfc_mbuf_free(phba, bmp->virt, bmp->phys); + kfree(inp); + kfree(bmp); + mempool_free(cmdiocb, phba->iocb_mem_pool); + return; +} +int +lpfc_fdmi_cmd(struct lpfc_hba * phba, struct lpfc_nodelist * ndlp, int cmdcode) +{ + struct lpfc_dmabuf *mp, *bmp; + struct lpfc_sli_ct_request *CtReq; + struct ulp_bde64 *bpl; + uint32_t size; + REG_HBA *rh; + PORT_ENTRY *pe; + REG_PORT_ATTRIBUTE *pab; + ATTRIBUTE_BLOCK *ab; + ATTRIBUTE_ENTRY *ae; + void (*cmpl) (struct lpfc_hba *, struct lpfc_iocbq *, + struct lpfc_iocbq *); + + + /* fill in BDEs for command */ + /* Allocate buffer for command payload */ + mp = kmalloc(sizeof (struct lpfc_dmabuf), GFP_ATOMIC); + if (!mp) + goto fdmi_cmd_exit; + + mp->virt = lpfc_mbuf_alloc(phba, 0, &(mp->phys)); + if (!mp->virt) + goto fdmi_cmd_free_mp; + + /* Allocate buffer for Buffer ptr list */ + bmp = kmalloc(sizeof (struct lpfc_dmabuf), GFP_ATOMIC); + if (!bmp) + goto fdmi_cmd_free_mpvirt; + + bmp->virt = lpfc_mbuf_alloc(phba, 0, &(bmp->phys)); + if (!bmp->virt) + goto fdmi_cmd_free_bmp; + + INIT_LIST_HEAD(&mp->list); + INIT_LIST_HEAD(&bmp->list); + + /* FDMI request */ + lpfc_printf_log(phba, + KERN_INFO, + LOG_DISCOVERY, + "%d:0218 FDMI Request Data: x%x x%x x%x\n", + phba->brd_no, + phba->fc_flag, phba->hba_state, cmdcode); + + CtReq = (struct lpfc_sli_ct_request *) mp->virt; + + memset(CtReq, 0, sizeof(struct lpfc_sli_ct_request)); + CtReq->RevisionId.bits.Revision = SLI_CT_REVISION; + CtReq->RevisionId.bits.InId = 0; + + CtReq->FsType = SLI_CT_MANAGEMENT_SERVICE; + CtReq->FsSubType = SLI_CT_FDMI_Subtypes; + size = 0; + + switch (cmdcode) { + case SLI_MGMT_RHBA: + { + lpfc_vpd_t *vp = &phba->vpd; + uint32_t i, j, incr; + int len; + + CtReq->CommandResponse.bits.CmdRsp = + be16_to_cpu(SLI_MGMT_RHBA); + CtReq->CommandResponse.bits.Size = 0; + rh = (REG_HBA *) & CtReq->un.PortID; + memcpy(&rh->hi.PortName, &phba->fc_sparam.portName, + sizeof (struct lpfc_name)); + /* One entry (port) per adapter */ + rh->rpl.EntryCnt = be32_to_cpu(1); + memcpy(&rh->rpl.pe, &phba->fc_sparam.portName, + sizeof (struct lpfc_name)); + + /* point to the HBA attribute block */ + size = 2 * sizeof (struct lpfc_name) + FOURBYTES; + ab = (ATTRIBUTE_BLOCK *) ((uint8_t *) rh + size); + ab->EntryCnt = 0; + + /* Point to the beginning of the first HBA attribute + entry */ + /* #1 HBA attribute entry */ + size += FOURBYTES; + ae = (ATTRIBUTE_ENTRY *) ((uint8_t *) rh + size); + ae->ad.bits.AttrType = be16_to_cpu(NODE_NAME); + ae->ad.bits.AttrLen = be16_to_cpu(FOURBYTES + + sizeof (struct lpfc_name)); + memcpy(&ae->un.NodeName, &phba->fc_sparam.nodeName, + sizeof (struct lpfc_name)); + ab->EntryCnt++; + size += FOURBYTES + sizeof (struct lpfc_name); + + /* #2 HBA attribute entry */ + ae = (ATTRIBUTE_ENTRY *) ((uint8_t *) rh + size); + ae->ad.bits.AttrType = be16_to_cpu(MANUFACTURER); + strcpy(ae->un.Manufacturer, "Emulex Corporation"); + len = strlen(ae->un.Manufacturer); + len += (len & 3) ? (4 - (len & 3)) : 4; + ae->ad.bits.AttrLen = be16_to_cpu(FOURBYTES + len); + ab->EntryCnt++; + size += FOURBYTES + len; + + /* #3 HBA attribute entry */ + ae = (ATTRIBUTE_ENTRY *) ((uint8_t *) rh + size); + ae->ad.bits.AttrType = be16_to_cpu(SERIAL_NUMBER); + strcpy(ae->un.SerialNumber, phba->SerialNumber); + len = strlen(ae->un.SerialNumber); + len += (len & 3) ? (4 - (len & 3)) : 4; + ae->ad.bits.AttrLen = be16_to_cpu(FOURBYTES + len); + ab->EntryCnt++; + size += FOURBYTES + len; + + /* #4 HBA attribute entry */ + ae = (ATTRIBUTE_ENTRY *) ((uint8_t *) rh + size); + ae->ad.bits.AttrType = be16_to_cpu(MODEL); + lpfc_get_hba_model_desc(phba, ae->un.Model, NULL); + len = strlen(ae->un.Model); + len += (len & 3) ? (4 - (len & 3)) : 4; + ae->ad.bits.AttrLen = be16_to_cpu(FOURBYTES + len); + ab->EntryCnt++; + size += FOURBYTES + len; + + /* #5 HBA attribute entry */ + ae = (ATTRIBUTE_ENTRY *) ((uint8_t *) rh + size); + ae->ad.bits.AttrType = be16_to_cpu(MODEL_DESCRIPTION); + lpfc_get_hba_model_desc(phba, NULL, + ae->un.ModelDescription); + len = strlen(ae->un.ModelDescription); + len += (len & 3) ? (4 - (len & 3)) : 4; + ae->ad.bits.AttrLen = be16_to_cpu(FOURBYTES + len); + ab->EntryCnt++; + size += FOURBYTES + len; + + /* #6 HBA attribute entry */ + ae = (ATTRIBUTE_ENTRY *) ((uint8_t *) rh + size); + ae->ad.bits.AttrType = be16_to_cpu(HARDWARE_VERSION); + ae->ad.bits.AttrLen = be16_to_cpu(FOURBYTES + 8); + /* Convert JEDEC ID to ascii for hardware version */ + incr = vp->rev.biuRev; + for (i = 0; i < 8; i++) { + j = (incr & 0xf); + if (j <= 9) + ae->un.HardwareVersion[7 - i] = + (char)((uint8_t) 0x30 + + (uint8_t) j); + else + ae->un.HardwareVersion[7 - i] = + (char)((uint8_t) 0x61 + + (uint8_t) (j - 10)); + incr = (incr >> 4); + } + ab->EntryCnt++; + size += FOURBYTES + 8; + + /* #7 HBA attribute entry */ + ae = (ATTRIBUTE_ENTRY *) ((uint8_t *) rh + size); + ae->ad.bits.AttrType = be16_to_cpu(DRIVER_VERSION); + strcpy(ae->un.DriverVersion, lpfc_release_version); + len = strlen(ae->un.DriverVersion); + len += (len & 3) ? (4 - (len & 3)) : 4; + ae->ad.bits.AttrLen = be16_to_cpu(FOURBYTES + len); + ab->EntryCnt++; + size += FOURBYTES + len; + + /* #8 HBA attribute entry */ + ae = (ATTRIBUTE_ENTRY *) ((uint8_t *) rh + size); + ae->ad.bits.AttrType = be16_to_cpu(OPTION_ROM_VERSION); + strcpy(ae->un.OptionROMVersion, phba->OptionROMVersion); + len = strlen(ae->un.OptionROMVersion); + len += (len & 3) ? (4 - (len & 3)) : 4; + ae->ad.bits.AttrLen = be16_to_cpu(FOURBYTES + len); + ab->EntryCnt++; + size += FOURBYTES + len; + + /* #9 HBA attribute entry */ + ae = (ATTRIBUTE_ENTRY *) ((uint8_t *) rh + size); + ae->ad.bits.AttrType = be16_to_cpu(FIRMWARE_VERSION); + lpfc_decode_firmware_rev(phba, ae->un.FirmwareVersion, + 1); + len = strlen(ae->un.FirmwareVersion); + len += (len & 3) ? (4 - (len & 3)) : 4; + ae->ad.bits.AttrLen = be16_to_cpu(FOURBYTES + len); + ab->EntryCnt++; + size += FOURBYTES + len; + + /* #10 HBA attribute entry */ + ae = (ATTRIBUTE_ENTRY *) ((uint8_t *) rh + size); + ae->ad.bits.AttrType = be16_to_cpu(OS_NAME_VERSION); + sprintf(ae->un.OsNameVersion, "%s %s %s", + system_utsname.sysname, system_utsname.release, + system_utsname.version); + len = strlen(ae->un.OsNameVersion); + len += (len & 3) ? (4 - (len & 3)) : 4; + ae->ad.bits.AttrLen = be16_to_cpu(FOURBYTES + len); + ab->EntryCnt++; + size += FOURBYTES + len; + + /* #11 HBA attribute entry */ + ae = (ATTRIBUTE_ENTRY *) ((uint8_t *) rh + size); + ae->ad.bits.AttrType = be16_to_cpu(MAX_CT_PAYLOAD_LEN); + ae->ad.bits.AttrLen = be16_to_cpu(FOURBYTES + 4); + ae->un.MaxCTPayloadLen = (65 * 4096); + ab->EntryCnt++; + size += FOURBYTES + 4; + + ab->EntryCnt = be32_to_cpu(ab->EntryCnt); + /* Total size */ + size = GID_REQUEST_SZ - 4 + size; + } + break; + + case SLI_MGMT_RPA: + { + lpfc_vpd_t *vp; + struct serv_parm *hsp; + int len; + + vp = &phba->vpd; + + CtReq->CommandResponse.bits.CmdRsp = + be16_to_cpu(SLI_MGMT_RPA); + CtReq->CommandResponse.bits.Size = 0; + pab = (REG_PORT_ATTRIBUTE *) & CtReq->un.PortID; + size = sizeof (struct lpfc_name) + FOURBYTES; + memcpy((uint8_t *) & pab->PortName, + (uint8_t *) & phba->fc_sparam.portName, + sizeof (struct lpfc_name)); + pab->ab.EntryCnt = 0; + + /* #1 Port attribute entry */ + ae = (ATTRIBUTE_ENTRY *) ((uint8_t *) pab + size); + ae->ad.bits.AttrType = be16_to_cpu(SUPPORTED_FC4_TYPES); + ae->ad.bits.AttrLen = be16_to_cpu(FOURBYTES + 32); + ae->un.SupportFC4Types[2] = 1; + ae->un.SupportFC4Types[7] = 1; + pab->ab.EntryCnt++; + size += FOURBYTES + 32; + + /* #2 Port attribute entry */ + ae = (ATTRIBUTE_ENTRY *) ((uint8_t *) pab + size); + ae->ad.bits.AttrType = be16_to_cpu(SUPPORTED_SPEED); + ae->ad.bits.AttrLen = be16_to_cpu(FOURBYTES + 4); + if (FC_JEDEC_ID(vp->rev.biuRev) == VIPER_JEDEC_ID) + ae->un.SupportSpeed = HBA_PORTSPEED_10GBIT; + else if (FC_JEDEC_ID(vp->rev.biuRev) == HELIOS_JEDEC_ID) + ae->un.SupportSpeed = HBA_PORTSPEED_4GBIT; + else if ((FC_JEDEC_ID(vp->rev.biuRev) == + CENTAUR_2G_JEDEC_ID) + || (FC_JEDEC_ID(vp->rev.biuRev) == + PEGASUS_JEDEC_ID) + || (FC_JEDEC_ID(vp->rev.biuRev) == + THOR_JEDEC_ID)) + ae->un.SupportSpeed = HBA_PORTSPEED_2GBIT; + else + ae->un.SupportSpeed = HBA_PORTSPEED_1GBIT; + pab->ab.EntryCnt++; + size += FOURBYTES + 4; + + /* #3 Port attribute entry */ + ae = (ATTRIBUTE_ENTRY *) ((uint8_t *) pab + size); + ae->ad.bits.AttrType = be16_to_cpu(PORT_SPEED); + ae->ad.bits.AttrLen = be16_to_cpu(FOURBYTES + 4); + switch(phba->fc_linkspeed) { + case LA_1GHZ_LINK: + ae->un.PortSpeed = HBA_PORTSPEED_1GBIT; + break; + case LA_2GHZ_LINK: + ae->un.PortSpeed = HBA_PORTSPEED_2GBIT; + break; + case LA_4GHZ_LINK: + ae->un.PortSpeed = HBA_PORTSPEED_4GBIT; + break; + default: + ae->un.PortSpeed = + HBA_PORTSPEED_UNKNOWN; + break; + } + pab->ab.EntryCnt++; + size += FOURBYTES + 4; + + /* #4 Port attribute entry */ + ae = (ATTRIBUTE_ENTRY *) ((uint8_t *) pab + size); + ae->ad.bits.AttrType = be16_to_cpu(MAX_FRAME_SIZE); + ae->ad.bits.AttrLen = be16_to_cpu(FOURBYTES + 4); + hsp = (struct serv_parm *) & phba->fc_sparam; + ae->un.MaxFrameSize = + (((uint32_t) hsp->cmn. + bbRcvSizeMsb) << 8) | (uint32_t) hsp->cmn. + bbRcvSizeLsb; + pab->ab.EntryCnt++; + size += FOURBYTES + 4; + + /* #5 Port attribute entry */ + ae = (ATTRIBUTE_ENTRY *) ((uint8_t *) pab + size); + ae->ad.bits.AttrType = be16_to_cpu(OS_DEVICE_NAME); + strcpy((char *)ae->un.OsDeviceName, LPFC_DRIVER_NAME); + len = strlen((char *)ae->un.OsDeviceName); + len += (len & 3) ? (4 - (len & 3)) : 4; + ae->ad.bits.AttrLen = be16_to_cpu(FOURBYTES + len); + pab->ab.EntryCnt++; + size += FOURBYTES + len; + + if (phba->cfg_fdmi_on == 2) { + /* #6 Port attribute entry */ + ae = (ATTRIBUTE_ENTRY *) ((uint8_t *) pab + + size); + ae->ad.bits.AttrType = be16_to_cpu(HOST_NAME); + sprintf(ae->un.HostName, "%s", + system_utsname.nodename); + len = strlen(ae->un.HostName); + len += (len & 3) ? (4 - (len & 3)) : 4; + ae->ad.bits.AttrLen = + be16_to_cpu(FOURBYTES + len); + pab->ab.EntryCnt++; + size += FOURBYTES + len; + } + + pab->ab.EntryCnt = be32_to_cpu(pab->ab.EntryCnt); + /* Total size */ + size = GID_REQUEST_SZ - 4 + size; + } + break; + + case SLI_MGMT_DHBA: + CtReq->CommandResponse.bits.CmdRsp = be16_to_cpu(SLI_MGMT_DHBA); + CtReq->CommandResponse.bits.Size = 0; + pe = (PORT_ENTRY *) & CtReq->un.PortID; + memcpy((uint8_t *) & pe->PortName, + (uint8_t *) & phba->fc_sparam.portName, + sizeof (struct lpfc_name)); + size = GID_REQUEST_SZ - 4 + sizeof (struct lpfc_name); + break; + + case SLI_MGMT_DPRT: + CtReq->CommandResponse.bits.CmdRsp = be16_to_cpu(SLI_MGMT_DPRT); + CtReq->CommandResponse.bits.Size = 0; + pe = (PORT_ENTRY *) & CtReq->un.PortID; + memcpy((uint8_t *) & pe->PortName, + (uint8_t *) & phba->fc_sparam.portName, + sizeof (struct lpfc_name)); + size = GID_REQUEST_SZ - 4 + sizeof (struct lpfc_name); + break; + } + + bpl = (struct ulp_bde64 *) bmp->virt; + bpl->addrHigh = le32_to_cpu( putPaddrHigh(mp->phys) ); + bpl->addrLow = le32_to_cpu( putPaddrLow(mp->phys) ); + bpl->tus.f.bdeFlags = 0; + bpl->tus.f.bdeSize = size; + bpl->tus.w = le32_to_cpu(bpl->tus.w); + + cmpl = lpfc_cmpl_ct_cmd_fdmi; + + if (!lpfc_ct_cmd(phba, mp, bmp, ndlp, cmpl, FC_MAX_NS_RSP)) + return 0; + + lpfc_mbuf_free(phba, bmp->virt, bmp->phys); +fdmi_cmd_free_bmp: + kfree(bmp); +fdmi_cmd_free_mpvirt: + lpfc_mbuf_free(phba, mp->virt, mp->phys); +fdmi_cmd_free_mp: + kfree(mp); +fdmi_cmd_exit: + /* Issue FDMI request failed */ + lpfc_printf_log(phba, + KERN_INFO, + LOG_DISCOVERY, + "%d:0244 Issue FDMI request failed Data: x%x\n", + phba->brd_no, + cmdcode); + return 1; +} + + +void +lpfc_fdmi_tmo(unsigned long ptr) +{ + struct lpfc_hba *phba = (struct lpfc_hba*)ptr; + struct lpfc_nodelist *ndlp; + unsigned long iflag; + + spin_lock_irqsave(phba->host->host_lock, iflag); + ndlp = lpfc_findnode_did(phba, NLP_SEARCH_ALL, FDMI_DID); + if (ndlp) { + if (system_utsname.nodename[0] != '\0') { + lpfc_fdmi_cmd(phba, ndlp, SLI_MGMT_DHBA); + } else { + mod_timer(&phba->fc_fdmitmo, jiffies + HZ * 60); + } + } + spin_unlock_irqrestore(phba->host->host_lock, iflag); + return; +} + + +void +lpfc_decode_firmware_rev(struct lpfc_hba * phba, char *fwrevision, int flag) +{ + struct lpfc_sli *psli = &phba->sli; + lpfc_vpd_t *vp = &phba->vpd; + uint32_t b1, b2, b3, b4, i, rev; + char c; + uint32_t *ptr, str[4]; + uint8_t *fwname; + + if (vp->rev.rBit) { + if (psli->sliinit.sli_flag & LPFC_SLI2_ACTIVE) + rev = vp->rev.sli2FwRev; + else + rev = vp->rev.sli1FwRev; + + b1 = (rev & 0x0000f000) >> 12; + b2 = (rev & 0x00000f00) >> 8; + b3 = (rev & 0x000000c0) >> 6; + b4 = (rev & 0x00000030) >> 4; + + switch (b4) { + case 0: + c = 'N'; + break; + case 1: + c = 'A'; + break; + case 2: + c = 'B'; + break; + default: + c = 0; + break; + } + b4 = (rev & 0x0000000f); + + if (psli->sliinit.sli_flag & LPFC_SLI2_ACTIVE) + fwname = vp->rev.sli2FwName; + else + fwname = vp->rev.sli1FwName; + + for (i = 0; i < 16; i++) + if(fwname[i] == 0x20) + fwname[i] = 0; + + ptr = (uint32_t*)fwname; + + for (i = 0; i < 3; i++) + str[i] = be32_to_cpu(*ptr++); + + if (c == 0) { + if (flag) + sprintf(fwrevision, "%d.%d%d (%s)", + b1, b2, b3, (char *)str); + else + sprintf(fwrevision, "%d.%d%d", b1, + b2, b3); + } else { + if (flag) + sprintf(fwrevision, "%d.%d%d%c%d (%s)", + b1, b2, b3, c, + b4, (char *)str); + else + sprintf(fwrevision, "%d.%d%d%c%d", + b1, b2, b3, c, b4); + } + } else { + rev = vp->rev.smFwRev; + + b1 = (rev & 0xff000000) >> 24; + b2 = (rev & 0x00f00000) >> 20; + b3 = (rev & 0x000f0000) >> 16; + c = (rev & 0x0000ff00) >> 8; + b4 = (rev & 0x000000ff); + + if (flag) + sprintf(fwrevision, "%d.%d%d%c%d ", b1, + b2, b3, c, b4); + else + sprintf(fwrevision, "%d.%d%d%c%d ", b1, + b2, b3, c, b4); + } + return; +} + +void +lpfc_get_hba_model_desc(struct lpfc_hba * phba, uint8_t * mdp, uint8_t * descp) +{ + lpfc_vpd_t *vp; + uint32_t id; + char str[16]; + + vp = &phba->vpd; + pci_read_config_dword(phba->pcidev, PCI_VENDOR_ID, &id); + + switch ((id >> 16) & 0xffff) { + case PCI_DEVICE_ID_SUPERFLY: + if (vp->rev.biuRev >= 1 && vp->rev.biuRev <= 3) + strcpy(str, "LP7000 1"); + else + strcpy(str, "LP7000E 1"); + break; + case PCI_DEVICE_ID_DRAGONFLY: + strcpy(str, "LP8000 1"); + break; + case PCI_DEVICE_ID_CENTAUR: + if (FC_JEDEC_ID(vp->rev.biuRev) == CENTAUR_2G_JEDEC_ID) + strcpy(str, "LP9002 2"); + else + strcpy(str, "LP9000 1"); + break; + case PCI_DEVICE_ID_RFLY: + strcpy(str, "LP952 2"); + break; + case PCI_DEVICE_ID_PEGASUS: + strcpy(str, "LP9802 2"); + break; + case PCI_DEVICE_ID_THOR: + strcpy(str, "LP10000 2"); + break; + case PCI_DEVICE_ID_VIPER: + strcpy(str, "LPX1000 10"); + break; + case PCI_DEVICE_ID_PFLY: + strcpy(str, "LP982 2"); + break; + case PCI_DEVICE_ID_TFLY: + strcpy(str, "LP1050 2"); + break; + case PCI_DEVICE_ID_HELIOS: + strcpy(str, "LP11000 4"); + break; + case PCI_DEVICE_ID_BMID: + strcpy(str, "LP1150 4"); + break; + case PCI_DEVICE_ID_BSMB: + strcpy(str, "LP111 4"); + break; + case PCI_DEVICE_ID_ZEPHYR: + strcpy(str, "LP11000e 4"); + break; + case PCI_DEVICE_ID_ZMID: + strcpy(str, "LP1150e 4"); + break; + case PCI_DEVICE_ID_ZSMB: + strcpy(str, "LP111e 4"); + break; + case PCI_DEVICE_ID_LP101: + strcpy(str, "LP101 2"); + break; + } + if (mdp) + sscanf(str, "%s", mdp); + if (descp) + sprintf(descp, "Emulex LightPulse %s Gigabit PCI Fibre " + "Channel Adapter", str); +} + diff --git a/drivers/scsi/lpfc/lpfc_disc.h b/drivers/scsi/lpfc/lpfc_disc.h new file mode 100644 index 000000000..0b7ae7f55 --- /dev/null +++ b/drivers/scsi/lpfc/lpfc_disc.h @@ -0,0 +1,270 @@ +/******************************************************************* + * This file is part of the Emulex Linux Device Driver for * + * Enterprise Fibre Channel Host Bus Adapters. * + * Refer to the README file included with this package for * + * driver version and adapter support. * + * Copyright (C) 2004 Emulex Corporation. * + * www.emulex.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, a copy of which * + * can be found in the file COPYING included with this package. * + *******************************************************************/ + +/* + * $Id: lpfc_disc.h 1.45 2004/11/10 11:40:40EST sf_support Exp $ + */ + +#ifndef _H_LPFC_DISC +#define _H_LPFC_DISC + +#include "lpfc_hw.h" + +struct lpfc_target; + +#define FC_MAX_HOLD_RSCN 32 /* max number of deferred RSCNs */ +#define FC_MAX_NS_RSP 65536 /* max size NameServer rsp */ +#define FC_MAXLOOP 126 /* max devices supported on a fc loop */ +#define LPFC_DISC_FLOGI_TMO 10 /* Discovery FLOGI ratov */ + +/* Defines for failMask bitmask + * These are reasons that the device is not currently available + * for I/O to be sent. + */ +#define LPFC_DEV_LINK_DOWN 0x1 /* Link is down */ +#define LPFC_DEV_DISAPPEARED 0x2 /* Device disappeared from mapped + list */ +#define LPFC_DEV_DISCOVERY_INP 0x4 /* Device to go through discovery */ +#define LPFC_DEV_DISCONNECTED 0x8 /* noactive connection to remote dev */ + +/* These defines are used for set failMask routines */ +#define LPFC_SET_BITMASK 1 +#define LPFC_CLR_BITMASK 2 + +/* Provide an enumeration for the Types of addresses a FARP can resolve. */ +typedef enum lpfc_farp_addr_type { + LPFC_FARP_BY_IEEE, + LPFC_FARP_BY_WWPN, + LPFC_FARP_BY_WWNN, +} LPFC_FARP_ADDR_TYPE; + +/* This is the protocol dependent definition for a Node List Entry. + * This is used by Fibre Channel protocol to support FCP. + */ + +struct lpfc_bindlist { + struct list_head nlp_listp; + struct lpfc_target *nlp_Target; /* ptr to the tgt structure */ + struct lpfc_name nlp_portname; /* port name */ + struct lpfc_name nlp_nodename; /* node name */ + uint16_t nlp_bind_type; + uint16_t nlp_sid; /* scsi id */ + uint32_t nlp_DID; /* FibreChannel D_ID of entry */ +}; + +struct lpfc_nodelist { + struct list_head nlp_listp; + struct lpfc_name nlp_portname; /* port name */ + struct lpfc_name nlp_nodename; /* node name */ + uint32_t nlp_failMask; /* failure mask for device */ + uint32_t nlp_flag; /* entry flags */ + uint32_t nlp_DID; /* FC D_ID of entry */ + uint32_t nlp_last_elscmd; /* Last ELS cmd sent */ + uint16_t nlp_type; +#define NLP_FC_NODE 0x1 /* entry is an FC node */ +#define NLP_FABRIC 0x4 /* entry rep a Fabric entity */ +#define NLP_FCP_TARGET 0x8 /* entry is an FCP target */ + + uint16_t nlp_rpi; + uint16_t nlp_state; /* state transition indicator */ + uint16_t nlp_xri; /* output exchange id for RPI */ + uint16_t nlp_sid; /* scsi id */ + uint8_t nlp_retry; /* used for ELS retries */ + uint8_t nlp_disc_refcnt; /* used for DSM */ + uint8_t nlp_fcp_info; /* class info, bits 0-3 */ +#define NLP_FCP_2_DEVICE 0x10 /* FCP-2 device */ + + struct timer_list nlp_delayfunc; /* Used for delayed ELS cmds */ + struct timer_list nlp_tmofunc; /* Used for nodev tmo */ + struct lpfc_target *nlp_Target; /* Pointer to the target + structure */ + + struct lpfc_bindlist *nlp_listp_bind; /* Linked list bounded remote + ports */ + struct lpfc_nodelist *nlp_rpi_hash_next; + struct lpfc_hba *nlp_phba; +}; + +/*++ + * lpfc_node_farp_list: + * This data structure defines the attributes associated with + * an outstanding FARP REQ to a remote node. + * + * listentry - head of this list of pending farp requests. + * rnode_addr - The address of the remote node. Either the IEEE, WWPN, or + * WWNN. Used in the FARP request. + * + --*/ +struct lpfc_node_farp_pend { + struct list_head listentry; + struct lpfc_name rnode_addr; +}; + +/* Defines for nlp_flag (uint32) */ +#define NLP_NO_LIST 0x0 /* Indicates immediately free node */ +#define NLP_UNUSED_LIST 0x1 /* Flg to indicate node will be freed */ +#define NLP_PLOGI_LIST 0x2 /* Flg to indicate sent PLOGI */ +#define NLP_ADISC_LIST 0x3 /* Flg to indicate sent ADISC */ +#define NLP_REGLOGIN_LIST 0x4 /* Flg to indicate sent REG_LOGIN */ +#define NLP_PRLI_LIST 0x5 /* Flg to indicate sent PRLI */ +#define NLP_UNMAPPED_LIST 0x6 /* Node is now unmapped */ +#define NLP_MAPPED_LIST 0x7 /* Node is now mapped */ +#define NLP_NPR_LIST 0x8 /* Node is in NPort Recovery state */ +#define NLP_JUST_DQ 0x9 /* just deque ndlp in lpfc_nlp_list */ +#define NLP_LIST_MASK 0xf /* mask to see what list node is on */ +#define NLP_PLOGI_SND 0x20 /* sent PLOGI request for this entry */ +#define NLP_PRLI_SND 0x40 /* sent PRLI request for this entry */ +#define NLP_ADISC_SND 0x80 /* sent ADISC request for this entry */ +#define NLP_LOGO_SND 0x100 /* sent LOGO request for this entry */ +#define NLP_RNID_SND 0x400 /* sent RNID request for this entry */ +#define NLP_ELS_SND_MASK 0x7e0 /* sent ELS request for this entry */ +#define NLP_AUTOMAP 0x800 /* Entry was automap'ed */ +#define NLP_SEED_WWPN 0x1000 /* Entry scsi id is seeded for WWPN */ +#define NLP_SEED_WWNN 0x2000 /* Entry scsi id is seeded for WWNN */ +#define NLP_SEED_DID 0x4000 /* Entry scsi id is seeded for DID */ +#define NLP_SEED_MASK 0x807000 /* mask for seeded flags */ +#define NLP_NS_NODE 0x8000 /* Authenticated entry by NameServer */ +#define NLP_NODEV_TMO 0x10000 /* nodev timeout is running for node */ +#define NLP_DELAY_TMO 0x20000 /* delay timeout is running for node */ +#define NLP_NPR_2B_DISC 0x40000 /* node is included in num_disc_nodes */ +#define NLP_RCV_PLOGI 0x80000 /* Rcv'ed PLOGI from remote system */ +#define NLP_LOGO_ACC 0x100000 /* Process LOGO after ACC completes */ +#define NLP_TGT_NO_SCSIID 0x200000 /* good PRLI but no binding for scsid */ +#define NLP_SEED_ALPA 0x800000 /* SCSI id is derived from alpa array */ +#define NLP_ACC_REGLOGIN 0x1000000 /* Issue Reg Login after successful + ACC */ +#define NLP_NPR_ADISC 0x2000000 /* Issue ADISC when dq'ed from + NPR list */ +#define NLP_DELAY_REMOVE 0x4000000 /* Defer removal till end of DSM */ + +/* Defines for list searchs */ +#define NLP_SEARCH_MAPPED 0x1 /* search mapped */ +#define NLP_SEARCH_UNMAPPED 0x2 /* search unmapped */ +#define NLP_SEARCH_PLOGI 0x4 /* search plogi */ +#define NLP_SEARCH_ADISC 0x8 /* search adisc */ +#define NLP_SEARCH_REGLOGIN 0x10 /* search reglogin */ +#define NLP_SEARCH_PRLI 0x20 /* search prli */ +#define NLP_SEARCH_NPR 0x40 /* search npr */ +#define NLP_SEARCH_UNUSED 0x80 /* search mapped */ +#define NLP_SEARCH_ALL 0xff /* search all lists */ + +/* There are 4 different double linked lists nodelist entries can reside on. + * The Port Login (PLOGI) list and Address Discovery (ADISC) list are used + * when Link Up discovery or Registered State Change Notification (RSCN) + * processing is needed. Each list holds the nodes that require a PLOGI or + * ADISC Extended Link Service (ELS) request. These lists keep track of the + * nodes affected by an RSCN, or a Link Up (Typically, all nodes are effected + * by Link Up) event. The unmapped_list contains all nodes that have + * successfully logged into at the Fibre Channel level. The + * mapped_list will contain all nodes that are mapped FCP targets. + * + * The bind list is a list of undiscovered (potentially non-existent) nodes + * that we have saved binding information on. This information is used when + * nodes transition from the unmapped to the mapped list. + */ + +/* Defines for nlp_state */ +#define NLP_STE_UNUSED_NODE 0x0 /* node is just allocated */ +#define NLP_STE_PLOGI_ISSUE 0x1 /* PLOGI was sent to NL_PORT */ +#define NLP_STE_ADISC_ISSUE 0x2 /* ADISC was sent to NL_PORT */ +#define NLP_STE_REG_LOGIN_ISSUE 0x3 /* REG_LOGIN was issued for NL_PORT */ +#define NLP_STE_PRLI_ISSUE 0x4 /* PRLI was sent to NL_PORT */ +#define NLP_STE_UNMAPPED_NODE 0x5 /* PRLI completed from NL_PORT */ +#define NLP_STE_MAPPED_NODE 0x6 /* Identified as a FCP Target */ +#define NLP_STE_NPR_NODE 0x7 /* NPort disappeared */ +#define NLP_STE_MAX_STATE 0x8 +#define NLP_STE_FREED_NODE 0xff /* node entry was freed to MEM_NLP */ + +/* For UNUSED_NODE state, the node has just been allocated. + * For PLOGI_ISSUE and REG_LOGIN_ISSUE, the node is on + * the PLOGI list. For REG_LOGIN_COMPL, the node is taken off the PLOGI list + * and put on the unmapped list. For ADISC processing, the node is taken off + * the ADISC list and placed on either the mapped or unmapped list (depending + * on its previous state). Once on the unmapped list, a PRLI is issued and the + * state changed to PRLI_ISSUE. When the PRLI completion occurs, the state is + * changed to PRLI_COMPL. If the completion indicates a mapped + * node, the node is taken off the unmapped list. The binding list is checked + * for a valid binding, or a binding is automatically assigned. If binding + * assignment is unsuccessful, the node is left on the unmapped list. If + * binding assignment is successful, the associated binding list entry (if + * any) is removed, and the node is placed on the mapped list. + */ +/* + * For a Link Down, all nodes on the ADISC, PLOGI, unmapped or mapped + * lists will receive a DEVICE_RECOVERY event. If the linkdown or nodev timers + * expire, all effected nodes will receive a DEVICE_RM event. + */ +/* + * For a Link Up or RSCN, all nodes will move from the mapped / unmapped lists + * to either the ADISC or PLOGI list. After a Nameserver query or ALPA loopmap + * check, additional nodes may be added (DEVICE_ADD) or removed (DEVICE_RM) to / + * from the PLOGI or ADISC lists. Once the PLOGI and ADISC lists are populated, + * we will first process the ADISC list. 32 entries are processed initially and + * ADISC is initited for each one. Completions / Events for each node are + * funnelled thru the state machine. As each node finishes ADISC processing, it + * starts ADISC for any nodes waiting for ADISC processing. If no nodes are + * waiting, and the ADISC list count is identically 0, then we are done. For + * Link Up discovery, since all nodes on the PLOGI list are UNREG_LOGIN'ed, we + * can issue a CLEAR_LA and reenable Link Events. Next we will process the PLOGI + * list. 32 entries are processed initially and PLOGI is initited for each one. + * Completions / Events for each node are funnelled thru the state machine. As + * each node finishes PLOGI processing, it starts PLOGI for any nodes waiting + * for PLOGI processing. If no nodes are waiting, and the PLOGI list count is + * identically 0, then we are done. We have now completed discovery / RSCN + * handling. Upon completion, ALL nodes should be on either the mapped or + * unmapped lists. + */ + +/* Defines for Node List Entry Events that could happen */ +#define NLP_EVT_RCV_PLOGI 0x0 /* Rcv'd an ELS PLOGI command */ +#define NLP_EVT_RCV_PRLI 0x1 /* Rcv'd an ELS PRLI command */ +#define NLP_EVT_RCV_LOGO 0x2 /* Rcv'd an ELS LOGO command */ +#define NLP_EVT_RCV_ADISC 0x3 /* Rcv'd an ELS ADISC command */ +#define NLP_EVT_RCV_PDISC 0x4 /* Rcv'd an ELS PDISC command */ +#define NLP_EVT_RCV_PRLO 0x5 /* Rcv'd an ELS PRLO command */ +#define NLP_EVT_CMPL_PLOGI 0x6 /* Sent an ELS PLOGI command */ +#define NLP_EVT_CMPL_PRLI 0x7 /* Sent an ELS PRLI command */ +#define NLP_EVT_CMPL_LOGO 0x8 /* Sent an ELS LOGO command */ +#define NLP_EVT_CMPL_ADISC 0x9 /* Sent an ELS ADISC command */ +#define NLP_EVT_CMPL_REG_LOGIN 0xa /* REG_LOGIN mbox cmd completed */ +#define NLP_EVT_DEVICE_RM 0xb /* Device not found in NS / ALPAmap */ +#define NLP_EVT_DEVICE_RECOVERY 0xc /* Device existence unknown */ +#define NLP_EVT_MAX_EVENT 0xd + +/* structure used to queue event to the discovery tasklet */ +struct lpfc_disc_evt { + struct list_head evt_listp; + void * evt_arg1; + void * evt_arg2; + uint32_t evt; +}; +typedef struct lpfc_disc_evt LPFC_DISC_EVT_t; + +#define LPFC_EVT_MBOX 0x1 +#define LPFC_EVT_SOL_IOCB 0x2 +#define LPFC_EVT_UNSOL_IOCB 0x3 +#define LPFC_EVT_NODEV_TMO 0x4 +#define LPFC_EVT_SCAN 0x5 + +/* Definitions for Binding Entry Type for lpfc_parse_binding_entry() */ +#define LPFC_BIND_WW_NN_PN 0 +#define LPFC_BIND_DID 1 + +#endif /* _H_LPFC_DISC */ diff --git a/drivers/scsi/lpfc/lpfc_els.c b/drivers/scsi/lpfc/lpfc_els.c new file mode 100644 index 000000000..427f68b1d --- /dev/null +++ b/drivers/scsi/lpfc/lpfc_els.c @@ -0,0 +1,3235 @@ +/******************************************************************* + * This file is part of the Emulex Linux Device Driver for * + * Enterprise Fibre Channel Host Bus Adapters. * + * Refer to the README file included with this package for * + * driver version and adapter support. * + * Copyright (C) 2004 Emulex Corporation. * + * www.emulex.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, a copy of which * + * can be found in the file COPYING included with this package. * + *******************************************************************/ + +/* + * $Id: lpfc_els.c 1.152 2004/11/18 18:27:53EST sf_support Exp $ + */ +#include +#include +#include +#include +#include +#include +#include +#include "lpfc_sli.h" +#include "lpfc_disc.h" +#include "lpfc_scsi.h" +#include "lpfc.h" +#include "lpfc_crtn.h" +#include "lpfc_hw.h" +#include "lpfc_logmsg.h" +#include "lpfc_mem.h" + + +static int lpfc_els_retry(struct lpfc_hba *, struct lpfc_iocbq *, + struct lpfc_iocbq *); +static int lpfc_max_els_tries = 3; + +static int +lpfc_els_chk_latt(struct lpfc_hba * phba) +{ + struct lpfc_sli *psli; + LPFC_MBOXQ_t *mbox; + uint32_t ha_copy; + + psli = &phba->sli; + + if ((phba->hba_state < LPFC_HBA_READY) && + (phba->hba_state != LPFC_LINK_DOWN)) { + + /* Read the HBA Host Attention Register */ + ha_copy = readl(phba->HAregaddr); + + if (ha_copy & HA_LATT) { /* Link Attention interrupt */ + + /* Pending Link Event during Discovery */ + lpfc_printf_log(phba, KERN_WARNING, LOG_DISCOVERY, + "%d:0237 Pending Link Event during " + "Discovery: State x%x\n", + phba->brd_no, phba->hba_state); + + /* CLEAR_LA should re-enable link attention events and + * we should then imediately take a LATT event. The + * LATT processing should call lpfc_linkdown() which + * will cleanup any left over in-progress discovery + * events. + */ + phba->fc_flag |= FC_ABORT_DISCOVERY; + + if (phba->hba_state != LPFC_CLEAR_LA) { + if ((mbox = mempool_alloc(phba->mbox_mem_pool, + GFP_ATOMIC))) { + phba->hba_state = LPFC_CLEAR_LA; + lpfc_clear_la(phba, mbox); + mbox->mbox_cmpl = + lpfc_mbx_cmpl_clear_la; + if (lpfc_sli_issue_mbox + (phba, mbox, + (MBX_NOWAIT | MBX_STOP_IOCB)) + == MBX_NOT_FINISHED) { + mempool_free(mbox, + phba->mbox_mem_pool); + phba->hba_state = + LPFC_HBA_ERROR; + } + } + } + return (1); + } + } + + return (0); +} + +struct lpfc_iocbq * +lpfc_prep_els_iocb(struct lpfc_hba * phba, + uint8_t expectRsp, + uint16_t cmdSize, + uint8_t retry, struct lpfc_nodelist * ndlp, uint32_t elscmd) +{ + struct lpfc_sli *psli; + struct lpfc_sli_ring *pring; + struct lpfc_iocbq *elsiocb; + struct lpfc_dmabuf *pcmd, *prsp, *pbuflist; + struct ulp_bde64 *bpl; + IOCB_t *icmd; + uint32_t tag; + + psli = &phba->sli; + pring = &psli->ring[LPFC_ELS_RING]; /* ELS ring */ + + if (phba->hba_state < LPFC_LINK_UP) + return NULL; + + + /* Allocate buffer for command iocb */ + elsiocb = mempool_alloc(phba->iocb_mem_pool, GFP_ATOMIC); + if (!elsiocb) + return NULL; + + memset(elsiocb, 0, sizeof (struct lpfc_iocbq)); + icmd = &elsiocb->iocb; + + /* fill in BDEs for command */ + /* Allocate buffer for command payload */ + if (((pcmd = kmalloc(sizeof (struct lpfc_dmabuf), GFP_ATOMIC)) == 0) || + ((pcmd->virt = lpfc_mbuf_alloc(phba, + MEM_PRI, &(pcmd->phys))) == 0)) { + if (pcmd) + kfree(pcmd); + mempool_free( elsiocb, phba->iocb_mem_pool); + return NULL; + } + + INIT_LIST_HEAD(&pcmd->list); + + /* Allocate buffer for response payload */ + if (expectRsp) { + prsp = kmalloc(sizeof (struct lpfc_dmabuf), GFP_ATOMIC); + if (prsp) + prsp->virt = lpfc_mbuf_alloc(phba, MEM_PRI, + &prsp->phys); + if (prsp == 0 || prsp->virt == 0) { + if (prsp) + kfree(prsp); + lpfc_mbuf_free(phba, pcmd->virt, pcmd->phys); + kfree(pcmd); + mempool_free( elsiocb, phba->iocb_mem_pool); + return NULL; + } + INIT_LIST_HEAD(&prsp->list); + } else { + prsp = NULL; + } + + /* Allocate buffer for Buffer ptr list */ + pbuflist = kmalloc(sizeof (struct lpfc_dmabuf), GFP_ATOMIC); + if (pbuflist) + pbuflist->virt = lpfc_mbuf_alloc(phba, MEM_PRI, + &pbuflist->phys); + if (pbuflist == 0 || pbuflist->virt == 0) { + mempool_free( elsiocb, phba->iocb_mem_pool); + lpfc_mbuf_free(phba, pcmd->virt, pcmd->phys); + lpfc_mbuf_free(phba, prsp->virt, prsp->phys); + kfree(pcmd); + kfree(prsp); + if (pbuflist) + kfree(pbuflist); + return NULL; + } + + INIT_LIST_HEAD(&pbuflist->list); + + icmd->un.elsreq64.bdl.addrHigh = putPaddrHigh(pbuflist->phys); + icmd->un.elsreq64.bdl.addrLow = putPaddrLow(pbuflist->phys); + icmd->un.elsreq64.bdl.bdeFlags = BUFF_TYPE_BDL; + if (expectRsp) { + icmd->un.elsreq64.bdl.bdeSize = (2 * sizeof (struct ulp_bde64)); + icmd->un.elsreq64.remoteID = ndlp->nlp_DID; /* DID */ + icmd->ulpCommand = CMD_ELS_REQUEST64_CR; + } else { + icmd->un.elsreq64.bdl.bdeSize = sizeof (struct ulp_bde64); + icmd->ulpCommand = CMD_XMIT_ELS_RSP64_CX; + } + + /* NOTE: we don't use ulpIoTag0 because it is a t2 structure */ + tag = lpfc_sli_next_iotag(phba, pring); + icmd->ulpIoTag = (uint16_t)(tag & 0xffff); + icmd->un.elsreq64.bdl.ulpIoTag32 = tag; + icmd->ulpBdeCount = 1; + icmd->ulpLe = 1; + icmd->ulpClass = CLASS3; + + bpl = (struct ulp_bde64 *) pbuflist->virt; + bpl->addrLow = le32_to_cpu(putPaddrLow(pcmd->phys)); + bpl->addrHigh = le32_to_cpu(putPaddrHigh(pcmd->phys)); + bpl->tus.f.bdeSize = cmdSize; + bpl->tus.f.bdeFlags = 0; + bpl->tus.w = le32_to_cpu(bpl->tus.w); + + if (expectRsp) { + bpl++; + bpl->addrLow = le32_to_cpu(putPaddrLow(prsp->phys)); + bpl->addrHigh = le32_to_cpu(putPaddrHigh(prsp->phys)); + bpl->tus.f.bdeSize = FCELSSIZE; + bpl->tus.f.bdeFlags = BUFF_USE_RCV; + bpl->tus.w = le32_to_cpu(bpl->tus.w); + } + + /* Save for completion so we can release these resources */ + elsiocb->context1 = (uint8_t *) ndlp; + elsiocb->context2 = (uint8_t *) pcmd; + elsiocb->context3 = (uint8_t *) pbuflist; + elsiocb->retry = retry; + elsiocb->drvrTimeout = (phba->fc_ratov << 1) + LPFC_DRVR_TIMEOUT; + + if (prsp) { + list_add(&prsp->list, &pcmd->list); + } + + /* The els iocb is fully initialize. Flush it to main store for the + * HBA. Note that all els iocb context buffer are from the driver's + * dma pool and have length LPFC_BPL_SIZE. Get a short-hand pointer to + * the physical address. + */ + pci_dma_sync_single_for_device(phba->pcidev, pbuflist->phys, + LPFC_BPL_SIZE, PCI_DMA_TODEVICE); + + if (expectRsp) { + /* Xmit ELS command to remote NPORT */ + lpfc_printf_log(phba, KERN_INFO, LOG_ELS, + "%d:0116 Xmit ELS command x%x to remote " + "NPORT x%x Data: x%x x%x\n", + phba->brd_no, elscmd, + ndlp->nlp_DID, icmd->ulpIoTag, phba->hba_state); + } else { + /* Xmit ELS response to remote NPORT */ + lpfc_printf_log(phba, KERN_INFO, LOG_ELS, + "%d:0117 Xmit ELS response x%x to remote " + "NPORT x%x Data: x%x x%x\n", + phba->brd_no, elscmd, + ndlp->nlp_DID, icmd->ulpIoTag, cmdSize); + } + + return (elsiocb); +} + +static void +lpfc_cmpl_els_flogi(struct lpfc_hba * phba, + struct lpfc_iocbq * cmdiocb, struct lpfc_iocbq * rspiocb) +{ + IOCB_t *irsp; + struct lpfc_dmabuf *pcmd, *prsp; + struct serv_parm *sp; + uint32_t *lp; + LPFC_MBOXQ_t *mbox; + struct lpfc_sli *psli; + struct lpfc_nodelist *ndlp; + int rc; + + psli = &phba->sli; + irsp = &(rspiocb->iocb); + ndlp = (struct lpfc_nodelist *) cmdiocb->context1; + pcmd = (struct lpfc_dmabuf *) cmdiocb->context2; + + /* Check to see if link went down during discovery */ + if (lpfc_els_chk_latt(phba)) { + lpfc_nlp_remove(phba, ndlp); + goto out; + } + + if (irsp->ulpStatus) { + /* Check for retry */ + if (lpfc_els_retry(phba, cmdiocb, rspiocb)) { + /* ELS command is being retried */ + goto out; + } + /* FLOGI failed, so there is no fabric */ + phba->fc_flag &= ~(FC_FABRIC | FC_PUBLIC_LOOP); + + /* If private loop, then allow max outstandting els to be + * LPFC_MAX_DISC_THREADS (32). Scanning in the case of no + * alpa map would take too long otherwise. + */ + if (phba->alpa_map[0] == 0) { + phba->cfg_discovery_threads = + LPFC_MAX_DISC_THREADS; + } + + /* FLOGI failure */ + lpfc_printf_log(phba, + KERN_INFO, + LOG_ELS, + "%d:0100 FLOGI failure Data: x%x x%x\n", + phba->brd_no, + irsp->ulpStatus, irsp->un.ulpWord[4]); + } else { + /* The FLogI succeeded. Sync the data for the CPU before + * accessing it. + */ + prsp = (struct lpfc_dmabuf *) pcmd->list.next; + lp = (uint32_t *) prsp->virt; + + /* The HBA populated the response buffer. Flush cpu cache to + * before the driver touches this memory. + */ + pci_dma_sync_single_for_cpu(phba->pcidev, prsp->phys, + LPFC_BPL_SIZE, PCI_DMA_FROMDEVICE); + sp = (struct serv_parm *) ((uint8_t *) lp + sizeof (uint32_t)); + + /* FLOGI completes successfully */ + lpfc_printf_log(phba, KERN_INFO, LOG_ELS, + "%d:0101 FLOGI completes sucessfully " + "Data: x%x x%x x%x x%x\n", + phba->brd_no, + irsp->un.ulpWord[4], sp->cmn.e_d_tov, + sp->cmn.w2.r_a_tov, sp->cmn.edtovResolution); + + if (phba->hba_state == LPFC_FLOGI) { + /* If Common Service Parameters indicate Nport + * we are point to point, if Fport we are Fabric. + */ + if (sp->cmn.fPort) { + phba->fc_flag |= FC_FABRIC; + if (sp->cmn.edtovResolution) { + /* E_D_TOV ticks are in nanoseconds */ + phba->fc_edtov = + (be32_to_cpu(sp->cmn.e_d_tov) + + 999999) / 1000000; + } else { + /* E_D_TOV ticks are in milliseconds */ + phba->fc_edtov = + be32_to_cpu(sp->cmn.e_d_tov); + } + phba->fc_ratov = + (be32_to_cpu(sp->cmn.w2.r_a_tov) + + 999) / 1000; + + if (phba->fc_topology == TOPOLOGY_LOOP) { + phba->fc_flag |= FC_PUBLIC_LOOP; + } else { + /* If we are a N-port connected to a + * Fabric, fixup sparam's so logins to + * devices on remote loops work. + */ + phba->fc_sparam.cmn.altBbCredit = 1; + } + + phba->fc_myDID = irsp->un.ulpWord[4] & Mask_DID; + + memcpy(&ndlp->nlp_portname, &sp->portName, + sizeof (struct lpfc_name)); + memcpy(&ndlp->nlp_nodename, &sp->nodeName, + sizeof (struct lpfc_name)); + memcpy(&phba->fc_fabparam, sp, + sizeof (struct serv_parm)); + if ((mbox = mempool_alloc(phba->mbox_mem_pool, + GFP_ATOMIC)) == 0) { + goto flogifail; + } + phba->hba_state = LPFC_FABRIC_CFG_LINK; + lpfc_config_link(phba, mbox); + if (lpfc_sli_issue_mbox + (phba, mbox, (MBX_NOWAIT | MBX_STOP_IOCB)) + == MBX_NOT_FINISHED) { + mempool_free(mbox, phba->mbox_mem_pool); + goto flogifail; + } + + if ((mbox = mempool_alloc(phba->mbox_mem_pool, + GFP_ATOMIC)) == 0) { + goto flogifail; + } + if (lpfc_reg_login(phba, Fabric_DID, + (uint8_t *) sp, mbox, + 0) == 0) { + /* set_slim mailbox command needs to + * execute first, queue this command to + * be processed later. + */ + mbox->mbox_cmpl = + lpfc_mbx_cmpl_fabric_reg_login; + mbox->context2 = ndlp; + if (lpfc_sli_issue_mbox + (phba, mbox, + (MBX_NOWAIT | MBX_STOP_IOCB)) + == MBX_NOT_FINISHED) { + mempool_free(mbox, + phba->mbox_mem_pool); + goto flogifail; + } + } else { + mempool_free(mbox, phba->mbox_mem_pool); + goto flogifail; + } + } else { + /* We FLOGIed into an NPort, initiate pt2pt + protocol */ + phba->fc_flag &= ~(FC_FABRIC | FC_PUBLIC_LOOP); + phba->fc_edtov = FF_DEF_EDTOV; + phba->fc_ratov = FF_DEF_RATOV; + rc = memcmp(&phba->fc_portname, &sp->portName, + sizeof(struct lpfc_name)); + if (rc >= 0) { + /* This side will initiate the PLOGI */ + phba->fc_flag |= FC_PT2PT_PLOGI; + + /* N_Port ID cannot be 0, set our to + * LocalID the other side will be + * RemoteID. + */ + + /* not equal */ + if (rc) + phba->fc_myDID = PT2PT_LocalID; + + if ((mbox = + mempool_alloc(phba->mbox_mem_pool, + GFP_ATOMIC)) + == 0) { + goto flogifail; + } + lpfc_config_link(phba, mbox); + if (lpfc_sli_issue_mbox + (phba, mbox, + (MBX_NOWAIT | MBX_STOP_IOCB)) + == MBX_NOT_FINISHED) { + mempool_free(mbox, + phba->mbox_mem_pool); + goto flogifail; + } + mempool_free( ndlp, phba->nlp_mem_pool); + + if ((ndlp = + lpfc_findnode_did(phba, + NLP_SEARCH_ALL, + PT2PT_RemoteID)) + == 0) { + /* Cannot find existing Fabric + ndlp, so allocate a new + one */ + if ((ndlp = + mempool_alloc( + phba->nlp_mem_pool, + GFP_ATOMIC)) == 0) { + goto flogifail; + } + lpfc_nlp_init(phba, ndlp, + PT2PT_RemoteID); + } + memcpy(&ndlp->nlp_portname, + &sp->portName, + sizeof (struct lpfc_name)); + memcpy(&ndlp->nlp_nodename, + &sp->nodeName, + sizeof (struct lpfc_name)); + ndlp->nlp_state = NLP_STE_NPR_NODE; + lpfc_nlp_list(phba, ndlp, NLP_NPR_LIST); + ndlp->nlp_flag |= NLP_NPR_2B_DISC; + } + else { + /* This side will wait for the PLOGI */ + mempool_free( ndlp, phba->nlp_mem_pool); + } + + phba->fc_flag |= FC_PT2PT; + + /* Start discovery - this should just do + CLEAR_LA */ + lpfc_disc_start(phba); + } + goto out; + } + } + +flogifail: + lpfc_nlp_remove(phba, ndlp); + + if((irsp->ulpStatus != IOSTAT_LOCAL_REJECT) || + ((irsp->un.ulpWord[4] != IOERR_SLI_ABORTED) && + (irsp->un.ulpWord[4] != IOERR_SLI_DOWN))) { + + /* FLOGI failed, so just use loop map to make discovery list */ + lpfc_disc_list_loopmap(phba); + + /* Start discovery */ + lpfc_disc_start(phba); + } + +out: + lpfc_els_free_iocb(phba, cmdiocb); + return; +} + +static int +lpfc_issue_els_flogi(struct lpfc_hba * phba, struct lpfc_nodelist * ndlp, + uint8_t retry) +{ + struct serv_parm *sp; + IOCB_t *icmd; + struct lpfc_iocbq *elsiocb; + struct lpfc_sli_ring *pring; + struct lpfc_sli *psli; + struct lpfc_dmabuf *bmp; + uint8_t *pcmd; + uint16_t cmdsize; + uint32_t tmo; + + psli = &phba->sli; + pring = &psli->ring[LPFC_ELS_RING]; /* ELS ring */ + + cmdsize = (sizeof (uint32_t) + sizeof (struct serv_parm)); + if ((elsiocb = lpfc_prep_els_iocb(phba, 1, cmdsize, retry, + ndlp, ELS_CMD_FLOGI)) == 0) { + return (1); + } + + icmd = &elsiocb->iocb; + pcmd = (uint8_t *) (((struct lpfc_dmabuf *) elsiocb->context2)->virt); + + /* For FLOGI request, remainder of payload is service parameters */ + *((uint32_t *) (pcmd)) = ELS_CMD_FLOGI; + pcmd += sizeof (uint32_t); + memcpy(pcmd, &phba->fc_sparam, sizeof (struct serv_parm)); + sp = (struct serv_parm *) pcmd; + + /* Setup CSPs accordingly for Fabric */ + sp->cmn.e_d_tov = 0; + sp->cmn.w2.r_a_tov = 0; + sp->cls1.classValid = 0; + sp->cls2.seqDelivery = 1; + sp->cls3.seqDelivery = 1; + if (sp->cmn.fcphLow < FC_PH3) + sp->cmn.fcphLow = FC_PH3; + if (sp->cmn.fcphHigh < FC_PH3) + sp->cmn.fcphHigh = FC_PH3; + + tmo = phba->fc_ratov; + phba->fc_ratov = LPFC_DISC_FLOGI_TMO; + lpfc_set_disctmo(phba); + phba->fc_ratov = tmo; + + /* Flush the els buffer to main store for the HBA. This context always + * comes from the driver's dma pool and is always LPFC_BPL_SIZE. + */ + bmp = (struct lpfc_dmabuf *) (elsiocb->context2); + pci_dma_sync_single_for_device(phba->pcidev, bmp->phys, + LPFC_BPL_SIZE, PCI_DMA_TODEVICE); + + phba->fc_stat.elsXmitFLOGI++; + elsiocb->iocb_cmpl = lpfc_cmpl_els_flogi; + if (lpfc_sli_issue_iocb(phba, pring, elsiocb, 0) == IOCB_ERROR) { + lpfc_els_free_iocb(phba, elsiocb); + return (1); + } + return (0); +} + +int +lpfc_els_abort_flogi(struct lpfc_hba * phba) +{ + struct lpfc_sli *psli; + struct lpfc_sli_ring *pring; + struct lpfc_iocbq *iocb, *next_iocb; + struct lpfc_nodelist *ndlp; + IOCB_t *icmd; + struct list_head *curr, *next; + + /* Abort outstanding I/O on NPort */ + lpfc_printf_log(phba, KERN_INFO, LOG_DISCOVERY, + "%d:0201 Abort outstanding I/O on NPort x%x\n", + phba->brd_no, Fabric_DID); + + psli = &phba->sli; + pring = &psli->ring[LPFC_ELS_RING]; + + /* check the txcmplq */ + list_for_each_safe(curr, next, &pring->txcmplq) { + next_iocb = list_entry(curr, struct lpfc_iocbq, list); + iocb = next_iocb; + /* Check to see if iocb matches the nport we are + looking for */ + icmd = &iocb->iocb; + if (icmd->ulpCommand == CMD_ELS_REQUEST64_CR) { + ndlp = (struct lpfc_nodelist *)(iocb->context1); + if(ndlp && (ndlp->nlp_DID == Fabric_DID)) { + /* It matches, so deque and call compl + with an error */ + list_del(&iocb->list); + pring->txcmplq_cnt--; + + if ((icmd->un.elsreq64.bdl.ulpIoTag32)) { + lpfc_sli_issue_abort_iotag32 + (phba, pring, iocb); + } + if (iocb->iocb_cmpl) { + icmd->ulpStatus = + IOSTAT_LOCAL_REJECT; + icmd->un.ulpWord[4] = + IOERR_SLI_ABORTED; + (iocb->iocb_cmpl) (phba, iocb, iocb); + } else { + mempool_free(iocb, phba->iocb_mem_pool); + } + } + } + } + return (0); +} + +int +lpfc_initial_flogi(struct lpfc_hba * phba) +{ + struct lpfc_nodelist *ndlp; + + /* First look for Fabric ndlp on the unmapped list */ + + if ((ndlp = + lpfc_findnode_did(phba, NLP_SEARCH_UNMAPPED, + Fabric_DID)) == 0) { + /* Cannot find existing Fabric ndlp, so allocate a new one */ + if ((ndlp = mempool_alloc(phba->nlp_mem_pool, GFP_ATOMIC)) + == 0) { + return (0); + } + lpfc_nlp_init(phba, ndlp, Fabric_DID); + } + else { + phba->fc_unmap_cnt--; + list_del(&ndlp->nlp_listp); + ndlp->nlp_flag &= ~NLP_LIST_MASK; + } + if (lpfc_issue_els_flogi(phba, ndlp, 0)) { + mempool_free( ndlp, phba->nlp_mem_pool); + } + return (1); +} + +static void +lpfc_more_plogi(struct lpfc_hba * phba) +{ + int sentplogi; + + if (phba->num_disc_nodes) + phba->num_disc_nodes--; + + /* Continue discovery with PLOGIs to go */ + lpfc_printf_log(phba, KERN_INFO, LOG_DISCOVERY, + "%d:0232 Continue discovery with %d PLOGIs to go " + "Data: x%x x%x x%x\n", + phba->brd_no, phba->num_disc_nodes, phba->fc_plogi_cnt, + phba->fc_flag, phba->hba_state); + + /* Check to see if there are more PLOGIs to be sent */ + if (phba->fc_flag & FC_NLP_MORE) { + /* go thru NPR list and issue any remaining ELS PLOGIs */ + sentplogi = lpfc_els_disc_plogi(phba); + } + return; +} + +static void +lpfc_cmpl_els_plogi(struct lpfc_hba * phba, struct lpfc_iocbq * cmdiocb, + struct lpfc_iocbq * rspiocb) +{ + IOCB_t *irsp; + struct lpfc_sli *psli; + struct lpfc_nodelist *ndlp; + int disc, rc, did, type; + + psli = &phba->sli; + + /* we pass cmdiocb to state machine which needs rspiocb as well */ + cmdiocb->context_un.rsp_iocb = rspiocb; + + irsp = &rspiocb->iocb; + ndlp = (struct lpfc_nodelist *) cmdiocb->context1; + ndlp->nlp_flag &= ~NLP_PLOGI_SND; + + /* Since ndlp can be freed in the disc state machine, note if this node + * is being used during discovery. + */ + disc = (ndlp->nlp_flag & NLP_NPR_2B_DISC); + rc = 0; + + /* PLOGI completes to NPort */ + lpfc_printf_log(phba, KERN_INFO, LOG_ELS, + "%d:0102 PLOGI completes to NPort x%x " + "Data: x%x x%x x%x x%x\n", + phba->brd_no, ndlp->nlp_DID, irsp->ulpStatus, + irsp->un.ulpWord[4], disc, phba->num_disc_nodes); + + /* Check to see if link went down during discovery */ + if (lpfc_els_chk_latt(phba)) { + ndlp->nlp_flag |= NLP_NPR_2B_DISC; + goto out; + } + + /* ndlp could be freed in DSM, save these values now */ + type = ndlp->nlp_type; + did = ndlp->nlp_DID; + + if (irsp->ulpStatus) { + /* Check for retry */ + if (lpfc_els_retry(phba, cmdiocb, rspiocb)) { + /* ELS command is being retried */ + if (disc) { + ndlp->nlp_flag |= NLP_NPR_2B_DISC; + } + goto out; + } + + /* PLOGI failed */ + /* Do not call DSM for lpfc_els_abort'ed ELS cmds */ + if((irsp->ulpStatus == IOSTAT_LOCAL_REJECT) && + ((irsp->un.ulpWord[4] == IOERR_SLI_ABORTED) || + (irsp->un.ulpWord[4] == IOERR_SLI_DOWN))) { + disc = (ndlp->nlp_flag & NLP_NPR_2B_DISC); + } + else { + rc = lpfc_disc_state_machine(phba, ndlp, cmdiocb, + NLP_EVT_CMPL_PLOGI); + } + } else { + /* Good status, call state machine */ + rc = lpfc_disc_state_machine(phba, ndlp, cmdiocb, + NLP_EVT_CMPL_PLOGI); + } + + if(type & NLP_FABRIC) { + /* If we cannot login to Nameserver, kick off discovery now */ + if ((did == NameServer_DID) && (rc == NLP_STE_FREED_NODE)) { + lpfc_disc_start(phba); + } + goto out; + } + + if (disc && phba->num_disc_nodes) { + /* Check to see if there are more PLOGIs to be sent */ + lpfc_more_plogi(phba); + } + + if (rc != NLP_STE_FREED_NODE) + ndlp->nlp_flag &= ~NLP_NPR_2B_DISC; + + if (phba->num_disc_nodes == 0) { + lpfc_can_disctmo(phba); + if (phba->fc_flag & FC_RSCN_MODE) { + /* Check to see if more RSCNs came in while we were + * processing this one. + */ + if ((phba->fc_rscn_id_cnt == 0) && + (!(phba->fc_flag & FC_RSCN_DISCOVERY))) { + lpfc_els_flush_rscn(phba); + } else { + lpfc_els_handle_rscn(phba); + } + } + } + +out: + lpfc_els_free_iocb(phba, cmdiocb); + return; +} + +int +lpfc_issue_els_plogi(struct lpfc_hba * phba, struct lpfc_nodelist * ndlp, + uint8_t retry) +{ + struct serv_parm *sp; + IOCB_t *icmd; + struct lpfc_iocbq *elsiocb; + struct lpfc_sli_ring *pring; + struct lpfc_sli *psli; + struct lpfc_dmabuf *bmp; + uint8_t *pcmd; + uint16_t cmdsize; + + psli = &phba->sli; + pring = &psli->ring[LPFC_ELS_RING]; /* ELS ring */ + + cmdsize = (sizeof (uint32_t) + sizeof (struct serv_parm)); + if ((elsiocb = lpfc_prep_els_iocb(phba, 1, cmdsize, retry, + ndlp, ELS_CMD_PLOGI)) == 0) { + return (1); + } + + icmd = &elsiocb->iocb; + pcmd = (uint8_t *) (((struct lpfc_dmabuf *) elsiocb->context2)->virt); + + /* For PLOGI request, remainder of payload is service parameters */ + *((uint32_t *) (pcmd)) = ELS_CMD_PLOGI; + pcmd += sizeof (uint32_t); + memcpy(pcmd, &phba->fc_sparam, sizeof (struct serv_parm)); + sp = (struct serv_parm *) pcmd; + + if (sp->cmn.fcphLow < FC_PH_4_3) + sp->cmn.fcphLow = FC_PH_4_3; + + if (sp->cmn.fcphHigh < FC_PH3) + sp->cmn.fcphHigh = FC_PH3; + + /* The lpfc iocb is fully initialize. Flush it to main store for the + * HBA. Note that all els iocb context buffer are from the driver's + * dma pool and have length LPFC_BPL_SIZE. + */ + bmp = (struct lpfc_dmabuf *) (elsiocb->context2); + pci_dma_sync_single_for_device(phba->pcidev, bmp->phys, + LPFC_BPL_SIZE, PCI_DMA_TODEVICE); + + phba->fc_stat.elsXmitPLOGI++; + elsiocb->iocb_cmpl = lpfc_cmpl_els_plogi; + ndlp->nlp_flag |= NLP_PLOGI_SND; + if (lpfc_sli_issue_iocb(phba, pring, elsiocb, 0) == IOCB_ERROR) { + ndlp->nlp_flag &= ~NLP_PLOGI_SND; + lpfc_els_free_iocb(phba, elsiocb); + return (1); + } + return (0); +} + +static void +lpfc_cmpl_els_prli(struct lpfc_hba * phba, struct lpfc_iocbq * cmdiocb, + struct lpfc_iocbq * rspiocb) +{ + IOCB_t *irsp; + struct lpfc_sli *psli; + struct lpfc_nodelist *ndlp; + + psli = &phba->sli; + /* we pass cmdiocb to state machine which needs rspiocb as well */ + cmdiocb->context_un.rsp_iocb = rspiocb; + + irsp = &(rspiocb->iocb); + ndlp = (struct lpfc_nodelist *) cmdiocb->context1; + ndlp->nlp_flag &= ~NLP_PRLI_SND; + + /* PRLI completes to NPort */ + lpfc_printf_log(phba, KERN_INFO, LOG_ELS, + "%d:0103 PRLI completes to NPort x%x " + "Data: x%x x%x x%x\n", + phba->brd_no, ndlp->nlp_DID, irsp->ulpStatus, + irsp->un.ulpWord[4], phba->num_disc_nodes); + + phba->fc_prli_sent--; + /* Check to see if link went down during discovery */ + if (lpfc_els_chk_latt(phba)) + goto out; + + if (irsp->ulpStatus) { + /* Check for retry */ + if (lpfc_els_retry(phba, cmdiocb, rspiocb)) { + /* ELS command is being retried */ + goto out; + } + /* PRLI failed */ + /* Do not call DSM for lpfc_els_abort'ed ELS cmds */ + if((irsp->ulpStatus == IOSTAT_LOCAL_REJECT) && + ((irsp->un.ulpWord[4] == IOERR_SLI_ABORTED) || + (irsp->un.ulpWord[4] == IOERR_SLI_DOWN))) { + goto out; + } + else { + lpfc_disc_state_machine(phba, ndlp, cmdiocb, + NLP_EVT_CMPL_PRLI); + } + } else { + /* Good status, call state machine */ + lpfc_disc_state_machine(phba, ndlp, cmdiocb, NLP_EVT_CMPL_PRLI); + } + +out: + lpfc_els_free_iocb(phba, cmdiocb); + return; +} + +int +lpfc_issue_els_prli(struct lpfc_hba * phba, struct lpfc_nodelist * ndlp, + uint8_t retry) +{ + PRLI *npr; + IOCB_t *icmd; + struct lpfc_iocbq *elsiocb; + struct lpfc_sli_ring *pring; + struct lpfc_sli *psli; + struct lpfc_dmabuf *bmp; + uint8_t *pcmd; + uint16_t cmdsize; + + psli = &phba->sli; + pring = &psli->ring[LPFC_ELS_RING]; /* ELS ring */ + + cmdsize = (sizeof (uint32_t) + sizeof (PRLI)); + if ((elsiocb = lpfc_prep_els_iocb(phba, 1, cmdsize, retry, + ndlp, ELS_CMD_PRLI)) == 0) { + return (1); + } + + icmd = &elsiocb->iocb; + pcmd = (uint8_t *) (((struct lpfc_dmabuf *) elsiocb->context2)->virt); + + /* For PRLI request, remainder of payload is service parameters */ + memset(pcmd, 0, (sizeof (PRLI) + sizeof (uint32_t))); + *((uint32_t *) (pcmd)) = ELS_CMD_PRLI; + pcmd += sizeof (uint32_t); + + /* For PRLI, remainder of payload is PRLI parameter page */ + npr = (PRLI *) pcmd; + /* + * If our firmware version is 3.20 or later, + * set the following bits for FC-TAPE support. + */ + if (phba->vpd.rev.feaLevelHigh >= 0x02) { + npr->ConfmComplAllowed = 1; + npr->Retry = 1; + npr->TaskRetryIdReq = 1; + } + npr->estabImagePair = 1; + npr->readXferRdyDis = 1; + + /* For FCP support */ + npr->prliType = PRLI_FCP_TYPE; + npr->initiatorFunc = 1; + + /* The lpfc iocb is fully initialize. Flush it to main store for the + * HBA. Note that all els iocb context buffer are from the driver's + * dma pool and have length LPFC_BPL_SIZE. + */ + bmp = (struct lpfc_dmabuf *) (elsiocb->context2); + pci_dma_sync_single_for_device(phba->pcidev, bmp->phys, + LPFC_BPL_SIZE, PCI_DMA_TODEVICE); + + phba->fc_stat.elsXmitPRLI++; + elsiocb->iocb_cmpl = lpfc_cmpl_els_prli; + ndlp->nlp_flag |= NLP_PRLI_SND; + if (lpfc_sli_issue_iocb(phba, pring, elsiocb, 0) == IOCB_ERROR) { + ndlp->nlp_flag &= ~NLP_PRLI_SND; + lpfc_els_free_iocb(phba, elsiocb); + return (1); + } + phba->fc_prli_sent++; + return (0); +} + +static void +lpfc_more_adisc(struct lpfc_hba * phba) +{ + int sentadisc; + + if (phba->num_disc_nodes) + phba->num_disc_nodes--; + + /* Continue discovery with ADISCs to go */ + lpfc_printf_log(phba, KERN_INFO, LOG_DISCOVERY, + "%d:0210 Continue discovery with %d ADISCs to go " + "Data: x%x x%x x%x\n", + phba->brd_no, phba->num_disc_nodes, phba->fc_adisc_cnt, + phba->fc_flag, phba->hba_state); + + /* Check to see if there are more ADISCs to be sent */ + if (phba->fc_flag & FC_NLP_MORE) { + lpfc_set_disctmo(phba); + + /* go thru NPR list and issue any remaining ELS ADISCs */ + sentadisc = lpfc_els_disc_adisc(phba); + } + return; +} + +static void +lpfc_rscn_disc(struct lpfc_hba * phba) +{ + /* RSCN discovery */ + /* go thru NPR list and issue ELS PLOGIs */ + if (phba->fc_npr_cnt) { + lpfc_els_disc_plogi(phba); + return; + } + if (phba->fc_flag & FC_RSCN_MODE) { + /* Check to see if more RSCNs came in while we were + * processing this one. + */ + if ((phba->fc_rscn_id_cnt == 0) && + (!(phba->fc_flag & FC_RSCN_DISCOVERY))) { + lpfc_els_flush_rscn(phba); + } else { + lpfc_els_handle_rscn(phba); + } + } +} + +static void +lpfc_cmpl_els_adisc(struct lpfc_hba * phba, struct lpfc_iocbq * cmdiocb, + struct lpfc_iocbq * rspiocb) +{ + IOCB_t *irsp; + struct lpfc_sli *psli; + struct lpfc_nodelist *ndlp; + LPFC_MBOXQ_t *mbox; + int disc; + + psli = &phba->sli; + + /* we pass cmdiocb to state machine which needs rspiocb as well */ + cmdiocb->context_un.rsp_iocb = rspiocb; + + irsp = &(rspiocb->iocb); + ndlp = (struct lpfc_nodelist *) cmdiocb->context1; + ndlp->nlp_flag &= ~NLP_ADISC_SND; + + /* Since ndlp can be freed in the disc state machine, note if this node + * is being used during discovery. + */ + disc = (ndlp->nlp_flag & NLP_NPR_2B_DISC); + + /* ADISC completes to NPort */ + lpfc_printf_log(phba, KERN_INFO, LOG_ELS, + "%d:0104 ADISC completes to NPort x%x " + "Data: x%x x%x x%x x%x\n", + phba->brd_no, ndlp->nlp_DID, irsp->ulpStatus, + irsp->un.ulpWord[4], disc, phba->num_disc_nodes); + + /* Check to see if link went down during discovery */ + if (lpfc_els_chk_latt(phba)) { + ndlp->nlp_flag |= NLP_NPR_2B_DISC; + goto out; + } + + if (irsp->ulpStatus) { + /* Check for retry */ + if (lpfc_els_retry(phba, cmdiocb, rspiocb)) { + /* ELS command is being retried */ + if (disc) { + ndlp->nlp_flag |= NLP_NPR_2B_DISC; + lpfc_set_disctmo(phba); + } + goto out; + } + /* ADISC failed */ + /* Do not call DSM for lpfc_els_abort'ed ELS cmds */ + if((irsp->ulpStatus == IOSTAT_LOCAL_REJECT) && + ((irsp->un.ulpWord[4] == IOERR_SLI_ABORTED) || + (irsp->un.ulpWord[4] == IOERR_SLI_DOWN))) { + disc = (ndlp->nlp_flag & NLP_NPR_2B_DISC); + } + else { + lpfc_disc_state_machine(phba, ndlp, cmdiocb, + NLP_EVT_CMPL_ADISC); + } + } else { + /* Good status, call state machine */ + lpfc_disc_state_machine(phba, ndlp, cmdiocb, + NLP_EVT_CMPL_ADISC); + } + + if (disc && phba->num_disc_nodes) { + /* Check to see if there are more ADISCs to be sent */ + lpfc_more_adisc(phba); + + /* Check to see if we are done with ADISC authentication */ + if (phba->num_disc_nodes == 0) { + /* If we get here, there is nothing left to wait for */ + if ((phba->hba_state < LPFC_HBA_READY) && + (phba->hba_state != LPFC_CLEAR_LA)) { + /* Link up discovery */ + if ((mbox = mempool_alloc(phba->mbox_mem_pool, + GFP_ATOMIC))) { + phba->hba_state = LPFC_CLEAR_LA; + lpfc_clear_la(phba, mbox); + mbox->mbox_cmpl = + lpfc_mbx_cmpl_clear_la; + if (lpfc_sli_issue_mbox + (phba, mbox, + (MBX_NOWAIT | MBX_STOP_IOCB)) + == MBX_NOT_FINISHED) { + mempool_free(mbox, + phba->mbox_mem_pool); + lpfc_disc_flush_list(phba); + psli->ring[(psli->ip_ring)]. + flag &= + ~LPFC_STOP_IOCB_EVENT; + psli->ring[(psli->fcp_ring)]. + flag &= + ~LPFC_STOP_IOCB_EVENT; + psli->ring[(psli->next_ring)]. + flag &= + ~LPFC_STOP_IOCB_EVENT; + phba->hba_state = + LPFC_HBA_READY; + } + } + } else { + lpfc_rscn_disc(phba); + } + } + } + ndlp->nlp_flag &= ~NLP_NPR_2B_DISC; +out: + lpfc_els_free_iocb(phba, cmdiocb); + return; +} + +int +lpfc_issue_els_adisc(struct lpfc_hba * phba, struct lpfc_nodelist * ndlp, + uint8_t retry) +{ + ADISC *ap; + IOCB_t *icmd; + struct lpfc_iocbq *elsiocb; + struct lpfc_sli_ring *pring; + struct lpfc_sli *psli; + struct lpfc_dmabuf *bmp; + uint8_t *pcmd; + uint16_t cmdsize; + + psli = &phba->sli; + pring = &psli->ring[LPFC_ELS_RING]; /* ELS ring */ + + cmdsize = (sizeof (uint32_t) + sizeof (ADISC)); + if ((elsiocb = lpfc_prep_els_iocb(phba, 1, cmdsize, retry, + ndlp, ELS_CMD_ADISC)) == 0) { + return (1); + } + + icmd = &elsiocb->iocb; + pcmd = (uint8_t *) (((struct lpfc_dmabuf *) elsiocb->context2)->virt); + + /* For ADISC request, remainder of payload is service parameters */ + *((uint32_t *) (pcmd)) = ELS_CMD_ADISC; + pcmd += sizeof (uint32_t); + + /* Fill in ADISC payload */ + ap = (ADISC *) pcmd; + ap->hardAL_PA = phba->fc_pref_ALPA; + memcpy(&ap->portName, &phba->fc_portname, sizeof (struct lpfc_name)); + memcpy(&ap->nodeName, &phba->fc_nodename, sizeof (struct lpfc_name)); + ap->DID = be32_to_cpu(phba->fc_myDID); + + /* The lpfc iocb is fully initialize. Flush it to main store for the + * HBA. Note that all els iocb context buffer are from the driver's + * dma pool and have length LPFC_BPL_SIZE. Get a short-hand pointer to + * the physical address. + */ + bmp = (struct lpfc_dmabuf *) (elsiocb->context2); + pci_dma_sync_single_for_device(phba->pcidev, bmp->phys, + LPFC_BPL_SIZE, PCI_DMA_TODEVICE); + + phba->fc_stat.elsXmitADISC++; + elsiocb->iocb_cmpl = lpfc_cmpl_els_adisc; + ndlp->nlp_flag |= NLP_ADISC_SND; + if (lpfc_sli_issue_iocb(phba, pring, elsiocb, 0) == IOCB_ERROR) { + ndlp->nlp_flag &= ~NLP_ADISC_SND; + lpfc_els_free_iocb(phba, elsiocb); + return (1); + } + return (0); +} + +static void +lpfc_cmpl_els_logo(struct lpfc_hba * phba, struct lpfc_iocbq * cmdiocb, + struct lpfc_iocbq * rspiocb) +{ + IOCB_t *irsp; + struct lpfc_sli *psli; + struct lpfc_nodelist *ndlp; + + psli = &phba->sli; + /* we pass cmdiocb to state machine which needs rspiocb as well */ + cmdiocb->context_un.rsp_iocb = rspiocb; + + irsp = &(rspiocb->iocb); + ndlp = (struct lpfc_nodelist *) cmdiocb->context1; + ndlp->nlp_flag &= ~NLP_LOGO_SND; + + /* LOGO completes to NPort */ + lpfc_printf_log(phba, KERN_INFO, LOG_ELS, + "%d:0105 LOGO completes to NPort x%x " + "Data: x%x x%x x%x\n", + phba->brd_no, ndlp->nlp_DID, irsp->ulpStatus, + irsp->un.ulpWord[4], phba->num_disc_nodes); + + /* Check to see if link went down during discovery */ + if (lpfc_els_chk_latt(phba)) + goto out; + + if (irsp->ulpStatus) { + /* Check for retry */ + if (lpfc_els_retry(phba, cmdiocb, rspiocb)) { + /* ELS command is being retried */ + goto out; + } + /* LOGO failed */ + /* Do not call DSM for lpfc_els_abort'ed ELS cmds */ + if((irsp->ulpStatus == IOSTAT_LOCAL_REJECT) && + ((irsp->un.ulpWord[4] == IOERR_SLI_ABORTED) || + (irsp->un.ulpWord[4] == IOERR_SLI_DOWN))) { + goto out; + } + else { + lpfc_disc_state_machine(phba, ndlp, cmdiocb, + NLP_EVT_CMPL_LOGO); + } + } else { + /* Good status, call state machine */ + lpfc_disc_state_machine(phba, ndlp, cmdiocb, NLP_EVT_CMPL_LOGO); + + if(ndlp->nlp_flag & NLP_DELAY_TMO) { + lpfc_unreg_rpi(phba, ndlp); + } + } + +out: + lpfc_els_free_iocb(phba, cmdiocb); + return; +} + +int +lpfc_issue_els_logo(struct lpfc_hba * phba, struct lpfc_nodelist * ndlp, + uint8_t retry) +{ + IOCB_t *icmd; + struct lpfc_iocbq *elsiocb; + struct lpfc_sli_ring *pring; + struct lpfc_sli *psli; + struct lpfc_dmabuf *bmp; + uint8_t *pcmd; + uint16_t cmdsize; + + psli = &phba->sli; + pring = &psli->ring[LPFC_ELS_RING]; + + cmdsize = 2 * (sizeof (uint32_t) + sizeof (struct lpfc_name)); + if ((elsiocb = lpfc_prep_els_iocb(phba, 1, cmdsize, retry, + ndlp, ELS_CMD_LOGO)) == 0) { + return (1); + } + + icmd = &elsiocb->iocb; + pcmd = (uint8_t *) (((struct lpfc_dmabuf *) elsiocb->context2)->virt); + *((uint32_t *) (pcmd)) = ELS_CMD_LOGO; + pcmd += sizeof (uint32_t); + + /* Fill in LOGO payload */ + *((uint32_t *) (pcmd)) = be32_to_cpu(phba->fc_myDID); + pcmd += sizeof (uint32_t); + memcpy(pcmd, &phba->fc_portname, sizeof (struct lpfc_name)); + + /* The els iocb is fully initialize. Flush it to main store for the + * HBA. Note that all els iocb context buffer are from the driver's + * dma pool and have length LPFC_BPL_SIZE. Get a short-hand pointer to + * the physical address. + */ + bmp = (struct lpfc_dmabuf *) (elsiocb->context2); + pci_dma_sync_single_for_device(phba->pcidev, bmp->phys, + LPFC_BPL_SIZE, PCI_DMA_TODEVICE); + + phba->fc_stat.elsXmitLOGO++; + elsiocb->iocb_cmpl = lpfc_cmpl_els_logo; + ndlp->nlp_flag |= NLP_LOGO_SND; + if (lpfc_sli_issue_iocb(phba, pring, elsiocb, 0) == IOCB_ERROR) { + ndlp->nlp_flag &= ~NLP_LOGO_SND; + lpfc_els_free_iocb(phba, elsiocb); + return (1); + } + return (0); +} + +static void +lpfc_cmpl_els_cmd(struct lpfc_hba * phba, struct lpfc_iocbq * cmdiocb, + struct lpfc_iocbq * rspiocb) +{ + IOCB_t *irsp; + + irsp = &rspiocb->iocb; + + /* ELS cmd tag completes */ + lpfc_printf_log(phba, + KERN_INFO, + LOG_ELS, + "%d:0106 ELS cmd tag x%x completes Data: x%x x%x\n", + phba->brd_no, + irsp->ulpIoTag, irsp->ulpStatus, irsp->un.ulpWord[4]); + + /* Check to see if link went down during discovery */ + lpfc_els_chk_latt(phba); + lpfc_els_free_iocb(phba, cmdiocb); + return; +} + +int +lpfc_issue_els_scr(struct lpfc_hba * phba, uint32_t nportid, uint8_t retry) +{ + IOCB_t *icmd; + struct lpfc_iocbq *elsiocb; + struct lpfc_sli_ring *pring; + struct lpfc_sli *psli; + struct lpfc_dmabuf *bmp; + uint8_t *pcmd; + uint16_t cmdsize; + struct lpfc_nodelist *ndlp; + + psli = &phba->sli; + pring = &psli->ring[LPFC_ELS_RING]; /* ELS ring */ + cmdsize = (sizeof (uint32_t) + sizeof (SCR)); + if ((ndlp = mempool_alloc(phba->nlp_mem_pool, GFP_ATOMIC)) == 0) { + return (1); + } + + lpfc_nlp_init(phba, ndlp, nportid); + + if ((elsiocb = lpfc_prep_els_iocb(phba, 1, cmdsize, retry, + ndlp, ELS_CMD_SCR)) == 0) { + mempool_free( ndlp, phba->nlp_mem_pool); + return (1); + } + + icmd = &elsiocb->iocb; + pcmd = (uint8_t *) (((struct lpfc_dmabuf *) elsiocb->context2)->virt); + + *((uint32_t *) (pcmd)) = ELS_CMD_SCR; + pcmd += sizeof (uint32_t); + + /* For SCR, remainder of payload is SCR parameter page */ + memset(pcmd, 0, sizeof (SCR)); + ((SCR *) pcmd)->Function = SCR_FUNC_FULL; + + /* The els iocb is fully initialize. Flush it to main store for the + * HBA. Note that all els iocb context buffer are from the driver's + * dma pool and have length LPFC_BPL_SIZE. Get a short-hand pointer to + * the physical address. + */ + bmp = (struct lpfc_dmabuf *) (elsiocb->context2); + pci_dma_sync_single_for_device(phba->pcidev, bmp->phys, + LPFC_BPL_SIZE, PCI_DMA_TODEVICE); + + phba->fc_stat.elsXmitSCR++; + elsiocb->iocb_cmpl = lpfc_cmpl_els_cmd; + if (lpfc_sli_issue_iocb(phba, pring, elsiocb, 0) == IOCB_ERROR) { + mempool_free( ndlp, phba->nlp_mem_pool); + lpfc_els_free_iocb(phba, elsiocb); + return (1); + } + mempool_free( ndlp, phba->nlp_mem_pool); + return (0); +} + +static int +lpfc_issue_els_farpr(struct lpfc_hba * phba, uint32_t nportid, uint8_t retry) +{ + IOCB_t *icmd; + struct lpfc_iocbq *elsiocb; + struct lpfc_sli_ring *pring; + struct lpfc_sli *psli; + struct lpfc_dmabuf *bmp; + FARP *fp; + uint8_t *pcmd; + uint32_t *lp; + uint16_t cmdsize; + struct lpfc_nodelist *ondlp; + struct lpfc_nodelist *ndlp; + + psli = &phba->sli; + pring = &psli->ring[LPFC_ELS_RING]; /* ELS ring */ + cmdsize = (sizeof (uint32_t) + sizeof (FARP)); + if ((ndlp = mempool_alloc(phba->nlp_mem_pool, GFP_ATOMIC)) == 0) { + return (1); + } + lpfc_nlp_init(phba, ndlp, nportid); + + if ((elsiocb = lpfc_prep_els_iocb(phba, 1, cmdsize, retry, + ndlp, ELS_CMD_RNID)) == 0) { + mempool_free( ndlp, phba->nlp_mem_pool); + return (1); + } + + icmd = &elsiocb->iocb; + pcmd = (uint8_t *) (((struct lpfc_dmabuf *) elsiocb->context2)->virt); + + *((uint32_t *) (pcmd)) = ELS_CMD_FARPR; + pcmd += sizeof (uint32_t); + + /* Fill in FARPR payload */ + fp = (FARP *) (pcmd); + memset(fp, 0, sizeof (FARP)); + lp = (uint32_t *) pcmd; + *lp++ = be32_to_cpu(nportid); + *lp++ = be32_to_cpu(phba->fc_myDID); + fp->Rflags = 0; + fp->Mflags = (FARP_MATCH_PORT | FARP_MATCH_NODE); + + memcpy(&fp->RportName, &phba->fc_portname, sizeof (struct lpfc_name)); + memcpy(&fp->RnodeName, &phba->fc_nodename, sizeof (struct lpfc_name)); + if ((ondlp = lpfc_findnode_did(phba, NLP_SEARCH_ALL, nportid))) { + memcpy(&fp->OportName, &ondlp->nlp_portname, + sizeof (struct lpfc_name)); + memcpy(&fp->OnodeName, &ondlp->nlp_nodename, + sizeof (struct lpfc_name)); + } + + /* The els iocb is fully initialize. Flush it to main store for the + * HBA. Note that all els iocb context buffer are from the driver's + * dma pool and have length LPFC_BPL_SIZE. Get a short-hand pointer to + * the physical address. + */ + bmp = (struct lpfc_dmabuf *) (elsiocb->context2); + pci_dma_sync_single_for_device(phba->pcidev, bmp->phys, + LPFC_BPL_SIZE, PCI_DMA_TODEVICE); + + phba->fc_stat.elsXmitFARPR++; + elsiocb->iocb_cmpl = lpfc_cmpl_els_cmd; + if (lpfc_sli_issue_iocb(phba, pring, elsiocb, 0) == IOCB_ERROR) { + mempool_free( ndlp, phba->nlp_mem_pool); + lpfc_els_free_iocb(phba, elsiocb); + return (1); + } + mempool_free( ndlp, phba->nlp_mem_pool); + return (0); +} + +void +lpfc_els_retry_delay(unsigned long ptr) +{ + struct lpfc_hba *phba; + struct lpfc_nodelist *ndlp; + uint32_t cmd; + uint32_t did; + uint8_t retry; + unsigned long iflag; + + ndlp = (struct lpfc_nodelist *)ptr; + phba = ndlp->nlp_phba; + spin_lock_irqsave(phba->host->host_lock, iflag); + did = (uint32_t) (ndlp->nlp_DID); + cmd = (uint32_t) (ndlp->nlp_last_elscmd); + + ndlp->nlp_flag &= ~NLP_DELAY_TMO; + retry = ndlp->nlp_retry; + + switch (cmd) { + case ELS_CMD_FLOGI: + lpfc_issue_els_flogi(phba, ndlp, retry); + break; + case ELS_CMD_PLOGI: + ndlp->nlp_state = NLP_STE_PLOGI_ISSUE; + lpfc_nlp_list(phba, ndlp, NLP_PLOGI_LIST); + lpfc_issue_els_plogi(phba, ndlp, retry); + break; + case ELS_CMD_ADISC: + ndlp->nlp_state = NLP_STE_ADISC_ISSUE; + lpfc_nlp_list(phba, ndlp, NLP_ADISC_LIST); + lpfc_issue_els_adisc(phba, ndlp, retry); + break; + case ELS_CMD_PRLI: + ndlp->nlp_state = NLP_STE_PRLI_ISSUE; + lpfc_nlp_list(phba, ndlp, NLP_PRLI_LIST); + lpfc_issue_els_prli(phba, ndlp, retry); + break; + case ELS_CMD_LOGO: + ndlp->nlp_state = NLP_STE_NPR_NODE; + lpfc_nlp_list(phba, ndlp, NLP_NPR_LIST); + lpfc_issue_els_logo(phba, ndlp, retry); + break; + } + spin_unlock_irqrestore(phba->host->host_lock, iflag); + return; +} + +static int +lpfc_els_retry(struct lpfc_hba * phba, struct lpfc_iocbq * cmdiocb, + struct lpfc_iocbq * rspiocb) +{ + IOCB_t *irsp; + struct lpfc_dmabuf *pcmd; + struct lpfc_nodelist *ndlp; + uint32_t *elscmd; + struct ls_rjt stat; + int retry, maxretry; + int delay; + uint32_t cmd; + + retry = 0; + delay = 0; + maxretry = lpfc_max_els_tries; + irsp = &rspiocb->iocb; + ndlp = (struct lpfc_nodelist *) cmdiocb->context1; + pcmd = (struct lpfc_dmabuf *) cmdiocb->context2; + cmd = 0; + /* Note: context2 may be 0 for internal driver abort + * of delays ELS command. + */ + + if (pcmd && pcmd->virt) { + elscmd = (uint32_t *) (pcmd->virt); + cmd = *elscmd++; + } + + switch (irsp->ulpStatus) { + case IOSTAT_FCP_RSP_ERROR: + case IOSTAT_REMOTE_STOP: + break; + + case IOSTAT_LOCAL_REJECT: + switch ((irsp->un.ulpWord[4] & 0xff)) { + case IOERR_LOOP_OPEN_FAILURE: + if (cmd == ELS_CMD_PLOGI) { + if (cmdiocb->retry == 0) { + delay = 1; + } + } + retry = 1; + break; + + case IOERR_SEQUENCE_TIMEOUT: + retry = 1; + if ((cmd == ELS_CMD_FLOGI) + && (phba->fc_topology != TOPOLOGY_LOOP)) { + delay = 1; + maxretry = 48; + } + break; + + case IOERR_NO_RESOURCES: + if (cmd == ELS_CMD_PLOGI) { + delay = 1; + } + retry = 1; + break; + + case IOERR_INVALID_RPI: + retry = 1; + break; + } + break; + + case IOSTAT_NPORT_RJT: + case IOSTAT_FABRIC_RJT: + if (irsp->un.ulpWord[4] & RJT_UNAVAIL_TEMP) { + retry = 1; + break; + } + break; + + case IOSTAT_NPORT_BSY: + case IOSTAT_FABRIC_BSY: + retry = 1; + break; + + case IOSTAT_LS_RJT: + stat.un.lsRjtError = be32_to_cpu(irsp->un.ulpWord[4]); + /* Added for Vendor specifc support + * Just keep retrying for these Rsn / Exp codes + */ + switch (stat.un.b.lsRjtRsnCode) { + case LSRJT_UNABLE_TPC: + if (stat.un.b.lsRjtRsnCodeExp == + LSEXP_CMD_IN_PROGRESS) { + if (cmd == ELS_CMD_PLOGI) { + delay = 1; + maxretry = 48; + } + retry = 1; + break; + } + if (cmd == ELS_CMD_PLOGI) { + delay = 1; + maxretry = lpfc_max_els_tries + 1; + retry = 1; + break; + } + break; + + case LSRJT_LOGICAL_BSY: + if (cmd == ELS_CMD_PLOGI) { + delay = 1; + maxretry = 48; + } + retry = 1; + break; + } + break; + + case IOSTAT_INTERMED_RSP: + case IOSTAT_BA_RJT: + break; + + default: + break; + } + + if (ndlp->nlp_DID == FDMI_DID) { + retry = 1; + } + + if ((++cmdiocb->retry) >= maxretry) { + phba->fc_stat.elsRetryExceeded++; + retry = 0; + } + + if (retry) { + + /* Retry ELS command to remote NPORT */ + lpfc_printf_log(phba, KERN_INFO, LOG_ELS, + "%d:0107 Retry ELS command x%x to remote " + "NPORT x%x Data: x%x x%x\n", + phba->brd_no, + cmd, ndlp->nlp_DID, cmdiocb->retry, delay); + + if ((cmd == ELS_CMD_PLOGI) || (cmd == ELS_CMD_ADISC)) { + /* If discovery / RSCN timer is running, reset it */ + if (timer_pending(&phba->fc_disctmo) || + (phba->fc_flag & FC_RSCN_MODE)) { + lpfc_set_disctmo(phba); + } + } + + phba->fc_stat.elsXmitRetry++; + if (delay) { + phba->fc_stat.elsDelayRetry++; + ndlp->nlp_retry = cmdiocb->retry; + + mod_timer(&ndlp->nlp_delayfunc, jiffies + HZ); + ndlp->nlp_flag |= NLP_DELAY_TMO; + + ndlp->nlp_state = NLP_STE_NPR_NODE; + lpfc_nlp_list(phba, ndlp, NLP_NPR_LIST); + ndlp->nlp_last_elscmd = cmd; + + return (1); + } + switch (cmd) { + case ELS_CMD_FLOGI: + lpfc_issue_els_flogi(phba, ndlp, cmdiocb->retry); + return (1); + case ELS_CMD_PLOGI: + ndlp->nlp_state = NLP_STE_PLOGI_ISSUE; + lpfc_nlp_list(phba, ndlp, NLP_PLOGI_LIST); + lpfc_issue_els_plogi(phba, ndlp, cmdiocb->retry); + return (1); + case ELS_CMD_ADISC: + ndlp->nlp_state = NLP_STE_ADISC_ISSUE; + lpfc_nlp_list(phba, ndlp, NLP_ADISC_LIST); + lpfc_issue_els_adisc(phba, ndlp, cmdiocb->retry); + return (1); + case ELS_CMD_PRLI: + ndlp->nlp_state = NLP_STE_PRLI_ISSUE; + lpfc_nlp_list(phba, ndlp, NLP_PRLI_LIST); + lpfc_issue_els_prli(phba, ndlp, cmdiocb->retry); + return (1); + case ELS_CMD_LOGO: + ndlp->nlp_state = NLP_STE_NPR_NODE; + lpfc_nlp_list(phba, ndlp, NLP_NPR_LIST); + lpfc_issue_els_logo(phba, ndlp, cmdiocb->retry); + return (1); + } + } + + /* No retry ELS command to remote NPORT */ + lpfc_printf_log(phba, KERN_INFO, LOG_ELS, + "%d:0108 No retry ELS command x%x to remote NPORT x%x " + "Data: x%x x%x\n", + phba->brd_no, + cmd, ndlp->nlp_DID, cmdiocb->retry, ndlp->nlp_flag); + + return (0); +} + +int +lpfc_els_free_iocb(struct lpfc_hba * phba, struct lpfc_iocbq * elsiocb) +{ + struct lpfc_dmabuf *buf_ptr, *buf_ptr1; + + /* context2 = cmd, context2->next = rsp, context3 = bpl */ + if (elsiocb->context2) { + buf_ptr1 = (struct lpfc_dmabuf *) elsiocb->context2; + /* Free the response before processing the command. */ + if (!list_empty(&buf_ptr1->list)) { + buf_ptr = list_entry(buf_ptr1->list.next, + struct lpfc_dmabuf, list); + lpfc_mbuf_free(phba, buf_ptr->virt, buf_ptr->phys); + kfree(buf_ptr); + } + lpfc_mbuf_free(phba, buf_ptr1->virt, buf_ptr1->phys); + kfree(buf_ptr1); + } + + if (elsiocb->context3) { + buf_ptr = (struct lpfc_dmabuf *) elsiocb->context3; + lpfc_mbuf_free(phba, buf_ptr->virt, buf_ptr->phys); + kfree(buf_ptr); + } + + mempool_free( elsiocb, phba->iocb_mem_pool); + return 0; +} + +static void +lpfc_cmpl_els_logo_acc(struct lpfc_hba * phba, struct lpfc_iocbq * cmdiocb, + struct lpfc_iocbq * rspiocb) +{ + struct lpfc_nodelist *ndlp; + + ndlp = (struct lpfc_nodelist *) cmdiocb->context1; + + /* ACC to LOGO completes to NPort */ + lpfc_printf_log(phba, KERN_INFO, LOG_ELS, + "%d:0109 ACC to LOGO completes to NPort x%x " + "Data: x%x x%x x%x\n", + phba->brd_no, ndlp->nlp_DID, ndlp->nlp_flag, + ndlp->nlp_state, ndlp->nlp_rpi); + + ndlp->nlp_flag &= ~NLP_LOGO_ACC; + + switch (ndlp->nlp_state) { + case NLP_STE_UNUSED_NODE: /* node is just allocated */ + lpfc_nlp_list(phba, ndlp, NLP_NO_LIST); + break; + case NLP_STE_NPR_NODE: /* NPort Recovery mode */ + lpfc_unreg_rpi(phba, ndlp); + break; + default: + break; + } + lpfc_els_free_iocb(phba, cmdiocb); + return; +} + +static void +lpfc_cmpl_els_acc(struct lpfc_hba * phba, struct lpfc_iocbq * cmdiocb, + struct lpfc_iocbq * rspiocb) +{ + struct lpfc_nodelist *ndlp; + LPFC_MBOXQ_t *mbox = NULL; + + ndlp = (struct lpfc_nodelist *) cmdiocb->context1; + if (cmdiocb->context_un.mbox) + mbox = cmdiocb->context_un.mbox; + + + /* Check to see if link went down during discovery */ + if ((lpfc_els_chk_latt(phba)) || !ndlp) { + if (mbox) { + mempool_free( mbox, phba->mbox_mem_pool); + } + goto out; + } + + /* ELS response tag completes */ + lpfc_printf_log(phba, KERN_INFO, LOG_ELS, + "%d:0110 ELS response tag x%x completes " + "Data: x%x x%x x%x x%x x%x x%x\n", + phba->brd_no, + cmdiocb->iocb.ulpIoTag, rspiocb->iocb.ulpStatus, + rspiocb->iocb.un.ulpWord[4], ndlp->nlp_DID, + ndlp->nlp_flag, ndlp->nlp_state, ndlp->nlp_rpi); + + if (mbox) { + if ((rspiocb->iocb.ulpStatus == 0) + && (ndlp->nlp_flag & NLP_ACC_REGLOGIN)) { + /* set_slim mailbox command needs to execute first, + * queue this command to be processed later. + */ + lpfc_unreg_rpi(phba, ndlp); + mbox->mbox_cmpl = lpfc_mbx_cmpl_reg_login; + mbox->context2 = ndlp; + ndlp->nlp_state = NLP_STE_REG_LOGIN_ISSUE; + lpfc_nlp_list(phba, ndlp, NLP_REGLOGIN_LIST); + if (lpfc_sli_issue_mbox(phba, mbox, + (MBX_NOWAIT | MBX_STOP_IOCB)) + != MBX_NOT_FINISHED) { + goto out; + } + /* NOTE: we should have messages for unsuccessful + reglogin */ + mempool_free( mbox, phba->mbox_mem_pool); + } else { + mempool_free( mbox, phba->mbox_mem_pool); + if (ndlp->nlp_flag & NLP_ACC_REGLOGIN) { + lpfc_nlp_list(phba, ndlp, NLP_NO_LIST); + } + } + } +out: + if(ndlp) + ndlp->nlp_flag &= ~NLP_ACC_REGLOGIN; + lpfc_els_free_iocb(phba, cmdiocb); + return; +} + +int +lpfc_els_rsp_acc(struct lpfc_hba * phba, uint32_t flag, + struct lpfc_iocbq * oldiocb, struct lpfc_nodelist * ndlp, + LPFC_MBOXQ_t * mbox, uint8_t newnode) +{ + IOCB_t *icmd; + IOCB_t *oldcmd; + struct lpfc_iocbq *elsiocb; + struct lpfc_sli_ring *pring; + struct lpfc_sli *psli; + struct lpfc_dmabuf *bmp; + uint8_t *pcmd; + uint16_t cmdsize; + + psli = &phba->sli; + pring = &psli->ring[LPFC_ELS_RING]; /* ELS ring */ + oldcmd = &oldiocb->iocb; + + switch (flag) { + case ELS_CMD_ACC: + cmdsize = sizeof (uint32_t); + if ((elsiocb = + lpfc_prep_els_iocb(phba, 0, cmdsize, oldiocb->retry, + ndlp, ELS_CMD_ACC)) == 0) { + return (1); + } + icmd = &elsiocb->iocb; + icmd->ulpContext = oldcmd->ulpContext; /* Xri */ + pcmd = (((struct lpfc_dmabuf *) elsiocb->context2)->virt); + *((uint32_t *) (pcmd)) = ELS_CMD_ACC; + pcmd += sizeof (uint32_t); + break; + case ELS_CMD_PLOGI: + cmdsize = (sizeof (struct serv_parm) + sizeof (uint32_t)); + if ((elsiocb = + lpfc_prep_els_iocb(phba, 0, cmdsize, oldiocb->retry, + ndlp, ELS_CMD_ACC)) == 0) { + return (1); + } + icmd = &elsiocb->iocb; + icmd->ulpContext = oldcmd->ulpContext; /* Xri */ + pcmd = (((struct lpfc_dmabuf *) elsiocb->context2)->virt); + + if (mbox) + elsiocb->context_un.mbox = mbox; + + *((uint32_t *) (pcmd)) = ELS_CMD_ACC; + pcmd += sizeof (uint32_t); + memcpy(pcmd, &phba->fc_sparam, sizeof (struct serv_parm)); + break; + default: + return (1); + } + + if (newnode) + elsiocb->context1 = NULL; + + /* Xmit ELS ACC response tag */ + lpfc_printf_log(phba, KERN_INFO, LOG_ELS, + "%d:0128 Xmit ELS ACC response tag x%x " + "Data: x%x x%x x%x x%x x%x\n", + phba->brd_no, + elsiocb->iocb.ulpIoTag, + elsiocb->iocb.ulpContext, ndlp->nlp_DID, + ndlp->nlp_flag, ndlp->nlp_state, ndlp->nlp_rpi); + + bmp = (struct lpfc_dmabuf *) (elsiocb->context2); + pci_dma_sync_single_for_device(phba->pcidev, bmp->phys, + LPFC_BPL_SIZE, PCI_DMA_TODEVICE); + + if (ndlp->nlp_flag & NLP_LOGO_ACC) { + elsiocb->iocb_cmpl = lpfc_cmpl_els_logo_acc; + } else { + elsiocb->iocb_cmpl = lpfc_cmpl_els_acc; + } + + phba->fc_stat.elsXmitACC++; + if (lpfc_sli_issue_iocb(phba, pring, elsiocb, 0) == IOCB_ERROR) { + lpfc_els_free_iocb(phba, elsiocb); + return (1); + } + return (0); +} + +int +lpfc_els_rsp_reject(struct lpfc_hba * phba, uint32_t rejectError, + struct lpfc_iocbq * oldiocb, struct lpfc_nodelist * ndlp) +{ + IOCB_t *icmd; + IOCB_t *oldcmd; + struct lpfc_iocbq *elsiocb; + struct lpfc_sli_ring *pring; + struct lpfc_sli *psli; + struct lpfc_dmabuf *bmp; + uint8_t *pcmd; + uint16_t cmdsize; + + psli = &phba->sli; + pring = &psli->ring[LPFC_ELS_RING]; /* ELS ring */ + + cmdsize = 2 * sizeof (uint32_t); + if ((elsiocb = lpfc_prep_els_iocb(phba, 0, cmdsize, oldiocb->retry, + ndlp, ELS_CMD_LS_RJT)) == 0) { + return (1); + } + + icmd = &elsiocb->iocb; + oldcmd = &oldiocb->iocb; + icmd->ulpContext = oldcmd->ulpContext; /* Xri */ + pcmd = (uint8_t *) (((struct lpfc_dmabuf *) elsiocb->context2)->virt); + + *((uint32_t *) (pcmd)) = ELS_CMD_LS_RJT; + pcmd += sizeof (uint32_t); + *((uint32_t *) (pcmd)) = rejectError; + + /* The els iocb is fully initialize. Flush it to main store for the + * HBA. Note that all els iocb context buffer are from the driver's + * dma pool and have length LPFC_BPL_SIZE. Get a short-hand pointer to + * the physical address. + */ + bmp = (struct lpfc_dmabuf *) (elsiocb->context2); + pci_dma_sync_single_for_device(phba->pcidev, bmp->phys, + LPFC_BPL_SIZE, PCI_DMA_TODEVICE); + + /* Xmit ELS RJT response tag */ + lpfc_printf_log(phba, KERN_INFO, LOG_ELS, + "%d:0129 Xmit ELS RJT x%x response tag x%x " + "Data: x%x x%x x%x x%x x%x\n", + phba->brd_no, + rejectError, elsiocb->iocb.ulpIoTag, + elsiocb->iocb.ulpContext, ndlp->nlp_DID, + ndlp->nlp_flag, ndlp->nlp_state, ndlp->nlp_rpi); + + phba->fc_stat.elsXmitLSRJT++; + elsiocb->iocb_cmpl = lpfc_cmpl_els_acc; + + if (lpfc_sli_issue_iocb(phba, pring, elsiocb, 0) == IOCB_ERROR) { + lpfc_els_free_iocb(phba, elsiocb); + return (1); + } + return (0); +} + +int +lpfc_els_rsp_adisc_acc(struct lpfc_hba * phba, + struct lpfc_iocbq * oldiocb, struct lpfc_nodelist * ndlp) +{ + ADISC *ap; + IOCB_t *icmd; + IOCB_t *oldcmd; + struct lpfc_iocbq *elsiocb; + struct lpfc_sli_ring *pring; + struct lpfc_sli *psli; + struct lpfc_dmabuf *bmp; + uint8_t *pcmd; + uint16_t cmdsize; + + psli = &phba->sli; + pring = &psli->ring[LPFC_ELS_RING]; /* ELS ring */ + + cmdsize = sizeof (uint32_t) + sizeof (ADISC); + if ((elsiocb = lpfc_prep_els_iocb(phba, 0, cmdsize, oldiocb->retry, + ndlp, ELS_CMD_ACC)) == 0) { + return (1); + } + + /* Xmit ADISC ACC response tag */ + lpfc_printf_log(phba, KERN_INFO, LOG_ELS, + "%d:0130 Xmit ADISC ACC response tag x%x " + "Data: x%x x%x x%x x%x x%x\n", + phba->brd_no, + elsiocb->iocb.ulpIoTag, + elsiocb->iocb.ulpContext, ndlp->nlp_DID, + ndlp->nlp_flag, ndlp->nlp_state, ndlp->nlp_rpi); + + icmd = &elsiocb->iocb; + oldcmd = &oldiocb->iocb; + icmd->ulpContext = oldcmd->ulpContext; /* Xri */ + pcmd = (uint8_t *) (((struct lpfc_dmabuf *) elsiocb->context2)->virt); + + *((uint32_t *) (pcmd)) = ELS_CMD_ACC; + pcmd += sizeof (uint32_t); + + ap = (ADISC *) (pcmd); + ap->hardAL_PA = phba->fc_pref_ALPA; + memcpy(&ap->portName, &phba->fc_portname, sizeof (struct lpfc_name)); + memcpy(&ap->nodeName, &phba->fc_nodename, sizeof (struct lpfc_name)); + ap->DID = be32_to_cpu(phba->fc_myDID); + + /* The els iocb is fully initialize. Flush it to main store for the + * HBA. Note that all els iocb context buffer are from the driver's + * dma pool and have length LPFC_BPL_SIZE. Get a short-hand pointer to + * the physical address. + */ + bmp = (struct lpfc_dmabuf *) (elsiocb->context2); + pci_dma_sync_single_for_device(phba->pcidev, bmp->phys, + LPFC_BPL_SIZE, PCI_DMA_TODEVICE); + + phba->fc_stat.elsXmitACC++; + elsiocb->iocb_cmpl = lpfc_cmpl_els_acc; + + if (lpfc_sli_issue_iocb(phba, pring, elsiocb, 0) == IOCB_ERROR) { + lpfc_els_free_iocb(phba, elsiocb); + return (1); + } + return (0); +} + +int +lpfc_els_rsp_prli_acc(struct lpfc_hba * phba, + struct lpfc_iocbq * oldiocb, struct lpfc_nodelist * ndlp) +{ + PRLI *npr; + lpfc_vpd_t *vpd; + IOCB_t *icmd; + IOCB_t *oldcmd; + struct lpfc_iocbq *elsiocb; + struct lpfc_sli_ring *pring; + struct lpfc_sli *psli; + struct lpfc_dmabuf *bmp; + uint8_t *pcmd; + uint16_t cmdsize; + + psli = &phba->sli; + pring = &psli->ring[LPFC_ELS_RING]; /* ELS ring */ + + cmdsize = sizeof (uint32_t) + sizeof (PRLI); + if ((elsiocb = lpfc_prep_els_iocb(phba, 0, cmdsize, oldiocb->retry, + ndlp, + (ELS_CMD_ACC | + (ELS_CMD_PRLI & ~ELS_RSP_MASK)))) == + 0) { + return (1); + } + + /* Xmit PRLI ACC response tag */ + lpfc_printf_log(phba, KERN_INFO, LOG_ELS, + "%d:0131 Xmit PRLI ACC response tag x%x " + "Data: x%x x%x x%x x%x x%x\n", + phba->brd_no, + elsiocb->iocb.ulpIoTag, + elsiocb->iocb.ulpContext, ndlp->nlp_DID, + ndlp->nlp_flag, ndlp->nlp_state, ndlp->nlp_rpi); + + icmd = &elsiocb->iocb; + oldcmd = &oldiocb->iocb; + icmd->ulpContext = oldcmd->ulpContext; /* Xri */ + pcmd = (uint8_t *) (((struct lpfc_dmabuf *) elsiocb->context2)->virt); + + *((uint32_t *) (pcmd)) = (ELS_CMD_ACC | (ELS_CMD_PRLI & ~ELS_RSP_MASK)); + pcmd += sizeof (uint32_t); + + /* For PRLI, remainder of payload is PRLI parameter page */ + memset(pcmd, 0, sizeof (PRLI)); + + npr = (PRLI *) pcmd; + vpd = &phba->vpd; + /* + * If our firmware version is 3.20 or later, + * set the following bits for FC-TAPE support. + */ + if (vpd->rev.feaLevelHigh >= 0x02) { + npr->ConfmComplAllowed = 1; + npr->Retry = 1; + npr->TaskRetryIdReq = 1; + } + + npr->acceptRspCode = PRLI_REQ_EXECUTED; + npr->estabImagePair = 1; + npr->readXferRdyDis = 1; + npr->ConfmComplAllowed = 1; + + npr->prliType = PRLI_FCP_TYPE; + npr->initiatorFunc = 1; + + /* The els iocb is fully initialize. Flush it to main store for the + * HBA. Note that all els iocb context buffer are from the driver's + * dma pool and have length LPFC_BPL_SIZE. Get a short-hand pointer to + * the physical address. + */ + bmp = (struct lpfc_dmabuf *) (elsiocb->context2); + pci_dma_sync_single_for_device(phba->pcidev, bmp->phys, + LPFC_BPL_SIZE, PCI_DMA_TODEVICE); + + phba->fc_stat.elsXmitACC++; + elsiocb->iocb_cmpl = lpfc_cmpl_els_acc; + + if (lpfc_sli_issue_iocb(phba, pring, elsiocb, 0) == IOCB_ERROR) { + lpfc_els_free_iocb(phba, elsiocb); + return (1); + } + return (0); +} + +static int +lpfc_els_rsp_rnid_acc(struct lpfc_hba * phba, + uint8_t format, + struct lpfc_iocbq * oldiocb, struct lpfc_nodelist * ndlp) +{ + RNID *rn; + IOCB_t *icmd; + IOCB_t *oldcmd; + struct lpfc_iocbq *elsiocb; + struct lpfc_sli_ring *pring; + struct lpfc_sli *psli; + struct lpfc_dmabuf *bmp; + uint8_t *pcmd; + uint16_t cmdsize; + + psli = &phba->sli; + pring = &psli->ring[LPFC_ELS_RING]; + + cmdsize = sizeof (uint32_t) + sizeof (uint32_t) + + (2 * sizeof (struct lpfc_name)); + if (format) + cmdsize += sizeof (RNID_TOP_DISC); + + if ((elsiocb = lpfc_prep_els_iocb(phba, 0, cmdsize, oldiocb->retry, + ndlp, ELS_CMD_ACC)) == 0) { + return (1); + } + + /* Xmit RNID ACC response tag */ + lpfc_printf_log(phba, KERN_INFO, LOG_ELS, + "%d:0132 Xmit RNID ACC response tag x%x " + "Data: x%x\n", + phba->brd_no, + elsiocb->iocb.ulpIoTag, + elsiocb->iocb.ulpContext); + + icmd = &elsiocb->iocb; + oldcmd = &oldiocb->iocb; + icmd->ulpContext = oldcmd->ulpContext; /* Xri */ + pcmd = (uint8_t *) (((struct lpfc_dmabuf *) elsiocb->context2)->virt); + + *((uint32_t *) (pcmd)) = ELS_CMD_ACC; + pcmd += sizeof (uint32_t); + + memset(pcmd, 0, sizeof (RNID)); + rn = (RNID *) (pcmd); + rn->Format = format; + rn->CommonLen = (2 * sizeof (struct lpfc_name)); + memcpy(&rn->portName, &phba->fc_portname, sizeof (struct lpfc_name)); + memcpy(&rn->nodeName, &phba->fc_nodename, sizeof (struct lpfc_name)); + switch (format) { + case 0: + rn->SpecificLen = 0; + break; + case RNID_TOPOLOGY_DISC: + rn->SpecificLen = sizeof (RNID_TOP_DISC); + memcpy(&rn->un.topologyDisc.portName, + &phba->fc_portname, sizeof (struct lpfc_name)); + rn->un.topologyDisc.unitType = RNID_HBA; + rn->un.topologyDisc.physPort = 0; + rn->un.topologyDisc.attachedNodes = 0; + break; + default: + rn->CommonLen = 0; + rn->SpecificLen = 0; + break; + } + + /* The els iocb is fully initialize. Flush it to main store for the + * HBA. Note that all els iocb context buffer are from the driver's + * dma pool and have length LPFC_BPL_SIZE. Get a short-hand pointer to + * the physical address. + */ + bmp = (struct lpfc_dmabuf *) (elsiocb->context2); + pci_dma_sync_single_for_device(phba->pcidev, bmp->phys, + LPFC_BPL_SIZE, PCI_DMA_TODEVICE); + + phba->fc_stat.elsXmitACC++; + elsiocb->iocb_cmpl = lpfc_cmpl_els_acc; + elsiocb->context1 = NULL; /* Don't need ndlp for cmpl, + * it could be freed */ + + if (lpfc_sli_issue_iocb(phba, pring, elsiocb, 0) == IOCB_ERROR) { + lpfc_els_free_iocb(phba, elsiocb); + return (1); + } + return (0); +} + +int +lpfc_els_disc_adisc(struct lpfc_hba * phba) +{ + int sentadisc; + struct lpfc_nodelist *ndlp, *next_ndlp; + + sentadisc = 0; + /* go thru NPR list and issue any remaining ELS ADISCs */ + list_for_each_entry_safe(ndlp, next_ndlp, &phba->fc_npr_list, + nlp_listp) { + if(ndlp->nlp_flag & NLP_NPR_2B_DISC) { + if(ndlp->nlp_flag & NLP_NPR_ADISC) { + ndlp->nlp_flag &= ~NLP_NPR_ADISC; + ndlp->nlp_state = NLP_STE_ADISC_ISSUE; + lpfc_nlp_list(phba, ndlp, + NLP_ADISC_LIST); + lpfc_issue_els_adisc(phba, ndlp, 0); + sentadisc++; + phba->num_disc_nodes++; + if (phba->num_disc_nodes >= + phba->cfg_discovery_threads) { + phba->fc_flag |= FC_NLP_MORE; + break; + } + } + } + } + if (sentadisc == 0) { + phba->fc_flag &= ~FC_NLP_MORE; + } + return(sentadisc); +} + +int +lpfc_els_disc_plogi(struct lpfc_hba * phba) +{ + int sentplogi; + struct lpfc_nodelist *ndlp, *next_ndlp; + + sentplogi = 0; + /* go thru NPR list and issue any remaining ELS PLOGIs */ + list_for_each_entry_safe(ndlp, next_ndlp, &phba->fc_npr_list, + nlp_listp) { + if((ndlp->nlp_flag & NLP_NPR_2B_DISC) && + (!(ndlp->nlp_flag & NLP_DELAY_TMO))) { + if(!(ndlp->nlp_flag & NLP_NPR_ADISC)) { + ndlp->nlp_state = NLP_STE_PLOGI_ISSUE; + lpfc_nlp_list(phba, ndlp, NLP_PLOGI_LIST); + lpfc_issue_els_plogi(phba, ndlp, 0); + sentplogi++; + phba->num_disc_nodes++; + if (phba->num_disc_nodes >= + phba->cfg_discovery_threads) { + phba->fc_flag |= FC_NLP_MORE; + break; + } + } + } + } + if (sentplogi == 0) { + phba->fc_flag &= ~FC_NLP_MORE; + } + return(sentplogi); +} + +int +lpfc_els_flush_rscn(struct lpfc_hba * phba) +{ + struct lpfc_dmabuf *mp; + int i; + + for (i = 0; i < phba->fc_rscn_id_cnt; i++) { + mp = phba->fc_rscn_id_list[i]; + lpfc_mbuf_free(phba, mp->virt, mp->phys); + kfree(mp); + phba->fc_rscn_id_list[i] = NULL; + } + phba->fc_rscn_id_cnt = 0; + phba->fc_flag &= ~(FC_RSCN_MODE | FC_RSCN_DISCOVERY); + lpfc_can_disctmo(phba); + return (0); +} + +int +lpfc_rscn_payload_check(struct lpfc_hba * phba, uint32_t did) +{ + D_ID ns_did; + D_ID rscn_did; + struct lpfc_dmabuf *mp; + uint32_t *lp; + uint32_t payload_len, cmd, i, match; + + ns_did.un.word = did; + match = 0; + + /* If we are doing a FULL RSCN rediscovery, match everything */ + if (phba->fc_flag & FC_RSCN_DISCOVERY) { + return (did); + } + + for (i = 0; i < phba->fc_rscn_id_cnt; i++) { + mp = phba->fc_rscn_id_list[i]; + lp = (uint32_t *) mp->virt; + cmd = *lp++; + payload_len = be32_to_cpu(cmd) & 0xffff; /* payload length */ + payload_len -= sizeof (uint32_t); /* take off word 0 */ + while (payload_len) { + rscn_did.un.word = *lp++; + rscn_did.un.word = be32_to_cpu(rscn_did.un.word); + payload_len -= sizeof (uint32_t); + switch (rscn_did.un.b.resv) { + case 0: /* Single N_Port ID effected */ + if (ns_did.un.word == rscn_did.un.word) { + match = did; + } + break; + case 1: /* Whole N_Port Area effected */ + if ((ns_did.un.b.domain == rscn_did.un.b.domain) + && (ns_did.un.b.area == rscn_did.un.b.area)) + { + match = did; + } + break; + case 2: /* Whole N_Port Domain effected */ + if (ns_did.un.b.domain == rscn_did.un.b.domain) + { + match = did; + } + break; + case 3: /* Whole Fabric effected */ + match = did; + break; + default: + /* Unknown Identifier in RSCN list */ + lpfc_printf_log(phba, KERN_ERR, LOG_DISCOVERY, + "%d:0217 Unknown Identifier in " + "RSCN payload Data: x%x\n", + phba->brd_no, rscn_did.un.word); + break; + } + if (match) { + break; + } + } + } + return (match); +} + +static int +lpfc_rscn_recovery_check(struct lpfc_hba * phba) +{ + struct lpfc_nodelist *ndlp = NULL, *next_ndlp; + struct list_head *listp; + struct list_head *node_list[7]; + int i; + + /* Look at all nodes effected by pending RSCNs and move + * them to NPR list. + */ + node_list[0] = &phba->fc_npr_list; /* MUST do this list first */ + node_list[1] = &phba->fc_nlpmap_list; + node_list[2] = &phba->fc_nlpunmap_list; + node_list[3] = &phba->fc_prli_list; + node_list[4] = &phba->fc_reglogin_list; + node_list[5] = &phba->fc_adisc_list; + node_list[6] = &phba->fc_plogi_list; + for (i = 0; i < 7; i++) { + listp = node_list[i]; + if (list_empty(listp)) + continue; + + list_for_each_entry_safe(ndlp, next_ndlp, listp, nlp_listp) { + if((lpfc_rscn_payload_check(phba, ndlp->nlp_DID))) { + /* part of RSCN, process this entry */ + lpfc_set_failmask(phba, ndlp, + LPFC_DEV_DISCOVERY_INP, + LPFC_SET_BITMASK); + + lpfc_disc_state_machine(phba, ndlp, NULL, + NLP_EVT_DEVICE_RECOVERY); + if(ndlp->nlp_flag & NLP_DELAY_TMO) { + ndlp->nlp_flag &= ~NLP_DELAY_TMO; + del_timer_sync(&ndlp->nlp_delayfunc); + } + } + } + } + return (0); +} + +static int +lpfc_els_rcv_rscn(struct lpfc_hba * phba, + struct lpfc_iocbq * cmdiocb, + struct lpfc_nodelist * ndlp, uint8_t newnode) +{ + struct lpfc_dmabuf *pcmd; + uint32_t *lp; + IOCB_t *icmd; + uint32_t payload_len, cmd; + + icmd = &cmdiocb->iocb; + pcmd = (struct lpfc_dmabuf *) cmdiocb->context2; + lp = (uint32_t *) pcmd->virt; + + /* The response iocb was populated by the HBA. Flush it to main store + * for the driver. Note that all iocb context buffers are from the + * driver's dma pool and have length LPFC_BPL_SIZE. + */ + pci_dma_sync_single_for_device(phba->pcidev, pcmd->phys, + LPFC_BPL_SIZE, PCI_DMA_FROMDEVICE); + + cmd = *lp++; + payload_len = be32_to_cpu(cmd) & 0xffff; /* payload length */ + payload_len -= sizeof (uint32_t); /* take off word 0 */ + cmd &= ELS_CMD_MASK; + + /* RSCN received */ + lpfc_printf_log(phba, + KERN_INFO, + LOG_DISCOVERY, + "%d:0214 RSCN received Data: x%x x%x x%x x%x\n", + phba->brd_no, + phba->fc_flag, payload_len, *lp, phba->fc_rscn_id_cnt); + + /* if we are already processing an RSCN, save the received + * RSCN payload buffer, cmdiocb->context2 to process later. + * If we zero, cmdiocb->context2, the calling routine will + * not try to free it. + */ + if (phba->fc_flag & FC_RSCN_MODE) { + if ((phba->fc_rscn_id_cnt < FC_MAX_HOLD_RSCN) && + !(phba->fc_flag & FC_RSCN_DISCOVERY)) { + phba->fc_rscn_id_list[phba->fc_rscn_id_cnt++] = pcmd; + cmdiocb->context2 = NULL; + /* Deferred RSCN */ + lpfc_printf_log(phba, KERN_INFO, LOG_DISCOVERY, + "%d:0235 Deferred RSCN " + "Data: x%x x%x x%x\n", + phba->brd_no, phba->fc_rscn_id_cnt, + phba->fc_flag, phba->hba_state); + } else { + phba->fc_flag |= FC_RSCN_DISCOVERY; + /* ReDiscovery RSCN */ + lpfc_printf_log(phba, KERN_INFO, LOG_DISCOVERY, + "%d:0234 ReDiscovery RSCN " + "Data: x%x x%x x%x\n", + phba->brd_no, phba->fc_rscn_id_cnt, + phba->fc_flag, phba->hba_state); + } + /* Send back ACC */ + lpfc_els_rsp_acc(phba, ELS_CMD_ACC, cmdiocb, ndlp, NULL, + newnode); + + /* send RECOVERY event for ALL nodes that match RSCN payload */ + lpfc_rscn_recovery_check(phba); + return (0); + } + + phba->fc_flag |= FC_RSCN_MODE; + phba->fc_rscn_id_list[phba->fc_rscn_id_cnt++] = pcmd; + /* + * If we zero, cmdiocb->context2, the calling routine will + * not try to free it. + */ + cmdiocb->context2 = NULL; + + lpfc_set_disctmo(phba); + + /* Send back ACC */ + lpfc_els_rsp_acc(phba, ELS_CMD_ACC, cmdiocb, ndlp, NULL, newnode); + + /* send RECOVERY event for ALL nodes that match RSCN payload */ + lpfc_rscn_recovery_check(phba); + + return (lpfc_els_handle_rscn(phba)); +} + +int +lpfc_els_handle_rscn(struct lpfc_hba * phba) +{ + struct lpfc_nodelist *ndlp; + + lpfc_put_event(phba, HBA_EVENT_RSCN, phba->fc_myDID, + (void *)(unsigned long)(phba->fc_myDID), 0, 0); + + /* Start timer for RSCN processing */ + lpfc_set_disctmo(phba); + + /* RSCN processed */ + lpfc_printf_log(phba, + KERN_INFO, + LOG_DISCOVERY, + "%d:0215 RSCN processed Data: x%x x%x x%x x%x\n", + phba->brd_no, + phba->fc_flag, 0, phba->fc_rscn_id_cnt, + phba->hba_state); + + /* To process RSCN, first compare RSCN data with NameServer */ + phba->fc_ns_retry = 0; + if ((ndlp = lpfc_findnode_did(phba, NLP_SEARCH_UNMAPPED, + NameServer_DID))) { + /* Good ndlp, issue CT Request to NameServer */ + if (lpfc_ns_cmd(phba, ndlp, SLI_CTNS_GID_FT) == 0) { + /* Wait for NameServer query cmpl before we can + continue */ + return (1); + } + } else { + /* If login to NameServer does not exist, issue one */ + /* Good status, issue PLOGI to NameServer */ + if ((ndlp = + lpfc_findnode_did(phba, NLP_SEARCH_ALL, NameServer_DID))) { + /* Wait for NameServer login cmpl before we can + continue */ + return (1); + } + if ((ndlp = mempool_alloc(phba->nlp_mem_pool, GFP_ATOMIC)) + == 0) { + lpfc_els_flush_rscn(phba); + return (0); + } else { + lpfc_nlp_init(phba, ndlp, NameServer_DID); + ndlp->nlp_type |= NLP_FABRIC; + ndlp->nlp_state = NLP_STE_PLOGI_ISSUE; + lpfc_issue_els_plogi(phba, ndlp, 0); + /* Wait for NameServer login cmpl before we can + continue */ + return (1); + } + } + + lpfc_els_flush_rscn(phba); + return (0); +} + +static int +lpfc_els_rcv_flogi(struct lpfc_hba * phba, + struct lpfc_iocbq * cmdiocb, + struct lpfc_nodelist * ndlp, uint8_t newnode) +{ + struct lpfc_dmabuf *pcmd = (struct lpfc_dmabuf *) cmdiocb->context2; + uint32_t *lp = (uint32_t *) pcmd->virt; + IOCB_t *icmd = &cmdiocb->iocb; + struct serv_parm *sp; + LPFC_MBOXQ_t *mbox; + struct ls_rjt stat; + uint32_t cmd, did; + + + /* The response iocb was populated by the HBA. Flush it to main store + * for the driver. Note that all iocb context buffers are from the + * driver's dma pool and have length LPFC_BPL_SIZE. + */ + pci_dma_sync_single_for_device(phba->pcidev, pcmd->phys, + LPFC_BPL_SIZE, PCI_DMA_FROMDEVICE); + + cmd = *lp++; + sp = (struct serv_parm *) lp; + + /* FLOGI received */ + + lpfc_set_disctmo(phba); + + if (phba->fc_topology == TOPOLOGY_LOOP) { + /* We should never receive a FLOGI in loop mode, ignore it */ + did = icmd->un.elsreq64.remoteID; + + /* An FLOGI ELS command was received from DID in + Loop Mode */ + lpfc_printf_log(phba, KERN_ERR, LOG_ELS, + "%d:0113 An FLOGI ELS command x%x was received " + "from DID x%x in Loop Mode\n", + phba->brd_no, cmd, did); + return (1); + } + + did = Fabric_DID; + + if ((lpfc_check_sparm(phba, ndlp, sp, CLASS3))) { + /* For a FLOGI we accept, then if our portname is greater + * then the remote portname we initiate Nport login. + */ + int rc; + + rc = memcmp(&phba->fc_portname, &sp->portName, + sizeof (struct lpfc_name)); + + if (!rc) { + if ((mbox = mempool_alloc(phba->mbox_mem_pool, + GFP_ATOMIC)) == 0) { + return (1); + } + lpfc_linkdown(phba); + lpfc_init_link(phba, mbox, + phba->cfg_topology, + phba->cfg_link_speed); + mbox->mb.un.varInitLnk.lipsr_AL_PA = 0; + if (lpfc_sli_issue_mbox + (phba, mbox, (MBX_NOWAIT | MBX_STOP_IOCB)) + == MBX_NOT_FINISHED) { + mempool_free( mbox, phba->mbox_mem_pool); + } + return (1); + } + + else if (rc > 0) { /* greater than */ + phba->fc_flag |= FC_PT2PT_PLOGI; + } + phba->fc_flag |= FC_PT2PT; + phba->fc_flag &= ~(FC_FABRIC | FC_PUBLIC_LOOP); + } else { + /* Reject this request because invalid parameters */ + stat.un.b.lsRjtRsvd0 = 0; + stat.un.b.lsRjtRsnCode = LSRJT_UNABLE_TPC; + stat.un.b.lsRjtRsnCodeExp = LSEXP_SPARM_OPTIONS; + stat.un.b.vendorUnique = 0; + lpfc_els_rsp_reject(phba, stat.un.lsRjtError, cmdiocb, ndlp); + return (1); + } + + /* Send back ACC */ + lpfc_els_rsp_acc(phba, ELS_CMD_PLOGI, cmdiocb, ndlp, NULL, newnode); + + return (0); +} + +static int +lpfc_els_rcv_rnid(struct lpfc_hba * phba, + struct lpfc_iocbq * cmdiocb, struct lpfc_nodelist * ndlp) +{ + struct lpfc_dmabuf *pcmd; + uint32_t *lp; + IOCB_t *icmd; + RNID *rn; + struct ls_rjt stat; + uint32_t cmd, did; + + icmd = &cmdiocb->iocb; + did = icmd->un.elsreq64.remoteID; + pcmd = (struct lpfc_dmabuf *) cmdiocb->context2; + lp = (uint32_t *) pcmd->virt; + + /* The response iocb was populated by the HBA. Flush it to main store + * for the driver. Note that all iocb context buffers are from the + * driver's dma pool and have length LPFC_BPL_SIZE. + */ + pci_dma_sync_single_for_device(phba->pcidev, pcmd->phys, + LPFC_BPL_SIZE, PCI_DMA_FROMDEVICE); + + cmd = *lp++; + rn = (RNID *) lp; + + /* RNID received */ + + switch (rn->Format) { + case 0: + case RNID_TOPOLOGY_DISC: + /* Send back ACC */ + lpfc_els_rsp_rnid_acc(phba, rn->Format, cmdiocb, ndlp); + break; + default: + /* Reject this request because format not supported */ + stat.un.b.lsRjtRsvd0 = 0; + stat.un.b.lsRjtRsnCode = LSRJT_UNABLE_TPC; + stat.un.b.lsRjtRsnCodeExp = LSEXP_CANT_GIVE_DATA; + stat.un.b.vendorUnique = 0; + lpfc_els_rsp_reject(phba, stat.un.lsRjtError, cmdiocb, ndlp); + } + return (0); +} + +static int +lpfc_els_rcv_rrq(struct lpfc_hba * phba, struct lpfc_iocbq * cmdiocb, + struct lpfc_nodelist * ndlp) +{ + struct lpfc_dmabuf *pcmd; + uint32_t *lp; + IOCB_t *icmd; + struct lpfc_sli_ring *pring; + struct lpfc_sli *psli; + RRQ *rrq; + uint32_t cmd, did; + + psli = &phba->sli; + pring = &psli->ring[LPFC_FCP_RING]; + icmd = &cmdiocb->iocb; + did = icmd->un.elsreq64.remoteID; + pcmd = (struct lpfc_dmabuf *) cmdiocb->context2; + lp = (uint32_t *) pcmd->virt; + + /* The response iocb was populated by the HBA. Flush it to main store + * for the driver. Note that all iocb context buffers are from the + * driver's dma pool and have length LPFC_BPL_SIZE. + */ + pci_dma_sync_single_for_cpu(phba->pcidev, pcmd->phys, + LPFC_BPL_SIZE, PCI_DMA_FROMDEVICE); + + cmd = *lp++; + rrq = (RRQ *) lp; + + /* RRQ received */ + /* Get oxid / rxid from payload and abort it */ + if ((rrq->SID == be32_to_cpu(phba->fc_myDID))) { + lpfc_sli_abort_iocb_ctx(phba, pring, rrq->Oxid); + } else { + lpfc_sli_abort_iocb_ctx(phba, pring, rrq->Rxid); + } + /* ACCEPT the rrq request */ + lpfc_els_rsp_acc(phba, ELS_CMD_ACC, cmdiocb, ndlp, NULL, 0); + + return 0; +} + +static int +lpfc_els_rcv_farp(struct lpfc_hba * phba, + struct lpfc_iocbq * cmdiocb, struct lpfc_nodelist * ndlp) +{ + struct lpfc_dmabuf *pcmd; + uint32_t *lp; + IOCB_t *icmd; + FARP *fp; + uint32_t cmd, cnt, did; + + icmd = &cmdiocb->iocb; + did = icmd->un.elsreq64.remoteID; + pcmd = (struct lpfc_dmabuf *) cmdiocb->context2; + lp = (uint32_t *) pcmd->virt; + + /* The response iocb was populated by the HBA. Flush it to main store + * for the driver. Note that all iocb context buffers are from the + * driver's dma pool and have length LPFC_BPL_SIZE. + */ + pci_dma_sync_single_for_cpu(phba->pcidev, pcmd->phys, + LPFC_BPL_SIZE, PCI_DMA_FROMDEVICE); + + cmd = *lp++; + fp = (FARP *) lp; + + /* FARP-REQ received from DID */ + lpfc_printf_log(phba, + KERN_INFO, + LOG_IP, + "%d:0601 FARP-REQ received from DID x%x\n", + phba->brd_no, did); + + /* We will only support match on WWPN or WWNN */ + if (fp->Mflags & ~(FARP_MATCH_NODE | FARP_MATCH_PORT)) { + return (0); + } + + cnt = 0; + /* If this FARP command is searching for my portname */ + if (fp->Mflags & FARP_MATCH_PORT) { + if (memcmp(&fp->RportName, &phba->fc_portname, + sizeof (struct lpfc_name)) == 0) + cnt = 1; + } + + /* If this FARP command is searching for my nodename */ + if (fp->Mflags & FARP_MATCH_NODE) { + if (memcmp(&fp->RnodeName, &phba->fc_nodename, + sizeof (struct lpfc_name)) == 0) + cnt = 1; + } + + if (cnt) { + if((ndlp->nlp_state == NLP_STE_UNMAPPED_NODE) || + (ndlp->nlp_state == NLP_STE_MAPPED_NODE)) { + /* Log back into the node before sending the FARP. */ + if (fp->Rflags & FARP_REQUEST_PLOGI) { + ndlp->nlp_state = NLP_STE_PLOGI_ISSUE; + lpfc_nlp_list(phba, ndlp, NLP_PLOGI_LIST); + lpfc_issue_els_plogi(phba, ndlp, 0); + } + + /* Send a FARP response to that node */ + if (fp->Rflags & FARP_REQUEST_FARPR) { + lpfc_issue_els_farpr(phba, did, 0); + } + } + } + return (0); +} + +static int +lpfc_els_rcv_farpr(struct lpfc_hba * phba, + struct lpfc_iocbq * cmdiocb, struct lpfc_nodelist * ndlp) +{ + struct lpfc_dmabuf *pcmd; + uint32_t *lp; + IOCB_t *icmd; + uint32_t cmd, did; + + icmd = &cmdiocb->iocb; + did = icmd->un.elsreq64.remoteID; + pcmd = (struct lpfc_dmabuf *) cmdiocb->context2; + lp = (uint32_t *) pcmd->virt; + + /* The response iocb was populated by the HBA. Flush it to main store + * for the driver. Note that all iocb context buffers are from the + * driver's dma pool and have length LPFC_BPL_SIZE. + */ + pci_dma_sync_single_for_cpu(phba->pcidev, pcmd->phys, + LPFC_BPL_SIZE, PCI_DMA_FROMDEVICE); + + cmd = *lp++; + /* FARP-RSP received from DID */ + lpfc_printf_log(phba, + KERN_INFO, + LOG_IP, + "%d:0600 FARP-RSP received from DID x%x\n", + phba->brd_no, did); + + /* ACCEPT the Farp resp request */ + lpfc_els_rsp_acc(phba, ELS_CMD_ACC, cmdiocb, ndlp, NULL, 0); + + return 0; +} + +static int +lpfc_els_rcv_fan(struct lpfc_hba * phba, struct lpfc_iocbq * cmdiocb, + struct lpfc_nodelist * ndlp) +{ + struct lpfc_dmabuf *pcmd; + uint32_t *lp; + IOCB_t *icmd; + FAN *fp; + uint32_t cmd, did; + + icmd = &cmdiocb->iocb; + did = icmd->un.elsreq64.remoteID; + pcmd = (struct lpfc_dmabuf *) cmdiocb->context2; + lp = (uint32_t *) pcmd->virt; + + /* The response iocb was populated by the HBA. Flush it to main store + * for the driver. Note that all iocb context buffers are from the + * driver's dma pool and have length LPFC_BPL_SIZE. + */ + pci_dma_sync_single_for_cpu(phba->pcidev, pcmd->phys, + LPFC_BPL_SIZE, PCI_DMA_FROMDEVICE); + + cmd = *lp++; + fp = (FAN *) lp; + + /* FAN received */ + + /* ACCEPT the FAN request */ + lpfc_els_rsp_acc(phba, ELS_CMD_ACC, cmdiocb, ndlp, NULL, 0); + + if (phba->hba_state == LPFC_LOCAL_CFG_LINK) { + /* The discovery state machine needs to take a different + * action if this node has switched fabrics + */ + if ((memcmp(&fp->FportName, &phba->fc_fabparam.portName, + sizeof (struct lpfc_name)) != 0) + || + (memcmp(&fp->FnodeName, &phba->fc_fabparam.nodeName, + sizeof (struct lpfc_name)) != 0)) { + /* This node has switched fabrics. An FLOGI is required + * after the timeout + */ + return (0); + } + + /* Start discovery */ + lpfc_disc_start(phba); + } + + return (0); +} + +void +lpfc_els_timeout_handler(unsigned long ptr) +{ + struct lpfc_hba *phba; + struct lpfc_sli *psli; + struct lpfc_sli_ring *pring; + struct lpfc_iocbq *tmp_iocb, *piocb; + IOCB_t *cmd = NULL; + struct lpfc_dmabuf *pcmd; + struct list_head *dlp; + uint32_t *elscmd; + uint32_t els_command; + uint32_t timeout; + uint32_t remote_ID; + unsigned long iflag; + + phba = (struct lpfc_hba *)ptr; + if(phba == 0) + return; + spin_lock_irqsave(phba->host->host_lock, iflag); + timeout = (uint32_t)(phba->fc_ratov << 1); + + psli = &phba->sli; + pring = &psli->ring[LPFC_ELS_RING]; + dlp = &pring->txcmplq; + + list_for_each_entry_safe(piocb, tmp_iocb, &pring->txcmplq, list) { + cmd = &piocb->iocb; + + if (piocb->iocb_flag & LPFC_IO_LIBDFC) { + continue; + } + pcmd = (struct lpfc_dmabuf *) piocb->context2; + elscmd = (uint32_t *) (pcmd->virt); + els_command = *elscmd; + + if ((els_command == ELS_CMD_FARP) + || (els_command == ELS_CMD_FARPR)) { + continue; + } + + if (piocb->drvrTimeout > 0) { + if (piocb->drvrTimeout >= timeout) { + piocb->drvrTimeout -= timeout; + } else { + piocb->drvrTimeout = 0; + } + continue; + } + + list_del(&piocb->list); + pring->txcmplq_cnt--; + + if (cmd->ulpCommand == CMD_GEN_REQUEST64_CR) { + struct lpfc_nodelist *ndlp; + + ndlp = lpfc_findnode_rpi(phba, cmd->ulpContext); + remote_ID = ndlp->nlp_DID; + if (cmd->un.elsreq64.bdl.ulpIoTag32) { + lpfc_sli_issue_abort_iotag32(phba, + pring, piocb); + } + } else { + remote_ID = cmd->un.elsreq64.remoteID; + } + + lpfc_printf_log(phba, + KERN_ERR, + LOG_ELS, + "%d:0127 ELS timeout Data: x%x x%x x%x x%x\n", + phba->brd_no, els_command, + remote_ID, cmd->ulpCommand, cmd->ulpIoTag); + + /* + * The iocb has timed out; abort it. + */ + if (piocb->iocb_cmpl) { + cmd->ulpStatus = IOSTAT_LOCAL_REJECT; + cmd->un.ulpWord[4] = IOERR_SLI_ABORTED; + (piocb->iocb_cmpl) (phba, piocb, piocb); + } else { + mempool_free(piocb, phba->iocb_mem_pool); + } + } + + phba->els_tmofunc.expires = jiffies + HZ * timeout; + add_timer(&phba->els_tmofunc); + spin_unlock_irqrestore(phba->host->host_lock, iflag); +} + +void +lpfc_els_flush_cmd(struct lpfc_hba * phba) +{ + struct lpfc_sli *psli; + struct lpfc_sli_ring *pring; + struct lpfc_iocbq *tmp_iocb, *piocb; + IOCB_t *cmd = NULL; + struct lpfc_dmabuf *pcmd; + uint32_t *elscmd; + uint32_t els_command; + uint32_t remote_ID; + + psli = &phba->sli; + pring = &psli->ring[LPFC_ELS_RING]; + + list_for_each_entry_safe(piocb, tmp_iocb, &pring->txq, list) { + cmd = &piocb->iocb; + + if (piocb->iocb_flag & LPFC_IO_LIBDFC) { + continue; + } + + /* Do not flush out the QUE_RING and ABORT/CLOSE iocbs */ + if ((cmd->ulpCommand == CMD_QUE_RING_BUF_CN) || + (cmd->ulpCommand == CMD_QUE_RING_BUF64_CN) || + (cmd->ulpCommand == CMD_CLOSE_XRI_CN) || + (cmd->ulpCommand == CMD_ABORT_XRI_CN)) { + continue; + } + + pcmd = (struct lpfc_dmabuf *) piocb->context2; + elscmd = (uint32_t *) (pcmd->virt); + els_command = *elscmd; + + if (cmd->ulpCommand == CMD_GEN_REQUEST64_CR) { + struct lpfc_nodelist *ndlp; + + ndlp = lpfc_findnode_rpi(phba, cmd->ulpContext); + remote_ID = ndlp->nlp_DID; + if (phba->hba_state == LPFC_HBA_READY) { + continue; + } + } else { + remote_ID = cmd->un.elsreq64.remoteID; + } + + list_del(&piocb->list); + pring->txcmplq_cnt--; + + cmd->ulpStatus = IOSTAT_LOCAL_REJECT; + cmd->un.ulpWord[4] = IOERR_SLI_ABORTED; + + if (piocb->iocb_cmpl) { + (piocb->iocb_cmpl) (phba, piocb, piocb); + } else { + mempool_free( piocb, phba->iocb_mem_pool); + } + } + + list_for_each_entry_safe(piocb, tmp_iocb, &pring->txcmplq, list) { + cmd = &piocb->iocb; + + if (piocb->iocb_flag & LPFC_IO_LIBDFC) { + continue; + } + pcmd = (struct lpfc_dmabuf *) piocb->context2; + elscmd = (uint32_t *) (pcmd->virt); + els_command = *elscmd; + + if (cmd->ulpCommand == CMD_GEN_REQUEST64_CR) { + struct lpfc_nodelist *ndlp; + + ndlp = lpfc_findnode_rpi(phba, cmd->ulpContext); + remote_ID = ndlp->nlp_DID; + if (phba->hba_state == LPFC_HBA_READY) { + continue; + } + } else { + remote_ID = cmd->un.elsreq64.remoteID; + } + + list_del(&piocb->list); + pring->txcmplq_cnt--; + + cmd->ulpStatus = IOSTAT_LOCAL_REJECT; + cmd->un.ulpWord[4] = IOERR_SLI_ABORTED; + + if (piocb->iocb_cmpl) { + (piocb->iocb_cmpl) (phba, piocb, piocb); + } else { + mempool_free( piocb, phba->iocb_mem_pool); + } + } + return; +} + +void +lpfc_els_unsol_event(struct lpfc_hba * phba, + struct lpfc_sli_ring * pring, struct lpfc_iocbq * elsiocb) +{ + struct lpfc_sli *psli; + struct lpfc_nodelist *ndlp; + struct lpfc_dmabuf *mp; + uint32_t *lp; + IOCB_t *icmd; + struct ls_rjt stat; + uint32_t cmd; + uint32_t did; + uint32_t newnode; + uint32_t drop_cmd = 0; /* by default do NOT drop received cmd */ + uint32_t rjt_err = 0; + + psli = &phba->sli; + icmd = &elsiocb->iocb; + + /* type of ELS cmd is first 32bit word in packet */ + mp = lpfc_sli_ringpostbuf_get(phba, pring, getPaddr(icmd->un. + cont64[0]. + addrHigh, + icmd->un. + cont64[0].addrLow)); + if (mp == 0) { + drop_cmd = 1; + goto dropit; + } + + newnode = 0; + lp = (uint32_t *) mp->virt; + cmd = *lp++; + lpfc_post_buffer(phba, &psli->ring[LPFC_ELS_RING], 1, 1); + + if (icmd->ulpStatus) { + lpfc_mbuf_free(phba, mp->virt, mp->phys); + kfree(mp); + drop_cmd = 1; + goto dropit; + } + + /* Check to see if link went down during discovery */ + if (lpfc_els_chk_latt(phba)) { + lpfc_mbuf_free(phba, mp->virt, mp->phys); + kfree(mp); + drop_cmd = 1; + goto dropit; + } + + did = icmd->un.rcvels.remoteID; + if ((ndlp = lpfc_findnode_did(phba, NLP_SEARCH_ALL, did)) == 0) { + /* Cannot find existing Fabric ndlp, so allocate a new one */ + if ((ndlp = mempool_alloc(phba->nlp_mem_pool, GFP_ATOMIC)) + == 0) { + lpfc_mbuf_free(phba, mp->virt, mp->phys); + kfree(mp); + drop_cmd = 1; + goto dropit; + } + + lpfc_nlp_init(phba, ndlp, did); + newnode = 1; + if ((did & Fabric_DID_MASK) == Fabric_DID_MASK) { + ndlp->nlp_type |= NLP_FABRIC; + } + } + + phba->fc_stat.elsRcvFrame++; + elsiocb->context1 = ndlp; + elsiocb->context2 = mp; + + if ((cmd & ELS_CMD_MASK) == ELS_CMD_RSCN) { + cmd &= ELS_CMD_MASK; + } + /* ELS command received from NPORT */ + lpfc_printf_log(phba, KERN_INFO, LOG_ELS, + "%d:0112 ELS command x%x received from NPORT x%x " + "Data: x%x\n", phba->brd_no, cmd, did, phba->hba_state); + + switch (cmd) { + case ELS_CMD_PLOGI: + phba->fc_stat.elsRcvPLOGI++; + if(phba->hba_state < LPFC_DISC_AUTH) { + rjt_err = LSEXP_NOTHING_MORE; + break; + } + lpfc_disc_state_machine(phba, ndlp, elsiocb, NLP_EVT_RCV_PLOGI); + break; + case ELS_CMD_FLOGI: + phba->fc_stat.elsRcvFLOGI++; + lpfc_els_rcv_flogi(phba, elsiocb, ndlp, newnode); + if (newnode) { + mempool_free( ndlp, phba->nlp_mem_pool); + } + break; + case ELS_CMD_LOGO: + phba->fc_stat.elsRcvLOGO++; + if(phba->hba_state < LPFC_DISC_AUTH) { + rjt_err = LSEXP_NOTHING_MORE; + break; + } + lpfc_disc_state_machine(phba, ndlp, elsiocb, NLP_EVT_RCV_LOGO); + break; + case ELS_CMD_PRLO: + phba->fc_stat.elsRcvPRLO++; + if(phba->hba_state < LPFC_DISC_AUTH) { + rjt_err = LSEXP_NOTHING_MORE; + break; + } + lpfc_disc_state_machine(phba, ndlp, elsiocb, NLP_EVT_RCV_PRLO); + break; + case ELS_CMD_RSCN: + phba->fc_stat.elsRcvRSCN++; + lpfc_els_rcv_rscn(phba, elsiocb, ndlp, newnode); + if (newnode) { + mempool_free( ndlp, phba->nlp_mem_pool); + } + break; + case ELS_CMD_ADISC: + phba->fc_stat.elsRcvADISC++; + if(phba->hba_state < LPFC_DISC_AUTH) { + rjt_err = LSEXP_NOTHING_MORE; + break; + } + lpfc_disc_state_machine(phba, ndlp, elsiocb, NLP_EVT_RCV_ADISC); + break; + case ELS_CMD_PDISC: + phba->fc_stat.elsRcvPDISC++; + if(phba->hba_state < LPFC_DISC_AUTH) { + rjt_err = LSEXP_NOTHING_MORE; + break; + } + lpfc_disc_state_machine(phba, ndlp, elsiocb, NLP_EVT_RCV_PDISC); + break; + case ELS_CMD_FARPR: + phba->fc_stat.elsRcvFARPR++; + lpfc_els_rcv_farpr(phba, elsiocb, ndlp); + break; + case ELS_CMD_FARP: + phba->fc_stat.elsRcvFARP++; + lpfc_els_rcv_farp(phba, elsiocb, ndlp); + break; + case ELS_CMD_FAN: + phba->fc_stat.elsRcvFAN++; + lpfc_els_rcv_fan(phba, elsiocb, ndlp); + break; + case ELS_CMD_RRQ: + phba->fc_stat.elsRcvRRQ++; + lpfc_els_rcv_rrq(phba, elsiocb, ndlp); + break; + case ELS_CMD_PRLI: + phba->fc_stat.elsRcvPRLI++; + if(phba->hba_state < LPFC_DISC_AUTH) { + rjt_err = LSEXP_NOTHING_MORE; + break; + } + lpfc_disc_state_machine(phba, ndlp, elsiocb, NLP_EVT_RCV_PRLI); + break; + case ELS_CMD_RNID: + phba->fc_stat.elsRcvRNID++; + lpfc_els_rcv_rnid(phba, elsiocb, ndlp); + break; + default: + /* Unsupported ELS command, reject */ + rjt_err = LSEXP_NOTHING_MORE; + + /* Unknown ELS command received from NPORT */ + lpfc_printf_log(phba, KERN_ERR, LOG_ELS, + "%d:0115 Unknown ELS command x%x received from " + "NPORT x%x\n", phba->brd_no, cmd, did); + if (newnode) { + mempool_free( ndlp, phba->nlp_mem_pool); + } + break; + } + + /* check if need to LS_RJT received ELS cmd */ + if (rjt_err) { + stat.un.b.lsRjtRsvd0 = 0; + stat.un.b.lsRjtRsnCode = LSRJT_UNABLE_TPC; + stat.un.b.lsRjtRsnCodeExp = rjt_err; + stat.un.b.vendorUnique = 0; + lpfc_els_rsp_reject(phba, stat.un.lsRjtError, elsiocb, ndlp); + } + + if (elsiocb->context2) { + lpfc_mbuf_free(phba, mp->virt, mp->phys); + kfree(mp); + } +dropit: + /* check if need to drop received ELS cmd */ + if (drop_cmd == 1) { + lpfc_printf_log(phba, KERN_ERR, LOG_ELS, + "%d:0111 Dropping received ELS cmd " + "Data: x%x x%x\n", phba->brd_no, + icmd->ulpStatus, icmd->un.ulpWord[4]); + phba->fc_stat.elsRcvDrop++; + } + return; +} diff --git a/drivers/scsi/lpfc/lpfc_fcp.c b/drivers/scsi/lpfc/lpfc_fcp.c new file mode 100644 index 000000000..a1ed8d9ea --- /dev/null +++ b/drivers/scsi/lpfc/lpfc_fcp.c @@ -0,0 +1,3419 @@ +/******************************************************************* + * This file is part of the Emulex Linux Device Driver for * + * Enterprise Fibre Channel Host Bus Adapters. * + * Refer to the README file included with this package for * + * driver version and adapter support. * + * Copyright (C) 2004 Emulex Corporation. * + * www.emulex.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, a copy of which * + * can be found in the file COPYING included with this package. * + *******************************************************************/ + +/* + * $Id: lpfc_fcp.c 1.431 2004/11/23 13:18:57EST sf_support Exp $ + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include + +#include "lpfc_sli.h" +#include "lpfc_disc.h" +#include "lpfc_scsi.h" +#include "lpfc.h" +#include "lpfc_fcp.h" +#include "lpfc_hw.h" +#include "lpfc_logmsg.h" +#include "lpfc_mem.h" +#include "lpfc_version.h" +#include "lpfc_compat.h" +#include "lpfc_crtn.h" + +static char *lpfc_drvr_name = LPFC_DRIVER_NAME; + +static struct scsi_transport_template *lpfc_transport_template = NULL; + +static struct list_head lpfc_hba_list = LIST_HEAD_INIT(lpfc_hba_list); + +static const char * +lpfc_info(struct Scsi_Host *host) +{ + struct lpfc_hba *phba = (struct lpfc_hba *) host->hostdata[0]; + int len; + static char lpfcinfobuf[128]; + + memset(lpfcinfobuf,0,128); + if (phba && phba->pcidev){ + lpfc_get_hba_model_desc(phba, NULL, lpfcinfobuf); + len = strlen(lpfcinfobuf); + snprintf(lpfcinfobuf + len, + 128-len, + " on PCI bus %02x device %02x irq %d", + phba->pcidev->bus->number, + phba->pcidev->devfn, + phba->pcidev->irq); + } + return lpfcinfobuf; +} + +static void +lpfc_jedec_to_ascii(int incr, char hdw[]) +{ + int i, j; + for (i = 0; i < 8; i++) { + j = (incr & 0xf); + if (j <= 9) + hdw[7 - i] = 0x30 + j; + else + hdw[7 - i] = 0x61 + j - 10; + incr = (incr >> 4); + } + hdw[8] = 0; + return; +} + +static ssize_t +lpfc_drvr_version_show(struct class_device *cdev, char *buf) +{ + return snprintf(buf, PAGE_SIZE, LPFC_MODULE_DESC "\n"); +} + +static ssize_t +management_version_show(struct class_device *cdev, char *buf) +{ + return snprintf(buf, PAGE_SIZE, DFC_API_VERSION "\n"); +} + +static ssize_t +lpfc_info_show(struct class_device *cdev, char *buf) +{ + struct Scsi_Host *host = class_to_shost(cdev); + return snprintf(buf, PAGE_SIZE, "%s\n",lpfc_info(host)); +} + +static ssize_t +lpfc_serialnum_show(struct class_device *cdev, char *buf) +{ + struct Scsi_Host *host = class_to_shost(cdev); + struct lpfc_hba *phba = (struct lpfc_hba*)host->hostdata[0]; + return snprintf(buf, PAGE_SIZE, "%s\n",phba->SerialNumber); +} + +static ssize_t +lpfc_fwrev_show(struct class_device *cdev, char *buf) +{ + struct Scsi_Host *host = class_to_shost(cdev); + struct lpfc_hba *phba = (struct lpfc_hba*)host->hostdata[0]; + char fwrev[32]; + lpfc_decode_firmware_rev(phba, fwrev, 1); + return snprintf(buf, PAGE_SIZE, "%s\n",fwrev); +} + +static ssize_t +lpfc_hdw_show(struct class_device *cdev, char *buf) +{ + char hdw[9]; + struct Scsi_Host *host = class_to_shost(cdev); + struct lpfc_hba *phba = (struct lpfc_hba*)host->hostdata[0]; + lpfc_vpd_t *vp = &phba->vpd; + lpfc_jedec_to_ascii(vp->rev.biuRev, hdw); + return snprintf(buf, PAGE_SIZE, "%s\n", hdw); +} +static ssize_t +lpfc_option_rom_version_show(struct class_device *cdev, char *buf) +{ + struct Scsi_Host *host = class_to_shost(cdev); + struct lpfc_hba *phba = (struct lpfc_hba*)host->hostdata[0]; + return snprintf(buf, PAGE_SIZE, "%s\n", phba->OptionROMVersion); +} +static ssize_t +lpfc_state_show(struct class_device *cdev, char *buf) +{ + struct Scsi_Host *host = class_to_shost(cdev); + struct lpfc_hba *phba = (struct lpfc_hba*)host->hostdata[0]; + int len = 0; + switch (phba->hba_state) { + case LPFC_INIT_START: + case LPFC_INIT_MBX_CMDS: + case LPFC_LINK_DOWN: + len += snprintf(buf + len, PAGE_SIZE-len, "Link Down\n"); + break; + case LPFC_LINK_UP: + case LPFC_LOCAL_CFG_LINK: + len += snprintf(buf + len, PAGE_SIZE-len, "Link Up\n"); + break; + case LPFC_FLOGI: + case LPFC_FABRIC_CFG_LINK: + case LPFC_NS_REG: + case LPFC_NS_QRY: + case LPFC_BUILD_DISC_LIST: + case LPFC_DISC_AUTH: + case LPFC_CLEAR_LA: + len += snprintf(buf + len, PAGE_SIZE-len, + "Link Up - Discovery\n"); + break; + case LPFC_HBA_READY: + len += snprintf(buf + len, PAGE_SIZE-len, + "Link Up - Ready:\n"); + if (phba->fc_topology == TOPOLOGY_LOOP) { + if (phba->fc_flag & FC_PUBLIC_LOOP) + len += snprintf(buf + len, PAGE_SIZE-len, + " Public Loop\n"); + else + len += snprintf(buf + len, PAGE_SIZE-len, + " Private Loop\n"); + } else { + if (phba->fc_flag & FC_FABRIC) + len += snprintf(buf + len, PAGE_SIZE-len, + " Fabric\n"); + else + len += snprintf(buf + len, PAGE_SIZE-len, + " Point-2-Point\n"); + } + } + return len; +} + +static ssize_t +lpfc_num_discovered_ports_show(struct class_device *cdev, char *buf) +{ + struct Scsi_Host *host = class_to_shost(cdev); + struct lpfc_hba *phba = (struct lpfc_hba*)host->hostdata[0]; + return snprintf(buf, PAGE_SIZE, "%d\n", phba->fc_map_cnt + + phba->fc_unmap_cnt); +} + +#ifndef FC_TRANS_VER2 +/* + * These are replaced by Generic FC transport attributes + */ +static ssize_t +lpfc_speed_show(struct class_device *cdev, char *buf) +{ + struct Scsi_Host *host = class_to_shost(cdev); + struct lpfc_hba *phba = (struct lpfc_hba*)host->hostdata[0]; + int len = 0; + if (phba->fc_linkspeed == LA_4GHZ_LINK) + len += snprintf(buf + len, PAGE_SIZE-len, "4 Gigabit\n"); + else + if (phba->fc_linkspeed == LA_2GHZ_LINK) + len += snprintf(buf + len, PAGE_SIZE-len, "2 Gigabit\n"); + else + len += snprintf(buf + len, PAGE_SIZE-len, "1 Gigabit\n"); + return len; +} + +static ssize_t +lpfc_node_name_show(struct class_device *cdev, char *buf) +{ + struct Scsi_Host *host = class_to_shost(cdev); + struct lpfc_hba *phba = (struct lpfc_hba*)host->hostdata[0]; + uint64_t node_name = 0; + memcpy (&node_name, &phba->fc_nodename, sizeof (struct lpfc_name)); + return snprintf(buf, PAGE_SIZE, "0x%llx\n", be64_to_cpu(node_name)); +} +static ssize_t +lpfc_port_name_show(struct class_device *cdev, char *buf) +{ + struct Scsi_Host *host = class_to_shost(cdev); + struct lpfc_hba *phba = (struct lpfc_hba*)host->hostdata[0]; + uint64_t port_name = 0; + memcpy (&port_name, &phba->fc_portname, sizeof (struct lpfc_name)); + return snprintf(buf, PAGE_SIZE, "0x%llx\n", be64_to_cpu(port_name)); +} +static ssize_t +lpfc_did_show(struct class_device *cdev, char *buf) +{ + struct Scsi_Host *host = class_to_shost(cdev); + struct lpfc_hba *phba = (struct lpfc_hba*)host->hostdata[0]; + return snprintf(buf, PAGE_SIZE, "0x%x\n", phba->fc_myDID); +} + +static ssize_t +lpfc_port_type_show(struct class_device *cdev, char *buf) +{ + struct Scsi_Host *host = class_to_shost(cdev); + struct lpfc_hba *phba = (struct lpfc_hba*)host->hostdata[0]; + + size_t retval = 0; + + if (phba->fc_topology == TOPOLOGY_LOOP) { + if (phba->fc_flag & FC_PUBLIC_LOOP) + retval = snprintf(buf, PAGE_SIZE, "NL_Port\n"); + else + retval = snprintf(buf, PAGE_SIZE, "L_Port\n"); + } else { + if (phba->fc_flag & FC_FABRIC) + retval = snprintf(buf, PAGE_SIZE, "N_Port\n"); + else + retval = snprintf(buf, PAGE_SIZE, + "Point-to-Point N_Port\n"); + } + + return retval; +} + +static ssize_t +lpfc_fabric_name_show(struct class_device *cdev, char *buf) +{ + struct Scsi_Host *host = class_to_shost(cdev); + struct lpfc_hba *phba = (struct lpfc_hba*)host->hostdata[0]; + uint64_t node_name = 0; + memcpy (&node_name, &phba->fc_nodename, sizeof (struct lpfc_name)); + + if ((phba->fc_flag & FC_FABRIC) || + ((phba->fc_topology == TOPOLOGY_LOOP) && + (phba->fc_flag & FC_PUBLIC_LOOP))) { + memcpy(&node_name, + & phba->fc_fabparam.nodeName, + sizeof (struct lpfc_name)); + } + + return snprintf(buf, PAGE_SIZE, "0x%08llx\n", be64_to_cpu(node_name)); +} +#endif /* not FC_TRANS_VER2 */ + +static ssize_t +lpfc_events_show(struct class_device *cdev, char *buf) +{ + struct Scsi_Host *host = class_to_shost(cdev); + struct lpfc_hba *phba = (struct lpfc_hba*)host->hostdata[0]; + int i = 0, len = 0, get = phba->hba_event_put; + struct lpfc_hba_event *rec; + + if (get == phba->hba_event_get) + return snprintf(buf, PAGE_SIZE, "None\n"); + + for (i = 0; i < MAX_HBAEVT; i++) { + if (get == 0) + get = MAX_HBAEVT; + get--; + rec = &phba->hbaevt[get]; + switch (rec->fc_eventcode) { + case 0: + len += snprintf(buf+len, PAGE_SIZE-len, + "---------"); + break; + case HBA_EVENT_RSCN: + len += snprintf(buf+len, PAGE_SIZE-len, + "RSCN "); + break; + case HBA_EVENT_LINK_UP: + len += snprintf(buf+len, PAGE_SIZE-len, + "LINK UP "); + break; + case HBA_EVENT_LINK_DOWN: + len += snprintf(buf+len, PAGE_SIZE-len, + "LINK DOWN"); + break; + default: + len += snprintf(buf+len, PAGE_SIZE-len, + "?????????"); + break; + + } + len += snprintf(buf+len, PAGE_SIZE-len, " %d,%d,%d,%d\n", + rec->fc_evdata1, rec->fc_evdata2, + rec->fc_evdata3, rec->fc_evdata4); + } + return len; +} + +static ssize_t +lpfc_issue_lip (struct class_device *cdev, const char *buf, size_t count) +{ + struct Scsi_Host *host = class_to_shost(cdev); + struct lpfc_hba *phba = (struct lpfc_hba *) host->hostdata[0]; + int val = 0; + LPFC_MBOXQ_t *pmboxq; + int mbxstatus = MBXERR_ERROR; + + if ((sscanf(buf, "%d", &val) != 1) || + (val != 1)) + return -EINVAL; + + if ((phba->fc_flag & FC_OFFLINE_MODE) || + (phba->hba_state != LPFC_HBA_READY)) + return -EPERM; + + pmboxq = mempool_alloc(phba->mbox_mem_pool,GFP_KERNEL); + + if (!pmboxq) + return -ENOMEM; + + memset((void *)pmboxq, 0, sizeof (LPFC_MBOXQ_t)); + lpfc_init_link(phba, pmboxq, phba->cfg_topology, phba->cfg_link_speed); + mbxstatus = lpfc_sli_issue_mbox_wait(phba, pmboxq, phba->fc_ratov * 2); + + if (mbxstatus == MBX_TIMEOUT) + pmboxq->mbox_cmpl = NULL; + else + mempool_free( pmboxq, phba->mbox_mem_pool); + + if (mbxstatus == MBXERR_ERROR) + return -EIO; + + return strlen(buf); +} + +static ssize_t +lpfc_nport_evt_cnt_show(struct class_device *cdev, char *buf) +{ + struct Scsi_Host *host = class_to_shost(cdev); + struct lpfc_hba *phba = (struct lpfc_hba*)host->hostdata[0]; + return snprintf(buf, PAGE_SIZE, "%d\n", phba->nport_event_cnt); +} + +static ssize_t +lpfc_board_online_show(struct class_device *cdev, char *buf) +{ + struct Scsi_Host *host = class_to_shost(cdev); + struct lpfc_hba *phba = (struct lpfc_hba*)host->hostdata[0]; + + if (!phba) return 0; + + if (phba->fc_flag & FC_OFFLINE_MODE) + return snprintf(buf, PAGE_SIZE, "0\n"); + else + return snprintf(buf, PAGE_SIZE, "1\n"); +} + +static ssize_t +lpfc_board_online_store(struct class_device *cdev, const char *buf, + size_t count) +{ + struct Scsi_Host *host = class_to_shost(cdev); + struct lpfc_hba *phba = (struct lpfc_hba*)host->hostdata[0]; + int val=0; + + if (!phba) return 0; + + if (sscanf(buf, "%d", &val) != 1) + return 0; + + if (val && (phba->fc_flag & FC_OFFLINE_MODE)) { + lpfc_online(phba); + } + else if (!val && !(phba->fc_flag & FC_OFFLINE_MODE)) { + lpfc_offline(phba); + } + + return strlen(buf); +} + +static int +lpfc_disc_ndlp_show(struct lpfc_hba * phba, struct lpfc_nodelist *ndlp, + char *buf, int offset) +{ + int len = 0, pgsz = PAGE_SIZE; + uint8_t name[sizeof (struct lpfc_name)]; + + buf += offset; + pgsz -= offset; + len += snprintf(buf + len, pgsz -len, + "DID %06x WWPN ", ndlp->nlp_DID); + + /* A Fibre Channel node or port name is 8 octets + * long and delimited by colons. + */ + memcpy (&name[0], &ndlp->nlp_portname, + sizeof (struct lpfc_name)); + len += snprintf(buf + len, pgsz-len, + "%02x:%02x:%02x:%02x:%02x:%02x:" + "%02x:%02x", + name[0], name[1], name[2], + name[3], name[4], name[5], + name[6], name[7]); + + len += snprintf(buf + len, pgsz-len, + " WWNN "); + memcpy (&name[0], &ndlp->nlp_nodename, + sizeof (struct lpfc_name)); + len += snprintf(buf + len, pgsz-len, + "%02x:%02x:%02x:%02x:%02x:%02x:" + "%02x:%02x\n", + name[0], name[1], name[2], + name[3], name[4], name[5], + name[6], name[7]); + len += snprintf(buf + len, pgsz-len, + " INFO %02x:%08x:%02x:%02x:%02x:%02x:" + "%02x:%02x:%02x\n", + ndlp->nlp_state, ndlp->nlp_flag, ndlp->nlp_type, + ndlp->nlp_rpi, ndlp->nlp_sid, ndlp->nlp_failMask, + ndlp->nlp_retry, ndlp->nlp_disc_refcnt, + ndlp->nlp_fcp_info); + return len; +} + +#define LPFC_MAX_SYS_DISC_ENTRIES 35 + +static ssize_t +lpfc_disc_npr_show(struct class_device *cdev, char *buf) +{ + struct Scsi_Host *host = class_to_shost(cdev); + struct lpfc_hba *phba = (struct lpfc_hba*)host->hostdata[0]; + struct lpfc_nodelist *ndlp, *next_ndlp; + struct list_head *listp; + unsigned long iflag; + int i = 0, len = 0; + + if (!phba) return 0; + + spin_lock_irqsave(phba->host->host_lock, iflag); + listp = &phba->fc_npr_list; + if (list_empty(listp)) { + spin_unlock_irqrestore(phba->host->host_lock, iflag); + return snprintf(buf, PAGE_SIZE, "NPR list: Empty\n"); + } + + len += snprintf(buf+len, PAGE_SIZE-len, "NPR list: %d Entries\n", + phba->fc_npr_cnt); + list_for_each_entry_safe(ndlp, next_ndlp, listp, nlp_listp) { + i++; + if(i > LPFC_MAX_SYS_DISC_ENTRIES) { + len += snprintf(buf+len, PAGE_SIZE-len, + "Missed %d entries - sysfs %ld limit exceeded\n", + (phba->fc_npr_cnt - i + 1), PAGE_SIZE); + break; + } + if(len > (PAGE_SIZE-1)) /* double check */ + break; + len += lpfc_disc_ndlp_show(phba, ndlp, buf, len); + } + spin_unlock_irqrestore(phba->host->host_lock, iflag); + return len; +} + +static ssize_t +lpfc_disc_map_show(struct class_device *cdev, char *buf) +{ + struct Scsi_Host *host = class_to_shost(cdev); + struct lpfc_hba *phba = (struct lpfc_hba*)host->hostdata[0]; + struct lpfc_nodelist *ndlp, *next_ndlp; + struct list_head *listp; + unsigned long iflag; + int i = 0, len = 0; + + if (!phba) return 0; + + spin_lock_irqsave(phba->host->host_lock, iflag); + listp = &phba->fc_nlpmap_list; + if (list_empty(listp)) { + spin_unlock_irqrestore(phba->host->host_lock, iflag); + return snprintf(buf, PAGE_SIZE, "Map list: Empty\n"); + } + + len += snprintf(buf+len, PAGE_SIZE-len, "Map list: %d Entries\n", + phba->fc_map_cnt); + list_for_each_entry_safe(ndlp, next_ndlp, listp, nlp_listp) { + i++; + if(i > LPFC_MAX_SYS_DISC_ENTRIES) { + len += snprintf(buf+len, PAGE_SIZE-len, + "Missed %d entries - sysfs %ld limit exceeded\n", + (phba->fc_map_cnt - i + 1), PAGE_SIZE); + break; + } + if(len > (PAGE_SIZE-1)) /* double check */ + break; + len += lpfc_disc_ndlp_show(phba, ndlp, buf, len); + } + spin_unlock_irqrestore(phba->host->host_lock, iflag); + return len; +} + +static ssize_t +lpfc_disc_unmap_show(struct class_device *cdev, char *buf) +{ + struct Scsi_Host *host = class_to_shost(cdev); + struct lpfc_hba *phba = (struct lpfc_hba*)host->hostdata[0]; + struct lpfc_nodelist *ndlp, *next_ndlp; + struct list_head *listp; + unsigned long iflag; + int i = 0, len = 0; + + if (!phba) return 0; + + spin_lock_irqsave(phba->host->host_lock, iflag); + listp = &phba->fc_nlpunmap_list; + if (list_empty(listp)) { + spin_unlock_irqrestore(phba->host->host_lock, iflag); + return snprintf(buf, PAGE_SIZE, "Unmap list: Empty\n"); + } + + len += snprintf(buf+len, PAGE_SIZE-len, "Unmap list: %d Entries\n", + phba->fc_unmap_cnt); + list_for_each_entry_safe(ndlp, next_ndlp, listp, nlp_listp) { + i++; + if(i > LPFC_MAX_SYS_DISC_ENTRIES) { + len += snprintf(buf+len, PAGE_SIZE-len, + "Missed %d entries - sysfs %ld limit exceeded\n", + (phba->fc_unmap_cnt - i + 1), PAGE_SIZE); + break; + } + if(len > (PAGE_SIZE-1)) /* double check */ + break; + len += lpfc_disc_ndlp_show(phba, ndlp, buf, len); + } + spin_unlock_irqrestore(phba->host->host_lock, iflag); + return len; +} + +static ssize_t +lpfc_disc_prli_show(struct class_device *cdev, char *buf) +{ + struct Scsi_Host *host = class_to_shost(cdev); + struct lpfc_hba *phba = (struct lpfc_hba*)host->hostdata[0]; + struct lpfc_nodelist *ndlp, *next_ndlp; + struct list_head *listp; + unsigned long iflag; + int i = 0, len = 0; + + if (!phba) return 0; + + spin_lock_irqsave(phba->host->host_lock, iflag); + listp = &phba->fc_prli_list; + if (list_empty(listp)) { + spin_unlock_irqrestore(phba->host->host_lock, iflag); + return snprintf(buf, PAGE_SIZE, "PRLI list: Empty\n"); + } + + len += snprintf(buf+len, PAGE_SIZE-len, "PRLI list: %d Entries\n", + phba->fc_prli_cnt); + list_for_each_entry_safe(ndlp, next_ndlp, listp, nlp_listp) { + i++; + if(i > LPFC_MAX_SYS_DISC_ENTRIES) { + len += snprintf(buf+len, PAGE_SIZE-len, + "Missed %d entries - sysfs %ld limit exceeded\n", + (phba->fc_prli_cnt - i + 1), PAGE_SIZE); + break; + } + if(len > (PAGE_SIZE-1)) /* double check */ + break; + len += lpfc_disc_ndlp_show(phba, ndlp, buf, len); + } + spin_unlock_irqrestore(phba->host->host_lock, iflag); + return len; +} + +static ssize_t +lpfc_disc_reglgn_show(struct class_device *cdev, char *buf) +{ + struct Scsi_Host *host = class_to_shost(cdev); + struct lpfc_hba *phba = (struct lpfc_hba*)host->hostdata[0]; + struct lpfc_nodelist *ndlp, *next_ndlp; + struct list_head *listp; + unsigned long iflag; + int i = 0, len = 0; + + if (!phba) return 0; + + spin_lock_irqsave(phba->host->host_lock, iflag); + listp = &phba->fc_reglogin_list; + if (list_empty(listp)) { + spin_unlock_irqrestore(phba->host->host_lock, iflag); + return snprintf(buf, PAGE_SIZE, "RegLgn list: Empty\n"); + } + + len += snprintf(buf+len, PAGE_SIZE-len, "RegLgn list: %d Entries\n", + phba->fc_reglogin_cnt); + list_for_each_entry_safe(ndlp, next_ndlp, listp, nlp_listp) { + i++; + if(i > LPFC_MAX_SYS_DISC_ENTRIES) { + len += snprintf(buf+len, PAGE_SIZE-len, + "Missed %d entries - sysfs %ld limit exceeded\n", + (phba->fc_reglogin_cnt - i + 1), PAGE_SIZE); + break; + } + if(len > (PAGE_SIZE-1)) /* double check */ + break; + len += lpfc_disc_ndlp_show(phba, ndlp, buf, len); + } + spin_unlock_irqrestore(phba->host->host_lock, iflag); + return len; +} + +static ssize_t +lpfc_disc_adisc_show(struct class_device *cdev, char *buf) +{ + struct Scsi_Host *host = class_to_shost(cdev); + struct lpfc_hba *phba = (struct lpfc_hba*)host->hostdata[0]; + struct lpfc_nodelist *ndlp, *next_ndlp; + struct list_head *listp; + unsigned long iflag; + int i = 0, len = 0; + + if (!phba) return 0; + + spin_lock_irqsave(phba->host->host_lock, iflag); + listp = &phba->fc_adisc_list; + if (list_empty(listp)) { + spin_unlock_irqrestore(phba->host->host_lock, iflag); + return snprintf(buf, PAGE_SIZE, "ADISC list: Empty\n"); + } + + len += snprintf(buf+len, PAGE_SIZE-len, "ADISC list: %d Entries\n", + phba->fc_adisc_cnt); + list_for_each_entry_safe(ndlp, next_ndlp, listp, nlp_listp) { + i++; + if(i > LPFC_MAX_SYS_DISC_ENTRIES) { + len += snprintf(buf+len, PAGE_SIZE-len, + "Missed %d entries - sysfs %ld limit exceeded\n", + (phba->fc_adisc_cnt - i + 1), PAGE_SIZE); + break; + } + if(len > (PAGE_SIZE-1)) /* double check */ + break; + len += lpfc_disc_ndlp_show(phba, ndlp, buf, len); + } + spin_unlock_irqrestore(phba->host->host_lock, iflag); + return len; +} + +static ssize_t +lpfc_disc_plogi_show(struct class_device *cdev, char *buf) +{ + struct Scsi_Host *host = class_to_shost(cdev); + struct lpfc_hba *phba = (struct lpfc_hba*)host->hostdata[0]; + struct lpfc_nodelist *ndlp, *next_ndlp; + struct list_head *listp; + unsigned long iflag; + int i = 0, len = 0; + + if (!phba) return 0; + + spin_lock_irqsave(phba->host->host_lock, iflag); + listp = &phba->fc_plogi_list; + if (list_empty(listp)) { + spin_unlock_irqrestore(phba->host->host_lock, iflag); + return snprintf(buf, PAGE_SIZE, "PLOGI list: Empty\n"); + } + + len += snprintf(buf+len, PAGE_SIZE-len, "PLOGI list: %d Entries\n", + phba->fc_plogi_cnt); + list_for_each_entry_safe(ndlp, next_ndlp, listp, nlp_listp) { + i++; + if(i > LPFC_MAX_SYS_DISC_ENTRIES) { + len += snprintf(buf+len, PAGE_SIZE-len, + "Missed %d entries - sysfs %ld limit exceeded\n", + (phba->fc_plogi_cnt - i + 1), PAGE_SIZE); + break; + } + if(len > (PAGE_SIZE-1)) /* double check */ + break; + len += lpfc_disc_ndlp_show(phba, ndlp, buf, len); + } + spin_unlock_irqrestore(phba->host->host_lock, iflag); + return len; +} + +static ssize_t +lpfc_disc_unused_show(struct class_device *cdev, char *buf) +{ + struct Scsi_Host *host = class_to_shost(cdev); + struct lpfc_hba *phba = (struct lpfc_hba*)host->hostdata[0]; + struct lpfc_nodelist *ndlp, *next_ndlp; + struct list_head *listp; + unsigned long iflag; + int i = 0, len = 0; + + if (!phba) return 0; + + spin_lock_irqsave(phba->host->host_lock, iflag); + listp = &phba->fc_unused_list; + if (list_empty(listp)) { + spin_unlock_irqrestore(phba->host->host_lock, iflag); + return snprintf(buf, PAGE_SIZE, "Unused list: Empty\n"); + } + + len += snprintf(buf+len, PAGE_SIZE-len, "Unused list: %d Entries\n", + phba->fc_unused_cnt); + list_for_each_entry_safe(ndlp, next_ndlp, listp, nlp_listp) { + i++; + if(i > LPFC_MAX_SYS_DISC_ENTRIES) { + len += snprintf(buf+len, PAGE_SIZE-len, + "Missed %d entries - sysfs %ld limit exceeded\n", + (phba->fc_unused_cnt - i + 1), PAGE_SIZE); + break; + } + if(len > (PAGE_SIZE-1)) /* double check */ + break; + len += lpfc_disc_ndlp_show(phba, ndlp, buf, len); + } + spin_unlock_irqrestore(phba->host->host_lock, iflag); + return len; +} + +#define LPFC_MAX_SYS_OUTFCPIO_ENTRIES 50 + +static ssize_t +lpfc_outfcpio_show(struct class_device *cdev, char *buf) +{ + struct Scsi_Host *host = class_to_shost(cdev); + struct lpfc_hba *phba = (struct lpfc_hba*)host->hostdata[0]; + struct lpfc_sli *psli; + struct lpfc_sli_ring *pring; + struct lpfc_target *targetp; + struct lpfc_nodelist *ndlp; + struct lpfc_scsi_buf *lpfc_cmd; + struct list_head *curr, *next; + struct lpfc_iocbq *iocb; + struct lpfc_iocbq *next_iocb; + IOCB_t *cmd; + unsigned long iflag; + int i = 0, len = 0; + int cnt = 0, unused = 0, total = 0; + int tx_count, txcmpl_count; + + if (!phba) return 0; + psli = &phba->sli; + pring = &psli->ring[psli->fcp_ring]; + + + spin_lock_irqsave(phba->host->host_lock, iflag); + + for(i=0;idevice_queue_hash[i]; + if(targetp) { + if(cnt >= LPFC_MAX_SYS_OUTFCPIO_ENTRIES) { + unused++; + continue; + } + cnt++; + len += snprintf(buf+len, PAGE_SIZE-len, + "ID %03d:qcmd %08x done %08x err %08x " + "slv %03x ", targetp->scsi_id, targetp->qcmdcnt, + targetp->iodonecnt, targetp->errorcnt, + targetp->slavecnt); + total += (targetp->qcmdcnt - targetp->iodonecnt); + + tx_count = 0; + txcmpl_count = 0; + + /* Count I/Os on txq and txcmplq. */ + list_for_each_safe(curr, next, &pring->txq) { + next_iocb = list_entry(curr, struct lpfc_iocbq, + list); + iocb = next_iocb; + cmd = &iocb->iocb; + + /* Must be a FCP command */ + if ((cmd->ulpCommand != CMD_FCP_ICMND64_CR) && + (cmd->ulpCommand != CMD_FCP_IWRITE64_CR) && + (cmd->ulpCommand != CMD_FCP_IREAD64_CR)) { + continue; + } + + /* context1 MUST be a struct lpfc_scsi_buf */ + lpfc_cmd = + (struct lpfc_scsi_buf *) (iocb->context1); + if ((lpfc_cmd == 0) + || (lpfc_cmd->pCmd->device->id != + targetp->scsi_id)) { + continue; + } + tx_count++; + } + + /* Next check the txcmplq */ + list_for_each_safe(curr, next, &pring->txcmplq) { + next_iocb = list_entry(curr, struct lpfc_iocbq, + list); + iocb = next_iocb; + cmd = &iocb->iocb; + + /* Must be a FCP command */ + if ((cmd->ulpCommand != CMD_FCP_ICMND64_CR) && + (cmd->ulpCommand != CMD_FCP_IWRITE64_CR) && + (cmd->ulpCommand != CMD_FCP_IREAD64_CR)) { + continue; + } + + /* context1 MUST be a struct lpfc_scsi_buf */ + lpfc_cmd = + (struct lpfc_scsi_buf *) (iocb->context1); + if ((lpfc_cmd == 0) + || (lpfc_cmd->pCmd->device->id != + targetp->scsi_id)) { + continue; + } + + txcmpl_count++; + } + len += snprintf(buf+len, PAGE_SIZE-len, + "tx %04x txc %04x ", + tx_count, txcmpl_count); + + ndlp = targetp->pnode; + if(ndlp == NULL) { + len += snprintf(buf+len, PAGE_SIZE-len, + "DISAPPERED\n"); + } + else { + if(ndlp->nlp_state == NLP_STE_MAPPED_NODE) { + len += snprintf(buf+len, PAGE_SIZE-len, + "MAPPED\n"); + } + else { + len += snprintf(buf+len, PAGE_SIZE-len, + "RECOVERY (%d)\n", + ndlp->nlp_state); + } + } + } + if(len > (PAGE_SIZE-1)) /* double check */ + break; + } + if(unused) { + len += snprintf(buf+len, PAGE_SIZE-len, + "Missed x%x entries - sysfs %ld limit exceeded\n", + unused, PAGE_SIZE); + } + len += snprintf(buf+len, PAGE_SIZE-len, + "x%x total I/Os outstanding\n", total); + + spin_unlock_irqrestore(phba->host->host_lock, iflag); + return len; +} + +#define lpfc_param_show(attr) \ +static ssize_t \ +lpfc_##attr##_show(struct class_device *cdev, char *buf) \ +{ \ + struct Scsi_Host *host = class_to_shost(cdev);\ + struct lpfc_hba *phba = (struct lpfc_hba*)host->hostdata[0];\ + int val = 0;\ + if (phba){\ + val = phba->cfg_##attr;\ + return snprintf(buf, PAGE_SIZE, "%d\n",\ + phba->cfg_##attr);\ + }\ + return 0;\ +} + +#define lpfc_param_store(attr, minval, maxval) \ +static ssize_t \ +lpfc_##attr##_store(struct class_device *cdev, const char *buf, size_t count) \ +{ \ + struct Scsi_Host *host = class_to_shost(cdev);\ + struct lpfc_hba *phba = (struct lpfc_hba*)host->hostdata[0];\ + int val=0;\ + if (sscanf(buf, "%d", &val) != 1)\ + return 0;\ + if (phba){\ + if (val >= minval && val <= maxval) {\ + phba->cfg_##attr = val;\ + return strlen(buf);\ + }\ + }\ + return 0;\ +} + +#define LPFC_ATTR_R_NOINIT(name, desc) \ +extern int lpfc_##name;\ +module_param(lpfc_##name, int, 0);\ +MODULE_PARM_DESC(lpfc_##name, desc);\ +lpfc_param_show(name)\ +static CLASS_DEVICE_ATTR(lpfc_##name, S_IRUGO , lpfc_##name##_show, NULL) + +#define LPFC_ATTR_R(name, defval, minval, maxval, desc) \ +static int lpfc_##name = defval;\ +module_param(lpfc_##name, int, 0);\ +MODULE_PARM_DESC(lpfc_##name, desc);\ +lpfc_param_show(name)\ +static CLASS_DEVICE_ATTR(lpfc_##name, S_IRUGO , lpfc_##name##_show, NULL) + +#define LPFC_ATTR_RW(name, defval, minval, maxval, desc) \ +static int lpfc_##name = defval;\ +module_param(lpfc_##name, int, 0);\ +MODULE_PARM_DESC(lpfc_##name, desc);\ +lpfc_param_show(name)\ +lpfc_param_store(name, minval, maxval)\ +static CLASS_DEVICE_ATTR(lpfc_##name, S_IRUGO | S_IWUSR,\ + lpfc_##name##_show, lpfc_##name##_store) + +static CLASS_DEVICE_ATTR(info, S_IRUGO, lpfc_info_show, NULL); +static CLASS_DEVICE_ATTR(serialnum, S_IRUGO, lpfc_serialnum_show, NULL); +static CLASS_DEVICE_ATTR(fwrev, S_IRUGO, lpfc_fwrev_show, NULL); +static CLASS_DEVICE_ATTR(hdw, S_IRUGO, lpfc_hdw_show, NULL); +static CLASS_DEVICE_ATTR(state, S_IRUGO, lpfc_state_show, NULL); +static CLASS_DEVICE_ATTR(option_rom_version, S_IRUGO, + lpfc_option_rom_version_show, NULL); +static CLASS_DEVICE_ATTR(num_discovered_ports, S_IRUGO, + lpfc_num_discovered_ports_show, NULL); +#ifndef FC_TRANS_VER2 +static CLASS_DEVICE_ATTR(speed, S_IRUGO, lpfc_speed_show, NULL); +static CLASS_DEVICE_ATTR(node_name, S_IRUGO, lpfc_node_name_show, NULL); +static CLASS_DEVICE_ATTR(port_name, S_IRUGO, lpfc_port_name_show, NULL); +static CLASS_DEVICE_ATTR(portfcid, S_IRUGO, lpfc_did_show, NULL); +static CLASS_DEVICE_ATTR(port_type, S_IRUGO, lpfc_port_type_show, NULL); +static CLASS_DEVICE_ATTR(fabric_name, S_IRUGO, lpfc_fabric_name_show, NULL); +#endif /* FC_TRANS_VER2 */ +static CLASS_DEVICE_ATTR(events, S_IRUGO, lpfc_events_show, NULL); +static CLASS_DEVICE_ATTR(nport_evt_cnt, S_IRUGO, lpfc_nport_evt_cnt_show, NULL); +static CLASS_DEVICE_ATTR(lpfc_drvr_version, S_IRUGO, lpfc_drvr_version_show, + NULL); +static CLASS_DEVICE_ATTR(management_version, S_IRUGO, management_version_show, + NULL); +static CLASS_DEVICE_ATTR(issue_lip, S_IWUSR, NULL, lpfc_issue_lip); +static CLASS_DEVICE_ATTR(board_online, S_IRUGO | S_IWUSR, + lpfc_board_online_show, lpfc_board_online_store); + +static CLASS_DEVICE_ATTR(disc_npr, S_IRUGO, lpfc_disc_npr_show, NULL); +static CLASS_DEVICE_ATTR(disc_map, S_IRUGO, lpfc_disc_map_show, NULL); +static CLASS_DEVICE_ATTR(disc_unmap, S_IRUGO, lpfc_disc_unmap_show, NULL); +static CLASS_DEVICE_ATTR(disc_prli, S_IRUGO, lpfc_disc_prli_show, NULL); +static CLASS_DEVICE_ATTR(disc_reglgn, S_IRUGO, lpfc_disc_reglgn_show, NULL); +static CLASS_DEVICE_ATTR(disc_adisc, S_IRUGO, lpfc_disc_adisc_show, NULL); +static CLASS_DEVICE_ATTR(disc_plogi, S_IRUGO, lpfc_disc_plogi_show, NULL); +static CLASS_DEVICE_ATTR(disc_unused, S_IRUGO, lpfc_disc_unused_show, NULL); +static CLASS_DEVICE_ATTR(outfcpio, S_IRUGO, lpfc_outfcpio_show, NULL); + +/* +# lpfc_log_verbose: Only turn this flag on if you are willing to risk being +# deluged with LOTS of information. +# You can set a bit mask to record specific types of verbose messages: +# +# LOG_ELS 0x1 ELS events +# LOG_DISCOVERY 0x2 Link discovery events +# LOG_MBOX 0x4 Mailbox events +# LOG_INIT 0x8 Initialization events +# LOG_LINK_EVENT 0x10 Link events +# LOG_IP 0x20 IP traffic history +# LOG_FCP 0x40 FCP traffic history +# LOG_NODE 0x80 Node table events +# LOG_MISC 0x400 Miscellaneous events +# LOG_SLI 0x800 SLI events +# LOG_CHK_COND 0x1000 FCP Check condition flag +# LOG_LIBDFC 0x2000 LIBDFC events +# LOG_ALL_MSG 0xffff LOG all messages +*/ +LPFC_ATTR_RW(log_verbose, 0x0, 0x0, 0xffff, "Verbose logging bit-mask"); + +/* +# lun_queue_depth: This parameter is used to limit the number of outstanding +# commands per FCP LUN. Value range is [1,128]. Default value is 30. +*/ +LPFC_ATTR_R(lun_queue_depth, 30, 1, 128, + "Max number of FCP commands we can queue to a specific LUN"); + +/* +# Some disk devices have a "select ID" or "select Target" capability. +# From a protocol standpoint "select ID" usually means select the +# Fibre channel "ALPA". In the FC-AL Profile there is an "informative +# annex" which contains a table that maps a "select ID" (a number +# between 0 and 7F) to an ALPA. By default, for compatibility with +# older drivers, the lpfc driver scans this table from low ALPA to high +# ALPA. +# +# Turning on the scan-down variable (on = 1, off = 0) will +# cause the lpfc driver to use an inverted table, effectively +# scanning ALPAs from high to low. Value range is [0,1]. Default value is 1. +# +# (Note: This "select ID" functionality is a LOOP ONLY characteristic +# and will not work across a fabric. Also this parameter will take +# effect only in the case when ALPA map is not available.) +*/ +LPFC_ATTR_RW(scan_down, 1, 0, 1, + "Start scanning for devices from highest ALPA to lowest"); + +/* +# lpfc_nodev_tmo: If set, it will hold all I/O errors on devices that disappear +# until the timer expires. Value range is [0,255]. Default value is 20. +# NOTE: this MUST be less then the SCSI Layer command timeout - 1. +*/ +LPFC_ATTR_RW(nodev_tmo, 30, 0, 255, + "Seconds driver will hold I/O waiting for a device to come back"); + +/* +# lpfc_topology: link topology for init link +# 0x0 = attempt loop mode then point-to-point +# 0x02 = attempt point-to-point mode only +# 0x04 = attempt loop mode only +# 0x06 = attempt point-to-point mode then loop +# Set point-to-point mode if you want to run as an N_Port. +# Set loop mode if you want to run as an NL_Port. Value range is [0,0x6]. +# Default value is 0. +*/ +LPFC_ATTR_R(topology, 0, 0, 6, "Select Fibre Channel topology"); + +/* +# lpfc_link_speed: Link speed selection for initializing the Fibre Channel +# connection. +# 0 = auto select (default) +# 1 = 1 Gigabaud +# 2 = 2 Gigabaud +# 4 = 4 Gigabaud +# Value range is [0,4]. Default value is 0. +*/ +LPFC_ATTR_R(link_speed, 0, 0, 4, "Select link speed"); + +/* +# lpfc_fcp_class: Determines FC class to use for the FCP protocol. +# Value range is [2,3]. Default value is 3. +*/ +LPFC_ATTR_R(fcp_class, 3, 2, 3, + "Select Fibre Channel class of service for FCP sequences"); + +/* +# lpfc_use_adisc: Use ADISC for FCP rediscovery instead of PLOGI. Value range +# is [0,1]. Default value is 0. +*/ +LPFC_ATTR_RW(use_adisc, 0, 0, 1, + "Use ADISC on rediscovery to authenticate FCP devices"); + +/* +# lpfc_ack0: Use ACK0, instead of ACK1 for class 2 acknowledgement. Value +# range is [0,1]. Default value is 0. +*/ +LPFC_ATTR_R(ack0, 0, 0, 1, "Enable ACK0 support"); + +/* +# If automap is set, SCSI IDs for all FCP nodes without +# consistent bindings will be automatically generated. +# If new FCP devices are added to the network when the system is down, +# there is no guarantee that these SCSI IDs will remain the same +# when the system is booted again. +# The bind method of the port is used as the binding method of +# automap devices to preserve SCSI IDs between link down and link up. +# If automap is 0, only devices with consistent bindings will be +# recognized by the system. User can change the automap property +# of port instance X by changing the value of lpfcX_automap parameter. +# Value range is [0,1]. Default value is 1. +*/ +LPFC_ATTR_RW(automap, 1, 0, 1, + "Automatically bind FCP devices as they are discovered"); + +/* +# lpfc_fcp_bind_method: It specifies the method of binding to be used for each +# port. This binding method is used for consistent binding and automaped +# binding. A value of 1 will force WWNN binding, value of 2 will force WWPN +# binding, value of 3 will force DID binding and value of 4 will force the +# driver to derive binding from ALPA. Any consistent binding whose type does +# not match with the bind method of the port will be ignored. Value range +# is [1,4]. Default value is 2. +*/ +LPFC_ATTR_RW(fcp_bind_method, 2, 0, 4, + "Select the bind method to be used"); + +/* +# lpfc_cr_delay & lpfc_cr_count: Default values for I/O colaesing +# cr_delay (msec) or cr_count outstanding commands. cr_delay can take +# value [0,63]. cr_count can take value [0,255]. Default value of cr_delay +# is 0. Default value of cr_count is 0. The cr_count feature is disabled if +# cr_delay is set to 0. +*/ +static int lpfc_cr_delay = 0; +module_param(lpfc_cr_delay, int , 0); +MODULE_PARM_DESC(lpfc_cr_delay, "A count of milliseconds after which an" + "interrupt response is generated"); + +static int lpfc_cr_count = 1; +module_param(lpfc_cr_count, int, 0); +MODULE_PARM_DESC(lpfc_cr_count, "A count of I/O completions after which an" + "interrupt response is generated"); + +/* +# lpfc_fdmi_on: controls FDMI support. +# 0 = no FDMI support +# 1 = support FDMI without attribute of hostname +# 2 = support FDMI with attribute of hostname +# Value range [0,2]. Default value is 0. +*/ +LPFC_ATTR_RW(fdmi_on, 0, 0, 2, "Enable FDMI support"); + +/* +# Specifies the maximum number of ELS cmds we can have outstanding (for +# discovery). Value range is [1,64]. Default value = 1. +*/ +static int lpfc_discovery_threads = 1; +module_param(lpfc_discovery_threads, int, 0); +MODULE_PARM_DESC(lpfc_discovery_threads, "Maximum number of ELS commands" + "during discovery"); + +#ifdef USE_SCAN_TARGET +/* +# This enables lpfc_target_add and lpfc_target_remove. +# Default value = 1, SCSI hotplug enabled. +*/ +static int lpfc_scsi_hotplug = 1; +#else +/* +# This enables lpfc_target_add and lpfc_target_remove. +# Default value = 0, SCSI hotplug disabled. +*/ +static int lpfc_scsi_hotplug = 0; +#endif +module_param(lpfc_scsi_hotplug, int, 0); +MODULE_PARM_DESC(lpfc_scsi_hotplug, "Enables support of SCSI hotplug"); + +/* +# lpfc_max_luns: maximum number of LUNs per target driver will support +# Value range is [1,32768]. Default value is 256. +# NOTE: The SCSI layer will scan each target for this many luns +*/ +LPFC_ATTR_RW(max_luns, 256, 1, 32768, + "Maximum number of LUNs per target driver will support"); + +#ifdef DFC_DEBUG + +static int +dfc_rsp_data_copy(struct lpfc_hba * phba, uint8_t * outdataptr, + DMABUFEXT_t * mlist, uint32_t size) +{ + DMABUFEXT_t *mlast = NULL; + int cnt, offset = 0; + struct list_head head, *curr, *next; + + if (!mlist) /* FIX ME - fix the return values */ + return 0; + + list_add_tail(&head, &mlist->dma.list); + + list_for_each_safe(curr, next, &head) { + mlast = list_entry(curr, DMABUFEXT_t , dma.list); + if (!size) + break; + + /* We copy chunks of 4K */ + cnt = size > 4096 ? 4096: size; + + if (outdataptr) { + pci_dma_sync_single_for_device(phba->pcidev, + mlast->dma.phys, LPFC_BPL_SIZE, PCI_DMA_TODEVICE); + + memcpy(outdataptr + offset, + mlast->dma.virt, cnt); + } + offset += cnt; + size -= cnt; + } + list_del(&head); + return 0; +} + +static int +dfc_cmd_data_free(struct lpfc_hba * phba, DMABUFEXT_t * mlist) +{ + DMABUFEXT_t *mlast; + struct pci_dev *pcidev = phba->pcidev; + struct list_head head, *curr, *next; + + if (!mlist) /* FIX ME - need different return value */ + return 0; + + list_add_tail(&head, &mlist->dma.list); + + list_for_each_safe(curr, next, &head) { + mlast = list_entry(curr, DMABUFEXT_t , dma.list); + if (mlast->dma.virt) { + dma_free_coherent(&pcidev->dev, + mlast->size, + mlast->dma.virt, + mlast->dma.phys); + + } + kfree(mlast); + } + return 0; +} + +static DMABUFEXT_t * +dfc_cmd_data_alloc(struct lpfc_hba * phba, char *indataptr, + struct ulp_bde64 * bpl, uint32_t size) +{ + DMABUFEXT_t *mlist = NULL, *dmp; + int cnt, offset = 0, i = 0, rc = 0; + struct pci_dev *pcidev = phba->pcidev; + + while (size) { + cnt = size > 4096 ? 4096: size; + + dmp = kmalloc(sizeof (DMABUFEXT_t), GFP_KERNEL); + if (!dmp) + goto dfc_cmd_data_alloc_exit; + + INIT_LIST_HEAD(&dmp->dma.list); + + if (mlist) + list_add_tail(&dmp->dma.list, &mlist->dma.list); + else + mlist = dmp; + + dmp->dma.virt = dma_alloc_coherent(&pcidev->dev, + cnt, + &(dmp->dma.phys), + GFP_KERNEL); + + if (!dmp->dma.virt) /* FIX ME - who free's the list ?*/ + goto dfc_cmd_data_alloc_free_dmp; + + dmp->size = cnt; + + if (!indataptr) { + bpl->tus.f.bdeFlags = BUFF_USE_RCV; + } else { + + /*rc = copy_from_user(dmp->dma.virt, indataptr + offset, + cnt);*/ + memcpy(dmp->dma.virt, indataptr+offset, cnt); + if (rc) /* FIX ME - who free's the list ?*/ + goto dfc_cmd_data_alloc_free_dmp; + bpl->tus.f.bdeFlags = 0; + + pci_dma_sync_single_for_device(phba->pcidev, + dmp->dma.phys, LPFC_BPL_SIZE, PCI_DMA_TODEVICE); + } + + /* build buffer ptr list for IOCB */ + bpl->addrLow = le32_to_cpu( putPaddrLow(dmp->dma.phys) ); + bpl->addrHigh = le32_to_cpu( putPaddrHigh(dmp->dma.phys) ); + bpl->tus.f.bdeSize = (ushort) cnt; + bpl->tus.w = le32_to_cpu(bpl->tus.w); + bpl++; + + i++; + offset += cnt; + size -= cnt; + } + + mlist->flag = i; + return (mlist); +dfc_cmd_data_alloc_free_dmp: + kfree(dmp); +dfc_cmd_data_alloc_exit: + dfc_cmd_data_free(phba, mlist); + return NULL; +} + +static ssize_t +sysfs_ctpass_write(struct kobject *kobj, char *buf, loff_t off, size_t count) +{ + struct Scsi_Host *host = class_to_shost(container_of(kobj, + struct class_device, kobj)); + struct lpfc_hba *phba = (struct lpfc_hba*)host->hostdata[0]; + struct lpfc_nodelist *pndl; + struct ulp_bde64 *bpl; + struct lpfc_iocbq *cmdiocbq = NULL, *rspiocbq = NULL; + DMABUFEXT_t *indmp = NULL, *outdmp = NULL; + IOCB_t *cmd = NULL, *rsp = NULL; + struct lpfc_dmabuf *bmp = NULL; + struct lpfc_sli *psli = &phba->sli; + struct lpfc_sli_ring *pring = &psli->ring[LPFC_ELS_RING]; + int i, rc = -4; + int reqbfrcnt, snsbfrcnt; + uint32_t timeout; + unsigned long iflag; + uint32_t portid; /* Port to send this to. */ + typedef struct tagctpassthruinput { + uint32_t portid; + uint32_t reqsize; + uint32_t rspsize; + } ctpassthruinput_t; + + spin_lock_irqsave(phba->host->host_lock, iflag); + + reqbfrcnt = ((ctpassthruinput_t*)buf)->reqsize; + snsbfrcnt = ((ctpassthruinput_t*)buf)->rspsize; + portid = ((ctpassthruinput_t*)buf)->portid; + + if((portid & CT_DID_MASK) != CT_DID_MASK) + goto ctpassthru_exit; + + if (!reqbfrcnt || !snsbfrcnt || + (reqbfrcnt > PAGE_SIZE - sizeof(ctpassthruinput_t)) || + (snsbfrcnt > PAGE_SIZE)) { + rc = -ERANGE; + goto ctpassthru_exit; + } + + pndl = lpfc_findnode_did(phba, NLP_SEARCH_MAPPED | NLP_SEARCH_UNMAPPED, + portid); + if(!pndl || pndl->nlp_flag & NLP_ELS_SND_MASK) { + rc = -ENODEV; + goto ctpassthru_exit; + } + + if (!(psli->sliinit.sli_flag & LPFC_SLI2_ACTIVE)) { + rc = -EACCES; + goto ctpassthru_exit; + } + + cmdiocbq = mempool_alloc(phba->iocb_mem_pool, GFP_ATOMIC); + if (!cmdiocbq) { + rc = -ENOMEM; + goto ctpassthru_exit; + } + memset(cmdiocbq, 0, sizeof (struct lpfc_iocbq)); + cmd = &cmdiocbq->iocb; + + rspiocbq = mempool_alloc(phba->iocb_mem_pool, GFP_ATOMIC); + if (!rspiocbq) { + rc = -ENOMEM; + goto ctpassthru_freecmdiocbq; + } + memset(rspiocbq, 0, sizeof (struct lpfc_iocbq)); + rsp = &rspiocbq->iocb; + + bmp = kmalloc(sizeof (struct lpfc_dmabuf), GFP_ATOMIC); + if (!bmp) { + rc = -ENOMEM; + goto ctpassthru_freerspiocbq; + } + + bmp->virt = lpfc_mbuf_alloc(phba, 0, &bmp->phys); + if (!bmp->virt) { + rc = -ENOMEM; + goto ctpassthru_freebmp; + } + + INIT_LIST_HEAD(&bmp->list); + bpl = (struct ulp_bde64 *) bmp->virt; + spin_unlock_irqrestore(phba->host->host_lock, iflag); + indmp = dfc_cmd_data_alloc(phba, buf + sizeof(ctpassthruinput_t), bpl, + reqbfrcnt); + spin_lock_irqsave(phba->host->host_lock, iflag); + if (!indmp) { + rc = -ENOMEM; + goto ctpassthru_freembuf; + } + + bpl += indmp->flag; /* flag contains total number of BPLs for xmit */ + + spin_unlock_irqrestore(phba->host->host_lock, iflag); + outdmp = dfc_cmd_data_alloc(phba, NULL, bpl, snsbfrcnt); + spin_lock_irqsave(phba->host->host_lock, iflag); + if (!outdmp) { + rc = -ENOMEM; + goto ctpassthru_free_indmp; + } + outdmp->data = snsbfrcnt; + outdmp->uniqueid = current_thread_info()->task->pid; + + cmd->un.genreq64.bdl.ulpIoTag32 = 0; + cmd->un.genreq64.bdl.addrHigh = putPaddrHigh(bmp->phys); + cmd->un.genreq64.bdl.addrLow = putPaddrLow(bmp->phys); + cmd->un.genreq64.bdl.bdeFlags = BUFF_TYPE_BDL; + cmd->un.genreq64.bdl.bdeSize = + (outdmp->flag + indmp->flag) * sizeof (struct ulp_bde64); + cmd->ulpCommand = CMD_GEN_REQUEST64_CR; + cmd->un.genreq64.w5.hcsw.Fctl = (SI | LA); + cmd->un.genreq64.w5.hcsw.Dfctl = 0; + cmd->un.genreq64.w5.hcsw.Rctl = FC_UNSOL_CTL; + cmd->un.genreq64.w5.hcsw.Type = FC_COMMON_TRANSPORT_ULP; + cmd->ulpIoTag = lpfc_sli_next_iotag(phba, pring); + cmd->ulpTimeout = 5; + cmd->ulpBdeCount = 1; + cmd->ulpLe = 1; + cmd->ulpClass = CLASS3; + cmd->ulpContext = pndl->nlp_rpi; + cmd->ulpOwner = OWN_CHIP; + cmdiocbq->context1 = NULL; + cmdiocbq->context2 = NULL; + cmdiocbq->iocb_flag |= LPFC_IO_LIBDFC; + + if (cmd->ulpTimeout < (phba->fc_ratov * 2 + LPFC_DRVR_TIMEOUT)) + timeout = phba->fc_ratov * 2 + LPFC_DRVR_TIMEOUT; + else + timeout = cmd->ulpTimeout; + + for (rc = -1, i = 0; i < 4 && rc != IOCB_SUCCESS; i++) { + spin_unlock_irqrestore(phba->host->host_lock, iflag); + rc = lpfc_sli_issue_iocb_wait(phba, pring, cmdiocbq, + rspiocbq, timeout); + spin_lock_irqsave(phba->host->host_lock, iflag); + } + + if (rc != IOCB_SUCCESS) { + rc = -EACCES; + goto ctpassthru_free_outdmp; + } + + if (!rsp->ulpStatus) { + outdmp->flag = rsp->un.genreq64.bdl.bdeSize; + } else { + if (rsp->ulpStatus == IOSTAT_LOCAL_REJECT) { + switch (rsp->un.ulpWord[4] & 0xff) { + case IOERR_SEQUENCE_TIMEOUT: + rc = -ETIMEDOUT; + break; + case IOERR_INVALID_RPI: + rc = -EFAULT; + break; + default: + rc = -EACCES; + break; + } + goto ctpassthru_free_outdmp; + } + } + if (outdmp->flag > snsbfrcnt) { + rc = -ERANGE; /* C_CT Request error */ + lpfc_printf_log(phba, + KERN_INFO, + LOG_LIBDFC, + "%d:1208 C_CT Request error Data: x%x x%x\n", + phba->brd_no, + outdmp->flag, 4096); + goto ctpassthru_free_outdmp; + } + list_add(&outdmp->list, &phba->ctrspbuflist); + rc = reqbfrcnt; + goto ctpassthru_free_indmp; + +ctpassthru_free_outdmp: + dfc_cmd_data_free(phba, outdmp); +ctpassthru_free_indmp: + dfc_cmd_data_free(phba, indmp); +ctpassthru_freembuf: + lpfc_mbuf_free(phba, bmp->virt, bmp->phys); +ctpassthru_freebmp: + kfree(bmp); +ctpassthru_freerspiocbq: + mempool_free(rspiocbq, phba->iocb_mem_pool); +ctpassthru_freecmdiocbq: + mempool_free(cmdiocbq, phba->iocb_mem_pool); +ctpassthru_exit: + spin_unlock_irqrestore(phba->host->host_lock, iflag); /* remove */ + return rc; +} + +static ssize_t +sysfs_ctpass_read(struct kobject *kobj, char *buf, loff_t off, size_t count) +{ + int rc = -EIO, uniqueid; + struct Scsi_Host *host = class_to_shost(container_of(kobj, + struct class_device, kobj)); + struct lpfc_hba *phba = (struct lpfc_hba*)host->hostdata[0]; + DMABUFEXT_t *outdmp = NULL, *tmpoutdmp; + + uniqueid = current_thread_info()->task->pid; + + list_for_each_entry_safe(outdmp, tmpoutdmp, &phba->ctrspbuflist, list){ + if (outdmp->uniqueid == uniqueid) { + dfc_rsp_data_copy(phba, (uint8_t*)buf, outdmp, + outdmp->data); + + rc = outdmp->flag; + list_del(&outdmp->list); + dfc_cmd_data_free(phba, outdmp); + break; + + } + } + return rc; +} + +static struct bin_attribute sysfs_ctpass_attr = { + .attr = { + .name = "ctpass", + .mode = S_IRUSR | S_IWUSR, + .owner = THIS_MODULE, + }, + .size = 0, + .read = sysfs_ctpass_read, + .write = sysfs_ctpass_write, +}; + +static ssize_t +sysfs_sendrnid_write(struct kobject *kobj, char *buf, loff_t off, size_t count) +{ + struct Scsi_Host *host = class_to_shost(container_of(kobj, + struct class_device, kobj)); + struct lpfc_hba *phba = (struct lpfc_hba*)host->hostdata[0]; + struct lpfc_sli *psli = &phba->sli; + struct lpfc_sli_ring *pring = &psli->ring[LPFC_ELS_RING]; + struct lpfc_name idn; + struct lpfc_iocbq *cmdiocbq = NULL, *rspiocbq = NULL; + RNID *prsp; + uint32_t *pcmd, *psta; + IOCB_t *rsp; + void *context2; + unsigned long iflag; + int rtnbfrsiz, i, rc = 0; + struct lpfc_nodelist *pndl; + uint32_t wwntype; + struct rnidrsp *rspbuf; + + rspbuf = kmalloc(sizeof (rspbuf), GFP_KERNEL); + if (!rspbuf) + return -ENOMEM; + + memcpy(&idn, buf, sizeof(struct lpfc_name)); + sscanf(buf + sizeof(struct lpfc_name), "%d", &wwntype); + + spin_lock_irqsave(phba->host->host_lock, iflag); /* remove */ + + if (wwntype) + pndl = lpfc_findnode_wwpn(phba, NLP_SEARCH_MAPPED | + NLP_SEARCH_UNMAPPED, &idn); + else + pndl = lpfc_findnode_wwnn(phba, NLP_SEARCH_MAPPED | + NLP_SEARCH_UNMAPPED, &idn); + + if (!pndl) { + rc = -ENODEV; + goto sendrnid_exit; + } + + if ((pndl->nlp_flag & NLP_ELS_SND_MASK) == NLP_RNID_SND) { + rc = -EACCES; + goto sendrnid_exit; + } + + cmdiocbq = lpfc_prep_els_iocb(phba, 1, 2 * sizeof (uint32_t), 0, pndl, + ELS_CMD_RNID); + if (!cmdiocbq) { + rc = -ENOMEM; + goto sendrnid_exit; + } + + /*********************************************************************/ + /* context2 is used by prep/free to locate cmd and rsp buffers, */ + /* but context2 is also used by iocb_wait to hold a rspiocb ptr, so */ + /* the rsp iocbq can be returned from the completion routine for */ + /* iocb_wait, so, save the prep/free value locally ... it will be */ + /* restored after returning from iocb_wait. */ + /*********************************************************************/ + context2 = cmdiocbq->context2; /* needed to use lpfc_els_free_iocb */ + + rspiocbq = mempool_alloc(phba->iocb_mem_pool, GFP_ATOMIC); + if (!rspiocbq) { + rc = -ENOMEM; + goto sendrnid_freecmdiocbq; + } + memset(rspiocbq, 0, sizeof (struct lpfc_iocbq)); + rsp = &rspiocbq->iocb; + + pcmd = ((struct lpfc_dmabuf *) cmdiocbq->context2)->virt; + *pcmd++ = ELS_CMD_RNID; + memset(pcmd, 0, sizeof (RNID)); /* fill in RNID payload */ + ((RNID *)pcmd)->Format = RNID_TOPOLOGY_DISC; + cmdiocbq->context1 = NULL; + cmdiocbq->context2 = NULL; + cmdiocbq->iocb_flag |= LPFC_IO_LIBDFC; + + for (rc = -1, i = 0; i < 4 && rc != IOCB_SUCCESS; i++) { + pndl->nlp_flag |= NLP_RNID_SND; + spin_unlock_irqrestore(phba->host->host_lock, iflag); + rc = lpfc_sli_issue_iocb_wait(phba, pring, cmdiocbq, + rspiocbq, + (phba->fc_ratov * 2) + + LPFC_DRVR_TIMEOUT); + spin_lock_irqsave(phba->host->host_lock, iflag); + pndl->nlp_flag &= ~NLP_RNID_SND; + cmdiocbq->context2 = context2; + if (rc == IOCB_ERROR) { + rc = -EACCES; + goto sendrnid_freerspiocbq; + } + } + + if (rc != IOCB_SUCCESS) { + rc = -EACCES; + goto sendrnid_freerspiocbq; + } + + if (rsp->ulpStatus) { + rc = -EACCES; + } else { + struct lpfc_dmabuf *buf_ptr1, *buf_ptr; + buf_ptr1 = (struct lpfc_dmabuf *)cmdiocbq->context2; + buf_ptr = list_entry(buf_ptr1->list.next, struct lpfc_dmabuf, + list); + psta = (uint32_t*)buf_ptr->virt; + if (*psta++ != ELS_CMD_ACC) { + rc = -EFAULT; + goto sendrnid_freerspiocbq; + } + prsp = (RNID*)psta; /* then rnid response data */ + rtnbfrsiz = prsp->CommonLen + prsp->SpecificLen; + if (rtnbfrsiz > PAGE_SIZE) { + rc = -EFAULT; + goto sendrnid_freerspiocbq; + } + rspbuf->buf = kmalloc(rtnbfrsiz, GFP_ATOMIC); + if (!rspbuf->buf) { + rc = -ENOMEM; + goto sendrnid_freerspiocbq; + } + + memcpy(rspbuf->buf, prsp, rtnbfrsiz); + rspbuf->data = rtnbfrsiz; + rspbuf->uniqueid = current_thread_info()->task->pid; + list_add(&rspbuf->list, &phba->rnidrspbuflist); + rc = rtnbfrsiz; + goto sendrnid_exit; + } +sendrnid_freerspiocbq: + mempool_free(rspiocbq, phba->iocb_mem_pool); +sendrnid_freecmdiocbq: + lpfc_els_free_iocb(phba, cmdiocbq); +sendrnid_exit: + spin_unlock_irqrestore(phba->host->host_lock, iflag); + return rc; +} + +static ssize_t +sysfs_sendrnid_read(struct kobject *kobj, char *buf, loff_t off, size_t count) +{ + int rc = -EIO, uniqueid; + struct Scsi_Host *host = class_to_shost(container_of(kobj, + struct class_device, kobj)); + struct lpfc_hba *phba = (struct lpfc_hba*)host->hostdata[0]; + struct rnidrsp *outdmp = NULL, *tmpoutdmp; + + uniqueid = current_thread_info()->task->pid; + + list_for_each_entry_safe(outdmp, tmpoutdmp, &phba->rnidrspbuflist, + list){ + if (outdmp->uniqueid == uniqueid) { + memcpy(buf, outdmp->buf, outdmp->data); + rc = outdmp->data; + kfree(outdmp->buf); + list_del(&outdmp->list); + kfree(outdmp); + break; + + } + } + + return rc; + +} + +static struct bin_attribute sysfs_sendrnid_attr = { + .attr = { + .name = "sendrnid", + .mode = S_IWUSR, + .owner = THIS_MODULE, + }, + .size = 0, + .write = sysfs_sendrnid_write, + .read = sysfs_sendrnid_read, +}; + +static ssize_t +sysfs_slimem_write(struct kobject *kobj, char *buf, loff_t off, size_t count) +{ + unsigned long iflag; + struct Scsi_Host *host = class_to_shost(container_of(kobj, + struct class_device, kobj)); + struct lpfc_hba *phba = (struct lpfc_hba*)host->hostdata[0]; + size_t slim_size; + + if (phba->sli.sliinit.sli_flag & LPFC_SLI2_ACTIVE) + slim_size = SLI2_SLIM_SIZE; + else + slim_size = SLI1_SLIM_SIZE; + + if ((count + off) > slim_size) + return -ERANGE; + + if (count == 0) return 0; + + if (off % 4 || count % 4 || (unsigned long)buf % 4) + return -EINVAL; + + spin_lock_irqsave(host->host_lock, iflag); + + if (!(phba->fc_flag & FC_OFFLINE_MODE)) { + if (off >= 256 && off + count <= (256 + 128)) { + spin_unlock_irqrestore(phba->host->host_lock, iflag); + return -EPERM; + } + } + + if (phba->sli.sliinit.sli_flag & LPFC_SLI2_ACTIVE) + lpfc_sli_pcimem_bcopy((uint32_t*)buf, + (uint32_t *)((uint8_t *) phba->slim2p+off), count); + else + lpfc_memcpy_to_slim((uint8_t *) phba->slim_memmap_p + off, + (void *)buf, count); + + spin_unlock_irqrestore(host->host_lock, iflag); + + return count; +} + +static ssize_t +sysfs_slimem_read(struct kobject *kobj, char *buf, loff_t off, size_t count) +{ + unsigned long iflag; + struct Scsi_Host *host = class_to_shost(container_of(kobj, + struct class_device, kobj)); + struct lpfc_hba *phba = (struct lpfc_hba*)host->hostdata[0]; + size_t slim_size; + + if (phba->sli.sliinit.sli_flag & LPFC_SLI2_ACTIVE) + slim_size = SLI2_SLIM_SIZE; + else + slim_size = SLI1_SLIM_SIZE; + + if (off > slim_size) + return -ERANGE; + + if ((count + off) > slim_size) + count = slim_size - off; + + if (count == 0) return 0; + + if (off % 4 || count % 4 || (unsigned long)buf % 4) + return -EINVAL; + + spin_lock_irqsave(phba->host->host_lock, iflag); + + if (phba->sli.sliinit.sli_flag & LPFC_SLI2_ACTIVE) + lpfc_sli_pcimem_bcopy((uint32_t *)((uint8_t *) phba->slim2p + + off), (uint32_t *)buf, count); + else + lpfc_memcpy_from_slim(buf, (uint8_t *)phba->slim_memmap_p + off, + count); + + spin_unlock_irqrestore(phba->host->host_lock, iflag); + + return count; +} + +static struct bin_attribute sysfs_slimem_attr = { + .attr = { + .name = "slimem", + .mode = S_IRUSR | S_IWUSR, + .owner = THIS_MODULE, + }, + .size = SLI1_SLIM_SIZE, + .read = sysfs_slimem_read, + .write = sysfs_slimem_write, +}; +#endif /* DFC_DEBUG */ + +static ssize_t +sysfs_ctlreg_write(struct kobject *kobj, char *buf, loff_t off, size_t count) +{ + unsigned long iflag; + size_t buf_off; + struct Scsi_Host *host = class_to_shost(container_of(kobj, + struct class_device, kobj)); + struct lpfc_hba *phba = (struct lpfc_hba*)host->hostdata[0]; + + if ((off + count) > FF_REG_AREA_SIZE) + return -ERANGE; + + if (count == 0) return 0; + + if (off % 4 || count % 4 || (unsigned long)buf % 4) + return -EINVAL; + + spin_lock_irqsave(phba->host->host_lock, iflag); + + if (!(phba->fc_flag & FC_OFFLINE_MODE)) { + spin_unlock_irqrestore(phba->host->host_lock, iflag); + return -EPERM; + } + + for (buf_off = 0; buf_off < count; buf_off += sizeof(uint32_t)) + writel(*((uint32_t *)(buf + buf_off)), + (uint8_t *)phba->ctrl_regs_memmap_p + off + buf_off); + + spin_unlock_irqrestore(phba->host->host_lock, iflag); + + return count; +} + +static ssize_t +sysfs_ctlreg_read(struct kobject *kobj, char *buf, loff_t off, size_t count) +{ + unsigned long iflag; + size_t buf_off; + uint32_t * tmp_ptr; + struct Scsi_Host *host = class_to_shost(container_of(kobj, + struct class_device, kobj)); + struct lpfc_hba *phba = (struct lpfc_hba*)host->hostdata[0]; + + if (off > FF_REG_AREA_SIZE) + return -ERANGE; + + if ((off + count) > FF_REG_AREA_SIZE) + count = FF_REG_AREA_SIZE - off; + + if (count == 0) return 0; + + if (off % 4 || count % 4 || (unsigned long)buf % 4) + return -EINVAL; + + spin_lock_irqsave(phba->host->host_lock, iflag); + + for (buf_off = 0; buf_off < count; buf_off += sizeof(uint32_t)) { + tmp_ptr = (uint32_t *)(buf + buf_off); + *tmp_ptr = readl((uint8_t *)(phba->ctrl_regs_memmap_p + + off + buf_off)); + } + + spin_unlock_irqrestore(phba->host->host_lock, iflag); + + return count; +} + +static struct bin_attribute sysfs_ctlreg_attr = { + .attr = { + .name = "ctlreg", + .mode = S_IRUSR | S_IWUSR, + .owner = THIS_MODULE, + }, + .size = 256, + .read = sysfs_ctlreg_read, + .write = sysfs_ctlreg_write, +}; + + +#define MBOX_BUFF_SIZE (MAILBOX_CMD_WSIZE*sizeof(uint32_t)) + +static void +sysfs_mbox_idle (struct lpfc_hba * phba) +{ + phba->sysfs_mbox.state = SMBOX_IDLE; + phba->sysfs_mbox.offset = 0; + + if (phba->sysfs_mbox.mbox) { + mempool_free(phba->sysfs_mbox.mbox, + phba->mbox_mem_pool); + phba->sysfs_mbox.mbox = NULL; + } +} + +static ssize_t +sysfs_mbox_write(struct kobject *kobj, char *buf, loff_t off, size_t count) +{ + unsigned long iflag; + struct Scsi_Host * host = + class_to_shost(container_of(kobj, struct class_device, kobj)); + struct lpfc_hba * phba = (struct lpfc_hba*)host->hostdata[0]; + struct lpfcMboxq * mbox = NULL; + + if ((count + off) > MBOX_BUFF_SIZE) + return -ERANGE; + + if (off % 4 || count % 4 || (unsigned long)buf % 4) + return -EINVAL; + + if (count == 0) + return 0; + + if (off == 0) { + mbox = mempool_alloc(phba->mbox_mem_pool, GFP_KERNEL); + if (!mbox) + return -ENOMEM; + + } + + spin_lock_irqsave(host->host_lock, iflag); + + if (off == 0) { + if (phba->sysfs_mbox.mbox) + mempool_free(mbox, phba->mbox_mem_pool); + else + phba->sysfs_mbox.mbox = mbox; + phba->sysfs_mbox.state = SMBOX_WRITING; + } + else { + if (phba->sysfs_mbox.state != SMBOX_WRITING || + phba->sysfs_mbox.offset != off || + phba->sysfs_mbox.mbox == NULL ) { + sysfs_mbox_idle(phba); + spin_unlock_irqrestore(host->host_lock, iflag); + return -EINVAL; + } + } + + memcpy((uint8_t *) & phba->sysfs_mbox.mbox->mb + off, + buf, count); + + phba->sysfs_mbox.offset = off + count; + + spin_unlock_irqrestore(host->host_lock, iflag); + + return count; +} + +static ssize_t +sysfs_mbox_read(struct kobject *kobj, char *buf, loff_t off, size_t count) +{ + unsigned long iflag; + struct Scsi_Host *host = + class_to_shost(container_of(kobj, struct class_device, + kobj)); + struct lpfc_hba *phba = (struct lpfc_hba*)host->hostdata[0]; + int rc; + + if (off > sizeof(MAILBOX_t)) + return -ERANGE; + + if ((count + off) > sizeof(MAILBOX_t)) + count = sizeof(MAILBOX_t) - off; + + if (off % 4 || count % 4 || (unsigned long)buf % 4) + return -EINVAL; + + if (off && count == 0) + return 0; + + spin_lock_irqsave(phba->host->host_lock, iflag); + + if (off == 0 && + phba->sysfs_mbox.state == SMBOX_WRITING && + phba->sysfs_mbox.offset >= 2 * sizeof(uint32_t)) { + + switch (phba->sysfs_mbox.mbox->mb.mbxCommand) { + /* Offline only */ + case MBX_WRITE_NV: + case MBX_INIT_LINK: + case MBX_DOWN_LINK: + case MBX_CONFIG_LINK: + case MBX_CONFIG_RING: + case MBX_RESET_RING: + case MBX_UNREG_LOGIN: + case MBX_CLEAR_LA: + case MBX_DUMP_CONTEXT: + case MBX_RUN_DIAGS: + case MBX_RESTART: + case MBX_FLASH_WR_ULA: + case MBX_SET_MASK: + case MBX_SET_SLIM: + case MBX_SET_DEBUG: + if (!(phba->fc_flag & FC_OFFLINE_MODE)) { + printk(KERN_WARNING "mbox_read:Command 0x%x " + "is illegal in on-line state\n", + phba->sysfs_mbox.mbox->mb.mbxCommand); + sysfs_mbox_idle(phba); + spin_unlock_irqrestore(phba->host->host_lock, + iflag); + return -EPERM; + } + case MBX_LOAD_SM: + case MBX_READ_NV: + case MBX_READ_CONFIG: + case MBX_READ_RCONFIG: + case MBX_READ_STATUS: + case MBX_READ_XRI: + case MBX_READ_REV: + case MBX_READ_LNK_STAT: + case MBX_DUMP_MEMORY: + case MBX_DOWN_LOAD: + case MBX_UPDATE_CFG: + case MBX_LOAD_AREA: + case MBX_LOAD_EXP_ROM: + break; + case MBX_READ_SPARM64: + case MBX_READ_LA: + case MBX_READ_LA64: + case MBX_REG_LOGIN: + case MBX_REG_LOGIN64: + case MBX_CONFIG_PORT: + case MBX_RUN_BIU_DIAG: + printk(KERN_WARNING "mbox_read: Illegal Command 0x%x\n", + phba->sysfs_mbox.mbox->mb.mbxCommand); + sysfs_mbox_idle(phba); + spin_unlock_irqrestore(phba->host->host_lock, + iflag); + return -EPERM; + default: + printk(KERN_WARNING "mbox_read: Unknown Command 0x%x\n", + phba->sysfs_mbox.mbox->mb.mbxCommand); + sysfs_mbox_idle(phba); + spin_unlock_irqrestore(phba->host->host_lock, + iflag); + return -EPERM; + } + + if ((phba->fc_flag & FC_OFFLINE_MODE) || + (!(phba->sli.sliinit.sli_flag & LPFC_SLI2_ACTIVE))){ + + spin_unlock_irqrestore(phba->host->host_lock, iflag); + rc = lpfc_sli_issue_mbox (phba, + phba->sysfs_mbox.mbox, + MBX_POLL); + spin_lock_irqsave(phba->host->host_lock, iflag); + + } else { + spin_unlock_irqrestore(phba->host->host_lock, iflag); + rc = lpfc_sli_issue_mbox_wait (phba, + phba->sysfs_mbox.mbox, + phba->fc_ratov * 2); + spin_lock_irqsave(phba->host->host_lock, iflag); + } + + if (rc != MBX_SUCCESS) { + sysfs_mbox_idle(phba); + spin_unlock_irqrestore(host->host_lock, iflag); + return -ENODEV; + } + phba->sysfs_mbox.state = SMBOX_READING; + } + else if (phba->sysfs_mbox.offset != off || + phba->sysfs_mbox.state != SMBOX_READING) { + printk(KERN_WARNING "mbox_read: Bad State\n"); + sysfs_mbox_idle(phba); + spin_unlock_irqrestore(host->host_lock, iflag); + return -EINVAL; + } + + memcpy(buf, (uint8_t *) & phba->sysfs_mbox.mbox->mb + off, count); + + phba->sysfs_mbox.offset = off + count; + + if (phba->sysfs_mbox.offset == sizeof(MAILBOX_t)) + sysfs_mbox_idle(phba); + + spin_unlock_irqrestore(phba->host->host_lock, iflag); + + return count; +} + +static struct bin_attribute sysfs_mbox_attr = { + .attr = { + .name = "mbox", + .mode = S_IRUSR | S_IWUSR, + .owner = THIS_MODULE, + }, + .size = sizeof(MAILBOX_t), + .read = sysfs_mbox_read, + .write = sysfs_mbox_write, +}; + + +#ifdef FC_TRANS_VER2 /* fc transport w/ statistics and attrs */ + +/* + * Dynamic FC Host Attributes Support + */ + +static void +lpfc_get_host_port_id(struct Scsi_Host *shost) +{ + struct lpfc_hba *phba = (struct lpfc_hba*)shost->hostdata[0]; + fc_host_port_id(shost) = be32_to_cpu(phba->fc_myDID); +} + +static void +lpfc_get_host_port_type(struct Scsi_Host *shost) +{ + struct lpfc_hba *phba = (struct lpfc_hba*)shost->hostdata[0]; + unsigned long iflag = 0; + + spin_lock_irqsave(shost->host_lock, iflag); + + if (phba->hba_state == LPFC_HBA_READY) { + if (phba->fc_topology == TOPOLOGY_LOOP) { + if (phba->fc_flag & FC_PUBLIC_LOOP) + fc_host_port_type(shost) = FC_PORTTYPE_NLPORT; + else + fc_host_port_type(shost) = FC_PORTTYPE_LPORT; + } else { + if (phba->fc_flag & FC_FABRIC) + fc_host_port_type(shost) = FC_PORTTYPE_NPORT; + else + fc_host_port_type(shost) = FC_PORTTYPE_PTP; + } + } else + fc_host_port_type(shost) = FC_PORTTYPE_UNKNOWN; + + spin_unlock_irqrestore(shost->host_lock, iflag); +} + +static void +lpfc_get_host_port_state(struct Scsi_Host *shost) +{ + struct lpfc_hba *phba = (struct lpfc_hba*)shost->hostdata[0]; + unsigned long iflag = 0; + + spin_lock_irqsave(shost->host_lock, iflag); + + if (phba->fc_flag & FC_OFFLINE_MODE) + fc_host_port_state(shost) = FC_PORTSTATE_OFFLINE; + else { + switch (phba->hba_state) { + case LPFC_INIT_START: + case LPFC_INIT_MBX_CMDS: + case LPFC_LINK_DOWN: + fc_host_port_state(shost) = FC_PORTSTATE_LINKDOWN; + break; + case LPFC_LINK_UP: + case LPFC_LOCAL_CFG_LINK: + case LPFC_FLOGI: + case LPFC_FABRIC_CFG_LINK: + case LPFC_NS_REG: + case LPFC_NS_QRY: + case LPFC_BUILD_DISC_LIST: + case LPFC_DISC_AUTH: + case LPFC_CLEAR_LA: + case LPFC_HBA_READY: + /* Links up, beyond this port_type reports state */ + fc_host_port_state(shost) = FC_PORTSTATE_ONLINE; + break; + case LPFC_HBA_ERROR: + fc_host_port_state(shost) = FC_PORTSTATE_ERROR; + break; + default: + fc_host_port_state(shost) = FC_PORTSTATE_UNKNOWN; + break; + } + } + + spin_unlock_irqrestore(shost->host_lock, iflag); +} + +static void +lpfc_get_host_speed(struct Scsi_Host *shost) +{ + struct lpfc_hba *phba = (struct lpfc_hba*)shost->hostdata[0]; + unsigned long iflag = 0; + + spin_lock_irqsave(shost->host_lock, iflag); + + if (phba->hba_state == LPFC_HBA_READY) { + switch(phba->fc_linkspeed) { + case LA_1GHZ_LINK: + fc_host_speed(shost) = FC_PORTSPEED_1GBIT; + break; + case LA_2GHZ_LINK: + fc_host_speed(shost) = FC_PORTSPEED_2GBIT; + break; + case LA_4GHZ_LINK: + fc_host_speed(shost) = FC_PORTSPEED_4GBIT; + break; + default: + fc_host_speed(shost) = FC_PORTSPEED_UNKNOWN; + break; + } + } + + spin_unlock_irqrestore(shost->host_lock, iflag); +} + +static void +lpfc_get_host_fabric_name (struct Scsi_Host *shost) +{ + struct lpfc_hba *phba = (struct lpfc_hba*)shost->hostdata[0]; + unsigned long iflag = 0; + u64 nodename; + + spin_lock_irqsave(shost->host_lock, iflag); + + if ((phba->fc_flag & FC_FABRIC) || + ((phba->fc_topology == TOPOLOGY_LOOP) && + (phba->fc_flag & FC_PUBLIC_LOOP))) + memcpy(&nodename, &phba->fc_fabparam.nodeName, sizeof(u64)); + else + /* fabric is local port if there is no F/FL_Port */ + memcpy(&nodename, &phba->fc_nodename, sizeof(u64)); + + spin_unlock_irqrestore(shost->host_lock, iflag); + + fc_host_fabric_name(shost) = be64_to_cpu(nodename); +} + + +static struct fc_host_statistics * +lpfc_get_stats(struct Scsi_Host *shost) +{ + struct lpfc_hba *phba = (struct lpfc_hba *)shost->hostdata[0]; + struct lpfc_sli *psli = &phba->sli; + struct fc_host_statistics *hs = + (struct fc_host_statistics *)phba->link_stats; + LPFC_MBOXQ_t *pmboxq; + MAILBOX_t *pmb; + int rc=0; + + pmboxq = mempool_alloc(phba->mbox_mem_pool, GFP_ATOMIC); + if (!pmboxq) + return NULL; + memset(pmboxq, 0, sizeof (LPFC_MBOXQ_t)); + + pmb = &pmboxq->mb; + pmb->mbxCommand = MBX_READ_STATUS; + pmb->mbxOwner = OWN_HOST; + pmboxq->context1 = NULL; + + if ((phba->fc_flag & FC_OFFLINE_MODE) || + (!(psli->sliinit.sli_flag & LPFC_SLI2_ACTIVE))){ + rc = lpfc_sli_issue_mbox(phba, pmboxq, MBX_POLL); + } else + rc = lpfc_sli_issue_mbox_wait(phba, pmboxq, phba->fc_ratov * 2); + + if (rc != MBX_SUCCESS) { + if (pmboxq) { + if (rc == MBX_TIMEOUT) + pmboxq->mbox_cmpl = NULL; + else + mempool_free( pmboxq, phba->mbox_mem_pool); + } + return NULL; + } + + hs->tx_frames = pmb->un.varRdStatus.xmitFrameCnt; + hs->tx_words = (pmb->un.varRdStatus.xmitByteCnt * 256); + hs->rx_frames = pmb->un.varRdStatus.rcvFrameCnt; + hs->rx_words = (pmb->un.varRdStatus.rcvByteCnt * 256); + + memset((void *)pmboxq, 0, sizeof (LPFC_MBOXQ_t)); + pmb->mbxCommand = MBX_READ_LNK_STAT; + pmb->mbxOwner = OWN_HOST; + pmboxq->context1 = NULL; + + if ((phba->fc_flag & FC_OFFLINE_MODE) || + (!(psli->sliinit.sli_flag & LPFC_SLI2_ACTIVE))) { + rc = lpfc_sli_issue_mbox(phba, pmboxq, MBX_POLL); + } else + rc = lpfc_sli_issue_mbox_wait(phba, pmboxq, phba->fc_ratov * 2); + + if (rc != MBX_SUCCESS) { + if (pmboxq) { + if (rc == MBX_TIMEOUT) + pmboxq->mbox_cmpl = NULL; + else + mempool_free( pmboxq, phba->mbox_mem_pool); + } + return NULL; + } + + hs->link_failure_count = pmb->un.varRdLnk.linkFailureCnt; + hs->loss_of_sync_count = pmb->un.varRdLnk.lossSyncCnt; + hs->loss_of_signal_count = pmb->un.varRdLnk.lossSignalCnt; + hs->prim_seq_protocol_err_count = pmb->un.varRdLnk.primSeqErrCnt; + hs->invalid_tx_word_count = pmb->un.varRdLnk.invalidXmitWord; + hs->invalid_crc_count = pmb->un.varRdLnk.crcCnt; + hs->error_frames = pmb->un.varRdLnk.crcCnt; + + if (phba->fc_topology == TOPOLOGY_LOOP) { + hs->lip_count = (phba->fc_eventTag >> 1); + hs->nos_count = -1; + } else { + hs->lip_count = -1; + hs->nos_count = (phba->fc_eventTag >> 1); + } + + hs->dumped_frames = -1; + +/* FIX ME */ + /*hs->SecondsSinceLastReset = (jiffies - lpfc_loadtime) / HZ;*/ + + return hs; +} + +#endif /* FC_TRANS_VER2 */ + +#ifdef FC_TRANS_VER1 +/* + * The LPFC driver treats linkdown handling as target loss events so there + * are no sysfs handlers for link_down_tmo. + */ +static void +lpfc_get_starget_port_id(struct scsi_target *starget) +{ + struct lpfc_nodelist *ndlp = NULL; + struct Scsi_Host *shost = dev_to_shost(starget->dev.parent); + struct lpfc_hba *phba = (struct lpfc_hba *) shost->hostdata[0]; + uint16_t did = 0; + + spin_lock_irq(shost->host_lock); + /* Search the mapped list for this target ID */ + list_for_each_entry(ndlp, &phba->fc_nlpmap_list, nlp_listp) { + if (starget->id == ndlp->nlp_sid) { + did = ndlp->nlp_DID; + break; + } + } + spin_unlock_irq(shost->host_lock); + + fc_starget_port_id(starget) = did; +} + +static void +lpfc_get_starget_node_name(struct scsi_target *starget) +{ + struct lpfc_nodelist *ndlp = NULL; + struct Scsi_Host *shost = dev_to_shost(starget->dev.parent); + struct lpfc_hba *phba = (struct lpfc_hba *) shost->hostdata[0]; + uint64_t node_name = 0; + + spin_lock_irq(shost->host_lock); + /* Search the mapped list for this target ID */ + list_for_each_entry(ndlp, &phba->fc_nlpmap_list, nlp_listp) { + if (starget->id == ndlp->nlp_sid) { + memcpy(&node_name, &ndlp->nlp_nodename, + sizeof(struct lpfc_name)); + break; + } + } + spin_unlock_irq(shost->host_lock); + + fc_starget_node_name(starget) = be64_to_cpu(node_name); +} + +static void +lpfc_get_starget_port_name(struct scsi_target *starget) +{ + struct lpfc_nodelist *ndlp = NULL; + struct Scsi_Host *shost = dev_to_shost(starget->dev.parent); + struct lpfc_hba *phba = (struct lpfc_hba *) shost->hostdata[0]; + uint64_t port_name = 0; + + spin_lock_irq(shost->host_lock); + /* Search the mapped list for this target ID */ + list_for_each_entry(ndlp, &phba->fc_nlpmap_list, nlp_listp) { + if (starget->id == ndlp->nlp_sid) { + memcpy(&port_name, &ndlp->nlp_portname, + sizeof(struct lpfc_name)); + break; + } + } + spin_unlock_irq(shost->host_lock); + + fc_starget_port_name(starget) = be64_to_cpu(port_name); +} + +static void +lpfc_get_starget_loss_tmo(struct scsi_target *starget) +{ + /* + * Return the driver's global value for device loss timeout plus + * five seconds to allow the driver's nodev timer to run. + */ + fc_starget_dev_loss_tmo(starget) = lpfc_nodev_tmo + 5; +} + +static void +lpfc_set_starget_loss_tmo(struct scsi_target *starget, uint32_t timeout) +{ + /* + * The driver doesn't have a per-target timeout setting. Set + * this value globally. + */ + lpfc_nodev_tmo = timeout; +} + +#else /* not defined FC_TRANS_VER1 */ + +static void +lpfc_get_port_id(struct scsi_device *sdev) +{ + struct lpfc_target *target = sdev->hostdata; + if (sdev->host->transportt && target->pnode) + fc_port_id(sdev) = target->pnode->nlp_DID; +} + +static void +lpfc_get_node_name(struct scsi_device *sdev) +{ + struct lpfc_target *target = sdev->hostdata; + uint64_t node_name = 0; + if (sdev->host->transportt && target->pnode) + memcpy(&node_name, &target->pnode->nlp_nodename, + sizeof(struct lpfc_name)); + fc_node_name(sdev) = be64_to_cpu(node_name); +} + +static void +lpfc_get_port_name(struct scsi_device *sdev) +{ + struct lpfc_target *target = sdev->hostdata; + uint64_t port_name = 0; + if (sdev->host->transportt && target->pnode) + memcpy(&port_name, &target->pnode->nlp_portname, + sizeof(struct lpfc_name)); + fc_port_name(sdev) = be64_to_cpu(port_name); +} +#endif + +static struct fc_function_template lpfc_transport_functions = { +#ifdef FC_TRANS_VER2 /* fc transport w/ statistics and attrs */ + + /* fixed attributes the driver supports */ + .show_host_node_name = 1, + .show_host_port_name = 1, + .show_host_supported_classes = 1, + .show_host_supported_fc4s = 1, + .show_host_symbolic_name = 1, + .show_host_supported_speeds = 1, + .show_host_maxframe_size = 1, + + /* dynamic attributes the driver supports */ + .get_host_port_id = lpfc_get_host_port_id, + .show_host_port_id = 1, + + .get_host_port_type = lpfc_get_host_port_type, + .show_host_port_type = 1, + + .get_host_port_state = lpfc_get_host_port_state, + .show_host_port_state = 1, + + /* active_fc4s is shown but doesn't change (thus no get function) */ + .show_host_active_fc4s = 1, + + .get_host_speed = lpfc_get_host_speed, + .show_host_speed = 1, + + .get_host_fabric_name = lpfc_get_host_fabric_name, + .show_host_fabric_name = 1, + + /* + * The LPFC driver treats linkdown handling as target loss events + * so there are no sysfs handlers for link_down_tmo. + */ + + .get_fc_host_stats = lpfc_get_stats, + /* the LPFC driver doesn't support resetting stats yet */ + +#endif /* FC_TRANS_VER2 */ + +/* note: FC_TRANS_VER1 will set if FC_TRANS_VER2 is set */ +#ifdef FC_TRANS_VER1 + .get_starget_port_id = lpfc_get_starget_port_id, + .show_starget_port_id = 1, + + .get_starget_node_name = lpfc_get_starget_node_name, + .show_starget_node_name = 1, + + .get_starget_port_name = lpfc_get_starget_port_name, + .show_starget_port_name = 1, + + .get_starget_dev_loss_tmo = lpfc_get_starget_loss_tmo, + .set_starget_dev_loss_tmo = lpfc_set_starget_loss_tmo, + .show_starget_dev_loss_tmo = 1, + +#else + .get_port_id = lpfc_get_port_id, + .show_port_id = 1, + + .get_node_name = lpfc_get_node_name, + .show_node_name = 1, + + .get_port_name = lpfc_get_port_name, + .show_port_name = 1, +#endif +}; + +static int +lpfc_proc_info(struct Scsi_Host *host, + char *buf, char **start, off_t offset, int count, int rw) +{ + struct lpfc_hba *phba = (struct lpfc_hba *)host->hostdata[0]; + struct lpfc_nodelist *ndlp; + int len = 0; + + /* Sufficient bytes to hold a port or node name. */ + uint8_t name[sizeof (struct lpfc_name)]; + + /* If rw = 0, then read info + * If rw = 1, then write info (NYI) + */ + if (rw) + return -EINVAL; + + list_for_each_entry(ndlp, &phba->fc_nlpmap_list, nlp_listp) { + if (ndlp->nlp_state == NLP_STE_MAPPED_NODE){ + len += snprintf(buf + len, PAGE_SIZE -len, + "lpfc%dt%02x DID %06x WWPN ", + phba->brd_no, + ndlp->nlp_sid, ndlp->nlp_DID); + + memcpy (&name[0], &ndlp->nlp_portname, + sizeof (struct lpfc_name)); + len += snprintf(buf + len, PAGE_SIZE-len, + "%02x:%02x:%02x:%02x:%02x:%02x:" + "%02x:%02x", + name[0], name[1], name[2], + name[3], name[4], name[5], + name[6], name[7]); + len += snprintf(buf + len, PAGE_SIZE-len, " WWNN "); + memcpy (&name[0], &ndlp->nlp_nodename, + sizeof (struct lpfc_name)); + len += snprintf(buf + len, PAGE_SIZE-len, + "%02x:%02x:%02x:%02x:%02x:%02x:" + "%02x:%02x\n", + name[0], name[1], name[2], + name[3], name[4], name[5], + name[6], name[7]); + } + if (PAGE_SIZE - len < 90) + break; + } + if (&ndlp->nlp_listp != &phba->fc_nlpmap_list) + len += snprintf(buf+len, PAGE_SIZE-len, "...\n"); + + return (len); +} + +static int +lpfc_slave_alloc(struct scsi_device *scsi_devs) +{ + struct lpfc_hba *phba; + struct lpfc_target *target; + + /* + * Store the lun pointer in the scsi_device hostdata pointer provided + * the driver has already discovered the target id. + */ + phba = (struct lpfc_hba *) scsi_devs->host->hostdata[0]; + target = lpfc_find_target(phba, scsi_devs->id, NULL); + if (target) { + scsi_devs->hostdata = target; + target->slavecnt++; + return 0; + } + +#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,9) + return -ENXIO; +#else + + /* + * The driver does not have a target id matching that in the scsi + * device. Allocate a dummy target initialized to zero so that + * the driver's queuecommand entry correctly fails the call + * forcing the midlayer to call lpfc_slave_destroy. This code + * will be removed in a subsequent kernel patch. + */ + + target = kmalloc(sizeof (struct lpfc_target), GFP_KERNEL); + if (!target) + return 1; + + memset(target, 0, sizeof (struct lpfc_target)); + scsi_devs->hostdata = target; + target->slavecnt++; + return 0; +#endif +} + +static int +lpfc_slave_configure(struct scsi_device *sdev) +{ + struct lpfc_hba *phba = (struct lpfc_hba *) sdev->host->hostdata[0]; + +#if defined(FC_TRANS_VER1) + struct lpfc_target *target = (struct lpfc_target *) sdev->hostdata; +#endif + + if (sdev->tagged_supported) + scsi_activate_tcq(sdev, phba->cfg_lun_queue_depth); + else + scsi_deactivate_tcq(sdev, phba->cfg_lun_queue_depth); + +#ifdef FC_TRANS_VER1 + if ((target) && (sdev->sdev_target)) { + /* + * Initialize the fc transport attributes for the target + * containing this scsi device. Also note that the driver's + * target pointer is stored in the starget_data for the + * driver's sysfs entry point functions. + */ + target->starget = sdev->sdev_target; + fc_starget_dev_loss_tmo(target->starget) = lpfc_nodev_tmo + 5; + } +#endif + + return 0; +} + +static void +lpfc_slave_destroy(struct scsi_device *sdev) +{ + struct lpfc_hba *phba; + struct lpfc_target *target; + int i; + + phba = (struct lpfc_hba *) sdev->host->hostdata[0]; + target = sdev->hostdata; + if (target) { + target->slavecnt--; + + /* Double check for valid lpfc_target */ + for (i = 0; i < MAX_FCP_TARGET; i++) { + if(target == phba->device_queue_hash[i]) { + if ((!target->slavecnt) && !(target->pnode)) { + kfree(target); + phba->device_queue_hash[i] = NULL; + } + sdev->hostdata = NULL; + return; + } + } + /* If we get here, this was a dummy lpfc_target allocated + * in lpfc_slave_alloc. + */ + if (!target->slavecnt) + kfree(target); + } + + /* + * Set this scsi device's hostdata to NULL since it is going + * away. Also, (future) don't set the starget_dev_loss_tmo + * this value is global to all targets managed by this + * host. + */ + sdev->hostdata = NULL; + return; +} + +static struct class_device_attribute *lpfc_host_attrs[] = { + &class_device_attr_info, + &class_device_attr_serialnum, + &class_device_attr_fwrev, + &class_device_attr_hdw, + &class_device_attr_option_rom_version, + &class_device_attr_state, + &class_device_attr_num_discovered_ports, +#ifndef FC_TRANS_VER2 + &class_device_attr_speed, + &class_device_attr_node_name, + &class_device_attr_port_name, + &class_device_attr_portfcid, + &class_device_attr_port_type, + &class_device_attr_fabric_name, +#endif /* FC_TRANS_VER2 */ + &class_device_attr_events, + &class_device_attr_lpfc_drvr_version, + &class_device_attr_lpfc_log_verbose, + &class_device_attr_lpfc_lun_queue_depth, + &class_device_attr_lpfc_nodev_tmo, + &class_device_attr_lpfc_automap, + &class_device_attr_lpfc_fcp_class, + &class_device_attr_lpfc_use_adisc, + &class_device_attr_lpfc_ack0, + &class_device_attr_lpfc_topology, + &class_device_attr_lpfc_scan_down, + &class_device_attr_lpfc_link_speed, + &class_device_attr_lpfc_fdmi_on, + &class_device_attr_lpfc_fcp_bind_method, + &class_device_attr_lpfc_max_luns, + &class_device_attr_nport_evt_cnt, + &class_device_attr_management_version, + &class_device_attr_issue_lip, + &class_device_attr_board_online, + &class_device_attr_disc_npr, + &class_device_attr_disc_map, + &class_device_attr_disc_unmap, + &class_device_attr_disc_prli, + &class_device_attr_disc_reglgn, + &class_device_attr_disc_adisc, + &class_device_attr_disc_plogi, + &class_device_attr_disc_unused, + &class_device_attr_outfcpio, + NULL, +}; + +static struct scsi_host_template driver_template = { + .module = THIS_MODULE, + .name = LPFC_DRIVER_NAME, + .info = lpfc_info, + .queuecommand = lpfc_queuecommand, + .eh_abort_handler = lpfc_abort_handler, + .eh_device_reset_handler= lpfc_reset_lun_handler, + .eh_bus_reset_handler = lpfc_reset_bus_handler, + .slave_alloc = lpfc_slave_alloc, + .slave_configure = lpfc_slave_configure, + .slave_destroy = lpfc_slave_destroy, + .proc_info = lpfc_proc_info, + .proc_name = LPFC_DRIVER_NAME, + .this_id = -1, + .sg_tablesize = SG_ALL, + .cmd_per_lun = 30, + .shost_attrs = lpfc_host_attrs, + .use_clustering = ENABLE_CLUSTERING, +}; + +static int +lpfc_sli_setup(struct lpfc_hba * phba) +{ + int i, totiocb = 0; + struct lpfc_sli *psli = &phba->sli; + LPFC_RING_INIT_t *pring; + + psli->sliinit.num_rings = MAX_CONFIGURED_RINGS; + psli->fcp_ring = LPFC_FCP_RING; + psli->next_ring = LPFC_FCP_NEXT_RING; + psli->ip_ring = LPFC_IP_RING; + + for (i = 0; i < psli->sliinit.num_rings; i++) { + pring = &psli->sliinit.ringinit[i]; + switch (i) { + case LPFC_FCP_RING: /* ring 0 - FCP */ + /* numCiocb and numRiocb are used in config_port */ + pring->numCiocb = SLI2_IOCB_CMD_R0_ENTRIES; + pring->numRiocb = SLI2_IOCB_RSP_R0_ENTRIES; + pring->numCiocb += SLI2_IOCB_CMD_R1XTRA_ENTRIES; + pring->numRiocb += SLI2_IOCB_RSP_R1XTRA_ENTRIES; + pring->numCiocb += SLI2_IOCB_CMD_R3XTRA_ENTRIES; + pring->numRiocb += SLI2_IOCB_RSP_R3XTRA_ENTRIES; + pring->iotag_ctr = 0; + pring->iotag_max = + (phba->cfg_hba_queue_depth * 2); + pring->fast_iotag = pring->iotag_max; + pring->num_mask = 0; + break; + case LPFC_IP_RING: /* ring 1 - IP */ + /* numCiocb and numRiocb are used in config_port */ + pring->numCiocb = SLI2_IOCB_CMD_R1_ENTRIES; + pring->numRiocb = SLI2_IOCB_RSP_R1_ENTRIES; + pring->num_mask = 0; + break; + case LPFC_ELS_RING: /* ring 2 - ELS / CT */ + /* numCiocb and numRiocb are used in config_port */ + pring->numCiocb = SLI2_IOCB_CMD_R2_ENTRIES; + pring->numRiocb = SLI2_IOCB_RSP_R2_ENTRIES; + pring->fast_iotag = 0; + pring->iotag_ctr = 0; + pring->iotag_max = 4096; + pring->num_mask = 4; + pring->prt[0].profile = 0; /* Mask 0 */ + pring->prt[0].rctl = FC_ELS_REQ; + pring->prt[0].type = FC_ELS_DATA; + pring->prt[0].lpfc_sli_rcv_unsol_event = + lpfc_els_unsol_event; + pring->prt[1].profile = 0; /* Mask 1 */ + pring->prt[1].rctl = FC_ELS_RSP; + pring->prt[1].type = FC_ELS_DATA; + pring->prt[1].lpfc_sli_rcv_unsol_event = + lpfc_els_unsol_event; + pring->prt[2].profile = 0; /* Mask 2 */ + /* NameServer Inquiry */ + pring->prt[2].rctl = FC_UNSOL_CTL; + /* NameServer */ + pring->prt[2].type = FC_COMMON_TRANSPORT_ULP; + pring->prt[2].lpfc_sli_rcv_unsol_event = + lpfc_ct_unsol_event; + pring->prt[3].profile = 0; /* Mask 3 */ + /* NameServer response */ + pring->prt[3].rctl = FC_SOL_CTL; + /* NameServer */ + pring->prt[3].type = FC_COMMON_TRANSPORT_ULP; + pring->prt[3].lpfc_sli_rcv_unsol_event = + lpfc_ct_unsol_event; + break; + } + totiocb += (pring->numCiocb + pring->numRiocb); + } + if (totiocb > MAX_SLI2_IOCB) { + /* Too many cmd / rsp ring entries in SLI2 SLIM */ + lpfc_printf_log(phba, KERN_ERR, LOG_INIT, + "%d:0462 Too many cmd / rsp ring entries in " + "SLI2 SLIM Data: x%x x%x\n", + phba->brd_no, totiocb, MAX_SLI2_IOCB); + } + +#ifdef USE_HGP_HOST_SLIM + psli->sliinit.sli_flag = LPFC_HGP_HOSTSLIM; +#else + psli->sliinit.sli_flag = 0; +#endif + + return (0); +} + +static int +lpfc_set_bind_type(struct lpfc_hba * phba) +{ + int bind_type = phba->cfg_fcp_bind_method; + int ret = LPFC_BIND_WW_NN_PN; + + switch (bind_type) { + case 1: + phba->fcp_mapping = FCP_SEED_WWNN; + break; + + case 2: + phba->fcp_mapping = FCP_SEED_WWPN; + break; + + case 3: + phba->fcp_mapping = FCP_SEED_DID; + ret = LPFC_BIND_DID; + break; + + case 4: + phba->fcp_mapping = FCP_SEED_DID; + ret = LPFC_BIND_DID; + break; + } + + return (ret); +} + +static void +lpfc_get_cfgparam(struct lpfc_hba *phba) +{ + phba->cfg_log_verbose = lpfc_log_verbose; + phba->cfg_automap = lpfc_automap; + phba->cfg_fcp_bind_method = lpfc_fcp_bind_method; + phba->cfg_cr_delay = lpfc_cr_delay; + phba->cfg_cr_count = lpfc_cr_count; + phba->cfg_lun_queue_depth = lpfc_lun_queue_depth; + phba->cfg_fcp_class = lpfc_fcp_class; + phba->cfg_use_adisc = lpfc_use_adisc; + phba->cfg_ack0 = lpfc_ack0; + phba->cfg_topology = lpfc_topology; + phba->cfg_scan_down = lpfc_scan_down; + phba->cfg_nodev_tmo = lpfc_nodev_tmo; + phba->cfg_link_speed = lpfc_link_speed; + phba->cfg_fdmi_on = lpfc_fdmi_on; + phba->cfg_discovery_threads = lpfc_discovery_threads; + phba->cfg_max_luns = lpfc_max_luns; + phba->cfg_scsi_hotplug = lpfc_scsi_hotplug; + + if (phba->cfg_discovery_threads) + if (phba->cfg_automap == 0) + phba->cfg_discovery_threads = LPFC_MAX_DISC_THREADS; + + switch (phba->pcidev->device) { + case PCI_DEVICE_ID_LP101: + case PCI_DEVICE_ID_BSMB: + case PCI_DEVICE_ID_ZSMB: + phba->cfg_hba_queue_depth = LPFC_LP101_HBA_Q_DEPTH; + break; + case PCI_DEVICE_ID_RFLY: + case PCI_DEVICE_ID_PFLY: + case PCI_DEVICE_ID_BMID: + case PCI_DEVICE_ID_ZMID: + case PCI_DEVICE_ID_TFLY: + phba->cfg_hba_queue_depth = LPFC_LC_HBA_Q_DEPTH; + break; + default: + phba->cfg_hba_queue_depth = LPFC_DFT_HBA_Q_DEPTH; + } + return; +} + +static void +lpfc_consistent_bind_setup(struct lpfc_hba * phba) +{ + INIT_LIST_HEAD(&phba->fc_nlpbind_list); + phba->fc_bind_cnt = 0; +} + +static uint8_t +lpfc_get_brd_no(struct lpfc_hba * phba) +{ + uint8_t brd, found = 1; + + brd = 0; + while(found) { + phba = NULL; + found = 0; + list_for_each_entry(phba, &lpfc_hba_list, hba_list) { + if (phba->brd_no == brd) { + found = 1; + brd++; + break; + } + } + } + return (brd); +} + + +static int __devinit +lpfc_pci_probe_one(struct pci_dev *pdev, const struct pci_device_id *pid) +{ + struct Scsi_Host *host; + struct lpfc_hba *phba; + struct lpfc_sli *psli; + unsigned long iflag; + unsigned long bar0map_len, bar2map_len; + int error = -ENODEV, retval; +#ifdef FC_TRANS_VER2 /* fc transport w/ statistics and attrs */ + u64 wwname; +#endif /* FC_TRANS_VER2 */ + + if (pci_enable_device(pdev)) + goto out; + if (pci_request_regions(pdev, LPFC_DRIVER_NAME)) + goto out_disable_device; + + /* + * Allocate space for adapter info structure + */ +#ifdef FC_TRANS_VER2 /* fc transport w/ statistics and attrs */ + phba = kmalloc((sizeof(*phba) + sizeof(struct fc_host_statistics)), + GFP_KERNEL); +#else /* not FC_TRANS_VER2 */ + phba = kmalloc(sizeof(*phba), GFP_KERNEL); +#endif /* not FC_TRANS_VER2 */ + if (!phba) + goto out_release_regions; + memset(phba, 0, sizeof (struct lpfc_hba)); +#ifdef FC_TRANS_VER2 /* fc transport w/ statistics and attrs */ + phba->link_stats = (void *)&phba[1]; +#endif /* FC_TRANS_VER2 */ + + host = scsi_host_alloc(&driver_template, sizeof (unsigned long)); + if (!host) { + printk (KERN_WARNING "%s: scsi_host_alloc failed.\n", + lpfc_drvr_name); + error = -ENOMEM; + goto out_kfree_phba; + } + + phba->fc_flag |= FC_LOADING; + phba->pcidev = pdev; + phba->host = host; + + INIT_LIST_HEAD(&phba->ctrspbuflist); + INIT_LIST_HEAD(&phba->rnidrspbuflist); + INIT_LIST_HEAD(&phba->freebufList); + + /* Initialize timers used by driver */ + init_timer(&phba->fc_estabtmo); + phba->fc_estabtmo.function = lpfc_establish_link_tmo; + phba->fc_estabtmo.data = (unsigned long)phba; + init_timer(&phba->fc_disctmo); + phba->fc_disctmo.function = lpfc_disc_timeout; + phba->fc_disctmo.data = (unsigned long)phba; + init_timer(&phba->fc_scantmo); + phba->fc_scantmo.function = lpfc_scan_timeout; + phba->fc_scantmo.data = (unsigned long)phba; + + init_timer(&phba->fc_fdmitmo); + phba->fc_fdmitmo.function = lpfc_fdmi_tmo; + phba->fc_fdmitmo.data = (unsigned long)phba; + init_timer(&phba->els_tmofunc); + phba->els_tmofunc.function = lpfc_els_timeout_handler; + phba->els_tmofunc.data = (unsigned long)phba; + psli = &phba->sli; + init_timer(&psli->mbox_tmo); + psli->mbox_tmo.function = lpfc_mbox_timeout; + psli->mbox_tmo.data = (unsigned long)phba; + + /* Assign an unused board number */ + phba->brd_no = lpfc_get_brd_no(phba); + host->unique_id = phba->brd_no; + + /* + * Get all the module params for configuring this host and then + * establish the host parameters. + */ + lpfc_get_cfgparam(phba); + + host->max_id = LPFC_MAX_TARGET; + host->max_lun = phba->cfg_max_luns; + host->this_id = -1; + + if(phba->cfg_scsi_hotplug) { + lpfc_printf_log(phba, KERN_ERR, LOG_FCP, + "%d:0264 HotPlug Support Enabled\n", + phba->brd_no); + } + + /* Add adapter structure to list */ + list_add_tail(&phba->hba_list, &lpfc_hba_list); + + /* Initialize all internally managed lists. */ + INIT_LIST_HEAD(&phba->fc_nlpmap_list); + INIT_LIST_HEAD(&phba->fc_nlpunmap_list); + INIT_LIST_HEAD(&phba->fc_unused_list); + INIT_LIST_HEAD(&phba->fc_plogi_list); + INIT_LIST_HEAD(&phba->fc_adisc_list); + INIT_LIST_HEAD(&phba->fc_reglogin_list); + INIT_LIST_HEAD(&phba->fc_prli_list); + INIT_LIST_HEAD(&phba->fc_npr_list); + lpfc_consistent_bind_setup(phba); + + init_waitqueue_head(&phba->linkevtwq); + init_waitqueue_head(&phba->rscnevtwq); + init_waitqueue_head(&phba->ctevtwq); + + pci_set_master(pdev); + retval = pci_set_mwi(pdev); + if (retval) + dev_printk(KERN_WARNING, &pdev->dev, + "Warning: pci_set_mwi returned %d\n", retval); + + /* Configure DMA attributes. */ + if (dma_set_mask(&phba->pcidev->dev, 0xffffffffffffffffULL) && + dma_set_mask(&phba->pcidev->dev, 0xffffffffULL)) + goto out_list_del; + + /* + * Get the physical address of Bar0 and Bar2 and the number of bytes + * required by each mapping. + */ + phba->pci_bar0_map = pci_resource_start(phba->pcidev, 0); + bar0map_len = pci_resource_len(phba->pcidev, 0); + + phba->pci_bar2_map = pci_resource_start(phba->pcidev, 2); + bar2map_len = pci_resource_len(phba->pcidev, 2); + + /* Map HBA SLIM and Control Registers to a kernel virtual address. */ + phba->slim_memmap_p = ioremap(phba->pci_bar0_map, bar0map_len); + phba->ctrl_regs_memmap_p = ioremap(phba->pci_bar2_map, bar2map_len); + + /* + * Allocate memory for SLI-2 structures + */ + phba->slim2p = dma_alloc_coherent(&phba->pcidev->dev, SLI2_SLIM_SIZE, + &phba->slim2p_mapping, GFP_KERNEL); + if (!phba->slim2p) + goto out_iounmap; + + + lpfc_sli_setup(phba); /* Setup SLI Layer to run over lpfc HBAs */ + lpfc_sli_queue_setup(phba); /* Initialize the SLI Layer */ + + error = lpfc_mem_alloc(phba); + if (error) + goto out_dec_nhbas; + + lpfc_set_bind_type(phba); + + /* Initialize HBA structure */ + phba->fc_edtov = FF_DEF_EDTOV; + phba->fc_ratov = FF_DEF_RATOV; + phba->fc_altov = FF_DEF_ALTOV; + phba->fc_arbtov = FF_DEF_ARBTOV; + + INIT_LIST_HEAD(&phba->dpc_disc); + init_completion(&phba->dpc_startup); + init_completion(&phba->dpc_exiting); + + /* + * Startup the kernel thread for this host adapter + */ + phba->dpc_kill = 0; + phba->dpc_pid = kernel_thread(lpfc_do_dpc, phba, 0); + if (phba->dpc_pid < 0) { + error = phba->dpc_pid; + goto out_free_mem; + } + wait_for_completion(&phba->dpc_startup); + + /* Call SLI to initialize the HBA. */ + error = lpfc_sli_hba_setup(phba); + if (error) + goto out_hba_down; + + /* We can rely on a queue depth attribute only after SLI HBA setup */ + host->can_queue = phba->cfg_hba_queue_depth - 10; + + /* + * Starting with 2.4.0 kernel, Linux can support commands longer + * than 12 bytes. However, scsi_register() always sets it to 12. + * For it to be useful to the midlayer, we have to set it here. + */ + host->max_cmd_len = 16; + + /* + * Queue depths per lun + */ + host->transportt = lpfc_transport_template; + host->hostdata[0] = (unsigned long)phba; + pci_set_drvdata(pdev, host); + error = scsi_add_host(host, &pdev->dev); + if (error) + goto out_put_host; + +#ifdef FC_TRANS_VER2 + /* + * set fixed host attributes + */ + + memcpy(&wwname, &phba->fc_nodename, sizeof(u64)); + fc_host_node_name(host) = be64_to_cpu(wwname); + memcpy(&wwname, &phba->fc_portname, sizeof(u64)); + fc_host_port_name(host) = be64_to_cpu(wwname); + fc_host_supported_classes(host) = FC_COS_CLASS3; + + memset(fc_host_supported_fc4s(host), 0, + sizeof(fc_host_supported_fc4s(host))); + fc_host_supported_fc4s(host)[2] = 1; + fc_host_supported_fc4s(host)[7] = 1; + + lpfc_get_hba_sym_node_name(phba, fc_host_symbolic_name(host)); + + if (FC_JEDEC_ID(phba->vpd.rev.biuRev) == VIPER_JEDEC_ID) + fc_host_supported_speeds(host) = FC_PORTSPEED_10GBIT; + else if (FC_JEDEC_ID(phba->vpd.rev.biuRev) == HELIOS_JEDEC_ID) + fc_host_supported_speeds(host) = + (FC_PORTSPEED_1GBIT | FC_PORTSPEED_2GBIT | + FC_PORTSPEED_4GBIT); + else if ((FC_JEDEC_ID(phba->vpd.rev.biuRev) == + CENTAUR_2G_JEDEC_ID) + || (FC_JEDEC_ID(phba->vpd.rev.biuRev) == + PEGASUS_JEDEC_ID) + || (FC_JEDEC_ID(phba->vpd.rev.biuRev) == + THOR_JEDEC_ID)) + fc_host_supported_speeds(host) = + (FC_PORTSPEED_1GBIT | FC_PORTSPEED_2GBIT); + else + fc_host_supported_speeds(host) = FC_PORTSPEED_1GBIT; + + fc_host_maxframe_size(host) = be32_to_cpu( + ((((uint32_t) phba->fc_sparam.cmn.bbRcvSizeMsb) << 8) | + (uint32_t) phba->fc_sparam.cmn.bbRcvSizeLsb)); + + /* This value is also unchanging */ + memset(fc_host_active_fc4s(host), 0, + sizeof(fc_host_active_fc4s(host))); + fc_host_active_fc4s(host)[2] = 1; + fc_host_active_fc4s(host)[7] = 1; +#endif /* FC_TRANS_VER2 */ + +#ifdef DFC_DEBUG + sysfs_create_bin_file(&host->shost_classdev.kobj, &sysfs_ctpass_attr); + sysfs_create_bin_file(&host->shost_classdev.kobj, + &sysfs_sendrnid_attr); + + + if (phba->sli.sliinit.sli_flag & LPFC_SLI2_ACTIVE) + sysfs_slimem_attr.size = SLI2_SLIM_SIZE; + else + sysfs_slimem_attr.size = SLI1_SLIM_SIZE; + + sysfs_create_bin_file(&host->shost_classdev.kobj, &sysfs_slimem_attr); +#endif + sysfs_create_bin_file(&host->shost_classdev.kobj, &sysfs_ctlreg_attr); + sysfs_create_bin_file(&host->shost_classdev.kobj, &sysfs_mbox_attr); + scsi_scan_host(host); + phba->fc_flag &= ~FC_LOADING; + return 0; + +out_put_host: + scsi_host_put(host); +out_hba_down: + lpfc_sli_hba_down(phba); + + /* Stop any timers that were started during this attach. */ + spin_lock_irqsave(phba->host->host_lock, iflag); + lpfc_stop_timer(phba); + spin_unlock_irqrestore(phba->host->host_lock, iflag); + + /* Kill the kernel thread for this host */ + if (phba->dpc_pid >= 0) { + phba->dpc_kill = 1; + wmb(); + kill_proc(phba->dpc_pid, SIGHUP, 1); + wait_for_completion(&phba->dpc_exiting); + } + + free_irq(phba->pcidev->irq, phba); +out_free_mem: + lpfc_mem_free(phba); +out_dec_nhbas: + dma_free_coherent(&pdev->dev, SLI2_SLIM_SIZE, + phba->slim2p, phba->slim2p_mapping); +out_iounmap: + iounmap(phba->ctrl_regs_memmap_p); + iounmap(phba->slim_memmap_p); +out_list_del: + list_del_init(&phba->hba_list); +out_kfree_phba: + kfree(phba); +out_release_regions: + pci_release_regions(pdev); +out_disable_device: + pci_disable_device(pdev); +out: + return error; +} + +static void __devexit +lpfc_pci_remove_one(struct pci_dev *pdev) +{ + struct Scsi_Host *host = pci_get_drvdata(pdev); + struct lpfc_hba *phba = (struct lpfc_hba *)host->hostdata[0]; + struct lpfc_target *targetp; + int i; + unsigned long iflag; + + sysfs_remove_bin_file(&host->shost_classdev.kobj, &sysfs_mbox_attr); + sysfs_remove_bin_file(&host->shost_classdev.kobj, &sysfs_ctlreg_attr); +#ifdef DFC_DEBUG + sysfs_remove_bin_file(&host->shost_classdev.kobj, &sysfs_slimem_attr); + sysfs_remove_bin_file(&host->shost_classdev.kobj, + &sysfs_sendrnid_attr); + sysfs_remove_bin_file(&host->shost_gendev.kobj, &sysfs_ctpass_attr); +#endif + +/* FIX ME */ + + /* In case we are offline or link is down */ + /*scsi_unblock_requests(phba->host);*/ + + phba->fc_flag |= FC_UNLOADING; + + spin_lock_irqsave(phba->host->host_lock, iflag); + /* Since we are going to scsi_remove_host(), disassociate scsi_dev + * from lpfc_target, and make sure its unblocked. + */ + for (i = 0; i < MAX_FCP_TARGET; i++) { + targetp = phba->device_queue_hash[i]; + if (!targetp) + continue; +#if defined(FC_TRANS_VER1) || defined(FC_TRANS_265_BLKPATCH) + if(targetp->pnode) { + if(targetp->blocked) { + /* If we are blocked, force a nodev_tmo */ + del_timer_sync(&targetp->pnode->nlp_tmofunc); + lpfc_process_nodev_timeout(phba, + targetp->pnode); + } + else { + /* If we are unblocked, just remove + * the scsi device. + */ + lpfc_target_remove(phba, targetp); + } + } +#endif +#if defined(FC_TRANS_VER1) + targetp->starget = NULL; +#endif + } + spin_unlock_irqrestore(phba->host->host_lock, iflag); + + list_del(&phba->hba_list); + scsi_remove_host(phba->host); + + /* detach the board */ + + /* Kill the kernel thread for this host */ + if (phba->dpc_pid >= 0) { + phba->dpc_kill = 1; + wmb(); + kill_proc(phba->dpc_pid, SIGHUP, 1); + wait_for_completion(&phba->dpc_exiting); + } + + /* + * Bring down the SLI Layer. This step disable all interrupts, + * clears the rings, discards all mailbox commands, and resets + * the HBA. + */ + lpfc_sli_hba_down(phba); + + /* Release the irq reservation */ + free_irq(phba->pcidev->irq, phba); + + spin_lock_irqsave(phba->host->host_lock, iflag); + lpfc_cleanup(phba, 0); + lpfc_stop_timer(phba); + spin_unlock_irqrestore(phba->host->host_lock, iflag); + lpfc_scsi_free(phba); + + lpfc_mem_free(phba); + + /* Free resources associated with SLI2 interface */ + dma_free_coherent(&pdev->dev, SLI2_SLIM_SIZE, + phba->slim2p, phba->slim2p_mapping); + + /* unmap adapter SLIM and Control Registers */ + iounmap(phba->ctrl_regs_memmap_p); + iounmap(phba->slim_memmap_p); + + pci_release_regions(phba->pcidev); + pci_disable_device(phba->pcidev); + + scsi_host_put(phba->host); + kfree(phba); + + pci_set_drvdata(pdev, NULL); +} + +static struct pci_device_id lpfc_id_table[] = { + {PCI_VENDOR_ID_EMULEX, PCI_DEVICE_ID_VIPER, + PCI_ANY_ID, PCI_ANY_ID, }, + {PCI_VENDOR_ID_EMULEX, PCI_DEVICE_ID_THOR, + PCI_ANY_ID, PCI_ANY_ID, }, + {PCI_VENDOR_ID_EMULEX, PCI_DEVICE_ID_PEGASUS, + PCI_ANY_ID, PCI_ANY_ID, }, + {PCI_VENDOR_ID_EMULEX, PCI_DEVICE_ID_CENTAUR, + PCI_ANY_ID, PCI_ANY_ID, }, + {PCI_VENDOR_ID_EMULEX, PCI_DEVICE_ID_DRAGONFLY, + PCI_ANY_ID, PCI_ANY_ID, }, + {PCI_VENDOR_ID_EMULEX, PCI_DEVICE_ID_SUPERFLY, + PCI_ANY_ID, PCI_ANY_ID, }, + {PCI_VENDOR_ID_EMULEX, PCI_DEVICE_ID_RFLY, + PCI_ANY_ID, PCI_ANY_ID, }, + {PCI_VENDOR_ID_EMULEX, PCI_DEVICE_ID_PFLY, + PCI_ANY_ID, PCI_ANY_ID, }, + {PCI_VENDOR_ID_EMULEX, PCI_DEVICE_ID_HELIOS, + PCI_ANY_ID, PCI_ANY_ID, }, + {PCI_VENDOR_ID_EMULEX, PCI_DEVICE_ID_BMID, + PCI_ANY_ID, PCI_ANY_ID, }, + {PCI_VENDOR_ID_EMULEX, PCI_DEVICE_ID_BSMB, + PCI_ANY_ID, PCI_ANY_ID, }, + {PCI_VENDOR_ID_EMULEX, PCI_DEVICE_ID_ZEPHYR, + PCI_ANY_ID, PCI_ANY_ID, }, + {PCI_VENDOR_ID_EMULEX, PCI_DEVICE_ID_ZMID, + PCI_ANY_ID, PCI_ANY_ID, }, + {PCI_VENDOR_ID_EMULEX, PCI_DEVICE_ID_ZSMB, + PCI_ANY_ID, PCI_ANY_ID, }, + {PCI_VENDOR_ID_EMULEX, PCI_DEVICE_ID_TFLY, + PCI_ANY_ID, PCI_ANY_ID, }, + {PCI_VENDOR_ID_EMULEX, PCI_DEVICE_ID_LP101, + PCI_ANY_ID, PCI_ANY_ID, }, + { 0 } +}; +MODULE_DEVICE_TABLE(pci, lpfc_id_table); + + +static struct pci_driver lpfc_driver = { + .name = LPFC_DRIVER_NAME, + .id_table = lpfc_id_table, + .probe = lpfc_pci_probe_one, + .remove = __devexit_p(lpfc_pci_remove_one), +}; + +static int __init +lpfc_init(void) +{ + int rc; + + printk(LPFC_MODULE_DESC "\n"); + + + lpfc_transport_template = + fc_attach_transport(&lpfc_transport_functions); + if (!lpfc_transport_template) + return -ENODEV; + rc = pci_module_init(&lpfc_driver); + return rc; + +} + +static void __exit +lpfc_exit(void) +{ + pci_unregister_driver(&lpfc_driver); + fc_release_transport(lpfc_transport_template); +} +module_init(lpfc_init); +module_exit(lpfc_exit); +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION(LPFC_MODULE_DESC); +MODULE_AUTHOR("Emulex Corporation - tech.support@emulex.com"); +MODULE_VERSION("0:" LPFC_DRIVER_VERSION); diff --git a/drivers/scsi/lpfc/lpfc_fcp.h b/drivers/scsi/lpfc/lpfc_fcp.h new file mode 100644 index 000000000..f6ec3a483 --- /dev/null +++ b/drivers/scsi/lpfc/lpfc_fcp.h @@ -0,0 +1,108 @@ +/******************************************************************* + * This file is part of the Emulex Linux Device Driver for * + * Enterprise Fibre Channel Host Bus Adapters. * + * Refer to the README file included with this package for * + * driver version and adapter support. * + * Copyright (C) 2004 Emulex Corporation. * + * www.emulex.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, a copy of which * + * can be found in the file COPYING included with this package. * + *******************************************************************/ + +/* + * $Id: lpfc_fcp.h 1.8 2004/10/14 12:03:17EDT sf_support Exp $ + */ + +#ifndef H_LPFC_DFC +#define H_LPFC_DFC + +#define MAX_LPFC_SNS 128 + +struct fcp_rsp { + uint32_t rspRsvd1; /* FC Word 0, byte 0:3 */ + uint32_t rspRsvd2; /* FC Word 1, byte 0:3 */ + + uint8_t rspStatus0; /* FCP_STATUS byte 0 (reserved) */ + uint8_t rspStatus1; /* FCP_STATUS byte 1 (reserved) */ + uint8_t rspStatus2; /* FCP_STATUS byte 2 field validity */ +#define RSP_LEN_VALID 0x01 /* bit 0 */ +#define SNS_LEN_VALID 0x02 /* bit 1 */ +#define RESID_OVER 0x04 /* bit 2 */ +#define RESID_UNDER 0x08 /* bit 3 */ + uint8_t rspStatus3; /* FCP_STATUS byte 3 SCSI status byte */ + + uint32_t rspResId; /* Residual xfer if residual count field set in + fcpStatus2 */ + /* Received in Big Endian format */ + uint32_t rspSnsLen; /* Length of sense data in fcpSnsInfo */ + /* Received in Big Endian format */ + uint32_t rspRspLen; /* Length of FCP response data in fcpRspInfo */ + /* Received in Big Endian format */ + + uint8_t rspInfo0; /* FCP_RSP_INFO byte 0 (reserved) */ + uint8_t rspInfo1; /* FCP_RSP_INFO byte 1 (reserved) */ + uint8_t rspInfo2; /* FCP_RSP_INFO byte 2 (reserved) */ + uint8_t rspInfo3; /* FCP_RSP_INFO RSP_CODE byte 3 */ + +#define RSP_NO_FAILURE 0x00 +#define RSP_DATA_BURST_ERR 0x01 +#define RSP_CMD_FIELD_ERR 0x02 +#define RSP_RO_MISMATCH_ERR 0x03 +#define RSP_TM_NOT_SUPPORTED 0x04 /* Task mgmt function not supported */ +#define RSP_TM_NOT_COMPLETED 0x05 /* Task mgmt function not performed */ + + uint32_t rspInfoRsvd; /* FCP_RSP_INFO bytes 4-7 (reserved) */ + + uint8_t rspSnsInfo[MAX_LPFC_SNS]; +#define SNS_ILLEGAL_REQ 0x05 /* sense key is byte 3 ([2]) */ +#define SNSCOD_BADCMD 0x20 /* sense code is byte 13 ([12]) */ +}; + +struct fcp_cmnd { + uint32_t fcpLunMsl; /* most significant lun word (32 bits) */ + uint32_t fcpLunLsl; /* least significant lun word (32 bits) */ + /* # of bits to shift lun id to end up in right + * payload word, little endian = 8, big = 16. + */ +#if __BIG_ENDIAN +#define FC_LUN_SHIFT 16 +#define FC_ADDR_MODE_SHIFT 24 +#else /* __LITTLE_ENDIAN */ +#define FC_LUN_SHIFT 8 +#define FC_ADDR_MODE_SHIFT 0 +#endif + + uint8_t fcpCntl0; /* FCP_CNTL byte 0 (reserved) */ + uint8_t fcpCntl1; /* FCP_CNTL byte 1 task codes */ +#define SIMPLE_Q 0x00 +#define HEAD_OF_Q 0x01 +#define ORDERED_Q 0x02 +#define ACA_Q 0x04 +#define UNTAGGED 0x05 + uint8_t fcpCntl2; /* FCP_CTL byte 2 task management codes */ +#define FCP_ABORT_TASK_SET 0x02 /* Bit 1 */ +#define FCP_CLEAR_TASK_SET 0x04 /* bit 2 */ +#define FCP_BUS_RESET 0x08 /* bit 3 */ +#define FCP_LUN_RESET 0x10 /* bit 4 */ +#define FCP_TARGET_RESET 0x20 /* bit 5 */ +#define FCP_CLEAR_ACA 0x40 /* bit 6 */ +#define FCP_TERMINATE_TASK 0x80 /* bit 7 */ + uint8_t fcpCntl3; +#define WRITE_DATA 0x01 /* Bit 0 */ +#define READ_DATA 0x02 /* Bit 1 */ + + uint8_t fcpCdb[16]; /* SRB cdb field is copied here */ + uint32_t fcpDl; /* Total transfer length */ + +}; + +#endif diff --git a/drivers/scsi/lpfc/lpfc_hbadisc.c b/drivers/scsi/lpfc/lpfc_hbadisc.c new file mode 100644 index 000000000..74c454311 --- /dev/null +++ b/drivers/scsi/lpfc/lpfc_hbadisc.c @@ -0,0 +1,2703 @@ +/******************************************************************* + * This file is part of the Emulex Linux Device Driver for * + * Enterprise Fibre Channel Host Bus Adapters. * + * Refer to the README file included with this package for * + * driver version and adapter support. * + * Copyright (C) 2004 Emulex Corporation. * + * www.emulex.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, a copy of which * + * can be found in the file COPYING included with this package. * + *******************************************************************/ + +/* + * $Id: lpfc_hbadisc.c 1.199 2004/11/18 20:19:30EST sf_support Exp $ + */ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include + +#include "lpfc_sli.h" +#include "lpfc_disc.h" +#include "lpfc_scsi.h" +#include "lpfc.h" +#include "lpfc_crtn.h" +#include "lpfc_fcp.h" +#include "lpfc_hw.h" +#include "lpfc_logmsg.h" +#include "lpfc_mem.h" + +/* AlpaArray for assignment of scsid for scan-down and bind_method */ +uint8_t lpfcAlpaArray[] = { + 0xEF, 0xE8, 0xE4, 0xE2, 0xE1, 0xE0, 0xDC, 0xDA, 0xD9, 0xD6, + 0xD5, 0xD4, 0xD3, 0xD2, 0xD1, 0xCE, 0xCD, 0xCC, 0xCB, 0xCA, + 0xC9, 0xC7, 0xC6, 0xC5, 0xC3, 0xBC, 0xBA, 0xB9, 0xB6, 0xB5, + 0xB4, 0xB3, 0xB2, 0xB1, 0xAE, 0xAD, 0xAC, 0xAB, 0xAA, 0xA9, + 0xA7, 0xA6, 0xA5, 0xA3, 0x9F, 0x9E, 0x9D, 0x9B, 0x98, 0x97, + 0x90, 0x8F, 0x88, 0x84, 0x82, 0x81, 0x80, 0x7C, 0x7A, 0x79, + 0x76, 0x75, 0x74, 0x73, 0x72, 0x71, 0x6E, 0x6D, 0x6C, 0x6B, + 0x6A, 0x69, 0x67, 0x66, 0x65, 0x63, 0x5C, 0x5A, 0x59, 0x56, + 0x55, 0x54, 0x53, 0x52, 0x51, 0x4E, 0x4D, 0x4C, 0x4B, 0x4A, + 0x49, 0x47, 0x46, 0x45, 0x43, 0x3C, 0x3A, 0x39, 0x36, 0x35, + 0x34, 0x33, 0x32, 0x31, 0x2E, 0x2D, 0x2C, 0x2B, 0x2A, 0x29, + 0x27, 0x26, 0x25, 0x23, 0x1F, 0x1E, 0x1D, 0x1B, 0x18, 0x17, + 0x10, 0x0F, 0x08, 0x04, 0x02, 0x01 +}; + +static void +lpfc_evt_iocb_free(struct lpfc_hba * phba, struct lpfc_iocbq * saveq) +{ + struct lpfc_iocbq *rspiocbp, *tmpiocbp; + + /* Free up iocb buffer chain for cmd just processed */ + list_for_each_entry_safe(rspiocbp, tmpiocbp, + &saveq->list, list) { + list_del(&rspiocbp->list); + mempool_free( rspiocbp, phba->iocb_mem_pool); + } + mempool_free( saveq, phba->iocb_mem_pool); +} + +void +lpfc_process_nodev_timeout(struct lpfc_hba *phba, struct lpfc_nodelist *ndlp) +{ + struct lpfc_target *targetp; + int scsid; + + if (!(ndlp->nlp_type & NLP_FABRIC)) { + /* Nodev timeout on NPort */ + lpfc_printf_log(phba, KERN_ERR, LOG_DISCOVERY, + "%d:0203 Nodev timeout on NPort x%x " + "Data: x%x x%x x%x\n", + phba->brd_no, ndlp->nlp_DID, ndlp->nlp_flag, + ndlp->nlp_state, ndlp->nlp_rpi); + } + + ndlp->nlp_flag &= ~NLP_NODEV_TMO; + + for(scsid=0;scsiddevice_queue_hash[scsid]; + /* First see if the SCSI ID has an allocated struct + lpfc_target */ + if (targetp) { + if (targetp->pnode == ndlp) { + /* flush the target */ + lpfc_sli_abort_iocb_tgt(phba, + &phba->sli.ring[phba->sli.fcp_ring], + scsid, LPFC_ABORT_ALLQ); + } + } + } + + lpfc_disc_state_machine(phba, ndlp, NULL, NLP_EVT_DEVICE_RM); + return; +} + +static void +lpfc_disc_done(struct lpfc_hba * phba) +{ + struct lpfc_sli *psli = &phba->sli; + LPFC_DISC_EVT_t *evtp, *next_evtp; + LPFC_MBOXQ_t *pmb; + struct lpfc_iocbq *cmdiocbp, *saveq; + struct lpfc_nodelist *ndlp; + LPFC_RING_MASK_t *func; + struct Scsi_Host *shost; + LIST_HEAD(local_dpc_disc); + + list_splice_init(&phba->dpc_disc, &local_dpc_disc); + + /* check discovery event list */ + list_for_each_entry_safe(evtp, next_evtp, &local_dpc_disc, evt_listp) { + list_del(&evtp->evt_listp); + + switch(evtp->evt) { + case LPFC_EVT_MBOX: + pmb = (LPFC_MBOXQ_t *)(evtp->evt_arg1); + (pmb->mbox_cmpl) (phba, pmb); + break; + case LPFC_EVT_SOL_IOCB: + cmdiocbp = (struct lpfc_iocbq *)(evtp->evt_arg1); + saveq = (struct lpfc_iocbq *)(evtp->evt_arg2); + (cmdiocbp->iocb_cmpl) (phba, cmdiocbp, saveq); + lpfc_evt_iocb_free(phba, saveq); + break; + case LPFC_EVT_UNSOL_IOCB: + func = (LPFC_RING_MASK_t *)(evtp->evt_arg1); + saveq = (struct lpfc_iocbq *)(evtp->evt_arg2); + (func->lpfc_sli_rcv_unsol_event) (phba, + &psli->ring[LPFC_ELS_RING], saveq); + lpfc_evt_iocb_free(phba, saveq); + break; + case LPFC_EVT_NODEV_TMO: + ndlp = (struct lpfc_nodelist *)(evtp->evt_arg1); + lpfc_process_nodev_timeout(phba, ndlp); + break; + case LPFC_EVT_SCAN: + /* SCSI HOTPLUG supported */ + shost = phba->host; +#ifdef USE_SCAN_TARGET + { + struct lpfc_target *targetp; + + targetp = (struct lpfc_target *)(evtp->evt_arg1); + lpfc_printf_log(phba, KERN_ERR, LOG_DISCOVERY | LOG_FCP, + "%d:0251 Rescanning scsi target %d\n", + phba->brd_no, targetp->scsi_id); + + if(targetp == + phba->device_queue_hash[targetp->scsi_id]) { + spin_unlock_irq(phba->host->host_lock); + scsi_scan_single_target(shost, 0, + targetp->scsi_id); + spin_lock_irq(phba->host->host_lock); + } + } +#else + lpfc_printf_log(phba, KERN_ERR, LOG_DISCOVERY | LOG_FCP, + "%d:0251 Rescanning scsi host\n", phba->brd_no); + spin_unlock_irq(shost->host_lock); + scsi_scan_host(shost); + spin_lock_irq(shost->host_lock); +#endif + break; + } + kfree(evtp); + } +} + +int +lpfc_do_dpc(void *p) +{ + unsigned long flags; + DECLARE_MUTEX_LOCKED(sem); + struct lpfc_hba *phba = (struct lpfc_hba *)p; + + lock_kernel(); + + daemonize("lpfc_dpc_%d", phba->brd_no); + allow_signal(SIGHUP); + + phba->dpc_wait = &sem; + set_user_nice(current, -20); + + unlock_kernel(); + + complete(&phba->dpc_startup); + + while (1) { + if (down_interruptible(&sem)) + break; + + if (signal_pending(current)) + break; + + if (phba->dpc_kill) + break; + + spin_lock_irqsave(phba->host->host_lock, flags); + lpfc_disc_done(phba); + spin_unlock_irqrestore(phba->host->host_lock, flags); + } + + /* Zero out semaphore we were waiting on. */ + phba->dpc_wait = NULL; + complete_and_exit(&phba->dpc_exiting, 0); + return(0); +} + +/* + * This is only called to handle FC discovery events. Since this a rare + * occurance, we allocate an LPFC_DISC_EVT_t structure here instead of + * embedding it in the IOCB. + */ +int +lpfc_discq_post_event(struct lpfc_hba * phba, void *arg1, void *arg2, + uint32_t evt) +{ + LPFC_DISC_EVT_t *evtp; + + /* All Mailbox completions and LPFC_ELS_RING rcv ring IOCB events + * will be queued to DPC for processing + */ + evtp = (LPFC_DISC_EVT_t *) kmalloc(sizeof(LPFC_DISC_EVT_t), GFP_ATOMIC); + if (!evtp) + return 0; + + evtp->evt_arg1 = arg1; + evtp->evt_arg2 = arg2; + evtp->evt = evt; + evtp->evt_listp.next = NULL; + evtp->evt_listp.prev = NULL; + + /* Queue the event to the DPC to be processed later */ + list_add_tail(&evtp->evt_listp, &phba->dpc_disc); + if (phba->dpc_wait) + up(phba->dpc_wait); + + return 1; +} + +int +lpfc_linkdown(struct lpfc_hba * phba) +{ + struct lpfc_sli *psli; + struct lpfc_nodelist *ndlp, *next_ndlp; + struct list_head *listp; + struct list_head *node_list[7]; + LPFC_MBOXQ_t *mb; + int rc, i; + + psli = &phba->sli; + phba->hba_state = LPFC_LINK_DOWN; + +#if !defined(FC_TRANS_VER1) && !defined(FC_TRANS_265_BLKPATCH) + /* Stop all requests to the driver from the midlayer. */ + scsi_block_requests(phba->host); +#endif + + lpfc_put_event(phba, HBA_EVENT_LINK_DOWN, phba->fc_myDID, NULL, 0, 0); + + /* Clean up any firmware default rpi's */ + if ((mb = mempool_alloc(phba->mbox_mem_pool, GFP_ATOMIC))) { + lpfc_unreg_did(phba, 0xffffffff, mb); + if (lpfc_sli_issue_mbox(phba, mb, (MBX_NOWAIT | MBX_STOP_IOCB)) + == MBX_NOT_FINISHED) { + mempool_free( mb, phba->mbox_mem_pool); + } + } + + /* Cleanup any outstanding RSCN activity */ + lpfc_els_flush_rscn(phba); + + /* Cleanup any outstanding ELS commands */ + lpfc_els_flush_cmd(phba); + + /* + * If this function was called by the lpfc_do_dpc, don't recurse into + * the routine again. If not, just process any outstanding + * discovery events. + */ + if (!list_empty(&phba->dpc_disc)) { + lpfc_disc_done(phba); + } + + /* Issue a LINK DOWN event to all nodes */ + node_list[0] = &phba->fc_npr_list; /* MUST do this list first */ + node_list[1] = &phba->fc_nlpmap_list; + node_list[2] = &phba->fc_nlpunmap_list; + node_list[3] = &phba->fc_prli_list; + node_list[4] = &phba->fc_reglogin_list; + node_list[5] = &phba->fc_adisc_list; + node_list[6] = &phba->fc_plogi_list; + for (i = 0; i < 7; i++) { + listp = node_list[i]; + if (list_empty(listp)) + continue; + + list_for_each_entry_safe(ndlp, next_ndlp, listp, nlp_listp) { + /* Fabric nodes are not handled thru state machine for + link down */ + if (ndlp->nlp_type & NLP_FABRIC) { + /* Remove ALL Fabric nodes except Fabric_DID */ + if (ndlp->nlp_DID != Fabric_DID) { + /* Take it off current list and free */ + lpfc_nlp_list(phba, ndlp, + NLP_NO_LIST); + } + } + else { + lpfc_set_failmask(phba, ndlp, + LPFC_DEV_LINK_DOWN, + LPFC_SET_BITMASK); + + rc = lpfc_disc_state_machine(phba, ndlp, NULL, + NLP_EVT_DEVICE_RECOVERY); + } + } + } + + /* free any ndlp's on unused list */ + list_for_each_entry_safe(ndlp, next_ndlp, &phba->fc_unused_list, + nlp_listp) { + lpfc_nlp_list(phba, ndlp, NLP_NO_LIST); + } + + /* Setup myDID for link up if we are in pt2pt mode */ + if (phba->fc_flag & FC_PT2PT) { + phba->fc_myDID = 0; + if ((mb = mempool_alloc(phba->mbox_mem_pool, GFP_ATOMIC))) { + lpfc_config_link(phba, mb); + if (lpfc_sli_issue_mbox + (phba, mb, (MBX_NOWAIT | MBX_STOP_IOCB)) + == MBX_NOT_FINISHED) { + mempool_free( mb, phba->mbox_mem_pool); + } + } + phba->fc_flag &= ~(FC_PT2PT | FC_PT2PT_PLOGI); + } + phba->fc_flag &= ~FC_LBIT; + + /* Turn off discovery timer if its running */ + lpfc_can_disctmo(phba); + + /* Must process IOCBs on all rings to handle ABORTed I/Os */ + return (0); +} + +static int +lpfc_linkup(struct lpfc_hba * phba) +{ + struct lpfc_nodelist *ndlp, *next_ndlp; + struct list_head *listp; + struct list_head *node_list[7]; + int i; + + phba->hba_state = LPFC_LINK_UP; + phba->fc_flag &= ~(FC_PT2PT | FC_PT2PT_PLOGI | FC_ABORT_DISCOVERY | + FC_RSCN_MODE | FC_NLP_MORE | FC_RSCN_DISCOVERY); + phba->fc_ns_retry = 0; + + + lpfc_put_event(phba, HBA_EVENT_LINK_UP, phba->fc_myDID, + (void *)(unsigned long)(phba->fc_topology), + 0, phba->fc_linkspeed); + + /* + * Clean up old Fabric NLP_FABRIC logins. + */ + list_for_each_entry_safe(ndlp, next_ndlp, &phba->fc_nlpunmap_list, + nlp_listp) { + if (ndlp->nlp_DID == Fabric_DID) { + /* Take it off current list and free */ + lpfc_nlp_list(phba, ndlp, NLP_NO_LIST); + } + } + + /* free any ndlp's on unused list */ + list_for_each_entry_safe(ndlp, next_ndlp, &phba->fc_unused_list, + nlp_listp) { + lpfc_nlp_list(phba, ndlp, NLP_NO_LIST); + } + + /* Mark all nodes for LINK UP */ + node_list[0] = &phba->fc_plogi_list; + node_list[1] = &phba->fc_adisc_list; + node_list[2] = &phba->fc_reglogin_list; + node_list[3] = &phba->fc_prli_list; + node_list[4] = &phba->fc_nlpunmap_list; + node_list[5] = &phba->fc_nlpmap_list; + node_list[6] = &phba->fc_npr_list; + for (i = 0; i < 7; i++) { + listp = node_list[i]; + if (list_empty(listp)) + continue; + + list_for_each_entry(ndlp, listp, nlp_listp) { + lpfc_set_failmask(phba, ndlp, LPFC_DEV_DISCOVERY_INP, + LPFC_SET_BITMASK); + lpfc_set_failmask(phba, ndlp, LPFC_DEV_LINK_DOWN, + LPFC_CLR_BITMASK); + } + } + +#if !defined(FC_TRANS_VER1) && !defined(FC_TRANS_265_BLKPATCH) + spin_unlock_irq(phba->host->host_lock); + scsi_unblock_requests(phba->host); + spin_lock_irq(phba->host->host_lock); +#endif + return 0; +} + +/* + * This routine handles processing a CLEAR_LA mailbox + * command upon completion. It is setup in the LPFC_MBOXQ + * as the completion routine when the command is + * handed off to the SLI layer. + */ +void +lpfc_mbx_cmpl_clear_la(struct lpfc_hba * phba, LPFC_MBOXQ_t * pmb) +{ + struct lpfc_sli *psli; + MAILBOX_t *mb; + uint32_t control; + + psli = &phba->sli; + mb = &pmb->mb; + /* Since we don't do discovery right now, turn these off here */ + psli->ring[psli->ip_ring].flag &= ~LPFC_STOP_IOCB_EVENT; + psli->ring[psli->fcp_ring].flag &= ~LPFC_STOP_IOCB_EVENT; + psli->ring[psli->next_ring].flag &= ~LPFC_STOP_IOCB_EVENT; + /* Check for error */ + if ((mb->mbxStatus) && (mb->mbxStatus != 0x1601)) { + /* CLEAR_LA mbox error state */ + lpfc_printf_log(phba, KERN_ERR, LOG_MBOX, + "%d:0320 CLEAR_LA mbxStatus error x%x hba " + "state x%x\n", + phba->brd_no, mb->mbxStatus, phba->hba_state); + + phba->hba_state = LPFC_HBA_ERROR; + goto out; + } + + if(phba->fc_flag & FC_ABORT_DISCOVERY) + goto out; + + phba->num_disc_nodes = 0; + /* go thru NPR list and issue ELS PLOGIs */ + if (phba->fc_npr_cnt) { + lpfc_els_disc_plogi(phba); + } + + phba->hba_state = LPFC_HBA_READY; + +out: + phba->fc_flag &= ~FC_ABORT_DISCOVERY; + /* Device Discovery completes */ + lpfc_printf_log(phba, + KERN_INFO, + LOG_DISCOVERY, + "%d:0225 Device Discovery completes\n", + phba->brd_no); + + mempool_free( pmb, phba->mbox_mem_pool); + if (phba->fc_flag & FC_ESTABLISH_LINK) { + phba->fc_flag &= ~FC_ESTABLISH_LINK; + } + del_timer_sync(&phba->fc_estabtmo); + lpfc_can_disctmo(phba); + + /* turn on Link Attention interrupts */ + psli->sliinit.sli_flag |= LPFC_PROCESS_LA; + control = readl(phba->HCregaddr); + control |= HC_LAINT_ENA; + writel(control, phba->HCregaddr); + readl(phba->HCregaddr); /* flush */ + + return; +} + +static void +lpfc_mbx_cmpl_config_link(struct lpfc_hba * phba, LPFC_MBOXQ_t * pmb) +{ + struct lpfc_sli *psli; + MAILBOX_t *mb; + + psli = &phba->sli; + mb = &pmb->mb; + /* Check for error */ + if (mb->mbxStatus) { + /* CONFIG_LINK mbox error state */ + lpfc_printf_log(phba, KERN_ERR, LOG_MBOX, + "%d:0306 CONFIG_LINK mbxStatus error x%x " + "HBA state x%x\n", + phba->brd_no, mb->mbxStatus, phba->hba_state); + + lpfc_linkdown(phba); + phba->hba_state = LPFC_HBA_ERROR; + goto out; + } + + if (phba->hba_state == LPFC_LOCAL_CFG_LINK) { + if (phba->fc_topology == TOPOLOGY_LOOP) { + /* If we are public loop and L bit was set */ + if ((phba->fc_flag & FC_PUBLIC_LOOP) && + !(phba->fc_flag & FC_LBIT)) { + /* Need to wait for FAN - use discovery timer + * for timeout. hba_state is identically + * LPFC_LOCAL_CFG_LINK while waiting for FAN + */ + lpfc_set_disctmo(phba); + mempool_free( pmb, phba->mbox_mem_pool); + return; + } + } + + /* Start discovery by sending a FLOGI hba_state is identically + * LPFC_FLOGI while waiting for FLOGI cmpl + */ + phba->hba_state = LPFC_FLOGI; + lpfc_set_disctmo(phba); + lpfc_initial_flogi(phba); + mempool_free( pmb, phba->mbox_mem_pool); + return; + } + if (phba->hba_state == LPFC_FABRIC_CFG_LINK) { + mempool_free( pmb, phba->mbox_mem_pool); + return; + } + +out: + /* CONFIG_LINK bad hba state */ + lpfc_printf_log(phba, + KERN_ERR, + LOG_DISCOVERY, + "%d:0200 CONFIG_LINK bad hba state x%x\n", + phba->brd_no, phba->hba_state); + + if (phba->hba_state != LPFC_CLEAR_LA) { + lpfc_clear_la(phba, pmb); + pmb->mbox_cmpl = lpfc_mbx_cmpl_clear_la; + if (lpfc_sli_issue_mbox(phba, pmb, (MBX_NOWAIT | MBX_STOP_IOCB)) + == MBX_NOT_FINISHED) { + mempool_free( pmb, phba->mbox_mem_pool); + lpfc_disc_flush_list(phba); + psli->ring[(psli->ip_ring)].flag &= + ~LPFC_STOP_IOCB_EVENT; + psli->ring[(psli->fcp_ring)].flag &= + ~LPFC_STOP_IOCB_EVENT; + psli->ring[(psli->next_ring)].flag &= + ~LPFC_STOP_IOCB_EVENT; + phba->hba_state = LPFC_HBA_READY; + } + } else { + mempool_free( pmb, phba->mbox_mem_pool); + } + return; +} + +static void +lpfc_mbx_cmpl_read_sparam(struct lpfc_hba * phba, LPFC_MBOXQ_t * pmb) +{ + struct lpfc_sli *psli = &phba->sli; + MAILBOX_t *mb = &pmb->mb; + struct lpfc_dmabuf *mp = (struct lpfc_dmabuf *) pmb->context1; + + + /* Check for error */ + if (mb->mbxStatus) { + /* READ_SPARAM mbox error state */ + lpfc_printf_log(phba, KERN_ERR, LOG_MBOX, + "%d:0319 READ_SPARAM mbxStatus error x%x " + "hba state x%x>\n", + phba->brd_no, mb->mbxStatus, phba->hba_state); + + lpfc_linkdown(phba); + phba->hba_state = LPFC_HBA_ERROR; + goto out; + } + + /* The mailbox was populated by the HBA. Flush it to main store for the + * driver. Note that all context buffers are from the driver's + * dma pool and have length LPFC_BPL_SIZE. + */ + pci_dma_sync_single_for_cpu(phba->pcidev, mp->phys, LPFC_BPL_SIZE, + PCI_DMA_FROMDEVICE); + + memcpy((uint8_t *) & phba->fc_sparam, (uint8_t *) mp->virt, + sizeof (struct serv_parm)); + memcpy((uint8_t *) & phba->fc_nodename, + (uint8_t *) & phba->fc_sparam.nodeName, + sizeof (struct lpfc_name)); + memcpy((uint8_t *) & phba->fc_portname, + (uint8_t *) & phba->fc_sparam.portName, + sizeof (struct lpfc_name)); + lpfc_mbuf_free(phba, mp->virt, mp->phys); + kfree(mp); + mempool_free( pmb, phba->mbox_mem_pool); + return; + +out: + pmb->context1 = NULL; + lpfc_mbuf_free(phba, mp->virt, mp->phys); + kfree(mp); + if (phba->hba_state != LPFC_CLEAR_LA) { + lpfc_clear_la(phba, pmb); + pmb->mbox_cmpl = lpfc_mbx_cmpl_clear_la; + if (lpfc_sli_issue_mbox(phba, pmb, (MBX_NOWAIT | MBX_STOP_IOCB)) + == MBX_NOT_FINISHED) { + mempool_free( pmb, phba->mbox_mem_pool); + lpfc_disc_flush_list(phba); + psli->ring[(psli->ip_ring)].flag &= + ~LPFC_STOP_IOCB_EVENT; + psli->ring[(psli->fcp_ring)].flag &= + ~LPFC_STOP_IOCB_EVENT; + psli->ring[(psli->next_ring)].flag &= + ~LPFC_STOP_IOCB_EVENT; + phba->hba_state = LPFC_HBA_READY; + } + } else { + mempool_free( pmb, phba->mbox_mem_pool); + } + return; +} + +/* + * This routine handles processing a READ_LA mailbox + * command upon completion. It is setup in the LPFC_MBOXQ + * as the completion routine when the command is + * handed off to the SLI layer. + */ +void +lpfc_mbx_cmpl_read_la(struct lpfc_hba * phba, LPFC_MBOXQ_t * pmb) +{ + struct lpfc_sli *psli = &phba->sli; + READ_LA_VAR *la; + LPFC_MBOXQ_t *mbox; + MAILBOX_t *mb = &pmb->mb; + struct lpfc_dmabuf *mp = (struct lpfc_dmabuf *) (pmb->context1); + uint32_t control; + int i; + + /* Check for error */ + if (mb->mbxStatus) { + /* READ_LA mbox error state */ + lpfc_printf_log(phba, + KERN_INFO, + LOG_LINK_EVENT, + "%d:1307 READ_LA mbox error x%x state x%x\n", + phba->brd_no, + mb->mbxStatus, phba->hba_state); + + lpfc_linkdown(phba); + phba->hba_state = LPFC_HBA_ERROR; + + /* turn on Link Attention interrupts */ + psli->sliinit.sli_flag |= LPFC_PROCESS_LA; + control = readl(phba->HCregaddr); + control |= HC_LAINT_ENA; + writel(control, phba->HCregaddr); + readl(phba->HCregaddr); /* flush */ + return; + } + la = (READ_LA_VAR *) & pmb->mb.un.varReadLA; + + + /* The mailbox was populated by the HBA. Flush it to main store for the + * driver. Note that all context buffers are from the driver's + * dma pool and have length LPFC_BPL_SIZE. + */ + pci_dma_sync_single_for_cpu(phba->pcidev, mp->phys, LPFC_BPL_SIZE, + PCI_DMA_FROMDEVICE); + + /* Get Loop Map information */ + if (mp) { + memcpy(&phba->alpa_map[0], mp->virt, 128); + } else { + memset(&phba->alpa_map[0], 0, 128); + } + + if (((phba->fc_eventTag + 1) < la->eventTag) || + (phba->fc_eventTag == la->eventTag)) { + phba->fc_stat.LinkMultiEvent++; + if (la->attType == AT_LINK_UP) { + if (phba->fc_eventTag != 0) { + + lpfc_linkdown(phba); + } + } + } + + phba->fc_eventTag = la->eventTag; + + if (la->attType == AT_LINK_UP) { + phba->fc_stat.LinkUp++; + /* Link Up Event received */ + lpfc_printf_log(phba, KERN_ERR, LOG_LINK_EVENT, + "%d:1303 Link Up Event x%x received " + "Data: x%x x%x x%x x%x\n", + phba->brd_no, la->eventTag, phba->fc_eventTag, + la->granted_AL_PA, la->UlnkSpeed, + phba->alpa_map[0]); + + switch(la->UlnkSpeed) { + case LA_1GHZ_LINK: + phba->fc_linkspeed = LA_1GHZ_LINK; + break; + case LA_2GHZ_LINK: + phba->fc_linkspeed = LA_2GHZ_LINK; + break; + case LA_4GHZ_LINK: + phba->fc_linkspeed = LA_4GHZ_LINK; + break; + default: + phba->fc_linkspeed = LA_UNKNW_LINK; + break; + } + + if ((phba->fc_topology = la->topology) == TOPOLOGY_LOOP) { + + if (la->il) { + phba->fc_flag |= FC_LBIT; + } + + phba->fc_myDID = la->granted_AL_PA; + + i = la->un.lilpBde64.tus.f.bdeSize; + if (i == 0) { + phba->alpa_map[0] = 0; + } else { + if (phba->cfg_log_verbose + & LOG_LINK_EVENT) { + int numalpa, j, k; + union { + uint8_t pamap[16]; + struct { + uint32_t wd1; + uint32_t wd2; + uint32_t wd3; + uint32_t wd4; + } pa; + } un; + + numalpa = phba->alpa_map[0]; + j = 0; + while (j < numalpa) { + memset(un.pamap, 0, 16); + for (k = 1; j < numalpa; k++) { + un.pamap[k - 1] = + phba->alpa_map[j + + 1]; + j++; + if (k == 16) + break; + } + /* Link Up Event ALPA map */ + lpfc_printf_log(phba, + KERN_WARNING, + LOG_LINK_EVENT, + "%d:1304 Link Up Event " + "ALPA map Data: x%x " + "x%x x%x x%x\n", + phba->brd_no, + un.pa.wd1, un.pa.wd2, + un.pa.wd3, un.pa.wd4); + } + } + } + } else { + phba->fc_myDID = phba->fc_pref_DID; + phba->fc_flag |= FC_LBIT; + } + + lpfc_linkup(phba); + if ((mbox = mempool_alloc(phba->mbox_mem_pool, GFP_ATOMIC))) { + lpfc_read_sparam(phba, mbox); + mbox->mbox_cmpl = lpfc_mbx_cmpl_read_sparam; + lpfc_sli_issue_mbox + (phba, mbox, (MBX_NOWAIT | MBX_STOP_IOCB)); + } + + if ((mbox = mempool_alloc(phba->mbox_mem_pool, GFP_ATOMIC))) { + phba->hba_state = LPFC_LOCAL_CFG_LINK; + lpfc_config_link(phba, mbox); + mbox->mbox_cmpl = lpfc_mbx_cmpl_config_link; + lpfc_sli_issue_mbox + (phba, mbox, (MBX_NOWAIT | MBX_STOP_IOCB)); + } + } else { + phba->fc_stat.LinkDown++; + /* Link Down Event received */ + lpfc_printf_log(phba, KERN_ERR, LOG_LINK_EVENT, + "%d:1305 Link Down Event x%x received " + "Data: x%x x%x x%x\n", + phba->brd_no, la->eventTag, phba->fc_eventTag, + phba->hba_state, phba->fc_flag); + + lpfc_linkdown(phba); + + /* turn on Link Attention interrupts - no CLEAR_LA needed */ + psli->sliinit.sli_flag |= LPFC_PROCESS_LA; + control = readl(phba->HCregaddr); + control |= HC_LAINT_ENA; + writel(control, phba->HCregaddr); + readl(phba->HCregaddr); /* flush */ + } + + pmb->context1 = NULL; + lpfc_mbuf_free(phba, mp->virt, mp->phys); + kfree(mp); + mempool_free( pmb, phba->mbox_mem_pool); + return; +} + +/* + * This routine handles processing a REG_LOGIN mailbox + * command upon completion. It is setup in the LPFC_MBOXQ + * as the completion routine when the command is + * handed off to the SLI layer. + */ +void +lpfc_mbx_cmpl_reg_login(struct lpfc_hba * phba, LPFC_MBOXQ_t * pmb) +{ + struct lpfc_sli *psli; + MAILBOX_t *mb; + struct lpfc_dmabuf *mp; + struct lpfc_nodelist *ndlp; + + psli = &phba->sli; + mb = &pmb->mb; + + ndlp = (struct lpfc_nodelist *) pmb->context2; + mp = (struct lpfc_dmabuf *) (pmb->context1); + + /* The mailbox was populated by the HBA. Flush it to main store for the + * driver. Note that all context buffers are from the driver's + * dma pool and have length LPFC_BPL_SIZE. + */ + pci_dma_sync_single_for_cpu(phba->pcidev, mp->phys, LPFC_BPL_SIZE, + PCI_DMA_FROMDEVICE); + + pmb->context1 = NULL; + + /* Good status, call state machine */ + lpfc_disc_state_machine(phba, ndlp, pmb, NLP_EVT_CMPL_REG_LOGIN); + lpfc_mbuf_free(phba, mp->virt, mp->phys); + kfree(mp); + mempool_free( pmb, phba->mbox_mem_pool); + + return; +} + +/* + * This routine handles processing a Fabric REG_LOGIN mailbox + * command upon completion. It is setup in the LPFC_MBOXQ + * as the completion routine when the command is + * handed off to the SLI layer. + */ +void +lpfc_mbx_cmpl_fabric_reg_login(struct lpfc_hba * phba, LPFC_MBOXQ_t * pmb) +{ + struct lpfc_sli *psli; + MAILBOX_t *mb; + struct lpfc_dmabuf *mp; + struct lpfc_nodelist *ndlp; + struct lpfc_nodelist *ndlp_fdmi; + + + psli = &phba->sli; + mb = &pmb->mb; + + ndlp = (struct lpfc_nodelist *) pmb->context2; + mp = (struct lpfc_dmabuf *) (pmb->context1); + + if (mb->mbxStatus) { + lpfc_mbuf_free(phba, mp->virt, mp->phys); + kfree(mp); + mempool_free( pmb, phba->mbox_mem_pool); + mempool_free( ndlp, phba->nlp_mem_pool); + + /* FLOGI failed, so just use loop map to make discovery list */ + lpfc_disc_list_loopmap(phba); + + /* Start discovery */ + lpfc_disc_start(phba); + return; + } + + /* The mailbox was populated by the HBA. Flush it to main store for the + * driver. Note that all context buffers are from the driver's + * dma pool and have length LPFC_BPL_SIZE. + */ + pci_dma_sync_single_for_cpu(phba->pcidev, mp->phys, LPFC_BPL_SIZE, + PCI_DMA_FROMDEVICE); + + pmb->context1 = NULL; + + if (ndlp->nlp_rpi != 0) + lpfc_findnode_remove_rpi(phba, ndlp->nlp_rpi); + ndlp->nlp_rpi = mb->un.varWords[0]; + lpfc_addnode_rpi(phba, ndlp, ndlp->nlp_rpi); + ndlp->nlp_type |= NLP_FABRIC; + ndlp->nlp_state = NLP_STE_UNMAPPED_NODE; + lpfc_nlp_list(phba, ndlp, NLP_UNMAPPED_LIST); + + if (phba->hba_state == LPFC_FABRIC_CFG_LINK) { + /* This NPort has been assigned an NPort_ID by the fabric as a + * result of the completed fabric login. Issue a State Change + * Registration (SCR) ELS request to the fabric controller + * (SCR_DID) so that this NPort gets RSCN events from the + * fabric. + */ + lpfc_issue_els_scr(phba, SCR_DID, 0); + + /* Allocate a new node instance. If the pool is empty, just + * start the discovery process and skip the Nameserver login + * process. This is attempted again later on. Otherwise, issue + * a Port Login (PLOGI) to the NameServer + */ + if ((ndlp = mempool_alloc(phba->nlp_mem_pool, GFP_ATOMIC)) + == 0) { + lpfc_disc_start(phba); + } else { + lpfc_nlp_init(phba, ndlp, NameServer_DID); + ndlp->nlp_type |= NLP_FABRIC; + ndlp->nlp_state = NLP_STE_PLOGI_ISSUE; + lpfc_nlp_list(phba, ndlp, NLP_PLOGI_LIST); + lpfc_issue_els_plogi(phba, ndlp, 0); + if (phba->cfg_fdmi_on) { + if ((ndlp_fdmi = mempool_alloc( + phba->nlp_mem_pool, + GFP_ATOMIC))) { + lpfc_nlp_init(phba, ndlp_fdmi, + FDMI_DID); + ndlp_fdmi->nlp_type |= NLP_FABRIC; + ndlp_fdmi->nlp_state = + NLP_STE_PLOGI_ISSUE; + lpfc_issue_els_plogi(phba, ndlp_fdmi, + 0); + } + } + } + } + + lpfc_mbuf_free(phba, mp->virt, mp->phys); + kfree(mp); + mempool_free( pmb, phba->mbox_mem_pool); + + return; +} + +/* + * This routine handles processing a NameServer REG_LOGIN mailbox + * command upon completion. It is setup in the LPFC_MBOXQ + * as the completion routine when the command is + * handed off to the SLI layer. + */ +void +lpfc_mbx_cmpl_ns_reg_login(struct lpfc_hba * phba, LPFC_MBOXQ_t * pmb) +{ + struct lpfc_sli *psli; + MAILBOX_t *mb; + struct lpfc_dmabuf *mp; + struct lpfc_nodelist *ndlp; + + psli = &phba->sli; + mb = &pmb->mb; + + ndlp = (struct lpfc_nodelist *) pmb->context2; + mp = (struct lpfc_dmabuf *) (pmb->context1); + + if (mb->mbxStatus) { + lpfc_mbuf_free(phba, mp->virt, mp->phys); + kfree(mp); + mempool_free( pmb, phba->mbox_mem_pool); + lpfc_nlp_list(phba, ndlp, NLP_NO_LIST); + + /* RegLogin failed, so just use loop map to make discovery + list */ + lpfc_disc_list_loopmap(phba); + + /* Start discovery */ + lpfc_disc_start(phba); + return; + } + + /* The mailbox was populated by the HBA. Flush it to main store for the + * driver. Note that all context buffers are from the driver's + * dma pool and have length LPFC_BPL_SIZE. + */ + pci_dma_sync_single_for_cpu(phba->pcidev, mp->phys, LPFC_BPL_SIZE, + PCI_DMA_FROMDEVICE); + + pmb->context1 = NULL; + + if (ndlp->nlp_rpi != 0) + lpfc_findnode_remove_rpi(phba, ndlp->nlp_rpi); + ndlp->nlp_rpi = mb->un.varWords[0]; + lpfc_addnode_rpi(phba, ndlp, ndlp->nlp_rpi); + ndlp->nlp_type |= NLP_FABRIC; + ndlp->nlp_state = NLP_STE_UNMAPPED_NODE; + lpfc_nlp_list(phba, ndlp, NLP_UNMAPPED_LIST); + + if (phba->hba_state < LPFC_HBA_READY) { + /* Link up discovery requires Fabrib registration. */ + lpfc_ns_cmd(phba, ndlp, SLI_CTNS_RNN_ID); + lpfc_ns_cmd(phba, ndlp, SLI_CTNS_RSNN_NN); + lpfc_ns_cmd(phba, ndlp, SLI_CTNS_RFT_ID); + } + + phba->fc_ns_retry = 0; + /* Good status, issue CT Request to NameServer */ + if (lpfc_ns_cmd(phba, ndlp, SLI_CTNS_GID_FT)) { + /* Cannot issue NameServer Query, so finish up discovery */ + lpfc_disc_start(phba); + } + + lpfc_mbuf_free(phba, mp->virt, mp->phys); + kfree(mp); + mempool_free( pmb, phba->mbox_mem_pool); + + return; +} + +/* Put blp on the bind list */ +int +lpfc_consistent_bind_save(struct lpfc_hba * phba, struct lpfc_bindlist * blp) +{ + /* Put it at the end of the bind list */ + list_add_tail(&blp->nlp_listp, &phba->fc_nlpbind_list); + phba->fc_bind_cnt++; + + /* Add scsiid to BIND list */ + lpfc_printf_log(phba, KERN_INFO, LOG_NODE, + "%d:0903 Add scsiid %d to BIND list " + "Data: x%x x%x x%x x%p\n", + phba->brd_no, blp->nlp_sid, phba->fc_bind_cnt, + blp->nlp_DID, blp->nlp_bind_type, blp); + + return (0); +} + +int +lpfc_nlp_list(struct lpfc_hba * phba, struct lpfc_nodelist * nlp, int list) +{ + struct lpfc_bindlist *blp; + struct lpfc_target *targetp; + struct lpfc_sli *psli; + psli = &phba->sli; + + /* Sanity check to ensure we are not moving to / from the same list */ + if((nlp->nlp_flag & NLP_LIST_MASK) == list) { + if(list != NLP_NO_LIST) + return(0); + } + + blp = nlp->nlp_listp_bind; + + switch(nlp->nlp_flag & NLP_LIST_MASK) { + case NLP_NO_LIST: /* Not on any list */ + break; + case NLP_UNUSED_LIST: + phba->fc_unused_cnt--; + list_del(&nlp->nlp_listp); + nlp->nlp_flag &= ~NLP_LIST_MASK; + break; + case NLP_PLOGI_LIST: + phba->fc_plogi_cnt--; + list_del(&nlp->nlp_listp); + nlp->nlp_flag &= ~NLP_LIST_MASK; + break; + case NLP_ADISC_LIST: + phba->fc_adisc_cnt--; + list_del(&nlp->nlp_listp); + nlp->nlp_flag &= ~NLP_LIST_MASK; + break; + case NLP_REGLOGIN_LIST: + phba->fc_reglogin_cnt--; + list_del(&nlp->nlp_listp); + nlp->nlp_flag &= ~NLP_LIST_MASK; + break; + case NLP_PRLI_LIST: + phba->fc_prli_cnt--; + list_del(&nlp->nlp_listp); + nlp->nlp_flag &= ~NLP_LIST_MASK; + break; + case NLP_UNMAPPED_LIST: + phba->fc_unmap_cnt--; + list_del(&nlp->nlp_listp); + nlp->nlp_flag &= ~NLP_LIST_MASK; + nlp->nlp_flag &= ~NLP_TGT_NO_SCSIID; + nlp->nlp_type &= ~NLP_FC_NODE; + phba->nport_event_cnt++; + break; + case NLP_MAPPED_LIST: + phba->fc_map_cnt--; + list_del(&nlp->nlp_listp); + nlp->nlp_flag &= ~NLP_LIST_MASK; + phba->nport_event_cnt++; + lpfc_set_failmask(phba, nlp, LPFC_DEV_DISAPPEARED, + LPFC_SET_BITMASK); + nlp->nlp_type &= ~NLP_FCP_TARGET; + targetp = nlp->nlp_Target; + if (targetp && (list != NLP_MAPPED_LIST)) { + nlp->nlp_Target = NULL; +#if defined(FC_TRANS_VER1) || defined(FC_TRANS_265_BLKPATCH) + /* + * Do not block the target if the driver has just reset + * its interface to the hardware. + */ + if (phba->hba_state != LPFC_INIT_START) + lpfc_target_block(phba, targetp); +#endif + } + + break; + case NLP_NPR_LIST: + phba->fc_npr_cnt--; + list_del(&nlp->nlp_listp); + nlp->nlp_flag &= ~NLP_LIST_MASK; + /* Stop delay tmo if taking node off NPR list */ + if ((nlp->nlp_flag & NLP_DELAY_TMO) && + (list != NLP_NPR_LIST)) { + nlp->nlp_flag &= ~NLP_DELAY_TMO; + del_timer_sync(&nlp->nlp_delayfunc); + } + break; + } + + /* Add NPort to list */ + lpfc_printf_log(phba, + KERN_INFO, + LOG_NODE, + "%d:0904 Add NPort x%x to %d list Data: x%x x%p\n", + phba->brd_no, + nlp->nlp_DID, list, nlp->nlp_flag, blp); + + nlp->nlp_listp_bind = NULL; + + switch(list) { + case NLP_NO_LIST: /* No list, just remove it */ + lpfc_nlp_remove(phba, nlp); + break; + case NLP_UNUSED_LIST: + nlp->nlp_flag |= list; + /* Put it at the end of the unused list */ + list_add_tail(&nlp->nlp_listp, &phba->fc_unused_list); + phba->fc_unused_cnt++; + break; + case NLP_PLOGI_LIST: + nlp->nlp_flag |= list; + /* Put it at the end of the plogi list */ + list_add_tail(&nlp->nlp_listp, &phba->fc_plogi_list); + phba->fc_plogi_cnt++; + break; + case NLP_ADISC_LIST: + nlp->nlp_flag |= list; + /* Put it at the end of the adisc list */ + list_add_tail(&nlp->nlp_listp, &phba->fc_adisc_list); + phba->fc_adisc_cnt++; + break; + case NLP_REGLOGIN_LIST: + nlp->nlp_flag |= list; + /* Put it at the end of the reglogin list */ + list_add_tail(&nlp->nlp_listp, &phba->fc_reglogin_list); + phba->fc_reglogin_cnt++; + break; + case NLP_PRLI_LIST: + nlp->nlp_flag |= list; + /* Put it at the end of the prli list */ + list_add_tail(&nlp->nlp_listp, &phba->fc_prli_list); + phba->fc_prli_cnt++; + break; + case NLP_UNMAPPED_LIST: + nlp->nlp_flag |= list; + /* Put it at the end of the unmap list */ + list_add_tail(&nlp->nlp_listp, &phba->fc_nlpunmap_list); + phba->fc_unmap_cnt++; + phba->nport_event_cnt++; + /* stop nodev tmo if running */ + if (nlp->nlp_flag & NLP_NODEV_TMO) { + del_timer_sync(&nlp->nlp_tmofunc); + } + nlp->nlp_flag &= ~NLP_NODEV_TMO; + nlp->nlp_type |= NLP_FC_NODE; + lpfc_set_failmask(phba, nlp, LPFC_DEV_DISCOVERY_INP, + LPFC_CLR_BITMASK); + break; + case NLP_MAPPED_LIST: + nlp->nlp_flag |= list; + /* Put it at the end of the map list */ + list_add_tail(&nlp->nlp_listp, &phba->fc_nlpmap_list); + phba->fc_map_cnt++; + phba->nport_event_cnt++; + /* stop nodev tmo if running */ + if (nlp->nlp_flag & NLP_NODEV_TMO) { + del_timer_sync(&nlp->nlp_tmofunc); + } + nlp->nlp_flag &= ~NLP_NODEV_TMO; + nlp->nlp_type |= NLP_FCP_TARGET; + lpfc_set_failmask(phba, nlp, LPFC_DEV_DISAPPEARED, + LPFC_CLR_BITMASK); + lpfc_set_failmask(phba, nlp, LPFC_DEV_DISCOVERY_INP, + LPFC_CLR_BITMASK); + + targetp = phba->device_queue_hash[nlp->nlp_sid]; + if (targetp && targetp->pnode) { + nlp->nlp_Target = targetp; +#if defined(FC_TRANS_VER1) || defined(FC_TRANS_265_BLKPATCH) + /* Unblock I/Os on target */ + if(targetp->blocked) + lpfc_target_unblock(phba, targetp); +#endif + } + break; + case NLP_NPR_LIST: + nlp->nlp_flag |= list; + /* Put it at the end of the npr list */ + list_add_tail(&nlp->nlp_listp, &phba->fc_npr_list); + phba->fc_npr_cnt++; + + /* + * Sanity check for Fabric entity. + * Set nodev_tmo for NPR state, for Fabric use 1 sec. + */ + if (nlp->nlp_type & NLP_FABRIC) { + mod_timer(&nlp->nlp_tmofunc, jiffies + HZ); + } + else { + mod_timer(&nlp->nlp_tmofunc, + jiffies + HZ * phba->cfg_nodev_tmo); + } + nlp->nlp_flag |= NLP_NODEV_TMO; + nlp->nlp_flag &= ~NLP_RCV_PLOGI; + break; + case NLP_JUST_DQ: + break; + } + + if (blp) { + nlp->nlp_sid = 0; + nlp->nlp_flag &= ~NLP_SEED_MASK; + nlp->nlp_Target = NULL; + lpfc_consistent_bind_save(phba, blp); + } + return (0); +} + +/* + * Start / ReStart rescue timer for Discovery / RSCN handling + */ +void +lpfc_set_disctmo(struct lpfc_hba * phba) +{ + uint32_t tmo; + + tmo = ((phba->fc_ratov * 2) + 1); + + mod_timer(&phba->fc_disctmo, jiffies + HZ * tmo); + phba->fc_flag |= FC_DISC_TMO; + + /* Start Discovery Timer state */ + lpfc_printf_log(phba, KERN_INFO, LOG_DISCOVERY, + "%d:0247 Start Discovery Timer state x%x " + "Data: x%x x%lx x%x x%x\n", + phba->brd_no, + phba->hba_state, tmo, (unsigned long)&phba->fc_disctmo, + phba->fc_plogi_cnt, phba->fc_adisc_cnt); + + return; +} + +/* + * Cancel rescue timer for Discovery / RSCN handling + */ +int +lpfc_can_disctmo(struct lpfc_hba * phba) +{ + /* Turn off discovery timer if its running */ + if(phba->fc_flag & FC_DISC_TMO) { + del_timer_sync(&phba->fc_disctmo); + } + phba->fc_flag &= ~FC_DISC_TMO; + + /* Cancel Discovery Timer state */ + lpfc_printf_log(phba, KERN_INFO, LOG_DISCOVERY, + "%d:0248 Cancel Discovery Timer state x%x " + "Data: x%x x%x x%x\n", + phba->brd_no, phba->hba_state, phba->fc_flag, + phba->fc_plogi_cnt, phba->fc_adisc_cnt); + + return (0); +} + +/* + * Check specified ring for outstanding IOCB on the SLI queue + * Return true if iocb matches the specified nport + */ +int +lpfc_check_sli_ndlp(struct lpfc_hba * phba, + struct lpfc_sli_ring * pring, + struct lpfc_iocbq * iocb, struct lpfc_nodelist * ndlp) +{ + struct lpfc_sli *psli; + IOCB_t *icmd; + + psli = &phba->sli; + icmd = &iocb->iocb; + if (pring->ringno == LPFC_ELS_RING) { + switch (icmd->ulpCommand) { + case CMD_GEN_REQUEST64_CR: + if (icmd->ulpContext == (volatile ushort)ndlp->nlp_rpi) + return (1); + case CMD_ELS_REQUEST64_CR: + case CMD_XMIT_ELS_RSP64_CX: + if (iocb->context1 == (uint8_t *) ndlp) + return (1); + } + } else if (pring->ringno == psli->ip_ring) { + + } else if (pring->ringno == psli->fcp_ring) { + /* Skip match check if waiting to relogin to FCP target */ + if ((ndlp->nlp_type & NLP_FCP_TARGET) && + (ndlp->nlp_flag & NLP_DELAY_TMO)) { + return (0); + } + if (icmd->ulpContext == (volatile ushort)ndlp->nlp_rpi) { + return (1); + } + } else if (pring->ringno == psli->next_ring) { + + } + return (0); +} + +/* + * Free resources / clean up outstanding I/Os + * associated with nlp_rpi in the LPFC_NODELIST entry. + */ +static int +lpfc_no_rpi(struct lpfc_hba * phba, struct lpfc_nodelist * ndlp) +{ + struct lpfc_sli *psli; + struct lpfc_sli_ring *pring; + struct lpfc_iocbq *iocb, *next_iocb; + IOCB_t *icmd; + uint32_t rpi, i; + + psli = &phba->sli; + rpi = ndlp->nlp_rpi; + if (rpi) { + /* Now process each ring */ + for (i = 0; i < psli->sliinit.num_rings; i++) { + pring = &psli->ring[i]; + + list_for_each_entry_safe(iocb, next_iocb, &pring->txq, + list) { + /* + * Check to see if iocb matches the nport we are + * looking for + */ + if ((lpfc_check_sli_ndlp + (phba, pring, iocb, ndlp))) { + /* It matches, so deque and call compl + with an error */ + list_del(&iocb->list); + pring->txq_cnt--; + if (iocb->iocb_cmpl) { + icmd = &iocb->iocb; + icmd->ulpStatus = + IOSTAT_LOCAL_REJECT; + icmd->un.ulpWord[4] = + IOERR_SLI_ABORTED; + (iocb->iocb_cmpl) (phba, + iocb, iocb); + } else { + mempool_free(iocb, + phba->iocb_mem_pool); + } + } + } + /* Everything that matches on txcmplq will be returned + * by firmware with a no rpi error. + */ + } + } + return (0); +} + +/* + * Free rpi associated with LPFC_NODELIST entry. + * This routine is called from lpfc_freenode(), when we are removing + * a LPFC_NODELIST entry. It is also called if the driver initiates a + * LOGO that completes successfully, and we are waiting to PLOGI back + * to the remote NPort. In addition, it is called after we receive + * and unsolicated ELS cmd, send back a rsp, the rsp completes and + * we are waiting to PLOGI back to the remote NPort. + */ +int +lpfc_unreg_rpi(struct lpfc_hba * phba, struct lpfc_nodelist * ndlp) +{ + LPFC_MBOXQ_t *mbox; + + if (ndlp->nlp_rpi) { + if ((mbox = mempool_alloc(phba->mbox_mem_pool, GFP_ATOMIC))) { + lpfc_unreg_login(phba, ndlp->nlp_rpi, mbox); + if (lpfc_sli_issue_mbox + (phba, mbox, (MBX_NOWAIT | MBX_STOP_IOCB)) + == MBX_NOT_FINISHED) { + mempool_free( mbox, phba->mbox_mem_pool); + } + } + lpfc_findnode_remove_rpi(phba, ndlp->nlp_rpi); + lpfc_no_rpi(phba, ndlp); + ndlp->nlp_rpi = 0; + lpfc_set_failmask(phba, ndlp, LPFC_DEV_DISCONNECTED, + LPFC_SET_BITMASK); + return 1; + } + return 0; +} + +/* + * Free resources associated with LPFC_NODELIST entry + * so it can be freed. + */ +static int +lpfc_freenode(struct lpfc_hba * phba, struct lpfc_nodelist * ndlp) +{ + struct lpfc_target *targetp; + struct lpfc_sli *psli; + int scsid; + + /* The psli variable gets rid of the long pointer deference. */ + psli = &phba->sli; + + /* Cleanup node for NPort */ + lpfc_printf_log(phba, KERN_INFO, LOG_NODE, + "%d:0900 Cleanup node for NPort x%x " + "Data: x%x x%x x%x\n", + phba->brd_no, ndlp->nlp_DID, ndlp->nlp_flag, + ndlp->nlp_state, ndlp->nlp_rpi); + + lpfc_nlp_list(phba, ndlp, NLP_JUST_DQ); + + if(ndlp->nlp_flag & NLP_NODEV_TMO) { + del_timer_sync(&ndlp->nlp_tmofunc); + } + ndlp->nlp_flag &= ~NLP_NODEV_TMO; + + if(ndlp->nlp_flag & NLP_DELAY_TMO) { + del_timer_sync(&ndlp->nlp_delayfunc); + } + ndlp->nlp_flag &= ~NLP_DELAY_TMO; + + lpfc_unreg_rpi(phba, ndlp); + + for(scsid=0;scsiddevice_queue_hash[scsid]; + /* First see if the SCSI ID has an allocated struct + lpfc_target */ + if (targetp) { + if (targetp->pnode == ndlp) { + targetp->pnode = NULL; + ndlp->nlp_Target = NULL; +#ifdef FC_TRANS_VER1 + /* + * This code does not apply to SLES9 since there + * is no starget defined in the midlayer. + * Additionally, dynamic target discovery to the + * midlayer is not supported yet. + */ + if (targetp->starget) { + /* Remove SCSI target / SCSI Hotplug */ + lpfc_target_remove(phba, targetp); + } +#endif + break; + } + } + } + return (0); +} + +/* + * Check to see if we can free the nlp back to the freelist. + * If we are in the middle of using the nlp in the discovery state + * machine, defer the free till we reach the end of the state machine. + */ +int +lpfc_nlp_remove(struct lpfc_hba * phba, struct lpfc_nodelist * ndlp) +{ + if(ndlp->nlp_flag & NLP_NODEV_TMO) { + del_timer_sync(&ndlp->nlp_tmofunc); + } + ndlp->nlp_flag &= ~NLP_NODEV_TMO; + + if(ndlp->nlp_flag & NLP_DELAY_TMO) { + del_timer_sync(&ndlp->nlp_delayfunc); + } + ndlp->nlp_flag &= ~NLP_DELAY_TMO; + + if (ndlp->nlp_disc_refcnt) { + ndlp->nlp_flag |= NLP_DELAY_REMOVE; + } + else { + lpfc_freenode(phba, ndlp); + mempool_free( ndlp, phba->nlp_mem_pool); + } + return(0); +} + +static int +lpfc_matchdid(struct lpfc_hba * phba, struct lpfc_nodelist * ndlp, uint32_t did) +{ + D_ID mydid; + D_ID ndlpdid; + D_ID matchdid; + + if (did == Bcast_DID) + return (0); + + if (ndlp->nlp_DID == 0) { + return (0); + } + + /* First check for Direct match */ + if (ndlp->nlp_DID == did) + return (1); + + /* Next check for area/domain identically equals 0 match */ + mydid.un.word = phba->fc_myDID; + if ((mydid.un.b.domain == 0) && (mydid.un.b.area == 0)) { + return (0); + } + + matchdid.un.word = did; + ndlpdid.un.word = ndlp->nlp_DID; + if (matchdid.un.b.id == ndlpdid.un.b.id) { + if ((mydid.un.b.domain == matchdid.un.b.domain) && + (mydid.un.b.area == matchdid.un.b.area)) { + if ((ndlpdid.un.b.domain == 0) && + (ndlpdid.un.b.area == 0)) { + if (ndlpdid.un.b.id) + return (1); + } + return (0); + } + + matchdid.un.word = ndlp->nlp_DID; + if ((mydid.un.b.domain == ndlpdid.un.b.domain) && + (mydid.un.b.area == ndlpdid.un.b.area)) { + if ((matchdid.un.b.domain == 0) && + (matchdid.un.b.area == 0)) { + if (matchdid.un.b.id) + return (1); + } + } + } + return (0); +} + +/* Search for a nodelist entry on a specific list */ +struct lpfc_nodelist * +lpfc_findnode_wwpn(struct lpfc_hba * phba, uint32_t order, + struct lpfc_name * wwpn) +{ + struct lpfc_nodelist *ndlp, *next_ndlp; + uint32_t data1; + + if (order & NLP_SEARCH_UNMAPPED) { + list_for_each_entry_safe(ndlp, next_ndlp, + &phba->fc_nlpunmap_list, nlp_listp) { + if (memcmp(&ndlp->nlp_portname, wwpn, + sizeof(struct lpfc_name)) == 0) { + + data1 = (((uint32_t) ndlp->nlp_state << 24) | + ((uint32_t) ndlp->nlp_xri << 16) | + ((uint32_t) ndlp->nlp_type << 8) | + ((uint32_t) ndlp->nlp_rpi & 0xff)); + /* FIND node DID unmapped */ + lpfc_printf_log(phba, + KERN_INFO, + LOG_NODE, + "%d:0911 FIND node DID unmapped" + " Data: x%p x%x x%x x%x\n", + phba->brd_no, + ndlp, ndlp->nlp_DID, + ndlp->nlp_flag, data1); + return (ndlp); + } + } + } + + if (order & NLP_SEARCH_MAPPED) { + list_for_each_entry_safe(ndlp, next_ndlp, &phba->fc_nlpmap_list, + nlp_listp) { + if (memcmp(&ndlp->nlp_portname, wwpn, + sizeof(struct lpfc_name)) == 0) { + + data1 = (((uint32_t) ndlp->nlp_state << 24) | + ((uint32_t) ndlp->nlp_xri << 16) | + ((uint32_t) ndlp->nlp_type << 8) | + ((uint32_t) ndlp->nlp_rpi & 0xff)); + /* FIND node DID mapped */ + lpfc_printf_log(phba, KERN_INFO, LOG_NODE, + "%d:0901 FIND node DID mapped " + "Data: x%p x%x x%x x%x\n", + phba->brd_no, + ndlp, ndlp->nlp_DID, + ndlp->nlp_flag, data1); + return (ndlp); + } + } + } + + /* no match found */ + return ((struct lpfc_nodelist *) 0); +} +/* Search for a nodelist entry on a specific list */ +struct lpfc_nodelist * +lpfc_findnode_wwnn(struct lpfc_hba * phba, uint32_t order, + struct lpfc_name * wwnn) +{ + struct lpfc_nodelist *ndlp, *next_ndlp; + uint32_t data1; + + if (order & NLP_SEARCH_UNMAPPED) { + list_for_each_entry_safe(ndlp, next_ndlp, + &phba->fc_nlpunmap_list, nlp_listp) { + if (memcmp(&ndlp->nlp_nodename, wwnn, + sizeof(struct lpfc_name)) == 0) { + + data1 = (((uint32_t) ndlp->nlp_state << 24) | + ((uint32_t) ndlp->nlp_xri << 16) | + ((uint32_t) ndlp->nlp_type << 8) | + ((uint32_t) ndlp->nlp_rpi & 0xff)); + /* FIND node DID unmapped */ + lpfc_printf_log(phba, KERN_INFO, LOG_NODE, + "%d:0910 FIND node DID unmapped" + "Data: x%p x%x x%x x%x\n", + phba->brd_no, + ndlp, ndlp->nlp_DID, + ndlp->nlp_flag, data1); + return (ndlp); + } + } + } + + if (order & NLP_SEARCH_MAPPED) { + list_for_each_entry_safe(ndlp, next_ndlp, &phba->fc_nlpmap_list, + nlp_listp) { + if (memcmp(&ndlp->nlp_nodename, wwnn, + sizeof(struct lpfc_name)) == 0) { + + data1 = (((uint32_t) ndlp->nlp_state << 24) | + ((uint32_t) ndlp->nlp_xri << 16) | + ((uint32_t) ndlp->nlp_type << 8) | + ((uint32_t) ndlp->nlp_rpi & 0xff)); + /* FIND node did mapped */ + lpfc_printf_log(phba, KERN_INFO, LOG_NODE, + "%d:0902 FIND node DID mapped " + "Data: x%p x%x x%x x%x\n", + phba->brd_no, + ndlp, ndlp->nlp_DID, + ndlp->nlp_flag, data1); + return (ndlp); + } + } + } + + /* no match found */ + return ((struct lpfc_nodelist *) 0); +} +/* Search for a nodelist entry on a specific list */ +struct lpfc_nodelist * +lpfc_findnode_did(struct lpfc_hba * phba, uint32_t order, uint32_t did) +{ + struct lpfc_nodelist *ndlp, *next_ndlp; + uint32_t data1; + + if (order & NLP_SEARCH_UNMAPPED) { + list_for_each_entry_safe(ndlp, next_ndlp, + &phba->fc_nlpunmap_list, nlp_listp) { + if (lpfc_matchdid(phba, ndlp, did)) { + data1 = (((uint32_t) ndlp->nlp_state << 24) | + ((uint32_t) ndlp->nlp_xri << 16) | + ((uint32_t) ndlp->nlp_type << 8) | + ((uint32_t) ndlp->nlp_rpi & 0xff)); + /* FIND node DID unmapped */ + lpfc_printf_log(phba, KERN_INFO, LOG_NODE, + "%d:0929 FIND node DID unmapped" + " Data: x%p x%x x%x x%x\n", + phba->brd_no, + ndlp, ndlp->nlp_DID, + ndlp->nlp_flag, data1); + return (ndlp); + } + } + } + + if (order & NLP_SEARCH_MAPPED) { + list_for_each_entry_safe(ndlp, next_ndlp, &phba->fc_nlpmap_list, + nlp_listp) { + if (lpfc_matchdid(phba, ndlp, did)) { + + data1 = (((uint32_t) ndlp->nlp_state << 24) | + ((uint32_t) ndlp->nlp_xri << 16) | + ((uint32_t) ndlp->nlp_type << 8) | + ((uint32_t) ndlp->nlp_rpi & 0xff)); + /* FIND node DID mapped */ + lpfc_printf_log(phba, KERN_INFO, LOG_NODE, + "%d:0930 FIND node DID mapped " + "Data: x%p x%x x%x x%x\n", + phba->brd_no, + ndlp, ndlp->nlp_DID, + ndlp->nlp_flag, data1); + return (ndlp); + } + } + } + + if (order & NLP_SEARCH_PLOGI) { + list_for_each_entry_safe(ndlp, next_ndlp, &phba->fc_plogi_list, + nlp_listp) { + if (lpfc_matchdid(phba, ndlp, did)) { + + data1 = (((uint32_t) ndlp->nlp_state << 24) | + ((uint32_t) ndlp->nlp_xri << 16) | + ((uint32_t) ndlp->nlp_type << 8) | + ((uint32_t) ndlp->nlp_rpi & 0xff)); + /* LOG change to PLOGI */ + /* FIND node DID plogi */ + lpfc_printf_log(phba, KERN_INFO, LOG_NODE, + "%d:0908 FIND node DID plogi " + "Data: x%p x%x x%x x%x\n", + phba->brd_no, + ndlp, ndlp->nlp_DID, + ndlp->nlp_flag, data1); + return (ndlp); + } + } + } + + if (order & NLP_SEARCH_ADISC) { + list_for_each_entry_safe(ndlp, next_ndlp, &phba->fc_adisc_list, + nlp_listp) { + if (lpfc_matchdid(phba, ndlp, did)) { + + data1 = (((uint32_t) ndlp->nlp_state << 24) | + ((uint32_t) ndlp->nlp_xri << 16) | + ((uint32_t) ndlp->nlp_type << 8) | + ((uint32_t) ndlp->nlp_rpi & 0xff)); + /* LOG change to ADISC */ + /* FIND node DID adisc */ + lpfc_printf_log(phba, KERN_INFO, LOG_NODE, + "%d:0931 FIND node DID adisc " + "Data: x%p x%x x%x x%x\n", + phba->brd_no, + ndlp, ndlp->nlp_DID, + ndlp->nlp_flag, data1); + return (ndlp); + } + } + } + + if (order & NLP_SEARCH_REGLOGIN) { + list_for_each_entry_safe(ndlp, next_ndlp, + &phba->fc_reglogin_list, nlp_listp) { + if (lpfc_matchdid(phba, ndlp, did)) { + + data1 = (((uint32_t) ndlp->nlp_state << 24) | + ((uint32_t) ndlp->nlp_xri << 16) | + ((uint32_t) ndlp->nlp_type << 8) | + ((uint32_t) ndlp->nlp_rpi & 0xff)); + /* LOG change to REGLOGIN */ + /* FIND node DID reglogin */ + lpfc_printf_log(phba, KERN_INFO, LOG_NODE, + "%d:0931 FIND node DID reglogin" + " Data: x%p x%x x%x x%x\n", + phba->brd_no, + ndlp, ndlp->nlp_DID, + ndlp->nlp_flag, data1); + return (ndlp); + } + } + } + + if (order & NLP_SEARCH_PRLI) { + list_for_each_entry_safe(ndlp, next_ndlp, &phba->fc_prli_list, + nlp_listp) { + if (lpfc_matchdid(phba, ndlp, did)) { + + data1 = (((uint32_t) ndlp->nlp_state << 24) | + ((uint32_t) ndlp->nlp_xri << 16) | + ((uint32_t) ndlp->nlp_type << 8) | + ((uint32_t) ndlp->nlp_rpi & 0xff)); + /* LOG change to PRLI */ + /* FIND node DID prli */ + lpfc_printf_log(phba, KERN_INFO, LOG_NODE, + "%d:0931 FIND node DID prli " + "Data: x%p x%x x%x x%x\n", + phba->brd_no, + ndlp, ndlp->nlp_DID, + ndlp->nlp_flag, data1); + return (ndlp); + } + } + } + + if (order & NLP_SEARCH_NPR) { + list_for_each_entry_safe(ndlp, next_ndlp, &phba->fc_npr_list, + nlp_listp) { + if (lpfc_matchdid(phba, ndlp, did)) { + + data1 = (((uint32_t) ndlp->nlp_state << 24) | + ((uint32_t) ndlp->nlp_xri << 16) | + ((uint32_t) ndlp->nlp_type << 8) | + ((uint32_t) ndlp->nlp_rpi & 0xff)); + /* LOG change to NPR */ + /* FIND node DID npr */ + lpfc_printf_log(phba, KERN_INFO, LOG_NODE, + "%d:0931 FIND node DID npr " + "Data: x%p x%x x%x x%x\n", + phba->brd_no, + ndlp, ndlp->nlp_DID, + ndlp->nlp_flag, data1); + return (ndlp); + } + } + } + + if (order & NLP_SEARCH_UNUSED) { + list_for_each_entry_safe(ndlp, next_ndlp, &phba->fc_adisc_list, + nlp_listp) { + if (lpfc_matchdid(phba, ndlp, did)) { + + data1 = (((uint32_t) ndlp->nlp_state << 24) | + ((uint32_t) ndlp->nlp_xri << 16) | + ((uint32_t) ndlp->nlp_type << 8) | + ((uint32_t) ndlp->nlp_rpi & 0xff)); + /* LOG change to UNUSED */ + /* FIND node DID unused */ + lpfc_printf_log(phba, KERN_INFO, LOG_NODE, + "%d:0931 FIND node DID unused " + "Data: x%p x%x x%x x%x\n", + phba->brd_no, + ndlp, ndlp->nlp_DID, + ndlp->nlp_flag, data1); + return (ndlp); + } + } + } + + /* FIND node did NOT FOUND */ + lpfc_printf_log(phba, + KERN_INFO, + LOG_NODE, + "%d:0932 FIND node did x%x NOT FOUND Data: x%x\n", + phba->brd_no, did, order); + + /* no match found */ + return ((struct lpfc_nodelist *) 0); +} + +struct lpfc_nodelist * +lpfc_setup_disc_node(struct lpfc_hba * phba, uint32_t did) +{ + struct lpfc_nodelist *ndlp; + uint32_t flg; + + if((ndlp = lpfc_findnode_did(phba, NLP_SEARCH_ALL, did)) == 0) { + if ((phba->hba_state == LPFC_HBA_READY) && + ((lpfc_rscn_payload_check(phba, did) == 0))) + return NULL; + ndlp = (struct lpfc_nodelist *) + mempool_alloc(phba->nlp_mem_pool, GFP_ATOMIC); + if (!ndlp) + return NULL; + lpfc_nlp_init(phba, ndlp, did); + ndlp->nlp_state = NLP_STE_NPR_NODE; + lpfc_nlp_list(phba, ndlp, NLP_NPR_LIST); + ndlp->nlp_flag |= NLP_NPR_2B_DISC; + return ndlp; + } + if ((phba->hba_state == LPFC_HBA_READY) && + (phba->fc_flag & FC_RSCN_MODE)) { + if(lpfc_rscn_payload_check(phba, did)) { + ndlp->nlp_flag |= NLP_NPR_2B_DISC; + } + else { + ndlp->nlp_flag &= ~NLP_NPR_2B_DISC; + ndlp = NULL; + } + } + else { + flg = ndlp->nlp_flag & NLP_LIST_MASK; + if ((flg == NLP_ADISC_LIST) || + (flg == NLP_PLOGI_LIST)) { + return NULL; + } + ndlp->nlp_state = NLP_STE_NPR_NODE; + lpfc_nlp_list(phba, ndlp, NLP_NPR_LIST); + ndlp->nlp_flag |= NLP_NPR_2B_DISC; + } + return ndlp; +} + +/* Build a list of nodes to discover based on the loopmap */ +void +lpfc_disc_list_loopmap(struct lpfc_hba * phba) +{ + int j; + uint32_t alpa, index; + + if (phba->hba_state <= LPFC_LINK_DOWN) { + return; + } + if (phba->fc_topology != TOPOLOGY_LOOP) { + return; + } + + /* Check for loop map present or not */ + if (phba->alpa_map[0]) { + for (j = 1; j <= phba->alpa_map[0]; j++) { + alpa = phba->alpa_map[j]; + + if (((phba->fc_myDID & 0xff) == alpa) || (alpa == 0)) { + continue; + } + lpfc_setup_disc_node(phba, alpa); + } + } else { + /* No alpamap, so try all alpa's */ + for (j = 0; j < FC_MAXLOOP; j++) { + /* If cfg_scan_down is set, start from highest + * ALPA (0xef) to lowest (0x1). + */ + if (phba->cfg_scan_down) + index = j; + else + index = FC_MAXLOOP - j - 1; + alpa = lpfcAlpaArray[index]; + if ((phba->fc_myDID & 0xff) == alpa) { + continue; + } + + lpfc_setup_disc_node(phba, alpa); + } + } + return; +} + +/* Start Link up / RSCN discovery on NPR list */ +void +lpfc_disc_start(struct lpfc_hba * phba) +{ + struct lpfc_sli *psli; + LPFC_MBOXQ_t *mbox; + struct lpfc_nodelist *ndlp, *next_ndlp; + uint32_t did_changed, num_sent; + uint32_t clear_la_pending; + + psli = &phba->sli; + + if (phba->hba_state <= LPFC_LINK_DOWN) { + return; + } + if (phba->hba_state == LPFC_CLEAR_LA) + clear_la_pending = 1; + else + clear_la_pending = 0; + + if (phba->hba_state < LPFC_HBA_READY) { + phba->hba_state = LPFC_DISC_AUTH; + } + lpfc_set_disctmo(phba); + + if (phba->fc_prevDID == phba->fc_myDID) { + did_changed = 0; + } else { + did_changed = 1; + } + phba->fc_prevDID = phba->fc_myDID; + phba->num_disc_nodes = 0; + + /* Start Discovery state */ + lpfc_printf_log(phba, KERN_INFO, LOG_DISCOVERY, + "%d:0202 Start Discovery hba state x%x " + "Data: x%x x%x x%x\n", + phba->brd_no, phba->hba_state, phba->fc_flag, + phba->fc_plogi_cnt, phba->fc_adisc_cnt); + + /* If our did changed, we MUST do PLOGI */ + list_for_each_entry_safe(ndlp, next_ndlp, &phba->fc_npr_list, + nlp_listp) { + if(ndlp->nlp_flag & NLP_NPR_2B_DISC) { + if(did_changed) + ndlp->nlp_flag &= ~NLP_NPR_ADISC; + } + } + + /* First do ADISCs - if any */ + num_sent = lpfc_els_disc_adisc(phba); + + if(num_sent) + return; + + if ((phba->hba_state < LPFC_HBA_READY) && (!clear_la_pending)) { + /* If we get here, there is nothing to ADISC */ + if ((mbox = mempool_alloc(phba->mbox_mem_pool, GFP_ATOMIC))) { + phba->hba_state = LPFC_CLEAR_LA; + lpfc_clear_la(phba, mbox); + mbox->mbox_cmpl = lpfc_mbx_cmpl_clear_la; + if (lpfc_sli_issue_mbox + (phba, mbox, (MBX_NOWAIT | MBX_STOP_IOCB)) + == MBX_NOT_FINISHED) { + mempool_free( mbox, phba->mbox_mem_pool); + lpfc_disc_flush_list(phba); + psli->ring[(psli->ip_ring)].flag &= + ~LPFC_STOP_IOCB_EVENT; + psli->ring[(psli->fcp_ring)].flag &= + ~LPFC_STOP_IOCB_EVENT; + psli->ring[(psli->next_ring)].flag &= + ~LPFC_STOP_IOCB_EVENT; + phba->hba_state = LPFC_HBA_READY; + } + } + } else { + /* Next do PLOGIs - if any */ + num_sent = lpfc_els_disc_plogi(phba); + + if(num_sent) + return; + + if (phba->fc_flag & FC_RSCN_MODE) { + /* Check to see if more RSCNs came in while we + * were processing this one. + */ + if ((phba->fc_rscn_id_cnt == 0) && + (!(phba->fc_flag & FC_RSCN_DISCOVERY))) { + lpfc_els_flush_rscn(phba); + } else { + lpfc_els_handle_rscn(phba); + } + } + } + return; +} + +/* + * Ignore completion for all IOCBs on tx and txcmpl queue for ELS + * ring the match the sppecified nodelist. + */ +static void +lpfc_free_tx(struct lpfc_hba * phba, struct lpfc_nodelist * ndlp) +{ + struct lpfc_sli *psli; + IOCB_t *icmd; + struct lpfc_iocbq *iocb, *next_iocb; + struct lpfc_sli_ring *pring; + struct lpfc_dmabuf *mp; + + psli = &phba->sli; + pring = &psli->ring[LPFC_ELS_RING]; + + /* Error matching iocb on txq or txcmplq + * First check the txq. + */ + list_for_each_entry_safe(iocb, next_iocb, &pring->txq, list) { + if (iocb->context1 != ndlp) { + continue; + } + icmd = &iocb->iocb; + if ((icmd->ulpCommand == CMD_ELS_REQUEST64_CR) || + (icmd->ulpCommand == CMD_XMIT_ELS_RSP64_CX)) { + + list_del(&iocb->list); + pring->txq_cnt--; + lpfc_els_free_iocb(phba, iocb); + } + } + + /* Next check the txcmplq */ + list_for_each_entry_safe(iocb, next_iocb, &pring->txcmplq, list) { + if (iocb->context1 != ndlp) { + continue; + } + icmd = &iocb->iocb; + if ((icmd->ulpCommand == CMD_ELS_REQUEST64_CR) || + (icmd->ulpCommand == CMD_XMIT_ELS_RSP64_CX)) { + + iocb->iocb_cmpl = NULL; + /* context2 = cmd, context2->next = rsp, context3 = + bpl */ + if (iocb->context2) { + /* Free the response IOCB before handling the + command. */ + + mp = (struct lpfc_dmabuf *) + (((struct lpfc_dmabuf *) (iocb->context2)) + ->list.next); + if (mp) { + /* Delay before releasing rsp buffer to + * give UNREG mbox a chance to take + * effect. + */ + list_add(&mp->list, + &phba->freebufList); + } + lpfc_mbuf_free(phba, + ((struct lpfc_dmabuf *) + iocb->context2)->virt, + ((struct lpfc_dmabuf *) + iocb->context2)->phys); + kfree(iocb->context2); + } + + if (iocb->context3) { + lpfc_mbuf_free(phba, + ((struct lpfc_dmabuf *) + iocb->context3)->virt, + ((struct lpfc_dmabuf *) + iocb->context3)->phys); + kfree(iocb->context3); + } + } + } + + return; +} + +void +lpfc_disc_flush_list(struct lpfc_hba * phba) +{ + struct lpfc_nodelist *ndlp, *next_ndlp; + + if (phba->fc_plogi_cnt) { + list_for_each_entry_safe(ndlp, next_ndlp, &phba->fc_plogi_list, + nlp_listp) { + lpfc_set_failmask(phba, ndlp, LPFC_DEV_DISCONNECTED, + LPFC_SET_BITMASK); + lpfc_free_tx(phba, ndlp); + lpfc_nlp_remove(phba, ndlp); + } + } + if (phba->fc_adisc_cnt) { + list_for_each_entry_safe(ndlp, next_ndlp, &phba->fc_adisc_list, + nlp_listp) { + lpfc_set_failmask(phba, ndlp, LPFC_DEV_DISCONNECTED, + LPFC_SET_BITMASK); + lpfc_free_tx(phba, ndlp); + lpfc_nlp_remove(phba, ndlp); + } + } + return; +} + +/*****************************************************************************/ +/* + * NAME: lpfc_disc_timeout + * + * FUNCTION: Fibre Channel driver discovery timeout routine. + * + * EXECUTION ENVIRONMENT: interrupt only + * + * CALLED FROM: + * Timer function + * + * RETURNS: + * none + */ +/*****************************************************************************/ +void +lpfc_disc_timeout(unsigned long ptr) +{ + struct lpfc_hba *phba; + struct lpfc_sli *psli; + struct lpfc_nodelist *ndlp; + LPFC_MBOXQ_t *mbox; + unsigned long iflag; + + phba = (struct lpfc_hba *)ptr; + if (!phba) { + return; + } + spin_lock_irqsave(phba->host->host_lock, iflag); + + psli = &phba->sli; + phba->fc_flag &= ~FC_DISC_TMO; + + /* hba_state is identically LPFC_LOCAL_CFG_LINK while waiting for FAN */ + if (phba->hba_state == LPFC_LOCAL_CFG_LINK) { + /* FAN timeout */ + lpfc_printf_log(phba, + KERN_WARNING, + LOG_DISCOVERY, + "%d:0221 FAN timeout\n", + phba->brd_no); + + /* Forget about FAN, Start discovery by sending a FLOGI + * hba_state is identically LPFC_FLOGI while waiting for FLOGI + * cmpl + */ + phba->hba_state = LPFC_FLOGI; + lpfc_set_disctmo(phba); + lpfc_initial_flogi(phba); + goto out; + } + + /* hba_state is identically LPFC_FLOGI while waiting for FLOGI cmpl */ + if (phba->hba_state == LPFC_FLOGI) { + /* Initial FLOGI timeout */ + lpfc_printf_log(phba, + KERN_ERR, + LOG_DISCOVERY, + "%d:0222 Initial FLOGI timeout\n", + phba->brd_no); + + /* Assume no Fabric and go on with discovery. + * Check for outstanding ELS FLOGI to abort. + */ + + /* FLOGI failed, so just use loop map to make discovery list */ + lpfc_disc_list_loopmap(phba); + + /* Start discovery */ + lpfc_disc_start(phba); + goto out; + } + + /* hba_state is identically LPFC_FABRIC_CFG_LINK while waiting for + NameServer login */ + if (phba->hba_state == LPFC_FABRIC_CFG_LINK) { + /* Timeout while waiting for NameServer login */ + lpfc_printf_log(phba, KERN_ERR, LOG_DISCOVERY, + "%d:0223 Timeout while waiting for NameServer " + "login\n", phba->brd_no); + + /* Next look for NameServer ndlp */ + if ((ndlp = lpfc_findnode_did(phba, + NLP_SEARCH_ALL, NameServer_DID))) { + lpfc_nlp_remove(phba, ndlp); + } + /* Start discovery */ + lpfc_disc_start(phba); + goto out; + } + + /* Check for wait for NameServer Rsp timeout */ + if (phba->hba_state == LPFC_NS_QRY) { + /* NameServer Query timeout */ + lpfc_printf_log(phba, KERN_ERR, LOG_DISCOVERY, + "%d:0224 NameServer Query timeout " + "Data: x%x x%x\n", + phba->brd_no, + phba->fc_ns_retry, LPFC_MAX_NS_RETRY); + + if ((ndlp = + lpfc_findnode_did(phba, NLP_SEARCH_UNMAPPED, + NameServer_DID))) { + if (phba->fc_ns_retry < LPFC_MAX_NS_RETRY) { + /* Try it one more time */ + if (lpfc_ns_cmd(phba, ndlp, SLI_CTNS_GID_FT) == + 0) { + goto out; + } + } + phba->fc_ns_retry = 0; + } + + /* Nothing to authenticate, so CLEAR_LA right now */ + if (phba->hba_state != LPFC_CLEAR_LA) { + if ((mbox = mempool_alloc(phba->mbox_mem_pool, + GFP_ATOMIC))) { + phba->hba_state = LPFC_CLEAR_LA; + lpfc_clear_la(phba, mbox); + mbox->mbox_cmpl = lpfc_mbx_cmpl_clear_la; + if (lpfc_sli_issue_mbox + (phba, mbox, (MBX_NOWAIT | MBX_STOP_IOCB)) + == MBX_NOT_FINISHED) { + mempool_free(mbox, phba->mbox_mem_pool); + goto clrlaerr; + } + } else { + /* Device Discovery completion error */ + lpfc_printf_log(phba, KERN_ERR, LOG_DISCOVERY, + "%d:0226 Device Discovery " + "completion error\n", + phba->brd_no); + phba->hba_state = LPFC_HBA_ERROR; + } + } + if ((mbox = mempool_alloc(phba->mbox_mem_pool, GFP_ATOMIC))) { + /* Setup and issue mailbox INITIALIZE LINK command */ + lpfc_linkdown(phba); + lpfc_init_link(phba, mbox, + phba->cfg_topology, + phba->cfg_link_speed); + mbox->mb.un.varInitLnk.lipsr_AL_PA = 0; + if (lpfc_sli_issue_mbox + (phba, mbox, (MBX_NOWAIT | MBX_STOP_IOCB)) + == MBX_NOT_FINISHED) { + mempool_free( mbox, phba->mbox_mem_pool); + } + } + goto out; + } + + if (phba->hba_state == LPFC_DISC_AUTH) { + /* Node Authentication timeout */ + lpfc_printf_log(phba, + KERN_ERR, + LOG_DISCOVERY, + "%d:0227 Node Authentication timeout\n", + phba->brd_no); + lpfc_disc_flush_list(phba); + if (phba->hba_state != LPFC_CLEAR_LA) { + if ((mbox = mempool_alloc(phba->mbox_mem_pool, + GFP_ATOMIC))) { + phba->hba_state = LPFC_CLEAR_LA; + lpfc_clear_la(phba, mbox); + mbox->mbox_cmpl = lpfc_mbx_cmpl_clear_la; + if (lpfc_sli_issue_mbox + (phba, mbox, (MBX_NOWAIT | MBX_STOP_IOCB)) + == MBX_NOT_FINISHED) { + mempool_free(mbox, phba->mbox_mem_pool); + goto clrlaerr; + } + } + } + goto out; + } + + if (phba->hba_state == LPFC_CLEAR_LA) { + /* CLEAR LA timeout */ + lpfc_printf_log(phba, + KERN_ERR, + LOG_DISCOVERY, + "%d:0228 CLEAR LA timeout\n", + phba->brd_no); +clrlaerr: + lpfc_disc_flush_list(phba); + psli->ring[(psli->ip_ring)].flag &= ~LPFC_STOP_IOCB_EVENT; + psli->ring[(psli->fcp_ring)].flag &= ~LPFC_STOP_IOCB_EVENT; + psli->ring[(psli->next_ring)].flag &= ~LPFC_STOP_IOCB_EVENT; + phba->hba_state = LPFC_HBA_READY; + goto out; + } + + if ((phba->hba_state == LPFC_HBA_READY) && + (phba->fc_flag & FC_RSCN_MODE)) { + /* RSCN timeout */ + lpfc_printf_log(phba, + KERN_ERR, + LOG_DISCOVERY, + "%d:0231 RSCN timeout Data: x%x x%x\n", + phba->brd_no, + phba->fc_ns_retry, LPFC_MAX_NS_RETRY); + + /* Cleanup any outstanding ELS commands */ + lpfc_els_flush_cmd(phba); + + lpfc_els_flush_rscn(phba); + lpfc_disc_flush_list(phba); + goto out; + } + +out: + spin_unlock_irqrestore(phba->host->host_lock, iflag); + return; +} + +/*****************************************************************************/ +/* + * NAME: lpfc_scan_timeout + * + * FUNCTION: Fibre Channel driver scsi_scan_host timeout routine. + * + * EXECUTION ENVIRONMENT: interrupt only + * + * CALLED FROM: + * Timer function + * + * RETURNS: + * none + */ +/*****************************************************************************/ +void +lpfc_scan_timeout(unsigned long ptr) +{ + struct lpfc_hba *phba; + unsigned long iflag; + + phba = (struct lpfc_hba *)ptr; + if (!phba) { + return; + } + spin_lock_irqsave(phba->host->host_lock, iflag); + phba->fc_flag &= ~FC_SCSI_SCAN_TMO; + lpfc_discq_post_event(phba, NULL, NULL, LPFC_EVT_SCAN); + spin_unlock_irqrestore(phba->host->host_lock, iflag); + return; +} + +static void +lpfc_nodev_timeout(unsigned long ptr) +{ + struct lpfc_hba *phba; + struct lpfc_nodelist *ndlp; + unsigned long iflag; + + ndlp = (struct lpfc_nodelist *)ptr; + phba = ndlp->nlp_phba; + spin_lock_irqsave(phba->host->host_lock, iflag); + + /* All nodev timeouts are posted to discovery tasklet */ + lpfc_discq_post_event(phba, ndlp, NULL, LPFC_EVT_NODEV_TMO); + + spin_unlock_irqrestore(phba->host->host_lock, iflag); + return; +} + +/*****************************************************************************/ +/* + * NAME: lpfc_find_target + * + * FUNCTION: Fibre Channel bus/target/LUN to struct lpfc_target lookup + * + * EXECUTION ENVIRONMENT: + * + * RETURNS: + * ptr to desired struct lpfc_target + */ +/*****************************************************************************/ +struct lpfc_target * +lpfc_find_target(struct lpfc_hba * phba, uint32_t tgt, + struct lpfc_nodelist *nlp) +{ + struct lpfc_target *targetp; + + /* search the mapped list for this target ID */ + if(!nlp) { + list_for_each_entry(nlp, &phba->fc_nlpmap_list, nlp_listp) { + if (tgt == nlp->nlp_sid) + break; + } + + if (&(nlp->nlp_listp) == &(phba->fc_nlpmap_list)) + return NULL; + } + + targetp = phba->device_queue_hash[tgt]; + + /* First see if the SCSI ID has an allocated struct lpfc_target */ + if (!targetp) { + targetp = kmalloc(sizeof (struct lpfc_target), GFP_ATOMIC); + if (!targetp) + return NULL; + + memset(targetp, 0, sizeof (struct lpfc_target)); + phba->device_queue_hash[tgt] = targetp; + targetp->scsi_id = tgt; + + /* Create SCSI Target */ + lpfc_printf_log(phba, + KERN_INFO, + LOG_DISCOVERY | LOG_FCP, + "%d:0204 Create SCSI Target %d\n", + phba->brd_no, tgt); + } + + if (targetp->pnode == NULL) { + targetp->pnode = nlp; + nlp->nlp_Target = targetp; +#ifdef FC_TRANS_VER1 + /* + * This code does not apply to SLES9 since there is no + * starget defined in the midlayer. Additionally, + * dynamic target discovery to the midlayer is not + * supported yet. + */ + if(!(phba->fc_flag & FC_LOADING)) { + /* Add SCSI target / SCSI Hotplug if called + * after initial driver load. + */ + lpfc_target_add(phba, targetp); + } +#endif + } + else { + if(targetp->pnode != nlp) { + /* + * Get rid of the old nlp before updating + * targetp with the new one. + */ + lpfc_nlp_list(phba, targetp->pnode, NLP_NO_LIST); + targetp->pnode = nlp; + } + } + nlp->nlp_Target = targetp; + return (targetp); +} + +/* + * lpfc_set_failmask + * Set, or clear, failMask bits in struct lpfc_nodelist + */ +void +lpfc_set_failmask(struct lpfc_hba * phba, + struct lpfc_nodelist * ndlp, uint32_t bitmask, uint32_t flag) +{ + uint32_t oldmask; + uint32_t changed; + + /* Failmask change on NPort */ + lpfc_printf_log(phba, KERN_INFO, LOG_DISCOVERY, + "%d:0208 Failmask change on NPort x%x " + "Data: x%x x%x x%x\n", + phba->brd_no, + ndlp->nlp_DID, ndlp->nlp_failMask, bitmask, flag); + + if (flag == LPFC_SET_BITMASK) { + oldmask = ndlp->nlp_failMask; + /* Set failMask event */ + ndlp->nlp_failMask |= bitmask; + if (oldmask != ndlp->nlp_failMask) { + changed = 1; + } else { + changed = 0; + } + + } else { + /* Clear failMask event */ + ndlp->nlp_failMask &= ~bitmask; + changed = 1; + } + return; +} + +/* + * This routine handles processing a NameServer REG_LOGIN mailbox + * command upon completion. It is setup in the LPFC_MBOXQ + * as the completion routine when the command is + * handed off to the SLI layer. + */ +void +lpfc_mbx_cmpl_fdmi_reg_login(struct lpfc_hba * phba, LPFC_MBOXQ_t * pmb) +{ + struct lpfc_sli *psli; + MAILBOX_t *mb; + struct lpfc_dmabuf *mp; + struct lpfc_nodelist *ndlp; + + psli = &phba->sli; + mb = &pmb->mb; + + ndlp = (struct lpfc_nodelist *) pmb->context2; + mp = (struct lpfc_dmabuf *) (pmb->context1); + + /* The mailbox was populated by the HBA. Flush it to main store for the + * driver. Note that all context buffers are from the driver's + * dma pool and have length LPFC_BPL_SIZE. + */ + pci_dma_sync_single_for_cpu(phba->pcidev, mp->phys, LPFC_BPL_SIZE, + PCI_DMA_FROMDEVICE); + + pmb->context1 = NULL; + + if (ndlp->nlp_rpi != 0) + lpfc_findnode_remove_rpi(phba, ndlp->nlp_rpi); + ndlp->nlp_rpi = mb->un.varWords[0]; + lpfc_addnode_rpi(phba, ndlp, ndlp->nlp_rpi); + ndlp->nlp_type |= NLP_FABRIC; + ndlp->nlp_state = NLP_STE_UNMAPPED_NODE; + lpfc_nlp_list(phba, ndlp, NLP_UNMAPPED_LIST); + + /* Start issuing Fabric-Device Management Interface (FDMI) + * command to 0xfffffa (FDMI well known port) + */ + if (phba->cfg_fdmi_on == 1) { + lpfc_fdmi_cmd(phba, ndlp, SLI_MGMT_DHBA); + } else { + /* + * Delay issuing FDMI command if fdmi-on=2 + * (supporting RPA/hostnmae) + */ + mod_timer(&phba->fc_fdmitmo, jiffies + HZ * 60); + } + + lpfc_mbuf_free(phba, mp->virt, mp->phys); + kfree(mp); + mempool_free( pmb, phba->mbox_mem_pool); + + return; +} + +/* + * This routine looks up the ndlp hash + * table for the given RPI. If rpi found + * it return the node list pointer + * else return 0. + */ +struct lpfc_nodelist * +lpfc_findnode_rpi(struct lpfc_hba * phba, uint16_t rpi) +{ + struct lpfc_nodelist *ret; + + ret = phba->fc_nlplookup[LPFC_RPI_HASH_FUNC(rpi)]; + while ((ret != 0) && (ret->nlp_rpi != rpi)) { + ret = ret->nlp_rpi_hash_next; + } + return ret; +} + +/* + * This routine looks up the ndlp hash table for the + * given RPI. If rpi found it return the node list + * pointer else return 0 after deleting the entry + * from hash table. + */ +struct lpfc_nodelist * +lpfc_findnode_remove_rpi(struct lpfc_hba * phba, uint16_t rpi) +{ + struct lpfc_nodelist *ret, *temp;; + + ret = phba->fc_nlplookup[LPFC_RPI_HASH_FUNC(rpi)]; + if (ret == 0) + return NULL; + + if (ret->nlp_rpi == rpi) { + phba->fc_nlplookup[LPFC_RPI_HASH_FUNC(rpi)] = + ret->nlp_rpi_hash_next; + ret->nlp_rpi_hash_next = NULL; + return ret; + } + + while ((ret->nlp_rpi_hash_next != 0) && + (ret->nlp_rpi_hash_next->nlp_rpi != rpi)) { + ret = ret->nlp_rpi_hash_next; + } + + if (ret->nlp_rpi_hash_next != 0) { + temp = ret->nlp_rpi_hash_next; + ret->nlp_rpi_hash_next = temp->nlp_rpi_hash_next; + temp->nlp_rpi_hash_next = NULL; + return temp; + } else { + return NULL; + } +} + +/* + * This routine adds the node list entry to the + * ndlp hash table. + */ +void +lpfc_addnode_rpi(struct lpfc_hba * phba, struct lpfc_nodelist * ndlp, + uint16_t rpi) +{ + + uint32_t index; + + index = LPFC_RPI_HASH_FUNC(rpi); + ndlp->nlp_rpi_hash_next = phba->fc_nlplookup[index]; + phba->fc_nlplookup[index] = ndlp; + return; +} + +void +lpfc_nlp_init(struct lpfc_hba * phba, struct lpfc_nodelist * ndlp, + uint32_t did) +{ + memset(ndlp, 0, sizeof (struct lpfc_nodelist)); + init_timer(&ndlp->nlp_tmofunc); + ndlp->nlp_tmofunc.function = lpfc_nodev_timeout; + ndlp->nlp_tmofunc.data = (unsigned long)ndlp; + 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->nlp_phba = phba; + return; +} + diff --git a/drivers/scsi/lpfc/lpfc_hw.h b/drivers/scsi/lpfc/lpfc_hw.h new file mode 100644 index 000000000..b5c023a95 --- /dev/null +++ b/drivers/scsi/lpfc/lpfc_hw.h @@ -0,0 +1,2688 @@ +/******************************************************************* + * This file is part of the Emulex Linux Device Driver for * + * Enterprise Fibre Channel Host Bus Adapters. * + * Refer to the README file included with this package for * + * driver version and adapter support. * + * Copyright (C) 2004 Emulex Corporation. * + * www.emulex.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, a copy of which * + * can be found in the file COPYING included with this package. * + *******************************************************************/ + +/* + * $Id: lpfc_hw.h 1.29 2004/11/18 17:28:05EST sf_support Exp $ + */ + +#ifndef _H_LPFC_HW +#define _H_LPFC_HW + +#define FDMI_DID ((uint32_t)0xfffffa) +#define NameServer_DID ((uint32_t)0xfffffc) +#define SCR_DID ((uint32_t)0xfffffd) +#define Fabric_DID ((uint32_t)0xfffffe) +#define Bcast_DID ((uint32_t)0xffffff) +#define Mask_DID ((uint32_t)0xffffff) +#define CT_DID_MASK ((uint32_t)0xffff00) +#define Fabric_DID_MASK ((uint32_t)0xfff000) +#define WELL_KNOWN_DID_MASK ((uint32_t)0xfffff0) + +#define PT2PT_LocalID ((uint32_t)1) +#define PT2PT_RemoteID ((uint32_t)2) + +#define FF_DEF_EDTOV 2000 /* Default E_D_TOV (2000ms) */ +#define FF_DEF_ALTOV 15 /* Default AL_TIME (15ms) */ +#define FF_DEF_RATOV 2 /* Default RA_TOV (2s) */ +#define FF_DEF_ARBTOV 1900 /* Default ARB_TOV (1900ms) */ + +#define LPFC_BUF_RING0 64 /* Number of buffers to post to RING + 0 */ + +#define FCELSSIZE 1024 /* maximum ELS transfer size */ + +#define LPFC_FCP_RING 0 /* ring 2 for FCP initiator commands */ +#define LPFC_IP_RING 1 /* ring 1 for IP commands */ +#define LPFC_ELS_RING 2 /* ring 0 for ELS commands */ +#define LPFC_FCP_NEXT_RING 3 + +#define SLI2_IOCB_CMD_R0_ENTRIES 172 /* SLI-2 FCP command ring entries */ +#define SLI2_IOCB_RSP_R0_ENTRIES 134 /* SLI-2 FCP response ring entries */ +#define SLI2_IOCB_CMD_R1_ENTRIES 4 /* SLI-2 IP command ring entries */ +#define SLI2_IOCB_RSP_R1_ENTRIES 4 /* SLI-2 IP response ring entries */ +#define SLI2_IOCB_CMD_R1XTRA_ENTRIES 36 /* SLI-2 extra FCP cmd ring entries */ +#define SLI2_IOCB_RSP_R1XTRA_ENTRIES 52 /* SLI-2 extra FCP rsp ring entries */ +#define SLI2_IOCB_CMD_R2_ENTRIES 20 /* SLI-2 ELS command ring entries */ +#define SLI2_IOCB_RSP_R2_ENTRIES 20 /* SLI-2 ELS response ring entries */ +#define SLI2_IOCB_CMD_R3_ENTRIES 0 +#define SLI2_IOCB_RSP_R3_ENTRIES 0 +#define SLI2_IOCB_CMD_R3XTRA_ENTRIES 24 +#define SLI2_IOCB_RSP_R3XTRA_ENTRIES 32 + +/* Common Transport structures and definitions */ + +union CtRevisionId { + /* Structure is in Big Endian format */ + struct { + uint32_t Revision:8; + uint32_t InId:24; + } bits; + uint32_t word; +}; + +union CtCommandResponse { + /* Structure is in Big Endian format */ + struct { + uint32_t CmdRsp:16; + uint32_t Size:16; + } bits; + uint32_t word; +}; + +struct lpfc_sli_ct_request { + /* Structure is in Big Endian format */ + union CtRevisionId RevisionId; + uint8_t FsType; + uint8_t FsSubType; + uint8_t Options; + uint8_t Rsrvd1; + union CtCommandResponse CommandResponse; + uint8_t Rsrvd2; + uint8_t ReasonCode; + uint8_t Explanation; + uint8_t VendorUnique; + + union { + uint32_t PortID; + struct gid { + uint8_t PortType; /* for GID_PT requests */ + uint8_t DomainScope; + uint8_t AreaScope; + uint8_t Fc4Type; /* for GID_FT requests */ + } gid; + struct rft { + uint32_t PortId; /* For RFT_ID requests */ + +#ifdef __BIG_ENDIAN_BITFIELD + uint32_t rsvd0:16; + uint32_t rsvd1:7; + uint32_t fcpReg:1; /* Type 8 */ + uint32_t rsvd2:2; + uint32_t ipReg:1; /* Type 5 */ + uint32_t rsvd3:5; +#else /* __LITTLE_ENDIAN_BITFIELD */ + uint32_t rsvd0:16; + uint32_t fcpReg:1; /* Type 8 */ + uint32_t rsvd1:7; + uint32_t rsvd3:5; + uint32_t ipReg:1; /* Type 5 */ + uint32_t rsvd2:2; +#endif + + uint32_t rsvd[7]; + } rft; + struct rnn { + uint32_t PortId; /* For RNN_ID requests */ + uint8_t wwnn[8]; + } rnn; + struct rsnn { /* For RSNN_ID requests */ + uint8_t wwnn[8]; + uint8_t len; + uint8_t symbname[255]; + } rsnn; + } un; +}; + +#define SLI_CT_REVISION 1 +#define GID_REQUEST_SZ (sizeof(struct lpfc_sli_ct_request) - 260) +#define RFT_REQUEST_SZ (sizeof(struct lpfc_sli_ct_request) - 228) +#define RNN_REQUEST_SZ (sizeof(struct lpfc_sli_ct_request) - 252) +#define RSNN_REQUEST_SZ (sizeof(struct lpfc_sli_ct_request)) + +/* + * FsType Definitions + */ + +#define SLI_CT_MANAGEMENT_SERVICE 0xFA +#define SLI_CT_TIME_SERVICE 0xFB +#define SLI_CT_DIRECTORY_SERVICE 0xFC +#define SLI_CT_FABRIC_CONTROLLER_SERVICE 0xFD + +/* + * Directory Service Subtypes + */ + +#define SLI_CT_DIRECTORY_NAME_SERVER 0x02 + +/* + * Response Codes + */ + +#define SLI_CT_RESPONSE_FS_RJT 0x8001 +#define SLI_CT_RESPONSE_FS_ACC 0x8002 + +/* + * Reason Codes + */ + +#define SLI_CT_NO_ADDITIONAL_EXPL 0x0 +#define SLI_CT_INVALID_COMMAND 0x01 +#define SLI_CT_INVALID_VERSION 0x02 +#define SLI_CT_LOGICAL_ERROR 0x03 +#define SLI_CT_INVALID_IU_SIZE 0x04 +#define SLI_CT_LOGICAL_BUSY 0x05 +#define SLI_CT_PROTOCOL_ERROR 0x07 +#define SLI_CT_UNABLE_TO_PERFORM_REQ 0x09 +#define SLI_CT_REQ_NOT_SUPPORTED 0x0b +#define SLI_CT_HBA_INFO_NOT_REGISTERED 0x10 +#define SLI_CT_MULTIPLE_HBA_ATTR_OF_SAME_TYPE 0x11 +#define SLI_CT_INVALID_HBA_ATTR_BLOCK_LEN 0x12 +#define SLI_CT_HBA_ATTR_NOT_PRESENT 0x13 +#define SLI_CT_PORT_INFO_NOT_REGISTERED 0x20 +#define SLI_CT_MULTIPLE_PORT_ATTR_OF_SAME_TYPE 0x21 +#define SLI_CT_INVALID_PORT_ATTR_BLOCK_LEN 0x22 +#define SLI_CT_VENDOR_UNIQUE 0xff + +/* + * Name Server SLI_CT_UNABLE_TO_PERFORM_REQ Explanations + */ + +#define SLI_CT_NO_PORT_ID 0x01 +#define SLI_CT_NO_PORT_NAME 0x02 +#define SLI_CT_NO_NODE_NAME 0x03 +#define SLI_CT_NO_CLASS_OF_SERVICE 0x04 +#define SLI_CT_NO_IP_ADDRESS 0x05 +#define SLI_CT_NO_IPA 0x06 +#define SLI_CT_NO_FC4_TYPES 0x07 +#define SLI_CT_NO_SYMBOLIC_PORT_NAME 0x08 +#define SLI_CT_NO_SYMBOLIC_NODE_NAME 0x09 +#define SLI_CT_NO_PORT_TYPE 0x0A +#define SLI_CT_ACCESS_DENIED 0x10 +#define SLI_CT_INVALID_PORT_ID 0x11 +#define SLI_CT_DATABASE_EMPTY 0x12 + +/* + * Name Server Command Codes + */ + +#define SLI_CTNS_GA_NXT 0x0100 +#define SLI_CTNS_GPN_ID 0x0112 +#define SLI_CTNS_GNN_ID 0x0113 +#define SLI_CTNS_GCS_ID 0x0114 +#define SLI_CTNS_GFT_ID 0x0117 +#define SLI_CTNS_GSPN_ID 0x0118 +#define SLI_CTNS_GPT_ID 0x011A +#define SLI_CTNS_GID_PN 0x0121 +#define SLI_CTNS_GID_NN 0x0131 +#define SLI_CTNS_GIP_NN 0x0135 +#define SLI_CTNS_GIPA_NN 0x0136 +#define SLI_CTNS_GSNN_NN 0x0139 +#define SLI_CTNS_GNN_IP 0x0153 +#define SLI_CTNS_GIPA_IP 0x0156 +#define SLI_CTNS_GID_FT 0x0171 +#define SLI_CTNS_GID_PT 0x01A1 +#define SLI_CTNS_RPN_ID 0x0212 +#define SLI_CTNS_RNN_ID 0x0213 +#define SLI_CTNS_RCS_ID 0x0214 +#define SLI_CTNS_RFT_ID 0x0217 +#define SLI_CTNS_RSPN_ID 0x0218 +#define SLI_CTNS_RPT_ID 0x021A +#define SLI_CTNS_RIP_NN 0x0235 +#define SLI_CTNS_RIPA_NN 0x0236 +#define SLI_CTNS_RSNN_NN 0x0239 +#define SLI_CTNS_DA_ID 0x0300 + +/* + * Port Types + */ + +#define SLI_CTPT_N_PORT 0x01 +#define SLI_CTPT_NL_PORT 0x02 +#define SLI_CTPT_FNL_PORT 0x03 +#define SLI_CTPT_IP 0x04 +#define SLI_CTPT_FCP 0x08 +#define SLI_CTPT_NX_PORT 0x7F +#define SLI_CTPT_F_PORT 0x81 +#define SLI_CTPT_FL_PORT 0x82 +#define SLI_CTPT_E_PORT 0x84 + +#define SLI_CT_LAST_ENTRY 0x80000000 + +/* Fibre Channel Service Parameter definitions */ + +#define FC_PH_4_0 6 /* FC-PH version 4.0 */ +#define FC_PH_4_1 7 /* FC-PH version 4.1 */ +#define FC_PH_4_2 8 /* FC-PH version 4.2 */ +#define FC_PH_4_3 9 /* FC-PH version 4.3 */ + +#define FC_PH_LOW 8 /* Lowest supported FC-PH version */ +#define FC_PH_HIGH 9 /* Highest supported FC-PH version */ +#define FC_PH3 0x20 /* FC-PH-3 version */ + +#define FF_FRAME_SIZE 2048 + +struct lpfc_name { +#ifdef __BIG_ENDIAN_BITFIELD + uint8_t nameType:4; /* FC Word 0, bit 28:31 */ + uint8_t IEEEextMsn:4; /* FC Word 0, bit 24:27, bit 8:11 of IEEE ext */ +#else /* __LITTLE_ENDIAN_BITFIELD */ + uint8_t IEEEextMsn:4; /* FC Word 0, bit 24:27, bit 8:11 of IEEE ext */ + uint8_t nameType:4; /* FC Word 0, bit 28:31 */ +#endif + +#define NAME_IEEE 0x1 /* IEEE name - nameType */ +#define NAME_IEEE_EXT 0x2 /* IEEE extended name */ +#define NAME_FC_TYPE 0x3 /* FC native name type */ +#define NAME_IP_TYPE 0x4 /* IP address */ +#define NAME_CCITT_TYPE 0xC +#define NAME_CCITT_GR_TYPE 0xE + uint8_t IEEEextLsb; /* FC Word 0, bit 16:23, IEEE extended Lsb */ + uint8_t IEEE[6]; /* FC IEEE address */ +}; + +struct csp { + uint8_t fcphHigh; /* FC Word 0, byte 0 */ + uint8_t fcphLow; + uint8_t bbCreditMsb; + uint8_t bbCreditlsb; /* FC Word 0, byte 3 */ + +#ifdef __BIG_ENDIAN_BITFIELD + uint16_t increasingOffset:1; /* FC Word 1, bit 31 */ + uint16_t randomOffset:1; /* FC Word 1, bit 30 */ + uint16_t word1Reserved2:1; /* FC Word 1, bit 29 */ + uint16_t fPort:1; /* FC Word 1, bit 28 */ + uint16_t altBbCredit:1; /* FC Word 1, bit 27 */ + uint16_t edtovResolution:1; /* FC Word 1, bit 26 */ + uint16_t multicast:1; /* FC Word 1, bit 25 */ + uint16_t broadcast:1; /* FC Word 1, bit 24 */ + + uint16_t huntgroup:1; /* FC Word 1, bit 23 */ + uint16_t simplex:1; /* FC Word 1, bit 22 */ + uint16_t word1Reserved1:3; /* FC Word 1, bit 21:19 */ + uint16_t dhd:1; /* FC Word 1, bit 18 */ + uint16_t contIncSeqCnt:1; /* FC Word 1, bit 17 */ + uint16_t payloadlength:1; /* FC Word 1, bit 16 */ +#else /* __LITTLE_ENDIAN_BITFIELD */ + uint16_t broadcast:1; /* FC Word 1, bit 24 */ + uint16_t multicast:1; /* FC Word 1, bit 25 */ + uint16_t edtovResolution:1; /* FC Word 1, bit 26 */ + uint16_t altBbCredit:1; /* FC Word 1, bit 27 */ + uint16_t fPort:1; /* FC Word 1, bit 28 */ + uint16_t word1Reserved2:1; /* FC Word 1, bit 29 */ + uint16_t randomOffset:1; /* FC Word 1, bit 30 */ + uint16_t increasingOffset:1; /* FC Word 1, bit 31 */ + + uint16_t payloadlength:1; /* FC Word 1, bit 16 */ + uint16_t contIncSeqCnt:1; /* FC Word 1, bit 17 */ + uint16_t dhd:1; /* FC Word 1, bit 18 */ + uint16_t word1Reserved1:3; /* FC Word 1, bit 21:19 */ + uint16_t simplex:1; /* FC Word 1, bit 22 */ + uint16_t huntgroup:1; /* FC Word 1, bit 23 */ +#endif + + uint8_t bbRcvSizeMsb; /* Upper nibble is reserved */ + uint8_t bbRcvSizeLsb; /* FC Word 1, byte 3 */ + union { + struct { + uint8_t word2Reserved1; /* FC Word 2 byte 0 */ + + uint8_t totalConcurrSeq; /* FC Word 2 byte 1 */ + uint8_t roByCategoryMsb; /* FC Word 2 byte 2 */ + + uint8_t roByCategoryLsb; /* FC Word 2 byte 3 */ + } nPort; + uint32_t r_a_tov; /* R_A_TOV must be in B.E. format */ + } w2; + + uint32_t e_d_tov; /* E_D_TOV must be in B.E. format */ +}; + +struct class_parms { +#ifdef __BIG_ENDIAN_BITFIELD + uint8_t classValid:1; /* FC Word 0, bit 31 */ + uint8_t intermix:1; /* FC Word 0, bit 30 */ + uint8_t stackedXparent:1; /* FC Word 0, bit 29 */ + uint8_t stackedLockDown:1; /* FC Word 0, bit 28 */ + uint8_t seqDelivery:1; /* FC Word 0, bit 27 */ + uint8_t word0Reserved1:3; /* FC Word 0, bit 24:26 */ +#else /* __LITTLE_ENDIAN_BITFIELD */ + uint8_t word0Reserved1:3; /* FC Word 0, bit 24:26 */ + uint8_t seqDelivery:1; /* FC Word 0, bit 27 */ + uint8_t stackedLockDown:1; /* FC Word 0, bit 28 */ + uint8_t stackedXparent:1; /* FC Word 0, bit 29 */ + uint8_t intermix:1; /* FC Word 0, bit 30 */ + uint8_t classValid:1; /* FC Word 0, bit 31 */ + +#endif + + uint8_t word0Reserved2; /* FC Word 0, bit 16:23 */ + +#ifdef __BIG_ENDIAN_BITFIELD + uint8_t iCtlXidReAssgn:2; /* FC Word 0, Bit 14:15 */ + uint8_t iCtlInitialPa:2; /* FC Word 0, bit 12:13 */ + uint8_t iCtlAck0capable:1; /* FC Word 0, bit 11 */ + uint8_t iCtlAckNcapable:1; /* FC Word 0, bit 10 */ + uint8_t word0Reserved3:2; /* FC Word 0, bit 8: 9 */ +#else /* __LITTLE_ENDIAN_BITFIELD */ + uint8_t word0Reserved3:2; /* FC Word 0, bit 8: 9 */ + uint8_t iCtlAckNcapable:1; /* FC Word 0, bit 10 */ + uint8_t iCtlAck0capable:1; /* FC Word 0, bit 11 */ + uint8_t iCtlInitialPa:2; /* FC Word 0, bit 12:13 */ + uint8_t iCtlXidReAssgn:2; /* FC Word 0, Bit 14:15 */ +#endif + + uint8_t word0Reserved4; /* FC Word 0, bit 0: 7 */ + +#ifdef __BIG_ENDIAN_BITFIELD + uint8_t rCtlAck0capable:1; /* FC Word 1, bit 31 */ + uint8_t rCtlAckNcapable:1; /* FC Word 1, bit 30 */ + uint8_t rCtlXidInterlck:1; /* FC Word 1, bit 29 */ + uint8_t rCtlErrorPolicy:2; /* FC Word 1, bit 27:28 */ + uint8_t word1Reserved1:1; /* FC Word 1, bit 26 */ + uint8_t rCtlCatPerSeq:2; /* FC Word 1, bit 24:25 */ +#else /* __LITTLE_ENDIAN_BITFIELD */ + uint8_t rCtlCatPerSeq:2; /* FC Word 1, bit 24:25 */ + uint8_t word1Reserved1:1; /* FC Word 1, bit 26 */ + uint8_t rCtlErrorPolicy:2; /* FC Word 1, bit 27:28 */ + uint8_t rCtlXidInterlck:1; /* FC Word 1, bit 29 */ + uint8_t rCtlAckNcapable:1; /* FC Word 1, bit 30 */ + uint8_t rCtlAck0capable:1; /* FC Word 1, bit 31 */ +#endif + + uint8_t word1Reserved2; /* FC Word 1, bit 16:23 */ + uint8_t rcvDataSizeMsb; /* FC Word 1, bit 8:15 */ + uint8_t rcvDataSizeLsb; /* FC Word 1, bit 0: 7 */ + + uint8_t concurrentSeqMsb; /* FC Word 2, bit 24:31 */ + uint8_t concurrentSeqLsb; /* FC Word 2, bit 16:23 */ + uint8_t EeCreditSeqMsb; /* FC Word 2, bit 8:15 */ + uint8_t EeCreditSeqLsb; /* FC Word 2, bit 0: 7 */ + + uint8_t openSeqPerXchgMsb; /* FC Word 3, bit 24:31 */ + uint8_t openSeqPerXchgLsb; /* FC Word 3, bit 16:23 */ + uint8_t word3Reserved1; /* Fc Word 3, bit 8:15 */ + uint8_t word3Reserved2; /* Fc Word 3, bit 0: 7 */ +}; + +struct serv_parm { /* Structure is in Big Endian format */ + struct csp cmn; + struct lpfc_name portName; + struct lpfc_name nodeName; + struct class_parms cls1; + struct class_parms cls2; + struct class_parms cls3; + struct class_parms cls4; + uint8_t vendorVersion[16]; +}; + +/* + * Extended Link Service LS_COMMAND codes (Payload Word 0) + */ +#ifdef __BIG_ENDIAN_BITFIELD +#define ELS_CMD_MASK 0xffff0000 +#define ELS_RSP_MASK 0xff000000 +#define ELS_CMD_LS_RJT 0x01000000 +#define ELS_CMD_ACC 0x02000000 +#define ELS_CMD_PLOGI 0x03000000 +#define ELS_CMD_FLOGI 0x04000000 +#define ELS_CMD_LOGO 0x05000000 +#define ELS_CMD_ABTX 0x06000000 +#define ELS_CMD_RCS 0x07000000 +#define ELS_CMD_RES 0x08000000 +#define ELS_CMD_RSS 0x09000000 +#define ELS_CMD_RSI 0x0A000000 +#define ELS_CMD_ESTS 0x0B000000 +#define ELS_CMD_ESTC 0x0C000000 +#define ELS_CMD_ADVC 0x0D000000 +#define ELS_CMD_RTV 0x0E000000 +#define ELS_CMD_RLS 0x0F000000 +#define ELS_CMD_ECHO 0x10000000 +#define ELS_CMD_TEST 0x11000000 +#define ELS_CMD_RRQ 0x12000000 +#define ELS_CMD_PRLI 0x20100014 +#define ELS_CMD_PRLO 0x21100014 +#define ELS_CMD_PDISC 0x50000000 +#define ELS_CMD_FDISC 0x51000000 +#define ELS_CMD_ADISC 0x52000000 +#define ELS_CMD_FARP 0x54000000 +#define ELS_CMD_FARPR 0x55000000 +#define ELS_CMD_FAN 0x60000000 +#define ELS_CMD_RSCN 0x61040000 +#define ELS_CMD_SCR 0x62000000 +#define ELS_CMD_RNID 0x78000000 +#else /* __LITTLE_ENDIAN_BITFIELD */ +#define ELS_CMD_MASK 0xffff +#define ELS_RSP_MASK 0xff +#define ELS_CMD_LS_RJT 0x01 +#define ELS_CMD_ACC 0x02 +#define ELS_CMD_PLOGI 0x03 +#define ELS_CMD_FLOGI 0x04 +#define ELS_CMD_LOGO 0x05 +#define ELS_CMD_ABTX 0x06 +#define ELS_CMD_RCS 0x07 +#define ELS_CMD_RES 0x08 +#define ELS_CMD_RSS 0x09 +#define ELS_CMD_RSI 0x0A +#define ELS_CMD_ESTS 0x0B +#define ELS_CMD_ESTC 0x0C +#define ELS_CMD_ADVC 0x0D +#define ELS_CMD_RTV 0x0E +#define ELS_CMD_RLS 0x0F +#define ELS_CMD_ECHO 0x10 +#define ELS_CMD_TEST 0x11 +#define ELS_CMD_RRQ 0x12 +#define ELS_CMD_PRLI 0x14001020 +#define ELS_CMD_PRLO 0x14001021 +#define ELS_CMD_PDISC 0x50 +#define ELS_CMD_FDISC 0x51 +#define ELS_CMD_ADISC 0x52 +#define ELS_CMD_FARP 0x54 +#define ELS_CMD_FARPR 0x55 +#define ELS_CMD_FAN 0x60 +#define ELS_CMD_RSCN 0x0461 +#define ELS_CMD_SCR 0x62 +#define ELS_CMD_RNID 0x78 +#endif + +/* + * LS_RJT Payload Definition + */ + +struct ls_rjt { /* Structure is in Big Endian format */ + union { + uint32_t lsRjtError; + struct { + uint8_t lsRjtRsvd0; /* FC Word 0, bit 24:31 */ + + uint8_t lsRjtRsnCode; /* FC Word 0, bit 16:23 */ + /* LS_RJT reason codes */ +#define LSRJT_INVALID_CMD 0x01 +#define LSRJT_LOGICAL_ERR 0x03 +#define LSRJT_LOGICAL_BSY 0x05 +#define LSRJT_PROTOCOL_ERR 0x07 +#define LSRJT_UNABLE_TPC 0x09 /* Unable to perform command */ +#define LSRJT_CMD_UNSUPPORTED 0x0B +#define LSRJT_VENDOR_UNIQUE 0xFF /* See Byte 3 */ + + uint8_t lsRjtRsnCodeExp; /* FC Word 0, bit 8:15 */ + /* LS_RJT reason explanation */ +#define LSEXP_NOTHING_MORE 0x00 +#define LSEXP_SPARM_OPTIONS 0x01 +#define LSEXP_SPARM_ICTL 0x03 +#define LSEXP_SPARM_RCTL 0x05 +#define LSEXP_SPARM_RCV_SIZE 0x07 +#define LSEXP_SPARM_CONCUR_SEQ 0x09 +#define LSEXP_SPARM_CREDIT 0x0B +#define LSEXP_INVALID_PNAME 0x0D +#define LSEXP_INVALID_NNAME 0x0E +#define LSEXP_INVALID_CSP 0x0F +#define LSEXP_INVALID_ASSOC_HDR 0x11 +#define LSEXP_ASSOC_HDR_REQ 0x13 +#define LSEXP_INVALID_O_SID 0x15 +#define LSEXP_INVALID_OX_RX 0x17 +#define LSEXP_CMD_IN_PROGRESS 0x19 +#define LSEXP_INVALID_NPORT_ID 0x1F +#define LSEXP_INVALID_SEQ_ID 0x21 +#define LSEXP_INVALID_XCHG 0x23 +#define LSEXP_INACTIVE_XCHG 0x25 +#define LSEXP_RQ_REQUIRED 0x27 +#define LSEXP_OUT_OF_RESOURCE 0x29 +#define LSEXP_CANT_GIVE_DATA 0x2A +#define LSEXP_REQ_UNSUPPORTED 0x2C + uint8_t vendorUnique; /* FC Word 0, bit 0: 7 */ + } b; + } un; +}; + +/* + * N_Port Login (FLOGO/PLOGO Request) Payload Definition + */ + +typedef struct _LOGO { /* Structure is in Big Endian format */ + union { + uint32_t nPortId32; /* Access nPortId as a word */ + struct { + uint8_t word1Reserved1; /* FC Word 1, bit 31:24 */ + uint8_t nPortIdByte0; /* N_port ID bit 16:23 */ + uint8_t nPortIdByte1; /* N_port ID bit 8:15 */ + uint8_t nPortIdByte2; /* N_port ID bit 0: 7 */ + } b; + } un; + struct lpfc_name portName; /* N_port name field */ +} LOGO; + +/* + * FCP Login (PRLI Request / ACC) Payload Definition + */ + +#define PRLX_PAGE_LEN 0x10 +#define TPRLO_PAGE_LEN 0x14 + +typedef struct _PRLI { /* Structure is in Big Endian format */ + uint8_t prliType; /* FC Parm Word 0, bit 24:31 */ + +#define PRLI_FCP_TYPE 0x08 + uint8_t word0Reserved1; /* FC Parm Word 0, bit 16:23 */ + +#ifdef __BIG_ENDIAN_BITFIELD + uint8_t origProcAssocV:1; /* FC Parm Word 0, bit 15 */ + uint8_t respProcAssocV:1; /* FC Parm Word 0, bit 14 */ + uint8_t estabImagePair:1; /* FC Parm Word 0, bit 13 */ + + /* ACC = imagePairEstablished */ + uint8_t word0Reserved2:1; /* FC Parm Word 0, bit 12 */ + uint8_t acceptRspCode:4; /* FC Parm Word 0, bit 8:11, ACC ONLY */ +#else /* __LITTLE_ENDIAN_BITFIELD */ + uint8_t acceptRspCode:4; /* FC Parm Word 0, bit 8:11, ACC ONLY */ + uint8_t word0Reserved2:1; /* FC Parm Word 0, bit 12 */ + uint8_t estabImagePair:1; /* FC Parm Word 0, bit 13 */ + uint8_t respProcAssocV:1; /* FC Parm Word 0, bit 14 */ + uint8_t origProcAssocV:1; /* FC Parm Word 0, bit 15 */ + /* ACC = imagePairEstablished */ +#endif + +#define PRLI_REQ_EXECUTED 0x1 /* acceptRspCode */ +#define PRLI_NO_RESOURCES 0x2 +#define PRLI_INIT_INCOMPLETE 0x3 +#define PRLI_NO_SUCH_PA 0x4 +#define PRLI_PREDEF_CONFIG 0x5 +#define PRLI_PARTIAL_SUCCESS 0x6 +#define PRLI_INVALID_PAGE_CNT 0x7 + uint8_t word0Reserved3; /* FC Parm Word 0, bit 0:7 */ + + uint32_t origProcAssoc; /* FC Parm Word 1, bit 0:31 */ + + uint32_t respProcAssoc; /* FC Parm Word 2, bit 0:31 */ + + uint8_t word3Reserved1; /* FC Parm Word 3, bit 24:31 */ + uint8_t word3Reserved2; /* FC Parm Word 3, bit 16:23 */ + +#ifdef __BIG_ENDIAN_BITFIELD + uint16_t Word3bit15Resved:1; /* FC Parm Word 3, bit 15 */ + uint16_t Word3bit14Resved:1; /* FC Parm Word 3, bit 14 */ + uint16_t Word3bit13Resved:1; /* FC Parm Word 3, bit 13 */ + uint16_t Word3bit12Resved:1; /* FC Parm Word 3, bit 12 */ + uint16_t Word3bit11Resved:1; /* FC Parm Word 3, bit 11 */ + uint16_t Word3bit10Resved:1; /* FC Parm Word 3, bit 10 */ + uint16_t TaskRetryIdReq:1; /* FC Parm Word 3, bit 9 */ + uint16_t Retry:1; /* FC Parm Word 3, bit 8 */ + uint16_t ConfmComplAllowed:1; /* FC Parm Word 3, bit 7 */ + uint16_t dataOverLay:1; /* FC Parm Word 3, bit 6 */ + uint16_t initiatorFunc:1; /* FC Parm Word 3, bit 5 */ + uint16_t targetFunc:1; /* FC Parm Word 3, bit 4 */ + uint16_t cmdDataMixEna:1; /* FC Parm Word 3, bit 3 */ + uint16_t dataRspMixEna:1; /* FC Parm Word 3, bit 2 */ + uint16_t readXferRdyDis:1; /* FC Parm Word 3, bit 1 */ + uint16_t writeXferRdyDis:1; /* FC Parm Word 3, bit 0 */ +#else /* __LITTLE_ENDIAN_BITFIELD */ + uint16_t Retry:1; /* FC Parm Word 3, bit 8 */ + uint16_t TaskRetryIdReq:1; /* FC Parm Word 3, bit 9 */ + uint16_t Word3bit10Resved:1; /* FC Parm Word 3, bit 10 */ + uint16_t Word3bit11Resved:1; /* FC Parm Word 3, bit 11 */ + uint16_t Word3bit12Resved:1; /* FC Parm Word 3, bit 12 */ + uint16_t Word3bit13Resved:1; /* FC Parm Word 3, bit 13 */ + uint16_t Word3bit14Resved:1; /* FC Parm Word 3, bit 14 */ + uint16_t Word3bit15Resved:1; /* FC Parm Word 3, bit 15 */ + uint16_t writeXferRdyDis:1; /* FC Parm Word 3, bit 0 */ + uint16_t readXferRdyDis:1; /* FC Parm Word 3, bit 1 */ + uint16_t dataRspMixEna:1; /* FC Parm Word 3, bit 2 */ + uint16_t cmdDataMixEna:1; /* FC Parm Word 3, bit 3 */ + uint16_t targetFunc:1; /* FC Parm Word 3, bit 4 */ + uint16_t initiatorFunc:1; /* FC Parm Word 3, bit 5 */ + uint16_t dataOverLay:1; /* FC Parm Word 3, bit 6 */ + uint16_t ConfmComplAllowed:1; /* FC Parm Word 3, bit 7 */ +#endif +} PRLI; + +/* + * FCP Logout (PRLO Request / ACC) Payload Definition + */ + +typedef struct _PRLO { /* Structure is in Big Endian format */ + uint8_t prloType; /* FC Parm Word 0, bit 24:31 */ + +#define PRLO_FCP_TYPE 0x08 + uint8_t word0Reserved1; /* FC Parm Word 0, bit 16:23 */ + +#ifdef __BIG_ENDIAN_BITFIELD + uint8_t origProcAssocV:1; /* FC Parm Word 0, bit 15 */ + uint8_t respProcAssocV:1; /* FC Parm Word 0, bit 14 */ + uint8_t word0Reserved2:2; /* FC Parm Word 0, bit 12:13 */ + uint8_t acceptRspCode:4; /* FC Parm Word 0, bit 8:11, ACC ONLY */ +#else /* __LITTLE_ENDIAN_BITFIELD */ + uint8_t acceptRspCode:4; /* FC Parm Word 0, bit 8:11, ACC ONLY */ + uint8_t word0Reserved2:2; /* FC Parm Word 0, bit 12:13 */ + uint8_t respProcAssocV:1; /* FC Parm Word 0, bit 14 */ + uint8_t origProcAssocV:1; /* FC Parm Word 0, bit 15 */ +#endif + +#define PRLO_REQ_EXECUTED 0x1 /* acceptRspCode */ +#define PRLO_NO_SUCH_IMAGE 0x4 +#define PRLO_INVALID_PAGE_CNT 0x7 + + uint8_t word0Reserved3; /* FC Parm Word 0, bit 0:7 */ + + uint32_t origProcAssoc; /* FC Parm Word 1, bit 0:31 */ + + uint32_t respProcAssoc; /* FC Parm Word 2, bit 0:31 */ + + uint32_t word3Reserved1; /* FC Parm Word 3, bit 0:31 */ +} PRLO; + +typedef struct _ADISC { /* Structure is in Big Endian format */ + uint32_t hardAL_PA; + struct lpfc_name portName; + struct lpfc_name nodeName; + uint32_t DID; +} ADISC; + +typedef struct _FARP { /* Structure is in Big Endian format */ + uint32_t Mflags:8; + uint32_t Odid:24; +#define FARP_NO_ACTION 0 /* FARP information enclosed, no + action */ +#define FARP_MATCH_PORT 0x1 /* Match on Responder Port Name */ +#define FARP_MATCH_NODE 0x2 /* Match on Responder Node Name */ +#define FARP_MATCH_IP 0x4 /* Match on IP address, not supported */ +#define FARP_MATCH_IPV4 0x5 /* Match on IPV4 address, not + supported */ +#define FARP_MATCH_IPV6 0x6 /* Match on IPV6 address, not + supported */ + uint32_t Rflags:8; + uint32_t Rdid:24; +#define FARP_REQUEST_PLOGI 0x1 /* Request for PLOGI */ +#define FARP_REQUEST_FARPR 0x2 /* Request for FARP Response */ + struct lpfc_name OportName; + struct lpfc_name OnodeName; + struct lpfc_name RportName; + struct lpfc_name RnodeName; + uint8_t Oipaddr[16]; + uint8_t Ripaddr[16]; +} FARP; + +typedef struct _FAN { /* Structure is in Big Endian format */ + uint32_t Fdid; + struct lpfc_name FportName; + struct lpfc_name FnodeName; +} FAN; + +typedef struct _SCR { /* Structure is in Big Endian format */ + uint8_t resvd1; + uint8_t resvd2; + uint8_t resvd3; + uint8_t Function; +#define SCR_FUNC_FABRIC 0x01 +#define SCR_FUNC_NPORT 0x02 +#define SCR_FUNC_FULL 0x03 +#define SCR_CLEAR 0xff +} SCR; + +typedef struct _RNID_TOP_DISC { + struct lpfc_name portName; + uint8_t resvd[8]; + uint32_t unitType; +#define RNID_HBA 0x7 +#define RNID_HOST 0xa +#define RNID_DRIVER 0xd + uint32_t physPort; + uint32_t attachedNodes; + uint16_t ipVersion; +#define RNID_IPV4 0x1 +#define RNID_IPV6 0x2 + uint16_t UDPport; + uint8_t ipAddr[16]; + uint16_t resvd1; + uint16_t flags; +#define RNID_TD_SUPPORT 0x1 +#define RNID_LP_VALID 0x2 +} RNID_TOP_DISC; + +typedef struct _RNID { /* Structure is in Big Endian format */ + uint8_t Format; +#define RNID_TOPOLOGY_DISC 0xdf + uint8_t CommonLen; + uint8_t resvd1; + uint8_t SpecificLen; + struct lpfc_name portName; + struct lpfc_name nodeName; + union { + RNID_TOP_DISC topologyDisc; /* topology disc (0xdf) */ + } un; +} RNID; + +typedef struct _RRQ { /* Structure is in Big Endian format */ + uint32_t SID; + uint16_t Oxid; + uint16_t Rxid; + uint8_t resv[32]; /* optional association hdr */ +} RRQ; + +/* This is used for RSCN command */ +typedef struct _D_ID { /* Structure is in Big Endian format */ + union { + uint32_t word; + struct { +#ifdef __BIG_ENDIAN_BITFIELD + uint8_t resv; + uint8_t domain; + uint8_t area; + uint8_t id; +#else /* __LITTLE_ENDIAN_BITFIELD */ + uint8_t id; + uint8_t area; + uint8_t domain; + uint8_t resv; +#endif + } b; + } un; +} D_ID; + +/* + * Structure to define all ELS Payload types + */ + +typedef struct _ELS_PKT { /* Structure is in Big Endian format */ + uint8_t elsCode; /* FC Word 0, bit 24:31 */ + uint8_t elsByte1; + uint8_t elsByte2; + uint8_t elsByte3; + union { + struct ls_rjt lsRjt; /* Payload for LS_RJT ELS response */ + struct serv_parm logi; /* Payload for PLOGI/FLOGI/PDISC/ACC */ + LOGO logo; /* Payload for PLOGO/FLOGO/ACC */ + PRLI prli; /* Payload for PRLI/ACC */ + PRLO prlo; /* Payload for PRLO/ACC */ + ADISC adisc; /* Payload for ADISC/ACC */ + FARP farp; /* Payload for FARP/ACC */ + FAN fan; /* Payload for FAN */ + SCR scr; /* Payload for SCR/ACC */ + RRQ rrq; /* Payload for RRQ */ + RNID rnid; /* Payload for RNID */ + uint8_t pad[128 - 4]; /* Pad out to payload of 128 bytes */ + } un; +} ELS_PKT; + +/* + * FDMI + * HBA MAnagement Operations Command Codes + */ +#define SLI_MGMT_GRHL 0x100 /* Get registered HBA list */ +#define SLI_MGMT_GHAT 0x101 /* Get HBA attributes */ +#define SLI_MGMT_GRPL 0x102 /* Get registered Port list */ +#define SLI_MGMT_GPAT 0x110 /* Get Port attributes */ +#define SLI_MGMT_RHBA 0x200 /* Register HBA */ +#define SLI_MGMT_RHAT 0x201 /* Register HBA atttributes */ +#define SLI_MGMT_RPRT 0x210 /* Register Port */ +#define SLI_MGMT_RPA 0x211 /* Register Port attributes */ +#define SLI_MGMT_DHBA 0x300 /* De-register HBA */ +#define SLI_MGMT_DPRT 0x310 /* De-register Port */ + +/* + * Management Service Subtypes + */ +#define SLI_CT_FDMI_Subtypes 0x10 + +/* + * HBA Management Service Reject Code + */ +#define REJECT_CODE 0x9 /* Unable to perform command request */ + +/* + * HBA Management Service Reject Reason Code + * Please refer to the Reason Codes above + */ + +/* + * HBA Attribute Types + */ +#define NODE_NAME 0x1 +#define MANUFACTURER 0x2 +#define SERIAL_NUMBER 0x3 +#define MODEL 0x4 +#define MODEL_DESCRIPTION 0x5 +#define HARDWARE_VERSION 0x6 +#define DRIVER_VERSION 0x7 +#define OPTION_ROM_VERSION 0x8 +#define FIRMWARE_VERSION 0x9 +#define OS_NAME_VERSION 0xa +#define MAX_CT_PAYLOAD_LEN 0xb + +/* + * Port Attrubute Types + */ +#define SUPPORTED_FC4_TYPES 0x1 +#define SUPPORTED_SPEED 0x2 +#define PORT_SPEED 0x3 +#define MAX_FRAME_SIZE 0x4 +#define OS_DEVICE_NAME 0x5 +#define HOST_NAME 0x6 + +union AttributesDef { + /* Structure is in Big Endian format */ + struct { + uint32_t AttrType:16; + uint32_t AttrLen:16; + } bits; + uint32_t word; +}; + + +/* + * HBA Attribute Entry (8 - 260 bytes) + */ +typedef struct { + union AttributesDef ad; + union { + uint32_t VendorSpecific; + uint8_t Manufacturer[64]; + uint8_t SerialNumber[64]; + uint8_t Model[256]; + uint8_t ModelDescription[256]; + uint8_t HardwareVersion[256]; + uint8_t DriverVersion[256]; + uint8_t OptionROMVersion[256]; + uint8_t FirmwareVersion[256]; + struct lpfc_name NodeName; + uint8_t SupportFC4Types[32]; + uint32_t SupportSpeed; + uint32_t PortSpeed; + uint32_t MaxFrameSize; + uint8_t OsDeviceName[256]; + uint8_t OsNameVersion[256]; + uint32_t MaxCTPayloadLen; + uint8_t HostName[256]; + } un; +} ATTRIBUTE_ENTRY; + +/* + * HBA Attribute Block + */ +typedef struct { + uint32_t EntryCnt; /* Number of HBA attribute entries */ + ATTRIBUTE_ENTRY Entry; /* Variable-length array */ +} ATTRIBUTE_BLOCK; + +/* + * Port Entry + */ +typedef struct { + struct lpfc_name PortName; +} PORT_ENTRY; + +/* + * HBA Identifier + */ +typedef struct { + struct lpfc_name PortName; +} HBA_IDENTIFIER; + +/* + * Registered Port List Format + */ +typedef struct { + uint32_t EntryCnt; + PORT_ENTRY pe; /* Variable-length array */ +} REG_PORT_LIST; + +/* + * Register HBA(RHBA) + */ +typedef struct { + HBA_IDENTIFIER hi; + REG_PORT_LIST rpl; /* variable-length array */ +/* ATTRIBUTE_BLOCK ab; */ +} REG_HBA; + +/* + * Register HBA Attributes (RHAT) + */ +typedef struct { + struct lpfc_name HBA_PortName; + ATTRIBUTE_BLOCK ab; +} REG_HBA_ATTRIBUTE; + +/* + * Register Port Attributes (RPA) + */ +typedef struct { + struct lpfc_name PortName; + ATTRIBUTE_BLOCK ab; +} REG_PORT_ATTRIBUTE; + +/* + * Get Registered HBA List (GRHL) Accept Payload Format + */ +typedef struct { + uint32_t HBA__Entry_Cnt; /* Number of Registered HBA Identifiers */ + struct lpfc_name HBA_PortName; /* Variable-length array */ +} GRHL_ACC_PAYLOAD; + +/* + * Get Registered Port List (GRPL) Accept Payload Format + */ +typedef struct { + uint32_t RPL_Entry_Cnt; /* Number of Registered Port Entries */ + PORT_ENTRY Reg_Port_Entry[1]; /* Variable-length array */ +} GRPL_ACC_PAYLOAD; + +/* + * Get Port Attributes (GPAT) Accept Payload Format + */ + +typedef struct { + ATTRIBUTE_BLOCK pab; +} GPAT_ACC_PAYLOAD; + + +/* + * Begin HBA configuration parameters. + * The PCI configuration register BAR assignments are: + * BAR0, offset 0x10 - SLIM base memory address + * BAR1, offset 0x14 - SLIM base memory high address + * BAR2, offset 0x18 - REGISTER base memory address + * BAR3, offset 0x1c - REGISTER base memory high address + * BAR4, offset 0x20 - BIU I/O registers + * BAR5, offset 0x24 - REGISTER base io high address + */ + +/* Number of rings currently used and available. */ +#define MAX_CONFIGURED_RINGS 3 +#define MAX_RINGS 4 + +/* IOCB / Mailbox is owned by FireFly */ +#define OWN_CHIP 1 + +/* IOCB / Mailbox is owned by Host */ +#define OWN_HOST 0 + +/* Number of 4-byte words in an IOCB. */ +#define IOCB_WORD_SZ 8 + +/* defines for type field in fc header */ +#define FC_ELS_DATA 0x1 +#define FC_LLC_SNAP 0x5 +#define FC_FCP_DATA 0x8 +#define FC_COMMON_TRANSPORT_ULP 0x20 + +/* defines for rctl field in fc header */ +#define FC_DEV_DATA 0x0 +#define FC_UNSOL_CTL 0x2 +#define FC_SOL_CTL 0x3 +#define FC_UNSOL_DATA 0x4 +#define FC_FCP_CMND 0x6 +#define FC_ELS_REQ 0x22 +#define FC_ELS_RSP 0x23 + +/* network headers for Dfctl field */ +#define FC_NET_HDR 0x20 + +/* Start FireFly Register definitions */ +#define PCI_VENDOR_ID_EMULEX 0x10df +#define PCI_DEVICE_ID_FIREFLY 0x1ae5 +#define PCI_DEVICE_ID_SUPERFLY 0xf700 +#define PCI_DEVICE_ID_DRAGONFLY 0xf800 +#define PCI_DEVICE_ID_RFLY 0xf095 +#define PCI_DEVICE_ID_PFLY 0xf098 +#define PCI_DEVICE_ID_TFLY 0xf0a5 +#define PCI_DEVICE_ID_CENTAUR 0xf900 +#define PCI_DEVICE_ID_PEGASUS 0xf980 +#define PCI_DEVICE_ID_THOR 0xfa00 +#define PCI_DEVICE_ID_VIPER 0xfb00 +#define PCI_DEVICE_ID_HELIOS 0xfd00 +#define PCI_DEVICE_ID_BMID 0xf0d5 +#define PCI_DEVICE_ID_BSMB 0xf0d1 +#define PCI_DEVICE_ID_ZEPHYR 0xfe00 +#define PCI_DEVICE_ID_ZMID 0xf0e5 +#define PCI_DEVICE_ID_ZSMB 0xf0e1 +#define PCI_DEVICE_ID_LP101 0xf0a1 + +#define JEDEC_ID_ADDRESS 0x0080001c +#define FIREFLY_JEDEC_ID 0x1ACC +#define SUPERFLY_JEDEC_ID 0x0020 +#define DRAGONFLY_JEDEC_ID 0x0021 +#define DRAGONFLY_V2_JEDEC_ID 0x0025 +#define CENTAUR_2G_JEDEC_ID 0x0026 +#define CENTAUR_1G_JEDEC_ID 0x0028 +#define PEGASUS_ORION_JEDEC_ID 0x0036 +#define PEGASUS_JEDEC_ID 0x0038 +#define THOR_JEDEC_ID 0x0012 +#define HELIOS_JEDEC_ID 0x0364 +#define ZEPHYR_JEDEC_ID 0x0577 +#define VIPER_JEDEC_ID 0x4838 + +#define JEDEC_ID_MASK 0x0FFFF000 +#define JEDEC_ID_SHIFT 12 +#define FC_JEDEC_ID(id) ((id & JEDEC_ID_MASK) >> JEDEC_ID_SHIFT) + +typedef struct { /* FireFly BIU registers */ + uint32_t hostAtt; /* See definitions for Host Attention + register */ + uint32_t chipAtt; /* See definitions for Chip Attention + register */ + uint32_t hostStatus; /* See definitions for Host Status register */ + uint32_t hostControl; /* See definitions for Host Control register */ + uint32_t buiConfig; /* See definitions for BIU configuration + register */ +} FF_REGS; + +/* IO Register size in bytes */ +#define FF_REG_AREA_SIZE 256 + +/* Host Attention Register */ + +#define HA_REG_OFFSET 0 /* Word offset from register base address */ + +#define HA_R0RE_REQ 0x00000001 /* Bit 0 */ +#define HA_R0CE_RSP 0x00000002 /* Bit 1 */ +#define HA_R0ATT 0x00000008 /* Bit 3 */ +#define HA_R1RE_REQ 0x00000010 /* Bit 4 */ +#define HA_R1CE_RSP 0x00000020 /* Bit 5 */ +#define HA_R1ATT 0x00000080 /* Bit 7 */ +#define HA_R2RE_REQ 0x00000100 /* Bit 8 */ +#define HA_R2CE_RSP 0x00000200 /* Bit 9 */ +#define HA_R2ATT 0x00000800 /* Bit 11 */ +#define HA_R3RE_REQ 0x00001000 /* Bit 12 */ +#define HA_R3CE_RSP 0x00002000 /* Bit 13 */ +#define HA_R3ATT 0x00008000 /* Bit 15 */ +#define HA_LATT 0x20000000 /* Bit 29 */ +#define HA_MBATT 0x40000000 /* Bit 30 */ +#define HA_ERATT 0x80000000 /* Bit 31 */ + +#define HA_RXRE_REQ 0x00000001 /* Bit 0 */ +#define HA_RXCE_RSP 0x00000002 /* Bit 1 */ +#define HA_RXATT 0x00000008 /* Bit 3 */ +#define HA_RXMASK 0x0000000f + +/* Chip Attention Register */ + +#define CA_REG_OFFSET 1 /* Word offset from register base address */ + +#define CA_R0CE_REQ 0x00000001 /* Bit 0 */ +#define CA_R0RE_RSP 0x00000002 /* Bit 1 */ +#define CA_R0ATT 0x00000008 /* Bit 3 */ +#define CA_R1CE_REQ 0x00000010 /* Bit 4 */ +#define CA_R1RE_RSP 0x00000020 /* Bit 5 */ +#define CA_R1ATT 0x00000080 /* Bit 7 */ +#define CA_R2CE_REQ 0x00000100 /* Bit 8 */ +#define CA_R2RE_RSP 0x00000200 /* Bit 9 */ +#define CA_R2ATT 0x00000800 /* Bit 11 */ +#define CA_R3CE_REQ 0x00001000 /* Bit 12 */ +#define CA_R3RE_RSP 0x00002000 /* Bit 13 */ +#define CA_R3ATT 0x00008000 /* Bit 15 */ +#define CA_MBATT 0x40000000 /* Bit 30 */ + +/* Host Status Register */ + +#define HS_REG_OFFSET 2 /* Word offset from register base address */ + +#define HS_MBRDY 0x00400000 /* Bit 22 */ +#define HS_FFRDY 0x00800000 /* Bit 23 */ +#define HS_FFER8 0x01000000 /* Bit 24 */ +#define HS_FFER7 0x02000000 /* Bit 25 */ +#define HS_FFER6 0x04000000 /* Bit 26 */ +#define HS_FFER5 0x08000000 /* Bit 27 */ +#define HS_FFER4 0x10000000 /* Bit 28 */ +#define HS_FFER3 0x20000000 /* Bit 29 */ +#define HS_FFER2 0x40000000 /* Bit 30 */ +#define HS_FFER1 0x80000000 /* Bit 31 */ +#define HS_FFERM 0xFF000000 /* Mask for error bits 31:24 */ + +/* Host Control Register */ + +#define HC_REG_OFFSET 3 /* Word offset from register base address */ + +#define HC_MBINT_ENA 0x00000001 /* Bit 0 */ +#define HC_R0INT_ENA 0x00000002 /* Bit 1 */ +#define HC_R1INT_ENA 0x00000004 /* Bit 2 */ +#define HC_R2INT_ENA 0x00000008 /* Bit 3 */ +#define HC_R3INT_ENA 0x00000010 /* Bit 4 */ +#define HC_INITHBI 0x02000000 /* Bit 25 */ +#define HC_INITMB 0x04000000 /* Bit 26 */ +#define HC_INITFF 0x08000000 /* Bit 27 */ +#define HC_LAINT_ENA 0x20000000 /* Bit 29 */ +#define HC_ERINT_ENA 0x80000000 /* Bit 31 */ + +/* Mailbox Commands */ +#define MBX_SHUTDOWN 0x00 /* terminate testing */ +#define MBX_LOAD_SM 0x01 +#define MBX_READ_NV 0x02 +#define MBX_WRITE_NV 0x03 +#define MBX_RUN_BIU_DIAG 0x04 +#define MBX_INIT_LINK 0x05 +#define MBX_DOWN_LINK 0x06 +#define MBX_CONFIG_LINK 0x07 +#define MBX_CONFIG_RING 0x09 +#define MBX_RESET_RING 0x0A +#define MBX_READ_CONFIG 0x0B +#define MBX_READ_RCONFIG 0x0C +#define MBX_READ_SPARM 0x0D +#define MBX_READ_STATUS 0x0E +#define MBX_READ_RPI 0x0F +#define MBX_READ_XRI 0x10 +#define MBX_READ_REV 0x11 +#define MBX_READ_LNK_STAT 0x12 +#define MBX_REG_LOGIN 0x13 +#define MBX_UNREG_LOGIN 0x14 +#define MBX_READ_LA 0x15 +#define MBX_CLEAR_LA 0x16 +#define MBX_DUMP_MEMORY 0x17 +#define MBX_DUMP_CONTEXT 0x18 +#define MBX_RUN_DIAGS 0x19 +#define MBX_RESTART 0x1A +#define MBX_UPDATE_CFG 0x1B +#define MBX_DOWN_LOAD 0x1C +#define MBX_DEL_LD_ENTRY 0x1D +#define MBX_RUN_PROGRAM 0x1E +#define MBX_SET_MASK 0x20 +#define MBX_SET_SLIM 0x21 +#define MBX_UNREG_D_ID 0x23 +#define MBX_CONFIG_FARP 0x25 + +#define MBX_LOAD_AREA 0x81 +#define MBX_RUN_BIU_DIAG64 0x84 +#define MBX_CONFIG_PORT 0x88 +#define MBX_READ_SPARM64 0x8D +#define MBX_READ_RPI64 0x8F +#define MBX_REG_LOGIN64 0x93 +#define MBX_READ_LA64 0x95 + +#define MBX_FLASH_WR_ULA 0x98 +#define MBX_SET_DEBUG 0x99 +#define MBX_LOAD_EXP_ROM 0x9C + +#define MBX_MAX_CMDS 0x9D +#define MBX_SLI2_CMD_MASK 0x80 + +/* IOCB Commands */ + +#define CMD_RCV_SEQUENCE_CX 0x01 +#define CMD_XMIT_SEQUENCE_CR 0x02 +#define CMD_XMIT_SEQUENCE_CX 0x03 +#define CMD_XMIT_BCAST_CN 0x04 +#define CMD_XMIT_BCAST_CX 0x05 +#define CMD_QUE_RING_BUF_CN 0x06 +#define CMD_QUE_XRI_BUF_CX 0x07 +#define CMD_IOCB_CONTINUE_CN 0x08 +#define CMD_RET_XRI_BUF_CX 0x09 +#define CMD_ELS_REQUEST_CR 0x0A +#define CMD_ELS_REQUEST_CX 0x0B +#define CMD_RCV_ELS_REQ_CX 0x0D +#define CMD_ABORT_XRI_CN 0x0E +#define CMD_ABORT_XRI_CX 0x0F +#define CMD_CLOSE_XRI_CN 0x10 +#define CMD_CLOSE_XRI_CX 0x11 +#define CMD_CREATE_XRI_CR 0x12 +#define CMD_CREATE_XRI_CX 0x13 +#define CMD_GET_RPI_CN 0x14 +#define CMD_XMIT_ELS_RSP_CX 0x15 +#define CMD_GET_RPI_CR 0x16 +#define CMD_XRI_ABORTED_CX 0x17 +#define CMD_FCP_IWRITE_CR 0x18 +#define CMD_FCP_IWRITE_CX 0x19 +#define CMD_FCP_IREAD_CR 0x1A +#define CMD_FCP_IREAD_CX 0x1B +#define CMD_FCP_ICMND_CR 0x1C +#define CMD_FCP_ICMND_CX 0x1D + +#define CMD_ADAPTER_MSG 0x20 +#define CMD_ADAPTER_DUMP 0x22 + +/* SLI_2 IOCB Command Set */ + +#define CMD_RCV_SEQUENCE64_CX 0x81 +#define CMD_XMIT_SEQUENCE64_CR 0x82 +#define CMD_XMIT_SEQUENCE64_CX 0x83 +#define CMD_XMIT_BCAST64_CN 0x84 +#define CMD_XMIT_BCAST64_CX 0x85 +#define CMD_QUE_RING_BUF64_CN 0x86 +#define CMD_QUE_XRI_BUF64_CX 0x87 +#define CMD_IOCB_CONTINUE64_CN 0x88 +#define CMD_RET_XRI_BUF64_CX 0x89 +#define CMD_ELS_REQUEST64_CR 0x8A +#define CMD_ELS_REQUEST64_CX 0x8B +#define CMD_ABORT_MXRI64_CN 0x8C +#define CMD_RCV_ELS_REQ64_CX 0x8D +#define CMD_XMIT_ELS_RSP64_CX 0x95 +#define CMD_FCP_IWRITE64_CR 0x98 +#define CMD_FCP_IWRITE64_CX 0x99 +#define CMD_FCP_IREAD64_CR 0x9A +#define CMD_FCP_IREAD64_CX 0x9B +#define CMD_FCP_ICMND64_CR 0x9C +#define CMD_FCP_ICMND64_CX 0x9D + +#define CMD_GEN_REQUEST64_CR 0xC2 +#define CMD_GEN_REQUEST64_CX 0xC3 + +#define CMD_MAX_IOCB_CMD 0xE6 +#define CMD_IOCB_MASK 0xff + +#define MAX_MSG_DATA 28 /* max msg data in CMD_ADAPTER_MSG + iocb */ +#define LPFC_MAX_ADPTMSG 32 /* max msg data */ +/* + * Define Status + */ +#define MBX_SUCCESS 0 +#define MBXERR_NUM_RINGS 1 +#define MBXERR_NUM_IOCBS 2 +#define MBXERR_IOCBS_EXCEEDED 3 +#define MBXERR_BAD_RING_NUMBER 4 +#define MBXERR_MASK_ENTRIES_RANGE 5 +#define MBXERR_MASKS_EXCEEDED 6 +#define MBXERR_BAD_PROFILE 7 +#define MBXERR_BAD_DEF_CLASS 8 +#define MBXERR_BAD_MAX_RESPONDER 9 +#define MBXERR_BAD_MAX_ORIGINATOR 10 +#define MBXERR_RPI_REGISTERED 11 +#define MBXERR_RPI_FULL 12 +#define MBXERR_NO_RESOURCES 13 +#define MBXERR_BAD_RCV_LENGTH 14 +#define MBXERR_DMA_ERROR 15 +#define MBXERR_ERROR 16 +#define MBX_NOT_FINISHED 255 + +#define MBX_BUSY 0xffffff /* Attempted cmd to busy Mailbox */ +#define MBX_TIMEOUT 0xfffffe /* time-out expired waiting for */ + +/* + * Begin Structure Definitions for Mailbox Commands + */ + +typedef struct { +#ifdef __BIG_ENDIAN_BITFIELD + uint8_t tval; + uint8_t tmask; + uint8_t rval; + uint8_t rmask; +#else /* __LITTLE_ENDIAN_BITFIELD */ + uint8_t rmask; + uint8_t rval; + uint8_t tmask; + uint8_t tval; +#endif +} RR_REG; + +struct ulp_bde { + uint32_t bdeAddress; +#ifdef __BIG_ENDIAN_BITFIELD + uint32_t bdeReserved:4; + uint32_t bdeAddrHigh:4; + uint32_t bdeSize:24; +#else /* __LITTLE_ENDIAN_BITFIELD */ + uint32_t bdeSize:24; + uint32_t bdeAddrHigh:4; + uint32_t bdeReserved:4; +#endif +}; + +struct ulp_bde64 { /* SLI-2 */ + union ULP_BDE_TUS { + uint32_t w; + struct { +#ifdef __BIG_ENDIAN_BITFIELD + uint32_t bdeFlags:8; /* BDE Flags 0 IS A SUPPORTED + VALUE !! */ + uint32_t bdeSize:24; /* Size of buffer (in bytes) */ +#else /* __LITTLE_ENDIAN_BITFIELD */ + uint32_t bdeSize:24; /* Size of buffer (in bytes) */ + uint32_t bdeFlags:8; /* BDE Flags 0 IS A SUPPORTED + VALUE !! */ +#endif + +#define BUFF_USE_RSVD 0x01 /* bdeFlags */ +#define BUFF_USE_INTRPT 0x02 /* Not Implemented with LP6000 */ +#define BUFF_USE_CMND 0x04 /* Optional, 1=cmd/rsp 0=data buffer */ +#define BUFF_USE_RCV 0x08 /* "" "", 1=rcv buffer, 0=xmit + buffer */ +#define BUFF_TYPE_32BIT 0x10 /* "" "", 1=32 bit addr 0=64 bit + addr */ +#define BUFF_TYPE_SPECIAL 0x20 /* Not Implemented with LP6000 */ +#define BUFF_TYPE_BDL 0x40 /* Optional, may be set in BDL */ +#define BUFF_TYPE_INVALID 0x80 /* "" "" */ + } f; + } tus; + uint32_t addrLow; + uint32_t addrHigh; +}; +#define BDE64_SIZE_WORD 0 +#define BPL64_SIZE_WORD 0x40 + +typedef struct ULP_BDL { /* SLI-2 */ +#ifdef __BIG_ENDIAN_BITFIELD + uint32_t bdeFlags:8; /* BDL Flags */ + uint32_t bdeSize:24; /* Size of BDL array in host memory (bytes) */ +#else /* __LITTLE_ENDIAN_BITFIELD */ + uint32_t bdeSize:24; /* Size of BDL array in host memory (bytes) */ + uint32_t bdeFlags:8; /* BDL Flags */ +#endif + + uint32_t addrLow; /* Address 0:31 */ + uint32_t addrHigh; /* Address 32:63 */ + uint32_t ulpIoTag32; /* Can be used for 32 bit I/O Tag */ +} ULP_BDL; + +/* Structure for MB Command LOAD_SM and DOWN_LOAD */ + +typedef struct { +#ifdef __BIG_ENDIAN_BITFIELD + uint32_t rsvd2:25; + uint32_t acknowledgment:1; + uint32_t version:1; + uint32_t erase_or_prog:1; + uint32_t update_flash:1; + uint32_t update_ram:1; + uint32_t method:1; + uint32_t load_cmplt:1; +#else /* __LITTLE_ENDIAN_BITFIELD */ + uint32_t load_cmplt:1; + uint32_t method:1; + uint32_t update_ram:1; + uint32_t update_flash:1; + uint32_t erase_or_prog:1; + uint32_t version:1; + uint32_t acknowledgment:1; + uint32_t rsvd2:25; +#endif + + uint32_t dl_to_adr_low; + uint32_t dl_to_adr_high; + uint32_t dl_len; + union { + uint32_t dl_from_mbx_offset; + struct ulp_bde dl_from_bde; + struct ulp_bde64 dl_from_bde64; + } un; + +} LOAD_SM_VAR; + +/* Structure for MB Command READ_NVPARM (02) */ + +typedef struct { + uint32_t rsvd1[3]; /* Read as all one's */ + uint32_t rsvd2; /* Read as all zero's */ + uint32_t portname[2]; /* N_PORT name */ + uint32_t nodename[2]; /* NODE name */ + +#ifdef __BIG_ENDIAN_BITFIELD + uint32_t pref_DID:24; + uint32_t hardAL_PA:8; +#else /* __LITTLE_ENDIAN_BITFIELD */ + uint32_t hardAL_PA:8; + uint32_t pref_DID:24; +#endif + + uint32_t rsvd3[21]; /* Read as all one's */ +} READ_NV_VAR; + +/* Structure for MB Command WRITE_NVPARMS (03) */ + +typedef struct { + uint32_t rsvd1[3]; /* Must be all one's */ + uint32_t rsvd2; /* Must be all zero's */ + uint32_t portname[2]; /* N_PORT name */ + uint32_t nodename[2]; /* NODE name */ + +#ifdef __BIG_ENDIAN_BITFIELD + uint32_t pref_DID:24; + uint32_t hardAL_PA:8; +#else /* __LITTLE_ENDIAN_BITFIELD */ + uint32_t hardAL_PA:8; + uint32_t pref_DID:24; +#endif + + uint32_t rsvd3[21]; /* Must be all one's */ +} WRITE_NV_VAR; + +/* Structure for MB Command RUN_BIU_DIAG (04) */ +/* Structure for MB Command RUN_BIU_DIAG64 (0x84) */ + +typedef struct { + uint32_t rsvd1; + union { + struct { + struct ulp_bde xmit_bde; + struct ulp_bde rcv_bde; + } s1; + struct { + struct ulp_bde64 xmit_bde64; + struct ulp_bde64 rcv_bde64; + } s2; + } un; +} BIU_DIAG_VAR; + +/* Structure for MB Command INIT_LINK (05) */ + +typedef struct { +#ifdef __BIG_ENDIAN_BITFIELD + uint32_t rsvd1:24; + uint32_t lipsr_AL_PA:8; /* AL_PA to issue Lip Selective Reset to */ +#else /* __LITTLE_ENDIAN_BITFIELD */ + uint32_t lipsr_AL_PA:8; /* AL_PA to issue Lip Selective Reset to */ + uint32_t rsvd1:24; +#endif + +#ifdef __BIG_ENDIAN_BITFIELD + uint8_t fabric_AL_PA; /* If using a Fabric Assigned AL_PA */ + uint8_t rsvd2; + uint16_t link_flags; +#else /* __LITTLE_ENDIAN_BITFIELD */ + uint16_t link_flags; + uint8_t rsvd2; + uint8_t fabric_AL_PA; /* If using a Fabric Assigned AL_PA */ +#endif + +#define FLAGS_LOCAL_LB 0x01 /* link_flags (=1) ENDEC loopback */ +#define FLAGS_TOPOLOGY_MODE_LOOP_PT 0x00 /* Attempt loop then pt-pt */ +#define FLAGS_TOPOLOGY_MODE_PT_PT 0x02 /* Attempt pt-pt only */ +#define FLAGS_TOPOLOGY_MODE_LOOP 0x04 /* Attempt loop only */ +#define FLAGS_TOPOLOGY_MODE_PT_LOOP 0x06 /* Attempt pt-pt then loop */ +#define FLAGS_LIRP_LILP 0x80 /* LIRP / LILP is disabled */ + +#define FLAGS_TOPOLOGY_FAILOVER 0x0400 /* Bit 10 */ +#define FLAGS_LINK_SPEED 0x0800 /* Bit 11 */ + + uint32_t link_speed; +#define LINK_SPEED_AUTO 0 /* Auto selection */ +#define LINK_SPEED_1G 1 /* 1 Gigabaud */ +#define LINK_SPEED_2G 2 /* 2 Gigabaud */ +#define LINK_SPEED_4G 4 /* 4 Gigabaud */ +#define LINK_SPEED_8G 8 /* 4 Gigabaud */ +#define LINK_SPEED_10G 16 /* 10 Gigabaud */ + +} INIT_LINK_VAR; + +/* Structure for MB Command DOWN_LINK (06) */ + +typedef struct { + uint32_t rsvd1; +} DOWN_LINK_VAR; + +/* Structure for MB Command CONFIG_LINK (07) */ + +typedef struct { +#ifdef __BIG_ENDIAN_BITFIELD + uint32_t cr:1; + uint32_t ci:1; + uint32_t cr_delay:6; + uint32_t cr_count:8; + uint32_t rsvd1:8; + uint32_t MaxBBC:8; +#else /* __LITTLE_ENDIAN_BITFIELD */ + uint32_t MaxBBC:8; + uint32_t rsvd1:8; + uint32_t cr_count:8; + uint32_t cr_delay:6; + uint32_t ci:1; + uint32_t cr:1; +#endif + + uint32_t myId; + uint32_t rsvd2; + uint32_t edtov; + uint32_t arbtov; + uint32_t ratov; + uint32_t rttov; + uint32_t altov; + uint32_t crtov; + uint32_t citov; +#ifdef __BIG_ENDIAN_BITFIELD + uint32_t rrq_enable:1; + uint32_t rrq_immed:1; + uint32_t rsvd4:29; + uint32_t ack0_enable:1; +#else /* __LITTLE_ENDIAN_BITFIELD */ + uint32_t ack0_enable:1; + uint32_t rsvd4:29; + uint32_t rrq_immed:1; + uint32_t rrq_enable:1; +#endif +} CONFIG_LINK; + +/* Structure for MB Command PART_SLIM (08) + * will be removed since SLI1 is no longer supported! + */ +typedef struct { +#ifdef __BIG_ENDIAN_BITFIELD + uint16_t offCiocb; + uint16_t numCiocb; + uint16_t offRiocb; + uint16_t numRiocb; +#else /* __LITTLE_ENDIAN_BITFIELD */ + uint16_t numCiocb; + uint16_t offCiocb; + uint16_t numRiocb; + uint16_t offRiocb; +#endif +} RING_DEF; + +typedef struct { +#ifdef __BIG_ENDIAN_BITFIELD + uint32_t unused1:24; + uint32_t numRing:8; +#else /* __LITTLE_ENDIAN_BITFIELD */ + uint32_t numRing:8; + uint32_t unused1:24; +#endif + + RING_DEF ringdef[4]; + uint32_t hbainit; +} PART_SLIM_VAR; + +/* Structure for MB Command CONFIG_RING (09) */ + +typedef struct { +#ifdef __BIG_ENDIAN_BITFIELD + uint32_t unused2:6; + uint32_t recvSeq:1; + uint32_t recvNotify:1; + uint32_t numMask:8; + uint32_t profile:8; + uint32_t unused1:4; + uint32_t ring:4; +#else /* __LITTLE_ENDIAN_BITFIELD */ + uint32_t ring:4; + uint32_t unused1:4; + uint32_t profile:8; + uint32_t numMask:8; + uint32_t recvNotify:1; + uint32_t recvSeq:1; + uint32_t unused2:6; +#endif + +#ifdef __BIG_ENDIAN_BITFIELD + uint16_t maxRespXchg; + uint16_t maxOrigXchg; +#else /* __LITTLE_ENDIAN_BITFIELD */ + uint16_t maxOrigXchg; + uint16_t maxRespXchg; +#endif + + RR_REG rrRegs[6]; +} CONFIG_RING_VAR; + +/* Structure for MB Command RESET_RING (10) */ + +typedef struct { + uint32_t ring_no; +} RESET_RING_VAR; + +/* Structure for MB Command READ_CONFIG (11) */ + +typedef struct { +#ifdef __BIG_ENDIAN_BITFIELD + uint32_t cr:1; + uint32_t ci:1; + uint32_t cr_delay:6; + uint32_t cr_count:8; + uint32_t InitBBC:8; + uint32_t MaxBBC:8; +#else /* __LITTLE_ENDIAN_BITFIELD */ + uint32_t MaxBBC:8; + uint32_t InitBBC:8; + uint32_t cr_count:8; + uint32_t cr_delay:6; + uint32_t ci:1; + uint32_t cr:1; +#endif + +#ifdef __BIG_ENDIAN_BITFIELD + uint32_t topology:8; + uint32_t myDid:24; +#else /* __LITTLE_ENDIAN_BITFIELD */ + uint32_t myDid:24; + uint32_t topology:8; +#endif + + /* Defines for topology (defined previously) */ +#ifdef __BIG_ENDIAN_BITFIELD + uint32_t AR:1; + uint32_t IR:1; + uint32_t rsvd1:29; + uint32_t ack0:1; +#else /* __LITTLE_ENDIAN_BITFIELD */ + uint32_t ack0:1; + uint32_t rsvd1:29; + uint32_t IR:1; + uint32_t AR:1; +#endif + + uint32_t edtov; + uint32_t arbtov; + uint32_t ratov; + uint32_t rttov; + uint32_t altov; + uint32_t lmt; +#define LMT_RESERVED 0x0 /* Not used */ +#define LMT_266_10bit 0x1 /* 265.625 Mbaud 10 bit iface */ +#define LMT_532_10bit 0x2 /* 531.25 Mbaud 10 bit iface */ +#define LMT_1063_20bit 0x3 /* 1062.5 Mbaud 20 bit iface */ +#define LMT_1063_10bit 0x4 /* 1062.5 Mbaud 10 bit iface */ +#define LMT_2125_10bit 0x8 /* 2125 Mbaud 10 bit iface */ +#define LMT_4250_10bit 0x40 /* 4250 Mbaud 10 bit iface */ + + uint32_t rsvd2; + uint32_t rsvd3; + uint32_t max_xri; + uint32_t max_iocb; + uint32_t max_rpi; + uint32_t avail_xri; + uint32_t avail_iocb; + uint32_t avail_rpi; + uint32_t default_rpi; +} READ_CONFIG_VAR; + +/* Structure for MB Command READ_RCONFIG (12) */ + +typedef struct { +#ifdef __BIG_ENDIAN_BITFIELD + uint32_t rsvd2:7; + uint32_t recvNotify:1; + uint32_t numMask:8; + uint32_t profile:8; + uint32_t rsvd1:4; + uint32_t ring:4; +#else /* __LITTLE_ENDIAN_BITFIELD */ + uint32_t ring:4; + uint32_t rsvd1:4; + uint32_t profile:8; + uint32_t numMask:8; + uint32_t recvNotify:1; + uint32_t rsvd2:7; +#endif + +#ifdef __BIG_ENDIAN_BITFIELD + uint16_t maxResp; + uint16_t maxOrig; +#else /* __LITTLE_ENDIAN_BITFIELD */ + uint16_t maxOrig; + uint16_t maxResp; +#endif + + RR_REG rrRegs[6]; + +#ifdef __BIG_ENDIAN_BITFIELD + uint16_t cmdRingOffset; + uint16_t cmdEntryCnt; + uint16_t rspRingOffset; + uint16_t rspEntryCnt; + uint16_t nextCmdOffset; + uint16_t rsvd3; + uint16_t nextRspOffset; + uint16_t rsvd4; +#else /* __LITTLE_ENDIAN_BITFIELD */ + uint16_t cmdEntryCnt; + uint16_t cmdRingOffset; + uint16_t rspEntryCnt; + uint16_t rspRingOffset; + uint16_t rsvd3; + uint16_t nextCmdOffset; + uint16_t rsvd4; + uint16_t nextRspOffset; +#endif +} READ_RCONF_VAR; + +/* Structure for MB Command READ_SPARM (13) */ +/* Structure for MB Command READ_SPARM64 (0x8D) */ + +typedef struct { + uint32_t rsvd1; + uint32_t rsvd2; + union { + struct ulp_bde sp; /* This BDE points to struct serv_parm + structure */ + struct ulp_bde64 sp64; + } un; +} READ_SPARM_VAR; + +/* Structure for MB Command READ_STATUS (14) */ + +typedef struct { +#ifdef __BIG_ENDIAN_BITFIELD + uint32_t rsvd1:31; + uint32_t clrCounters:1; + uint16_t activeXriCnt; + uint16_t activeRpiCnt; +#else /* __LITTLE_ENDIAN_BITFIELD */ + uint32_t clrCounters:1; + uint32_t rsvd1:31; + uint16_t activeRpiCnt; + uint16_t activeXriCnt; +#endif + + uint32_t xmitByteCnt; + uint32_t rcvByteCnt; + uint32_t xmitFrameCnt; + uint32_t rcvFrameCnt; + uint32_t xmitSeqCnt; + uint32_t rcvSeqCnt; + uint32_t totalOrigExchanges; + uint32_t totalRespExchanges; + uint32_t rcvPbsyCnt; + uint32_t rcvFbsyCnt; +} READ_STATUS_VAR; + +/* Structure for MB Command READ_RPI (15) */ +/* Structure for MB Command READ_RPI64 (0x8F) */ + +typedef struct { +#ifdef __BIG_ENDIAN_BITFIELD + uint16_t nextRpi; + uint16_t reqRpi; + uint32_t rsvd2:8; + uint32_t DID:24; +#else /* __LITTLE_ENDIAN_BITFIELD */ + uint16_t reqRpi; + uint16_t nextRpi; + uint32_t DID:24; + uint32_t rsvd2:8; +#endif + + union { + struct ulp_bde sp; + struct ulp_bde64 sp64; + } un; + +} READ_RPI_VAR; + +/* Structure for MB Command READ_XRI (16) */ + +typedef struct { +#ifdef __BIG_ENDIAN_BITFIELD + uint16_t nextXri; + uint16_t reqXri; + uint16_t rsvd1; + uint16_t rpi; + uint32_t rsvd2:8; + uint32_t DID:24; + uint32_t rsvd3:8; + uint32_t SID:24; + uint32_t rsvd4; + uint8_t seqId; + uint8_t rsvd5; + uint16_t seqCount; + uint16_t oxId; + uint16_t rxId; + uint32_t rsvd6:30; + uint32_t si:1; + uint32_t exchOrig:1; +#else /* __LITTLE_ENDIAN_BITFIELD */ + uint16_t reqXri; + uint16_t nextXri; + uint16_t rpi; + uint16_t rsvd1; + uint32_t DID:24; + uint32_t rsvd2:8; + uint32_t SID:24; + uint32_t rsvd3:8; + uint32_t rsvd4; + uint16_t seqCount; + uint8_t rsvd5; + uint8_t seqId; + uint16_t rxId; + uint16_t oxId; + uint32_t exchOrig:1; + uint32_t si:1; + uint32_t rsvd6:30; +#endif +} READ_XRI_VAR; + +/* Structure for MB Command READ_REV (17) */ + +typedef struct { +#ifdef __BIG_ENDIAN_BITFIELD + uint32_t cv:1; + uint32_t rr:1; + uint32_t rsvd1:29; + uint32_t rv:1; +#else /* __LITTLE_ENDIAN_BITFIELD */ + uint32_t rv:1; + uint32_t rsvd1:29; + uint32_t rr:1; + uint32_t cv:1; +#endif + + uint32_t biuRev; + uint32_t smRev; + union { + uint32_t smFwRev; + struct { +#ifdef __BIG_ENDIAN_BITFIELD + uint8_t ProgType; + uint8_t ProgId; + uint16_t ProgVer:4; + uint16_t ProgRev:4; + uint16_t ProgFixLvl:2; + uint16_t ProgDistType:2; + uint16_t DistCnt:4; +#else /* __LITTLE_ENDIAN_BITFIELD */ + uint16_t DistCnt:4; + uint16_t ProgDistType:2; + uint16_t ProgFixLvl:2; + uint16_t ProgRev:4; + uint16_t ProgVer:4; + uint8_t ProgId; + uint8_t ProgType; +#endif + + } b; + } un; + uint32_t endecRev; +#ifdef __BIG_ENDIAN_BITFIELD + uint8_t feaLevelHigh; + uint8_t feaLevelLow; + uint8_t fcphHigh; + uint8_t fcphLow; +#else /* __LITTLE_ENDIAN_BITFIELD */ + uint8_t fcphLow; + uint8_t fcphHigh; + uint8_t feaLevelLow; + uint8_t feaLevelHigh; +#endif + + uint32_t postKernRev; + uint32_t opFwRev; + uint8_t opFwName[16]; + uint32_t sli1FwRev; + uint8_t sli1FwName[16]; + uint32_t sli2FwRev; + uint8_t sli2FwName[16]; + uint32_t rsvd2; + uint32_t RandomData[7]; +} READ_REV_VAR; + +/* Structure for MB Command READ_LINK_STAT (18) */ + +typedef struct { + uint32_t rsvd1; + uint32_t linkFailureCnt; + uint32_t lossSyncCnt; + + uint32_t lossSignalCnt; + uint32_t primSeqErrCnt; + uint32_t invalidXmitWord; + uint32_t crcCnt; + uint32_t primSeqTimeout; + uint32_t elasticOverrun; + uint32_t arbTimeout; +} READ_LNK_VAR; + +/* Structure for MB Command REG_LOGIN (19) */ +/* Structure for MB Command REG_LOGIN64 (0x93) */ + +typedef struct { +#ifdef __BIG_ENDIAN_BITFIELD + uint16_t rsvd1; + uint16_t rpi; + uint32_t rsvd2:8; + uint32_t did:24; +#else /* __LITTLE_ENDIAN_BITFIELD */ + uint16_t rpi; + uint16_t rsvd1; + uint32_t did:24; + uint32_t rsvd2:8; +#endif + + union { + struct ulp_bde sp; + struct ulp_bde64 sp64; + } un; + +} REG_LOGIN_VAR; + +/* Word 30 contents for REG_LOGIN */ +typedef union { + struct { +#ifdef __BIG_ENDIAN_BITFIELD + uint16_t rsvd1:12; + uint16_t wd30_class:4; + uint16_t xri; +#else /* __LITTLE_ENDIAN_BITFIELD */ + uint16_t xri; + uint16_t wd30_class:4; + uint16_t rsvd1:12; +#endif + } f; + uint32_t word; +} REG_WD30; + +/* Structure for MB Command UNREG_LOGIN (20) */ + +typedef struct { +#ifdef __BIG_ENDIAN_BITFIELD + uint16_t rsvd1; + uint16_t rpi; +#else /* __LITTLE_ENDIAN_BITFIELD */ + uint16_t rpi; + uint16_t rsvd1; +#endif +} UNREG_LOGIN_VAR; + +/* Structure for MB Command UNREG_D_ID (0x23) */ + +typedef struct { + uint32_t did; +} UNREG_D_ID_VAR; + +/* Structure for MB Command READ_LA (21) */ +/* Structure for MB Command READ_LA64 (0x95) */ + +typedef struct { + uint32_t eventTag; /* Event tag */ +#ifdef __BIG_ENDIAN_BITFIELD + uint32_t rsvd1:22; + uint32_t pb:1; + uint32_t il:1; + uint32_t attType:8; +#else /* __LITTLE_ENDIAN_BITFIELD */ + uint32_t attType:8; + uint32_t il:1; + uint32_t pb:1; + uint32_t rsvd1:22; +#endif + +#define AT_RESERVED 0x00 /* Reserved - attType */ +#define AT_LINK_UP 0x01 /* Link is up */ +#define AT_LINK_DOWN 0x02 /* Link is down */ + +#ifdef __BIG_ENDIAN_BITFIELD + uint8_t granted_AL_PA; + uint8_t lipAlPs; + uint8_t lipType; + uint8_t topology; +#else /* __LITTLE_ENDIAN_BITFIELD */ + uint8_t topology; + uint8_t lipType; + uint8_t lipAlPs; + uint8_t granted_AL_PA; +#endif + +#define TOPOLOGY_PT_PT 0x01 /* Topology is pt-pt / pt-fabric */ +#define TOPOLOGY_LOOP 0x02 /* Topology is FC-AL */ + + union { + struct ulp_bde lilpBde; /* This BDE points to a 128 byte buffer + to */ + /* store the LILP AL_PA position map into */ + struct ulp_bde64 lilpBde64; + } un; + +#ifdef __BIG_ENDIAN_BITFIELD + uint32_t Dlu:1; + uint32_t Dtf:1; + uint32_t Drsvd2:14; + uint32_t DlnkSpeed:8; + uint32_t DnlPort:4; + uint32_t Dtx:2; + uint32_t Drx:2; +#else /* __LITTLE_ENDIAN_BITFIELD */ + uint32_t Drx:2; + uint32_t Dtx:2; + uint32_t DnlPort:4; + uint32_t DlnkSpeed:8; + uint32_t Drsvd2:14; + uint32_t Dtf:1; + uint32_t Dlu:1; +#endif + +#ifdef __BIG_ENDIAN_BITFIELD + uint32_t Ulu:1; + uint32_t Utf:1; + uint32_t Ursvd2:14; + uint32_t UlnkSpeed:8; + uint32_t UnlPort:4; + uint32_t Utx:2; + uint32_t Urx:2; +#else /* __LITTLE_ENDIAN_BITFIELD */ + uint32_t Urx:2; + uint32_t Utx:2; + uint32_t UnlPort:4; + uint32_t UlnkSpeed:8; + uint32_t Ursvd2:14; + uint32_t Utf:1; + uint32_t Ulu:1; +#endif + +#define LA_UNKNW_LINK 0x0 /* lnkSpeed */ +#define LA_1GHZ_LINK 0x04 /* lnkSpeed */ +#define LA_2GHZ_LINK 0x08 /* lnkSpeed */ +#define LA_4GHZ_LINK 0x10 /* lnkSpeed */ +#define LA_8GHZ_LINK 0x20 /* lnkSpeed */ +#define LA_10GHZ_LINK 0x40 /* lnkSpeed */ + +} READ_LA_VAR; + +/* Structure for MB Command CLEAR_LA (22) */ + +typedef struct { + uint32_t eventTag; /* Event tag */ + uint32_t rsvd1; +} CLEAR_LA_VAR; + +/* Structure for MB Command DUMP */ + +typedef struct { +#ifdef __BIG_ENDIAN_BITFIELD + uint32_t rsvd:25; + uint32_t ra:1; + uint32_t co:1; + uint32_t cv:1; + uint32_t type:4; + uint32_t entry_index:16; + uint32_t region_id:16; +#else /* __LITTLE_ENDIAN_BITFIELD */ + uint32_t type:4; + uint32_t cv:1; + uint32_t co:1; + uint32_t ra:1; + uint32_t rsvd:25; + uint32_t region_id:16; + uint32_t entry_index:16; +#endif + + uint32_t rsvd1; + uint32_t word_cnt; + uint32_t resp_offset; +} DUMP_VAR; + +#define DMP_MEM_REG 0x1 +#define DMP_NV_PARAMS 0x2 + +#define DMP_REGION_VPD 0xe +#define DMP_VPD_SIZE 0x100 + +/* Structure for MB Command CONFIG_PORT (0x88) */ + +typedef struct { + uint32_t pcbLen; + 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[5]; +} CONFIG_PORT_VAR; + +/* SLI-2 Port Control Block */ + +/* SLIM POINTER */ +#define SLIMOFF 0x30 /* WORD */ + +typedef struct _SLI2_RDSC { + uint32_t cmdEntries; + uint32_t cmdAddrLow; + uint32_t cmdAddrHigh; + + uint32_t rspEntries; + uint32_t rspAddrLow; + uint32_t rspAddrHigh; +} SLI2_RDSC; + +typedef struct _PCB { +#ifdef __BIG_ENDIAN_BITFIELD + uint32_t type:8; +#define TYPE_NATIVE_SLI2 0x01; + uint32_t feature:8; +#define FEATURE_INITIAL_SLI2 0x01; + uint32_t rsvd:12; + uint32_t maxRing:4; +#else /* __LITTLE_ENDIAN_BITFIELD */ + uint32_t maxRing:4; + uint32_t rsvd:12; + uint32_t feature:8; +#define FEATURE_INITIAL_SLI2 0x01; + uint32_t type:8; +#define TYPE_NATIVE_SLI2 0x01; +#endif + + uint32_t mailBoxSize; + uint32_t mbAddrLow; + uint32_t mbAddrHigh; + + uint32_t hgpAddrLow; + uint32_t hgpAddrHigh; + + uint32_t pgpAddrLow; + uint32_t pgpAddrHigh; + SLI2_RDSC rdsc[MAX_RINGS]; +} PCB_t; + +/* NEW_FEATURE */ +typedef struct { +#ifdef __BIG_ENDIAN_BITFIELD + uint32_t rsvd0:27; + uint32_t discardFarp:1; + uint32_t IPEnable:1; + uint32_t nodeName:1; + uint32_t portName:1; + uint32_t filterEnable:1; +#else /* __LITTLE_ENDIAN_BITFIELD */ + uint32_t filterEnable:1; + uint32_t portName:1; + uint32_t nodeName:1; + uint32_t IPEnable:1; + uint32_t discardFarp:1; + uint32_t rsvd:27; +#endif + + uint8_t portname[8]; /* Used to be struct lpfc_name */ + uint8_t nodename[8]; + uint32_t rsvd1; + uint32_t rsvd2; + uint32_t rsvd3; + uint32_t IPAddress; +} CONFIG_FARP_VAR; + +/* Union of all Mailbox Command types */ +#define MAILBOX_CMD_WSIZE 32 + +typedef union { + uint32_t varWords[MAILBOX_CMD_WSIZE - 1]; + LOAD_SM_VAR varLdSM; /* cmd = 1 (LOAD_SM) */ + READ_NV_VAR varRDnvp; /* cmd = 2 (READ_NVPARMS) */ + WRITE_NV_VAR varWTnvp; /* cmd = 3 (WRITE_NVPARMS) */ + BIU_DIAG_VAR varBIUdiag; /* cmd = 4 (RUN_BIU_DIAG) */ + INIT_LINK_VAR varInitLnk; /* cmd = 5 (INIT_LINK) */ + DOWN_LINK_VAR varDwnLnk; /* cmd = 6 (DOWN_LINK) */ + CONFIG_LINK varCfgLnk; /* cmd = 7 (CONFIG_LINK) */ + PART_SLIM_VAR varSlim; /* cmd = 8 (PART_SLIM) */ + CONFIG_RING_VAR varCfgRing; /* cmd = 9 (CONFIG_RING) */ + RESET_RING_VAR varRstRing; /* cmd = 10 (RESET_RING) */ + READ_CONFIG_VAR varRdConfig; /* cmd = 11 (READ_CONFIG) */ + READ_RCONF_VAR varRdRConfig; /* cmd = 12 (READ_RCONFIG) */ + READ_SPARM_VAR varRdSparm; /* cmd = 13 (READ_SPARM(64)) */ + READ_STATUS_VAR varRdStatus; /* cmd = 14 (READ_STATUS) */ + READ_RPI_VAR varRdRPI; /* cmd = 15 (READ_RPI(64)) */ + READ_XRI_VAR varRdXRI; /* cmd = 16 (READ_XRI) */ + READ_REV_VAR varRdRev; /* cmd = 17 (READ_REV) */ + READ_LNK_VAR varRdLnk; /* cmd = 18 (READ_LNK_STAT) */ + REG_LOGIN_VAR varRegLogin; /* cmd = 19 (REG_LOGIN(64)) */ + UNREG_LOGIN_VAR varUnregLogin; /* cmd = 20 (UNREG_LOGIN) */ + READ_LA_VAR varReadLA; /* cmd = 21 (READ_LA(64)) */ + CLEAR_LA_VAR varClearLA; /* cmd = 22 (CLEAR_LA) */ + DUMP_VAR varDmp; /* Warm Start DUMP mbx cmd */ + UNREG_D_ID_VAR varUnregDID; /* cmd = 0x23 (UNREG_D_ID) */ + CONFIG_FARP_VAR varCfgFarp; /* cmd = 0x25 (CONFIG_FARP) NEW_FEATURE */ + CONFIG_PORT_VAR varCfgPort; /* cmd = 0x88 (CONFIG_PORT) */ +} MAILVARIANTS; + +/* + * SLI-2 specific structures + */ + +typedef struct { + uint32_t cmdPutInx; + uint32_t rspGetInx; +} HGP; + +typedef struct { + uint32_t cmdGetInx; + uint32_t rspPutInx; +} PGP; + +typedef struct _SLI2_DESC { + HGP host[MAX_RINGS]; + uint32_t unused1[16]; + PGP port[MAX_RINGS]; +} SLI2_DESC; + +typedef union { + SLI2_DESC s2; +} SLI_VAR; + +typedef volatile struct { +#ifdef __BIG_ENDIAN_BITFIELD + uint16_t mbxStatus; + uint8_t mbxCommand; + uint8_t mbxReserved:6; + uint8_t mbxHc:1; + uint8_t mbxOwner:1; /* Low order bit first word */ +#else /* __LITTLE_ENDIAN_BITFIELD */ + uint8_t mbxOwner:1; /* Low order bit first word */ + uint8_t mbxHc:1; + uint8_t mbxReserved:6; + uint8_t mbxCommand; + uint16_t mbxStatus; +#endif + + MAILVARIANTS un; + SLI_VAR us; +} MAILBOX_t; + +/* + * Begin Structure Definitions for IOCB Commands + */ + +typedef struct { +#ifdef __BIG_ENDIAN_BITFIELD + uint8_t statAction; + uint8_t statRsn; + uint8_t statBaExp; + uint8_t statLocalError; +#else /* __LITTLE_ENDIAN_BITFIELD */ + uint8_t statLocalError; + uint8_t statBaExp; + uint8_t statRsn; + uint8_t statAction; +#endif + /* statRsn P/F_RJT reason codes */ +#define RJT_BAD_D_ID 0x01 /* Invalid D_ID field */ +#define RJT_BAD_S_ID 0x02 /* Invalid S_ID field */ +#define RJT_UNAVAIL_TEMP 0x03 /* N_Port unavailable temp. */ +#define RJT_UNAVAIL_PERM 0x04 /* N_Port unavailable perm. */ +#define RJT_UNSUP_CLASS 0x05 /* Class not supported */ +#define RJT_DELIM_ERR 0x06 /* Delimiter usage error */ +#define RJT_UNSUP_TYPE 0x07 /* Type not supported */ +#define RJT_BAD_CONTROL 0x08 /* Invalid link conrtol */ +#define RJT_BAD_RCTL 0x09 /* R_CTL invalid */ +#define RJT_BAD_FCTL 0x0A /* F_CTL invalid */ +#define RJT_BAD_OXID 0x0B /* OX_ID invalid */ +#define RJT_BAD_RXID 0x0C /* RX_ID invalid */ +#define RJT_BAD_SEQID 0x0D /* SEQ_ID invalid */ +#define RJT_BAD_DFCTL 0x0E /* DF_CTL invalid */ +#define RJT_BAD_SEQCNT 0x0F /* SEQ_CNT invalid */ +#define RJT_BAD_PARM 0x10 /* Param. field invalid */ +#define RJT_XCHG_ERR 0x11 /* Exchange error */ +#define RJT_PROT_ERR 0x12 /* Protocol error */ +#define RJT_BAD_LENGTH 0x13 /* Invalid Length */ +#define RJT_UNEXPECTED_ACK 0x14 /* Unexpected ACK */ +#define RJT_LOGIN_REQUIRED 0x16 /* Login required */ +#define RJT_TOO_MANY_SEQ 0x17 /* Excessive sequences */ +#define RJT_XCHG_NOT_STRT 0x18 /* Exchange not started */ +#define RJT_UNSUP_SEC_HDR 0x19 /* Security hdr not supported */ +#define RJT_UNAVAIL_PATH 0x1A /* Fabric Path not available */ +#define RJT_VENDOR_UNIQUE 0xFF /* Vendor unique error */ + +#define IOERR_SUCCESS 0x00 /* statLocalError */ +#define IOERR_MISSING_CONTINUE 0x01 +#define IOERR_SEQUENCE_TIMEOUT 0x02 +#define IOERR_INTERNAL_ERROR 0x03 +#define IOERR_INVALID_RPI 0x04 +#define IOERR_NO_XRI 0x05 +#define IOERR_ILLEGAL_COMMAND 0x06 +#define IOERR_XCHG_DROPPED 0x07 +#define IOERR_ILLEGAL_FIELD 0x08 +#define IOERR_BAD_CONTINUE 0x09 +#define IOERR_TOO_MANY_BUFFERS 0x0A +#define IOERR_RCV_BUFFER_WAITING 0x0B +#define IOERR_NO_CONNECTION 0x0C +#define IOERR_TX_DMA_FAILED 0x0D +#define IOERR_RX_DMA_FAILED 0x0E +#define IOERR_ILLEGAL_FRAME 0x0F +#define IOERR_EXTRA_DATA 0x10 +#define IOERR_NO_RESOURCES 0x11 +#define IOERR_RESERVED 0x12 +#define IOERR_ILLEGAL_LENGTH 0x13 +#define IOERR_UNSUPPORTED_FEATURE 0x14 +#define IOERR_ABORT_IN_PROGRESS 0x15 +#define IOERR_ABORT_REQUESTED 0x16 +#define IOERR_RECEIVE_BUFFER_TIMEOUT 0x17 +#define IOERR_LOOP_OPEN_FAILURE 0x18 +#define IOERR_RING_RESET 0x19 +#define IOERR_LINK_DOWN 0x1A +#define IOERR_CORRUPTED_DATA 0x1B +#define IOERR_CORRUPTED_RPI 0x1C +#define IOERR_OUT_OF_ORDER_DATA 0x1D +#define IOERR_OUT_OF_ORDER_ACK 0x1E +#define IOERR_DUP_FRAME 0x1F +#define IOERR_LINK_CONTROL_FRAME 0x20 /* ACK_N received */ +#define IOERR_BAD_HOST_ADDRESS 0x21 +#define IOERR_RCV_HDRBUF_WAITING 0x22 +#define IOERR_MISSING_HDR_BUFFER 0x23 +#define IOERR_MSEQ_CHAIN_CORRUPTED 0x24 +#define IOERR_ABORTMULT_REQUESTED 0x25 +#define IOERR_BUFFER_SHORTAGE 0x28 +#define IOERR_DEFAULT 0x29 +#define IOERR_CNT 0x2A + +#define IOERR_DRVR_MASK 0x100 +#define IOERR_SLI_DOWN 0x101 /* ulpStatus - Driver defined */ +#define IOERR_SLI_BRESET 0x102 +#define IOERR_SLI_ABORTED 0x103 +} PARM_ERR; + +typedef union { + struct { +#ifdef __BIG_ENDIAN_BITFIELD + uint8_t Rctl; /* R_CTL field */ + uint8_t Type; /* TYPE field */ + uint8_t Dfctl; /* DF_CTL field */ + uint8_t Fctl; /* Bits 0-7 of IOCB word 5 */ +#else /* __LITTLE_ENDIAN_BITFIELD */ + uint8_t Fctl; /* Bits 0-7 of IOCB word 5 */ + uint8_t Dfctl; /* DF_CTL field */ + uint8_t Type; /* TYPE field */ + uint8_t Rctl; /* R_CTL field */ +#endif + +#define BC 0x02 /* Broadcast Received - Fctl */ +#define SI 0x04 /* Sequence Initiative */ +#define LA 0x08 /* Ignore Link Attention state */ +#define LS 0x80 /* Last Sequence */ + } hcsw; + uint32_t reserved; +} WORD5; + +/* IOCB Command template for a generic response */ +typedef struct { + uint32_t reserved[4]; + PARM_ERR perr; +} GENERIC_RSP; + +/* IOCB Command template for XMIT / XMIT_BCAST / RCV_SEQUENCE / XMIT_ELS */ +typedef struct { + struct ulp_bde xrsqbde[2]; + uint32_t xrsqRo; /* Starting Relative Offset */ + WORD5 w5; /* Header control/status word */ +} XR_SEQ_FIELDS; + +/* IOCB Command template for ELS_REQUEST */ +typedef struct { + struct ulp_bde elsReq; + struct ulp_bde elsRsp; + +#ifdef __BIG_ENDIAN_BITFIELD + uint32_t word4Rsvd:7; + uint32_t fl:1; + uint32_t myID:24; + uint32_t word5Rsvd:8; + uint32_t remoteID:24; +#else /* __LITTLE_ENDIAN_BITFIELD */ + uint32_t myID:24; + uint32_t fl:1; + uint32_t word4Rsvd:7; + uint32_t remoteID:24; + uint32_t word5Rsvd:8; +#endif +} ELS_REQUEST; + +/* IOCB Command template for RCV_ELS_REQ */ +typedef struct { + struct ulp_bde elsReq[2]; + uint32_t parmRo; + +#ifdef __BIG_ENDIAN_BITFIELD + uint32_t word5Rsvd:8; + uint32_t remoteID:24; +#else /* __LITTLE_ENDIAN_BITFIELD */ + uint32_t remoteID:24; + uint32_t word5Rsvd:8; +#endif +} RCV_ELS_REQ; + +/* IOCB Command template for ABORT / CLOSE_XRI */ +typedef struct { + uint32_t rsvd[3]; + uint32_t abortType; +#define ABORT_TYPE_ABTX 0x00000000 +#define ABORT_TYPE_ABTS 0x00000001 + uint32_t parm; +#ifdef __BIG_ENDIAN_BITFIELD + uint16_t abortContextTag; /* ulpContext from command to abort/close */ + uint16_t abortIoTag; /* ulpIoTag from command to abort/close */ +#else /* __LITTLE_ENDIAN_BITFIELD */ + uint16_t abortIoTag; /* ulpIoTag from command to abort/close */ + uint16_t abortContextTag; /* ulpContext from command to abort/close */ +#endif +} AC_XRI; + +/* IOCB Command template for ABORT_MXRI64 */ +typedef struct { + uint32_t rsvd[3]; + uint32_t abortType; + uint32_t parm; + uint32_t iotag32; +} A_MXRI64; + +/* IOCB Command template for GET_RPI */ +typedef struct { + uint32_t rsvd[4]; + uint32_t parmRo; +#ifdef __BIG_ENDIAN_BITFIELD + uint32_t word5Rsvd:8; + uint32_t remoteID:24; +#else /* __LITTLE_ENDIAN_BITFIELD */ + uint32_t remoteID:24; + uint32_t word5Rsvd:8; +#endif +} GET_RPI; + +/* IOCB Command template for all FCP Initiator commands */ +typedef struct { + struct ulp_bde fcpi_cmnd; /* FCP_CMND payload descriptor */ + struct ulp_bde fcpi_rsp; /* Rcv buffer */ + uint32_t fcpi_parm; + uint32_t fcpi_XRdy; /* transfer ready for IWRITE */ +} FCPI_FIELDS; + +/* IOCB Command template for all FCP Target commands */ +typedef struct { + struct ulp_bde fcpt_Buffer[2]; /* FCP_CMND payload descriptor */ + uint32_t fcpt_Offset; + uint32_t fcpt_Length; /* transfer ready for IWRITE */ +} FCPT_FIELDS; + +/* SLI-2 IOCB structure definitions */ + +/* IOCB Command template for 64 bit XMIT / XMIT_BCAST / XMIT_ELS */ +typedef struct { + ULP_BDL bdl; + uint32_t xrsqRo; /* Starting Relative Offset */ + WORD5 w5; /* Header control/status word */ +} XMT_SEQ_FIELDS64; + +/* IOCB Command template for 64 bit RCV_SEQUENCE64 */ +typedef struct { + struct ulp_bde64 rcvBde; + uint32_t rsvd1; + uint32_t xrsqRo; /* Starting Relative Offset */ + WORD5 w5; /* Header control/status word */ +} RCV_SEQ_FIELDS64; + +/* IOCB Command template for ELS_REQUEST64 */ +typedef struct { + ULP_BDL bdl; +#ifdef __BIG_ENDIAN_BITFIELD + uint32_t word4Rsvd:7; + uint32_t fl:1; + uint32_t myID:24; + uint32_t word5Rsvd:8; + uint32_t remoteID:24; +#else /* __LITTLE_ENDIAN_BITFIELD */ + uint32_t myID:24; + uint32_t fl:1; + uint32_t word4Rsvd:7; + uint32_t remoteID:24; + uint32_t word5Rsvd:8; +#endif +} ELS_REQUEST64; + +/* IOCB Command template for GEN_REQUEST64 */ +typedef struct { + ULP_BDL bdl; + uint32_t xrsqRo; /* Starting Relative Offset */ + WORD5 w5; /* Header control/status word */ +} GEN_REQUEST64; + +/* IOCB Command template for RCV_ELS_REQ64 */ +typedef struct { + struct ulp_bde64 elsReq; + uint32_t rcvd1; + uint32_t parmRo; + +#ifdef __BIG_ENDIAN_BITFIELD + uint32_t word5Rsvd:8; + uint32_t remoteID:24; +#else /* __LITTLE_ENDIAN_BITFIELD */ + uint32_t remoteID:24; + uint32_t word5Rsvd:8; +#endif +} RCV_ELS_REQ64; + +/* IOCB Command template for all 64 bit FCP Initiator commands */ +typedef struct { + ULP_BDL bdl; + uint32_t fcpi_parm; + uint32_t fcpi_XRdy; /* transfer ready for IWRITE */ +} FCPI_FIELDS64; + +/* IOCB Command template for all 64 bit FCP Target commands */ +typedef struct { + ULP_BDL bdl; + uint32_t fcpt_Offset; + uint32_t fcpt_Length; /* transfer ready for IWRITE */ +} FCPT_FIELDS64; + +typedef volatile struct _IOCB { /* IOCB structure */ + union { + GENERIC_RSP grsp; /* Generic response */ + XR_SEQ_FIELDS xrseq; /* XMIT / BCAST / RCV_SEQUENCE cmd */ + struct ulp_bde cont[3]; /* up to 3 continuation bdes */ + RCV_ELS_REQ rcvels; /* RCV_ELS_REQ template */ + AC_XRI acxri; /* ABORT / CLOSE_XRI template */ + A_MXRI64 amxri; /* abort multiple xri command overlay */ + GET_RPI getrpi; /* GET_RPI template */ + FCPI_FIELDS fcpi; /* FCP Initiator template */ + FCPT_FIELDS fcpt; /* FCP target template */ + + /* SLI-2 structures */ + + struct ulp_bde64 cont64[2]; /* up to 2 64 bit continuation + bde_64s */ + ELS_REQUEST64 elsreq64; /* ELS_REQUEST template */ + GEN_REQUEST64 genreq64; /* GEN_REQUEST template */ + RCV_ELS_REQ64 rcvels64; /* RCV_ELS_REQ template */ + XMT_SEQ_FIELDS64 xseq64; /* XMIT / BCAST cmd */ + FCPI_FIELDS64 fcpi64; /* FCP 64 bit Initiator template */ + FCPT_FIELDS64 fcpt64; /* FCP 64 bit target template */ + + uint32_t ulpWord[IOCB_WORD_SZ - 2]; /* generic 6 'words' */ + } un; + union { + struct { +#ifdef __BIG_ENDIAN_BITFIELD + uint16_t ulpContext; /* High order bits word 6 */ + uint16_t ulpIoTag; /* Low order bits word 6 */ +#else /* __LITTLE_ENDIAN_BITFIELD */ + uint16_t ulpIoTag; /* Low order bits word 6 */ + uint16_t ulpContext; /* High order bits word 6 */ +#endif + } t1; + struct { +#ifdef __BIG_ENDIAN_BITFIELD + uint16_t ulpContext; /* High order bits word 6 */ + uint16_t ulpIoTag1:2; /* Low order bits word 6 */ + uint16_t ulpIoTag0:14; /* Low order bits word 6 */ +#else /* __LITTLE_ENDIAN_BITFIELD */ + uint16_t ulpIoTag0:14; /* Low order bits word 6 */ + uint16_t ulpIoTag1:2; /* Low order bits word 6 */ + uint16_t ulpContext; /* High order bits word 6 */ +#endif + } t2; + } un1; +#define ulpContext un1.t1.ulpContext +#define ulpIoTag un1.t1.ulpIoTag +#define ulpIoTag0 un1.t2.ulpIoTag0 + +#ifdef __BIG_ENDIAN_BITFIELD + uint32_t ulpTimeout:8; + uint32_t ulpXS:1; + uint32_t ulpFCP2Rcvy:1; + uint32_t ulpPU:2; + uint32_t ulpIr:1; + uint32_t ulpClass:3; + uint32_t ulpCommand:8; + uint32_t ulpStatus:4; + uint32_t ulpBdeCount:2; + uint32_t ulpLe:1; + uint32_t ulpOwner:1; /* Low order bit word 7 */ +#else /* __LITTLE_ENDIAN_BITFIELD */ + uint32_t ulpOwner:1; /* Low order bit word 7 */ + uint32_t ulpLe:1; + uint32_t ulpBdeCount:2; + uint32_t ulpStatus:4; + uint32_t ulpCommand:8; + uint32_t ulpClass:3; + uint32_t ulpIr:1; + uint32_t ulpPU:2; + uint32_t ulpFCP2Rcvy:1; + uint32_t ulpXS:1; + uint32_t ulpTimeout:8; +#endif + +#define PARM_UNUSED 0 /* PU field (Word 4) not used */ +#define PARM_REL_OFF 1 /* PU field (Word 4) = R. O. */ +#define PARM_READ_CHECK 2 /* PU field (Word 4) = Data Transfer Length */ +#define CLASS1 0 /* Class 1 */ +#define CLASS2 1 /* Class 2 */ +#define CLASS3 2 /* Class 3 */ +#define CLASS_FCP_INTERMIX 7 /* FCP Data->Cls 1, all else->Cls 2 */ + +#define IOSTAT_SUCCESS 0x0 /* ulpStatus - HBA defined */ +#define IOSTAT_FCP_RSP_ERROR 0x1 +#define IOSTAT_REMOTE_STOP 0x2 +#define IOSTAT_LOCAL_REJECT 0x3 +#define IOSTAT_NPORT_RJT 0x4 +#define IOSTAT_FABRIC_RJT 0x5 +#define IOSTAT_NPORT_BSY 0x6 +#define IOSTAT_FABRIC_BSY 0x7 +#define IOSTAT_INTERMED_RSP 0x8 +#define IOSTAT_LS_RJT 0x9 +#define IOSTAT_BA_RJT 0xA +#define IOSTAT_RSVD1 0xB +#define IOSTAT_RSVD2 0xC +#define IOSTAT_RSVD3 0xD +#define IOSTAT_RSVD4 0xE +#define IOSTAT_RSVD5 0xF +#define IOSTAT_DRIVER_REJECT 0x10 /* ulpStatus - Driver defined */ +#define IOSTAT_DEFAULT 0xF /* Same as rsvd5 for now */ +#define IOSTAT_CNT 0x11 + +} IOCB_t; + + +#define SLI1_SLIM_SIZE (4 * 1024) + +/* Up to 498 IOCBs will fit into 16k + * 256 (MAILBOX_t) + 140 (PCB_t) + ( 32 (IOCB_t) * 498 ) = < 16384 + */ +#define SLI2_SLIM_SIZE (16 * 1024) + +/* Maximum IOCBs that will fit in SLI2 slim */ +#define MAX_SLI2_IOCB 498 + +struct lpfc_sli2_slim { + MAILBOX_t mbx; + PCB_t pcb; + IOCB_t IOCBs[MAX_SLI2_IOCB]; +}; + +/******************************************************************* +This macro check PCI device to allow special handling for LC HBAs. + +Parameters: +device : struct pci_dev 's device field + +return 1 => TRUE + 0 => FALSE + *******************************************************************/ +static inline int +lpfc_is_LC_HBA(unsigned short device) +{ + if ((device == PCI_DEVICE_ID_TFLY) || + (device == PCI_DEVICE_ID_PFLY) || + (device == PCI_DEVICE_ID_LP101) || + (device == PCI_DEVICE_ID_BMID) || + (device == PCI_DEVICE_ID_BSMB) || + (device == PCI_DEVICE_ID_ZMID) || + (device == PCI_DEVICE_ID_ZSMB) || + (device == PCI_DEVICE_ID_RFLY)) + return 1; + else + return 0; +} + +#endif /* _H_LPFC_HW */ diff --git a/drivers/scsi/lpfc/lpfc_init.c b/drivers/scsi/lpfc/lpfc_init.c new file mode 100644 index 000000000..e7f927892 --- /dev/null +++ b/drivers/scsi/lpfc/lpfc_init.c @@ -0,0 +1,1345 @@ +/******************************************************************* + * This file is part of the Emulex Linux Device Driver for * + * Enterprise Fibre Channel Host Bus Adapters. * + * Refer to the README file included with this package for * + * driver version and adapter support. * + * Copyright (C) 2004 Emulex Corporation. * + * www.emulex.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, a copy of which * + * can be found in the file COPYING included with this package. * + *******************************************************************/ + +/* + * $Id: lpfc_init.c 1.168 2004/11/15 11:01:33EST sf_support Exp $ + */ + +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "lpfc_sli.h" +#include "lpfc_disc.h" +#include "lpfc_scsi.h" +#include "lpfc.h" +#include "lpfc_crtn.h" +#include "lpfc_hw.h" +#include "lpfc_logmsg.h" +#include "lpfc_mem.h" +#include "lpfc_version.h" +#include "lpfc_compat.h" + +static int lpfc_parse_vpd(struct lpfc_hba *, uint8_t *); +static int lpfc_post_rcv_buf(struct lpfc_hba *); +static int lpfc_rdrev_wd30 = 0; + +/************************************************************************/ +/* */ +/* lpfc_config_port_prep */ +/* This routine will do LPFC initialization prior to the */ +/* CONFIG_PORT mailbox command. This will be initialized */ +/* as a SLI layer callback routine. */ +/* This routine returns 0 on success or -ERESTART if it wants */ +/* the SLI layer to reset the HBA and try again. Any */ +/* other return value indicates an error. */ +/* */ +/************************************************************************/ +int +lpfc_config_port_prep(struct lpfc_hba * phba) +{ + lpfc_vpd_t *vp = &phba->vpd; + int i = 0; + LPFC_MBOXQ_t *pmb; + MAILBOX_t *mb; + + + /* Get a Mailbox buffer to setup mailbox commands for HBA + initialization */ + pmb = mempool_alloc(phba->mbox_mem_pool, GFP_ATOMIC); + if (!pmb) { + phba->hba_state = LPFC_HBA_ERROR; + return -ENOMEM; + } + + mb = &pmb->mb; + phba->hba_state = LPFC_INIT_MBX_CMDS; + + /* special handling for LC HBAs */ + if (lpfc_is_LC_HBA(phba->pcidev->device)) { + char licensed[56] = + "key unlock for use with gnu public licensed code only\0"; + uint32_t *ptext = (uint32_t *) licensed; + + for (i = 0; i < 56; i += sizeof (uint32_t), ptext++) + *ptext = cpu_to_be32(*ptext); + + /* Setup and issue mailbox READ NVPARAMS command */ + lpfc_read_nv(phba, pmb); + memset((char*)mb->un.varRDnvp.rsvd3, 0, + sizeof (mb->un.varRDnvp.rsvd3)); + memcpy((char*)mb->un.varRDnvp.rsvd3, licensed, + sizeof (licensed)); + + if (lpfc_sli_issue_mbox(phba, pmb, MBX_POLL) != MBX_SUCCESS) { + /* Adapter initialization error, mbxCmd + READ_NVPARM, mbxStatus */ + lpfc_printf_log(phba, + KERN_ERR, + LOG_MBOX, + "%d:0324 Config Port initialization " + "error, mbxCmd x%x READ_NVPARM, " + "mbxStatus x%x\n", + phba->brd_no, + mb->mbxCommand, mb->mbxStatus); + return -ERESTART; + } + memcpy(phba->wwnn, (char *)mb->un.varRDnvp.nodename, + sizeof (mb->un.varRDnvp.nodename)); + } + + /* Setup and issue mailbox READ REV command */ + lpfc_read_rev(phba, pmb); + if (lpfc_sli_issue_mbox(phba, pmb, MBX_POLL) != MBX_SUCCESS) { + /* Adapter failed to init, mbxCmd READ_REV, mbxStatus + */ + lpfc_printf_log(phba, + KERN_ERR, + LOG_INIT, + "%d:0439 Adapter failed to init, mbxCmd x%x " + "READ_REV, mbxStatus x%x\n", + phba->brd_no, + mb->mbxCommand, mb->mbxStatus); + mempool_free( pmb, phba->mbox_mem_pool); + return -ERESTART; + } + + /* The HBA's current state is provided by the ProgType and rr fields. + * Read and check the value of these fields before continuing to config + * this port. + */ + if (mb->un.varRdRev.rr == 0 || mb->un.varRdRev.un.b.ProgType != 2) { + /* Old firmware */ + vp->rev.rBit = 0; + /* Adapter failed to init, mbxCmd READ_REV detected + outdated firmware */ + lpfc_printf_log(phba, + KERN_ERR, + LOG_INIT, + "%d:0440 Adapter failed to init, mbxCmd x%x " + "READ_REV detected outdated firmware" + "Data: x%x\n", + phba->brd_no, + mb->mbxCommand, 0); + mempool_free(pmb, phba->mbox_mem_pool); + return -ERESTART; + } else { + vp->rev.rBit = 1; + vp->rev.sli1FwRev = mb->un.varRdRev.sli1FwRev; + memcpy(vp->rev.sli1FwName, + (char*)mb->un.varRdRev.sli1FwName, 16); + vp->rev.sli2FwRev = mb->un.varRdRev.sli2FwRev; + memcpy(vp->rev.sli2FwName, + (char *)mb->un.varRdRev.sli2FwName, 16); + } + + /* Save information as VPD data */ + vp->rev.biuRev = mb->un.varRdRev.biuRev; + vp->rev.smRev = mb->un.varRdRev.smRev; + vp->rev.smFwRev = mb->un.varRdRev.un.smFwRev; + vp->rev.endecRev = mb->un.varRdRev.endecRev; + vp->rev.fcphHigh = mb->un.varRdRev.fcphHigh; + vp->rev.fcphLow = mb->un.varRdRev.fcphLow; + vp->rev.feaLevelHigh = mb->un.varRdRev.feaLevelHigh; + vp->rev.feaLevelLow = mb->un.varRdRev.feaLevelLow; + vp->rev.postKernRev = mb->un.varRdRev.postKernRev; + vp->rev.opFwRev = mb->un.varRdRev.opFwRev; + lpfc_rdrev_wd30 = mb->un.varWords[30]; + + if (lpfc_is_LC_HBA(phba->pcidev->device)) + memcpy(phba->RandomData, (char *)&mb->un.varWords[24], + sizeof (phba->RandomData)); + + /* Get adapter VPD information */ + lpfc_dump_mem(phba, pmb); + if (lpfc_sli_issue_mbox(phba, pmb, MBX_POLL) != MBX_SUCCESS) { + /* Let it go through even if failed. */ + /* Adapter failed to init, mbxCmd DUMP VPD, + mbxStatus */ + lpfc_printf_log(phba, + KERN_INFO, + LOG_INIT, + "%d:0441 VPD not present on adapter, mbxCmd " + "x%x DUMP VPD, mbxStatus x%x\n", + phba->brd_no, + mb->mbxCommand, mb->mbxStatus); + } else if (mb->un.varDmp.ra == 1) { + lpfc_parse_vpd(phba, (uint8_t *)&mb->un.varDmp.resp_offset); + } + mempool_free(pmb, phba->mbox_mem_pool); + return 0; +} + +/************************************************************************/ +/* */ +/* lpfc_config_port_post */ +/* This routine will do LPFC initialization after the */ +/* CONFIG_PORT mailbox command. This will be initialized */ +/* as a SLI layer callback routine. */ +/* This routine returns 0 on success. Any other return value */ +/* indicates an error. */ +/* */ +/************************************************************************/ +int +lpfc_config_port_post(struct lpfc_hba * phba) +{ + LPFC_MBOXQ_t *pmb; + MAILBOX_t *mb; + struct lpfc_dmabuf *mp; + struct lpfc_sli *psli = &phba->sli; + uint32_t status, timeout; + int i, j, flogi_sent; + unsigned long isr_cnt, clk_cnt; + + + /* Get a Mailbox buffer to setup mailbox commands for HBA + initialization */ + pmb = mempool_alloc(phba->mbox_mem_pool, GFP_ATOMIC); + if (!pmb) { + phba->hba_state = LPFC_HBA_ERROR; + return -ENOMEM; + } + mb = &pmb->mb; + + /* Setup link timers */ + lpfc_config_link(phba, pmb); + if (lpfc_sli_issue_mbox(phba, pmb, MBX_POLL) != MBX_SUCCESS) { + lpfc_printf_log(phba, + KERN_ERR, + LOG_INIT, + "%d:0447 Adapter failed init, mbxCmd x%x " + "CONFIG_LINK mbxStatus x%x\n", + phba->brd_no, + mb->mbxCommand, mb->mbxStatus); + phba->hba_state = LPFC_HBA_ERROR; + mempool_free( pmb, phba->mbox_mem_pool); + return -EIO; + } + + /* Get login parameters for NID. */ + lpfc_read_sparam(phba, pmb); + if (lpfc_sli_issue_mbox(phba, pmb, MBX_POLL) != MBX_SUCCESS) { + lpfc_printf_log(phba, + KERN_ERR, + LOG_INIT, + "%d:0448 Adapter failed init, mbxCmd x%x " + "READ_SPARM mbxStatus x%x\n", + phba->brd_no, + mb->mbxCommand, mb->mbxStatus); + phba->hba_state = LPFC_HBA_ERROR; + mempool_free( pmb, phba->mbox_mem_pool); + return -EIO; + } + + mp = (struct lpfc_dmabuf *) pmb->context1; + + /* The mailbox was populated by the HBA. Flush it to main store for the + * driver. Note that all context buffers are from the driver's + * dma pool and have length LPFC_BPL_SIZE. + */ + pci_dma_sync_single_for_cpu(phba->pcidev, mp->phys, LPFC_BPL_SIZE, + PCI_DMA_FROMDEVICE); + + memcpy(&phba->fc_sparam, mp->virt, sizeof (struct serv_parm)); + lpfc_mbuf_free(phba, mp->virt, mp->phys); + kfree(mp); + pmb->context1 = NULL; + + memcpy(&phba->fc_nodename, &phba->fc_sparam.nodeName, + sizeof (struct lpfc_name)); + memcpy(&phba->fc_portname, &phba->fc_sparam.portName, + sizeof (struct lpfc_name)); + /* If no serial number in VPD data, use low 6 bytes of WWNN */ + /* This should be consolidated into parse_vpd ? - mr */ + if (phba->SerialNumber[0] == 0) { + uint8_t *outptr; + + outptr = (uint8_t *) & phba->fc_nodename.IEEE[0]; + for (i = 0; i < 12; i++) { + status = *outptr++; + j = ((status & 0xf0) >> 4); + if (j <= 9) + phba->SerialNumber[i] = + (char)((uint8_t) 0x30 + (uint8_t) j); + else + phba->SerialNumber[i] = + (char)((uint8_t) 0x61 + (uint8_t) (j - 10)); + i++; + j = (status & 0xf); + if (j <= 9) + phba->SerialNumber[i] = + (char)((uint8_t) 0x30 + (uint8_t) j); + else + phba->SerialNumber[i] = + (char)((uint8_t) 0x61 + (uint8_t) (j - 10)); + } + } + + /* This should turn on DELAYED ABTS for ELS timeouts */ + lpfc_set_slim(phba, pmb, 0x052198, 0x1); + if (lpfc_sli_issue_mbox(phba, pmb, MBX_POLL) != MBX_SUCCESS) { + phba->hba_state = LPFC_HBA_ERROR; + mempool_free( pmb, phba->mbox_mem_pool); + return -EIO; + } + + + lpfc_read_config(phba, pmb); + if (lpfc_sli_issue_mbox(phba, pmb, MBX_POLL) != MBX_SUCCESS) { + lpfc_printf_log(phba, + KERN_ERR, + LOG_INIT, + "%d:0453 Adapter failed to init, mbxCmd x%x " + "READ_CONFIG, mbxStatus x%x\n", + phba->brd_no, + mb->mbxCommand, mb->mbxStatus); + phba->hba_state = LPFC_HBA_ERROR; + mempool_free( pmb, phba->mbox_mem_pool); + return -EIO; + } + + /* Reset the DFT_HBA_Q_DEPTH to the max xri */ + if (phba->cfg_hba_queue_depth > (mb->un.varRdConfig.max_xri+1)) + phba->cfg_hba_queue_depth = + mb->un.varRdConfig.max_xri + 1; + + phba->lmt = mb->un.varRdConfig.lmt; + /* HBA is not 4GB capable, or HBA is not 2GB capable, + don't let link speed ask for it */ + if ((((phba->lmt & LMT_4250_10bit) != LMT_4250_10bit) && + (phba->cfg_link_speed > LINK_SPEED_2G)) || + (((phba->lmt & LMT_2125_10bit) != LMT_2125_10bit) && + (phba->cfg_link_speed > LINK_SPEED_1G))) { + /* Reset link speed to auto. 1G/2GB HBA cfg'd for 4G */ + lpfc_printf_log(phba, + KERN_WARNING, + LOG_LINK_EVENT, + "%d:1302 Invalid speed for this board: " + "Reset link speed to auto: x%x\n", + phba->brd_no, + phba->cfg_link_speed); + phba->cfg_link_speed = LINK_SPEED_AUTO; + } + + if (!phba->intr_inited) { + /* Add our interrupt routine to kernel's interrupt chain & + enable it */ + + if (request_irq(phba->pcidev->irq, + lpfc_intr_handler, + SA_SHIRQ, + LPFC_DRIVER_NAME, + phba) != 0) { + /* Enable interrupt handler failed */ + lpfc_printf_log(phba, + KERN_ERR, + LOG_INIT, + "%d:0451 Enable interrupt handler " + "failed\n", + phba->brd_no); + phba->hba_state = LPFC_HBA_ERROR; + mempool_free(pmb, phba->mbox_mem_pool); + return -EIO; + } + phba->intr_inited = + (HC_MBINT_ENA | HC_ERINT_ENA | HC_LAINT_ENA); + } + + phba->hba_state = LPFC_LINK_DOWN; + + /* Only process IOCBs on ring 0 till hba_state is READY */ + if (psli->ring[psli->ip_ring].cmdringaddr) + psli->ring[psli->ip_ring].flag |= LPFC_STOP_IOCB_EVENT; + if (psli->ring[psli->fcp_ring].cmdringaddr) + psli->ring[psli->fcp_ring].flag |= LPFC_STOP_IOCB_EVENT; + if (psli->ring[psli->next_ring].cmdringaddr) + psli->ring[psli->next_ring].flag |= LPFC_STOP_IOCB_EVENT; + + /* Post receive buffers for desired rings */ + lpfc_post_rcv_buf(phba); + + /* Enable appropriate host interrupts */ + status = readl(phba->HCregaddr); + status |= phba->intr_inited; + if (psli->sliinit.num_rings > 0) + status |= HC_R0INT_ENA; + if (psli->sliinit.num_rings > 1) + status |= HC_R1INT_ENA; + if (psli->sliinit.num_rings > 2) + status |= HC_R2INT_ENA; + if (psli->sliinit.num_rings > 3) + status |= HC_R3INT_ENA; + + writel(status, phba->HCregaddr); + readl(phba->HCregaddr); /* flush */ + + /* Setup and issue mailbox INITIALIZE LINK command */ + lpfc_init_link(phba, pmb, phba->cfg_topology, + phba->cfg_link_speed); + + isr_cnt = psli->slistat.sliIntr; + clk_cnt = jiffies; + + if (lpfc_sli_issue_mbox(phba, pmb, MBX_NOWAIT) != MBX_SUCCESS) { + lpfc_printf_log(phba, + KERN_ERR, + LOG_INIT, + "%d:0454 Adapter failed to init, mbxCmd x%x " + "INIT_LINK, mbxStatus x%x\n", + phba->brd_no, + mb->mbxCommand, mb->mbxStatus); + + /* 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 */ + + free_irq(phba->pcidev->irq, phba); + phba->hba_state = LPFC_HBA_ERROR; + mempool_free(pmb, phba->mbox_mem_pool); + return -EIO; + } + /* MBOX buffer will be freed in mbox compl */ + + /* + * Setup the ring 0 (els) timeout handler + */ + timeout = phba->fc_ratov << 1; + + phba->els_tmofunc.expires = jiffies + HZ * timeout; + add_timer(&phba->els_tmofunc); + + phba->fc_prevDID = Mask_DID; + flogi_sent = 0; + i = 0; + while ((phba->hba_state != LPFC_HBA_READY) || + (phba->num_disc_nodes) || (phba->fc_prli_sent) || + ((phba->fc_map_cnt == 0) && (i<2)) || + (psli->sliinit.sli_flag & LPFC_SLI_MBOX_ACTIVE)) { + /* Check every second for 30 retries. */ + i++; + if (i > 30) { + break; + } + if ((i >= 15) && (phba->hba_state <= LPFC_LINK_DOWN)) { + /* The link is down. Set linkdown timeout */ + break; + } + + /* Delay for 1 second to give discovery time to complete. */ + for (j = 0; j < 20; j++) { + /* On some systems, the driver's attach/detect routines + * are uninterruptible. Since the driver cannot predict + * when this is true, just manually call the ISR every + * 50 ms to service any interrupts. + */ + msleep(50); + if (isr_cnt == psli->slistat.sliIntr) { + lpfc_sli_intr(phba); + isr_cnt = psli->slistat.sliIntr; + } + } + isr_cnt = psli->slistat.sliIntr; + + if (clk_cnt == jiffies) { + /* REMOVE: IF THIS HAPPENS, SYSTEM CLOCK IS NOT RUNNING. + * WE HAVE TO MANUALLY CALL OUR TIMEOUT ROUTINES. + */ + clk_cnt = jiffies; + } + } + + /* Since num_disc_nodes keys off of PLOGI, delay a bit to let + * any potential PRLIs to flush thru the SLI sub-system. + */ + msleep(50); + if (isr_cnt == psli->slistat.sliIntr) { + lpfc_sli_intr(phba); + } + + return (0); +} + +/************************************************************************/ +/* */ +/* lpfc_hba_down_prep */ +/* This routine will do LPFC uninitialization before the */ +/* HBA is reset when bringing down the SLI Layer. This will be */ +/* initialized as a SLI layer callback routine. */ +/* This routine returns 0 on success. Any other return value */ +/* indicates an error. */ +/* */ +/************************************************************************/ +int +lpfc_hba_down_prep(struct lpfc_hba * phba) +{ + /* Disable interrupts */ + writel(0, phba->HCregaddr); + readl(phba->HCregaddr); /* flush */ + + /* Cleanup potential discovery resources */ + lpfc_els_flush_rscn(phba); + lpfc_els_flush_cmd(phba); + lpfc_disc_flush_list(phba); + + return (0); +} + +/************************************************************************/ +/* */ +/* lpfc_handle_eratt */ +/* This routine will handle processing a Host Attention */ +/* Error Status event. This will be initialized */ +/* as a SLI layer callback routine. */ +/* */ +/************************************************************************/ +void +lpfc_handle_eratt(struct lpfc_hba * phba, uint32_t status) +{ + struct lpfc_sli *psli; + struct lpfc_sli_ring *pring; + struct lpfc_iocbq *iocb, *next_iocb; + IOCB_t *icmd = NULL, *cmd = NULL; + struct lpfc_scsi_buf *lpfc_cmd; + volatile uint32_t status1, status2; + void *from_slim; + unsigned long iflag; + + psli = &phba->sli; + from_slim = ((uint8_t *)phba->MBslimaddr + 0xa8); + status1 = readl( from_slim); + from_slim = ((uint8_t *)phba->MBslimaddr + 0xac); + status2 = readl( from_slim); + + if (status & HS_FFER6) { + /* Re-establishing Link */ + spin_lock_irqsave(phba->host->host_lock, iflag); + lpfc_printf_log(phba, KERN_INFO, LOG_LINK_EVENT, + "%d:1301 Re-establishing Link " + "Data: x%x x%x x%x\n", + phba->brd_no, status, status1, status2); + phba->fc_flag |= FC_ESTABLISH_LINK; + + /* + * Firmware stops when it triggled erratt with HS_FFER6. + * That could cause the I/Os dropped by the firmware. + * Error iocb (I/O) on txcmplq and let the SCSI layer + * retry it after re-establishing link. + */ + pring = &psli->ring[psli->fcp_ring]; + + list_for_each_entry_safe(iocb, next_iocb, &pring->txcmplq, + list) { + cmd = &iocb->iocb; + + /* Must be a FCP command */ + if ((cmd->ulpCommand != CMD_FCP_ICMND64_CR) && + (cmd->ulpCommand != CMD_FCP_IWRITE64_CR) && + (cmd->ulpCommand != CMD_FCP_IREAD64_CR)) { + continue; + } + + /* context1 MUST be a struct lpfc_scsi_buf */ + lpfc_cmd = (struct lpfc_scsi_buf *)(iocb->context1); + if (lpfc_cmd == 0) { + continue; + } + + list_del(&iocb->list); + pring->txcmplq_cnt--; + + if (iocb->iocb_cmpl) { + icmd = &iocb->iocb; + icmd->ulpStatus = IOSTAT_LOCAL_REJECT; + icmd->un.ulpWord[4] = IOERR_SLI_ABORTED; + (iocb->iocb_cmpl)(phba, iocb, iocb); + } else { + mempool_free( iocb, phba->iocb_mem_pool); + } + } + + /* + * There was a firmware error. Take the hba offline and then + * attempt to restart it. + */ + spin_unlock_irqrestore(phba->host->host_lock, iflag); + lpfc_offline(phba); + if (lpfc_online(phba) == 0) { /* Initialize the HBA */ + mod_timer(&phba->fc_estabtmo, jiffies + HZ * 60); + return; + } + } else { + /* The if clause above forces this code path when the status + * failure is a value other than FFER6. Do not call the offline + * twice. This is the adapter hardware error path. + */ + lpfc_printf_log(phba, KERN_ERR, LOG_INIT, + "%d:0457 Adapter Hardware Error " + "Data: x%x x%x x%x\n", + phba->brd_no, status, status1, status2); + + lpfc_offline(phba); + + /* + * Restart all traffic to this host. Since the fc_transport + * block functions (future) were not called in lpfc_offline, + * don't call them here. + */ + scsi_unblock_requests(phba->host); + } + return; +} + +/************************************************************************/ +/* */ +/* lpfc_handle_latt */ +/* This routine will handle processing a Host Attention */ +/* Link Status event. This will be initialized */ +/* as a SLI layer callback routine. */ +/* */ +/************************************************************************/ +void +lpfc_handle_latt(struct lpfc_hba * phba) +{ + struct lpfc_sli *psli; + LPFC_MBOXQ_t *pmb; + volatile uint32_t control; + unsigned long iflag; + + + spin_lock_irqsave(phba->host->host_lock, iflag); + + /* called from host_interrupt, to process LATT */ + psli = &phba->sli; + psli->slistat.linkEvent++; + + /* Get a buffer which will be used for mailbox commands */ + if ((pmb = (LPFC_MBOXQ_t *) mempool_alloc(phba->mbox_mem_pool, + GFP_ATOMIC))) { + if (lpfc_read_la(phba, pmb) == 0) { + pmb->mbox_cmpl = lpfc_mbx_cmpl_read_la; + if (lpfc_sli_issue_mbox + (phba, pmb, (MBX_NOWAIT | MBX_STOP_IOCB)) + != MBX_NOT_FINISHED) { + /* Turn off Link Attention interrupts until + CLEAR_LA done */ + psli->sliinit.sli_flag &= ~LPFC_PROCESS_LA; + control = readl(phba->HCregaddr); + control &= ~HC_LAINT_ENA; + writel(control, phba->HCregaddr); + readl(phba->HCregaddr); /* flush */ + + /* Clear Link Attention in HA REG */ + writel(HA_LATT, phba->HAregaddr); + readl(phba->HAregaddr); /* flush */ + spin_unlock_irqrestore(phba->host->host_lock, + iflag); + return; + } else { + mempool_free(pmb, phba->mbox_mem_pool); + } + } else { + mempool_free(pmb, phba->mbox_mem_pool); + } + } + + /* Clear Link Attention in HA REG */ + writel(HA_LATT, phba->HAregaddr); + readl(phba->HAregaddr); /* flush */ + lpfc_linkdown(phba); + phba->hba_state = LPFC_HBA_ERROR; + spin_unlock_irqrestore(phba->host->host_lock, iflag); + return; +} + +/************************************************************************/ +/* */ +/* lpfc_parse_vpd */ +/* This routine will parse the VPD data */ +/* */ +/************************************************************************/ +static int +lpfc_parse_vpd(struct lpfc_hba * phba, uint8_t * vpd) +{ + uint8_t lenlo, lenhi; + uint8_t *Length; + int i, j; + int finished = 0; + int index = 0; + + /* Vital Product */ + lpfc_printf_log(phba, + KERN_INFO, + LOG_INIT, + "%d:0455 Vital Product Data: x%x x%x x%x x%x\n", + phba->brd_no, + (uint32_t) vpd[0], (uint32_t) vpd[1], (uint32_t) vpd[2], + (uint32_t) vpd[3]); + do { + switch (vpd[index]) { + case 0x82: + index += 1; + lenlo = vpd[index]; + index += 1; + lenhi = vpd[index]; + index += 1; + i = ((((unsigned short)lenhi) << 8) + lenlo); + index += i; + break; + case 0x90: + index += 1; + lenlo = vpd[index]; + index += 1; + lenhi = vpd[index]; + index += 1; + i = ((((unsigned short)lenhi) << 8) + lenlo); + do { + /* Look for Serial Number */ + if ((vpd[index] == 'S') + && (vpd[index + 1] == 'N')) { + index += 2; + Length = &vpd[index]; + index += 1; + i = *Length; + j = 0; + while (i--) { + phba->SerialNumber[j++] = + vpd[index++]; + if (j == 31) + break; + } + phba->SerialNumber[j] = 0; + return (1); + } else { + index += 2; + Length = &vpd[index]; + index += 1; + j = (int)(*Length); + index += j; + i -= (3 + j); + } + } while (i > 0); + finished = 0; + break; + case 0x78: + finished = 1; + break; + default: + return (0); + } + } while (!finished); + return (1); +} + +/**************************************************/ +/* lpfc_post_buffer */ +/* */ +/* This routine will post count buffers to the */ +/* ring with the QUE_RING_BUF_CN command. This */ +/* allows 3 buffers / command to be posted. */ +/* Returns the number of buffers NOT posted. */ +/**************************************************/ +int +lpfc_post_buffer(struct lpfc_hba * phba, struct lpfc_sli_ring * pring, int cnt, + int type) +{ + IOCB_t *icmd; + struct lpfc_iocbq *iocb; + struct lpfc_dmabuf *mp1, *mp2; + + cnt += pring->missbufcnt; + + /* While there are buffers to post */ + while (cnt > 0) { + /* Allocate buffer for command iocb */ + if ((iocb = mempool_alloc(phba->iocb_mem_pool, GFP_ATOMIC)) + == 0) { + pring->missbufcnt = cnt; + return (cnt); + } + memset(iocb, 0, sizeof (struct lpfc_iocbq)); + icmd = &iocb->iocb; + + /* 2 buffers can be posted per command */ + /* Allocate buffer to post */ + mp1 = kmalloc(sizeof (struct lpfc_dmabuf), GFP_ATOMIC); + if (mp1) + mp1->virt = lpfc_mbuf_alloc(phba, MEM_PRI, + &mp1->phys); + if (mp1 == 0 || mp1->virt == 0) { + if (mp1) + kfree(mp1); + + mempool_free( iocb, phba->iocb_mem_pool); + pring->missbufcnt = cnt; + return (cnt); + } + + INIT_LIST_HEAD(&mp1->list); + /* Allocate buffer to post */ + if (cnt > 1) { + mp2 = kmalloc(sizeof (struct lpfc_dmabuf), GFP_KERNEL); + if (mp2) + mp2->virt = lpfc_mbuf_alloc(phba, MEM_PRI, + &mp2->phys); + if (mp2 == 0 || mp2->virt == 0) { + if (mp2) + kfree(mp2); + lpfc_mbuf_free(phba, mp1->virt, mp1->phys); + kfree(mp1); + mempool_free( iocb, phba->iocb_mem_pool); + pring->missbufcnt = cnt; + return (cnt); + } + + INIT_LIST_HEAD(&mp2->list); + } else { + mp2 = NULL; + } + + icmd->un.cont64[0].addrHigh = putPaddrHigh(mp1->phys); + icmd->un.cont64[0].addrLow = putPaddrLow(mp1->phys); + icmd->un.cont64[0].tus.f.bdeSize = FCELSSIZE; + icmd->ulpBdeCount = 1; + cnt--; + if (mp2) { + icmd->un.cont64[1].addrHigh = putPaddrHigh(mp2->phys); + icmd->un.cont64[1].addrLow = putPaddrLow(mp2->phys); + icmd->un.cont64[1].tus.f.bdeSize = FCELSSIZE; + cnt--; + icmd->ulpBdeCount = 2; + } + + icmd->ulpCommand = CMD_QUE_RING_BUF64_CN; + icmd->ulpIoTag = lpfc_sli_next_iotag(phba, pring); + icmd->ulpLe = 1; + + if (lpfc_sli_issue_iocb(phba, pring, iocb, 0) == IOCB_ERROR) { + lpfc_mbuf_free(phba, mp1->virt, mp1->phys); + kfree(mp1); + if (mp2) { + lpfc_mbuf_free(phba, mp2->virt, mp2->phys); + kfree(mp2); + } + mempool_free( iocb, phba->iocb_mem_pool); + pring->missbufcnt = cnt; + return (cnt); + } + lpfc_sli_ringpostbuf_put(phba, pring, mp1); + if (mp2) { + lpfc_sli_ringpostbuf_put(phba, pring, mp2); + } + } + pring->missbufcnt = 0; + return (0); +} + +/************************************************************************/ +/* */ +/* lpfc_post_rcv_buf */ +/* This routine post initial rcv buffers to the configured rings */ +/* */ +/************************************************************************/ +static int +lpfc_post_rcv_buf(struct lpfc_hba * phba) +{ + struct lpfc_sli *psli = &phba->sli; + + /* Ring 0, ELS / CT buffers */ + lpfc_post_buffer(phba, &psli->ring[LPFC_ELS_RING], LPFC_BUF_RING0, 1); + /* Ring 2 - FCP no buffers needed */ + + return 0; +} + +#define S(N,V) (((V)<<(N))|((V)>>(32-(N)))) + +/************************************************************************/ +/* */ +/* lpfc_sha_init */ +/* */ +/************************************************************************/ +static void +lpfc_sha_init(uint32_t * HashResultPointer) +{ + HashResultPointer[0] = 0x67452301; + HashResultPointer[1] = 0xEFCDAB89; + HashResultPointer[2] = 0x98BADCFE; + HashResultPointer[3] = 0x10325476; + HashResultPointer[4] = 0xC3D2E1F0; +} + +/************************************************************************/ +/* */ +/* lpfc_sha_iterate */ +/* */ +/************************************************************************/ +static void +lpfc_sha_iterate(uint32_t * HashResultPointer, uint32_t * HashWorkingPointer) +{ + int t; + uint32_t TEMP; + uint32_t A, B, C, D, E; + t = 16; + do { + HashWorkingPointer[t] = + S(1, + HashWorkingPointer[t - 3] ^ HashWorkingPointer[t - + 8] ^ + HashWorkingPointer[t - 14] ^ HashWorkingPointer[t - 16]); + } while (++t <= 79); + t = 0; + A = HashResultPointer[0]; + B = HashResultPointer[1]; + C = HashResultPointer[2]; + D = HashResultPointer[3]; + E = HashResultPointer[4]; + + do { + if (t < 20) { + TEMP = ((B & C) | ((~B) & D)) + 0x5A827999; + } else if (t < 40) { + TEMP = (B ^ C ^ D) + 0x6ED9EBA1; + } else if (t < 60) { + TEMP = ((B & C) | (B & D) | (C & D)) + 0x8F1BBCDC; + } else { + TEMP = (B ^ C ^ D) + 0xCA62C1D6; + } + TEMP += S(5, A) + E + HashWorkingPointer[t]; + E = D; + D = C; + C = S(30, B); + B = A; + A = TEMP; + } while (++t <= 79); + + HashResultPointer[0] += A; + HashResultPointer[1] += B; + HashResultPointer[2] += C; + HashResultPointer[3] += D; + HashResultPointer[4] += E; + +} + +/************************************************************************/ +/* */ +/* lpfc_challenge_key */ +/* */ +/************************************************************************/ +static void +lpfc_challenge_key(uint32_t * RandomChallenge, uint32_t * HashWorking) +{ + *HashWorking = (*RandomChallenge ^ *HashWorking); +} + +/************************************************************************/ +/* */ +/* lpfc_hba_init */ +/* */ +/************************************************************************/ +void +lpfc_hba_init(struct lpfc_hba *phba, uint32_t *hbainit) +{ + int t; + uint32_t *HashWorking; + uint32_t *pwwnn = phba->wwnn; + + HashWorking = kmalloc(80 * sizeof(uint32_t), GFP_ATOMIC); + if (!HashWorking) + return; + + memset(HashWorking, 0, (80 * sizeof(uint32_t))); + HashWorking[0] = HashWorking[78] = *pwwnn++; + HashWorking[1] = HashWorking[79] = *pwwnn; + + for (t = 0; t < 7; t++) + lpfc_challenge_key(phba->RandomData + t, HashWorking + t); + + lpfc_sha_init(hbainit); + lpfc_sha_iterate(hbainit, HashWorking); + kfree(HashWorking); +} + +static void +lpfc_consistent_bind_cleanup(struct lpfc_hba * phba) +{ + struct lpfc_bindlist *bdlp, *next_bdlp; + + list_for_each_entry_safe(bdlp, next_bdlp, + &phba->fc_nlpbind_list, nlp_listp) { + list_del(&bdlp->nlp_listp); + mempool_free( bdlp, phba->bind_mem_pool); + } + phba->fc_bind_cnt = 0; +} + +void +lpfc_cleanup(struct lpfc_hba * phba, uint32_t save_bind) +{ + struct lpfc_nodelist *ndlp, *next_ndlp; + + /* clean up phba - lpfc specific */ + lpfc_can_disctmo(phba); + list_for_each_entry_safe(ndlp, next_ndlp, &phba->fc_nlpunmap_list, + nlp_listp) { + lpfc_nlp_remove(phba, ndlp); + } + + list_for_each_entry_safe(ndlp, next_ndlp, &phba->fc_nlpmap_list, + nlp_listp) { + lpfc_nlp_remove(phba, ndlp); + } + + list_for_each_entry_safe(ndlp, next_ndlp, &phba->fc_unused_list, + nlp_listp) { + lpfc_nlp_list(phba, ndlp, NLP_NO_LIST); + } + + list_for_each_entry_safe(ndlp, next_ndlp, &phba->fc_plogi_list, + nlp_listp) { + lpfc_nlp_remove(phba, ndlp); + } + + list_for_each_entry_safe(ndlp, next_ndlp, &phba->fc_adisc_list, + nlp_listp) { + lpfc_nlp_remove(phba, ndlp); + } + + list_for_each_entry_safe(ndlp, next_ndlp, &phba->fc_reglogin_list, + nlp_listp) { + lpfc_nlp_remove(phba, ndlp); + } + + list_for_each_entry_safe(ndlp, next_ndlp, &phba->fc_prli_list, + nlp_listp) { + lpfc_nlp_remove(phba, ndlp); + } + + list_for_each_entry_safe(ndlp, next_ndlp, &phba->fc_npr_list, + nlp_listp) { + lpfc_nlp_remove(phba, ndlp); + } + + if (save_bind == 0) { + lpfc_consistent_bind_cleanup(phba); + } + + INIT_LIST_HEAD(&phba->fc_nlpmap_list); + INIT_LIST_HEAD(&phba->fc_nlpunmap_list); + INIT_LIST_HEAD(&phba->fc_unused_list); + INIT_LIST_HEAD(&phba->fc_plogi_list); + INIT_LIST_HEAD(&phba->fc_adisc_list); + INIT_LIST_HEAD(&phba->fc_reglogin_list); + INIT_LIST_HEAD(&phba->fc_prli_list); + INIT_LIST_HEAD(&phba->fc_npr_list); + + phba->fc_map_cnt = 0; + phba->fc_unmap_cnt = 0; + phba->fc_plogi_cnt = 0; + phba->fc_adisc_cnt = 0; + phba->fc_reglogin_cnt = 0; + phba->fc_prli_cnt = 0; + phba->fc_npr_cnt = 0; + phba->fc_unused_cnt= 0; + return; +} + +void +lpfc_establish_link_tmo(unsigned long ptr) +{ + struct lpfc_hba *phba = (struct lpfc_hba *)ptr; + unsigned long iflag; + + spin_lock_irqsave(phba->host->host_lock, iflag); + + /* Re-establishing Link, timer expired */ + lpfc_printf_log(phba, KERN_ERR, LOG_LINK_EVENT, + "%d:1300 Re-establishing Link, timer expired " + "Data: x%x x%x\n", + phba->brd_no, phba->fc_flag, phba->hba_state); + phba->fc_flag &= ~FC_ESTABLISH_LINK; + spin_unlock_irqrestore(phba->host->host_lock, iflag); +} + +int +lpfc_online(struct lpfc_hba * phba) +{ + if (!phba) + return 0; + + if (!(phba->fc_flag & FC_OFFLINE_MODE)) + return 0; + + lpfc_printf_log(phba, + KERN_WARNING, + LOG_INIT, + "%d:0458 Bring Adapter online\n", + phba->brd_no); + + if (!lpfc_sli_queue_setup(phba)) + return 1; + + if (lpfc_sli_hba_setup(phba)) /* Initialize the HBA */ + return 1; + + phba->fc_flag &= ~FC_OFFLINE_MODE; + + /* + * Restart all traffic to this host. Since the fc_transport block + * functions (future) were not called in lpfc_offline, don't call them + * here. + */ + scsi_unblock_requests(phba->host); + return 0; +} + +int +lpfc_offline(struct lpfc_hba * phba) +{ + struct lpfc_sli_ring *pring; + struct lpfc_sli *psli; + unsigned long iflag; + int i = 0; + + if (!phba) + return 0; + + if (phba->fc_flag & FC_OFFLINE_MODE) + return 0; + + /* + * Don't call the fc_transport block api (future). The device is + * going offline and causing a timer to fire in the midlayer is + * unproductive. Just block all new requests until the driver + * comes back online. + */ + scsi_block_requests(phba->host); + psli = &phba->sli; + pring = &psli->ring[psli->fcp_ring]; + + lpfc_linkdown(phba); + + /* The linkdown event takes 30 seconds to timeout. */ + while (pring->txcmplq_cnt) { + mdelay(10); + if (i++ > 3000) + break; + } + + /* stop all timers associated with this hba */ + spin_lock_irqsave(phba->host->host_lock, iflag); + lpfc_stop_timer(phba); + spin_unlock_irqrestore(phba->host->host_lock, iflag); + + lpfc_printf_log(phba, + KERN_WARNING, + LOG_INIT, + "%d:0460 Bring Adapter offline\n", + phba->brd_no); + + /* Bring down the SLI Layer and cleanup. The HBA is offline + now. */ + lpfc_sli_hba_down(phba); + lpfc_cleanup(phba, 1); + phba->fc_flag |= FC_OFFLINE_MODE; + return 0; +} + +/****************************************************************************** +* Function name : lpfc_scsi_free +* +* Description : Called from fc_detach to free scsi tgt / lun resources +* +******************************************************************************/ +int +lpfc_scsi_free(struct lpfc_hba * phba) +{ + struct lpfc_target *targetp; + int i; + + for (i = 0; i < MAX_FCP_TARGET; i++) { + targetp = phba->device_queue_hash[i]; + if (targetp) { + kfree(targetp); + phba->device_queue_hash[i] = NULL; + } + } + return 0; +} + +static void +lpfc_wakeup_event(struct lpfc_hba * phba, fcEVTHDR_t * ep) +{ + ep->e_mode &= ~E_SLEEPING_MODE; + switch (ep->e_mask) { + case FC_REG_LINK_EVENT: + wake_up_interruptible(&phba->linkevtwq); + break; + case FC_REG_RSCN_EVENT: + wake_up_interruptible(&phba->rscnevtwq); + break; + case FC_REG_CT_EVENT: + wake_up_interruptible(&phba->ctevtwq); + break; + } + return; +} + +int +lpfc_put_event(struct lpfc_hba * phba, uint32_t evcode, uint32_t evdata0, + void * evdata1, uint32_t evdata2, uint32_t evdata3) +{ + fcEVT_t *ep; + fcEVTHDR_t *ehp = phba->fc_evt_head; + int found = 0; + void *fstype = NULL; + struct lpfc_dmabuf *mp; + struct lpfc_sli_ct_request *ctp; + struct lpfc_hba_event *rec; + uint32_t evtype; + + switch (evcode) { + case HBA_EVENT_RSCN: + evtype = FC_REG_RSCN_EVENT; + break; + case HBA_EVENT_LINK_DOWN: + case HBA_EVENT_LINK_UP: + evtype = FC_REG_LINK_EVENT; + break; + default: + evtype = FC_REG_CT_EVENT; + } + + if (evtype == FC_REG_RSCN_EVENT || evtype == FC_REG_LINK_EVENT) { + rec = &phba->hbaevt[phba->hba_event_put]; + rec->fc_eventcode = evcode; + rec->fc_evdata1 = evdata0; + rec->fc_evdata2 = (uint32_t)(unsigned long)evdata1; + rec->fc_evdata3 = evdata2; + rec->fc_evdata4 = evdata3; + + phba->hba_event_put++; + if (phba->hba_event_put >= MAX_HBAEVT) + phba->hba_event_put = 0; + + if (phba->hba_event_put == phba->hba_event_get) { + phba->hba_event_missed++; + phba->hba_event_get++; + if (phba->hba_event_get >= MAX_HBAEVT) + phba->hba_event_get = 0; + } + } + + if (evtype == FC_REG_CT_EVENT) { + mp = (struct lpfc_dmabuf *) evdata1; + ctp = (struct lpfc_sli_ct_request *) mp->virt; + fstype = (void *)(ulong) (ctp->FsType); + } + + while (ehp && ((ehp->e_mask != evtype) || (ehp->e_type != fstype))) + ehp = (fcEVTHDR_t *) ehp->e_next_header; + + if (!ehp) + return (0); + + ep = ehp->e_head; + + while (ep && !(found)) { + if (ep->evt_sleep) { + switch (evtype) { + case FC_REG_CT_EVENT: + if ((ep->evt_type == + (void *)(ulong) FC_FSTYPE_ALL) + || (ep->evt_type == fstype)) { + found++; + ep->evt_data0 = evdata0; /* tag */ + ep->evt_data1 = evdata1; /* buffer + ptr */ + ep->evt_data2 = evdata2; /* count */ + ep->evt_sleep = 0; + if (ehp->e_mode & E_SLEEPING_MODE) { + ehp->e_flag |= + E_GET_EVENT_ACTIVE; + lpfc_wakeup_event(phba, ehp); + } + /* For FC_REG_CT_EVENT just give it to + first one found */ + } + break; + default: + found++; + ep->evt_data0 = evdata0; + ep->evt_data1 = evdata1; + ep->evt_data2 = evdata2; + ep->evt_sleep = 0; + if ((ehp->e_mode & E_SLEEPING_MODE) + && !(ehp->e_flag & E_GET_EVENT_ACTIVE)) { + ehp->e_flag |= E_GET_EVENT_ACTIVE; + lpfc_wakeup_event(phba, ehp); + } + /* For all other events, give it to every one + waiting */ + break; + } + } + ep = ep->evt_next; + } + if (evtype == FC_REG_LINK_EVENT) + phba->nport_event_cnt++; + + return (found); +} + +int +lpfc_stop_timer(struct lpfc_hba * phba) +{ + struct lpfc_sli *psli = &phba->sli; + + /* Instead of a timer, this has been converted to a + * deferred procedding list. + */ + while (!list_empty(&phba->freebufList)) { + struct lpfc_dmabuf *mp; + + mp = (struct lpfc_dmabuf *)(phba->freebufList.next); + if (mp) { + lpfc_mbuf_free(phba, mp->virt, mp->phys); + list_del(&mp->list); + kfree(mp); + } + } + + del_timer_sync(&phba->fc_estabtmo); + del_timer_sync(&phba->fc_disctmo); + del_timer_sync(&phba->fc_scantmo); + del_timer_sync(&phba->fc_fdmitmo); + del_timer_sync(&phba->els_tmofunc); + psli = &phba->sli; + del_timer_sync(&psli->mbox_tmo); + return(1); +} diff --git a/drivers/scsi/lpfc/lpfc_logmsg.h b/drivers/scsi/lpfc/lpfc_logmsg.h new file mode 100644 index 000000000..5540e23f9 --- /dev/null +++ b/drivers/scsi/lpfc/lpfc_logmsg.h @@ -0,0 +1,46 @@ +/******************************************************************* + * This file is part of the Emulex Linux Device Driver for * + * Enterprise Fibre Channel Host Bus Adapters. * + * Refer to the README file included with this package for * + * driver version and adapter support. * + * Copyright (C) 2004 Emulex Corporation. * + * www.emulex.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, a copy of which * + * can be found in the file COPYING included with this package. * + *******************************************************************/ + +/* + * $Id: lpfc_logmsg.h 1.31 2004/08/26 19:17:52EDT sf_support Exp $ + */ + +#ifndef _H_LPFC_LOGMSG +#define _H_LPFC_LOGMSG + +#define LOG_ELS 0x1 /* ELS events */ +#define LOG_DISCOVERY 0x2 /* Link discovery events */ +#define LOG_MBOX 0x4 /* Mailbox events */ +#define LOG_INIT 0x8 /* Initialization events */ +#define LOG_LINK_EVENT 0x10 /* Link events */ +#define LOG_IP 0x20 /* IP traffic history */ +#define LOG_FCP 0x40 /* FCP traffic history */ +#define LOG_NODE 0x80 /* Node table events */ +#define LOG_MISC 0x400 /* Miscellaneous events */ +#define LOG_SLI 0x800 /* SLI events */ +#define LOG_CHK_COND 0x1000 /* FCP Check condition flag */ +#define LOG_LIBDFC 0x2000 /* Libdfc events */ +#define LOG_ALL_MSG 0xffff /* LOG all messages */ + +#define lpfc_printf_log(phba, level, mask, fmt, arg...) \ + { if (((mask) &(phba)->cfg_log_verbose) || (level[1] <= '3')) \ + dev_printk(level, &((phba)->pcidev)->dev, fmt, ##arg); } +#endif + diff --git a/drivers/scsi/lpfc/lpfc_mbox.c b/drivers/scsi/lpfc/lpfc_mbox.c new file mode 100644 index 000000000..436d1b7c5 --- /dev/null +++ b/drivers/scsi/lpfc/lpfc_mbox.c @@ -0,0 +1,672 @@ +/******************************************************************* + * This file is part of the Emulex Linux Device Driver for * + * Enterprise Fibre Channel Host Bus Adapters. * + * Refer to the README file included with this package for * + * driver version and adapter support. * + * Copyright (C) 2004 Emulex Corporation. * + * www.emulex.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, a copy of which * + * can be found in the file COPYING included with this package. * + *******************************************************************/ + +/* + * $Id: lpfc_mbox.c 1.70 2004/11/18 17:33:04EST sf_support Exp $ + */ +#include +#include +#include +#include +#include +#include +#include "lpfc_sli.h" +#include "lpfc_disc.h" +#include "lpfc_scsi.h" +#include "lpfc.h" +#include "lpfc_crtn.h" +#include "lpfc_hw.h" +#include "lpfc_logmsg.h" +#include "lpfc_mem.h" +#include "lpfc_compat.h" + +/**********************************************/ + +/* mailbox command */ +/**********************************************/ +void +lpfc_dump_mem(struct lpfc_hba * phba, LPFC_MBOXQ_t * pmb) +{ + MAILBOX_t *mb; + + mb = &pmb->mb; + /* Setup to dump VPD region */ + memset(pmb, 0, sizeof (LPFC_MBOXQ_t)); + mb->mbxCommand = MBX_DUMP_MEMORY; + mb->un.varDmp.cv = 1; + mb->un.varDmp.type = DMP_NV_PARAMS; + mb->un.varDmp.region_id = DMP_REGION_VPD; + mb->un.varDmp.word_cnt = (DMP_VPD_SIZE / sizeof (uint32_t)); + + mb->un.varDmp.co = 0; + mb->un.varDmp.resp_offset = 0; + mb->mbxOwner = OWN_HOST; + return; +} + +/**********************************************/ +/* lpfc_read_nv Issue a READ NVPARAM */ +/* mailbox command */ +/**********************************************/ +void +lpfc_read_nv(struct lpfc_hba * phba, LPFC_MBOXQ_t * pmb) +{ + MAILBOX_t *mb; + + mb = &pmb->mb; + memset(pmb, 0, sizeof (LPFC_MBOXQ_t)); + mb->mbxCommand = MBX_READ_NV; + mb->mbxOwner = OWN_HOST; + return; +} + +/**********************************************/ +/* lpfc_read_la Issue a READ LA */ +/* mailbox command */ +/**********************************************/ +int +lpfc_read_la(struct lpfc_hba * phba, LPFC_MBOXQ_t * pmb) +{ + MAILBOX_t *mb; + struct lpfc_dmabuf *mp; + struct lpfc_sli *psli; + + psli = &phba->sli; + mb = &pmb->mb; + memset(pmb, 0, sizeof (LPFC_MBOXQ_t)); + + /* Get a buffer to hold the loop map */ + if (((mp = kmalloc(sizeof (struct lpfc_dmabuf), GFP_ATOMIC)) == 0) || + ((mp->virt = lpfc_mbuf_alloc(phba, 0, &(mp->phys))) == 0)) { + if (mp) + kfree(mp); + mb->mbxCommand = MBX_READ_LA64; + /* READ_LA: no buffers */ + lpfc_printf_log(phba, + KERN_WARNING, + LOG_MBOX, + "%d:0300 READ_LA: no buffers\n", + phba->brd_no); + return (1); + } + INIT_LIST_HEAD(&mp->list); + mb->mbxCommand = MBX_READ_LA64; + mb->un.varReadLA.un.lilpBde64.tus.f.bdeSize = 128; + mb->un.varReadLA.un.lilpBde64.addrHigh = putPaddrHigh(mp->phys); + mb->un.varReadLA.un.lilpBde64.addrLow = putPaddrLow(mp->phys); + + /* Sync the mailbox data with its PCI memory address now. */ + pci_dma_sync_single_for_device(phba->pcidev, mp->phys, LPFC_BPL_SIZE, + PCI_DMA_TODEVICE); + + /* Save address for later completion and set the owner to host so that + * the FW knows this mailbox is available for processing. + */ + pmb->context1 = (uint8_t *) mp; + mb->mbxOwner = OWN_HOST; + return (0); +} + +/**********************************************/ +/* lpfc_clear_la Issue a CLEAR LA */ +/* mailbox command */ +/**********************************************/ +void +lpfc_clear_la(struct lpfc_hba * phba, LPFC_MBOXQ_t * pmb) +{ + MAILBOX_t *mb; + + mb = &pmb->mb; + memset(pmb, 0, sizeof (LPFC_MBOXQ_t)); + + mb->un.varClearLA.eventTag = phba->fc_eventTag; + mb->mbxCommand = MBX_CLEAR_LA; + mb->mbxOwner = OWN_HOST; + return; +} + +/**************************************************/ +/* lpfc_config_link Issue a CONFIG LINK */ +/* mailbox command */ +/**************************************************/ +void +lpfc_config_link(struct lpfc_hba * phba, LPFC_MBOXQ_t * pmb) +{ + MAILBOX_t *mb = &pmb->mb; + memset(pmb, 0, sizeof (LPFC_MBOXQ_t)); + + /* NEW_FEATURE + * SLI-2, Coalescing Response Feature. + */ + if (phba->cfg_cr_delay) { + mb->un.varCfgLnk.cr = 1; + mb->un.varCfgLnk.ci = 1; + mb->un.varCfgLnk.cr_delay = phba->cfg_cr_delay; + mb->un.varCfgLnk.cr_count = phba->cfg_cr_count; + } + + mb->un.varCfgLnk.myId = phba->fc_myDID; + mb->un.varCfgLnk.edtov = phba->fc_edtov; + mb->un.varCfgLnk.arbtov = phba->fc_arbtov; + mb->un.varCfgLnk.ratov = phba->fc_ratov; + mb->un.varCfgLnk.rttov = phba->fc_rttov; + mb->un.varCfgLnk.altov = phba->fc_altov; + mb->un.varCfgLnk.crtov = phba->fc_crtov; + mb->un.varCfgLnk.citov = phba->fc_citov; + + if (phba->cfg_ack0) + mb->un.varCfgLnk.ack0_enable = 1; + + mb->mbxCommand = MBX_CONFIG_LINK; + mb->mbxOwner = OWN_HOST; + return; +} + +/**********************************************/ +/* lpfc_init_link Issue an INIT LINK */ +/* mailbox command */ +/**********************************************/ +void +lpfc_init_link(struct lpfc_hba * phba, + LPFC_MBOXQ_t * pmb, uint32_t topology, uint32_t linkspeed) +{ + lpfc_vpd_t *vpd; + struct lpfc_sli *psli; + MAILBOX_t *mb; + + mb = &pmb->mb; + memset(pmb, 0, sizeof (LPFC_MBOXQ_t)); + + psli = &phba->sli; + switch (topology) { + case FLAGS_TOPOLOGY_MODE_LOOP_PT: + mb->un.varInitLnk.link_flags = FLAGS_TOPOLOGY_MODE_LOOP; + mb->un.varInitLnk.link_flags |= FLAGS_TOPOLOGY_FAILOVER; + break; + case FLAGS_TOPOLOGY_MODE_PT_PT: + mb->un.varInitLnk.link_flags = FLAGS_TOPOLOGY_MODE_PT_PT; + break; + case FLAGS_TOPOLOGY_MODE_LOOP: + mb->un.varInitLnk.link_flags = FLAGS_TOPOLOGY_MODE_LOOP; + break; + case FLAGS_TOPOLOGY_MODE_PT_LOOP: + mb->un.varInitLnk.link_flags = FLAGS_TOPOLOGY_MODE_PT_PT; + mb->un.varInitLnk.link_flags |= FLAGS_TOPOLOGY_FAILOVER; + break; + } + + /* NEW_FEATURE + * Setting up the link speed + */ + vpd = &phba->vpd; + if (vpd->rev.feaLevelHigh >= 0x02){ + switch(linkspeed){ + case LINK_SPEED_1G: + case LINK_SPEED_2G: + case LINK_SPEED_4G: + mb->un.varInitLnk.link_flags |= + FLAGS_LINK_SPEED; + mb->un.varInitLnk.link_speed = linkspeed; + break; + case LINK_SPEED_AUTO: + default: + mb->un.varInitLnk.link_speed = + LINK_SPEED_AUTO; + break; + } + + } + else + mb->un.varInitLnk.link_speed = LINK_SPEED_AUTO; + + mb->mbxCommand = (volatile uint8_t)MBX_INIT_LINK; + mb->mbxOwner = OWN_HOST; + mb->un.varInitLnk.fabric_AL_PA = phba->fc_pref_ALPA; + return; +} + +/**********************************************/ +/* lpfc_read_sparam Issue a READ SPARAM */ +/* mailbox command */ +/**********************************************/ +int +lpfc_read_sparam(struct lpfc_hba * phba, LPFC_MBOXQ_t * pmb) +{ + struct lpfc_dmabuf *mp; + MAILBOX_t *mb; + struct lpfc_sli *psli; + + psli = &phba->sli; + mb = &pmb->mb; + memset(pmb, 0, sizeof (LPFC_MBOXQ_t)); + + mb->mbxOwner = OWN_HOST; + + /* Get a buffer to hold the HBAs Service Parameters */ + + if (((mp = kmalloc(sizeof (struct lpfc_dmabuf), GFP_ATOMIC)) == 0) || + ((mp->virt = lpfc_mbuf_alloc(phba, 0, &(mp->phys))) == 0)) { + if (mp) + kfree(mp); + mb->mbxCommand = MBX_READ_SPARM64; + /* READ_SPARAM: no buffers */ + lpfc_printf_log(phba, + KERN_WARNING, + LOG_MBOX, + "%d:0301 READ_SPARAM: no buffers\n", + phba->brd_no); + return (1); + } + INIT_LIST_HEAD(&mp->list); + mb->mbxCommand = MBX_READ_SPARM64; + mb->un.varRdSparm.un.sp64.tus.f.bdeSize = sizeof (struct serv_parm); + mb->un.varRdSparm.un.sp64.addrHigh = putPaddrHigh(mp->phys); + mb->un.varRdSparm.un.sp64.addrLow = putPaddrLow(mp->phys); + + pci_dma_sync_single_for_device(phba->pcidev, mp->phys, LPFC_BPL_SIZE, + PCI_DMA_TODEVICE); + + /* save address for completion */ + pmb->context1 = mp; + + return (0); +} + +/********************************************/ +/* lpfc_unreg_did Issue a UNREG_DID */ +/* mailbox command */ +/********************************************/ +void +lpfc_unreg_did(struct lpfc_hba * phba, uint32_t did, LPFC_MBOXQ_t * pmb) +{ + MAILBOX_t *mb; + + mb = &pmb->mb; + memset(pmb, 0, sizeof (LPFC_MBOXQ_t)); + + mb->un.varUnregDID.did = did; + + mb->mbxCommand = MBX_UNREG_D_ID; + mb->mbxOwner = OWN_HOST; + return; +} + +/***********************************************/ + +/* command to write slim */ +/***********************************************/ +void +lpfc_set_slim(struct lpfc_hba * phba, LPFC_MBOXQ_t * pmb, uint32_t addr, + uint32_t value) +{ + MAILBOX_t *mb; + + mb = &pmb->mb; + memset(pmb, 0, sizeof (LPFC_MBOXQ_t)); + + /* addr = 0x090597 is AUTO ABTS disable for ELS commands */ + /* addr = 0x052198 is DELAYED ABTS enable for ELS commands */ + + /* + * Always turn on DELAYED ABTS for ELS timeouts + */ + if ((addr == 0x052198) && (value == 0)) + value = 1; + + mb->un.varWords[0] = addr; + mb->un.varWords[1] = value; + + mb->mbxCommand = MBX_SET_SLIM; + mb->mbxOwner = OWN_HOST; + return; +} + +/**********************************************/ +/* lpfc_read_nv Issue a READ CONFIG */ +/* mailbox command */ +/**********************************************/ +void +lpfc_read_config(struct lpfc_hba * phba, LPFC_MBOXQ_t * pmb) +{ + MAILBOX_t *mb; + + mb = &pmb->mb; + memset(pmb, 0, sizeof (LPFC_MBOXQ_t)); + + mb->mbxCommand = MBX_READ_CONFIG; + mb->mbxOwner = OWN_HOST; + return; +} + +/********************************************/ +/* lpfc_reg_login Issue a REG_LOGIN */ +/* mailbox command */ +/********************************************/ +int +lpfc_reg_login(struct lpfc_hba * phba, + uint32_t did, uint8_t * param, LPFC_MBOXQ_t * pmb, uint32_t flag) +{ + uint8_t *sparam; + struct lpfc_dmabuf *mp; + MAILBOX_t *mb; + struct lpfc_sli *psli; + + psli = &phba->sli; + mb = &pmb->mb; + memset(pmb, 0, sizeof (LPFC_MBOXQ_t)); + + mb->un.varRegLogin.rpi = 0; + mb->un.varRegLogin.did = did; + mb->un.varWords[30] = flag; /* Set flag to issue action on cmpl */ + + mb->mbxOwner = OWN_HOST; + + /* Get a buffer to hold NPorts Service Parameters */ + if (((mp = kmalloc(sizeof (struct lpfc_dmabuf), GFP_ATOMIC)) == 0) || + ((mp->virt = lpfc_mbuf_alloc(phba, 0, &(mp->phys))) == 0)) { + if (mp) + kfree(mp); + + mb->mbxCommand = MBX_REG_LOGIN64; + /* REG_LOGIN: no buffers */ + lpfc_printf_log(phba, + KERN_WARNING, + LOG_MBOX, + "%d:0302 REG_LOGIN: no buffers Data x%x x%x\n", + phba->brd_no, + (uint32_t) did, (uint32_t) flag); + return (1); + } + INIT_LIST_HEAD(&mp->list); + sparam = mp->virt; + + /* Copy param's into a new buffer */ + memcpy(sparam, param, sizeof (struct serv_parm)); + + /* Sync the mailbox data with its PCI memory address now. */ + pci_dma_sync_single_for_device(phba->pcidev, mp->phys, LPFC_BPL_SIZE, + PCI_DMA_TODEVICE); + + /* save address for completion */ + pmb->context1 = (uint8_t *) mp; + + mb->mbxCommand = MBX_REG_LOGIN64; + mb->un.varRegLogin.un.sp64.tus.f.bdeSize = sizeof (struct serv_parm); + mb->un.varRegLogin.un.sp64.addrHigh = putPaddrHigh(mp->phys); + mb->un.varRegLogin.un.sp64.addrLow = putPaddrLow(mp->phys); + + return (0); +} + +/**********************************************/ +/* lpfc_unreg_login Issue a UNREG_LOGIN */ +/* mailbox command */ +/**********************************************/ +void +lpfc_unreg_login(struct lpfc_hba * phba, uint32_t rpi, LPFC_MBOXQ_t * pmb) +{ + MAILBOX_t *mb; + + mb = &pmb->mb; + memset(pmb, 0, sizeof (LPFC_MBOXQ_t)); + + mb->un.varUnregLogin.rpi = (uint16_t) rpi; + mb->un.varUnregLogin.rsvd1 = 0; + + mb->mbxCommand = MBX_UNREG_LOGIN; + mb->mbxOwner = OWN_HOST; + return; +} + +static void +lpfc_config_pcb_setup(struct lpfc_hba * phba) +{ + struct lpfc_sli *psli = &phba->sli; + struct lpfc_sli_ring *pring; + PCB_t *pcbp = &phba->slim2p->pcb; + LPFC_RING_INIT_t *pringinit; + dma_addr_t pdma_addr; + uint32_t offset; + uint32_t iocbCnt; + int i; + + psli->MBhostaddr = (uint32_t *)&phba->slim2p->mbx; + pcbp->maxRing = (psli->sliinit.num_rings - 1); + + iocbCnt = 0; + for (i = 0; i < psli->sliinit.num_rings; i++) { + pringinit = &psli->sliinit.ringinit[i]; + pring = &psli->ring[i]; + /* A ring MUST have both cmd and rsp entries defined to be + valid */ + if ((pringinit->numCiocb == 0) || (pringinit->numRiocb == 0)) { + pcbp->rdsc[i].cmdEntries = 0; + pcbp->rdsc[i].rspEntries = 0; + pcbp->rdsc[i].cmdAddrHigh = 0; + pcbp->rdsc[i].rspAddrHigh = 0; + pcbp->rdsc[i].cmdAddrLow = 0; + pcbp->rdsc[i].rspAddrLow = 0; + pring->cmdringaddr = NULL; + pring->rspringaddr = NULL; + continue; + } + /* Command ring setup for ring */ + pring->cmdringaddr = + (void *)&phba->slim2p->IOCBs[iocbCnt]; + pcbp->rdsc[i].cmdEntries = pringinit->numCiocb; + + offset = (uint8_t *)&phba->slim2p->IOCBs[iocbCnt] - + (uint8_t *)phba->slim2p; + pdma_addr = phba->slim2p_mapping + offset; + pcbp->rdsc[i].cmdAddrHigh = putPaddrHigh(pdma_addr); + pcbp->rdsc[i].cmdAddrLow = putPaddrLow(pdma_addr); + iocbCnt += pringinit->numCiocb; + + /* Response ring setup for ring */ + pring->rspringaddr = + (void *)&phba->slim2p->IOCBs[iocbCnt]; + + pcbp->rdsc[i].rspEntries = pringinit->numRiocb; + offset = (uint8_t *)&phba->slim2p->IOCBs[iocbCnt] - + (uint8_t *)phba->slim2p; + pdma_addr = phba->slim2p_mapping + offset; + pcbp->rdsc[i].rspAddrHigh = putPaddrHigh(pdma_addr); + pcbp->rdsc[i].rspAddrLow = putPaddrLow(pdma_addr); + iocbCnt += pringinit->numRiocb; + } +} + +void +lpfc_read_rev(struct lpfc_hba * phba, LPFC_MBOXQ_t * pmb) +{ + MAILBOX_t *mb; + + mb = &pmb->mb; + memset(pmb, 0, sizeof (LPFC_MBOXQ_t)); + mb->un.varRdRev.cv = 1; + mb->mbxCommand = MBX_READ_REV; + mb->mbxOwner = OWN_HOST; + return; +} + +void +lpfc_config_ring(struct lpfc_hba * phba, int ring, LPFC_MBOXQ_t * pmb) +{ + int i; + MAILBOX_t *mb = &pmb->mb; + struct lpfc_sli *psli; + LPFC_RING_INIT_t *pring; + + memset(pmb, 0, sizeof (LPFC_MBOXQ_t)); + + mb->un.varCfgRing.ring = ring; + mb->un.varCfgRing.maxOrigXchg = 0; + mb->un.varCfgRing.maxRespXchg = 0; + mb->un.varCfgRing.recvNotify = 1; + + psli = &phba->sli; + pring = &psli->sliinit.ringinit[ring]; + mb->un.varCfgRing.numMask = pring->num_mask; + mb->mbxCommand = MBX_CONFIG_RING; + mb->mbxOwner = OWN_HOST; + + /* Is this ring configured for a specific profile */ + if (pring->prt[0].profile) { + mb->un.varCfgRing.profile = pring->prt[0].profile; + return; + } + + /* Otherwise we setup specific rctl / type masks for this ring */ + for (i = 0; i < pring->num_mask; i++) { + mb->un.varCfgRing.rrRegs[i].rval = pring->prt[i].rctl; + if (mb->un.varCfgRing.rrRegs[i].rval != FC_ELS_REQ) + mb->un.varCfgRing.rrRegs[i].rmask = 0xff; + else + mb->un.varCfgRing.rrRegs[i].rmask = 0xfe; + mb->un.varCfgRing.rrRegs[i].tval = pring->prt[i].type; + mb->un.varCfgRing.rrRegs[i].tmask = 0xff; + } + + return; +} + +void +lpfc_config_port(struct lpfc_hba * phba, LPFC_MBOXQ_t * pmb) +{ + MAILBOX_t *mb = &pmb->mb; + dma_addr_t pdma_addr; + uint32_t bar_low, bar_high; + size_t offset; + HGP hgp; + void *to_slim; + + memset(pmb, 0, sizeof(LPFC_MBOXQ_t)); + mb->mbxCommand = MBX_CONFIG_PORT; + mb->mbxOwner = OWN_HOST; + + mb->un.varCfgPort.pcbLen = sizeof(PCB_t); + offset = (uint8_t *)&phba->slim2p->pcb - (uint8_t *)phba->slim2p; + pdma_addr = phba->slim2p_mapping + offset; + mb->un.varCfgPort.pcbLow = putPaddrLow(pdma_addr); + mb->un.varCfgPort.pcbHigh = putPaddrHigh(pdma_addr); + + /* Now setup pcb */ + phba->slim2p->pcb.type = TYPE_NATIVE_SLI2; + phba->slim2p->pcb.feature = FEATURE_INITIAL_SLI2; + + /* Setup Mailbox pointers */ + phba->slim2p->pcb.mailBoxSize = sizeof(MAILBOX_t); + offset = (uint8_t *)&phba->slim2p->mbx - (uint8_t *)phba->slim2p; + pdma_addr = phba->slim2p_mapping + offset; + phba->slim2p->pcb.mbAddrHigh = putPaddrHigh(pdma_addr); + phba->slim2p->pcb.mbAddrLow = putPaddrLow(pdma_addr); + + /* + * Setup Host Group ring pointer. + * + * For efficiency reasons, the ring get/put pointers can be + * placed in adapter memory (SLIM) rather than in host memory. + * This allows firmware to avoid PCI reads/writes when updating + * and checking pointers. + * + * The firmware recognizes the use of SLIM memory by comparing + * the address of the get/put pointers structure with that of + * the SLIM BAR (BAR0). + * + * Caution: be sure to use the PCI config space value of BAR0/BAR1 + * (the hardware's view of the base address), not the OS's + * value of pci_resource_start() as the OS value may be a cookie + * for ioremap/iomap. + */ + + + pci_read_config_dword(phba->pcidev, PCI_BASE_ADDRESS_0, &bar_low); + pci_read_config_dword(phba->pcidev, PCI_BASE_ADDRESS_1, &bar_high); + + + /* mask off BAR0's flag bits 0 - 3 */ + phba->slim2p->pcb.hgpAddrLow = (bar_low & PCI_BASE_ADDRESS_MEM_MASK) + + (SLIMOFF*sizeof(uint32_t)); + if (bar_low & PCI_BASE_ADDRESS_MEM_TYPE_64) + phba->slim2p->pcb.hgpAddrHigh = bar_high; + else + phba->slim2p->pcb.hgpAddrHigh = 0; + /* write HGP data to SLIM at the required longword offset */ + memset(&hgp, 0, sizeof(HGP)); + to_slim = (uint8_t *)phba->MBslimaddr + (SLIMOFF*sizeof (uint32_t)); + lpfc_memcpy_to_slim(to_slim, &hgp, sizeof (HGP)); + + /* Setup Port Group ring pointer */ + offset = (uint8_t *)&phba->slim2p->mbx.us.s2.port - + (uint8_t *)phba->slim2p; + pdma_addr = phba->slim2p_mapping + offset; + phba->slim2p->pcb.pgpAddrHigh = putPaddrHigh(pdma_addr); + phba->slim2p->pcb.pgpAddrLow = putPaddrLow(pdma_addr); + + /* Use callback routine to setp rings in the pcb */ + lpfc_config_pcb_setup(phba); + + /* special handling for LC HBAs */ + if (lpfc_is_LC_HBA(phba->pcidev->device)) { + uint32_t hbainit[5]; + + lpfc_hba_init(phba, hbainit); + + memcpy(&mb->un.varCfgPort.hbainit, hbainit, 20); + } + + /* Swap PCB if needed */ + lpfc_sli_pcimem_bcopy((uint32_t *)&phba->slim2p->pcb, + (uint32_t *)&phba->slim2p->pcb, + sizeof (PCB_t)); + + lpfc_printf_log(phba, KERN_INFO, LOG_INIT, + "%d:0405 Service Level Interface (SLI) 2 selected\n", + phba->brd_no); +} + +void +lpfc_mbox_put(struct lpfc_hba * phba, LPFC_MBOXQ_t * mbq) +{ + struct lpfc_sli *psli; + + psli = &phba->sli; + + list_add_tail(&mbq->list, &psli->mboxq); + + psli->mboxq_cnt++; + + return; +} + +LPFC_MBOXQ_t * +lpfc_mbox_get(struct lpfc_hba * phba) +{ + LPFC_MBOXQ_t *mbq = NULL; + struct lpfc_sli *psli = &phba->sli; + + if (!list_empty(&psli->mboxq)) { + mbq = list_entry(psli->mboxq.next, LPFC_MBOXQ_t, list); + list_del_init(&mbq->list); + psli->mboxq_cnt--; + } + + return mbq; +} diff --git a/drivers/scsi/lpfc/lpfc_mem.c b/drivers/scsi/lpfc/lpfc_mem.c new file mode 100644 index 000000000..16e35d040 --- /dev/null +++ b/drivers/scsi/lpfc/lpfc_mem.c @@ -0,0 +1,192 @@ +/******************************************************************* + * This file is part of the Emulex Linux Device Driver for * + * Enterprise Fibre Channel Host Bus Adapters. * + * Refer to the README file included with this package for * + * driver version and adapter support. * + * Copyright (C) 2004 Emulex Corporation. * + * www.emulex.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, a copy of which * + * can be found in the file COPYING included with this package. * + *******************************************************************/ + +/* + * $Id: lpfc_mem.c 1.69 2004/09/28 07:54:24EDT sf_support Exp $ + */ + +#include +#include +#include +#include + +#include "lpfc_sli.h" +#include "lpfc_disc.h" +#include "lpfc_scsi.h" +#include "lpfc.h" +#include "lpfc_crtn.h" +#include "lpfc_mem.h" + +static void * +lpfc_pool_kmalloc(int gfp_flags, void *data) +{ + return kmalloc((unsigned long)data, gfp_flags); +} + +static void +lpfc_pool_kfree(void *obj, void *data) +{ + kfree(obj); +} + +int +lpfc_mem_alloc(struct lpfc_hba * phba) +{ + struct lpfc_dma_pool *pool = &phba->lpfc_mbuf_safety_pool; + int i; + + phba->lpfc_scsi_dma_ext_pool = pci_pool_create("lpfc_scsi_dma_ext_pool", + phba->pcidev, LPFC_SCSI_DMA_EXT_SIZE, 8, 0); + if (!phba->lpfc_scsi_dma_ext_pool) + goto fail; + + phba->lpfc_mbuf_pool = pci_pool_create("lpfc_mbuf_pool", phba->pcidev, + LPFC_BPL_SIZE, 8,0); + if (!phba->lpfc_mbuf_pool) + goto fail_free_dma_ext_pool; + + pool->elements = kmalloc(sizeof(struct lpfc_dmabuf) * + LPFC_MBUF_POOL_SIZE, GFP_KERNEL); + pool->max_count = 0; + pool->current_count = 0; + for ( i = 0; i < LPFC_MBUF_POOL_SIZE; i++) { + pool->elements[i].virt = pci_pool_alloc(phba->lpfc_mbuf_pool, + GFP_KERNEL, &pool->elements[i].phys); + if (!pool->elements[i].virt) + goto fail_free_mbuf_pool; + pool->max_count++; + pool->current_count++; + } + + phba->iocb_mem_pool = mempool_create(LPFC_MEM_POOL_SIZE, + lpfc_pool_kmalloc, lpfc_pool_kfree, + (void *)(unsigned long)sizeof(struct lpfc_iocbq)); + if (!phba->iocb_mem_pool) + goto fail_free_mbuf_pool; + + phba->scsibuf_mem_pool = mempool_create(LPFC_MEM_POOL_SIZE, + lpfc_pool_kmalloc, lpfc_pool_kfree, + (void *)(unsigned long)sizeof(struct lpfc_scsi_buf)); + if (!phba->scsibuf_mem_pool) + goto fail_free_iocb_pool; + + phba->mbox_mem_pool = mempool_create(LPFC_MEM_POOL_SIZE, + lpfc_pool_kmalloc, lpfc_pool_kfree, + (void *)(unsigned long)sizeof(LPFC_MBOXQ_t)); + if (!phba->mbox_mem_pool) + goto fail_free_scsibuf_pool; + + phba->nlp_mem_pool = mempool_create(LPFC_MEM_POOL_SIZE, + lpfc_pool_kmalloc, lpfc_pool_kfree, + (void *)(unsigned long)sizeof(struct lpfc_nodelist)); + if (!phba->nlp_mem_pool) + goto fail_free_mbox_pool; + + phba->bind_mem_pool = mempool_create(LPFC_MEM_POOL_SIZE, + lpfc_pool_kmalloc, lpfc_pool_kfree, + (void *)(unsigned long)sizeof(struct lpfc_bindlist)); + if (!phba->bind_mem_pool) + goto fail_free_nlp_pool; + + return 0; + + fail_free_nlp_pool: + mempool_destroy(phba->nlp_mem_pool); + fail_free_mbox_pool: + mempool_destroy(phba->mbox_mem_pool); + fail_free_scsibuf_pool: + mempool_destroy(phba->scsibuf_mem_pool); + fail_free_iocb_pool: + mempool_destroy(phba->iocb_mem_pool); + fail_free_mbuf_pool: + while (--i) + pci_pool_free(phba->lpfc_mbuf_pool, pool->elements[i].virt, + pool->elements[i].phys); + kfree(pool->elements); + pci_pool_destroy(phba->lpfc_mbuf_pool); + fail_free_dma_ext_pool: + pci_pool_destroy(phba->lpfc_scsi_dma_ext_pool); + fail: + return -ENOMEM; +} + +void +lpfc_mem_free(struct lpfc_hba * phba) +{ + struct lpfc_sli *psli = &phba->sli; + struct lpfc_dma_pool *pool = &phba->lpfc_mbuf_safety_pool; + LPFC_MBOXQ_t *mbox, *next_mbox; + int i; + + list_for_each_entry_safe(mbox, next_mbox, &psli->mboxq, list) { + list_del(&mbox->list); + mempool_free(mbox, phba->mbox_mem_pool); + } + + psli->sliinit.sli_flag &= ~LPFC_SLI_MBOX_ACTIVE; + if (psli->mbox_active) { + mempool_free(psli->mbox_active, phba->mbox_mem_pool); + psli->mbox_active = NULL; + } + + for (i = 0; i < pool->current_count; i++) + pci_pool_free(phba->lpfc_mbuf_pool, pool->elements[i].virt, + pool->elements[i].phys); + kfree(pool->elements); + mempool_destroy(phba->bind_mem_pool); + mempool_destroy(phba->nlp_mem_pool); + mempool_destroy(phba->mbox_mem_pool); + mempool_destroy(phba->scsibuf_mem_pool); + mempool_destroy(phba->iocb_mem_pool); + + pci_pool_destroy(phba->lpfc_scsi_dma_ext_pool); + pci_pool_destroy(phba->lpfc_mbuf_pool); +} + +void * +lpfc_mbuf_alloc(struct lpfc_hba *phba, int mem_flags, dma_addr_t *handle) +{ + struct lpfc_dma_pool *pool = &phba->lpfc_mbuf_safety_pool; + void *ret; + + ret = pci_pool_alloc(phba->lpfc_mbuf_pool, GFP_ATOMIC, handle); + + if (!ret && ( mem_flags & MEM_PRI) && pool->current_count) { + pool->current_count--; + ret = pool->elements[pool->current_count].virt; + *handle = pool->elements[pool->current_count].phys; + } + return ret; +} + +void +lpfc_mbuf_free(struct lpfc_hba * phba, void *virt, dma_addr_t dma) +{ + struct lpfc_dma_pool *pool = &phba->lpfc_mbuf_safety_pool; + + if (pool->current_count < pool->max_count) { + pool->elements[pool->current_count].virt = virt; + pool->elements[pool->current_count].phys = dma; + pool->current_count++; + } else { + pci_pool_free(phba->lpfc_mbuf_pool, virt, dma); + } + return; +} diff --git a/drivers/scsi/lpfc/lpfc_mem.h b/drivers/scsi/lpfc/lpfc_mem.h new file mode 100644 index 000000000..ee68c10ba --- /dev/null +++ b/drivers/scsi/lpfc/lpfc_mem.h @@ -0,0 +1,56 @@ +/******************************************************************* + * This file is part of the Emulex Linux Device Driver for * + * Enterprise Fibre Channel Host Bus Adapters. * + * Refer to the README file included with this package for * + * driver version and adapter support. * + * Copyright (C) 2004 Emulex Corporation. * + * www.emulex.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, a copy of which * + * can be found in the file COPYING included with this package. * + *******************************************************************/ + +/* + * $Id: lpfc_mem.h 1.21 2004/09/22 19:40:07EDT sf_support Exp $ + */ + +#ifndef _H_LPFC_MEM +#define _H_LPFC_MEM + + +struct lpfc_dmabuf { + struct list_head list; + void *virt; /* virtual address ptr */ + dma_addr_t phys; /* mapped address */ +}; +struct lpfc_dmabufext { + struct lpfc_dmabuf dma; + uint32_t size; + uint32_t flag; + struct list_head list; + uint32_t uniqueid; + uint32_t data; +}; +typedef struct lpfc_dmabufext DMABUFEXT_t; + +struct lpfc_dma_pool { + struct lpfc_dmabuf *elements; + uint32_t max_count; + uint32_t current_count; +}; + + +#define MEM_PRI 0x100 /* Priority bit: set to exceed low + water */ +#define LPFC_MBUF_POOL_SIZE 64 /* max elements in MBUF safety pool */ +#define LPFC_MEM_POOL_SIZE 64 /* max elements in non DMA safety + pool */ +#endif /* _H_LPFC_MEM */ diff --git a/drivers/scsi/lpfc/lpfc_nportdisc.c b/drivers/scsi/lpfc/lpfc_nportdisc.c new file mode 100644 index 000000000..c3f431bff --- /dev/null +++ b/drivers/scsi/lpfc/lpfc_nportdisc.c @@ -0,0 +1,2145 @@ +/******************************************************************* + * This file is part of the Emulex Linux Device Driver for * + * Enterprise Fibre Channel Host Bus Adapters. * + * Refer to the README file included with this package for * + * driver version and adapter support. * + * Copyright (C) 2004 Emulex Corporation. * + * www.emulex.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, a copy of which * + * can be found in the file COPYING included with this package. * + *******************************************************************/ + +/* + * $Id: lpfc_nportdisc.c 1.146 2004/11/18 14:53:54EST sf_support Exp $ + */ + +#include +#include +#include +#include +#include +#include +#include "lpfc_sli.h" +#include "lpfc_disc.h" +#include "lpfc_scsi.h" +#include "lpfc.h" +#include "lpfc_crtn.h" +#include "lpfc_hw.h" +#include "lpfc_logmsg.h" +#include "lpfc_mem.h" + +extern uint8_t lpfcAlpaArray[]; + + +/* Called to verify a rcv'ed ADISC was intended for us. */ +static int +lpfc_check_adisc(struct lpfc_hba * phba, struct lpfc_nodelist * ndlp, + struct lpfc_name * nn, struct lpfc_name * pn) +{ + /* Compare the ADISC rsp WWNN / WWPN matches our internal node + * table entry for that node. + */ + if (memcmp(nn, &ndlp->nlp_nodename, sizeof (struct lpfc_name)) != 0) + return (0); + + if (memcmp(pn, &ndlp->nlp_portname, sizeof (struct lpfc_name)) != 0) + return (0); + + /* we match, return success */ + return (1); +} + + +int +lpfc_check_sparm(struct lpfc_hba * phba, + struct lpfc_nodelist * ndlp, struct serv_parm * sp, + uint32_t class) +{ + volatile struct serv_parm *hsp = &phba->fc_sparam; + /* First check for supported version */ + + /* Next check for class validity */ + if (sp->cls1.classValid) { + + if (sp->cls1.rcvDataSizeMsb > hsp->cls1.rcvDataSizeMsb) + sp->cls1.rcvDataSizeMsb = hsp->cls1.rcvDataSizeMsb; + if (sp->cls1.rcvDataSizeLsb > hsp->cls1.rcvDataSizeLsb) + sp->cls1.rcvDataSizeLsb = hsp->cls1.rcvDataSizeLsb; + } else if (class == CLASS1) { + return (0); + } + + if (sp->cls2.classValid) { + + if (sp->cls2.rcvDataSizeMsb > hsp->cls2.rcvDataSizeMsb) + sp->cls2.rcvDataSizeMsb = hsp->cls2.rcvDataSizeMsb; + if (sp->cls2.rcvDataSizeLsb > hsp->cls2.rcvDataSizeLsb) + sp->cls2.rcvDataSizeLsb = hsp->cls2.rcvDataSizeLsb; + } else if (class == CLASS2) { + return (0); + } + + if (sp->cls3.classValid) { + + if (sp->cls3.rcvDataSizeMsb > hsp->cls3.rcvDataSizeMsb) + sp->cls3.rcvDataSizeMsb = hsp->cls3.rcvDataSizeMsb; + if (sp->cls3.rcvDataSizeLsb > hsp->cls3.rcvDataSizeLsb) + sp->cls3.rcvDataSizeLsb = hsp->cls3.rcvDataSizeLsb; + } else if (class == CLASS3) { + return (0); + } + + if (sp->cmn.bbRcvSizeMsb > hsp->cmn.bbRcvSizeMsb) + sp->cmn.bbRcvSizeMsb = hsp->cmn.bbRcvSizeMsb; + if (sp->cmn.bbRcvSizeLsb > hsp->cmn.bbRcvSizeLsb) + sp->cmn.bbRcvSizeLsb = hsp->cmn.bbRcvSizeLsb; + + /* If check is good, copy wwpn wwnn into ndlp */ + memcpy(&ndlp->nlp_nodename, &sp->nodeName, sizeof (struct lpfc_name)); + memcpy(&ndlp->nlp_portname, &sp->portName, sizeof (struct lpfc_name)); + return (1); +} + +static void * +lpfc_check_elscmpl_iocb(struct lpfc_hba * phba, + struct lpfc_iocbq *cmdiocb, + struct lpfc_iocbq *rspiocb) +{ + struct lpfc_dmabuf *pcmd, *prsp; + uint32_t *lp; + void *ptr; + IOCB_t *irsp; + + irsp = &rspiocb->iocb; + pcmd = (struct lpfc_dmabuf *) cmdiocb->context2; + + /* For lpfc_els_abort, context2 could be zero'ed to delay + * freeing associated memory till after ABTS completes. + */ + if (pcmd) { + prsp = (struct lpfc_dmabuf *) pcmd->list.next; + lp = (uint32_t *) prsp->virt; + + pci_dma_sync_single_for_cpu(phba->pcidev, prsp->phys, + LPFC_BPL_SIZE, PCI_DMA_FROMDEVICE); + + ptr = (void *)((uint8_t *)lp + sizeof(uint32_t)); + } + else { + /* Force ulpStatus error since we are returning NULL ptr */ + if (!(irsp->ulpStatus)) { + irsp->ulpStatus = IOSTAT_LOCAL_REJECT; + irsp->un.ulpWord[4] = IOERR_SLI_ABORTED; + } + ptr = NULL; + } + return (ptr); +} + + +/* + * Free resources / clean up outstanding I/Os + * associated with a LPFC_NODELIST entry. This + * routine effectively results in a "software abort". + */ +static int +lpfc_els_abort(struct lpfc_hba * phba, struct lpfc_nodelist * ndlp, + int send_abts) +{ + struct lpfc_sli *psli; + struct lpfc_sli_ring *pring; + struct lpfc_iocbq *iocb, *next_iocb; + IOCB_t *icmd; + + /* Abort outstanding I/O on NPort */ + lpfc_printf_log(phba, KERN_INFO, LOG_DISCOVERY, + "%d:0201 Abort outstanding I/O on NPort x%x " + "Data: x%x x%x x%x\n", + phba->brd_no, ndlp->nlp_DID, ndlp->nlp_flag, + ndlp->nlp_state, ndlp->nlp_rpi); + + psli = &phba->sli; + pring = &psli->ring[LPFC_ELS_RING]; + + /* First check the txq */ + list_for_each_entry_safe(iocb, next_iocb, &pring->txq, list) { + /* Check to see if iocb matches the nport we are looking for */ + if ((lpfc_check_sli_ndlp(phba, pring, iocb, ndlp))) { + /* It matches, so deque and call compl with an error */ + list_del(&iocb->list); + pring->txq_cnt--; + if (iocb->iocb_cmpl) { + icmd = &iocb->iocb; + icmd->ulpStatus = IOSTAT_LOCAL_REJECT; + icmd->un.ulpWord[4] = IOERR_SLI_ABORTED; + (iocb->iocb_cmpl) (phba, iocb, iocb); + } else { + mempool_free(iocb, phba->iocb_mem_pool); + } + } + } + + /* Everything on txcmplq will be returned by firmware + * with a no rpi / linkdown / abort error. For ring 0, + * ELS discovery, we want to get rid of it right here. + */ + /* Next check the txcmplq */ + list_for_each_entry_safe(iocb, next_iocb, &pring->txcmplq, list) { + /* Check to see if iocb matches the nport we are looking for */ + if ((lpfc_check_sli_ndlp (phba, pring, iocb, ndlp))) { + /* It matches, so deque and call compl with an error */ + list_del(&iocb->list); + pring->txcmplq_cnt--; + + icmd = &iocb->iocb; + /* If the driver is completing an ELS + * command early, flush it out of the firmware. + */ + if (send_abts && + (icmd->ulpCommand == CMD_ELS_REQUEST64_CR) && + (icmd->un.elsreq64.bdl.ulpIoTag32)) { + lpfc_sli_issue_abort_iotag32(phba, pring, iocb); + } + if (iocb->iocb_cmpl) { + icmd->ulpStatus = IOSTAT_LOCAL_REJECT; + icmd->un.ulpWord[4] = IOERR_SLI_ABORTED; + (iocb->iocb_cmpl) (phba, iocb, iocb); + } else { + mempool_free(iocb, phba->iocb_mem_pool); + } + } + } + + /* If we are delaying issuing an ELS command, cancel it */ + if(ndlp->nlp_flag & NLP_DELAY_TMO) { + ndlp->nlp_flag &= ~NLP_DELAY_TMO; + del_timer_sync(&ndlp->nlp_delayfunc); + } + return (0); +} + +static int +lpfc_rcv_plogi(struct lpfc_hba * phba, + struct lpfc_nodelist * ndlp, + struct lpfc_iocbq *cmdiocb) +{ + struct lpfc_dmabuf *pcmd; + uint32_t *lp; + IOCB_t *icmd; + struct serv_parm *sp; + LPFC_MBOXQ_t *mbox; + struct ls_rjt stat; + + memset(&stat, 0, sizeof (struct ls_rjt)); + if (phba->hba_state <= LPFC_FLOGI) { + /* Before responding to PLOGI, check for pt2pt mode. + * If we are pt2pt, with an outstanding FLOGI, abort + * the FLOGI and resend it first. + */ + if (phba->fc_flag & FC_PT2PT) { + lpfc_els_abort_flogi(phba); + if(!(phba->fc_flag & FC_PT2PT_PLOGI)) { + /* If the other side is supposed to initiate + * the PLOGI anyway, just ACC it now and + * move on with discovery. + */ + phba->fc_edtov = FF_DEF_EDTOV; + phba->fc_ratov = FF_DEF_RATOV; + /* Start discovery - this should just do + CLEAR_LA */ + lpfc_disc_start(phba); + } + else { + lpfc_initial_flogi(phba); + } + } + else { + stat.un.b.lsRjtRsnCode = LSRJT_LOGICAL_BSY; + stat.un.b.lsRjtRsnCodeExp = LSEXP_NOTHING_MORE; + goto out; + } + } + pcmd = (struct lpfc_dmabuf *) cmdiocb->context2; + lp = (uint32_t *) pcmd->virt; + sp = (struct serv_parm *) ((uint8_t *) lp + sizeof (uint32_t)); + if ((lpfc_check_sparm(phba, ndlp, sp, CLASS3) == 0)) { + /* Reject this request because invalid parameters */ + stat.un.b.lsRjtRsnCode = LSRJT_UNABLE_TPC; + stat.un.b.lsRjtRsnCodeExp = LSEXP_SPARM_OPTIONS; + goto out; + } + icmd = &cmdiocb->iocb; + + /* PLOGI chkparm OK */ + lpfc_printf_log(phba, + KERN_INFO, + LOG_ELS, + "%d:0114 PLOGI chkparm OK Data: x%x x%x x%x x%x\n", + phba->brd_no, + ndlp->nlp_DID, ndlp->nlp_state, ndlp->nlp_flag, + ndlp->nlp_rpi); + + if ((phba->cfg_fcp_class == 2) && + (sp->cls2.classValid)) { + ndlp->nlp_fcp_info |= CLASS2; + } else { + ndlp->nlp_fcp_info |= CLASS3; + } + + /* no need to reg_login if we are already in one of these states */ + switch(ndlp->nlp_state) { + case NLP_STE_REG_LOGIN_ISSUE: + case NLP_STE_PRLI_ISSUE: + case NLP_STE_UNMAPPED_NODE: + case NLP_STE_MAPPED_NODE: + lpfc_els_rsp_acc(phba, ELS_CMD_PLOGI, cmdiocb, ndlp, NULL, 0); + return (1); + } + + if ((mbox = mempool_alloc(phba->mbox_mem_pool, GFP_ATOMIC)) == 0) { + stat.un.b.lsRjtRsnCode = LSRJT_UNABLE_TPC; + stat.un.b.lsRjtRsnCodeExp = LSEXP_OUT_OF_RESOURCE; + goto out; + } + + if ((phba->fc_flag & FC_PT2PT) + && !(phba->fc_flag & FC_PT2PT_PLOGI)) { + /* rcv'ed PLOGI decides what our NPortId will be */ + phba->fc_myDID = icmd->un.rcvels.parmRo; + lpfc_config_link(phba, mbox); + if (lpfc_sli_issue_mbox + (phba, mbox, (MBX_NOWAIT | MBX_STOP_IOCB)) + == MBX_NOT_FINISHED) { + mempool_free( mbox, phba->mbox_mem_pool); + stat.un.b.lsRjtRsnCode = LSRJT_UNABLE_TPC; + stat.un.b.lsRjtRsnCodeExp = LSEXP_OUT_OF_RESOURCE; + goto out; + } + if ((mbox = mempool_alloc(phba->mbox_mem_pool, + GFP_ATOMIC)) == 0) { + stat.un.b.lsRjtRsnCode = LSRJT_UNABLE_TPC; + stat.un.b.lsRjtRsnCodeExp = LSEXP_OUT_OF_RESOURCE; + goto out; + } + lpfc_can_disctmo(phba); + } + + if(lpfc_reg_login(phba, icmd->un.rcvels.remoteID, + (uint8_t *) sp, mbox, 0)) { + mempool_free( mbox, phba->mbox_mem_pool); + stat.un.b.lsRjtRsnCode = LSRJT_UNABLE_TPC; + stat.un.b.lsRjtRsnCodeExp = LSEXP_OUT_OF_RESOURCE; +out: + lpfc_els_rsp_reject(phba, stat.un.lsRjtError, cmdiocb, ndlp); + return (0); + } + + /* ACC PLOGI rsp command needs to execute first, + * queue this mbox command to be processed later. + */ + mbox->mbox_cmpl = lpfc_mbx_cmpl_reg_login; + mbox->context2 = ndlp; + ndlp->nlp_flag |= NLP_ACC_REGLOGIN; + + /* If there is an outstanding PLOGI issued, abort it before + * sending ACC rsp to PLOGI recieved. + */ + if(ndlp->nlp_state == NLP_STE_PLOGI_ISSUE) { + /* software abort outstanding PLOGI */ + lpfc_els_abort(phba, ndlp, 1); + } + ndlp->nlp_flag |= NLP_RCV_PLOGI; + lpfc_els_rsp_acc(phba, ELS_CMD_PLOGI, cmdiocb, ndlp, mbox, 0); + return (1); +} + +static int +lpfc_rcv_padisc(struct lpfc_hba * phba, + struct lpfc_nodelist * ndlp, + struct lpfc_iocbq *cmdiocb) +{ + struct lpfc_dmabuf *pcmd; + struct serv_parm *sp; + struct lpfc_name *pnn, *ppn; + struct ls_rjt stat; + ADISC *ap; + IOCB_t *icmd; + uint32_t *lp; + uint32_t cmd; + + pcmd = (struct lpfc_dmabuf *) cmdiocb->context2; + lp = (uint32_t *) pcmd->virt; + + cmd = *lp++; + if (cmd == ELS_CMD_ADISC) { + ap = (ADISC *) lp; + pnn = (struct lpfc_name *) & ap->nodeName; + ppn = (struct lpfc_name *) & ap->portName; + } else { + sp = (struct serv_parm *) lp; + pnn = (struct lpfc_name *) & sp->nodeName; + ppn = (struct lpfc_name *) & sp->portName; + } + + icmd = &cmdiocb->iocb; + if ((icmd->ulpStatus == 0) && + (lpfc_check_adisc(phba, ndlp, pnn, ppn))) { + if (cmd == ELS_CMD_ADISC) { + lpfc_els_rsp_adisc_acc(phba, cmdiocb, ndlp); + } + else { + lpfc_els_rsp_acc(phba, ELS_CMD_PLOGI, cmdiocb, ndlp, + NULL, 0); + } + return (1); + } + /* Reject this request because invalid parameters */ + stat.un.b.lsRjtRsvd0 = 0; + stat.un.b.lsRjtRsnCode = LSRJT_UNABLE_TPC; + stat.un.b.lsRjtRsnCodeExp = LSEXP_SPARM_OPTIONS; + stat.un.b.vendorUnique = 0; + lpfc_els_rsp_reject(phba, stat.un.lsRjtError, cmdiocb, ndlp); + + ndlp->nlp_last_elscmd = (unsigned long)ELS_CMD_PLOGI; + /* 1 sec timeout */ + mod_timer(&ndlp->nlp_delayfunc, jiffies + HZ); + + ndlp->nlp_flag |= NLP_DELAY_TMO; + ndlp->nlp_state = NLP_STE_NPR_NODE; + lpfc_nlp_list(phba, ndlp, NLP_NPR_LIST); + return (0); +} + +static int +lpfc_rcv_logo(struct lpfc_hba * phba, + struct lpfc_nodelist * ndlp, + struct lpfc_iocbq *cmdiocb) +{ + /* Put ndlp on NPR list with 1 sec timeout for plogi, ACC logo */ + /* Only call LOGO ACC for first LOGO, this avoids sending unnecessary + * PLOGIs during LOGO storms from a device. + */ + ndlp->nlp_flag |= NLP_LOGO_ACC; + lpfc_els_rsp_acc(phba, ELS_CMD_ACC, cmdiocb, ndlp, NULL, 0); + + if (!(ndlp->nlp_type & NLP_FABRIC)) { + /* Only try to re-login if this is NOT a Fabric Node */ + ndlp->nlp_last_elscmd = (unsigned long)ELS_CMD_PLOGI; + mod_timer(&ndlp->nlp_delayfunc, jiffies + HZ * 1); + ndlp->nlp_flag |= NLP_DELAY_TMO; + } + + ndlp->nlp_state = NLP_STE_NPR_NODE; + lpfc_nlp_list(phba, ndlp, NLP_NPR_LIST); + + ndlp->nlp_flag &= ~NLP_NPR_ADISC; + /* The driver has to wait until the ACC completes before it continues + * processing the LOGO. The action will resume in + * lpfc_cmpl_els_logo_acc routine. Since part of processing includes an + * unreg_login, the driver waits so the ACC does not get aborted. + */ + return (0); +} + +static int +lpfc_binding_found(struct lpfc_bindlist * blp, struct lpfc_nodelist * ndlp) +{ + uint16_t bindtype = blp->nlp_bind_type; + + if ((bindtype & FCP_SEED_DID) && + (ndlp->nlp_DID == be32_to_cpu(blp->nlp_DID))) { + return (1); + } else if ((bindtype & FCP_SEED_WWPN) && + (memcmp(&ndlp->nlp_portname, &blp->nlp_portname, + sizeof (struct lpfc_name)) == 0)) { + return (1); + } else if ((bindtype & FCP_SEED_WWNN) && + (memcmp(&ndlp->nlp_nodename, &blp->nlp_nodename, + sizeof (struct lpfc_name)) == 0)) { + return (1); + } + return (0); +} + +static int +lpfc_binding_useid(struct lpfc_hba * phba, uint32_t sid) +{ + struct lpfc_bindlist *blp; + + list_for_each_entry(blp, &phba->fc_nlpbind_list, nlp_listp) { + if (blp->nlp_sid == sid) { + return (1); + } + } + return (0); +} + +static int +lpfc_mapping_useid(struct lpfc_hba * phba, uint32_t sid) +{ + struct lpfc_nodelist *mapnode; + struct lpfc_bindlist *blp; + + list_for_each_entry(mapnode, &phba->fc_nlpmap_list, nlp_listp) { + blp = mapnode->nlp_listp_bind; + if (blp->nlp_sid == sid) { + return (1); + } + } + return (0); +} + +static struct lpfc_bindlist * +lpfc_create_binding(struct lpfc_hba * phba, + struct lpfc_nodelist * ndlp, uint16_t index, + uint16_t bindtype) +{ + struct lpfc_bindlist *blp; + + if ((blp = mempool_alloc(phba->bind_mem_pool, GFP_ATOMIC))) { + memset(blp, 0, sizeof (struct lpfc_bindlist)); + switch (bindtype) { + case FCP_SEED_WWPN: + blp->nlp_bind_type = FCP_SEED_WWPN; + break; + case FCP_SEED_WWNN: + blp->nlp_bind_type = FCP_SEED_WWNN; + break; + case FCP_SEED_DID: + blp->nlp_bind_type = FCP_SEED_DID; + break; + } + blp->nlp_sid = index; + blp->nlp_DID = ndlp->nlp_DID; + memcpy(&blp->nlp_nodename, &ndlp->nlp_nodename, + sizeof (struct lpfc_name)); + memcpy(&blp->nlp_portname, &ndlp->nlp_portname, + sizeof (struct lpfc_name)); + + return (blp); + } + return NULL; +} + + +static struct lpfc_bindlist * +lpfc_consistent_bind_get(struct lpfc_hba * phba, struct lpfc_nodelist * ndlp) +{ + struct lpfc_bindlist *blp, *next_blp; + uint16_t index; + + /* check binding list */ + list_for_each_entry_safe(blp, next_blp, &phba->fc_nlpbind_list, + nlp_listp) { + if (lpfc_binding_found(blp, ndlp)) { + + /* take it off the binding list */ + phba->fc_bind_cnt--; + list_del_init(&blp->nlp_listp); + + /* Reassign scsi id to NPort */ + lpfc_printf_log(phba, + KERN_INFO, + LOG_DISCOVERY | LOG_FCP, + "%d:0213 Reassign scsi id x%x to " + "NPort x%x Data: x%x x%x x%x x%x\n", + phba->brd_no, + blp->nlp_sid, ndlp->nlp_DID, + blp->nlp_bind_type, ndlp->nlp_flag, + ndlp->nlp_state, ndlp->nlp_rpi); + + return (blp); + } + } + + /* NOTE: if scan-down = 2 and we have private loop, then we use + * AlpaArray to determine sid. + */ + if ((phba->cfg_fcp_bind_method == 4) && + ((phba->fc_flag & (FC_PUBLIC_LOOP | FC_FABRIC)) || + (phba->fc_topology != TOPOLOGY_LOOP))) { + /* Log message: ALPA based binding used on a non loop + topology */ + lpfc_printf_log(phba, + KERN_WARNING, + LOG_DISCOVERY, + "%d:0245 ALPA based bind method used on an HBA " + "which is in a nonloop topology Data: x%x\n", + phba->brd_no, + phba->fc_topology); + } + + if ((phba->cfg_fcp_bind_method == 4) && + !(phba->fc_flag & (FC_PUBLIC_LOOP | FC_FABRIC)) && + (phba->fc_topology == TOPOLOGY_LOOP)) { + for (index = 0; index < FC_MAXLOOP; index++) { + if (ndlp->nlp_DID == (uint32_t) lpfcAlpaArray[index]) { + if ((blp = + lpfc_create_binding(phba, ndlp, index, + FCP_SEED_DID))) { + return (blp); + } + goto errid; + } + } + } + + if (phba->cfg_automap) { + while (1) { + if ((lpfc_binding_useid(phba, phba->sid_cnt)) + || (lpfc_mapping_useid (phba, phba->sid_cnt))) { + + phba->sid_cnt++; + } else { + if ((blp = + lpfc_create_binding(phba, ndlp, + phba->sid_cnt, + phba->fcp_mapping))) { + blp->nlp_bind_type |= FCP_SEED_AUTO; + + phba->sid_cnt++; + return (blp); + } + goto errid; + } + } + } + /* if automap on */ +errid: + /* Cannot assign scsi id on NPort */ + lpfc_printf_log(phba, + KERN_INFO, + LOG_DISCOVERY | LOG_FCP, + "%d:0230 Cannot assign scsi ID on NPort x%x " + "Data: x%x x%x x%x\n", + phba->brd_no, + ndlp->nlp_DID, ndlp->nlp_flag, ndlp->nlp_state, + ndlp->nlp_rpi); + + return NULL; +} + +static uint32_t +lpfc_assign_binding(struct lpfc_hba * phba, + struct lpfc_nodelist * ndlp, struct lpfc_bindlist *blp) +{ + struct lpfc_target *targetp; + + targetp = lpfc_find_target(phba, blp->nlp_sid, ndlp); + if(!targetp) { + /* Cannot assign scsi id to NPort */ + lpfc_printf_log(phba, + KERN_INFO, + LOG_DISCOVERY | LOG_FCP, + "%d:0229 Cannot assign scsi id x%x to NPort x%x " + "Data: x%x x%x x%x\n", + phba->brd_no, blp->nlp_sid, + ndlp->nlp_DID, ndlp->nlp_flag, ndlp->nlp_state, + ndlp->nlp_rpi); + return(0); + } + ndlp->nlp_sid = blp->nlp_sid; + ndlp->nlp_flag &= ~NLP_SEED_MASK; + switch ((blp->nlp_bind_type & FCP_SEED_MASK)) { + case FCP_SEED_WWPN: + ndlp->nlp_flag |= NLP_SEED_WWPN; + break; + case FCP_SEED_WWNN: + ndlp->nlp_flag |= NLP_SEED_WWNN; + break; + case FCP_SEED_DID: + ndlp->nlp_flag |= NLP_SEED_DID; + break; + } + if (blp->nlp_bind_type & FCP_SEED_AUTO) { + ndlp->nlp_flag |= NLP_AUTOMAP; + } + /* Assign scsi id to NPort */ + lpfc_printf_log(phba, + KERN_INFO, + LOG_DISCOVERY | LOG_FCP, + "%d:0216 Assign scsi " + "id x%x to NPort x%x " + "Data: x%x x%x x%x x%x\n", + phba->brd_no, + ndlp->nlp_sid, ndlp->nlp_DID, + blp->nlp_bind_type, + ndlp->nlp_flag, ndlp->nlp_state, + ndlp->nlp_rpi); + return(1); +} + +static uint32_t +lpfc_disc_set_adisc(struct lpfc_hba * phba, + struct lpfc_nodelist * ndlp) +{ + /* Check config parameter use-adisc or FCP-2 */ + if ((phba->cfg_use_adisc == 0) && + !(phba->fc_flag & FC_RSCN_MODE)) { + return (0); + } + ndlp->nlp_flag |= NLP_NPR_ADISC; + return (1); +} + +static uint32_t +lpfc_disc_noop(struct lpfc_hba * phba, + struct lpfc_nodelist * ndlp, void *arg, uint32_t evt) +{ + /* This routine does nothing, just return the current state */ + return (ndlp->nlp_state); +} + +static uint32_t +lpfc_disc_illegal(struct lpfc_hba * phba, + struct lpfc_nodelist * ndlp, void *arg, uint32_t evt) +{ + lpfc_printf_log(phba, + KERN_ERR, + LOG_DISCOVERY, + "%d:0253 Illegal State Transition: node x%x event x%x, " + "state x%x Data: x%x x%x\n", + phba->brd_no, + ndlp->nlp_DID, evt, ndlp->nlp_state, ndlp->nlp_rpi, + ndlp->nlp_flag); + return (ndlp->nlp_state); +} + +/* Start of Discovery State Machine routines */ + +static uint32_t +lpfc_rcv_plogi_unused_node(struct lpfc_hba * phba, + struct lpfc_nodelist * ndlp, void *arg, uint32_t evt) +{ + struct lpfc_iocbq *cmdiocb; + struct lpfc_dmabuf *pcmd; + + cmdiocb = (struct lpfc_iocbq *) arg; + pcmd = (struct lpfc_dmabuf *) cmdiocb->context2; + + pci_dma_sync_single_for_cpu(phba->pcidev, pcmd->phys, + LPFC_BPL_SIZE, PCI_DMA_FROMDEVICE); + + if(lpfc_rcv_plogi(phba, ndlp, cmdiocb)) { + ndlp->nlp_state = NLP_STE_UNUSED_NODE; + lpfc_nlp_list(phba, ndlp, NLP_UNUSED_LIST); + return (ndlp->nlp_state); + } + lpfc_nlp_list(phba, ndlp, NLP_NO_LIST); + return (NLP_STE_FREED_NODE); +} + +static uint32_t +lpfc_rcv_els_unused_node(struct lpfc_hba * phba, + struct lpfc_nodelist * ndlp, void *arg, uint32_t evt) +{ + lpfc_issue_els_logo(phba, ndlp, 0); + lpfc_nlp_list(phba, ndlp, NLP_UNUSED_LIST); + return (ndlp->nlp_state); +} + +static uint32_t +lpfc_rcv_logo_unused_node(struct lpfc_hba * phba, + struct lpfc_nodelist * ndlp, void *arg, uint32_t evt) +{ + struct lpfc_iocbq *cmdiocb; + struct lpfc_dmabuf *pcmd; + + cmdiocb = (struct lpfc_iocbq *) arg; + pcmd = (struct lpfc_dmabuf *) cmdiocb->context2; + + pci_dma_sync_single_for_cpu(phba->pcidev, pcmd->phys, + LPFC_BPL_SIZE, PCI_DMA_FROMDEVICE); + + ndlp->nlp_flag |= NLP_LOGO_ACC; + lpfc_els_rsp_acc(phba, ELS_CMD_ACC, cmdiocb, ndlp, NULL, 0); + lpfc_nlp_list(phba, ndlp, NLP_UNUSED_LIST); + + return (ndlp->nlp_state); +} + +static uint32_t +lpfc_cmpl_logo_unused_node(struct lpfc_hba * phba, + struct lpfc_nodelist * ndlp, void *arg, uint32_t evt) +{ + lpfc_nlp_list(phba, ndlp, NLP_NO_LIST); + return (NLP_STE_FREED_NODE); +} + +static uint32_t +lpfc_device_rm_unused_node(struct lpfc_hba * phba, + struct lpfc_nodelist * ndlp, void *arg, uint32_t evt) +{ + lpfc_nlp_list(phba, ndlp, NLP_NO_LIST); + return (NLP_STE_FREED_NODE); +} + +static uint32_t +lpfc_rcv_plogi_plogi_issue(struct lpfc_hba * phba, + struct lpfc_nodelist * ndlp, + struct lpfc_iocbq *cmdiocb, uint32_t evt) +{ + struct lpfc_dmabuf *pcmd; + struct serv_parm *sp; + uint32_t *lp; + struct ls_rjt stat; + int port_cmp; + + pcmd = (struct lpfc_dmabuf *) cmdiocb->context2; + lp = (uint32_t *) pcmd->virt; + sp = (struct serv_parm *) ((uint8_t *) lp + sizeof (uint32_t)); + + pci_dma_sync_single_for_cpu(phba->pcidev, pcmd->phys, + LPFC_BPL_SIZE, PCI_DMA_FROMDEVICE); + + memset(&stat, 0, sizeof (struct ls_rjt)); + + /* For a PLOGI, we only accept if our portname is less + * than the remote portname. + */ + phba->fc_stat.elsLogiCol++; + port_cmp = memcmp(&phba->fc_portname, &sp->portName, + sizeof (struct lpfc_name)); + + if (port_cmp >= 0) { + /* Reject this request because the remote node will accept + ours */ + stat.un.b.lsRjtRsnCode = LSRJT_UNABLE_TPC; + stat.un.b.lsRjtRsnCodeExp = LSEXP_CMD_IN_PROGRESS; + lpfc_els_rsp_reject(phba, stat.un.lsRjtError, cmdiocb, ndlp); + } + else { + lpfc_rcv_plogi(phba, ndlp, cmdiocb); + } /* if our portname was less */ + + return (ndlp->nlp_state); +} + +static uint32_t +lpfc_rcv_els_plogi_issue(struct lpfc_hba * phba, + struct lpfc_nodelist * ndlp, void *arg, uint32_t evt) +{ + struct lpfc_iocbq *cmdiocb; + struct lpfc_dmabuf *pcmd; + + cmdiocb = (struct lpfc_iocbq *) arg; + pcmd = (struct lpfc_dmabuf *) cmdiocb->context2; + + pci_dma_sync_single_for_cpu(phba->pcidev, pcmd->phys, + LPFC_BPL_SIZE, PCI_DMA_FROMDEVICE); + + /* software abort outstanding PLOGI */ + lpfc_els_abort(phba, ndlp, 1); + mod_timer(&ndlp->nlp_delayfunc, jiffies + HZ * 1); + ndlp->nlp_flag |= NLP_DELAY_TMO; + + if(evt == NLP_EVT_RCV_LOGO) { + lpfc_els_rsp_acc(phba, ELS_CMD_ACC, cmdiocb, ndlp, NULL, 0); + } + else { + lpfc_issue_els_logo(phba, ndlp, 0); + } + + /* Put ndlp in npr list set plogi timer for 1 sec */ + ndlp->nlp_last_elscmd = (unsigned long)ELS_CMD_PLOGI; + ndlp->nlp_state = NLP_STE_NPR_NODE; + lpfc_nlp_list(phba, ndlp, NLP_NPR_LIST); + + return (ndlp->nlp_state); +} + +static uint32_t +lpfc_cmpl_plogi_plogi_issue(struct lpfc_hba * phba, + struct lpfc_nodelist * ndlp, void *arg, + uint32_t evt) +{ + struct lpfc_iocbq *cmdiocb, *rspiocb; + struct lpfc_dmabuf *pcmd, *prsp; + uint32_t *lp; + IOCB_t *irsp; + struct serv_parm *sp; + LPFC_MBOXQ_t *mbox; + + cmdiocb = (struct lpfc_iocbq *) arg; + rspiocb = cmdiocb->context_un.rsp_iocb; + + if (ndlp->nlp_flag & NLP_ACC_REGLOGIN) { + return (ndlp->nlp_state); + } + + irsp = &rspiocb->iocb; + + if (irsp->ulpStatus == 0) { + pcmd = (struct lpfc_dmabuf *) cmdiocb->context2; + + prsp = (struct lpfc_dmabuf *) pcmd->list.next; + lp = (uint32_t *) prsp->virt; + + pci_dma_sync_single_for_cpu(phba->pcidev, prsp->phys, + LPFC_BPL_SIZE, PCI_DMA_FROMDEVICE); + + sp = (struct serv_parm *) ((uint8_t *) lp + sizeof (uint32_t)); + if ((lpfc_check_sparm(phba, ndlp, sp, CLASS3))) { + /* PLOGI chkparm OK */ + lpfc_printf_log(phba, + KERN_INFO, + LOG_ELS, + "%d:0121 PLOGI chkparm OK " + "Data: x%x x%x x%x x%x\n", + phba->brd_no, + ndlp->nlp_DID, ndlp->nlp_state, + ndlp->nlp_flag, ndlp->nlp_rpi); + + if ((phba->cfg_fcp_class == 2) && + (sp->cls2.classValid)) { + ndlp->nlp_fcp_info |= CLASS2; + } else { + ndlp->nlp_fcp_info |= CLASS3; + } + + if ((mbox = mempool_alloc(phba->mbox_mem_pool, + GFP_ATOMIC))) { + lpfc_unreg_rpi(phba, ndlp); + if (lpfc_reg_login + (phba, irsp->un.elsreq64.remoteID, + (uint8_t *) sp, mbox, 0) == 0) { + /* set_slim mailbox command needs to + * execute first, queue this command to + * be processed later. + */ + switch(ndlp->nlp_DID) { + case NameServer_DID: + mbox->mbox_cmpl = + lpfc_mbx_cmpl_ns_reg_login; + break; + case FDMI_DID: + mbox->mbox_cmpl = + lpfc_mbx_cmpl_fdmi_reg_login; + break; + default: + mbox->mbox_cmpl = + lpfc_mbx_cmpl_reg_login; + } + mbox->context2 = ndlp; + if (lpfc_sli_issue_mbox(phba, mbox, + (MBX_NOWAIT | MBX_STOP_IOCB)) + != MBX_NOT_FINISHED) { + ndlp->nlp_state = + NLP_STE_REG_LOGIN_ISSUE; + lpfc_nlp_list(phba, ndlp, + NLP_REGLOGIN_LIST); + return (ndlp->nlp_state); + } + mempool_free(mbox, phba->mbox_mem_pool); + } else { + mempool_free(mbox, phba->mbox_mem_pool); + } + } + } + } + + /* Free this node since the driver cannot login or has the wrong + sparm */ + lpfc_nlp_list(phba, ndlp, NLP_NO_LIST); + return (NLP_STE_FREED_NODE); +} + +static uint32_t +lpfc_device_rm_plogi_issue(struct lpfc_hba * phba, + struct lpfc_nodelist * ndlp, void *arg, uint32_t evt) +{ + /* software abort outstanding PLOGI */ + lpfc_els_abort(phba, ndlp, 1); + + lpfc_nlp_list(phba, ndlp, NLP_NO_LIST); + return (NLP_STE_FREED_NODE); +} + +static uint32_t +lpfc_device_recov_plogi_issue(struct lpfc_hba * phba, + struct lpfc_nodelist * ndlp, void *arg, + uint32_t evt) +{ + /* software abort outstanding PLOGI */ + lpfc_els_abort(phba, ndlp, 1); + + ndlp->nlp_state = NLP_STE_NPR_NODE; + lpfc_nlp_list(phba, ndlp, NLP_NPR_LIST); + ndlp->nlp_flag &= ~NLP_NPR_2B_DISC; + + return (ndlp->nlp_state); +} + +static uint32_t +lpfc_rcv_plogi_adisc_issue(struct lpfc_hba * phba, + struct lpfc_nodelist * ndlp, void *arg, + uint32_t evt) +{ + struct lpfc_iocbq *cmdiocb; + struct lpfc_dmabuf *pcmd; + + /* software abort outstanding ADISC */ + lpfc_els_abort(phba, ndlp, 1); + + cmdiocb = (struct lpfc_iocbq *) arg; + pcmd = (struct lpfc_dmabuf *) cmdiocb->context2; + + pci_dma_sync_single_for_cpu(phba->pcidev, pcmd->phys, + LPFC_BPL_SIZE, PCI_DMA_FROMDEVICE); + + if(lpfc_rcv_plogi(phba, ndlp, cmdiocb)) { + return (ndlp->nlp_state); + } + ndlp->nlp_state = NLP_STE_PLOGI_ISSUE; + lpfc_nlp_list(phba, ndlp, NLP_PLOGI_LIST); + lpfc_issue_els_plogi(phba, ndlp, 0); + + return (ndlp->nlp_state); +} + +static uint32_t +lpfc_rcv_prli_adisc_issue(struct lpfc_hba * phba, + struct lpfc_nodelist * ndlp, void *arg, + uint32_t evt) +{ + struct lpfc_iocbq *cmdiocb; + struct lpfc_dmabuf *pcmd; + + cmdiocb = (struct lpfc_iocbq *) arg; + pcmd = (struct lpfc_dmabuf *) cmdiocb->context2; + + pci_dma_sync_single_for_cpu(phba->pcidev, pcmd->phys, + LPFC_BPL_SIZE, PCI_DMA_FROMDEVICE); + + lpfc_els_rsp_prli_acc(phba, cmdiocb, ndlp); + return (ndlp->nlp_state); +} + +static uint32_t +lpfc_rcv_logo_adisc_issue(struct lpfc_hba * phba, + struct lpfc_nodelist * ndlp, void *arg, + uint32_t evt) +{ + struct lpfc_iocbq *cmdiocb; + struct lpfc_dmabuf *pcmd; + + cmdiocb = (struct lpfc_iocbq *) arg; + pcmd = (struct lpfc_dmabuf *) cmdiocb->context2; + + pci_dma_sync_single_for_cpu(phba->pcidev, pcmd->phys, + LPFC_BPL_SIZE, PCI_DMA_FROMDEVICE); + + /* software abort outstanding ADISC */ + lpfc_els_abort(phba, ndlp, 0); + + lpfc_rcv_logo(phba, ndlp, cmdiocb); + return (ndlp->nlp_state); +} + +static uint32_t +lpfc_rcv_padisc_adisc_issue(struct lpfc_hba * phba, + struct lpfc_nodelist * ndlp, void *arg, + uint32_t evt) +{ + struct lpfc_iocbq *cmdiocb; + struct lpfc_dmabuf *pcmd; + + cmdiocb = (struct lpfc_iocbq *) arg; + pcmd = (struct lpfc_dmabuf *) cmdiocb->context2; + + pci_dma_sync_single_for_cpu(phba->pcidev, pcmd->phys, + LPFC_BPL_SIZE, PCI_DMA_FROMDEVICE); + + lpfc_rcv_padisc(phba, ndlp, cmdiocb); + return (ndlp->nlp_state); +} + +static uint32_t +lpfc_rcv_prlo_adisc_issue(struct lpfc_hba * phba, + struct lpfc_nodelist * ndlp, void *arg, + uint32_t evt) +{ + struct lpfc_iocbq *cmdiocb; + struct lpfc_dmabuf *pcmd; + + cmdiocb = (struct lpfc_iocbq *) arg; + pcmd = (struct lpfc_dmabuf *) cmdiocb->context2; + + pci_dma_sync_single_for_cpu(phba->pcidev, pcmd->phys, + LPFC_BPL_SIZE, PCI_DMA_FROMDEVICE); + + /* Treat like rcv logo */ + lpfc_rcv_logo(phba, ndlp, cmdiocb); + return (ndlp->nlp_state); +} + +static uint32_t +lpfc_cmpl_adisc_adisc_issue(struct lpfc_hba * phba, + struct lpfc_nodelist * ndlp, void *arg, + uint32_t evt) +{ + struct lpfc_iocbq *cmdiocb, *rspiocb; + struct lpfc_bindlist *blp; + IOCB_t *irsp; + ADISC *ap; + + cmdiocb = (struct lpfc_iocbq *) arg; + rspiocb = cmdiocb->context_un.rsp_iocb; + + ap = (ADISC *)lpfc_check_elscmpl_iocb(phba, cmdiocb, rspiocb); + irsp = &rspiocb->iocb; + + if ((irsp->ulpStatus) || + (!lpfc_check_adisc(phba, ndlp, &ap->nodeName, &ap->portName))) { + ndlp->nlp_last_elscmd = (unsigned long)ELS_CMD_PLOGI; + /* 1 sec timeout */ + mod_timer(&ndlp->nlp_delayfunc, jiffies + HZ); + ndlp->nlp_flag |= NLP_DELAY_TMO; + + memset(&ndlp->nlp_nodename, 0, sizeof (struct lpfc_name)); + memset(&ndlp->nlp_portname, 0, sizeof (struct lpfc_name)); + + ndlp->nlp_state = NLP_STE_NPR_NODE; + lpfc_nlp_list(phba, ndlp, NLP_NPR_LIST); + lpfc_unreg_rpi(phba, ndlp); + return (ndlp->nlp_state); + } + /* move to mapped / unmapped list accordingly */ + /* Can we assign a SCSI Id to this NPort */ + if ((blp = lpfc_consistent_bind_get(phba, ndlp))) { + /* Next 4 lines MUST be in this order */ + if(lpfc_assign_binding(phba, ndlp, blp)) { + ndlp->nlp_state = NLP_STE_MAPPED_NODE; + lpfc_nlp_list(phba, ndlp, NLP_MAPPED_LIST); + ndlp->nlp_listp_bind = blp; + + lpfc_set_failmask(phba, ndlp, + (LPFC_DEV_DISCOVERY_INP|LPFC_DEV_DISCONNECTED), + LPFC_CLR_BITMASK); + + return (ndlp->nlp_state); + } + } + ndlp->nlp_flag |= NLP_TGT_NO_SCSIID; + ndlp->nlp_state = NLP_STE_UNMAPPED_NODE; + lpfc_nlp_list(phba, ndlp, NLP_UNMAPPED_LIST); + + lpfc_set_failmask(phba, ndlp, + (LPFC_DEV_DISCOVERY_INP | LPFC_DEV_DISCONNECTED), + LPFC_CLR_BITMASK); + + return (ndlp->nlp_state); +} + +static uint32_t +lpfc_device_rm_adisc_issue(struct lpfc_hba * phba, + struct lpfc_nodelist * ndlp, void *arg, + uint32_t evt) +{ + /* software abort outstanding ADISC */ + lpfc_els_abort(phba, ndlp, 1); + + lpfc_nlp_list(phba, ndlp, NLP_NO_LIST); + return (NLP_STE_FREED_NODE); +} + +static uint32_t +lpfc_device_recov_adisc_issue(struct lpfc_hba * phba, + struct lpfc_nodelist * ndlp, void *arg, + uint32_t evt) +{ + /* software abort outstanding ADISC */ + lpfc_els_abort(phba, ndlp, 1); + + ndlp->nlp_state = NLP_STE_NPR_NODE; + lpfc_nlp_list(phba, ndlp, NLP_NPR_LIST); + ndlp->nlp_flag &= ~NLP_NPR_2B_DISC; + + lpfc_disc_set_adisc(phba, ndlp); + return (ndlp->nlp_state); +} + +static uint32_t +lpfc_rcv_plogi_reglogin_issue(struct lpfc_hba * phba, + struct lpfc_nodelist * ndlp, void *arg, + uint32_t evt) +{ + struct lpfc_iocbq *cmdiocb; + struct lpfc_dmabuf *pcmd; + + cmdiocb = (struct lpfc_iocbq *) arg; + pcmd = (struct lpfc_dmabuf *) cmdiocb->context2; + + pci_dma_sync_single_for_cpu(phba->pcidev, pcmd->phys, + LPFC_BPL_SIZE, PCI_DMA_FROMDEVICE); + + lpfc_rcv_plogi(phba, ndlp, cmdiocb); + return (ndlp->nlp_state); +} + +static uint32_t +lpfc_rcv_prli_reglogin_issue(struct lpfc_hba * phba, + struct lpfc_nodelist * ndlp, void *arg, + uint32_t evt) +{ + struct lpfc_iocbq *cmdiocb; + struct lpfc_dmabuf *pcmd; + + cmdiocb = (struct lpfc_iocbq *) arg; + pcmd = (struct lpfc_dmabuf *) cmdiocb->context2; + + pci_dma_sync_single_for_cpu(phba->pcidev, pcmd->phys, + LPFC_BPL_SIZE, PCI_DMA_FROMDEVICE); + + lpfc_els_rsp_prli_acc(phba, cmdiocb, ndlp); + return (ndlp->nlp_state); +} + +static uint32_t +lpfc_rcv_logo_reglogin_issue(struct lpfc_hba * phba, + struct lpfc_nodelist * ndlp, void *arg, + uint32_t evt) +{ + struct lpfc_iocbq *cmdiocb; + struct lpfc_dmabuf *pcmd; + + cmdiocb = (struct lpfc_iocbq *) arg; + pcmd = (struct lpfc_dmabuf *) cmdiocb->context2; + + pci_dma_sync_single_for_cpu(phba->pcidev, pcmd->phys, + LPFC_BPL_SIZE, PCI_DMA_FROMDEVICE); + + lpfc_rcv_logo(phba, ndlp, cmdiocb); + return (ndlp->nlp_state); +} + +static uint32_t +lpfc_rcv_padisc_reglogin_issue(struct lpfc_hba * phba, + struct lpfc_nodelist * ndlp, void *arg, + uint32_t evt) +{ + struct lpfc_iocbq *cmdiocb; + struct lpfc_dmabuf *pcmd; + + cmdiocb = (struct lpfc_iocbq *) arg; + pcmd = (struct lpfc_dmabuf *) cmdiocb->context2; + + pci_dma_sync_single_for_cpu(phba->pcidev, pcmd->phys, + LPFC_BPL_SIZE, PCI_DMA_FROMDEVICE); + + lpfc_rcv_padisc(phba, ndlp, cmdiocb); + return (ndlp->nlp_state); +} + +static uint32_t +lpfc_rcv_prlo_reglogin_issue(struct lpfc_hba * phba, + struct lpfc_nodelist * ndlp, void *arg, + uint32_t evt) +{ + struct lpfc_iocbq *cmdiocb; + + cmdiocb = (struct lpfc_iocbq *) arg; + lpfc_els_rsp_acc(phba, ELS_CMD_ACC, cmdiocb, ndlp, NULL, 0); + return (ndlp->nlp_state); +} + +static uint32_t +lpfc_cmpl_reglogin_reglogin_issue(struct lpfc_hba * phba, + struct lpfc_nodelist * ndlp, + void *arg, uint32_t evt) +{ + LPFC_MBOXQ_t *pmb; + MAILBOX_t *mb; + uint32_t did; + + pmb = (LPFC_MBOXQ_t *) arg; + mb = &pmb->mb; + did = mb->un.varWords[1]; + if (mb->mbxStatus) { + /* RegLogin failed */ + lpfc_printf_log(phba, + KERN_ERR, + LOG_DISCOVERY, + "%d:0246 RegLogin failed Data: x%x x%x x%x\n", + phba->brd_no, + did, mb->mbxStatus, phba->hba_state); + + mod_timer(&ndlp->nlp_delayfunc, jiffies + HZ * 1); + ndlp->nlp_flag |= NLP_DELAY_TMO; + + lpfc_issue_els_logo(phba, ndlp, 0); + /* Put ndlp in npr list set plogi timer for 1 sec */ + ndlp->nlp_last_elscmd = (unsigned long)ELS_CMD_PLOGI; + ndlp->nlp_state = NLP_STE_NPR_NODE; + lpfc_nlp_list(phba, ndlp, NLP_NPR_LIST); + return (ndlp->nlp_state); + } + + if (ndlp->nlp_rpi != 0) + lpfc_findnode_remove_rpi(phba, ndlp->nlp_rpi); + + ndlp->nlp_rpi = mb->un.varWords[0]; + lpfc_addnode_rpi(phba, ndlp, ndlp->nlp_rpi); + + /* Only if we are not a fabric nport do we issue PRLI */ + if (!(ndlp->nlp_type & NLP_FABRIC)) { + ndlp->nlp_state = NLP_STE_PRLI_ISSUE; + lpfc_nlp_list(phba, ndlp, NLP_PRLI_LIST); + lpfc_issue_els_prli(phba, ndlp, 0); + } else { + ndlp->nlp_state = NLP_STE_UNMAPPED_NODE; + lpfc_nlp_list(phba, ndlp, NLP_UNMAPPED_LIST); + } + return (ndlp->nlp_state); +} + +static uint32_t +lpfc_device_rm_reglogin_issue(struct lpfc_hba * phba, + struct lpfc_nodelist * ndlp, void *arg, + uint32_t evt) +{ + lpfc_nlp_list(phba, ndlp, NLP_NO_LIST); + return (NLP_STE_FREED_NODE); +} + +static uint32_t +lpfc_device_recov_reglogin_issue(struct lpfc_hba * phba, + struct lpfc_nodelist * ndlp, void *arg, + uint32_t evt) +{ + ndlp->nlp_state = NLP_STE_NPR_NODE; + lpfc_nlp_list(phba, ndlp, NLP_NPR_LIST); + ndlp->nlp_flag &= ~NLP_NPR_2B_DISC; + return (ndlp->nlp_state); +} + +static uint32_t +lpfc_rcv_plogi_prli_issue(struct lpfc_hba * phba, + struct lpfc_nodelist * ndlp, void *arg, uint32_t evt) +{ + struct lpfc_iocbq *cmdiocb; + struct lpfc_dmabuf *pcmd; + + cmdiocb = (struct lpfc_iocbq *) arg; + pcmd = (struct lpfc_dmabuf *) cmdiocb->context2; + + pci_dma_sync_single_for_cpu(phba->pcidev, pcmd->phys, + LPFC_BPL_SIZE, PCI_DMA_FROMDEVICE); + + lpfc_rcv_plogi(phba, ndlp, cmdiocb); + return (ndlp->nlp_state); +} + +static uint32_t +lpfc_rcv_prli_prli_issue(struct lpfc_hba * phba, + struct lpfc_nodelist * ndlp, void *arg, uint32_t evt) +{ + struct lpfc_iocbq *cmdiocb; + struct lpfc_dmabuf *pcmd; + + cmdiocb = (struct lpfc_iocbq *) arg; + pcmd = (struct lpfc_dmabuf *) cmdiocb->context2; + + pci_dma_sync_single_for_cpu(phba->pcidev, pcmd->phys, + LPFC_BPL_SIZE, PCI_DMA_FROMDEVICE); + + lpfc_els_rsp_prli_acc(phba, cmdiocb, ndlp); + return (ndlp->nlp_state); +} + +static uint32_t +lpfc_rcv_logo_prli_issue(struct lpfc_hba * phba, + struct lpfc_nodelist * ndlp, void *arg, uint32_t evt) +{ + struct lpfc_iocbq *cmdiocb; + struct lpfc_dmabuf *pcmd; + + cmdiocb = (struct lpfc_iocbq *) arg; + pcmd = (struct lpfc_dmabuf *) cmdiocb->context2; + + pci_dma_sync_single_for_cpu(phba->pcidev, pcmd->phys, + LPFC_BPL_SIZE, PCI_DMA_FROMDEVICE); + + /* Software abort outstanding PRLI before sending acc */ + lpfc_els_abort(phba, ndlp, 1); + + lpfc_rcv_logo(phba, ndlp, cmdiocb); + return (ndlp->nlp_state); +} + +static uint32_t +lpfc_rcv_padisc_prli_issue(struct lpfc_hba * phba, + struct lpfc_nodelist * ndlp, void *arg, uint32_t evt) +{ + struct lpfc_iocbq *cmdiocb; + struct lpfc_dmabuf *pcmd; + + cmdiocb = (struct lpfc_iocbq *) arg; + pcmd = (struct lpfc_dmabuf *) cmdiocb->context2; + + pci_dma_sync_single_for_cpu(phba->pcidev, pcmd->phys, + LPFC_BPL_SIZE, PCI_DMA_FROMDEVICE); + + lpfc_rcv_padisc(phba, ndlp, cmdiocb); + return (ndlp->nlp_state); +} + +/* This routine is envoked when we rcv a PRLO request from a nport + * we are logged into. We should send back a PRLO rsp setting the + * appropriate bits. + * NEXT STATE = PRLI_ISSUE + */ +static uint32_t +lpfc_rcv_prlo_prli_issue(struct lpfc_hba * phba, + struct lpfc_nodelist * ndlp, void *arg, uint32_t evt) +{ + struct lpfc_iocbq *cmdiocb; + + cmdiocb = (struct lpfc_iocbq *) arg; + lpfc_els_rsp_acc(phba, ELS_CMD_ACC, cmdiocb, ndlp, NULL, 0); + return (ndlp->nlp_state); +} + +static uint32_t +lpfc_cmpl_prli_prli_issue(struct lpfc_hba * phba, + struct lpfc_nodelist * ndlp, void *arg, uint32_t evt) +{ + struct lpfc_iocbq *cmdiocb, *rspiocb; + IOCB_t *irsp; + PRLI *npr; + struct lpfc_bindlist *blp; + + cmdiocb = (struct lpfc_iocbq *) arg; + rspiocb = cmdiocb->context_un.rsp_iocb; + npr = (PRLI *)lpfc_check_elscmpl_iocb(phba, cmdiocb, rspiocb); + + irsp = &rspiocb->iocb; + if (irsp->ulpStatus) { + ndlp->nlp_state = NLP_STE_UNMAPPED_NODE; + lpfc_nlp_list(phba, ndlp, NLP_UNMAPPED_LIST); + lpfc_set_failmask(phba, ndlp, LPFC_DEV_DISCOVERY_INP, + LPFC_CLR_BITMASK); + return (ndlp->nlp_state); + } + + /* Check out PRLI rsp */ + if ((npr->acceptRspCode != PRLI_REQ_EXECUTED) || + (npr->prliType != PRLI_FCP_TYPE) || (npr->targetFunc != 1)) { + ndlp->nlp_state = NLP_STE_UNMAPPED_NODE; + lpfc_nlp_list(phba, ndlp, NLP_UNMAPPED_LIST); + lpfc_set_failmask(phba, ndlp, + (LPFC_DEV_DISCOVERY_INP | LPFC_DEV_DISCONNECTED), + LPFC_CLR_BITMASK); + return (ndlp->nlp_state); + } + if (npr->Retry == 1) { + ndlp->nlp_fcp_info |= NLP_FCP_2_DEVICE; + } + + /* Can we assign a SCSI Id to this NPort */ + if ((blp = lpfc_consistent_bind_get(phba, ndlp))) { + /* Next 4 lines MUST be in this order */ + if(lpfc_assign_binding(phba, ndlp, blp)) { + ndlp->nlp_state = NLP_STE_MAPPED_NODE; + lpfc_nlp_list(phba, ndlp, NLP_MAPPED_LIST); + ndlp->nlp_listp_bind = blp; + + lpfc_set_failmask(phba, ndlp, + (LPFC_DEV_DISCOVERY_INP|LPFC_DEV_DISCONNECTED), + LPFC_CLR_BITMASK); + return (ndlp->nlp_state); + } + } + ndlp->nlp_flag |= NLP_TGT_NO_SCSIID; + ndlp->nlp_state = NLP_STE_UNMAPPED_NODE; + lpfc_nlp_list(phba, ndlp, NLP_UNMAPPED_LIST); + + lpfc_set_failmask(phba, ndlp, + (LPFC_DEV_DISCOVERY_INP | LPFC_DEV_DISCONNECTED), + LPFC_CLR_BITMASK); + return (ndlp->nlp_state); +} + +/*! lpfc_device_rm_prli_issue + * + * \pre + * \post + * \param phba + * \param ndlp + * \param arg + * \param evt + * \return uint32_t + * + * \b Description: + * This routine is envoked when we a request to remove a nport we are in the + * process of PRLIing. We should software abort outstanding prli, unreg + * login, send a logout. We will change node state to UNUSED_NODE, put it + * on plogi list so it can be freed when LOGO completes. + * + */ +static uint32_t +lpfc_device_rm_prli_issue(struct lpfc_hba * phba, + struct lpfc_nodelist * ndlp, void *arg, uint32_t evt) +{ + /* software abort outstanding PRLI */ + lpfc_els_abort(phba, ndlp, 1); + + lpfc_nlp_list(phba, ndlp, NLP_NO_LIST); + return (NLP_STE_FREED_NODE); +} + + +/*! lpfc_device_recov_prli_issue + * + * \pre + * \post + * \param phba + * \param ndlp + * \param arg + * \param evt + * \return uint32_t + * + * \b Description: + * The routine is envoked when the state of a device is unknown, like + * during a link down. We should remove the nodelist entry from the + * unmapped list, issue a UNREG_LOGIN, do a software abort of the + * outstanding PRLI command, then free the node entry. + */ +static uint32_t +lpfc_device_recov_prli_issue(struct lpfc_hba * phba, + struct lpfc_nodelist * ndlp, void *arg, uint32_t evt) +{ + /* software abort outstanding PRLI */ + lpfc_els_abort(phba, ndlp, 1); + + ndlp->nlp_state = NLP_STE_NPR_NODE; + lpfc_nlp_list(phba, ndlp, NLP_NPR_LIST); + ndlp->nlp_flag &= ~NLP_NPR_2B_DISC; + return (ndlp->nlp_state); +} + +static uint32_t +lpfc_rcv_plogi_unmap_node(struct lpfc_hba * phba, + struct lpfc_nodelist * ndlp, void *arg, uint32_t evt) +{ + struct lpfc_iocbq *cmdiocb; + struct lpfc_dmabuf *pcmd; + + cmdiocb = (struct lpfc_iocbq *) arg; + pcmd = (struct lpfc_dmabuf *) cmdiocb->context2; + + pci_dma_sync_single_for_cpu(phba->pcidev, pcmd->phys, + LPFC_BPL_SIZE, PCI_DMA_FROMDEVICE); + + lpfc_rcv_plogi(phba, ndlp, cmdiocb); + return (ndlp->nlp_state); +} + +static uint32_t +lpfc_rcv_prli_unmap_node(struct lpfc_hba * phba, + struct lpfc_nodelist * ndlp, void *arg, uint32_t evt) +{ + struct lpfc_iocbq *cmdiocb; + struct lpfc_dmabuf *pcmd; + + cmdiocb = (struct lpfc_iocbq *) arg; + pcmd = (struct lpfc_dmabuf *) cmdiocb->context2; + + pci_dma_sync_single_for_cpu(phba->pcidev, pcmd->phys, + LPFC_BPL_SIZE, PCI_DMA_FROMDEVICE); + + lpfc_els_rsp_prli_acc(phba, cmdiocb, ndlp); + return (ndlp->nlp_state); +} + +static uint32_t +lpfc_rcv_logo_unmap_node(struct lpfc_hba * phba, + struct lpfc_nodelist * ndlp, void *arg, uint32_t evt) +{ + struct lpfc_iocbq *cmdiocb; + struct lpfc_dmabuf *pcmd; + + cmdiocb = (struct lpfc_iocbq *) arg; + pcmd = (struct lpfc_dmabuf *) cmdiocb->context2; + + pci_dma_sync_single_for_cpu(phba->pcidev, pcmd->phys, + LPFC_BPL_SIZE, PCI_DMA_FROMDEVICE); + + lpfc_rcv_logo(phba, ndlp, cmdiocb); + return (ndlp->nlp_state); +} + +static uint32_t +lpfc_rcv_padisc_unmap_node(struct lpfc_hba * phba, + struct lpfc_nodelist * ndlp, void *arg, uint32_t evt) +{ + struct lpfc_iocbq *cmdiocb; + struct lpfc_dmabuf *pcmd; + + cmdiocb = (struct lpfc_iocbq *) arg; + pcmd = (struct lpfc_dmabuf *) cmdiocb->context2; + + pci_dma_sync_single_for_cpu(phba->pcidev, pcmd->phys, + LPFC_BPL_SIZE, PCI_DMA_FROMDEVICE); + + lpfc_rcv_padisc(phba, ndlp, cmdiocb); + return (ndlp->nlp_state); +} + +static uint32_t +lpfc_rcv_prlo_unmap_node(struct lpfc_hba * phba, + struct lpfc_nodelist * ndlp, void *arg, uint32_t evt) +{ + struct lpfc_iocbq *cmdiocb; + struct lpfc_dmabuf *pcmd; + + cmdiocb = (struct lpfc_iocbq *) arg; + pcmd = (struct lpfc_dmabuf *) cmdiocb->context2; + + pci_dma_sync_single_for_cpu(phba->pcidev, pcmd->phys, + LPFC_BPL_SIZE, PCI_DMA_FROMDEVICE); + + /* Treat like rcv logo */ + lpfc_rcv_logo(phba, ndlp, cmdiocb); + return (ndlp->nlp_state); +} + +static uint32_t +lpfc_device_recov_unmap_node(struct lpfc_hba * phba, + struct lpfc_nodelist * ndlp, void *arg, uint32_t evt) +{ + ndlp->nlp_state = NLP_STE_NPR_NODE; + lpfc_nlp_list(phba, ndlp, NLP_NPR_LIST); + ndlp->nlp_flag &= ~NLP_NPR_2B_DISC; + lpfc_disc_set_adisc(phba, ndlp); + + return (ndlp->nlp_state); +} + +static uint32_t +lpfc_rcv_plogi_mapped_node(struct lpfc_hba * phba, + struct lpfc_nodelist * ndlp, void *arg, uint32_t evt) +{ + struct lpfc_iocbq *cmdiocb; + struct lpfc_dmabuf *pcmd; + + cmdiocb = (struct lpfc_iocbq *) arg; + pcmd = (struct lpfc_dmabuf *) cmdiocb->context2; + + pci_dma_sync_single_for_cpu(phba->pcidev, pcmd->phys, + LPFC_BPL_SIZE, PCI_DMA_FROMDEVICE); + + lpfc_rcv_plogi(phba, ndlp, cmdiocb); + return (ndlp->nlp_state); +} + +static uint32_t +lpfc_rcv_prli_mapped_node(struct lpfc_hba * phba, + struct lpfc_nodelist * ndlp, void *arg, uint32_t evt) +{ + struct lpfc_iocbq *cmdiocb; + struct lpfc_dmabuf *pcmd; + + cmdiocb = (struct lpfc_iocbq *) arg; + pcmd = (struct lpfc_dmabuf *) cmdiocb->context2; + + pci_dma_sync_single_for_cpu(phba->pcidev, pcmd->phys, + LPFC_BPL_SIZE, PCI_DMA_FROMDEVICE); + + lpfc_els_rsp_prli_acc(phba, cmdiocb, ndlp); + return (ndlp->nlp_state); +} + +static uint32_t +lpfc_rcv_logo_mapped_node(struct lpfc_hba * phba, + struct lpfc_nodelist * ndlp, void *arg, uint32_t evt) +{ + struct lpfc_iocbq *cmdiocb; + struct lpfc_dmabuf *pcmd; + + cmdiocb = (struct lpfc_iocbq *) arg; + pcmd = (struct lpfc_dmabuf *) cmdiocb->context2; + + pci_dma_sync_single_for_cpu(phba->pcidev, pcmd->phys, + LPFC_BPL_SIZE, PCI_DMA_FROMDEVICE); + + lpfc_rcv_logo(phba, ndlp, cmdiocb); + return (ndlp->nlp_state); +} + +static uint32_t +lpfc_rcv_padisc_mapped_node(struct lpfc_hba * phba, + struct lpfc_nodelist * ndlp, void *arg, + uint32_t evt) +{ + struct lpfc_iocbq *cmdiocb; + struct lpfc_dmabuf *pcmd; + + cmdiocb = (struct lpfc_iocbq *) arg; + pcmd = (struct lpfc_dmabuf *) cmdiocb->context2; + + pci_dma_sync_single_for_cpu(phba->pcidev, pcmd->phys, + LPFC_BPL_SIZE, PCI_DMA_FROMDEVICE); + + lpfc_rcv_padisc(phba, ndlp, cmdiocb); + return (ndlp->nlp_state); +} + +static uint32_t +lpfc_rcv_prlo_mapped_node(struct lpfc_hba * phba, + struct lpfc_nodelist * ndlp, void *arg, uint32_t evt) +{ + struct lpfc_iocbq *cmdiocb; + struct lpfc_dmabuf *pcmd; + + cmdiocb = (struct lpfc_iocbq *) arg; + pcmd = (struct lpfc_dmabuf *) cmdiocb->context2; + + pci_dma_sync_single_for_cpu(phba->pcidev, pcmd->phys, + LPFC_BPL_SIZE, PCI_DMA_FROMDEVICE); + + /* flush the target */ + lpfc_sli_abort_iocb_tgt(phba, + &phba->sli.ring[phba->sli.fcp_ring], + ndlp->nlp_sid, LPFC_ABORT_ALLQ); + + /* Treat like rcv logo */ + lpfc_rcv_logo(phba, ndlp, cmdiocb); + return (ndlp->nlp_state); +} + +static uint32_t +lpfc_device_recov_mapped_node(struct lpfc_hba * phba, + struct lpfc_nodelist * ndlp, void *arg, + uint32_t evt) +{ + ndlp->nlp_state = NLP_STE_NPR_NODE; + lpfc_nlp_list(phba, ndlp, NLP_NPR_LIST); + ndlp->nlp_flag &= ~NLP_NPR_2B_DISC; + lpfc_disc_set_adisc(phba, ndlp); + return (ndlp->nlp_state); +} + +static uint32_t +lpfc_rcv_plogi_npr_node(struct lpfc_hba * phba, + struct lpfc_nodelist * ndlp, void *arg, + uint32_t evt) +{ + struct lpfc_iocbq *cmdiocb; + struct lpfc_dmabuf *pcmd; + + cmdiocb = (struct lpfc_iocbq *) arg; + pcmd = (struct lpfc_dmabuf *) cmdiocb->context2; + + pci_dma_sync_single_for_cpu(phba->pcidev, pcmd->phys, + LPFC_BPL_SIZE, PCI_DMA_FROMDEVICE); + + /* Ignore PLOGI if we have an outstanding LOGO */ + if (ndlp->nlp_flag & NLP_LOGO_SND) { + return (ndlp->nlp_state); + } + + if(lpfc_rcv_plogi(phba, ndlp, cmdiocb)) { + ndlp->nlp_flag &= ~(NLP_NPR_ADISC | NLP_NPR_2B_DISC); + return (ndlp->nlp_state); + } + + /* send PLOGI immediately, move to PLOGI issue state */ + if(!(ndlp->nlp_flag & NLP_DELAY_TMO)) { + ndlp->nlp_state = NLP_STE_PLOGI_ISSUE; + lpfc_nlp_list(phba, ndlp, NLP_PLOGI_LIST); + lpfc_issue_els_plogi(phba, ndlp, 0); + } + return (ndlp->nlp_state); +} + +static uint32_t +lpfc_rcv_prli_npr_node(struct lpfc_hba * phba, + struct lpfc_nodelist * ndlp, void *arg, + uint32_t evt) +{ + struct lpfc_iocbq *cmdiocb; + struct lpfc_dmabuf *pcmd; + struct ls_rjt stat; + + cmdiocb = (struct lpfc_iocbq *) arg; + pcmd = (struct lpfc_dmabuf *) cmdiocb->context2; + + pci_dma_sync_single_for_cpu(phba->pcidev, pcmd->phys, + LPFC_BPL_SIZE, PCI_DMA_FROMDEVICE); + + memset(&stat, 0, sizeof (struct ls_rjt)); + stat.un.b.lsRjtRsnCode = LSRJT_UNABLE_TPC; + stat.un.b.lsRjtRsnCodeExp = LSEXP_NOTHING_MORE; + lpfc_els_rsp_reject(phba, stat.un.lsRjtError, cmdiocb, ndlp); + + if(!(ndlp->nlp_flag & NLP_DELAY_TMO)) { + if (ndlp->nlp_flag & NLP_NPR_ADISC) { + ndlp->nlp_state = NLP_STE_ADISC_ISSUE; + lpfc_nlp_list(phba, ndlp, NLP_ADISC_LIST); + lpfc_issue_els_adisc(phba, ndlp, 0); + } else { + ndlp->nlp_state = NLP_STE_PLOGI_ISSUE; + lpfc_nlp_list(phba, ndlp, NLP_PLOGI_LIST); + lpfc_issue_els_plogi(phba, ndlp, 0); + } + } + return (ndlp->nlp_state); +} + +static uint32_t +lpfc_rcv_logo_npr_node(struct lpfc_hba * phba, + struct lpfc_nodelist * ndlp, void *arg, + uint32_t evt) +{ + struct lpfc_iocbq *cmdiocb; + struct lpfc_dmabuf *pcmd; + + cmdiocb = (struct lpfc_iocbq *) arg; + pcmd = (struct lpfc_dmabuf *) cmdiocb->context2; + + pci_dma_sync_single_for_cpu(phba->pcidev, pcmd->phys, + LPFC_BPL_SIZE, PCI_DMA_FROMDEVICE); + + lpfc_rcv_logo(phba, ndlp, cmdiocb); + return (ndlp->nlp_state); +} + +static uint32_t +lpfc_rcv_padisc_npr_node(struct lpfc_hba * phba, + struct lpfc_nodelist * ndlp, void *arg, + uint32_t evt) +{ + struct lpfc_iocbq *cmdiocb; + struct lpfc_dmabuf *pcmd; + + cmdiocb = (struct lpfc_iocbq *) arg; + pcmd = (struct lpfc_dmabuf *) cmdiocb->context2; + + pci_dma_sync_single_for_cpu(phba->pcidev, pcmd->phys, + LPFC_BPL_SIZE, PCI_DMA_FROMDEVICE); + + lpfc_rcv_padisc(phba, ndlp, cmdiocb); + + if(!(ndlp->nlp_flag & NLP_DELAY_TMO)) { + if (ndlp->nlp_flag & NLP_NPR_ADISC) { + ndlp->nlp_state = NLP_STE_ADISC_ISSUE; + lpfc_nlp_list(phba, ndlp, NLP_ADISC_LIST); + lpfc_issue_els_adisc(phba, ndlp, 0); + } else { + ndlp->nlp_state = NLP_STE_PLOGI_ISSUE; + lpfc_nlp_list(phba, ndlp, NLP_PLOGI_LIST); + lpfc_issue_els_plogi(phba, ndlp, 0); + } + } + return (ndlp->nlp_state); +} + +static uint32_t +lpfc_rcv_prlo_npr_node(struct lpfc_hba * phba, + struct lpfc_nodelist * ndlp, void *arg, + uint32_t evt) +{ + struct lpfc_iocbq *cmdiocb; + struct lpfc_dmabuf *pcmd; + + cmdiocb = (struct lpfc_iocbq *) arg; + pcmd = (struct lpfc_dmabuf *) cmdiocb->context2; + + pci_dma_sync_single_for_cpu(phba->pcidev, pcmd->phys, + LPFC_BPL_SIZE, PCI_DMA_FROMDEVICE); + + lpfc_els_rsp_acc(phba, ELS_CMD_ACC, cmdiocb, ndlp, NULL, 0); + + if(ndlp->nlp_flag & NLP_DELAY_TMO) { + if (ndlp->nlp_last_elscmd == (unsigned long)ELS_CMD_PLOGI) { + return (ndlp->nlp_state); + } else { + del_timer_sync(&ndlp->nlp_delayfunc); + ndlp->nlp_flag &= ~NLP_DELAY_TMO; + } + } + + ndlp->nlp_state = NLP_STE_PLOGI_ISSUE; + lpfc_nlp_list(phba, ndlp, NLP_PLOGI_LIST); + lpfc_issue_els_plogi(phba, ndlp, 0); + return (ndlp->nlp_state); +} + +static uint32_t +lpfc_cmpl_logo_npr_node(struct lpfc_hba * phba, + struct lpfc_nodelist * ndlp, void *arg, uint32_t evt) +{ + lpfc_unreg_rpi(phba, ndlp); + /* This routine does nothing, just return the current state */ + return (ndlp->nlp_state); +} + +static uint32_t +lpfc_cmpl_reglogin_npr_node(struct lpfc_hba * phba, + struct lpfc_nodelist * ndlp, void *arg, + uint32_t evt) +{ + LPFC_MBOXQ_t *pmb; + MAILBOX_t *mb; + + pmb = (LPFC_MBOXQ_t *) arg; + mb = &pmb->mb; + + /* save rpi */ + if (ndlp->nlp_rpi != 0) + lpfc_findnode_remove_rpi(phba, ndlp->nlp_rpi); + + ndlp->nlp_rpi = mb->un.varWords[0]; + lpfc_addnode_rpi(phba, ndlp, ndlp->nlp_rpi); + + return (ndlp->nlp_state); +} + +static uint32_t +lpfc_device_rm_npr_node(struct lpfc_hba * phba, + struct lpfc_nodelist * ndlp, void *arg, + uint32_t evt) +{ + lpfc_nlp_list(phba, ndlp, NLP_NO_LIST); + return (NLP_STE_FREED_NODE); +} + +static uint32_t +lpfc_device_recov_npr_node(struct lpfc_hba * phba, + struct lpfc_nodelist * ndlp, void *arg, + uint32_t evt) +{ + ndlp->nlp_flag &= ~NLP_NPR_2B_DISC; + return (ndlp->nlp_state); +} + + +/* This next section defines the NPort Discovery State Machine */ + +/* There are 4 different double linked lists nodelist entries can reside on. + * The plogi list and adisc list are used when Link Up discovery or RSCN + * processing is needed. Each list holds the nodes that we will send PLOGI + * or ADISC on. These lists will keep track of what nodes will be effected + * by an RSCN, or a Link Up (Typically, all nodes are effected on Link Up). + * The unmapped_list will contain all nodes that we have successfully logged + * into at the Fibre Channel level. The mapped_list will contain all nodes + * that are mapped FCP targets. + */ +/* + * The bind list is a list of undiscovered (potentially non-existent) nodes + * that we have saved binding information on. This information is used when + * nodes transition from the unmapped to the mapped list. + */ +/* For UNUSED_NODE state, the node has just been allocated . + * For PLOGI_ISSUE and REG_LOGIN_ISSUE, the node is on + * the PLOGI list. For REG_LOGIN_COMPL, the node is taken off the PLOGI list + * and put on the unmapped list. For ADISC processing, the node is taken off + * the ADISC list and placed on either the mapped or unmapped list (depending + * on its previous state). Once on the unmapped list, a PRLI is issued and the + * state changed to PRLI_ISSUE. When the PRLI completion occurs, the state is + * changed to UNMAPPED_NODE. If the completion indicates a mapped + * node, the node is taken off the unmapped list. The binding list is checked + * for a valid binding, or a binding is automatically assigned. If binding + * assignment is unsuccessful, the node is left on the unmapped list. If + * binding assignment is successful, the associated binding list entry (if + * any) is removed, and the node is placed on the mapped list. + */ +/* + * For a Link Down, all nodes on the ADISC, PLOGI, unmapped or mapped + * lists will receive a DEVICE_RECOVERY event. If the linkdown or nodev timers + * expire, all effected nodes will receive a DEVICE_RM event. + */ +/* + * For a Link Up or RSCN, all nodes will move from the mapped / unmapped lists + * to either the ADISC or PLOGI list. After a Nameserver query or ALPA loopmap + * check, additional nodes may be added or removed (via DEVICE_RM) to / from + * the PLOGI or ADISC lists. Once the PLOGI and ADISC lists are populated, + * we will first process the ADISC list. 32 entries are processed initially and + * ADISC is initited for each one. Completions / Events for each node are + * funnelled thru the state machine. As each node finishes ADISC processing, it + * starts ADISC for any nodes waiting for ADISC processing. If no nodes are + * waiting, and the ADISC list count is identically 0, then we are done. For + * Link Up discovery, since all nodes on the PLOGI list are UNREG_LOGIN'ed, we + * can issue a CLEAR_LA and reenable Link Events. Next we will process the PLOGI + * list. 32 entries are processed initially and PLOGI is initited for each one. + * Completions / Events for each node are funnelled thru the state machine. As + * each node finishes PLOGI processing, it starts PLOGI for any nodes waiting + * for PLOGI processing. If no nodes are waiting, and the PLOGI list count is + * indentically 0, then we are done. We have now completed discovery / RSCN + * handling. Upon completion, ALL nodes should be on either the mapped or + * unmapped lists. + */ + +static void *lpfc_disc_action[NLP_STE_MAX_STATE * NLP_EVT_MAX_EVENT] = { + /* Action routine Event Current State */ + (void *)lpfc_rcv_plogi_unused_node, /* RCV_PLOGI UNUSED_NODE */ + (void *)lpfc_rcv_els_unused_node, /* RCV_PRLI */ + (void *)lpfc_rcv_logo_unused_node, /* RCV_LOGO */ + (void *)lpfc_rcv_els_unused_node, /* RCV_ADISC */ + (void *)lpfc_rcv_els_unused_node, /* RCV_PDISC */ + (void *)lpfc_rcv_els_unused_node, /* RCV_PRLO */ + (void *)lpfc_disc_illegal, /* CMPL_PLOGI */ + (void *)lpfc_disc_illegal, /* CMPL_PRLI */ + (void *)lpfc_cmpl_logo_unused_node, /* CMPL_LOGO */ + (void *)lpfc_disc_illegal, /* CMPL_ADISC */ + (void *)lpfc_disc_illegal, /* CMPL_REG_LOGIN */ + (void *)lpfc_device_rm_unused_node, /* DEVICE_RM */ + (void *)lpfc_disc_illegal, /* DEVICE_RECOVERY */ + + (void *)lpfc_rcv_plogi_plogi_issue, /* RCV_PLOGI PLOGI_ISSUE */ + (void *)lpfc_rcv_els_plogi_issue, /* RCV_PRLI */ + (void *)lpfc_rcv_els_plogi_issue, /* RCV_LOGO */ + (void *)lpfc_rcv_els_plogi_issue, /* RCV_ADISC */ + (void *)lpfc_rcv_els_plogi_issue, /* RCV_PDISC */ + (void *)lpfc_rcv_els_plogi_issue, /* RCV_PRLO */ + (void *)lpfc_cmpl_plogi_plogi_issue, /* CMPL_PLOGI */ + (void *)lpfc_disc_illegal, /* CMPL_PRLI */ + (void *)lpfc_disc_illegal, /* CMPL_LOGO */ + (void *)lpfc_disc_illegal, /* CMPL_ADISC */ + (void *)lpfc_disc_illegal, /* CMPL_REG_LOGIN */ + (void *)lpfc_device_rm_plogi_issue, /* DEVICE_RM */ + (void *)lpfc_device_recov_plogi_issue, /* DEVICE_RECOVERY */ + + (void *)lpfc_rcv_plogi_adisc_issue, /* RCV_PLOGI ADISC_ISSUE */ + (void *)lpfc_rcv_prli_adisc_issue, /* RCV_PRLI */ + (void *)lpfc_rcv_logo_adisc_issue, /* RCV_LOGO */ + (void *)lpfc_rcv_padisc_adisc_issue, /* RCV_ADISC */ + (void *)lpfc_rcv_padisc_adisc_issue, /* RCV_PDISC */ + (void *)lpfc_rcv_prlo_adisc_issue, /* RCV_PRLO */ + (void *)lpfc_disc_illegal, /* CMPL_PLOGI */ + (void *)lpfc_disc_illegal, /* CMPL_PRLI */ + (void *)lpfc_disc_illegal, /* CMPL_LOGO */ + (void *)lpfc_cmpl_adisc_adisc_issue, /* CMPL_ADISC */ + (void *)lpfc_disc_illegal, /* CMPL_REG_LOGIN */ + (void *)lpfc_device_rm_adisc_issue, /* DEVICE_RM */ + (void *)lpfc_device_recov_adisc_issue, /* DEVICE_RECOVERY */ + + (void *)lpfc_rcv_plogi_reglogin_issue, /* RCV_PLOGI REG_LOGIN_ISSUE */ + (void *)lpfc_rcv_prli_reglogin_issue, /* RCV_PLOGI */ + (void *)lpfc_rcv_logo_reglogin_issue, /* RCV_LOGO */ + (void *)lpfc_rcv_padisc_reglogin_issue, /* RCV_ADISC */ + (void *)lpfc_rcv_padisc_reglogin_issue, /* RCV_PDISC */ + (void *)lpfc_rcv_prlo_reglogin_issue, /* RCV_PRLO */ + (void *)lpfc_disc_illegal, /* CMPL_PLOGI */ + (void *)lpfc_disc_illegal, /* CMPL_PRLI */ + (void *)lpfc_disc_illegal, /* CMPL_LOGO */ + (void *)lpfc_disc_illegal, /* CMPL_ADISC */ + (void *)lpfc_cmpl_reglogin_reglogin_issue,/* CMPL_REG_LOGIN */ + (void *)lpfc_device_rm_reglogin_issue, /* DEVICE_RM */ + (void *)lpfc_device_recov_reglogin_issue,/* DEVICE_RECOVERY */ + + (void *)lpfc_rcv_plogi_prli_issue, /* RCV_PLOGI PRLI_ISSUE */ + (void *)lpfc_rcv_prli_prli_issue, /* RCV_PRLI */ + (void *)lpfc_rcv_logo_prli_issue, /* RCV_LOGO */ + (void *)lpfc_rcv_padisc_prli_issue, /* RCV_ADISC */ + (void *)lpfc_rcv_padisc_prli_issue, /* RCV_PDISC */ + (void *)lpfc_rcv_prlo_prli_issue, /* RCV_PRLO */ + (void *)lpfc_disc_illegal, /* CMPL_PLOGI */ + (void *)lpfc_cmpl_prli_prli_issue, /* CMPL_PRLI */ + (void *)lpfc_disc_illegal, /* CMPL_LOGO */ + (void *)lpfc_disc_illegal, /* CMPL_ADISC */ + (void *)lpfc_disc_illegal, /* CMPL_REG_LOGIN */ + (void *)lpfc_device_rm_prli_issue, /* DEVICE_RM */ + (void *)lpfc_device_recov_prli_issue, /* DEVICE_RECOVERY */ + + (void *)lpfc_rcv_plogi_unmap_node, /* RCV_PLOGI UNMAPPED_NODE */ + (void *)lpfc_rcv_prli_unmap_node, /* RCV_PRLI */ + (void *)lpfc_rcv_logo_unmap_node, /* RCV_LOGO */ + (void *)lpfc_rcv_padisc_unmap_node, /* RCV_ADISC */ + (void *)lpfc_rcv_padisc_unmap_node, /* RCV_PDISC */ + (void *)lpfc_rcv_prlo_unmap_node, /* RCV_PRLO */ + (void *)lpfc_disc_illegal, /* CMPL_PLOGI */ + (void *)lpfc_disc_illegal, /* CMPL_PRLI */ + (void *)lpfc_disc_illegal, /* CMPL_LOGO */ + (void *)lpfc_disc_illegal, /* CMPL_ADISC */ + (void *)lpfc_disc_illegal, /* CMPL_REG_LOGIN */ + (void *)lpfc_disc_illegal, /* DEVICE_RM */ + (void *)lpfc_device_recov_unmap_node, /* DEVICE_RECOVERY */ + + (void *)lpfc_rcv_plogi_mapped_node, /* RCV_PLOGI MAPPED_NODE */ + (void *)lpfc_rcv_prli_mapped_node, /* RCV_PRLI */ + (void *)lpfc_rcv_logo_mapped_node, /* RCV_LOGO */ + (void *)lpfc_rcv_padisc_mapped_node, /* RCV_ADISC */ + (void *)lpfc_rcv_padisc_mapped_node, /* RCV_PDISC */ + (void *)lpfc_rcv_prlo_mapped_node, /* RCV_PRLO */ + (void *)lpfc_disc_illegal, /* CMPL_PLOGI */ + (void *)lpfc_disc_illegal, /* CMPL_PRLI */ + (void *)lpfc_disc_illegal, /* CMPL_LOGO */ + (void *)lpfc_disc_illegal, /* CMPL_ADISC */ + (void *)lpfc_disc_illegal, /* CMPL_REG_LOGIN */ + (void *)lpfc_disc_illegal, /* DEVICE_RM */ + (void *)lpfc_device_recov_mapped_node, /* DEVICE_RECOVERY */ + + (void *)lpfc_rcv_plogi_npr_node, /* RCV_PLOGI NPR_NODE */ + (void *)lpfc_rcv_prli_npr_node, /* RCV_PRLI */ + (void *)lpfc_rcv_logo_npr_node, /* RCV_LOGO */ + (void *)lpfc_rcv_padisc_npr_node, /* RCV_ADISC */ + (void *)lpfc_rcv_padisc_npr_node, /* RCV_PDISC */ + (void *)lpfc_rcv_prlo_npr_node, /* RCV_PRLO */ + (void *)lpfc_disc_noop, /* CMPL_PLOGI */ + (void *)lpfc_disc_noop, /* CMPL_PRLI */ + (void *)lpfc_cmpl_logo_npr_node, /* CMPL_LOGO */ + (void *)lpfc_disc_noop, /* CMPL_ADISC */ + (void *)lpfc_cmpl_reglogin_npr_node, /* CMPL_REG_LOGIN */ + (void *)lpfc_device_rm_npr_node, /* DEVICE_RM */ + (void *)lpfc_device_recov_npr_node, /* DEVICE_RECOVERY */ +}; + +int +lpfc_disc_state_machine(struct lpfc_hba * phba, + struct lpfc_nodelist * ndlp, void *arg, uint32_t evt) +{ + uint32_t cur_state, rc; + uint32_t(*func) (struct lpfc_hba *, struct lpfc_nodelist *, void *, + uint32_t); + + ndlp->nlp_disc_refcnt++; + cur_state = ndlp->nlp_state; + + /* DSM in event on NPort in state */ + lpfc_printf_log(phba, + KERN_INFO, + LOG_DISCOVERY, + "%d:0211 DSM in event x%x on NPort x%x in state %d " + "Data: x%x\n", + phba->brd_no, + evt, ndlp->nlp_DID, cur_state, ndlp->nlp_flag); + + func = (uint32_t(*)(struct lpfc_hba *, struct lpfc_nodelist *, void *, + uint32_t)) + lpfc_disc_action[(cur_state * NLP_EVT_MAX_EVENT) + evt]; + rc = (func) (phba, ndlp, arg, evt); + + /* DSM out state on NPort */ + lpfc_printf_log(phba, + KERN_INFO, + LOG_DISCOVERY, + "%d:0212 DSM out state %d on NPort x%x Data: x%x\n", + phba->brd_no, + rc, ndlp->nlp_DID, ndlp->nlp_flag); + + ndlp->nlp_disc_refcnt--; + + /* Check to see if ndlp removal is deferred */ + if ((ndlp->nlp_disc_refcnt == 0) + && (ndlp->nlp_flag & NLP_DELAY_REMOVE)) { + + ndlp->nlp_flag &= ~NLP_DELAY_REMOVE; + lpfc_nlp_remove(phba, ndlp); + return (NLP_STE_FREED_NODE); + } + if (rc == NLP_STE_FREED_NODE) + return (NLP_STE_FREED_NODE); + ndlp->nlp_state = rc; + return (rc); +} diff --git a/drivers/scsi/lpfc/lpfc_scsi.h b/drivers/scsi/lpfc/lpfc_scsi.h new file mode 100644 index 000000000..12412c283 --- /dev/null +++ b/drivers/scsi/lpfc/lpfc_scsi.h @@ -0,0 +1,92 @@ +/******************************************************************* + * This file is part of the Emulex Linux Device Driver for * + * Enterprise Fibre Channel Host Bus Adapters. * + * Refer to the README file included with this package for * + * driver version and adapter support. * + * Copyright (C) 2004 Emulex Corporation. * + * www.emulex.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, a copy of which * + * can be found in the file COPYING included with this package. * + *******************************************************************/ + +/* + * $Id: lpfc_scsi.h 1.68 2004/11/10 11:40:43EST sf_support Exp $ + */ + +#ifndef _H_LPFC_SCSI +#define _H_LPFC_SCSI + +#include "lpfc_disc.h" +#include "lpfc_mem.h" +#include "lpfc_sli.h" + +struct lpfc_hba; + + +struct lpfc_target { + struct lpfc_nodelist *pnode; /* Pointer to the node structure. */ + uint16_t scsi_id; + uint32_t qcmdcnt; + uint32_t iodonecnt; + uint32_t errorcnt; + uint32_t slavecnt; +#if defined(FC_TRANS_VER1) || defined(FC_TRANS_265_BLKPATCH) + uint16_t blocked; +#endif +#ifdef FC_TRANS_VER1 + struct scsi_target *starget; /* Pointer to midlayer target + structure. */ +#endif +#if defined(FC_TRANS_265_BLKPATCH) + struct timer_list dev_loss_timer; +#endif +}; + +struct lpfc_scsi_buf { + struct scsi_cmnd *pCmd; + struct lpfc_hba *scsi_hba; + struct lpfc_target *target; + + uint32_t timeout; + + uint16_t status; /* From IOCB Word 7- ulpStatus */ + uint32_t result; /* From IOCB Word 4. */ + + uint32_t seg_cnt; /* Number of scatter-gather segments returned by + * dma_map_sg. The driver needs this for calls + * to dma_unmap_sg. */ + dma_addr_t nonsg_phys; /* Non scatter-gather physical address. */ + + /* dma_ext has both virt, phys to dma-able buffer + * which contains fcp_cmd, fcp_rsp and scatter gather list fro upto + * 68 (LPFC_SCSI_BPL_SIZE) BDE entries, + * xfer length, cdb, data direction.... + */ + struct lpfc_dmabuf dma_ext; + struct fcp_cmnd *fcp_cmnd; + struct fcp_rsp *fcp_rsp; + struct ulp_bde64 *fcp_bpl; + + /* cur_iocbq has phys of the dma-able buffer. + * Iotag is in here + */ + struct lpfc_iocbq cur_iocbq; +}; + +#define LPFC_SCSI_INITIAL_BPL_SIZE 4 /* Number of scsi buf BDEs in fcp_bpl */ + +#define LPFC_SCSI_DMA_EXT_SIZE 264 +#define LPFC_BPL_SIZE 1024 + +#define MDAC_DIRECT_CMD 0x22 + +#endif /* _H_LPFC_SCSI */ diff --git a/drivers/scsi/lpfc/lpfc_scsiport.c b/drivers/scsi/lpfc/lpfc_scsiport.c new file mode 100644 index 000000000..b6b146a98 --- /dev/null +++ b/drivers/scsi/lpfc/lpfc_scsiport.c @@ -0,0 +1,1300 @@ +/******************************************************************* + * This file is part of the Emulex Linux Device Driver for * + * Enterprise Fibre Channel Host Bus Adapters. * + * Refer to the README file included with this package for * + * driver version and adapter support. * + * Copyright (C) 2004 Emulex Corporation. * + * www.emulex.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, a copy of which * + * can be found in the file COPYING included with this package. * + *******************************************************************/ + +/* + * $Id: lpfc_scsiport.c 1.208 2004/12/03 11:27:34EST sf_support Exp $ + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "lpfc_hw.h" +#include "lpfc_sli.h" +#include "lpfc_mem.h" +#include "lpfc_disc.h" +#include "lpfc_scsi.h" +#include "lpfc.h" +#include "lpfc_logmsg.h" +#include "lpfc_fcp.h" +#include "lpfc_crtn.h" + +/* This routine allocates a scsi buffer, which contains all the necessary + * information needed to initiate a SCSI I/O. The non-DMAable region of + * the buffer contains the area to build the IOCB. The DMAable region contains + * the 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 and the BPL BDE is setup in the IOCB. + */ +struct lpfc_scsi_buf * +lpfc_get_scsi_buf(struct lpfc_hba * phba, int gfp_flags) +{ + struct lpfc_scsi_buf *psb; + struct ulp_bde64 *bpl; + IOCB_t *cmd; + uint8_t *ptr; + dma_addr_t pdma_phys; + + psb = mempool_alloc(phba->scsibuf_mem_pool, gfp_flags); + if (!psb) + return NULL; + + memset(psb, 0, sizeof (struct lpfc_scsi_buf)); + + /* Get a SCSI DMA extention for an I/O */ + /* + * The DMA buffer for struct fcp_cmnd, struct fcp_rsp and BPL use + * lpfc_scsi_dma_ext_pool with size LPFC_SCSI_DMA_EXT_SIZE + * + * + * The size of struct fcp_cmnd = 32 bytes. + * The size of struct fcp_rsp = 160 bytes. + * The size of struct ulp_bde64 = 12 bytes and driver can only + * support LPFC_SCSI_INITIAL_BPL_SIZE (3) S/G segments for scsi data. + * One struct ulp_bde64 is used for each of the struct fcp_cmnd and + * struct fcp_rsp + * + * Total usage for each I/O use 32 + 160 + (2 * 12) + + * (4 * 12) = 264 bytes. + */ + + INIT_LIST_HEAD(&psb->dma_ext.list); + + psb->dma_ext.virt = pci_pool_alloc(phba->lpfc_scsi_dma_ext_pool, + GFP_ATOMIC, &psb->dma_ext.phys); + if (!psb->dma_ext.virt) { + mempool_free(psb, phba->scsibuf_mem_pool); + return NULL; + } + + /* Save virtual ptrs to FCP Command, Response, and BPL */ + ptr = (uint8_t *) psb->dma_ext.virt; + + memset(ptr, 0, LPFC_SCSI_DMA_EXT_SIZE); + psb->fcp_cmnd = (struct fcp_cmnd *) ptr; + ptr += sizeof (struct fcp_cmnd); + psb->fcp_rsp = (struct fcp_rsp *) ptr; + ptr += (sizeof (struct fcp_rsp)); + psb->fcp_bpl = (struct ulp_bde64 *) ptr; + psb->scsi_hba = phba; + + /* Since this is for a FCP cmd, the first 2 BDEs in the BPL are always + * the FCP CMND and FCP RSP, so lets just set it up right here. + */ + bpl = psb->fcp_bpl; + /* ptr points to physical address of FCP CMD */ + pdma_phys = psb->dma_ext.phys; + bpl->addrHigh = le32_to_cpu(putPaddrHigh(pdma_phys)); + bpl->addrLow = le32_to_cpu(putPaddrLow(pdma_phys)); + bpl->tus.f.bdeSize = sizeof (struct fcp_cmnd); + bpl->tus.f.bdeFlags = BUFF_USE_CMND; + bpl->tus.w = le32_to_cpu(bpl->tus.w); + bpl++; + + /* Setup FCP RSP */ + pdma_phys += sizeof (struct fcp_cmnd); + bpl->addrHigh = le32_to_cpu(putPaddrHigh(pdma_phys)); + bpl->addrLow = le32_to_cpu(putPaddrLow(pdma_phys)); + bpl->tus.f.bdeSize = sizeof (struct fcp_rsp); + bpl->tus.f.bdeFlags = (BUFF_USE_CMND | BUFF_USE_RCV); + bpl->tus.w = le32_to_cpu(bpl->tus.w); + bpl++; + + /* Since the IOCB for the FCP I/O is built into the struct + * lpfc_scsi_buf, lets setup what we can right here. + */ + pdma_phys += (sizeof (struct fcp_rsp)); + cmd = &psb->cur_iocbq.iocb; + cmd->un.fcpi64.bdl.ulpIoTag32 = 0; + cmd->un.fcpi64.bdl.addrHigh = putPaddrHigh(pdma_phys); + cmd->un.fcpi64.bdl.addrLow = putPaddrLow(pdma_phys); + cmd->un.fcpi64.bdl.bdeSize = (2 * sizeof (struct ulp_bde64)); + cmd->un.fcpi64.bdl.bdeFlags = BUFF_TYPE_BDL; + cmd->ulpBdeCount = 1; + cmd->ulpClass = CLASS3; + + return (psb); +} + +static void +lpfc_free_scsi_buf(struct lpfc_scsi_buf * psb) +{ + struct lpfc_hba *phba = psb->scsi_hba; + struct lpfc_dmabuf *pbpl, *next_bpl; + + /* + * There are only two special cases to consider. (1) the scsi command + * requested scatter-gather usage or (2) the scsi command allocated + * a request buffer, but did not request use_sg. There is a third + * case, but it does not require resource deallocation. + */ + + if ((psb->seg_cnt > 0) && (psb->pCmd->use_sg)) { + /* + * Since the segment count is nonzero, the scsi command + * requested scatter-gather usage and the driver allocated + * addition memory buffers to chain BPLs. Traverse this list + * and release those resource before freeing the parent + * structure. + */ + dma_unmap_sg(&phba->pcidev->dev, psb->pCmd->request_buffer, + psb->seg_cnt, psb->pCmd->sc_data_direction); + + list_for_each_entry_safe(pbpl, next_bpl, + &psb->dma_ext.list, list) { + lpfc_mbuf_free(phba, pbpl->virt, pbpl->phys); + list_del(&pbpl->list); + kfree(pbpl); + } + } else { + if ((psb->nonsg_phys) && (psb->pCmd->request_bufflen)) { + /* + * Since either the segment count or the use_sg + * value is zero, the scsi command did not request + * scatter-gather usage and no additional buffers were + * required. Just unmap the dma single resource. + */ + dma_unmap_single(&phba->pcidev->dev, psb->nonsg_phys, + psb->pCmd->request_bufflen, + psb->pCmd->sc_data_direction); + } + } + + /* + * Release the pci pool resource and clean up the scsi buffer. Neither + * are required now that the IO has completed. + */ + pci_pool_free(phba->lpfc_scsi_dma_ext_pool, psb->dma_ext.virt, + psb->dma_ext.phys); + mempool_free(psb, phba->scsibuf_mem_pool); +} + +static int +lpfc_os_prep_io(struct lpfc_hba * phba, struct lpfc_scsi_buf * lpfc_cmd) +{ + struct fcp_cmnd *fcp_cmnd; + struct ulp_bde64 *topbpl = NULL; + struct ulp_bde64 *bpl; + struct lpfc_dmabuf *bmp; + struct lpfc_dmabuf *head_bmp; + IOCB_t *cmd; + struct scsi_cmnd *cmnd; + struct scatterlist *sgel = NULL; + struct scatterlist *sgel_begin = NULL; + dma_addr_t physaddr; + uint32_t i; + uint32_t num_bmps = 1, num_bde = 0, max_bde; + uint16_t use_sg; + int datadir; + int dma_error; + + bpl = lpfc_cmd->fcp_bpl; + fcp_cmnd = lpfc_cmd->fcp_cmnd; + + bpl += 2; /* Bump past FCP CMND and FCP RSP */ + max_bde = LPFC_SCSI_INITIAL_BPL_SIZE - 1; + + cmnd = lpfc_cmd->pCmd; + cmd = &lpfc_cmd->cur_iocbq.iocb; + + /* These are needed if we chain BPLs */ + head_bmp = &(lpfc_cmd->dma_ext); + use_sg = cmnd->use_sg; + + /* + * Fill in the FCP CMND + */ + memcpy(&fcp_cmnd->fcpCdb[0], cmnd->cmnd, 16); + + if (cmnd->device->tagged_supported) { + switch (cmnd->tag) { + case HEAD_OF_QUEUE_TAG: + fcp_cmnd->fcpCntl1 = HEAD_OF_Q; + break; + case ORDERED_QUEUE_TAG: + fcp_cmnd->fcpCntl1 = ORDERED_Q; + break; + default: + fcp_cmnd->fcpCntl1 = SIMPLE_Q; + break; + } + } else { + fcp_cmnd->fcpCntl1 = 0; + } + + datadir = cmnd->sc_data_direction; + + if (use_sg) { + /* + * Get a local pointer to the scatter-gather list. The + * scatter-gather list head must be preserved since + * sgel is incremented in the loop. The driver must store + * the segment count returned from pci_map_sg for calls to + * pci_unmap_sg later on because the use_sg field in the + * scsi_cmd is a count of physical memory pages, whereas the + * seg_cnt is a count of dma-mappings used by the MMIO to + * map the use_sg pages. They are not the same in most + * cases for those architectures that implement an MMIO. + */ + sgel = (struct scatterlist *)cmnd->request_buffer; + sgel_begin = sgel; + lpfc_cmd->seg_cnt = dma_map_sg(&phba->pcidev->dev, sgel, + use_sg, datadir); + + /* return error if we cannot map sg list */ + if (lpfc_cmd->seg_cnt == 0) + return 1; + + /* scatter-gather list case */ + for (i = 0; i < lpfc_cmd->seg_cnt; i++) { + /* Check to see if current BPL is full of BDEs */ + /* If this is last BDE and there is one left in */ + /* current BPL, use it. */ + if (num_bde == max_bde) { + bmp = kmalloc(sizeof (struct lpfc_dmabuf), + GFP_ATOMIC); + if (bmp == 0) { + goto error_out; + } + memset(bmp, 0, sizeof (struct lpfc_dmabuf)); + bmp->virt = + lpfc_mbuf_alloc(phba, 0, &bmp->phys); + if (!bmp->virt) { + kfree(bmp); + goto error_out; + } + max_bde = ((1024 / sizeof(struct ulp_bde64))-3); + /* Fill in continuation entry to next bpl */ + bpl->addrHigh = + le32_to_cpu(putPaddrHigh(bmp->phys)); + bpl->addrLow = + le32_to_cpu(putPaddrLow(bmp->phys)); + bpl->tus.f.bdeFlags = BPL64_SIZE_WORD; + num_bde++; + if (num_bmps == 1) { + cmd->un.fcpi64.bdl.bdeSize += (num_bde * + sizeof (struct ulp_bde64)); + } else { + topbpl->tus.f.bdeSize = (num_bde * + sizeof (struct ulp_bde64)); + topbpl->tus.w = + le32_to_cpu(topbpl->tus.w); + } + topbpl = bpl; + bpl = (struct ulp_bde64 *) bmp->virt; + list_add(&bmp->list, &head_bmp->list); + num_bde = 0; + num_bmps++; + } + + physaddr = sg_dma_address(sgel); + + bpl->addrLow = le32_to_cpu(putPaddrLow(physaddr)); + bpl->addrHigh = le32_to_cpu(putPaddrHigh(physaddr)); + bpl->tus.f.bdeSize = sg_dma_len(sgel); + if (datadir == DMA_TO_DEVICE) + bpl->tus.f.bdeFlags = 0; + else + bpl->tus.f.bdeFlags = BUFF_USE_RCV; + bpl->tus.w = le32_to_cpu(bpl->tus.w); + bpl++; + sgel++; + num_bde++; + } /* end for loop */ + + if (datadir == DMA_TO_DEVICE) { + cmd->ulpCommand = CMD_FCP_IWRITE64_CR; + fcp_cmnd->fcpCntl3 = WRITE_DATA; + + phba->fc4OutputRequests++; + } else { + cmd->ulpCommand = CMD_FCP_IREAD64_CR; + cmd->ulpPU = PARM_READ_CHECK; + cmd->un.fcpi.fcpi_parm = cmnd->request_bufflen; + fcp_cmnd->fcpCntl3 = READ_DATA; + + phba->fc4InputRequests++; + } + } else if (cmnd->request_buffer && cmnd->request_bufflen) { + physaddr = dma_map_single(&phba->pcidev->dev, + cmnd->request_buffer, + cmnd->request_bufflen, + datadir); + dma_error = dma_mapping_error(physaddr); + if (dma_error){ + lpfc_printf_log(phba, KERN_ERR, LOG_FCP, + "%d:0718 Unable to dma_map_single " + "request_buffer: x%x\n", + phba->brd_no, dma_error); + return 1; + } + + /* no scatter-gather list case */ + lpfc_cmd->nonsg_phys = physaddr; + bpl->addrLow = le32_to_cpu(putPaddrLow(physaddr)); + bpl->addrHigh = le32_to_cpu(putPaddrHigh(physaddr)); + bpl->tus.f.bdeSize = cmnd->request_bufflen; + if (datadir == DMA_TO_DEVICE) { + cmd->ulpCommand = CMD_FCP_IWRITE64_CR; + fcp_cmnd->fcpCntl3 = WRITE_DATA; + bpl->tus.f.bdeFlags = 0; + + phba->fc4OutputRequests++; + } else { + cmd->ulpCommand = CMD_FCP_IREAD64_CR; + cmd->ulpPU = PARM_READ_CHECK; + cmd->un.fcpi.fcpi_parm = cmnd->request_bufflen; + fcp_cmnd->fcpCntl3 = READ_DATA; + bpl->tus.f.bdeFlags = BUFF_USE_RCV; + + phba->fc4InputRequests++; + } + bpl->tus.w = le32_to_cpu(bpl->tus.w); + num_bde = 1; + bpl++; + } else { + cmd->ulpCommand = CMD_FCP_ICMND64_CR; + cmd->un.fcpi.fcpi_parm = 0; + fcp_cmnd->fcpCntl3 = 0; + + phba->fc4ControlRequests++; + } + + bpl->addrHigh = 0; + bpl->addrLow = 0; + bpl->tus.w = 0; + if (num_bmps == 1) { + cmd->un.fcpi64.bdl.bdeSize += + (num_bde * sizeof (struct ulp_bde64)); + } else { + topbpl->tus.f.bdeSize = (num_bde * sizeof (struct ulp_bde64)); + topbpl->tus.w = le32_to_cpu(topbpl->tus.w); + } + cmd->ulpBdeCount = 1; + cmd->ulpLe = 1; /* Set the LE bit in the iocb */ + + /* set the Data Length field in the FCP CMND accordingly */ + fcp_cmnd->fcpDl = be32_to_cpu(cmnd->request_bufflen); + + return 0; + +error_out: + /* + * Allocation of a chained BPL failed, unmap the sg list and return + * error. This will ultimately cause lpfc_free_scsi_buf to be called + * which will handle the rest of the cleanup. Set seg_cnt back to zero + * to avoid double unmaps of the sg resources. + */ + dma_unmap_sg(&phba->pcidev->dev, sgel_begin, lpfc_cmd->seg_cnt, + datadir); + lpfc_cmd->seg_cnt = 0; + return 1; +} + +static void +lpfc_handle_fcp_err(struct lpfc_scsi_buf *lpfc_cmd) +{ + struct scsi_cmnd *cmnd = lpfc_cmd->pCmd; + struct fcp_cmnd *fcpcmd = lpfc_cmd->fcp_cmnd; + struct fcp_rsp *fcprsp = lpfc_cmd->fcp_rsp; + struct lpfc_hba *phba = lpfc_cmd->scsi_hba; + uint32_t fcpi_parm = lpfc_cmd->cur_iocbq.iocb.un.fcpi.fcpi_parm; + uint32_t resp_info = fcprsp->rspStatus2; + uint32_t scsi_status = fcprsp->rspStatus3; + uint32_t host_status = DID_OK; + uint32_t rsplen = 0; + + /* + * If this is a task management command, there is no + * scsi packet associated with this lpfc_cmd. The driver + * consumes it. + */ + if (fcpcmd->fcpCntl2) { + scsi_status = 0; + goto out; + } + + lpfc_printf_log(phba, KERN_WARNING, LOG_FCP, + "%d:0730 FCP command failed: RSP " + "Data: x%x x%x x%x x%x x%x x%x\n", + phba->brd_no, resp_info, scsi_status, + be32_to_cpu(fcprsp->rspResId), + be32_to_cpu(fcprsp->rspSnsLen), + be32_to_cpu(fcprsp->rspRspLen), + fcprsp->rspInfo3); + + if (resp_info & RSP_LEN_VALID) { + rsplen = be32_to_cpu(fcprsp->rspRspLen); + if ((rsplen != 0 && rsplen != 4 && rsplen != 8) || + (fcprsp->rspInfo3 != RSP_NO_FAILURE)) { + host_status = DID_ERROR; + goto out; + } + } + + if ((resp_info & SNS_LEN_VALID) && fcprsp->rspSnsLen) { + uint32_t snslen = be32_to_cpu(fcprsp->rspSnsLen); + if (snslen > SCSI_SENSE_BUFFERSIZE) + snslen = SCSI_SENSE_BUFFERSIZE; + + memcpy(cmnd->sense_buffer, &fcprsp->rspInfo0 + rsplen, snslen); + } + + cmnd->resid = 0; + if (resp_info & RESID_UNDER) { + cmnd->resid = be32_to_cpu(fcprsp->rspResId); + + lpfc_printf_log(phba, KERN_INFO, LOG_FCP, + "%d:0716 FCP Read Underrun, expected %d, " + "residual %d Data: x%x x%x x%x\n", phba->brd_no, + be32_to_cpu(fcpcmd->fcpDl), cmnd->resid, + fcpi_parm, cmnd->cmnd[0], cmnd->underflow); + + /* + * 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 (!(resp_info & SNS_LEN_VALID) && + (scsi_status == SAM_STAT_GOOD) && + (cmnd->request_bufflen - cmnd->resid) < cmnd->underflow) { + lpfc_printf_log(phba, KERN_INFO, LOG_FCP, + "%d:0717 FCP command x%x residual " + "underrun converted to error " + "Data: x%x x%x x%x\n", phba->brd_no, + cmnd->cmnd[0], cmnd->request_bufflen, + cmnd->resid, cmnd->underflow); + + host_status = DID_ERROR; + } + } else if (resp_info & RESID_OVER) { + lpfc_printf_log(phba, KERN_WARNING, LOG_FCP, + "%d:0720 FCP command x%x residual " + "overrun error. Data: x%x x%x \n", + phba->brd_no, cmnd->cmnd[0], + cmnd->request_bufflen, cmnd->resid); + host_status = DID_ERROR; + + /* + * Check SLI validation that all the transfer was actually done + * (fcpi_parm should be zero). Apply check only to reads. + */ + } else if ((scsi_status == SAM_STAT_GOOD) && fcpi_parm && + (cmnd->sc_data_direction == DMA_FROM_DEVICE)) { + lpfc_printf_log(phba, KERN_WARNING, LOG_FCP, + "%d:0734 FCP Read Check Error Data: " + "x%x x%x x%x x%x\n", phba->brd_no, + be32_to_cpu(fcpcmd->fcpDl), + be32_to_cpu(fcprsp->rspResId), + fcpi_parm, cmnd->cmnd[0]); + host_status = DID_ERROR; + cmnd->resid = cmnd->request_bufflen; + } + + out: + cmnd->result = ScsiResult(host_status, scsi_status); +} + +static void +lpfc_scsi_cmd_iocb_cmpl(struct lpfc_hba *phba, struct lpfc_iocbq *pIocbIn, + struct lpfc_iocbq *pIocbOut) +{ + struct lpfc_scsi_buf *lpfc_cmd = + (struct lpfc_scsi_buf *) pIocbIn->context1; + struct lpfc_target *target = lpfc_cmd->target; + struct scsi_cmnd *cmd = lpfc_cmd->pCmd; + + lpfc_cmd->result = pIocbOut->iocb.un.ulpWord[4]; + lpfc_cmd->status = pIocbOut->iocb.ulpStatus; + + pci_dma_sync_single_for_cpu(phba->pcidev, lpfc_cmd->dma_ext.phys, + LPFC_BPL_SIZE, PCI_DMA_FROMDEVICE); + + target->iodonecnt++; + + if (lpfc_cmd->status) { + target->errorcnt++; + + if (lpfc_cmd->status == IOSTAT_LOCAL_REJECT && + (lpfc_cmd->result & IOERR_DRVR_MASK)) + lpfc_cmd->status = IOSTAT_DRIVER_REJECT; + else if (lpfc_cmd->status >= IOSTAT_CNT) + lpfc_cmd->status = IOSTAT_DEFAULT; + + lpfc_printf_log(phba, KERN_WARNING, LOG_FCP, + "%d:0729 FCP cmd x%x failed <%d/%d> status: " + "x%x result: x%x Data: x%x x%x\n", + phba->brd_no, cmd->cmnd[0], cmd->device->id, + cmd->device->lun, lpfc_cmd->status, + lpfc_cmd->result, pIocbOut->iocb.ulpContext, + lpfc_cmd->cur_iocbq.iocb.ulpIoTag); + + switch (lpfc_cmd->status) { + case IOSTAT_FCP_RSP_ERROR: + /* Call FCP RSP handler to determine result */ + lpfc_handle_fcp_err(lpfc_cmd); + break; + case IOSTAT_NPORT_BSY: + case IOSTAT_FABRIC_BSY: + cmd->result = ScsiResult(DID_BUS_BUSY, 0); + break; + default: + cmd->result = ScsiResult(DID_ERROR, 0); + break; + } + + if (target->pnode) { + if(target->pnode->nlp_state != NLP_STE_MAPPED_NODE) + cmd->result = ScsiResult(DID_BUS_BUSY, + SAM_STAT_BUSY); + } + else { + cmd->result = ScsiResult(DID_NO_CONNECT, 0); + } + } else { + cmd->result = ScsiResult(DID_OK, 0); + } + + if (cmd->result || lpfc_cmd->fcp_rsp->rspSnsLen) { + uint32_t *lp = (uint32_t *)cmd->sense_buffer; + + lpfc_printf_log(phba, KERN_INFO, LOG_FCP, + "%d:0710 Iodone <%d/%d> cmd %p, error x%x " + "SNS x%x x%x Data: x%x x%x\n", + phba->brd_no, cmd->device->id, + cmd->device->lun, cmd, cmd->result, + *lp, *(lp + 3), cmd->retries, cmd->resid); + } + + lpfc_free_scsi_buf(lpfc_cmd); + cmd->host_scribble = NULL; + cmd->scsi_done(cmd); +} + +static int +lpfc_scsi_prep_task_mgmt_cmd(struct lpfc_hba *phba, + struct lpfc_scsi_buf *lpfc_cmd, + uint8_t task_mgmt_cmd) +{ + + struct lpfc_sli *psli; + struct lpfc_iocbq *piocbq; + IOCB_t *piocb; + struct fcp_cmnd *fcp_cmnd; + struct scsi_device *scsi_dev = lpfc_cmd->pCmd->device; + struct lpfc_target *target = scsi_dev->hostdata; + struct lpfc_nodelist *ndlp = target->pnode; + + if ((ndlp == 0) || (ndlp->nlp_state != NLP_STE_MAPPED_NODE)) { + return 0; + } + + /* allocate an iocb command */ + psli = &phba->sli; + piocbq = &(lpfc_cmd->cur_iocbq); + piocb = &piocbq->iocb; + + + fcp_cmnd = lpfc_cmd->fcp_cmnd; + putLunHigh(fcp_cmnd->fcpLunMsl, 0); + putLunLow(fcp_cmnd->fcpLunLsl, lpfc_cmd->pCmd->device->lun) + fcp_cmnd->fcpCntl2 = task_mgmt_cmd; + + piocb->ulpIoTag = + lpfc_sli_next_iotag(phba, &phba->sli.ring[psli->fcp_ring]); + piocb->ulpCommand = CMD_FCP_ICMND64_CR; + + piocb->ulpContext = ndlp->nlp_rpi; + if (ndlp->nlp_fcp_info & NLP_FCP_2_DEVICE) { + piocb->ulpFCP2Rcvy = 1; + } + piocb->ulpClass = (ndlp->nlp_fcp_info & 0x0f); + + /* ulpTimeout is only one byte */ + if (lpfc_cmd->timeout > 0xff) { + /* + * Do not timeout the command at the firmware level. + * The driver will provide the timeout mechanism. + */ + piocb->ulpTimeout = 0; + } else { + piocb->ulpTimeout = lpfc_cmd->timeout; + } + + lpfc_cmd->target = target; + + switch (task_mgmt_cmd) { + case FCP_LUN_RESET: + /* Issue LUN Reset to TGT LUN */ + lpfc_printf_log(phba, + KERN_INFO, + LOG_FCP, + "%d:0703 Issue LUN Reset to TGT %d LUN %d " + "Data: x%x x%x\n", + phba->brd_no, + scsi_dev->id, scsi_dev->lun, + ndlp->nlp_rpi, ndlp->nlp_flag); + + break; + case FCP_ABORT_TASK_SET: + /* Issue Abort Task Set to TGT LUN */ + lpfc_printf_log(phba, + KERN_INFO, + LOG_FCP, + "%d:0701 Issue Abort Task Set to TGT %d LUN %d " + "Data: x%x x%x\n", + phba->brd_no, + scsi_dev->id, scsi_dev->lun, + ndlp->nlp_rpi, ndlp->nlp_flag); + + break; + case FCP_TARGET_RESET: + /* Issue Target Reset to TGT */ + lpfc_printf_log(phba, + KERN_INFO, + LOG_FCP, + "%d:0702 Issue Target Reset to TGT %d " + "Data: x%x x%x\n", + phba->brd_no, + scsi_dev->id, ndlp->nlp_rpi, + ndlp->nlp_flag); + break; + } + + return (1); +} + +static int +lpfc_scsi_tgt_reset(struct lpfc_scsi_buf * lpfc_cmd, struct lpfc_hba * phba) +{ + struct lpfc_iocbq *piocbq, *piocbqrsp; + struct lpfc_sli *psli; + int ret; + + ret = lpfc_scsi_prep_task_mgmt_cmd(phba, lpfc_cmd, FCP_TARGET_RESET); + if (!ret) + return FAILED; + + lpfc_cmd->scsi_hba = phba; + psli = &phba->sli; + piocbq = &lpfc_cmd->cur_iocbq; + piocbqrsp = mempool_alloc(phba->iocb_mem_pool, + GFP_ATOMIC); + if (!piocbqrsp) + return FAILED; + + /* First flush all outstanding commands on the txq for the target */ + lpfc_sli_abort_iocb_tgt(phba, &phba->sli.ring[phba->sli.fcp_ring], + lpfc_cmd->pCmd->device->id, LPFC_ABORT_TXQ); + + memset(piocbqrsp, 0, sizeof (struct lpfc_iocbq)); + + piocbq->iocb_flag |= LPFC_IO_POLL; + + ret = lpfc_sli_issue_iocb_wait_high_priority(phba, + &phba->sli.ring[psli->fcp_ring], + piocbq, SLI_IOCB_HIGH_PRIORITY, + piocbqrsp, + lpfc_cmd->timeout); + if (ret != IOCB_SUCCESS) { + lpfc_cmd->status = IOSTAT_DRIVER_REJECT; + } else { + lpfc_cmd->result = piocbqrsp->iocb.un.ulpWord[4]; + lpfc_cmd->status = piocbqrsp->iocb.ulpStatus; + if (lpfc_cmd->status == IOSTAT_LOCAL_REJECT && + (lpfc_cmd->result & IOERR_DRVR_MASK)) + lpfc_cmd->status = IOSTAT_DRIVER_REJECT; + } + + /* At this point in time, target reset completion, all outstanding + * txcmplq I/Os should have been aborted by the target. + * Unfortunately, all targets do not abide by this so we need + * to help it out a bit. + */ + lpfc_sli_abort_iocb_tgt(phba, &phba->sli.ring[phba->sli.fcp_ring], + lpfc_cmd->pCmd->device->id, LPFC_ABORT_ALLQ); + + /* Done with piocbqrsp, return to free list. */ + mempool_free(piocbqrsp, phba->iocb_mem_pool); + return ret; +} + + +int +lpfc_reset_bus_handler(struct scsi_cmnd *cmnd) +{ + struct Scsi_Host *shost = cmnd->device->host; + struct lpfc_hba *phba = (struct lpfc_hba *)shost->hostdata[0]; + struct lpfc_target *target; + int ret = FAILED, i, err_count = 0; + int cnt, loopcnt; + struct scsi_cmnd *lnx_cmd; + struct scsi_device *scsi_dev; + struct lpfc_scsi_buf * lpfc_cmd; + + lpfc_cmd = lpfc_get_scsi_buf(phba, GFP_ATOMIC); + if (!lpfc_cmd) + goto out; + + lnx_cmd = kmalloc(sizeof(struct scsi_cmnd), GFP_ATOMIC); + if (!lnx_cmd) + goto out_free_scsi_buf; + + scsi_dev = kmalloc(sizeof(struct scsi_device), GFP_ATOMIC); + if (!scsi_dev) + goto out_free_lnx_cmd; + + /* The lpfc_cmd storage is reused. Set all loop invariants. */ + lpfc_cmd->timeout = cmnd->timeout; + if (!cmnd->timeout) + lpfc_cmd->timeout = LPFC_DRVR_TIMEOUT; + + lpfc_cmd->pCmd = lnx_cmd; + lpfc_cmd->scsi_hba = phba; + lpfc_cmd->pCmd->device = scsi_dev; + lpfc_cmd->pCmd->device->channel = 0; + lpfc_cmd->pCmd->device->lun = 0; + + /* + * Since the driver manages a single bus device, reset all + * targets known to the driver. Should any target reset + * fail, this routine returns failure to the midlayer. + */ + for (i = 0; i < MAX_FCP_TARGET; i++) { + target = phba->device_queue_hash[i]; + if (!target) + continue; + + lpfc_cmd->pCmd->device->id = i; + lpfc_cmd->pCmd->device->hostdata = target; + ret = lpfc_scsi_tgt_reset(lpfc_cmd, phba); + if (ret != IOCB_SUCCESS) { + lpfc_printf_log(phba, KERN_INFO, LOG_FCP, + "%d:0713 Bus Reset on target %d failed\n", + phba->brd_no, i); + err_count++; + } + } + + loopcnt = 0; + while((cnt = lpfc_sli_sum_iocb_host(phba, + &phba->sli.ring[phba->sli.fcp_ring]))) { + spin_unlock_irq(phba->host->host_lock); +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,6) + mdelay(50); +#else + msleep(50); +#endif + spin_lock_irq(phba->host->host_lock); + + /* 50ms * 100 = 5 sec + * wait upto 5 seconds for all I/Os for this HBA to cmpl + */ + if(++loopcnt >= 100) + break; + } + + if(cnt) { + /* flush all outstanding commands on the host */ + i = lpfc_sli_abort_iocb_host(phba, + &phba->sli.ring[phba->sli.fcp_ring], + LPFC_ABORT_ALLQ); + + lpfc_printf_log(phba, KERN_INFO, LOG_FCP, + "%d:0715 Bus Reset I/O flush failure: cnt x%x left x%x\n", + phba->brd_no, cnt, i); + } + if (!err_count) + ret = SUCCESS; + +out_free_lnx_cmd: + kfree(lnx_cmd); +out_free_scsi_buf: + lpfc_free_scsi_buf(lpfc_cmd); + lpfc_printf_log(phba, + KERN_ERR, + LOG_FCP, + "%d:0714 SCSI layer issued Bus Reset Data: x%x\n", + phba->brd_no, ret); +out: + return ret; +} + + +int +lpfc_queuecommand(struct scsi_cmnd *cmnd, void (*done) (struct scsi_cmnd *)) +{ + struct lpfc_hba *phba = + (struct lpfc_hba *) cmnd->device->host->hostdata[0]; + struct lpfc_sli *psli = &phba->sli; + struct lpfc_target *targetp = cmnd->device->hostdata; + struct lpfc_nodelist *ndlp; + struct lpfc_iocbq *piocbq; + struct lpfc_scsi_buf *lpfc_cmd; + IOCB_t *piocb; + int err = 0; + uint16_t nlp_state; + + targetp->qcmdcnt++; + + /* + * The target pointer is guaranteed not to be NULL because the driver + * only clears the device->hostdata field in lpfc_slave_destroy. This + * approach guarantees no further IO calls on this target. + */ + ndlp = targetp->pnode; + if (!ndlp) { + if(phba->fc_flag & FC_UNLOADING) { + /* Driver is in process of unloading */ + cmnd->result = ScsiResult(DID_BAD_TARGET, 0); + } + else { + cmnd->result = ScsiResult(DID_NO_CONNECT, 0); + } + goto out_fail_command; + } + + nlp_state = ndlp->nlp_state; + + /* + * A Fibre Channel is present and functioning only when the node state + * is MAPPED. Any other state is a failure. + */ + if (nlp_state != NLP_STE_MAPPED_NODE) { + if ((nlp_state == NLP_STE_UNMAPPED_NODE) || + (nlp_state == NLP_STE_UNUSED_NODE)) { + cmnd->result = ScsiResult(DID_NO_CONNECT, 0); + goto out_fail_command; + } + /* + * The device is most likely recovered and the driver + * needs a bit more time to finish. Ask the midlayer + * to retry. + */ + goto out_host_busy; + } + + lpfc_cmd = lpfc_get_scsi_buf(phba, GFP_ATOMIC); + if (!lpfc_cmd) + goto out_host_busy; + + /* + * Store the midlayer's command structure for the completion phase + * and complete the command initialization. + */ + cmnd->scsi_done = done; + cmnd->host_scribble = (unsigned char *)lpfc_cmd; + + lpfc_cmd->target = targetp; + lpfc_cmd->timeout = 0; + lpfc_cmd->pCmd = cmnd; + putLunHigh(lpfc_cmd->fcp_cmnd->fcpLunMsl, lpfc_cmd->pCmd->device->lun); + putLunLow(lpfc_cmd->fcp_cmnd->fcpLunLsl, lpfc_cmd->pCmd->device->lun); + + err = lpfc_os_prep_io(phba, lpfc_cmd); + if (err) + goto out_host_busy_free_buf; + + piocbq = &(lpfc_cmd->cur_iocbq); + piocb = &piocbq->iocb; + piocb->ulpTimeout = lpfc_cmd->timeout; + piocbq->context1 = lpfc_cmd; + piocbq->iocb_cmpl = lpfc_scsi_cmd_iocb_cmpl; + + piocbq->iocb.ulpContext = ndlp->nlp_rpi; + if (ndlp->nlp_fcp_info & NLP_FCP_2_DEVICE) { + piocbq->iocb.ulpFCP2Rcvy = 1; + } + + piocbq->iocb.ulpClass = (ndlp->nlp_fcp_info & 0x0f); + + pci_dma_sync_single_for_device(phba->pcidev, lpfc_cmd->dma_ext.phys, + LPFC_SCSI_DMA_EXT_SIZE, PCI_DMA_TODEVICE); + + err = lpfc_sli_issue_iocb(phba, &phba->sli.ring[psli->fcp_ring], piocbq, + SLI_IOCB_RET_IOCB); + if (err) + goto out_host_busy_free_buf; + return 0; + + out_host_busy_free_buf: + lpfc_free_scsi_buf(lpfc_cmd); + out_host_busy: + targetp->iodonecnt++; + targetp->errorcnt++; + return SCSI_MLQUEUE_HOST_BUSY; + + out_fail_command: + targetp->iodonecnt++; + targetp->errorcnt++; + done(cmnd); + return 0; +} + +int +lpfc_reset_lun_handler(struct scsi_cmnd *cmnd) +{ + struct Scsi_Host *shost = cmnd->device->host; + struct lpfc_hba *phba = (struct lpfc_hba *)shost->hostdata[0]; + struct lpfc_sli *psli = &phba->sli; + struct lpfc_scsi_buf *lpfc_cmd; + struct lpfc_iocbq *piocbq, *piocbqrsp = NULL; + int ret = FAILED; + int cnt, loopcnt; + + lpfc_cmd = lpfc_get_scsi_buf(phba, GFP_ATOMIC); + if (!lpfc_cmd) + goto out; + + lpfc_cmd->pCmd = cmnd; + lpfc_cmd->timeout = 60; /* set command timeout to 60 seconds */ + lpfc_cmd->scsi_hba = phba; + + ret = lpfc_scsi_prep_task_mgmt_cmd(phba, lpfc_cmd, FCP_LUN_RESET); + if (!ret) + goto out_free_scsi_buf; + + piocbq = &lpfc_cmd->cur_iocbq; + + /* get a buffer for this IOCB command response */ + piocbqrsp = mempool_alloc(phba->iocb_mem_pool, GFP_ATOMIC); + if(!piocbqrsp) + goto out_free_scsi_buf; + + /* First flush all outstanding commands on the txq for the lun */ + lpfc_sli_abort_iocb_lun(phba, + &phba->sli.ring[phba->sli.fcp_ring], + cmnd->device->id, + cmnd->device->lun, LPFC_ABORT_TXQ); + + memset(piocbqrsp, 0, sizeof (struct lpfc_iocbq)); + + piocbq->iocb_flag |= LPFC_IO_POLL; + piocbq->iocb_cmpl = lpfc_sli_wake_iocb_high_priority; + + ret = lpfc_sli_issue_iocb_wait_high_priority(phba, + &phba->sli.ring[psli->fcp_ring], + piocbq, 0, + piocbqrsp, 60); + if (ret == IOCB_SUCCESS) + ret = SUCCESS; + + lpfc_cmd->result = piocbqrsp->iocb.un.ulpWord[4]; + lpfc_cmd->status = piocbqrsp->iocb.ulpStatus; + if (lpfc_cmd->status == IOSTAT_LOCAL_REJECT) + if (lpfc_cmd->result & IOERR_DRVR_MASK) + lpfc_cmd->status = IOSTAT_DRIVER_REJECT; + + /* At this point in time, lun reset completion, all outstanding + * txcmplq I/Os should have been aborted by the target. + * Unfortunately, all targets do not abide by this so we need + * to help it out a bit. + */ + lpfc_sli_abort_iocb_lun(phba, + &phba->sli.ring[phba->sli.fcp_ring], + cmnd->device->id, + cmnd->device->lun, LPFC_ABORT_ALLQ); + + loopcnt = 0; + while((cnt = lpfc_sli_sum_iocb_lun(phba, + &phba->sli.ring[phba->sli.fcp_ring], + cmnd->device->id, + cmnd->device->lun))) { + spin_unlock_irq(phba->host->host_lock); +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,6) + mdelay(50); +#else + msleep(50); +#endif + spin_lock_irq(phba->host->host_lock); + + /* 50ms * 100 = 5 sec + * wait upto 5 seconds for all I/Os for this lun to cmpl + */ + if(++loopcnt >= 100) + break; + } + + if(cnt) { + lpfc_printf_log(phba, KERN_INFO, LOG_FCP, + "%d:0719 LUN Reset I/O flush failure: cnt x%x\n", + phba->brd_no, cnt); + } + + mempool_free(piocbqrsp, phba->iocb_mem_pool); + +out_free_scsi_buf: + lpfc_printf_log(phba, KERN_ERR, LOG_FCP, + "%d:0713 SCSI layer issued LUN reset (%d, %d) " + "Data: x%x x%x x%x\n", + phba->brd_no, lpfc_cmd->pCmd->device->id, + lpfc_cmd->pCmd->device->lun, ret, lpfc_cmd->status, + lpfc_cmd->result); + lpfc_free_scsi_buf(lpfc_cmd); +out: + return ret; +} + + +int +lpfc_abort_handler(struct scsi_cmnd *cmnd) +{ + struct lpfc_hba *phba = + (struct lpfc_hba *)cmnd->device->host->hostdata[0]; + struct lpfc_sli_ring *pring = &phba->sli.ring[phba->sli.fcp_ring]; + struct lpfc_iocbq *iocb, *next_iocb, *abtsiocbp; + struct lpfc_scsi_buf *lpfc_cmd; + IOCB_t *cmd, *icmd; + unsigned long snum; + unsigned int id, lun; + int ret = IOCB_ERROR; + + /* Returns SUCESS if command aborted, else FAILED */ + + lpfc_cmd = (struct lpfc_scsi_buf *)cmnd->host_scribble; + if (!lpfc_cmd) + return(FAILED); + + /* save these now since lpfc_cmd can be freed */ + id = lpfc_cmd->pCmd->device->id; + lun = lpfc_cmd->pCmd->device->lun; + snum = lpfc_cmd->pCmd->serial_number; + + /* Search the txq first. */ + list_for_each_entry_safe(iocb, next_iocb, &pring->txq, list) { + cmd = &iocb->iocb; + if (iocb->context1 != lpfc_cmd) + continue; + + list_del_init(&iocb->list); + pring->txq_cnt--; + if (!iocb->iocb_cmpl) { + mempool_free(iocb, phba->iocb_mem_pool); + } + else { + cmd->ulpStatus = IOSTAT_LOCAL_REJECT; + cmd->un.ulpWord[4] = IOERR_SLI_ABORTED; + (iocb->iocb_cmpl)(phba, iocb, iocb); + } + ret = IOCB_SUCCESS; + goto out; + } + + abtsiocbp = mempool_alloc(phba->iocb_mem_pool, GFP_ATOMIC); + if (!abtsiocbp) + goto out; + memset(abtsiocbp, 0, sizeof (struct lpfc_iocbq)); + + /* + * The scsi command was not in the txq. Check the txcmplq and if it is + * found, send an abort to the FW. + */ + list_for_each_entry_safe(iocb, next_iocb, &pring->txcmplq, list) { + if (iocb->context1 != lpfc_cmd) + continue; + + iocb->iocb_cmpl = NULL; + cmd = &iocb->iocb; + icmd = &abtsiocbp->iocb; + icmd->un.acxri.abortType = ABORT_TYPE_ABTS; + icmd->un.acxri.abortContextTag = cmd->ulpContext; + icmd->un.acxri.abortIoTag = cmd->ulpIoTag; + + icmd->ulpLe = 1; + icmd->ulpClass = cmd->ulpClass; + icmd->ulpIoTag = lpfc_sli_next_iotag(phba, pring); + if (phba->hba_state >= LPFC_LINK_UP) + icmd->ulpCommand = CMD_ABORT_XRI_CN; + else + icmd->ulpCommand = CMD_CLOSE_XRI_CN; + + /* set up an iotag */ + icmd->ulpIoTag = lpfc_sli_next_iotag(phba, pring); + + if (lpfc_sli_issue_iocb(phba, pring, abtsiocbp, 0) == + IOCB_ERROR) { + mempool_free(abtsiocbp, phba->iocb_mem_pool); + break; + } + + /* + * The rsp ring completion will remove IOCB from txcmplq when + * abort is read by HBA. + */ + ret = IOCB_SUCCESS; + break; + } + + out: + lpfc_printf_log(phba, KERN_ERR, LOG_FCP, + "%d:0749 SCSI layer issued abort device " + "Data: x%x x%x x%x x%lx\n", + phba->brd_no, ret, id, lun, snum); + + return (ret == IOCB_SUCCESS ? SUCCESS : FAILED); +} + +#if defined(FC_TRANS_VER1) || defined(FC_TRANS_265_BLKPATCH) +void +lpfc_target_unblock(struct lpfc_hba *phba, struct lpfc_target *targetp) +{ +#if defined(FC_TRANS_VER1) + /* + * This code to be removed once block/unblock and the new + * dicovery state machine are fully debugged. + */ + if (!targetp || !targetp->starget) { +#else + if (!targetp) { +#endif + lpfc_printf_log(phba, KERN_ERR, LOG_DISCOVERY | LOG_FCP, + "%d:0262 Cannot unblock scsi target\n", phba->brd_no); + + return; + } + + /* Unblock IO to target scsi id to NPort */ + lpfc_printf_log(phba, KERN_INFO, LOG_DISCOVERY | LOG_FCP, + "%d:0258 Unblocking IO to Target scsi id x%x " + "NPort pointer x%p\n", + phba->brd_no, targetp->scsi_id, targetp->pnode); + + spin_unlock_irq(phba->host->host_lock); + +#if defined(FC_TRANS_VER1) + fc_target_unblock(targetp->starget); +#elif defined(FC_TRANS_265_BLKPATCH) + fc_target_unblock(phba->host, targetp->scsi_id, + &targetp->dev_loss_timer); +#endif + spin_lock_irq(phba->host->host_lock); + targetp->blocked--; +} + +void +lpfc_target_block(struct lpfc_hba *phba, struct lpfc_target *targetp) +{ +#if defined(FC_TRANS_VER1) + /* + * This code to be removed once block/unblock and the new + * dicovery state machine are fully debugged. + */ + if (!targetp || !targetp->starget) { +#else + if (!targetp) { +#endif + lpfc_printf_log(phba, KERN_ERR, LOG_DISCOVERY | LOG_FCP, + "%d:0263 Cannot block scsi target." + " target ptr x%p\n", + phba->brd_no, targetp); + return; + } + + /* Block all IO to target scsi id to NPort */ + lpfc_printf_log(phba, KERN_INFO, LOG_DISCOVERY | LOG_FCP, + "%d:0259 Blocking IO to Target scsi id x%x" + " NPort pointer x%p\n", + phba->brd_no, targetp->scsi_id, targetp->pnode); + + spin_unlock_irq(phba->host->host_lock); +#if defined(FC_TRANS_VER1) + fc_target_block(targetp->starget); +#elif defined(FC_TRANS_265_BLKPATCH) + fc_target_block(phba->host, targetp->scsi_id, &targetp->dev_loss_timer, + phba->cfg_nodev_tmo); +#endif + spin_lock_irq(phba->host->host_lock); + targetp->blocked++; +} +#endif + +#if defined(FC_TRANS_VER1) || defined(FC_TRANS_265_BLKPATCH) +int +lpfc_target_remove(struct lpfc_hba *phba, struct lpfc_target *targetp) +{ + struct scsi_device *sdev; + struct Scsi_Host *shost = phba->host; + + /* This is only called if scsi target (targetp->starget) is valid */ + lpfc_printf_log(phba, KERN_ERR, LOG_DISCOVERY | LOG_FCP, + "%d:0260 Remove Target scsi id x%x\n", + phba->brd_no, targetp->scsi_id); + + /* If this target is blocked, we must unblock it first */ + if (targetp->blocked) + lpfc_target_unblock(phba, targetp); + + /* Remove all associated devices for this target */ + if (phba->cfg_scsi_hotplug) { +top: + list_for_each_entry(sdev, &shost->__devices, siblings) { + if (sdev->channel == 0 + && sdev->id == targetp->scsi_id) { + spin_unlock_irq(shost->host_lock); + scsi_device_get(sdev); + scsi_remove_device(sdev); + scsi_device_put(sdev); + spin_lock_irq(shost->host_lock); + goto top; + } + } + } + + return 0; +} + +int +lpfc_target_add(struct lpfc_hba *phba, struct lpfc_target *targetp) +{ + struct Scsi_Host *shost; + + if(!phba->cfg_scsi_hotplug) + return 1; + + /* This is only called if scsi target (targetp->starget) is valid */ + + lpfc_printf_log(phba, KERN_ERR, LOG_DISCOVERY | LOG_FCP, + "%d:0261 Adding Target scsi id x%x\n", + phba->brd_no, targetp->scsi_id); + + shost = phba->host; + +#ifdef USE_SCAN_TARGET + lpfc_discq_post_event(phba, targetp, NULL, LPFC_EVT_SCAN); +#else + /* + * The driver discovered a new target. Call the midlayer and get this + * target's luns added into the device list. + * Since we are going to scan the entire host, kick off a timer to + * do this so we can possibly consolidate multiple target scans into + * one scsi host scan. + */ + mod_timer(&phba->fc_scantmo, jiffies + HZ); + phba->fc_flag |= FC_SCSI_SCAN_TMO; +#endif + return 0; +} +#endif diff --git a/drivers/scsi/lpfc/lpfc_sli.c b/drivers/scsi/lpfc/lpfc_sli.c new file mode 100644 index 000000000..e9068e721 --- /dev/null +++ b/drivers/scsi/lpfc/lpfc_sli.c @@ -0,0 +1,3349 @@ +/******************************************************************* + * This file is part of the Emulex Linux Device Driver for * + * Enterprise Fibre Channel Host Bus Adapters. * + * Refer to the README file included with this package for * + * driver version and adapter support. * + * Copyright (C) 2004 Emulex Corporation. * + * www.emulex.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, a copy of which * + * can be found in the file COPYING included with this package. * + *******************************************************************/ + +/* + * $Id: lpfc_sli.c 1.178 2004/11/23 16:57:11EST sf_support Exp $ + */ + +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "lpfc_sli.h" +#include "lpfc_disc.h" +#include "lpfc_scsi.h" +#include "lpfc.h" +#include "lpfc_crtn.h" +#include "lpfc_hw.h" +#include "lpfc_logmsg.h" +#include "lpfc_mem.h" +#include "lpfc_compat.h" + +static int lpfc_sli_reset_on_init = 1; + +/* + * Define macro to log: Mailbox command x%x cannot issue Data + * This allows multiple uses of lpfc_msgBlk0311 + * w/o perturbing log msg utility. +*/ +#define LOG_MBOX_CANNOT_ISSUE_DATA( phba, mb, psli, flag) \ + lpfc_printf_log(phba, \ + KERN_INFO, \ + LOG_MBOX | LOG_SLI, \ + "%d:0311 Mailbox command x%x cannot issue " \ + "Data: x%x x%x x%x\n", \ + phba->brd_no, \ + mb->mbxCommand, \ + phba->hba_state, \ + psli->sliinit.sli_flag, \ + flag); + + +/* This will save a huge switch to determine if the IOCB cmd + * is unsolicited or solicited. + */ +#define LPFC_UNKNOWN_IOCB 0 +#define LPFC_UNSOL_IOCB 1 +#define LPFC_SOL_IOCB 2 +#define LPFC_ABORT_IOCB 3 +static uint8_t lpfc_sli_iocb_cmd_type[CMD_MAX_IOCB_CMD] = { + LPFC_UNKNOWN_IOCB, /* 0x00 */ + LPFC_UNSOL_IOCB, /* CMD_RCV_SEQUENCE_CX 0x01 */ + LPFC_SOL_IOCB, /* CMD_XMIT_SEQUENCE_CR 0x02 */ + LPFC_SOL_IOCB, /* CMD_XMIT_SEQUENCE_CX 0x03 */ + LPFC_SOL_IOCB, /* CMD_XMIT_BCAST_CN 0x04 */ + LPFC_SOL_IOCB, /* CMD_XMIT_BCAST_CX 0x05 */ + LPFC_UNKNOWN_IOCB, /* CMD_QUE_RING_BUF_CN 0x06 */ + LPFC_UNKNOWN_IOCB, /* CMD_QUE_XRI_BUF_CX 0x07 */ + LPFC_UNKNOWN_IOCB, /* CMD_IOCB_CONTINUE_CN 0x08 */ + LPFC_UNKNOWN_IOCB, /* CMD_RET_XRI_BUF_CX 0x09 */ + LPFC_SOL_IOCB, /* CMD_ELS_REQUEST_CR 0x0A */ + LPFC_SOL_IOCB, /* CMD_ELS_REQUEST_CX 0x0B */ + LPFC_UNKNOWN_IOCB, /* 0x0C */ + LPFC_UNSOL_IOCB, /* CMD_RCV_ELS_REQ_CX 0x0D */ + LPFC_ABORT_IOCB, /* CMD_ABORT_XRI_CN 0x0E */ + LPFC_ABORT_IOCB, /* CMD_ABORT_XRI_CX 0x0F */ + LPFC_ABORT_IOCB, /* CMD_CLOSE_XRI_CR 0x10 */ + LPFC_ABORT_IOCB, /* CMD_CLOSE_XRI_CX 0x11 */ + LPFC_SOL_IOCB, /* CMD_CREATE_XRI_CR 0x12 */ + LPFC_SOL_IOCB, /* CMD_CREATE_XRI_CX 0x13 */ + LPFC_SOL_IOCB, /* CMD_GET_RPI_CN 0x14 */ + LPFC_SOL_IOCB, /* CMD_XMIT_ELS_RSP_CX 0x15 */ + LPFC_SOL_IOCB, /* CMD_GET_RPI_CR 0x16 */ + LPFC_ABORT_IOCB, /* CMD_XRI_ABORTED_CX 0x17 */ + LPFC_SOL_IOCB, /* CMD_FCP_IWRITE_CR 0x18 */ + LPFC_SOL_IOCB, /* CMD_FCP_IWRITE_CX 0x19 */ + LPFC_SOL_IOCB, /* CMD_FCP_IREAD_CR 0x1A */ + LPFC_SOL_IOCB, /* CMD_FCP_IREAD_CX 0x1B */ + LPFC_SOL_IOCB, /* CMD_FCP_ICMND_CR 0x1C */ + LPFC_SOL_IOCB, /* CMD_FCP_ICMND_CX 0x1D */ + LPFC_UNKNOWN_IOCB, /* 0x1E */ + LPFC_SOL_IOCB, /* CMD_FCP_TSEND_CX 0x1F */ + LPFC_SOL_IOCB, /* CMD_ADAPTER_MSG 0x20 */ + LPFC_SOL_IOCB, /* CMD_FCP_TRECEIVE_CX 0x21 */ + LPFC_SOL_IOCB, /* CMD_ADAPTER_DUMP 0x22 */ + LPFC_SOL_IOCB, /* CMD_FCP_TRSP_CX 0x23 */ + /* 0x24 - 0x80 */ + LPFC_UNKNOWN_IOCB, LPFC_UNKNOWN_IOCB, LPFC_UNKNOWN_IOCB, + LPFC_UNKNOWN_IOCB, LPFC_UNKNOWN_IOCB, LPFC_UNKNOWN_IOCB, + LPFC_UNKNOWN_IOCB, LPFC_UNKNOWN_IOCB, LPFC_UNKNOWN_IOCB, + LPFC_UNKNOWN_IOCB, LPFC_UNKNOWN_IOCB, LPFC_UNKNOWN_IOCB, + /* 0x30 */ + LPFC_UNKNOWN_IOCB, LPFC_UNKNOWN_IOCB, LPFC_UNKNOWN_IOCB, + LPFC_UNKNOWN_IOCB, LPFC_UNKNOWN_IOCB, LPFC_UNKNOWN_IOCB, + LPFC_UNKNOWN_IOCB, LPFC_UNKNOWN_IOCB, LPFC_UNKNOWN_IOCB, + LPFC_UNKNOWN_IOCB, LPFC_UNKNOWN_IOCB, LPFC_UNKNOWN_IOCB, + LPFC_UNKNOWN_IOCB, LPFC_UNKNOWN_IOCB, LPFC_UNKNOWN_IOCB, + LPFC_UNKNOWN_IOCB, + /* 0x40 */ + LPFC_UNKNOWN_IOCB, LPFC_UNKNOWN_IOCB, LPFC_UNKNOWN_IOCB, + LPFC_UNKNOWN_IOCB, LPFC_UNKNOWN_IOCB, LPFC_UNKNOWN_IOCB, + LPFC_UNKNOWN_IOCB, LPFC_UNKNOWN_IOCB, LPFC_UNKNOWN_IOCB, + LPFC_UNKNOWN_IOCB, LPFC_UNKNOWN_IOCB, LPFC_UNKNOWN_IOCB, + LPFC_UNKNOWN_IOCB, LPFC_UNKNOWN_IOCB, LPFC_UNKNOWN_IOCB, + LPFC_UNKNOWN_IOCB, + /* 0x50 */ + LPFC_SOL_IOCB, + LPFC_SOL_IOCB, + LPFC_UNKNOWN_IOCB, + LPFC_SOL_IOCB, + LPFC_SOL_IOCB, + LPFC_UNSOL_IOCB, + LPFC_UNSOL_IOCB, + LPFC_SOL_IOCB, + LPFC_SOL_IOCB, + + LPFC_UNKNOWN_IOCB, LPFC_UNKNOWN_IOCB, LPFC_UNKNOWN_IOCB, + LPFC_UNKNOWN_IOCB, LPFC_UNKNOWN_IOCB, LPFC_UNKNOWN_IOCB, + LPFC_UNKNOWN_IOCB, + /* 0x60 */ + LPFC_UNKNOWN_IOCB, LPFC_UNKNOWN_IOCB, LPFC_UNKNOWN_IOCB, + LPFC_UNKNOWN_IOCB, LPFC_UNKNOWN_IOCB, LPFC_UNKNOWN_IOCB, + LPFC_UNKNOWN_IOCB, LPFC_UNKNOWN_IOCB, LPFC_UNKNOWN_IOCB, + LPFC_UNKNOWN_IOCB, LPFC_UNKNOWN_IOCB, LPFC_UNKNOWN_IOCB, + LPFC_UNKNOWN_IOCB, LPFC_UNKNOWN_IOCB, LPFC_UNKNOWN_IOCB, + LPFC_UNKNOWN_IOCB, + /* 0x70 */ + LPFC_UNKNOWN_IOCB, LPFC_UNKNOWN_IOCB, LPFC_UNKNOWN_IOCB, + LPFC_UNKNOWN_IOCB, LPFC_UNKNOWN_IOCB, LPFC_UNKNOWN_IOCB, + LPFC_UNKNOWN_IOCB, LPFC_UNKNOWN_IOCB, LPFC_UNKNOWN_IOCB, + LPFC_UNKNOWN_IOCB, LPFC_UNKNOWN_IOCB, LPFC_UNKNOWN_IOCB, + LPFC_UNKNOWN_IOCB, LPFC_UNKNOWN_IOCB, LPFC_UNKNOWN_IOCB, + LPFC_UNKNOWN_IOCB, + /* 0x80 */ + LPFC_UNKNOWN_IOCB, + LPFC_UNSOL_IOCB, /* CMD_RCV_SEQUENCE64_CX 0x81 */ + LPFC_SOL_IOCB, /* CMD_XMIT_SEQUENCE64_CR 0x82 */ + LPFC_SOL_IOCB, /* CMD_XMIT_SEQUENCE64_CX 0x83 */ + LPFC_SOL_IOCB, /* CMD_XMIT_BCAST64_CN 0x84 */ + LPFC_SOL_IOCB, /* CMD_XMIT_BCAST64_CX 0x85 */ + LPFC_UNKNOWN_IOCB, /* CMD_QUE_RING_BUF64_CN 0x86 */ + LPFC_UNKNOWN_IOCB, /* CMD_QUE_XRI_BUF64_CX 0x87 */ + LPFC_UNKNOWN_IOCB, /* CMD_IOCB_CONTINUE64_CN 0x88 */ + LPFC_UNKNOWN_IOCB, /* CMD_RET_XRI_BUF64_CX 0x89 */ + LPFC_SOL_IOCB, /* CMD_ELS_REQUEST64_CR 0x8A */ + LPFC_SOL_IOCB, /* CMD_ELS_REQUEST64_CX 0x8B */ + LPFC_ABORT_IOCB, /* CMD_ABORT_MXRI64_CN 0x8C */ + LPFC_UNSOL_IOCB, /* CMD_RCV_ELS_REQ64_CX 0x8D */ + /* 0x8E - 0x94 */ + LPFC_UNKNOWN_IOCB, LPFC_UNKNOWN_IOCB, LPFC_UNKNOWN_IOCB, + LPFC_UNKNOWN_IOCB, LPFC_UNKNOWN_IOCB, LPFC_UNKNOWN_IOCB, + LPFC_UNKNOWN_IOCB, + LPFC_SOL_IOCB, /* CMD_XMIT_ELS_RSP64_CX 0x95 */ + LPFC_UNKNOWN_IOCB, /* 0x96 */ + LPFC_UNKNOWN_IOCB, /* 0x97 */ + LPFC_SOL_IOCB, /* CMD_FCP_IWRITE64_CR 0x98 */ + LPFC_SOL_IOCB, /* CMD_FCP_IWRITE64_CX 0x99 */ + LPFC_SOL_IOCB, /* CMD_FCP_IREAD64_CR 0x9A */ + LPFC_SOL_IOCB, /* CMD_FCP_IREAD64_CX 0x9B */ + LPFC_SOL_IOCB, /* CMD_FCP_ICMND64_CR 0x9C */ + LPFC_SOL_IOCB, /* CMD_FCP_ICMND64_CX 0x9D */ + LPFC_UNKNOWN_IOCB, /* 0x9E */ + LPFC_SOL_IOCB, /* CMD_FCP_TSEND64_CX 0x9F */ + LPFC_UNKNOWN_IOCB, /* 0xA0 */ + LPFC_SOL_IOCB, /* CMD_FCP_TRECEIVE64_CX 0xA1 */ + LPFC_UNKNOWN_IOCB, /* 0xA2 */ + LPFC_SOL_IOCB, /* CMD_FCP_TRSP64_CX 0xA3 */ + /* 0xA4 - 0xC1 */ + LPFC_UNKNOWN_IOCB, LPFC_UNKNOWN_IOCB, LPFC_UNKNOWN_IOCB, + LPFC_UNKNOWN_IOCB, LPFC_UNKNOWN_IOCB, LPFC_UNKNOWN_IOCB, + LPFC_UNKNOWN_IOCB, LPFC_UNKNOWN_IOCB, LPFC_UNKNOWN_IOCB, + LPFC_UNKNOWN_IOCB, LPFC_UNKNOWN_IOCB, LPFC_UNKNOWN_IOCB, + LPFC_UNKNOWN_IOCB, LPFC_UNKNOWN_IOCB, LPFC_UNKNOWN_IOCB, + LPFC_UNKNOWN_IOCB, LPFC_UNKNOWN_IOCB, LPFC_UNKNOWN_IOCB, + LPFC_UNKNOWN_IOCB, LPFC_UNKNOWN_IOCB, LPFC_UNKNOWN_IOCB, + LPFC_UNKNOWN_IOCB, LPFC_UNKNOWN_IOCB, LPFC_UNKNOWN_IOCB, + LPFC_UNKNOWN_IOCB, LPFC_UNKNOWN_IOCB, LPFC_UNKNOWN_IOCB, + LPFC_UNKNOWN_IOCB, LPFC_UNKNOWN_IOCB, LPFC_UNKNOWN_IOCB, + LPFC_SOL_IOCB, /* CMD_GEN_REQUEST64_CR 0xC2 */ + LPFC_SOL_IOCB, /* CMD_GEN_REQUEST64_CX 0xC3 */ + /* 0xC4 - 0xCF */ + LPFC_UNKNOWN_IOCB, LPFC_UNKNOWN_IOCB, LPFC_UNKNOWN_IOCB, + LPFC_UNKNOWN_IOCB, LPFC_UNKNOWN_IOCB, LPFC_UNKNOWN_IOCB, + LPFC_UNKNOWN_IOCB, LPFC_UNKNOWN_IOCB, LPFC_UNKNOWN_IOCB, + LPFC_UNKNOWN_IOCB, LPFC_UNKNOWN_IOCB, LPFC_UNKNOWN_IOCB, + + LPFC_SOL_IOCB, + LPFC_SOL_IOCB, /* CMD_SENDTEXT_CR 0xD1 */ + LPFC_SOL_IOCB, /* CMD_SENDTEXT_CX 0xD2 */ + LPFC_SOL_IOCB, /* CMD_RCV_LOGIN 0xD3 */ + LPFC_SOL_IOCB, /* CMD_ACCEPT_LOGIN 0xD4 */ + LPFC_SOL_IOCB, /* CMD_REJECT_LOGIN 0xD5 */ + LPFC_UNSOL_IOCB, + /* 0xD7 - 0xDF */ + LPFC_UNKNOWN_IOCB, LPFC_UNKNOWN_IOCB, LPFC_UNKNOWN_IOCB, + LPFC_UNKNOWN_IOCB, LPFC_UNKNOWN_IOCB, LPFC_UNKNOWN_IOCB, + LPFC_UNKNOWN_IOCB, LPFC_UNKNOWN_IOCB, LPFC_UNKNOWN_IOCB, + /* 0xE0 */ + LPFC_UNSOL_IOCB, + LPFC_SOL_IOCB, + LPFC_SOL_IOCB, + LPFC_SOL_IOCB, + LPFC_SOL_IOCB, + LPFC_UNSOL_IOCB +}; + + +static int +lpfc_sli_ring_map(struct lpfc_hba * phba) +{ + struct lpfc_sli *psli; + LPFC_MBOXQ_t *pmb; + MAILBOX_t *pmbox; + int i; + + psli = &phba->sli; + + /* Get a Mailbox buffer to setup mailbox commands for HBA + initialization */ + if ((pmb = (LPFC_MBOXQ_t *) mempool_alloc(phba->mbox_mem_pool, + GFP_ATOMIC)) == 0) { + phba->hba_state = LPFC_HBA_ERROR; + return -ENOMEM; + } + pmbox = &pmb->mb; + + /* Initialize the struct lpfc_sli_ring structure for each ring */ + for (i = 0; i < psli->sliinit.num_rings; i++) { + /* Issue a CONFIG_RING mailbox command for each ring */ + phba->hba_state = LPFC_INIT_MBX_CMDS; + lpfc_config_ring(phba, i, pmb); + if (lpfc_sli_issue_mbox(phba, pmb, MBX_POLL) != MBX_SUCCESS) { + /* Adapter failed to init, mbxCmd CFG_RING, + mbxStatus , ring */ + lpfc_printf_log(phba, + KERN_ERR, + LOG_INIT, + "%d:0446 Adapter failed to init, " + "mbxCmd x%x CFG_RING, mbxStatus x%x, " + "ring %d\n", + phba->brd_no, + pmbox->mbxCommand, + pmbox->mbxStatus, + i); + phba->hba_state = LPFC_HBA_ERROR; + mempool_free( pmb, phba->mbox_mem_pool); + return -ENXIO; + } + } + mempool_free( pmb, phba->mbox_mem_pool); + return 0; +} + +static int +lpfc_sli_ringtxcmpl_put(struct lpfc_hba * phba, + struct lpfc_sli_ring * pring, struct lpfc_iocbq * piocb) +{ + uint16_t iotag; + + list_add_tail(&piocb->list, &pring->txcmplq); + pring->txcmplq_cnt++; + + if (pring->fast_lookup) { + /* Setup fast lookup based on iotag for completion */ + iotag = piocb->iocb.ulpIoTag; + if (iotag && (iotag + < phba->sli.sliinit.ringinit[pring->ringno].fast_iotag)) + *(pring->fast_lookup + iotag) = piocb; + else { + + /* Cmd ring put: iotag greater then + configured max wd0 */ + lpfc_printf_log(phba, + KERN_ERR, + LOG_SLI, + "%d:0316 Cmd ring %d put: iotag x%x " + "greater then configured max x%x " + "wd0 x%x\n", + phba->brd_no, + pring->ringno, iotag, phba->sli.sliinit + .ringinit[pring->ringno].fast_iotag, + *(((uint32_t *)(&piocb->iocb)) + 7)); + } + } + return (0); +} + +static struct lpfc_iocbq * +lpfc_sli_ringtx_get(struct lpfc_hba * phba, struct lpfc_sli_ring * pring) +{ + struct list_head *dlp; + struct lpfc_iocbq *cmd_iocb; + struct lpfc_iocbq *next_iocb; + + dlp = &pring->txq; + cmd_iocb = NULL; + next_iocb = (struct lpfc_iocbq *) pring->txq.next; + if (next_iocb != (struct lpfc_iocbq *) & pring->txq) { + /* If the first ptr is not equal to the list header, + * deque the IOCBQ_t and return it. + */ + cmd_iocb = next_iocb; + list_del(&cmd_iocb->list); + pring->txq_cnt--; + } + return (cmd_iocb); +} + +static IOCB_t * +lpfc_sli_next_iocb_slot (struct lpfc_hba *phba, struct lpfc_sli_ring *pring) +{ + MAILBOX_t *mbox = (MAILBOX_t *)phba->sli.MBhostaddr; + PGP *pgp = (PGP *)&mbox->us.s2.port[pring->ringno]; + uint32_t max_cmd_idx = + phba->sli.sliinit.ringinit[pring->ringno].numCiocb; + IOCB_t *iocb = NULL; + + if((pring->next_cmdidx == pring->cmdidx) && + (++pring->next_cmdidx >= max_cmd_idx)) + pring->next_cmdidx = 0; + + if (unlikely(pring->local_getidx == pring->next_cmdidx)) { + + pring->local_getidx = le32_to_cpu(pgp->cmdGetInx); + + if (unlikely(pring->local_getidx >= max_cmd_idx)) { + lpfc_printf_log(phba, KERN_ERR, LOG_SLI, + "%d:0315 Ring %d issue: portCmdGet %d " + "is bigger then cmd ring %d\n", + phba->brd_no, pring->ringno, + pring->local_getidx, max_cmd_idx); + + phba->hba_state = LPFC_HBA_ERROR; + lpfc_handle_eratt(phba, HS_FFER3); + + return NULL; + } + + if (pring->local_getidx == pring->next_cmdidx) + return NULL; + } + + iocb = IOCB_ENTRY(pring->cmdringaddr, pring->cmdidx); + + return iocb; +} + +static void +lpfc_sli_submit_iocb(struct lpfc_hba *phba, struct lpfc_sli_ring *pring, + IOCB_t *iocb, struct lpfc_iocbq *nextiocb) +{ + struct lpfc_sli *psli = &phba->sli; + int ringno = pring->ringno; + + /* + * Alloocate and set up an iotag + */ + nextiocb->iocb.ulpIoTag = + lpfc_sli_next_iotag(phba, &psli->ring[psli->fcp_ring]); + + /* + * Issue iocb command to adapter + */ + lpfc_sli_pcimem_bcopy((uint32_t *)&nextiocb->iocb, + (uint32_t *)(iocb), sizeof (IOCB_t)); + wmb(); + psli->slistat.iocbCmd[ringno]++; + + /* + * If there is no completion routine to call, we can release the + * IOCB buffer back right now. For IOCBs, like QUE_RING_BUF, + * that have no rsp ring completion, iocb_cmpl MUST be NULL. + */ + if (nextiocb->iocb_cmpl) + lpfc_sli_ringtxcmpl_put(phba, pring, nextiocb); + else + mempool_free(nextiocb, phba->iocb_mem_pool); + + /* + * Let the HBA know what IOCB slot will be the next one the + * driver will put a command into. + */ + pring->cmdidx = pring->next_cmdidx; + writeb(pring->cmdidx, + (u8 *)phba->MBslimaddr + (SLIMOFF + (ringno * 2)) * 4); + + return; +} + +static void +lpfc_sli_update_full_ring(struct lpfc_hba * phba, + struct lpfc_sli_ring *pring) +{ + int ringno = pring->ringno; + + pring->flag |= LPFC_CALL_RING_AVAILABLE; + + wmb(); + + /* + * Set ring 'ringno' to SET R0CE_REQ in Chip Att register. + * The HBA will tell us when an IOCB entry is available. + */ + writel((CA_R0ATT|CA_R0CE_REQ) << (ringno*4), phba->CAregaddr); + readl(phba->CAregaddr); /* flush */ + + phba->sli.slistat.iocbCmdFull[ringno]++; +} + +static void +lpfc_sli_update_ring(struct lpfc_hba * phba, + struct lpfc_sli_ring *pring) +{ + int ringno = pring->ringno; + + /* + * Tell the HBA that there is work to do in this ring. + */ + wmb(); + writel(CA_R0ATT << (ringno * 4), phba->CAregaddr); + readl(phba->CAregaddr); /* flush */ +} + +static void +lpfc_sli_resume_iocb(struct lpfc_hba * phba, struct lpfc_sli_ring * pring) +{ + struct lpfc_sli *psli = &phba->sli; + IOCB_t *iocb; + struct lpfc_iocbq *nextiocb; + + /* + * Check to see if: + * (a) there is anything on the txq to send + * (b) link is up + * (c) link attention events can be processed (fcp ring only) + * (d) IOCB processing is not blocked by the outstanding mbox command. + */ + if (pring->txq_cnt && + (phba->hba_state > LPFC_LINK_DOWN) && + (pring->ringno != psli->fcp_ring || + psli->sliinit.sli_flag & LPFC_PROCESS_LA) && + !(pring->flag & LPFC_STOP_IOCB_MBX)) { + + while ((iocb = lpfc_sli_next_iocb_slot(phba, pring)) && + (nextiocb = lpfc_sli_ringtx_get(phba, pring))) + lpfc_sli_submit_iocb(phba, pring, iocb, nextiocb); + + if (iocb) + lpfc_sli_update_ring(phba, pring); + else + lpfc_sli_update_full_ring(phba, pring); + } + + return; +} + +/* lpfc_sli_turn_on_ring is only called by lpfc_sli_handle_mb_event below */ +static void +lpfc_sli_turn_on_ring(struct lpfc_hba * phba, int ringno) +{ + struct lpfc_sli *psli; + struct lpfc_sli_ring *pring; + PGP *pgp; + + psli = &phba->sli; + pring = &psli->ring[ringno]; + pgp = (PGP *) & (((MAILBOX_t *)psli->MBhostaddr)->us.s2.port[ringno]); + + /* If the ring is active, flag it */ + if (psli->ring[ringno].cmdringaddr) { + if (psli->ring[ringno].flag & LPFC_STOP_IOCB_MBX) { + psli->ring[ringno].flag &= ~LPFC_STOP_IOCB_MBX; + /* + * Force update of the local copy of cmdGetInx + */ + pring->local_getidx = le32_to_cpu(pgp->cmdGetInx); + lpfc_sli_resume_iocb(phba, pring); + } + } +} + +static int +lpfc_sli_chk_mbx_command(uint8_t mbxCommand) +{ + uint8_t ret; + + switch (mbxCommand) { + case MBX_LOAD_SM: + case MBX_READ_NV: + case MBX_WRITE_NV: + case MBX_RUN_BIU_DIAG: + case MBX_INIT_LINK: + case MBX_DOWN_LINK: + case MBX_CONFIG_LINK: + case MBX_CONFIG_RING: + case MBX_RESET_RING: + case MBX_READ_CONFIG: + case MBX_READ_RCONFIG: + case MBX_READ_SPARM: + case MBX_READ_STATUS: + case MBX_READ_RPI: + case MBX_READ_XRI: + case MBX_READ_REV: + case MBX_READ_LNK_STAT: + case MBX_REG_LOGIN: + case MBX_UNREG_LOGIN: + case MBX_READ_LA: + case MBX_CLEAR_LA: + case MBX_DUMP_MEMORY: + case MBX_DUMP_CONTEXT: + case MBX_RUN_DIAGS: + case MBX_RESTART: + case MBX_UPDATE_CFG: + case MBX_DOWN_LOAD: + case MBX_DEL_LD_ENTRY: + case MBX_RUN_PROGRAM: + case MBX_SET_MASK: + case MBX_SET_SLIM: + case MBX_UNREG_D_ID: + case MBX_CONFIG_FARP: + case MBX_LOAD_AREA: + case MBX_RUN_BIU_DIAG64: + case MBX_CONFIG_PORT: + case MBX_READ_SPARM64: + case MBX_READ_RPI64: + case MBX_REG_LOGIN64: + case MBX_READ_LA64: + case MBX_FLASH_WR_ULA: + case MBX_SET_DEBUG: + case MBX_LOAD_EXP_ROM: + ret = mbxCommand; + break; + default: + ret = MBX_SHUTDOWN; + break; + } + return (ret); +} +static int +lpfc_sli_handle_mb_event(struct lpfc_hba * phba) +{ + MAILBOX_t *mbox; + MAILBOX_t *pmbox; + LPFC_MBOXQ_t *pmb; + struct lpfc_dmabuf *mp; + struct lpfc_sli *psli; + int i; + unsigned long iflag; + uint32_t process_next; + + + psli = &phba->sli; + /* We should only get here if we are in SLI2 mode */ + if (!(psli->sliinit.sli_flag & LPFC_SLI2_ACTIVE)) { + return (1); + } + + spin_lock_irqsave(phba->host->host_lock, iflag); + + psli->slistat.mboxEvent++; + + /* Get a Mailbox buffer to setup mailbox commands for callback */ + if ((pmb = psli->mbox_active)) { + pmbox = &pmb->mb; + mbox = (MAILBOX_t *) psli->MBhostaddr; + + /* First check out the status word */ + lpfc_sli_pcimem_bcopy((uint32_t *) mbox, (uint32_t *) pmbox, + sizeof (uint32_t)); + + /* Sanity check to ensure the host owns the mailbox */ + if (pmbox->mbxOwner != OWN_HOST) { + /* Lets try for a while */ + for (i = 0; i < 10240; i++) { + /* First copy command data */ + lpfc_sli_pcimem_bcopy((uint32_t *) mbox, + (uint32_t *) pmbox, + sizeof (uint32_t)); + if (pmbox->mbxOwner == OWN_HOST) + goto mbout; + } + /* Stray Mailbox Interrupt, mbxCommand mbxStatus + */ + lpfc_printf_log(phba, + KERN_ERR, + LOG_MBOX | LOG_SLI, + "%d:0304 Stray Mailbox Interrupt " + "mbxCommand x%x mbxStatus x%x\n", + phba->brd_no, + pmbox->mbxCommand, + pmbox->mbxStatus); + + psli->sliinit.sli_flag |= LPFC_SLI_MBOX_ACTIVE; + spin_unlock_irqrestore(phba->host->host_lock, iflag); + return (1); + } + + mbout: + del_timer_sync(&psli->mbox_tmo); + + /* + * It is a fatal error if unknown mbox command completion. + */ + if (lpfc_sli_chk_mbx_command(pmbox->mbxCommand) == + MBX_SHUTDOWN) { + + /* Unknow mailbox command compl */ + lpfc_printf_log(phba, + KERN_ERR, + LOG_MBOX | LOG_SLI, + "%d:0323 Unknown Mailbox command %x Cmpl\n", + phba->brd_no, + pmbox->mbxCommand); + phba->hba_state = LPFC_HBA_ERROR; + spin_unlock_irqrestore(phba->host->host_lock, iflag); + lpfc_handle_eratt(phba, HS_FFER3); + return (0); + } + + psli->mbox_active = NULL; + if (pmbox->mbxStatus) { + psli->slistat.mboxStatErr++; + if (pmbox->mbxStatus == MBXERR_NO_RESOURCES) { + /* Mbox cmd cmpl error - RETRYing */ + lpfc_printf_log(phba, + KERN_INFO, + LOG_MBOX | LOG_SLI, + "%d:0305 Mbox cmd cmpl error - " + "RETRYing Data: x%x x%x x%x x%x\n", + phba->brd_no, + pmbox->mbxCommand, + pmbox->mbxStatus, + pmbox->un.varWords[0], + phba->hba_state); + pmbox->mbxStatus = 0; + pmbox->mbxOwner = OWN_HOST; + psli->sliinit.sli_flag &= ~LPFC_SLI_MBOX_ACTIVE; + if (lpfc_sli_issue_mbox(phba, pmb, MBX_NOWAIT) + == MBX_SUCCESS) { + spin_unlock_irqrestore( + phba->host->host_lock, + iflag); + return (0); + } + } + } + + /* Mailbox cmd Cmpl */ + lpfc_printf_log(phba, + KERN_INFO, + LOG_MBOX | LOG_SLI, + "%d:0307 Mailbox cmd x%x Cmpl x%p " + "Data: x%x x%x x%x x%x x%x x%x x%x x%x x%x\n", + phba->brd_no, + pmbox->mbxCommand, + pmb->mbox_cmpl, + *((uint32_t *) pmbox), + pmbox->un.varWords[0], + pmbox->un.varWords[1], + pmbox->un.varWords[2], + pmbox->un.varWords[3], + pmbox->un.varWords[4], + pmbox->un.varWords[5], + pmbox->un.varWords[6], + pmbox->un.varWords[7]); + + if (pmb->mbox_cmpl) { + /* Copy entire mbox completion over buffer */ + lpfc_sli_pcimem_bcopy((uint32_t *) mbox, + (uint32_t *) pmbox, + (sizeof (uint32_t) * + (MAILBOX_CMD_WSIZE))); + /* All mbox cmpls are posted to discovery tasklet */ + lpfc_discq_post_event(phba, pmb, NULL, + LPFC_EVT_MBOX); + } else { + mp = (struct lpfc_dmabuf *) (pmb->context1); + if (mp) { + lpfc_mbuf_free(phba, mp->virt, mp->phys); + kfree(mp); + } + mempool_free( pmb, phba->mbox_mem_pool); + } + } + + + do { + process_next = 0; /* by default don't loop */ + psli->sliinit.sli_flag &= ~LPFC_SLI_MBOX_ACTIVE; + + /* Process next mailbox command if there is one */ + if ((pmb = lpfc_mbox_get(phba))) { + if (lpfc_sli_issue_mbox(phba, pmb, MBX_NOWAIT) == + MBX_NOT_FINISHED) { + mp = (struct lpfc_dmabuf *) (pmb->context1); + if (mp) { + lpfc_mbuf_free(phba, mp->virt, + mp->phys); + kfree(mp); + } + mempool_free( pmb, phba->mbox_mem_pool); + process_next = 1; + continue; /* loop back */ + } + } else { + /* Turn on IOCB processing */ + for (i = 0; i < psli->sliinit.num_rings; i++) { + lpfc_sli_turn_on_ring(phba, i); + } + + /* Free any lpfc_dmabuf's waiting for mbox cmd cmpls */ + while (!list_empty(&phba->freebufList)) { + struct lpfc_dmabuf *mp; + + mp = (struct lpfc_dmabuf *) + (phba->freebufList.next); + if (mp) { + lpfc_mbuf_free(phba, mp->virt, + mp->phys); + list_del(&mp->list); + kfree(mp); + } + } + } + + } while (process_next); + + spin_unlock_irqrestore(phba->host->host_lock, iflag); + return (0); +} +static int +lpfc_sli_process_unsol_iocb(struct lpfc_hba *phba, struct lpfc_sli_ring *pring, + struct lpfc_iocbq *saveq) +{ + struct lpfc_sli * psli; + IOCB_t * irsp; + LPFC_RING_INIT_t * pringinit; + WORD5 * w5p; + uint32_t Rctl, Type; + uint32_t match, ringno, i; + + psli = &phba->sli; + match = 0; + ringno = pring->ringno; + irsp = &(saveq->iocb); + if ((irsp->ulpCommand == CMD_RCV_ELS_REQ64_CX) + || (irsp->ulpCommand == CMD_RCV_ELS_REQ_CX)) { + Rctl = FC_ELS_REQ; + Type = FC_ELS_DATA; + } else { + w5p = + (WORD5 *) & (saveq->iocb.un. + ulpWord[5]); + Rctl = w5p->hcsw.Rctl; + Type = w5p->hcsw.Type; + } + /* unSolicited Responses */ + pringinit = &psli->sliinit.ringinit[ringno]; + if (pringinit->prt[0].profile) { + /* If this ring has a profile set, just + send it to prt[0] */ + /* All unsol iocbs for LPFC_ELS_RING + * are posted to discovery tasklet. + */ + if (ringno == LPFC_ELS_RING) { + lpfc_discq_post_event(phba, (void *)&pringinit->prt[0], + (void *)saveq, LPFC_EVT_UNSOL_IOCB); + } + else { + (pringinit->prt[0]. + lpfc_sli_rcv_unsol_event) (phba, pring, saveq); + } + match = 1; + } else { + /* We must search, based on rctl / type + for the right routine */ + for (i = 0; i < pringinit->num_mask; + i++) { + if ((pringinit->prt[i].rctl == + Rctl) + && (pringinit->prt[i]. + type == Type)) { + /* All unsol iocbs for LPFC_ELS_RING + * are posted to discovery tasklet. + */ + if (ringno == LPFC_ELS_RING) { + lpfc_discq_post_event(phba, + (void *)&pringinit->prt[i], + (void *)saveq, LPFC_EVT_UNSOL_IOCB); + } + else { + (pringinit->prt[i]. + lpfc_sli_rcv_unsol_event) + (phba, pring, saveq); + } + match = 1; + break; + } + } + } + if (match == 0) { + /* Unexpected Rctl / Type received */ + /* Ring handler: unexpected + Rctl Type received */ + lpfc_printf_log(phba, + KERN_WARNING, + LOG_SLI, + "%d:0313 Ring %d handler: unexpected Rctl x%x " + "Type x%x received \n", + phba->brd_no, + ringno, + Rctl, + Type); + } + return(1); +} +static struct lpfc_iocbq * +lpfc_search_txcmpl(struct lpfc_sli_ring * pring, struct lpfc_iocbq * prspiocb) +{ + IOCB_t *icmd = NULL; + IOCB_t *irsp = NULL; + struct lpfc_iocbq *cmd_iocb; + struct lpfc_iocbq *iocb, *next_iocb; + uint16_t iotag; + + irsp = &prspiocb->iocb; + iotag = irsp->ulpIoTag; + cmd_iocb = NULL; + + /* Search through txcmpl from the begining */ + list_for_each_entry_safe(iocb, next_iocb, &(pring->txcmplq), list) { + icmd = &iocb->iocb; + if (iotag == icmd->ulpIoTag) { + /* Found a match. */ + cmd_iocb = iocb; + list_del(&iocb->list); + pring->txcmplq_cnt--; + break; + } + } + + return (cmd_iocb); +} +static struct lpfc_iocbq * +lpfc_sli_ringtxcmpl_get(struct lpfc_hba * phba, + struct lpfc_sli_ring * pring, + struct lpfc_iocbq * prspiocb, uint32_t srch) +{ + struct list_head *dlp; + IOCB_t *irsp = NULL; + struct lpfc_iocbq *cmd_iocb; + struct lpfc_sli *psli; + uint16_t iotag; + + + dlp = &pring->txcmplq; + + if (pring->fast_lookup && (srch == 0)) { + /* + * Use fast lookup based on iotag for completion + */ + psli = &phba->sli; + irsp = &prspiocb->iocb; + iotag = irsp->ulpIoTag; + if (iotag < psli->sliinit.ringinit[pring->ringno].fast_iotag) { + cmd_iocb = *(pring->fast_lookup + iotag); + *(pring->fast_lookup + iotag) = NULL; + if (cmd_iocb) { + list_del(&cmd_iocb->list); + pring->txcmplq_cnt--; + return cmd_iocb; + } + } else { + /* + * Rsp ring get: iotag greater then + * configured max wd0 + */ + lpfc_printf_log(phba, + KERN_ERR, + LOG_SLI, + "%d:0317 Rsp ring %d get: iotag x%x " + "greater then configured max x%x " + "wd0 x%x\n", + phba->brd_no, + pring->ringno, iotag, + psli->sliinit.ringinit[pring->ringno] + .fast_iotag, + *(((uint32_t *) irsp) + 7)); + } + } + + cmd_iocb = lpfc_search_txcmpl(pring, prspiocb); + + return cmd_iocb; +} + +static int +lpfc_sli_process_sol_iocb(struct lpfc_hba * phba, struct lpfc_sli_ring * pring, + struct lpfc_iocbq *saveq) +{ + struct lpfc_iocbq * cmdiocbp; + int ringno, rc; + unsigned long iflag; + + rc = 1; + ringno = pring->ringno; + /* Solicited Responses */ + /* Based on the iotag field, get the cmd IOCB + from the txcmplq */ + spin_lock_irqsave(phba->host->host_lock, iflag); + if ((cmdiocbp = + lpfc_sli_ringtxcmpl_get(phba, pring, saveq, + 0))) { + /* Call the specified completion + routine */ + if (cmdiocbp->iocb_cmpl) { + /* All iocb cmpls for LPFC_ELS_RING + * are posted to discovery tasklet. + */ + if (ringno == LPFC_ELS_RING) { + lpfc_discq_post_event(phba, (void *)cmdiocbp, + (void *)saveq, LPFC_EVT_SOL_IOCB); + } + else { + if (cmdiocbp->iocb_flag & LPFC_IO_POLL) { + rc = 0; + } + + spin_unlock_irqrestore(phba->host->host_lock, + iflag); + (cmdiocbp->iocb_cmpl) (phba, cmdiocbp, saveq); + spin_lock_irqsave(phba->host->host_lock, iflag); + } + } else { + mempool_free( cmdiocbp, phba->iocb_mem_pool); + } + } else { + /* Could not find the initiating command + * based of the response iotag. + * This is expected on ELS ring because of lpfc_els_abort(). + */ + if (ringno != LPFC_ELS_RING) { + /* Ring handler: unexpected + completion IoTag */ + lpfc_printf_log(phba, + KERN_WARNING, + LOG_SLI, + "%d:0322 Ring %d handler: unexpected " + "completion IoTag x%x Data: x%x x%x x%x x%x\n", + phba->brd_no, + ringno, + saveq->iocb.ulpIoTag, + saveq->iocb.ulpStatus, + saveq->iocb.un.ulpWord[4], + saveq->iocb.ulpCommand, + saveq->iocb.ulpContext); + } + } + spin_unlock_irqrestore(phba->host->host_lock, iflag); + return(rc); +} +static int +lpfc_sli_handle_ring_event(struct lpfc_hba * phba, + struct lpfc_sli_ring * pring, uint32_t mask) +{ + struct lpfc_sli * psli; + IOCB_t * entry; + IOCB_t * irsp; + struct lpfc_iocbq * rspiocbp, *next_iocb; + struct lpfc_iocbq * cmdiocbp; + struct lpfc_iocbq * saveq; + HGP * hgp; + PGP * pgp; + MAILBOX_t * mbox; + uint32_t status, free_saveq; + uint32_t portRspPut, portRspMax; + int ringno, loopcnt, rc; + uint8_t type; + unsigned long iflag; + void *to_slim; + + psli = &phba->sli; + ringno = pring->ringno; + irsp = NULL; + rc = 1; + + spin_lock_irqsave(phba->host->host_lock, iflag); + psli->slistat.iocbEvent[ringno]++; + + /* At this point we assume SLI-2 */ + mbox = (MAILBOX_t *) psli->MBhostaddr; + pgp = (PGP *) & mbox->us.s2.port[ringno]; + hgp = (HGP *) & mbox->us.s2.host[ringno]; + + /* portRspMax is the number of rsp ring entries for this specific + ring. */ + portRspMax = psli->sliinit.ringinit[ringno].numRiocb; + + rspiocbp = NULL; + loopcnt = 0; + + /* Gather iocb entries off response ring. + * rspidx is the IOCB index of the next IOCB that the driver + * is going to process. + */ + entry = IOCB_ENTRY(pring->rspringaddr, pring->rspidx); + portRspPut = le32_to_cpu(pgp->rspPutInx); + + if (portRspPut >= portRspMax) { + + /* Ring handler: portRspPut is bigger then + rsp ring */ + lpfc_printf_log(phba, + KERN_ERR, + LOG_SLI, + "%d:0312 Ring %d handler: portRspPut %d " + "is bigger then rsp ring %d\n", + phba->brd_no, + ringno, portRspPut, portRspMax); + /* + * Treat it as adapter hardware error. + */ + phba->hba_state = LPFC_HBA_ERROR; + spin_unlock_irqrestore(phba->host->host_lock, iflag); + lpfc_handle_eratt(phba, HS_FFER3); + return (1); + } + + rmb(); + + /* Get the next available response iocb. + * rspidx is the IOCB index of the next IOCB that the driver + * is going to process. + */ + while (pring->rspidx != portRspPut) { + /* get an iocb buffer to copy entry into */ + if ((rspiocbp = mempool_alloc(phba->iocb_mem_pool, + GFP_ATOMIC)) == 0) { + break; + } + + lpfc_sli_pcimem_bcopy((uint32_t *) entry, + (uint32_t *) & rspiocbp->iocb, + sizeof (IOCB_t)); + irsp = &rspiocbp->iocb; + + /* bump iocb available response index */ + if (++pring->rspidx >= portRspMax) { + pring->rspidx = 0; + } + + /* Let the HBA know what IOCB slot will be the next one the + * driver will read a response from. + */ + to_slim = (uint8_t *) phba->MBslimaddr + + (SLIMOFF + (ringno * 2) + 1) * 4; + writeb( pring->rspidx, to_slim); + + /* chain all iocb entries until LE is set */ + if (list_empty(&(pring->iocb_continueq))) { + list_add(&rspiocbp->list, &(pring->iocb_continueq)); + } else { + list_add_tail(&rspiocbp->list, + &(pring->iocb_continueq)); + } + pring->iocb_continueq_cnt++; + + /* + * When the ulpLe field is set, the entire Command has been + * received. Start by getting a pointer to the first iocb entry + * in the chain. + */ + if (irsp->ulpLe) { + /* + * By default, the driver expects to free all resources + * associated with this iocb completion. + */ + free_saveq = 1; + saveq = list_entry(pring->iocb_continueq.next, + struct lpfc_iocbq, list); + irsp = &(saveq->iocb); + list_del_init(&pring->iocb_continueq); + pring->iocb_continueq_cnt = 0; + + psli->slistat.iocbRsp[ringno]++; + + if(irsp->ulpStatus) { + /* Rsp ring error: IOCB */ + lpfc_printf_log(phba, + KERN_WARNING, + LOG_SLI, + "%d:0324 Rsp Ring %d error: IOCB Data: " + "x%x x%x x%x x%x x%x x%x x%x x%x\n", + phba->brd_no, + ringno, + irsp->un.ulpWord[0], + irsp->un.ulpWord[1], + irsp->un.ulpWord[2], + irsp->un.ulpWord[3], + irsp->un.ulpWord[4], + irsp->un.ulpWord[5], + *(((uint32_t *) irsp) + 6), + *(((uint32_t *) irsp) + 7)); + } + + /* Determine if IOCB command is a solicited or + unsolicited event */ + type = + lpfc_sli_iocb_cmd_type[(irsp-> + ulpCommand & + CMD_IOCB_MASK)]; + if (type == LPFC_SOL_IOCB) { + spin_unlock_irqrestore(phba->host->host_lock, + iflag); + rc = lpfc_sli_process_sol_iocb(phba, pring, + saveq); + spin_lock_irqsave(phba->host->host_lock, iflag); + /* + * If this solicted completion is an ELS + * command, don't free the resources now because + * the discoverytasklet does later. + */ + if (pring->ringno == LPFC_ELS_RING) + free_saveq = 0; + else + free_saveq = 1; + + } else if (type == LPFC_UNSOL_IOCB) { + spin_unlock_irqrestore(phba->host->host_lock, + iflag); + rc = lpfc_sli_process_unsol_iocb(phba, pring, + saveq); + spin_lock_irqsave(phba->host->host_lock, iflag); + + /* + * If this unsolicted completion is an ELS + * command, don't free the resources now because + * the discoverytasklet does later. + */ + if (pring->ringno == LPFC_ELS_RING) + free_saveq = 0; + else + free_saveq = 1; + + } else if (type == LPFC_ABORT_IOCB) { + /* Solicited ABORT Responses */ + /* Based on the iotag field, get the cmd IOCB + from the txcmplq */ + if ((irsp->ulpCommand != CMD_XRI_ABORTED_CX) && + ((cmdiocbp = + lpfc_sli_ringtxcmpl_get(phba, pring, + saveq, 0)))) { + /* Call the specified completion + routine */ + if (cmdiocbp->iocb_cmpl) { + spin_unlock_irqrestore( + phba->host->host_lock, + iflag); + (cmdiocbp->iocb_cmpl) (phba, + cmdiocbp, saveq); + spin_lock_irqsave( + phba->host->host_lock, + iflag); + } else { + mempool_free(cmdiocbp, + phba->iocb_mem_pool); + } + } + } else if (type == LPFC_UNKNOWN_IOCB) { + if (irsp->ulpCommand == CMD_ADAPTER_MSG) { + + char adaptermsg[LPFC_MAX_ADPTMSG]; + + memset(adaptermsg, 0, + LPFC_MAX_ADPTMSG); + memcpy(&adaptermsg[0], (uint8_t *) irsp, + MAX_MSG_DATA); + dev_warn(&((phba->pcidev)->dev), + "lpfc%d: %s", + phba->brd_no, adaptermsg); + } else { + /* Unknown IOCB command */ + lpfc_printf_log(phba, + KERN_ERR, + LOG_SLI, + "%d:0321 Unknown IOCB command " + "Data: x%x x%x x%x x%x\n", + phba->brd_no, + irsp->ulpCommand, + irsp->ulpStatus, + irsp->ulpIoTag, + irsp->ulpContext); + } + } + + if (free_saveq) { + /* + * Free up iocb buffer chain for command just + * processed + */ + if (!list_empty(&pring->iocb_continueq)) { + list_for_each_entry_safe(rspiocbp, + next_iocb, + &pring->iocb_continueq, list) { + list_del_init(&rspiocbp->list); + mempool_free(rspiocbp, + phba->iocb_mem_pool); + } + } + mempool_free( saveq, phba->iocb_mem_pool); + } + } + + /* Entire Command has been received */ + entry = IOCB_ENTRY(pring->rspringaddr, pring->rspidx); + + /* If the port response put pointer has not been updated, sync + * the pgp->rspPutInx in the MAILBOX_tand fetch the new port + * response put pointer. + */ + if (pring->rspidx == portRspPut) { + portRspPut = le32_to_cpu(pgp->rspPutInx); + } + } /* while (pring->rspidx != portRspPut) */ + + if ((rspiocbp != 0) && (mask & HA_R0RE_REQ)) { + /* At least one response entry has been freed */ + psli->slistat.iocbRspFull[ringno]++; + /* SET RxRE_RSP in Chip Att register */ + status = ((CA_R0ATT | CA_R0RE_RSP) << (ringno * 4)); + writel(status, phba->CAregaddr); + readl(phba->CAregaddr); /* flush */ + } + if ((mask & HA_R0CE_RSP) && (pring->flag & LPFC_CALL_RING_AVAILABLE)) { + pring->flag &= ~LPFC_CALL_RING_AVAILABLE; + psli->slistat.iocbCmdEmpty[ringno]++; + /* + * Force update of the local copy of cmdGetInx + */ + pring->local_getidx = le32_to_cpu(pgp->cmdGetInx); + lpfc_sli_resume_iocb(phba, pring); + + if ((psli->sliinit.ringinit[ringno].lpfc_sli_cmd_available)) + (psli->sliinit.ringinit[ringno]. + lpfc_sli_cmd_available) (phba, pring); + + } + + spin_unlock_irqrestore(phba->host->host_lock, iflag); + return (rc); +} + +static uint32_t +lpfc_intr_prep(struct lpfc_hba * phba) +{ + uint32_t ha_copy; + + /* Ignore all interrupts during initialization. */ + if (phba->hba_state < LPFC_LINK_DOWN) + return (0); + + /* Read host attention register to determine interrupt source */ + ha_copy = readl(phba->HAregaddr); + + /* Clear Attention Sources, except ERATT (to preserve status) & LATT + * (ha_copy & ~(HA_ERATT | HA_LATT)); + */ + writel((ha_copy & ~(HA_LATT | HA_ERATT)), phba->HAregaddr); + readl(phba->HAregaddr); /* flush */ + return (ha_copy); +} /* lpfc_intr_prep */ + +int +lpfc_sli_intr(struct lpfc_hba * phba) +{ + struct lpfc_sli *psli; + struct lpfc_sli_ring *pring; + uint32_t ha_copy, status; + int i; + + psli = &phba->sli; + psli->slistat.sliIntr++; + + /* + * Call the HBA to see if it is interrupting. If not, don't claim + * the interrupt + */ + ha_copy = lpfc_intr_prep(phba); + if (!ha_copy) { + return (1); + } + + if (ha_copy & HA_ERATT) { + /* + * There was a link/board error. Read the status register to + * retrieve the error event and process it. + */ + psli->slistat.errAttnEvent++; + status = readl(phba->HSregaddr); + + /* Clear Chip error bit */ + writel(HA_ERATT, phba->HAregaddr); + readl(phba->HAregaddr); /* flush */ + + lpfc_handle_eratt(phba, status); + return (0); + } + + if (ha_copy & HA_MBATT) { + /* There was a Mailbox event. */ + lpfc_sli_handle_mb_event(phba); + } + + if (ha_copy & HA_LATT) { + /* + * There was a link attention event. Provided the driver is in + * a state to handle link events, handle this event. + */ + if (psli->sliinit.sli_flag & LPFC_PROCESS_LA) { + lpfc_handle_latt(phba); + } + } + + /* Process all events on each ring */ + for (i = 0; i < psli->sliinit.num_rings; i++) { + pring = &psli->ring[i]; + if ((ha_copy & HA_RXATT) + || (pring->flag & LPFC_DEFERRED_RING_EVENT)) { + if (pring->flag & LPFC_STOP_IOCB_MASK) { + pring->flag |= LPFC_DEFERRED_RING_EVENT; + } else { + lpfc_sli_handle_ring_event(phba, pring, + (ha_copy & + HA_RXMASK)); + pring->flag &= ~LPFC_DEFERRED_RING_EVENT; + } + } + ha_copy = (ha_copy >> 4); + } + + return (0); +} + +static int +lpfc_sli_abort_iocb_ring(struct lpfc_hba * phba, struct lpfc_sli_ring * pring, + uint32_t flag) +{ + struct lpfc_sli *psli; + struct lpfc_iocbq *iocb, *next_iocb; + struct lpfc_iocbq *abtsiocbp; + IOCB_t *icmd = NULL, *cmd = NULL; + int errcnt; + uint16_t iotag; + + psli = &phba->sli; + errcnt = 0; + + /* Error everything on txq and txcmplq + * First do the txq. + */ + list_for_each_entry_safe(iocb, next_iocb, &pring->txq, list) { + list_del_init(&iocb->list); + if (iocb->iocb_cmpl) { + icmd = &iocb->iocb; + icmd->ulpStatus = IOSTAT_LOCAL_REJECT; + icmd->un.ulpWord[4] = IOERR_SLI_ABORTED; + (iocb->iocb_cmpl) (phba, iocb, iocb); + } else { + mempool_free( iocb, phba->iocb_mem_pool); + } + } + + pring->txq_cnt = 0; + INIT_LIST_HEAD(&(pring->txq)); + + /* Next issue ABTS for everything on the txcmplq */ + list_for_each_entry_safe(iocb, next_iocb, &pring->txcmplq, list) { + cmd = &iocb->iocb; + + if (flag == LPFC_SLI_ABORT_IMED) { + /* + * Imediate abort of IOCB, clear fast_lookup entry, + * if any, deque and call compl + */ + iotag = cmd->ulpIoTag; + if (pring->fast_lookup && + iotag && + (iotag < + psli->sliinit.ringinit[pring->ringno].fast_iotag)) + *(pring->fast_lookup + iotag) = NULL; + + list_del_init(&iocb->list); + pring->txcmplq_cnt--; + + if (iocb->iocb_cmpl) { + cmd->ulpStatus = IOSTAT_LOCAL_REJECT; + cmd->un.ulpWord[4] = IOERR_SLI_ABORTED; + (iocb->iocb_cmpl) (phba, iocb, iocb); + } else { + mempool_free( iocb, phba->iocb_mem_pool); + } + continue; + } + + /* issue ABTS for this IOCB based on iotag */ + + if ((abtsiocbp = mempool_alloc(phba->iocb_mem_pool, + GFP_ATOMIC)) == 0) { + errcnt++; + continue; + } + memset(abtsiocbp, 0, sizeof (struct lpfc_iocbq)); + icmd = &abtsiocbp->iocb; + + icmd->un.acxri.abortType = ABORT_TYPE_ABTS; + icmd->un.acxri.abortContextTag = cmd->ulpContext; + icmd->un.acxri.abortIoTag = cmd->ulpIoTag; + + icmd->ulpLe = 1; + icmd->ulpClass = cmd->ulpClass; + if (phba->hba_state >= LPFC_LINK_UP) { + icmd->ulpCommand = CMD_ABORT_XRI_CN; + } else { + icmd->ulpCommand = CMD_CLOSE_XRI_CN; + + } + + if (lpfc_sli_issue_iocb + (phba, pring, abtsiocbp, 0) == IOCB_ERROR) { + mempool_free(abtsiocbp, phba->iocb_mem_pool); + errcnt++; + continue; + } + /* The rsp ring completion will remove IOCB from txcmplq when + * abort is read by HBA. + */ + } + + if (flag == LPFC_SLI_ABORT_IMED) { + INIT_LIST_HEAD(&(pring->txcmplq)); + pring->txcmplq_cnt = 0; + } + + return (errcnt); +} + +int +lpfc_sli_brdreset(struct lpfc_hba * phba) +{ + MAILBOX_t *swpmb; + struct lpfc_sli *psli; + struct lpfc_sli_ring *pring; + uint16_t cfg_value, skip_post; + volatile uint32_t word0; + int i; + void *to_slim; + struct lpfc_dmabuf *mp, *next_mp; + + psli = &phba->sli; + + /* A board reset must use REAL SLIM. */ + psli->sliinit.sli_flag &= ~LPFC_SLI2_ACTIVE; + + word0 = 0; + swpmb = (MAILBOX_t *) & word0; + swpmb->mbxCommand = MBX_RESTART; + swpmb->mbxHc = 1; + + to_slim = phba->MBslimaddr; + writel(*(uint32_t *) swpmb, to_slim); + readl(to_slim); /* flush */ + + /* Only skip post after fc_ffinit is completed */ + if (phba->hba_state) { + skip_post = 1; + word0 = 1; /* This is really setting up word1 */ + } else { + skip_post = 0; + word0 = 0; /* This is really setting up word1 */ + } + to_slim = (uint8_t *) phba->MBslimaddr + sizeof (uint32_t); + writel(*(uint32_t *) swpmb, to_slim); + readl(to_slim); /* flush */ + + /* Reset HBA */ + lpfc_printf_log(phba, + KERN_INFO, + LOG_SLI, + "%d:0325 Reset HBA Data: x%x x%x\n", + phba->brd_no, + phba->hba_state, + psli->sliinit.sli_flag); + + /* Turn off SERR, PERR in PCI cmd register */ + phba->hba_state = LPFC_INIT_START; + + /* perform board reset */ + phba->fc_eventTag = 0; + phba->fc_myDID = 0; + phba->fc_prevDID = 0; + + /* Turn off parity checking and serr during the physical reset */ + pci_read_config_word(phba->pcidev, PCI_COMMAND, &cfg_value); + pci_write_config_word(phba->pcidev, PCI_COMMAND, + (cfg_value & + ~(PCI_COMMAND_PARITY | PCI_COMMAND_SERR))); + + /* Now toggle INITFF bit in the Host Control Register */ + writel(HC_INITFF, phba->HCregaddr); + mdelay(1); + readl(phba->HCregaddr); /* flush */ + writel(0, phba->HCregaddr); + readl(phba->HCregaddr); /* flush */ + + /* Restore PCI cmd register */ + + pci_write_config_word(phba->pcidev, PCI_COMMAND, cfg_value); + phba->hba_state = LPFC_INIT_START; + + /* Initialize relevant SLI info */ + for (i = 0; i < psli->sliinit.num_rings; i++) { + pring = &psli->ring[i]; + pring->flag = 0; + pring->rspidx = 0; + pring->next_cmdidx = 0; + pring->local_getidx = 0; + pring->cmdidx = 0; + pring->missbufcnt = 0; + } + + if (skip_post) { + mdelay(100); + } else { + mdelay(2000); + } + + /* Cleanup preposted buffers on the ELS ring */ + pring = &psli->ring[LPFC_ELS_RING]; + list_for_each_entry_safe(mp, next_mp, &pring->postbufq, list) { + list_del(&mp->list); + pring->postbufq_cnt--; + lpfc_mbuf_free(phba, mp->virt, mp->phys); + kfree(mp); + } + + for (i = 0; i < psli->sliinit.num_rings; i++) { + pring = &psli->ring[i]; + lpfc_sli_abort_iocb_ring(phba, pring, LPFC_SLI_ABORT_IMED); + } + + return (0); +} + +static void +lpfc_setup_slim_access(struct lpfc_hba *phba) +{ + phba->MBslimaddr = phba->slim_memmap_p; + phba->HAregaddr = (uint32_t *) (phba->ctrl_regs_memmap_p) + + HA_REG_OFFSET; + phba->HCregaddr = (uint32_t *) (phba->ctrl_regs_memmap_p) + + HC_REG_OFFSET; + phba->CAregaddr = (uint32_t *) (phba->ctrl_regs_memmap_p) + + CA_REG_OFFSET; + phba->HSregaddr = (uint32_t *) (phba->ctrl_regs_memmap_p) + + HS_REG_OFFSET; + return; +} + +int +lpfc_sli_hba_setup(struct lpfc_hba * phba) +{ + struct lpfc_sli *psli; + LPFC_MBOXQ_t *pmb; + int read_rev_reset, i, rc; + uint32_t status; + + psli = &phba->sli; + + /* Setep SLI interface for HBA register and HBA SLIM access */ + lpfc_setup_slim_access(phba); + + /* Set board state to initialization started */ + phba->hba_state = LPFC_INIT_START; + read_rev_reset = 0; + + /* On some platforms/OS's, the driver can't rely on the state the + * adapter may be in. For this reason, the driver is allowed to reset + * the HBA before initialization. + */ + if (lpfc_sli_reset_on_init) { + phba->hba_state = 0; /* Don't skip post */ + lpfc_sli_brdreset(phba); + phba->hba_state = LPFC_INIT_START; + + /* Sleep for 2.5 sec */ + msleep(2500); + } + +top: + /* Read the HBA Host Status Register */ + status = readl(phba->HSregaddr); + + /* Check status register to see what current state is */ + i = 0; + while ((status & (HS_FFRDY | HS_MBRDY)) != (HS_FFRDY | HS_MBRDY)) { + + /* Check every 100ms for 5 retries, then every 500ms for 5, then + * every 2.5 sec for 5, then reset board and every 2.5 sec for + * 4. + */ + if (i++ >= 20) { + /* Adapter failed to init, timeout, status reg + */ + lpfc_printf_log(phba, + KERN_ERR, + LOG_INIT, + "%d:0436 Adapter failed to init, " + "timeout, status reg x%x\n", + phba->brd_no, + status); + phba->hba_state = LPFC_HBA_ERROR; + return -ETIMEDOUT; + } + + /* Check to see if any errors occurred during init */ + if (status & HS_FFERM) { + /* ERROR: During chipset initialization */ + /* Adapter failed to init, chipset, status reg + */ + lpfc_printf_log(phba, + KERN_ERR, + LOG_INIT, + "%d:0437 Adapter failed to init, " + "chipset, status reg x%x\n", + phba->brd_no, + status); + phba->hba_state = LPFC_HBA_ERROR; + return -EIO; + } + + if (i <= 5) { + msleep(10); + } else if (i <= 10) { + msleep(500); + } else { + msleep(2500); + } + + if (i == 15) { + phba->hba_state = 0; /* Don't skip post */ + lpfc_sli_brdreset(phba); + phba->hba_state = LPFC_INIT_START; + } + /* Read the HBA Host Status Register */ + status = readl(phba->HSregaddr); + } + + /* Check to see if any errors occurred during init */ + if (status & HS_FFERM) { + /* ERROR: During chipset initialization */ + /* Adapter failed to init, chipset, status reg */ + lpfc_printf_log(phba, + KERN_ERR, + LOG_INIT, + "%d:0438 Adapter failed to init, chipset, " + "status reg x%x\n", + phba->brd_no, + status); + phba->hba_state = LPFC_HBA_ERROR; + return -EIO; + } + + /* Clear all interrupt enable conditions */ + writel(0, phba->HCregaddr); + readl(phba->HCregaddr); /* flush */ + + /* setup host attn register */ + writel(0xffffffff, phba->HAregaddr); + readl(phba->HAregaddr); /* flush */ + + /* Get a Mailbox buffer to setup mailbox commands for HBA + initialization */ + if ((pmb = (LPFC_MBOXQ_t *) mempool_alloc(phba->mbox_mem_pool, + GFP_ATOMIC)) == 0) { + phba->hba_state = LPFC_HBA_ERROR; + return -ENOMEM; + } + + /* Call pre CONFIG_PORT mailbox command initialization. A value of 0 + * means the call was successful. Any other nonzero value is a failure, + * but if ERESTART is returned, the driver may reset the HBA and try + * again. + */ + if ((rc = lpfc_config_port_prep(phba))) { + if ((rc == -ERESTART) && (read_rev_reset == 0)) { + mempool_free( pmb, phba->mbox_mem_pool); + phba->hba_state = 0; /* Don't skip post */ + lpfc_sli_brdreset(phba); + phba->hba_state = LPFC_INIT_START; + msleep(500); + read_rev_reset = 1; + goto top; + } + phba->hba_state = LPFC_HBA_ERROR; + mempool_free( pmb, phba->mbox_mem_pool); + return -ENXIO; + } + + /* Setup and issue mailbox CONFIG_PORT command */ + phba->hba_state = LPFC_INIT_MBX_CMDS; + lpfc_config_port(phba, pmb); + if (lpfc_sli_issue_mbox(phba, pmb, MBX_POLL) != MBX_SUCCESS) { + /* Adapter failed to init, mbxCmd CONFIG_PORT, + mbxStatus */ + lpfc_printf_log(phba, KERN_ERR, LOG_INIT, + "%d:0442 Adapter failed to init, mbxCmd x%x " + "CONFIG_PORT, mbxStatus x%x Data: x%x\n", + phba->brd_no, pmb->mb.mbxCommand, + pmb->mb.mbxStatus, 0); + + /* This clause gives the config_port call is given multiple + chances to succeed. */ + if (read_rev_reset == 0) { + mempool_free( pmb, phba->mbox_mem_pool); + phba->hba_state = 0; /* Don't skip post */ + lpfc_sli_brdreset(phba); + phba->hba_state = LPFC_INIT_START; + msleep(2500); + read_rev_reset = 1; + goto top; + } + + psli->sliinit.sli_flag &= ~LPFC_SLI2_ACTIVE; + phba->hba_state = LPFC_HBA_ERROR; + mempool_free( pmb, phba->mbox_mem_pool); + return -ENXIO; + } + + if ((rc = lpfc_sli_ring_map(phba))) { + phba->hba_state = LPFC_HBA_ERROR; + mempool_free( pmb, phba->mbox_mem_pool); + return -ENXIO; + } + psli->sliinit.sli_flag |= LPFC_PROCESS_LA; + + /* Call post CONFIG_PORT mailbox command initialization. */ + if ((rc = lpfc_config_port_post(phba))) { + phba->hba_state = LPFC_HBA_ERROR; + mempool_free( pmb, phba->mbox_mem_pool); + return -ENXIO; + } + mempool_free( pmb, phba->mbox_mem_pool); + return 0; +} + + + + + + + +static void +lpfc_mbox_abort(struct lpfc_hba * phba) +{ + struct lpfc_sli *psli; + LPFC_MBOXQ_t *pmbox; + MAILBOX_t *mb; + struct lpfc_dmabuf *mp; + + psli = &phba->sli; + + if (psli->mbox_active) { + del_timer_sync(&psli->mbox_tmo); + pmbox = psli->mbox_active; + mb = &pmbox->mb; + psli->mbox_active = NULL; + if (pmbox->mbox_cmpl) { + mb->mbxStatus = MBX_NOT_FINISHED; + (pmbox->mbox_cmpl) (phba, pmbox); + } else { + mp = (struct lpfc_dmabuf *) (pmbox->context1); + if (mp) { + lpfc_mbuf_free(phba, mp->virt, mp->phys); + kfree(mp); + } + mempool_free( pmbox, phba->mbox_mem_pool); + } + psli->sliinit.sli_flag &= ~LPFC_SLI_MBOX_ACTIVE; + } + + /* Abort all the non active mailbox commands. */ + pmbox = lpfc_mbox_get(phba); + while (pmbox) { + mb = &pmbox->mb; + if (pmbox->mbox_cmpl) { + mb->mbxStatus = MBX_NOT_FINISHED; + (pmbox->mbox_cmpl) (phba, pmbox); + } else { + mp = (struct lpfc_dmabuf *) (pmbox->context1); + if (mp) { + lpfc_mbuf_free(phba, mp->virt, mp->phys); + kfree(mp); + } + mempool_free( pmbox, phba->mbox_mem_pool); + } + pmbox = lpfc_mbox_get(phba); + } + return; +} +/*! lpfc_mbox_timeout + * + * \pre + * \post + * \param hba Pointer to per struct lpfc_hba structure + * \param l1 Pointer to the driver's mailbox queue. + * \return + * void + * + * \b Description: + * + * This routine handles mailbox timeout events at timer interrupt context. + */ +void +lpfc_mbox_timeout(unsigned long ptr) +{ + struct lpfc_hba *phba; + struct lpfc_sli *psli; + LPFC_MBOXQ_t *pmbox; + MAILBOX_t *mb; + struct lpfc_dmabuf *mp; + unsigned long iflag; + + phba = (struct lpfc_hba *)ptr; + psli = &phba->sli; + spin_lock_irqsave(phba->host->host_lock, iflag); + + pmbox = psli->mbox_active; + mb = &pmbox->mb; + + /* Mbox cmd timeout */ + lpfc_printf_log(phba, + KERN_ERR, + LOG_MBOX | LOG_SLI, + "%d:0310 Mailbox command x%x timeout Data: x%x x%x x%p\n", + phba->brd_no, + mb->mbxCommand, + phba->hba_state, + psli->sliinit.sli_flag, + psli->mbox_active); + + if (psli->mbox_active == pmbox) { + psli->mbox_active = NULL; + if (pmbox->mbox_cmpl) { + mb->mbxStatus = MBX_NOT_FINISHED; + (pmbox->mbox_cmpl) (phba, pmbox); + } else { + mp = (struct lpfc_dmabuf *) (pmbox->context1); + if (mp) { + lpfc_mbuf_free(phba, mp->virt, mp->phys); + kfree(mp); + } + mempool_free( pmbox, phba->mbox_mem_pool); + } + psli->sliinit.sli_flag &= ~LPFC_SLI_MBOX_ACTIVE; + } + + lpfc_mbox_abort(phba); + spin_unlock_irqrestore(phba->host->host_lock, iflag); + return; +} + + +int +lpfc_sli_issue_mbox(struct lpfc_hba * phba, LPFC_MBOXQ_t * pmbox, uint32_t flag) +{ + MAILBOX_t *mbox; + MAILBOX_t *mb; + struct lpfc_sli *psli; + uint32_t status, evtctr; + uint32_t ha_copy; + int i; + unsigned long drvr_flag = 0; + volatile uint32_t word0, ldata; + void *to_slim; + + psli = &phba->sli; + if (flag & MBX_POLL) { + spin_lock_irqsave(phba->host->host_lock, drvr_flag); + } + + mb = &pmbox->mb; + status = MBX_SUCCESS; + + if (psli->sliinit.sli_flag & LPFC_SLI_MBOX_ACTIVE) { + /* Polling for a mbox command when another one is already active + * is not allowed in SLI. Also, the driver must have established + * SLI2 mode to queue and process multiple mbox commands. + */ + + if (flag & MBX_POLL) { + spin_unlock_irqrestore(phba->host->host_lock, + drvr_flag); + + /* Mbox command cannot issue */ + LOG_MBOX_CANNOT_ISSUE_DATA( phba, mb, psli, flag) + return (MBX_NOT_FINISHED); + } + + if (!(psli->sliinit.sli_flag & LPFC_SLI2_ACTIVE)) { + + /* Mbox command cannot issue */ + LOG_MBOX_CANNOT_ISSUE_DATA( phba, mb, psli, flag) + return (MBX_NOT_FINISHED); + } + + /* Handle STOP IOCB processing flag. This is only meaningful + * if we are not polling for mbox completion. + */ + if (flag & MBX_STOP_IOCB) { + flag &= ~MBX_STOP_IOCB; + /* Now flag each ring */ + for (i = 0; i < psli->sliinit.num_rings; i++) { + /* If the ring is active, flag it */ + if (psli->ring[i].cmdringaddr) { + psli->ring[i].flag |= + LPFC_STOP_IOCB_MBX; + } + } + } + + /* Another mailbox command is still being processed, queue this + * command to be processed later. + */ + lpfc_mbox_put(phba, pmbox); + + /* Mbox cmd issue - BUSY */ + lpfc_printf_log(phba, + KERN_INFO, + LOG_MBOX | LOG_SLI, + "%d:0308 Mbox cmd issue - BUSY Data: x%x x%x x%x x%x\n", + phba->brd_no, + mb->mbxCommand, + phba->hba_state, + psli->sliinit.sli_flag, + flag); + + psli->slistat.mboxBusy++; + if (flag == MBX_POLL) { + spin_unlock_irqrestore(phba->host->host_lock, + drvr_flag); + } + return (MBX_BUSY); + } + + /* Handle STOP IOCB processing flag. This is only meaningful + * if we are not polling for mbox completion. + */ + if (flag & MBX_STOP_IOCB) { + flag &= ~MBX_STOP_IOCB; + if (flag == MBX_NOWAIT) { + /* Now flag each ring */ + for (i = 0; i < psli->sliinit.num_rings; i++) { + /* If the ring is active, flag it */ + if (psli->ring[i].cmdringaddr) { + psli->ring[i].flag |= + LPFC_STOP_IOCB_MBX; + } + } + } + } + + psli->sliinit.sli_flag |= LPFC_SLI_MBOX_ACTIVE; + + /* If we are not polling, we MUST be in SLI2 mode */ + if (flag != MBX_POLL) { + if (!(psli->sliinit.sli_flag & LPFC_SLI2_ACTIVE)) { + psli->sliinit.sli_flag &= ~LPFC_SLI_MBOX_ACTIVE; + + /* Mbox command cannot issue */ + LOG_MBOX_CANNOT_ISSUE_DATA( phba, mb, psli, flag); + return (MBX_NOT_FINISHED); + } + /* timeout active mbox command */ + mod_timer(&psli->mbox_tmo, jiffies + HZ * LPFC_MBOX_TMO); + } + + /* Mailbox cmd issue */ + lpfc_printf_log(phba, + KERN_INFO, + LOG_MBOX | LOG_SLI, + "%d:0309 Mailbox cmd x%x issue Data: x%x x%x x%x\n", + phba->brd_no, + mb->mbxCommand, + phba->hba_state, + psli->sliinit.sli_flag, + flag); + + psli->slistat.mboxCmd++; + evtctr = psli->slistat.mboxEvent; + + /* next set own bit for the adapter and copy over command word */ + mb->mbxOwner = OWN_CHIP; + + if (psli->sliinit.sli_flag & LPFC_SLI2_ACTIVE) { + + /* First copy command data to host SLIM area */ + mbox = (MAILBOX_t *) psli->MBhostaddr; + lpfc_sli_pcimem_bcopy((uint32_t *) mb, (uint32_t *) mbox, + (sizeof (uint32_t) * + (MAILBOX_CMD_WSIZE))); + + pci_dma_sync_single_for_device(phba->pcidev, + phba->slim2p_mapping, + sizeof (MAILBOX_t), + PCI_DMA_TODEVICE); + } else { + if (mb->mbxCommand == MBX_CONFIG_PORT) { + /* copy command data into host mbox for cmpl */ + mbox = (MAILBOX_t *) psli->MBhostaddr; + lpfc_sli_pcimem_bcopy((uint32_t *) mb, + (uint32_t *) mbox, + (sizeof (uint32_t) * + (MAILBOX_CMD_WSIZE))); + } + + /* First copy mbox command data to HBA SLIM, skip past first + word */ + to_slim = (uint8_t *) phba->MBslimaddr + sizeof (uint32_t); + lpfc_memcpy_to_slim(to_slim, (void *)&mb->un.varWords[0], + (MAILBOX_CMD_WSIZE - 1) * sizeof (uint32_t)); + + /* Next copy over first word, with mbxOwner set */ + ldata = *((volatile uint32_t *)mb); + to_slim = phba->MBslimaddr; + writel(ldata, to_slim); + readl(to_slim); /* flush */ + + if (mb->mbxCommand == MBX_CONFIG_PORT) { + /* switch over to host mailbox */ + psli->sliinit.sli_flag |= LPFC_SLI2_ACTIVE; + } + } + + wmb(); + /* interrupt board to doit right away */ + writel(CA_MBATT, phba->CAregaddr); + readl(phba->CAregaddr); /* flush */ + + switch (flag) { + case MBX_NOWAIT: + /* Don't wait for it to finish, just return */ + psli->mbox_active = pmbox; + break; + + case MBX_POLL: + i = 0; + psli->mbox_active = NULL; + if (psli->sliinit.sli_flag & LPFC_SLI2_ACTIVE) { + /* First read mbox status word */ + mbox = (MAILBOX_t *) psli->MBhostaddr; + word0 = *((volatile uint32_t *)mbox); + word0 = le32_to_cpu(word0); + } else { + /* First read mbox status word */ + word0 = readl(phba->MBslimaddr); + } + + /* Read the HBA Host Attention Register */ + ha_copy = readl(phba->HAregaddr); + + /* Wait for command to complete */ + while (((word0 & OWN_CHIP) == OWN_CHIP) + || !(ha_copy & HA_MBATT)) { + if (i++ >= 100) { + psli->sliinit.sli_flag &= ~LPFC_SLI_MBOX_ACTIVE; + spin_unlock_irqrestore(phba->host->host_lock, + drvr_flag); + return (MBX_NOT_FINISHED); + } + + /* Check if we took a mbox interrupt while we were + polling */ + if (((word0 & OWN_CHIP) != OWN_CHIP) + && (evtctr != psli->slistat.mboxEvent)) + break; + + spin_unlock_irqrestore(phba->host->host_lock, + drvr_flag); + + /* Can be in interrupt context, do not sleep */ + /* (or might be called with interrupts disabled) */ + mdelay(i); + + spin_lock_irqsave(phba->host->host_lock, drvr_flag); + + if (psli->sliinit.sli_flag & LPFC_SLI2_ACTIVE) { + /* First copy command data */ + mbox = (MAILBOX_t *) psli->MBhostaddr; + word0 = *((volatile uint32_t *)mbox); + word0 = le32_to_cpu(word0); + if (mb->mbxCommand == MBX_CONFIG_PORT) { + MAILBOX_t *slimmb; + volatile uint32_t slimword0; + /* Check real SLIM for any errors */ + slimword0 = readl(phba->MBslimaddr); + slimmb = (MAILBOX_t *) & slimword0; + if (((slimword0 & OWN_CHIP) != OWN_CHIP) + && slimmb->mbxStatus) { + psli->sliinit.sli_flag &= + ~LPFC_SLI2_ACTIVE; + word0 = slimword0; + } + } + } else { + /* First copy command data */ + word0 = readl(phba->MBslimaddr); + } + /* Read the HBA Host Attention Register */ + ha_copy = readl(phba->HAregaddr); + } + + if (psli->sliinit.sli_flag & LPFC_SLI2_ACTIVE) { + /* First copy command data */ + mbox = (MAILBOX_t *) psli->MBhostaddr; + /* copy results back to user */ + lpfc_sli_pcimem_bcopy((uint32_t *) mbox, + (uint32_t *) mb, + (sizeof (uint32_t) * + MAILBOX_CMD_WSIZE)); + } else { + /* First copy command data */ + lpfc_memcpy_from_slim((void *)mb, + phba->MBslimaddr, + sizeof (uint32_t) * (MAILBOX_CMD_WSIZE)); + } + + writel(HA_MBATT, phba->HAregaddr); + readl(phba->HAregaddr); /* flush */ + + psli->sliinit.sli_flag &= ~LPFC_SLI_MBOX_ACTIVE; + status = mb->mbxStatus; + } + + if (flag == MBX_POLL) { + spin_unlock_irqrestore(phba->host->host_lock, drvr_flag); + } + return (status); +} + +static int +lpfc_sli_ringtx_put(struct lpfc_hba * phba, struct lpfc_sli_ring * pring, + struct lpfc_iocbq * piocb) +{ + /* Insert the caller's iocb in the txq tail for later processing. */ + list_add_tail(&piocb->list, &pring->txq); + pring->txq_cnt++; + return (0); +} + +static struct lpfc_iocbq * +lpfc_sli_next_iocb(struct lpfc_hba *phba, struct lpfc_sli_ring *pring, + struct lpfc_iocbq ** piocb) +{ + struct lpfc_iocbq * nextiocb; + + nextiocb = lpfc_sli_ringtx_get(phba, pring); + if (!nextiocb) { + nextiocb = *piocb; + *piocb = NULL; + } + + return nextiocb; +} + +int +lpfc_sli_issue_iocb(struct lpfc_hba *phba, struct lpfc_sli_ring *pring, + struct lpfc_iocbq *piocb, uint32_t flag) +{ + struct lpfc_sli *psli = &phba->sli; + int ringno = pring->ringno; + struct lpfc_iocbq *nextiocb; + IOCB_t *iocb; + + /* + * We should never get an IOCB if we are in a < LINK_DOWN state + */ + if (unlikely(phba->hba_state < LPFC_LINK_DOWN)) + return IOCB_ERROR; + + /* + * Check to see if we are blocking IOCB processing because of a + * outstanding mbox command. + */ + if (unlikely(pring->flag & LPFC_STOP_IOCB_MBX)) + goto iocb_busy; + + if (unlikely(phba->hba_state == LPFC_LINK_DOWN)) { + /* + * Only CREATE_XRI, CLOSE_XRI, ABORT_XRI, and QUE_RING_BUF + * can be issued if the link is not up. + */ + switch (piocb->iocb.ulpCommand) { + case CMD_QUE_RING_BUF_CN: + case CMD_QUE_RING_BUF64_CN: + case CMD_CLOSE_XRI_CN: + case CMD_ABORT_XRI_CN: + /* + * For IOCBs, like QUE_RING_BUF, that have no rsp ring + * completion, iocb_cmpl MUST be 0. + */ + if (piocb->iocb_cmpl) + piocb->iocb_cmpl = NULL; + /*FALLTHROUGH*/ + case CMD_CREATE_XRI_CR: + break; + default: + goto iocb_busy; + } + + /* + * For FCP commands, we must be in a state where we can process link + * attention events. + */ + } else if (unlikely(pring->ringno == psli->fcp_ring && + !(psli->sliinit.sli_flag & LPFC_PROCESS_LA))) + goto iocb_busy; + + /* + * Check to see if this is a high priority command. + * If so bypass tx queue processing. + */ + if (unlikely((flag & SLI_IOCB_HIGH_PRIORITY) && + (iocb = lpfc_sli_next_iocb_slot(phba, pring)))) { + lpfc_sli_submit_iocb(phba, pring, iocb, piocb); + piocb = NULL; + } + + while ((iocb = lpfc_sli_next_iocb_slot(phba, pring)) && + (nextiocb = lpfc_sli_next_iocb(phba, pring, &piocb))) + lpfc_sli_submit_iocb(phba, pring, iocb, nextiocb); + + if (iocb) + lpfc_sli_update_ring(phba, pring); + else + lpfc_sli_update_full_ring(phba, pring); + + if (!piocb) + return IOCB_SUCCESS; + + goto out_busy; + + iocb_busy: + psli->slistat.iocbCmdDelay[ringno]++; + + out_busy: + + if (!(flag & SLI_IOCB_RET_IOCB)) { + lpfc_sli_ringtx_put(phba, pring, piocb); + return IOCB_SUCCESS; + } + + return IOCB_BUSY; +} + +int +lpfc_sli_queue_setup(struct lpfc_hba * phba) +{ + struct lpfc_sli *psli; + struct lpfc_sli_ring *pring; + int i, cnt; + + psli = &phba->sli; + INIT_LIST_HEAD(&psli->mboxq); + /* Initialize list headers for txq and txcmplq as double linked lists */ + for (i = 0; i < psli->sliinit.num_rings; i++) { + pring = &psli->ring[i]; + pring->ringno = i; + pring->next_cmdidx = 0; + pring->local_getidx = 0; + pring->cmdidx = 0; + INIT_LIST_HEAD(&pring->txq); + INIT_LIST_HEAD(&pring->txcmplq); + INIT_LIST_HEAD(&pring->iocb_continueq); + INIT_LIST_HEAD(&pring->postbufq); + cnt = psli->sliinit.ringinit[i].fast_iotag; + if (cnt) { + pring->fast_lookup = + kmalloc(cnt * sizeof (struct lpfc_iocbq *), + GFP_KERNEL); + if (pring->fast_lookup == 0) { + return (0); + } + memset((char *)pring->fast_lookup, 0, + cnt * sizeof (struct lpfc_iocbq *)); + } + } + return (1); +} + +int +lpfc_sli_hba_down(struct lpfc_hba * phba) +{ + struct lpfc_sli *psli; + struct lpfc_sli_ring *pring; + LPFC_MBOXQ_t *pmb; + struct lpfc_dmabuf *mp; + struct lpfc_iocbq *iocb, *next_iocb; + IOCB_t *icmd = NULL; + int i; + + psli = &phba->sli; + lpfc_hba_down_prep(phba); + + for (i = 0; i < psli->sliinit.num_rings; i++) { + pring = &psli->ring[i]; + pring->flag |= LPFC_DEFERRED_RING_EVENT; + + /* + * Error everything on the txq since these iocbs have not been + * given to the FW yet. + */ + pring->txq_cnt = 0; + + list_for_each_entry_safe(iocb, next_iocb, &pring->txq, list) { + list_del_init(&iocb->list); + if (iocb->iocb_cmpl) { + icmd = &iocb->iocb; + icmd->ulpStatus = IOSTAT_LOCAL_REJECT; + icmd->un.ulpWord[4] = IOERR_SLI_DOWN; + (iocb->iocb_cmpl) (phba, iocb, iocb); + } else { + mempool_free( iocb, phba->iocb_mem_pool); + } + } + + INIT_LIST_HEAD(&(pring->txq)); + + if (pring->fast_lookup) { + kfree(pring->fast_lookup); + pring->fast_lookup = NULL; + } + + } + + /* Return any active mbox cmds */ + del_timer_sync(&psli->mbox_tmo); + if ((psli->mbox_active)) { + pmb = psli->mbox_active; + mp = (struct lpfc_dmabuf *) (pmb->context1); + if (mp) { + lpfc_mbuf_free(phba, mp->virt, mp->phys); + kfree(mp); + } + mempool_free(psli->mbox_active, phba->mbox_mem_pool); + } + psli->sliinit.sli_flag &= ~LPFC_SLI_MBOX_ACTIVE; + psli->mbox_active = NULL; + + /* Return any pending mbox cmds */ + while ((pmb = lpfc_mbox_get(phba)) != NULL) { + mp = (struct lpfc_dmabuf *) (pmb->context1); + if (mp) { + lpfc_mbuf_free(phba, mp->virt, mp->phys); + kfree(mp); + } + mempool_free(pmb, phba->mbox_mem_pool); + } + + INIT_LIST_HEAD(&psli->mboxq); + + /* + * Provided the hba is not in an error state, reset it. It is not + * capable of IO anymore. + */ + if (phba->hba_state != LPFC_HBA_ERROR) { + phba->hba_state = LPFC_INIT_START; + lpfc_sli_brdreset(phba); + } + + return 1; +} + +void +lpfc_sli_pcimem_bcopy(uint32_t * src, uint32_t * dest, uint32_t cnt) +{ + uint32_t ldata; + int i; + + for (i = 0; i < (int)cnt; i += sizeof (uint32_t)) { + ldata = *src++; + ldata = le32_to_cpu(ldata); + *dest++ = ldata; + } +} + +int +lpfc_sli_ringpostbuf_put(struct lpfc_hba * phba, struct lpfc_sli_ring * pring, + struct lpfc_dmabuf * mp) +{ + /* Stick struct lpfc_dmabuf at end of postbufq so driver can look it up + later */ + list_add_tail(&mp->list, &pring->postbufq); + + pring->postbufq_cnt++; + return 0; +} + + +struct lpfc_dmabuf * +lpfc_sli_ringpostbuf_get(struct lpfc_hba *phba, struct lpfc_sli_ring *pring, + dma_addr_t phys) +{ + struct lpfc_dmabuf *mp, *next_mp; + struct list_head *slp = &pring->postbufq; + + /* Search postbufq, from the begining, looking for a match on phys */ + list_for_each_entry_safe(mp, next_mp, &pring->postbufq, list) { + if (mp->phys == phys) { + list_del_init(&mp->list); + pring->postbufq_cnt--; + pci_dma_sync_single_for_cpu(phba->pcidev, mp->phys, + LPFC_BPL_SIZE, PCI_DMA_FROMDEVICE); + return mp; + } + } + + lpfc_printf_log(phba, KERN_ERR, LOG_INIT, + "%d:0410 Cannot find virtual addr for mapped buf on " + "ring %d Data x%llx x%p x%p x%x\n", + phba->brd_no, pring->ringno, (unsigned long long)phys, + slp->next, slp->prev, pring->postbufq_cnt); + return NULL; +} + +uint32_t +lpfc_sli_next_iotag(struct lpfc_hba * phba, struct lpfc_sli_ring * pring) +{ + LPFC_RING_INIT_t *pringinit; + struct lpfc_sli *psli; + uint32_t search_start; + + psli = &phba->sli; + pringinit = &psli->sliinit.ringinit[pring->ringno]; + + if (pring->fast_lookup == NULL) { + pringinit->iotag_ctr++; + if (pringinit->iotag_ctr >= pringinit->iotag_max) + pringinit->iotag_ctr = 1; + return pringinit->iotag_ctr; + } + + search_start = pringinit->iotag_ctr; + + do { + pringinit->iotag_ctr++; + if (pringinit->iotag_ctr >= pringinit->fast_iotag) + pringinit->iotag_ctr = 1; + + if(*(pring->fast_lookup + pringinit->iotag_ctr) == NULL) + return pringinit->iotag_ctr; + + } while (pringinit->iotag_ctr != search_start); + + /* + * Outstanding I/O count for ring is at max + */ + lpfc_printf_log(phba, + KERN_ERR, + LOG_SLI, + "%d:0318 Outstanding I/O count for ring %d is at max x%x\n", + phba->brd_no, + pring->ringno, + psli->sliinit.ringinit[pring->ringno].fast_iotag); + return (0); +} + +static void +lpfc_sli_abort_elsreq_cmpl(struct lpfc_hba * phba, struct lpfc_iocbq * cmdiocb, + struct lpfc_iocbq * rspiocb) +{ + struct lpfc_dmabuf *buf_ptr, *buf_ptr1; + /* Free the resources associated with the ELS_REQUEST64 IOCB the driver + * just aborted. + * In this case, context2 = cmd, context2->next = rsp, context3 = bpl + */ + if (cmdiocb->context2) { + buf_ptr1 = (struct lpfc_dmabuf *) cmdiocb->context2; + + /* Free the response IOCB before completing the abort + command. */ + if (!list_empty(&buf_ptr1->list)) { + + buf_ptr = list_entry(buf_ptr1->list.next, + struct lpfc_dmabuf, list); + + list_del(&buf_ptr->list); + lpfc_mbuf_free(phba, buf_ptr->virt, buf_ptr->phys); + kfree(buf_ptr); + } + lpfc_mbuf_free(phba, buf_ptr1->virt, buf_ptr1->phys); + kfree(buf_ptr1); + } + + if (cmdiocb->context3) { + buf_ptr = (struct lpfc_dmabuf *) cmdiocb->context3; + lpfc_mbuf_free(phba, buf_ptr->virt, buf_ptr->phys); + kfree(buf_ptr); + } + mempool_free( cmdiocb, phba->iocb_mem_pool); + return; +} + +int +lpfc_sli_issue_abort_iotag32(struct lpfc_hba * phba, + struct lpfc_sli_ring * pring, + struct lpfc_iocbq * cmdiocb) +{ + struct lpfc_sli *psli; + struct lpfc_iocbq *abtsiocbp; + IOCB_t *icmd = NULL; + IOCB_t *iabt = NULL; + uint32_t iotag32; + + psli = &phba->sli; + + /* issue ABTS for this IOCB based on iotag */ + if ((abtsiocbp = mempool_alloc(phba->iocb_mem_pool, GFP_ATOMIC)) == 0) { + return (0); + } + memset(abtsiocbp, 0, sizeof (struct lpfc_iocbq)); + iabt = &abtsiocbp->iocb; + + icmd = &cmdiocb->iocb; + switch (icmd->ulpCommand) { + case CMD_ELS_REQUEST64_CR: + iotag32 = icmd->un.elsreq64.bdl.ulpIoTag32; + /* Even though we abort the ELS command, the firmware may access + * the BPL or other resources before it processes our + * ABORT_MXRI64. Thus we must delay reusing the cmdiocb + * resources till the actual abort request completes. + */ + abtsiocbp->context1 = (void *)((unsigned long)icmd->ulpCommand); + abtsiocbp->context2 = cmdiocb->context2; + abtsiocbp->context3 = cmdiocb->context3; + cmdiocb->context2 = NULL; + cmdiocb->context3 = NULL; + abtsiocbp->iocb_cmpl = lpfc_sli_abort_elsreq_cmpl; + break; + default: + mempool_free( abtsiocbp, phba->iocb_mem_pool); + return (0); + } + + iabt->un.amxri.abortType = ABORT_TYPE_ABTS; + iabt->un.amxri.iotag32 = iotag32; + + iabt->ulpLe = 1; + iabt->ulpClass = CLASS3; + iabt->ulpCommand = CMD_ABORT_MXRI64_CN; + + if (lpfc_sli_issue_iocb(phba, pring, abtsiocbp, 0) == IOCB_ERROR) { + mempool_free( abtsiocbp, phba->iocb_mem_pool); + return (0); + } + + return (1); +} + +int +lpfc_sli_abort_iocb_ctx(struct lpfc_hba * phba, struct lpfc_sli_ring * pring, + uint32_t ctx) +{ + struct lpfc_sli *psli; + struct lpfc_iocbq *iocb, *next_iocb; + struct lpfc_iocbq *abtsiocbp; + IOCB_t *icmd = NULL, *cmd = NULL; + int errcnt; + + psli = &phba->sli; + errcnt = 0; + + /* Error matching iocb on txq or txcmplq + * First check the txq. + */ + list_for_each_entry_safe(iocb, next_iocb, &pring->txq, list) { + cmd = &iocb->iocb; + if (cmd->ulpContext != ctx) { + continue; + } + + list_del_init(&iocb->list); + pring->txq_cnt--; + if (iocb->iocb_cmpl) { + icmd = &iocb->iocb; + icmd->ulpStatus = IOSTAT_LOCAL_REJECT; + icmd->un.ulpWord[4] = IOERR_SLI_ABORTED; + (iocb->iocb_cmpl) (phba, iocb, iocb); + } else { + mempool_free( iocb, phba->iocb_mem_pool); + } + } + + /* Next check the txcmplq */ + list_for_each_entry_safe(iocb, next_iocb, &pring->txcmplq, list) { + cmd = &iocb->iocb; + if (cmd->ulpContext != ctx) { + continue; + } + + /* issue ABTS for this IOCB based on iotag */ + if ((abtsiocbp = mempool_alloc(phba->iocb_mem_pool, + GFP_ATOMIC)) == 0) { + errcnt++; + continue; + } + memset(abtsiocbp, 0, sizeof (struct lpfc_iocbq)); + icmd = &abtsiocbp->iocb; + + icmd->un.acxri.abortType = ABORT_TYPE_ABTS; + icmd->un.acxri.abortContextTag = cmd->ulpContext; + icmd->un.acxri.abortIoTag = cmd->ulpIoTag; + + icmd->ulpLe = 1; + icmd->ulpClass = cmd->ulpClass; + if (phba->hba_state >= LPFC_LINK_UP) { + icmd->ulpCommand = CMD_ABORT_XRI_CN; + } else { + icmd->ulpCommand = CMD_CLOSE_XRI_CN; + } + + if (lpfc_sli_issue_iocb(phba, pring, abtsiocbp, 0) == + IOCB_ERROR) { + mempool_free( abtsiocbp, phba->iocb_mem_pool); + errcnt++; + continue; + } + /* The rsp ring completion will remove IOCB from txcmplq when + * abort is read by HBA. + */ + } + return (errcnt); +} + +int +lpfc_sli_sum_iocb_host(struct lpfc_hba * phba, + struct lpfc_sli_ring * pring) +{ + struct lpfc_sli *psli; + struct lpfc_iocbq *iocb, *next_iocb; + IOCB_t *cmd = NULL; + struct lpfc_scsi_buf *lpfc_cmd; + int sum; + + psli = &phba->sli; + sum = 0; + + /* Error matching iocb on txq or txcmplq + * First check the txq. + */ + list_for_each_entry_safe(iocb, next_iocb, &pring->txq, list) { + cmd = &iocb->iocb; + + /* Must be a FCP command */ + if ((cmd->ulpCommand != CMD_FCP_ICMND64_CR) && + (cmd->ulpCommand != CMD_FCP_IWRITE64_CR) && + (cmd->ulpCommand != CMD_FCP_IREAD64_CR)) { + continue; + } + + /* context1 MUST be a struct lpfc_scsi_buf */ + lpfc_cmd = (struct lpfc_scsi_buf *) (iocb->context1); + if (lpfc_cmd == 0) { + continue; + } + sum++; + } + + /* Next check the txcmplq */ + list_for_each_entry_safe(iocb, next_iocb, &pring->txcmplq, list) { + cmd = &iocb->iocb; + + /* Must be a FCP command */ + if ((cmd->ulpCommand != CMD_FCP_ICMND64_CR) && + (cmd->ulpCommand != CMD_FCP_IWRITE64_CR) && + (cmd->ulpCommand != CMD_FCP_IREAD64_CR)) { + continue; + } + + /* context1 MUST be a struct lpfc_scsi_buf */ + lpfc_cmd = (struct lpfc_scsi_buf *) (iocb->context1); + if (lpfc_cmd == 0) { + continue; + } + sum++; + } + return (sum); +} + +int +lpfc_sli_abort_iocb_host(struct lpfc_hba * phba, + struct lpfc_sli_ring * pring, int flag) +{ + struct lpfc_sli *psli; + struct lpfc_iocbq *iocb, *next_iocb; + struct lpfc_iocbq *abtsiocbp; + IOCB_t *icmd = NULL, *cmd = NULL; + struct lpfc_scsi_buf *lpfc_cmd; + int errcnt; + + psli = &phba->sli; + errcnt = 0; + + /* Error matching iocb on txq or txcmplq + * First check the txq. + */ + if(flag & LPFC_ABORT_TXQ) { + list_for_each_entry_safe(iocb, next_iocb, &pring->txq, list) { + cmd = &iocb->iocb; + + /* Must be a FCP command */ + if ((cmd->ulpCommand != CMD_FCP_ICMND64_CR) && + (cmd->ulpCommand != CMD_FCP_IWRITE64_CR) && + (cmd->ulpCommand != CMD_FCP_IREAD64_CR)) { + continue; + } + + /* context1 MUST be a struct lpfc_scsi_buf */ + lpfc_cmd = (struct lpfc_scsi_buf *) (iocb->context1); + if (lpfc_cmd == 0) { + continue; + } + + list_del_init(&iocb->list); + pring->txq_cnt--; + if (iocb->iocb_cmpl) { + icmd = &iocb->iocb; + icmd->ulpStatus = IOSTAT_LOCAL_REJECT; + icmd->un.ulpWord[4] = IOERR_SLI_ABORTED; + (iocb->iocb_cmpl) (phba, iocb, iocb); + } else { + mempool_free( iocb, phba->iocb_mem_pool); + } + } + } + + if(flag & LPFC_ABORT_TXCMPLQ) { + /* Next check the txcmplq */ + list_for_each_entry_safe(iocb, next_iocb, &pring->txcmplq, + list) { + cmd = &iocb->iocb; + + /* Must be a FCP command */ + if ((cmd->ulpCommand != CMD_FCP_ICMND64_CR) && + (cmd->ulpCommand != CMD_FCP_IWRITE64_CR) && + (cmd->ulpCommand != CMD_FCP_IREAD64_CR)) { + continue; + } + + /* context1 MUST be a struct lpfc_scsi_buf */ + lpfc_cmd = (struct lpfc_scsi_buf *) (iocb->context1); + if (lpfc_cmd == 0) { + continue; + } + + /* issue ABTS for this IOCB based on iotag */ + if ((abtsiocbp = mempool_alloc(phba->iocb_mem_pool, + GFP_ATOMIC)) == 0) { + errcnt++; + continue; + } + memset(abtsiocbp, 0, sizeof (struct lpfc_iocbq)); + icmd = &abtsiocbp->iocb; + + icmd->un.acxri.abortType = ABORT_TYPE_ABTS; + icmd->un.acxri.abortContextTag = cmd->ulpContext; + icmd->un.acxri.abortIoTag = cmd->ulpIoTag; + + icmd->ulpLe = 1; + icmd->ulpClass = cmd->ulpClass; + if (phba->hba_state >= LPFC_LINK_UP) { + icmd->ulpCommand = CMD_ABORT_XRI_CN; + } else { + icmd->ulpCommand = CMD_CLOSE_XRI_CN; + } + + if (lpfc_sli_issue_iocb(phba, pring, abtsiocbp, 0) == + IOCB_ERROR) { + mempool_free( abtsiocbp, phba->iocb_mem_pool); + errcnt++; + continue; + } + /* The rsp ring completion will remove IOCB from + * tacmplq when abort is read by HBA. + */ + } + } + return (errcnt); +} + +int +lpfc_sli_sum_iocb_lun(struct lpfc_hba * phba, + struct lpfc_sli_ring * pring, + uint16_t scsi_target, uint64_t scsi_lun) +{ + struct lpfc_sli *psli; + struct lpfc_iocbq *iocb, *next_iocb; + IOCB_t *cmd = NULL; + struct lpfc_scsi_buf *lpfc_cmd; + int sum; + + psli = &phba->sli; + sum = 0; + + /* Error matching iocb on txq or txcmplq + * First check the txq. + */ + list_for_each_entry_safe(iocb, next_iocb, &pring->txq, list) { + cmd = &iocb->iocb; + + /* Must be a FCP command */ + if ((cmd->ulpCommand != CMD_FCP_ICMND64_CR) && + (cmd->ulpCommand != CMD_FCP_IWRITE64_CR) && + (cmd->ulpCommand != CMD_FCP_IREAD64_CR)) { + continue; + } + + /* context1 MUST be a struct lpfc_scsi_buf */ + lpfc_cmd = (struct lpfc_scsi_buf *) (iocb->context1); + if ((lpfc_cmd == 0) || + (lpfc_cmd->pCmd->device->id != scsi_target) || + (lpfc_cmd->pCmd->device->lun != scsi_lun)) { + continue; + } + sum++; + } + + /* Next check the txcmplq */ + list_for_each_entry_safe(iocb, next_iocb, &pring->txcmplq, list) { + cmd = &iocb->iocb; + + /* Must be a FCP command */ + if ((cmd->ulpCommand != CMD_FCP_ICMND64_CR) && + (cmd->ulpCommand != CMD_FCP_IWRITE64_CR) && + (cmd->ulpCommand != CMD_FCP_IREAD64_CR)) { + continue; + } + + /* context1 MUST be a struct lpfc_scsi_buf */ + lpfc_cmd = (struct lpfc_scsi_buf *) (iocb->context1); + if ((lpfc_cmd == 0) || + (lpfc_cmd->pCmd->device->id != scsi_target) || + (lpfc_cmd->pCmd->device->lun != scsi_lun)) { + continue; + } + + sum++; + } + return (sum); +} + +int +lpfc_sli_abort_iocb_lun(struct lpfc_hba * phba, + struct lpfc_sli_ring * pring, + uint16_t scsi_target, uint64_t scsi_lun, int flag) +{ + struct lpfc_sli *psli; + struct lpfc_iocbq *iocb, *next_iocb; + struct lpfc_iocbq *abtsiocbp; + IOCB_t *icmd = NULL, *cmd = NULL; + struct lpfc_scsi_buf *lpfc_cmd; + int errcnt; + + psli = &phba->sli; + errcnt = 0; + + /* Error matching iocb on txq or txcmplq + * First check the txq. + */ + if(flag & LPFC_ABORT_TXQ) { + list_for_each_entry_safe(iocb, next_iocb, &pring->txq, list) { + cmd = &iocb->iocb; + + /* Must be a FCP command */ + if ((cmd->ulpCommand != CMD_FCP_ICMND64_CR) && + (cmd->ulpCommand != CMD_FCP_IWRITE64_CR) && + (cmd->ulpCommand != CMD_FCP_IREAD64_CR)) { + continue; + } + + /* context1 MUST be a struct lpfc_scsi_buf */ + lpfc_cmd = (struct lpfc_scsi_buf *) (iocb->context1); + if ((lpfc_cmd == 0) || + (lpfc_cmd->pCmd->device->id != scsi_target) || + (lpfc_cmd->pCmd->device->lun != scsi_lun)) { + continue; + } + + list_del_init(&iocb->list); + pring->txq_cnt--; + if (iocb->iocb_cmpl) { + icmd = &iocb->iocb; + icmd->ulpStatus = IOSTAT_LOCAL_REJECT; + icmd->un.ulpWord[4] = IOERR_SLI_ABORTED; + (iocb->iocb_cmpl) (phba, iocb, iocb); + } else { + mempool_free( iocb, phba->iocb_mem_pool); + } + } + } + + if(flag & LPFC_ABORT_TXCMPLQ) { + /* Next check the txcmplq */ + list_for_each_entry_safe(iocb, next_iocb, &pring->txcmplq, + list) { + cmd = &iocb->iocb; + + /* Must be a FCP command */ + if ((cmd->ulpCommand != CMD_FCP_ICMND64_CR) && + (cmd->ulpCommand != CMD_FCP_IWRITE64_CR) && + (cmd->ulpCommand != CMD_FCP_IREAD64_CR)) { + continue; + } + + /* context1 MUST be a struct lpfc_scsi_buf */ + lpfc_cmd = (struct lpfc_scsi_buf *) (iocb->context1); + if ((lpfc_cmd == 0) || + (lpfc_cmd->pCmd->device->id != scsi_target) || + (lpfc_cmd->pCmd->device->lun != scsi_lun)) { + continue; + } + + /* issue ABTS for this IOCB based on iotag */ + if ((abtsiocbp = mempool_alloc(phba->iocb_mem_pool, + GFP_ATOMIC)) == 0) { + errcnt++; + continue; + } + memset(abtsiocbp, 0, sizeof (struct lpfc_iocbq)); + icmd = &abtsiocbp->iocb; + + icmd->un.acxri.abortType = ABORT_TYPE_ABTS; + icmd->un.acxri.abortContextTag = cmd->ulpContext; + icmd->un.acxri.abortIoTag = cmd->ulpIoTag; + + icmd->ulpLe = 1; + icmd->ulpClass = cmd->ulpClass; + if (phba->hba_state >= LPFC_LINK_UP) { + icmd->ulpCommand = CMD_ABORT_XRI_CN; + } else { + icmd->ulpCommand = CMD_CLOSE_XRI_CN; + } + + if (lpfc_sli_issue_iocb(phba, pring, abtsiocbp, 0) == + IOCB_ERROR) { + mempool_free( abtsiocbp, phba->iocb_mem_pool); + errcnt++; + continue; + } + /* The rsp ring completion will remove IOCB from + * tacmplq when abort is read by HBA. + */ + } + } + return (errcnt); +} + +int +lpfc_sli_abort_iocb_tgt(struct lpfc_hba * phba, + struct lpfc_sli_ring * pring, + uint16_t scsi_target, int flag) +{ + struct lpfc_sli *psli; + struct lpfc_iocbq *iocb, *next_iocb; + struct lpfc_iocbq *abtsiocbp; + IOCB_t *icmd = NULL, *cmd = NULL; + struct lpfc_scsi_buf *lpfc_cmd; + int errcnt; + + psli = &phba->sli; + errcnt = 0; + + /* Error matching iocb on txq or txcmplq + * First check the txq. + */ + if(flag & LPFC_ABORT_TXQ) { + list_for_each_entry_safe(iocb, next_iocb, &pring->txq, list) { + cmd = &iocb->iocb; + + /* Must be a FCP command */ + if ((cmd->ulpCommand != CMD_FCP_ICMND64_CR) && + (cmd->ulpCommand != CMD_FCP_IWRITE64_CR) && + (cmd->ulpCommand != CMD_FCP_IREAD64_CR)) { + continue; + } + + /* context1 MUST be a struct lpfc_scsi_buf */ + lpfc_cmd = (struct lpfc_scsi_buf *) (iocb->context1); + if ((lpfc_cmd == 0) + || (lpfc_cmd->pCmd->device->id != scsi_target)) { + continue; + } + + list_del_init(&iocb->list); + pring->txq_cnt--; + if (iocb->iocb_cmpl) { + icmd = &iocb->iocb; + icmd->ulpStatus = IOSTAT_LOCAL_REJECT; + icmd->un.ulpWord[4] = IOERR_SLI_ABORTED; + (iocb->iocb_cmpl) (phba, iocb, iocb); + } else { + mempool_free( iocb, phba->iocb_mem_pool); + } + } + } + + if(flag & LPFC_ABORT_TXCMPLQ) { + /* Next check the txcmplq */ + list_for_each_entry_safe(iocb, next_iocb, &pring->txcmplq, + list) { + cmd = &iocb->iocb; + + /* Must be a FCP command */ + if ((cmd->ulpCommand != CMD_FCP_ICMND64_CR) && + (cmd->ulpCommand != CMD_FCP_IWRITE64_CR) && + (cmd->ulpCommand != CMD_FCP_IREAD64_CR)) { + continue; + } + + /* context1 MUST be a struct lpfc_scsi_buf */ + lpfc_cmd = (struct lpfc_scsi_buf *) (iocb->context1); + if ((lpfc_cmd == 0) + || (lpfc_cmd->pCmd->device->id != scsi_target)) { + continue; + } + + /* issue ABTS for this IOCB based on iotag */ + if ((abtsiocbp = mempool_alloc(phba->iocb_mem_pool, + GFP_ATOMIC)) == 0) { + errcnt++; + continue; + } + memset(abtsiocbp, 0, sizeof (struct lpfc_iocbq)); + icmd = &abtsiocbp->iocb; + + icmd->un.acxri.abortType = ABORT_TYPE_ABTS; + icmd->un.acxri.abortContextTag = cmd->ulpContext; + icmd->un.acxri.abortIoTag = cmd->ulpIoTag; + + icmd->ulpLe = 1; + icmd->ulpClass = cmd->ulpClass; + if (phba->hba_state >= LPFC_LINK_UP) { + icmd->ulpCommand = CMD_ABORT_XRI_CN; + } else { + icmd->ulpCommand = CMD_CLOSE_XRI_CN; + } + + if (lpfc_sli_issue_iocb(phba, pring, abtsiocbp, 0) == + IOCB_ERROR) { + mempool_free( abtsiocbp, phba->iocb_mem_pool); + errcnt++; + continue; + } + /* The rsp ring completion will remove IOCB from + * txcmplq when abort is read by HBA. + */ + } + } + return (errcnt); +} + + + +void +lpfc_sli_wake_iocb_high_priority(struct lpfc_hba * phba, + struct lpfc_iocbq * queue1, + struct lpfc_iocbq * queue2) +{ + if (queue1->context2 && queue2) + memcpy(queue1->context2, queue2, sizeof (struct lpfc_iocbq)); + + /* The waiter is looking for LPFC_IO_HIPRI bit to be set + as a signal to wake up */ + queue1->iocb_flag |= LPFC_IO_HIPRI; + return; +} + +int +lpfc_sli_issue_iocb_wait_high_priority(struct lpfc_hba * phba, + struct lpfc_sli_ring * pring, + struct lpfc_iocbq * piocb, + uint32_t flag, + struct lpfc_iocbq * prspiocbq, + uint32_t timeout) +{ + int j, delay_time, retval = IOCB_ERROR; + + /* The caller must left context1 empty. */ + if (piocb->context_un.hipri_wait_queue != 0) { + return IOCB_ERROR; + } + + /* + * If the caller has provided a response iocbq buffer, context2 must + * be NULL or its an error. + */ + if (prspiocbq && piocb->context2) { + return IOCB_ERROR; + } + + piocb->context2 = prspiocbq; + + /* Setup callback routine and issue the command. */ + piocb->iocb_cmpl = lpfc_sli_wake_iocb_high_priority; + retval = lpfc_sli_issue_iocb(phba, pring, piocb, + flag | SLI_IOCB_HIGH_PRIORITY); + if (retval != IOCB_SUCCESS) { + piocb->context2 = NULL; + return IOCB_ERROR; + } + + /* + * This high-priority iocb was sent out-of-band. Poll for its + * completion rather than wait for a signal. Note that the host_lock + * is held by the midlayer and must be released here to allow the + * interrupt handlers to complete the IO and signal this routine via + * the iocb_flag. + * Also, the delay_time is computed to be one second longer than + * the scsi command timeout to give the FW time to abort on + * timeout rather than the driver just giving up. Typically, + * the midlayer does not specify a time for this command so the + * driver is free to enforce its own timeout. + */ + + delay_time = ((timeout + 1) * 1000) >> 6; + retval = IOCB_ERROR; + spin_unlock_irq(phba->host->host_lock); + for (j = 0; j < 64; j++) { +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,6) + mdelay(delay_time); +#else + msleep(delay_time); +#endif + if (piocb->iocb_flag & LPFC_IO_HIPRI) { + piocb->iocb_flag &= ~LPFC_IO_HIPRI; + retval = IOCB_SUCCESS; + break; + } + } + + spin_lock_irq(phba->host->host_lock); + piocb->context2 = NULL; + return retval; +} +static void +lpfc_sli_wake_mbox_wait(struct lpfc_hba * phba, LPFC_MBOXQ_t * pmboxq) +{ + wait_queue_head_t *pdone_q; + + /* + * If pdone_q is empty, the driver thread gave up waiting and + * continued running. + */ + pdone_q = (wait_queue_head_t *) pmboxq->context1; + if (pdone_q) + wake_up_interruptible(pdone_q); + return; +} + +int +lpfc_sli_issue_mbox_wait(struct lpfc_hba * phba, LPFC_MBOXQ_t * pmboxq, + uint32_t timeout) +{ + DECLARE_WAIT_QUEUE_HEAD(done_q); + DECLARE_WAITQUEUE(wq_entry, current); + uint32_t timeleft = 0; + int retval; + + /* The caller must leave context1 empty. */ + if (pmboxq->context1 != 0) { + return (MBX_NOT_FINISHED); + } + + /* setup wake call as IOCB callback */ + pmboxq->mbox_cmpl = lpfc_sli_wake_mbox_wait; + /* setup context field to pass wait_queue pointer to wake function */ + pmboxq->context1 = &done_q; + + /* start to sleep before we wait, to avoid races */ + set_current_state(TASK_INTERRUPTIBLE); + add_wait_queue(&done_q, &wq_entry); + + /* now issue the command */ + spin_lock_irq(phba->host->host_lock); + retval = lpfc_sli_issue_mbox(phba, pmboxq, MBX_NOWAIT); + spin_unlock_irq(phba->host->host_lock); + + if (retval == MBX_BUSY || retval == MBX_SUCCESS) { + timeleft = schedule_timeout(timeout * HZ); + pmboxq->context1 = NULL; + /* if schedule_timeout returns 0, we timed out and were not + woken up */ + if (timeleft == 0) { + retval = MBX_TIMEOUT; + } else { + retval = MBX_SUCCESS; + } + } + + + set_current_state(TASK_RUNNING); + remove_wait_queue(&done_q, &wq_entry); + return retval; +} + +static void +lpfc_sli_wake_iocb_wait(struct lpfc_hba * phba, + struct lpfc_iocbq * queue1, struct lpfc_iocbq * queue2) +{ + wait_queue_head_t *pdone_q; + + queue1->iocb_flag |= LPFC_IO_WAIT; + if (queue1->context2 && queue2) + memcpy(queue1->context2, queue2, sizeof (struct lpfc_iocbq)); + + /* + * If pdone_q is empty, the waiter gave up and returned and this + * call has nothing to do. + */ + pdone_q = queue1->context_un.hipri_wait_queue; + if (pdone_q) { + wake_up(pdone_q); + } + + return; +} + +int +lpfc_sli_issue_iocb_wait(struct lpfc_hba * phba, + struct lpfc_sli_ring * pring, + struct lpfc_iocbq * piocb, + struct lpfc_iocbq * prspiocbq, uint32_t timeout) +{ + DECLARE_WAIT_QUEUE_HEAD(done_q); + DECLARE_WAITQUEUE(wq_entry, current); + uint32_t timeleft = 0; + int retval; + + /* The caller must leave context1 empty for the driver. */ + if (piocb->context_un.hipri_wait_queue != 0) + return (IOCB_ERROR); + + /* If the caller has provided a response iocbq buffer, then context2 + * is NULL or its an error. + */ + if (prspiocbq) { + if (piocb->context2) + return (IOCB_ERROR); + piocb->context2 = prspiocbq; + } + + /* setup wake call as IOCB callback */ + piocb->iocb_cmpl = lpfc_sli_wake_iocb_wait; + /* setup context field to pass wait_queue pointer to wake function */ + piocb->context_un.hipri_wait_queue = &done_q; + + /* start to sleep before we wait, to avoid races */ + set_current_state(TASK_UNINTERRUPTIBLE); + add_wait_queue(&done_q, &wq_entry); + + /* now issue the command */ + retval = lpfc_sli_issue_iocb(phba, pring, piocb, 0); + if (retval == IOCB_SUCCESS) { + /* Give up thread time and wait for the iocb to complete or for + * the alloted time to expire. + */ + timeleft = schedule_timeout(timeout * HZ); + + piocb->context_un.hipri_wait_queue = NULL; + piocb->iocb_cmpl = NULL; + if (piocb->context2 == prspiocbq) + piocb->context2 = NULL; + + /* + * Catch the error cases. A timeleft of zero is an error since + * the iocb should have completed. The iocb_flag not have value + * LPFC_IO_WAIT is also an error since the wakeup callback sets + * this flag when it runs. Handle each. + */ + if (timeleft == 0) { + printk(KERN_WARNING "lpfc driver detected iocb " + "Timeout!\n"); + retval = IOCB_TIMEDOUT; + } else if (!(piocb->iocb_flag & LPFC_IO_WAIT)) { + printk(KERN_WARNING "lpfc driver detected iocb " + "flag = 0x%X\n", piocb->iocb_flag); + retval = IOCB_TIMEDOUT; + } + } + + remove_wait_queue(&done_q, &wq_entry); + set_current_state(TASK_RUNNING); + piocb->context2 = NULL; + return retval; +} + +irqreturn_t +lpfc_intr_handler(int irq, void *dev_id, struct pt_regs * regs) +{ + struct lpfc_hba *phba; + int intr_status; + + /* + * Get the driver's phba structure from the dev_id and + * assume the HBA is not interrupting. + */ + phba = (struct lpfc_hba *) dev_id; + + if (phba) { + /* Call SLI to handle the interrupt event. */ + intr_status = lpfc_sli_intr(phba); + if (intr_status == 0) + return IRQ_HANDLED; + } + + return IRQ_NONE; + +} /* lpfc_intr_handler */ diff --git a/drivers/scsi/lpfc/lpfc_sli.h b/drivers/scsi/lpfc/lpfc_sli.h new file mode 100644 index 000000000..917a278d4 --- /dev/null +++ b/drivers/scsi/lpfc/lpfc_sli.h @@ -0,0 +1,218 @@ +/******************************************************************* + * This file is part of the Emulex Linux Device Driver for * + * Enterprise Fibre Channel Host Bus Adapters. * + * Refer to the README file included with this package for * + * driver version and adapter support. * + * Copyright (C) 2004 Emulex Corporation. * + * www.emulex.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, a copy of which * + * can be found in the file COPYING included with this package. * + *******************************************************************/ + +/* + * $Id: lpfc_sli.h 1.36 2004/11/23 16:56:58EST sf_support Exp $ + */ + +#ifndef _H_LPFC_SLI +#define _H_LPFC_SLI + +#include "lpfc_hw.h" + +/* forward declaration for LPFC_IOCB_t's use */ +struct lpfc_hba; + +/* This structure is used to handle IOCB requests / responses */ +struct lpfc_iocbq { + /* lpfc_iocbqs are used in double linked lists */ + struct list_head list; + IOCB_t iocb; /* IOCB cmd */ + uint8_t retry; /* retry counter for IOCB cmd - if needed */ + uint8_t iocb_flag; +#define LPFC_IO_POLL 1 /* Polling mode iocb */ +#define LPFC_IO_LIBDFC 2 /* libdfc iocb */ +#define LPFC_IO_WAIT 4 +#define LPFC_IO_HIPRI 8 /* High Priority Queue signal flag */ + + uint8_t abort_count; + uint8_t rsvd2; + uint32_t drvrTimeout; /* driver timeout in seconds */ + void *context1; /* caller context information */ + void *context2; /* caller context information */ + void *context3; /* caller context information */ + union { + wait_queue_head_t *hipri_wait_queue; /* High Priority Queue wait + queue */ + struct lpfc_iocbq *rsp_iocb; + struct lpfcMboxq *mbox; + } context_un; + + void (*iocb_cmpl) (struct lpfc_hba *, struct lpfc_iocbq *, + struct lpfc_iocbq *); + +}; + +#define SLI_IOCB_RET_IOCB 1 /* Return IOCB if cmd ring full */ +#define SLI_IOCB_HIGH_PRIORITY 2 /* High priority command */ + +#define IOCB_SUCCESS 0 +#define IOCB_BUSY 1 +#define IOCB_ERROR 2 +#define IOCB_TIMEDOUT 3 + +typedef struct lpfcMboxq { + /* MBOXQs are used in single linked lists */ + struct list_head list; /* ptr to next mailbox command */ + MAILBOX_t mb; /* Mailbox cmd */ + void *context1; /* caller context information */ + void *context2; /* caller context information */ + + void (*mbox_cmpl) (struct lpfc_hba *, struct lpfcMboxq *); + +} LPFC_MBOXQ_t; + +#define MBX_POLL 1 /* poll mailbox till command done, then + return */ +#define MBX_NOWAIT 2 /* issue command then return immediately */ +#define MBX_STOP_IOCB 4 /* Stop iocb processing till mbox cmds + complete */ + +#define LPFC_MAX_RING_MASK 4 /* max num of rctl/type masks allowed per + ring */ +#define LPFC_MAX_RING 4 /* max num of SLI rings used by driver */ + +/* Structure used to hold SLI ring information */ +struct lpfc_sli_ring { + uint16_t flag; /* ring flags */ +#define LPFC_DEFERRED_RING_EVENT 0x001 /* Deferred processing a ring event */ +#define LPFC_CALL_RING_AVAILABLE 0x002 /* indicates cmd was full */ +#define LPFC_STOP_IOCB_MBX 0x010 /* Stop processing IOCB cmds mbox */ +#define LPFC_STOP_IOCB_EVENT 0x020 /* Stop processing IOCB cmds event */ +#define LPFC_STOP_IOCB_MASK 0x030 /* Stop processing IOCB cmds mask */ + uint16_t abtsiotag; /* tracks next iotag to use for ABTS */ + + uint32_t local_getidx; /* last available cmd index (from cmdGetInx) */ + uint32_t next_cmdidx; /* next_cmd index */ + uint8_t rsvd; + uint8_t ringno; /* ring number */ + uint8_t rspidx; /* current index in response ring */ + uint8_t cmdidx; /* current index in command ring */ + struct lpfc_iocbq ** fast_lookup; /* array of IOCB ptrs indexed by + iotag */ + struct list_head txq; + uint16_t txq_cnt; /* current length of queue */ + uint16_t txq_max; /* max length */ + struct list_head txcmplq; + uint16_t txcmplq_cnt; /* current length of queue */ + uint16_t txcmplq_max; /* max length */ + volatile uint32_t *cmdringaddr; /* virtual address for cmd rings */ + volatile uint32_t *rspringaddr; /* virtual address for rsp rings */ + uint32_t missbufcnt; /* keep track of buffers to post */ + struct list_head postbufq; + uint16_t postbufq_cnt; /* current length of queue */ + uint16_t postbufq_max; /* max length */ + struct list_head iocb_continueq; + uint16_t iocb_continueq_cnt; /* current length of queue */ + uint16_t iocb_continueq_max; /* max length */ +}; + +typedef struct { + uint8_t profile; /* profile associated with ring */ + uint8_t rctl; /* rctl / type pair configured for ring */ + uint8_t type; /* rctl / type pair configured for ring */ + uint8_t rsvd; + /* rcv'd unsol event */ + void (*lpfc_sli_rcv_unsol_event) (struct lpfc_hba *, + struct lpfc_sli_ring *, + struct lpfc_iocbq *); +} LPFC_RING_MASK_t; + +/* Structure used for configuring rings to a specific profile or rctl / type */ +typedef struct { + LPFC_RING_MASK_t prt[LPFC_MAX_RING_MASK]; + uint32_t num_mask; /* number of mask entries in prt array */ + uint32_t iotag_ctr; /* keeps track of the next iotag to use */ + uint32_t iotag_max; /* max iotag value to use */ + uint32_t fast_iotag; /* max fastlookup based iotag */ + uint16_t numCiocb; /* number of command iocb's per ring */ + uint16_t numRiocb; /* number of rsp iocb's per ring */ + /* cmd ring available */ + void (*lpfc_sli_cmd_available) (struct lpfc_hba *, + struct lpfc_sli_ring *); +} LPFC_RING_INIT_t; + +typedef struct { + LPFC_RING_INIT_t ringinit[LPFC_MAX_RING]; /* ring initialization info */ + uint32_t num_rings; + uint32_t sli_flag; +} LPFC_SLI_INIT_t; + +/* Structure used to hold SLI statistical counters and info */ +typedef struct { + uint64_t iocbEvent[LPFC_MAX_RING]; /* IOCB event counters */ + uint64_t iocbCmd[LPFC_MAX_RING]; /* IOCB cmd issued */ + uint64_t iocbRsp[LPFC_MAX_RING]; /* IOCB rsp received */ + uint64_t iocbCmdDelay[LPFC_MAX_RING]; /* IOCB cmd ring delay */ + uint64_t iocbCmdFull[LPFC_MAX_RING]; /* IOCB cmd ring full */ + uint64_t iocbCmdEmpty[LPFC_MAX_RING]; /* IOCB cmd ring is now empty */ + uint64_t iocbRspFull[LPFC_MAX_RING]; /* IOCB rsp ring full */ + uint64_t mboxStatErr; /* Mbox cmds completed status error */ + uint64_t mboxCmd; /* Mailbox commands issued */ + uint64_t sliIntr; /* Count of Host Attention interrupts */ + uint32_t errAttnEvent; /* Error Attn event counters */ + uint32_t linkEvent; /* Link event counters */ + uint32_t mboxEvent; /* Mailbox event counters */ + uint32_t mboxBusy; /* Mailbox cmd busy */ +} LPFC_SLI_STAT_t; + +/* Structure used to hold SLI information */ +struct lpfc_sli { + LPFC_SLI_INIT_t sliinit; /* initialization info */ + /* Additional sli_flags */ +#define LPFC_SLI_MBOX_ACTIVE 0x100 /* HBA mailbox is currently active */ +#define LPFC_SLI2_ACTIVE 0x200 /* SLI2 overlay in firmware is active */ +#define LPFC_PROCESS_LA 0x400 /* Able to process link attention */ + + struct lpfc_sli_ring ring[LPFC_MAX_RING]; + int fcp_ring; /* ring used for FCP initiator commands */ + int next_ring; + + int ip_ring; /* ring used for IP network drv cmds */ + + LPFC_SLI_STAT_t slistat; /* SLI statistical info */ + struct list_head mboxq; + uint16_t mboxq_cnt; /* current length of queue */ + uint16_t mboxq_max; /* max length */ + LPFC_MBOXQ_t *mbox_active; /* active mboxq information */ + + struct timer_list mbox_tmo; /* Hold clk to timeout active mbox + cmd */ + + volatile uint32_t *MBhostaddr; /* virtual address for mbox cmds */ +}; + +/* Given a pointer to the start of the ring, and the slot number of + * the desired iocb entry, calc a pointer to that entry. + * (assume iocb entry size is 32 bytes, or 8 words) + */ +#define IOCB_ENTRY(ring,slot) ((IOCB_t *)(((char *)(ring)) + ((slot) * 32))) + +#define LPFC_SLI_ABORT_IMED 0 /* Immediate abort of IOCB, deque and + call compl routine immediately. */ +#define LPFC_MBOX_TMO 30 /* Sec tmo for outstanding mbox + command */ + +/* Flags for aborting I/Os on tx and txcmpl queues */ +#define LPFC_ABORT_TXQ 1 /* Abort I/Os on txq */ +#define LPFC_ABORT_TXCMPLQ 2 /* Abort I/Os on txcmplq */ +#define LPFC_ABORT_ALLQ 3 /* Abort I/Os both txq and txcmplq */ + +#endif /* _H_LPFC_SLI */ diff --git a/drivers/scsi/lpfc/lpfc_version.h b/drivers/scsi/lpfc/lpfc_version.h new file mode 100644 index 000000000..51c9dcee7 --- /dev/null +++ b/drivers/scsi/lpfc/lpfc_version.h @@ -0,0 +1,37 @@ +/******************************************************************* + * This file is part of the Emulex Linux Device Driver for * + * Enterprise Fibre Channel Host Bus Adapters. * + * Refer to the README file included with this package for * + * driver version and adapter support. * + * Copyright (C) 2004 Emulex Corporation. * + * www.emulex.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, a copy of which * + * can be found in the file COPYING included with this package. * + *******************************************************************/ + +/* + * $Id: lpfc_version.h 1.36 2004/12/07 14:51:53EST sf_support Exp $ + */ + +#ifndef _H_LPFC_VERSION +#define _H_LPFC_VERSION + +#define LPFC_DRIVER_VERSION "8.0.16" + +#define LPFC_DRIVER_NAME "lpfc" + +#define LPFC_MODULE_DESC "Emulex LightPulse Fibre Channel SCSI driver " \ + LPFC_DRIVER_VERSION + +#define DFC_API_VERSION "0.0.0" + +#endif diff --git a/drivers/scsi/megaraid/Kconfig.megaraid b/drivers/scsi/megaraid/Kconfig.megaraid new file mode 100644 index 000000000..97c7a7634 --- /dev/null +++ b/drivers/scsi/megaraid/Kconfig.megaraid @@ -0,0 +1,77 @@ +config MEGARAID_NEWGEN + bool "LSI Logic New Generation RAID Device Drivers" + depends on PCI && SCSI + help + LSI Logic RAID Device Drivers + +config MEGARAID_MM + tristate "LSI Logic Management Module (New Driver)" + depends on PCI && SCSI && MEGARAID_NEWGEN + help + Management Module provides ioctl, sysfs support for LSI Logic + RAID controllers. + To compile this driver as a module, choose M here: the + module will be called megaraid_mm + + +config MEGARAID_MAILBOX + tristate "LSI Logic MegaRAID Driver (New Driver)" + depends on PCI && SCSI && MEGARAID_MM + help + List of supported controllers + + OEM Product Name VID :DID :SVID:SSID + --- ------------ ---- ---- ---- ---- + Dell PERC3/QC 101E:1960:1028:0471 + Dell PERC3/DC 101E:1960:1028:0493 + Dell PERC3/SC 101E:1960:1028:0475 + Dell PERC3/Di 1028:000E:1028:0123 + Dell PERC4/SC 1000:1960:1028:0520 + Dell PERC4/DC 1000:1960:1028:0518 + Dell PERC4/QC 1000:0407:1028:0531 + Dell PERC4/Di 1028:000F:1028:014A + Dell PERC 4e/Si 1028:0013:1028:016c + Dell PERC 4e/Di 1028:0013:1028:016d + Dell PERC 4e/Di 1028:0013:1028:016e + Dell PERC 4e/Di 1028:0013:1028:016f + Dell PERC 4e/Di 1028:0013:1028:0170 + Dell PERC 4e/DC 1000:0408:1028:0002 + Dell PERC 4e/SC 1000:0408:1028:0001 + LSI MegaRAID SCSI 320-0 1000:1960:1000:A520 + LSI MegaRAID SCSI 320-1 1000:1960:1000:0520 + LSI MegaRAID SCSI 320-2 1000:1960:1000:0518 + LSI MegaRAID SCSI 320-0X 1000:0407:1000:0530 + LSI MegaRAID SCSI 320-2X 1000:0407:1000:0532 + LSI MegaRAID SCSI 320-4X 1000:0407:1000:0531 + LSI MegaRAID SCSI 320-1E 1000:0408:1000:0001 + LSI MegaRAID SCSI 320-2E 1000:0408:1000:0002 + LSI MegaRAID SATA 150-4 1000:1960:1000:4523 + LSI MegaRAID SATA 150-6 1000:1960:1000:0523 + LSI MegaRAID SATA 300-4X 1000:0409:1000:3004 + LSI MegaRAID SATA 300-8X 1000:0409:1000:3008 + INTEL RAID Controller SRCU42X 1000:0407:8086:0532 + INTEL RAID Controller SRCS16 1000:1960:8086:0523 + INTEL RAID Controller SRCU42E 1000:0408:8086:0002 + INTEL RAID Controller SRCZCRX 1000:0407:8086:0530 + INTEL RAID Controller SRCS28X 1000:0409:8086:3008 + INTEL RAID Controller SROMBU42E 1000:0408:8086:3431 + INTEL RAID Controller SROMBU42E 1000:0408:8086:3499 + INTEL RAID Controller SRCU51L 1000:1960:8086:0520 + FSC MegaRAID PCI Express ROMB 1000:0408:1734:1065 + ACER MegaRAID ROMB-2E 1000:0408:1025:004D + + To compile this driver as a module, choose M here: the + module will be called megaraid_mbox + +if MEGARAID_NEWGEN=n +config MEGARAID_LEGACY + tristate "LSI Logic Legacy MegaRAID Driver" + depends on PCI && SCSI + help + This driver supports the LSI MegaRAID 418, 428, 438, 466, 762, 490 + and 467 SCSI host adapters. This driver also support the all U320 + RAID controllers + + To compile this driver as a module, choose M here: the + module will be called megaraid +endif diff --git a/drivers/scsi/megaraid/Makefile b/drivers/scsi/megaraid/Makefile new file mode 100644 index 000000000..6dd99f275 --- /dev/null +++ b/drivers/scsi/megaraid/Makefile @@ -0,0 +1,2 @@ +obj-$(CONFIG_MEGARAID_MM) += megaraid_mm.o +obj-$(CONFIG_MEGARAID_MAILBOX) += megaraid_mbox.o diff --git a/drivers/scsi/megaraid/mbox_defs.h b/drivers/scsi/megaraid/mbox_defs.h new file mode 100644 index 000000000..3052869f5 --- /dev/null +++ b/drivers/scsi/megaraid/mbox_defs.h @@ -0,0 +1,790 @@ +/* + * + * Linux MegaRAID Unified device driver + * + * Copyright (c) 2003-2004 LSI Logic Corporation. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + * + * FILE : mbox_defs.h + * + */ +#ifndef _MRAID_MBOX_DEFS_H_ +#define _MRAID_MBOX_DEFS_H_ + +#include + +/* + * Commands and states for mailbox based controllers + */ + +#define MBOXCMD_LREAD 0x01 +#define MBOXCMD_LWRITE 0x02 +#define MBOXCMD_PASSTHRU 0x03 +#define MBOXCMD_ADPEXTINQ 0x04 +#define MBOXCMD_ADAPTERINQ 0x05 +#define MBOXCMD_LREAD64 0xA7 +#define MBOXCMD_LWRITE64 0xA8 +#define MBOXCMD_PASSTHRU64 0xC3 +#define MBOXCMD_EXTPTHRU 0xE3 + +#define MAIN_MISC_OPCODE 0xA4 +#define GET_MAX_SG_SUPPORT 0x01 +#define SUPPORT_EXT_CDB 0x16 + +#define FC_NEW_CONFIG 0xA1 +#define NC_SUBOP_PRODUCT_INFO 0x0E +#define NC_SUBOP_ENQUIRY3 0x0F +#define ENQ3_GET_SOLICITED_FULL 0x02 +#define OP_DCMD_READ_CONFIG 0x04 +#define NEW_READ_CONFIG_8LD 0x67 +#define READ_CONFIG_8LD 0x07 +#define FLUSH_ADAPTER 0x0A +#define FLUSH_SYSTEM 0xFE + +/* + * Command for random deletion of logical drives + */ +#define FC_DEL_LOGDRV 0xA4 +#define OP_SUP_DEL_LOGDRV 0x2A +#define OP_GET_LDID_MAP 0x18 +#define OP_DEL_LOGDRV 0x1C + +/* + * BIOS commands + */ +#define IS_BIOS_ENABLED 0x62 +#define GET_BIOS 0x01 +#define CHNL_CLASS 0xA9 +#define GET_CHNL_CLASS 0x00 +#define SET_CHNL_CLASS 0x01 +#define CH_RAID 0x01 +#define CH_SCSI 0x00 +#define BIOS_PVT_DATA 0x40 +#define GET_BIOS_PVT_DATA 0x00 + + +/* + * Commands to support clustering + */ +#define GET_TARGET_ID 0x7D +#define CLUSTER_OP 0x70 +#define GET_CLUSTER_MODE 0x02 +#define CLUSTER_CMD 0x6E +#define RESERVE_LD 0x01 +#define RELEASE_LD 0x02 +#define RESET_RESERVATIONS 0x03 +#define RESERVATION_STATUS 0x04 +#define RESERVE_PD 0x05 +#define RELEASE_PD 0x06 + + +/* + * Module battery status + */ +#define BATTERY_MODULE_MISSING 0x01 +#define BATTERY_LOW_VOLTAGE 0x02 +#define BATTERY_TEMP_HIGH 0x04 +#define BATTERY_PACK_MISSING 0x08 +#define BATTERY_CHARGE_MASK 0x30 +#define BATTERY_CHARGE_DONE 0x00 +#define BATTERY_CHARGE_INPROG 0x10 +#define BATTERY_CHARGE_FAIL 0x20 +#define BATTERY_CYCLES_EXCEEDED 0x40 + +/* + * Physical drive states. + */ +#define PDRV_UNCNF 0 +#define PDRV_ONLINE 3 +#define PDRV_FAILED 4 +#define PDRV_RBLD 5 +#define PDRV_HOTSPARE 6 + + +/* + * Raid logical drive states. + */ +#define RDRV_OFFLINE 0 +#define RDRV_DEGRADED 1 +#define RDRV_OPTIMAL 2 +#define RDRV_DELETED 3 + +/* + * Read, write and cache policies + */ +#define NO_READ_AHEAD 0 +#define READ_AHEAD 1 +#define ADAP_READ_AHEAD 2 +#define WRMODE_WRITE_THRU 0 +#define WRMODE_WRITE_BACK 1 +#define CACHED_IO 0 +#define DIRECT_IO 1 + +#define MAX_LOGICAL_DRIVES_8LD 8 +#define MAX_LOGICAL_DRIVES_40LD 40 +#define FC_MAX_PHYSICAL_DEVICES 256 +#define MAX_MBOX_CHANNELS 5 +#define MAX_MBOX_TARGET 15 +#define MBOX_MAX_PHYSICAL_DRIVES MAX_MBOX_CHANNELS*MAX_MBOX_TARGET +#define MAX_ROW_SIZE_40LD 32 +#define MAX_ROW_SIZE_8LD 8 +#define SPAN_DEPTH_8_SPANS 8 +#define SPAN_DEPTH_4_SPANS 4 +#define MAX_REQ_SENSE_LEN 0x20 + + + +/** + * struct mbox_t - Driver and f/w handshake structure. + * @cmd : firmware command + * @cmdid : command id + * @numsectors : number of sectors to be transferred + * @lba : Logical Block Address on LD + * @xferaddr : DMA address for data transfer + * @logdrv : logical drive number + * @numsge : number of scatter gather elements in sg list + * @resvd : reserved + * @busy : f/w busy, must wait to issue more commands. + * @numstatus : number of commands completed. + * @status : status of the commands completed + * @completed : array of completed command ids. + * @poll : poll and ack sequence + * @ack : poll and ack sequence + * + * The central handshake structure between the driver and the firmware. This + * structure must be allocated by the driver and aligned at 8-byte boundary. + */ +#define MBOX_MAX_FIRMWARE_STATUS 46 +typedef struct { + uint8_t cmd; + uint8_t cmdid; + uint16_t numsectors; + uint32_t lba; + uint32_t xferaddr; + uint8_t logdrv; + uint8_t numsge; + uint8_t resvd; + uint8_t busy; + uint8_t numstatus; + uint8_t status; + uint8_t completed[MBOX_MAX_FIRMWARE_STATUS]; + uint8_t poll; + uint8_t ack; +} __attribute__ ((packed)) mbox_t; + + +/** + * mbox64_t - 64-bit extension for the mailbox + * @segment_lo : the low 32-bits of the address of the scatter-gather list + * @segment_hi : the upper 32-bits of the address of the scatter-gather list + * @mbox : 32-bit mailbox, whose xferadder field must be set to + * 0xFFFFFFFF + * + * This is the extension of the 32-bit mailbox to be able to perform DMA + * beyond 4GB address range. + */ +typedef struct { + uint32_t xferaddr_lo; + uint32_t xferaddr_hi; + mbox_t mbox32; +} __attribute__ ((packed)) mbox64_t; + +/* + * mailbox structure used for internal commands + */ +typedef struct { + u8 cmd; + u8 cmdid; + u8 opcode; + u8 subopcode; + u32 lba; + u32 xferaddr; + u8 logdrv; + u8 rsvd[3]; + u8 numstatus; + u8 status; +} __attribute__ ((packed)) int_mbox_t; + +/** + * mraid_passthru_t - passthru structure to issue commands to physical devices + * @timeout : command timeout, 0=6sec, 1=60sec, 2=10min, 3=3hr + * @ars : set if ARS required after check condition + * @islogical : set if command meant for logical devices + * @logdrv : logical drive number if command for LD + * @channel : Channel on which physical device is located + * @target : SCSI target of the device + * @queuetag : unused + * @queueaction : unused + * @cdb : SCSI CDB + * @cdblen : length of the CDB + * @reqsenselen : amount of request sense data to be returned + * @reqsensearea : Sense information buffer + * @numsge : number of scatter-gather elements in the sg list + * @scsistatus : SCSI status of the command completed. + * @dataxferaddr : DMA data transfer address + * @dataxferlen : amount of the data to be transferred. + */ +typedef struct { + uint8_t timeout :3; + uint8_t ars :1; + uint8_t reserved :3; + uint8_t islogical :1; + uint8_t logdrv; + uint8_t channel; + uint8_t target; + uint8_t queuetag; + uint8_t queueaction; + uint8_t cdb[10]; + uint8_t cdblen; + uint8_t reqsenselen; + uint8_t reqsensearea[MAX_REQ_SENSE_LEN]; + uint8_t numsge; + uint8_t scsistatus; + uint32_t dataxferaddr; + uint32_t dataxferlen; +} __attribute__ ((packed)) mraid_passthru_t; + +typedef struct { + + uint32_t dataxferaddr_lo; + uint32_t dataxferaddr_hi; + mraid_passthru_t pthru32; + +} __attribute__ ((packed)) mega_passthru64_t; + +/** + * mraid_epassthru_t - passthru structure to issue commands to physical devices + * @timeout : command timeout, 0=6sec, 1=60sec, 2=10min, 3=3hr + * @ars : set if ARS required after check condition + * @rsvd1 : reserved field + * @cd_rom : (?) + * @rsvd2 : reserved field + * @islogical : set if command meant for logical devices + * @logdrv : logical drive number if command for LD + * @channel : Channel on which physical device is located + * @target : SCSI target of the device + * @queuetag : unused + * @queueaction : unused + * @cdblen : length of the CDB + * @rsvd3 : reserved field + * @cdb : SCSI CDB + * @numsge : number of scatter-gather elements in the sg list + * @status : SCSI status of the command completed. + * @reqsenselen : amount of request sense data to be returned + * @reqsensearea : Sense information buffer + * @rsvd4 : reserved field + * @dataxferaddr : DMA data transfer address + * @dataxferlen : amount of the data to be transferred. + */ +typedef struct { + uint8_t timeout :3; + uint8_t ars :1; + uint8_t rsvd1 :1; + uint8_t cd_rom :1; + uint8_t rsvd2 :1; + uint8_t islogical :1; + uint8_t logdrv; + uint8_t channel; + uint8_t target; + uint8_t queuetag; + uint8_t queueaction; + uint8_t cdblen; + uint8_t rsvd3; + uint8_t cdb[16]; + uint8_t numsge; + uint8_t status; + uint8_t reqsenselen; + uint8_t reqsensearea[MAX_REQ_SENSE_LEN]; + uint8_t rsvd4; + uint32_t dataxferaddr; + uint32_t dataxferlen; +} __attribute__ ((packed)) mraid_epassthru_t; + + +/** + * mraid_pinfo_t - product info, static information about the controller + * @data_size : current size in bytes (not including resvd) + * @config_signature : Current value is 0x00282008 + * @fw_version : Firmware version + * @bios_version : version of the BIOS + * @product_name : Name given to the controller + * @max_commands : Maximum concurrent commands supported + * @nchannels : Number of SCSI Channels detected + * @fc_loop_present : Number of Fibre Loops detected + * @mem_type : EDO, FPM, SDRAM etc + * @signature : + * @dram_size : In terms of MB + * @subsysid : device PCI subsystem ID + * @subsysvid : device PCI subsystem vendor ID + * @notify_counters : + * @pad1k : 135 + 889 resvd = 1024 total size + * + * This structures holds the information about the controller which is not + * expected to change dynamically. + * + * The current value of config signature is 0x00282008: + * 0x28 = MAX_LOGICAL_DRIVES, + * 0x20 = Number of stripes and + * 0x08 = Number of spans + */ +typedef struct { + uint32_t data_size; + uint32_t config_signature; + uint8_t fw_version[16]; + uint8_t bios_version[16]; + uint8_t product_name[80]; + uint8_t max_commands; + uint8_t nchannels; + uint8_t fc_loop_present; + uint8_t mem_type; + uint32_t signature; + uint16_t dram_size; + uint16_t subsysid; + uint16_t subsysvid; + uint8_t notify_counters; + uint8_t pad1k[889]; +} __attribute__ ((packed)) mraid_pinfo_t; + + +/** + * mraid_notify_t - the notification structure + * @global_counter : Any change increments this counter + * @param_counter : Indicates any params changed + * @param_id : Param modified - defined below + * @param_val : New val of last param modified + * @write_config_counter : write config occurred + * @write_config_rsvd : + * @ldrv_op_counter : Indicates ldrv op started/completed + * @ldrv_opid : ldrv num + * @ldrv_opcmd : ldrv operation - defined below + * @ldrv_opstatus : status of the operation + * @ldrv_state_counter : Indicates change of ldrv state + * @ldrv_state_id : ldrv num + * @ldrv_state_new : New state + * @ldrv_state_old : old state + * @pdrv_state_counter : Indicates change of ldrv state + * @pdrv_state_id : pdrv id + * @pdrv_state_new : New state + * @pdrv_state_old : old state + * @pdrv_fmt_counter : Indicates pdrv format started/over + * @pdrv_fmt_id : pdrv id + * @pdrv_fmt_val : format started/over + * @pdrv_fmt_rsvd : + * @targ_xfer_counter : Indicates SCSI-2 Xfer rate change + * @targ_xfer_id : pdrv Id + * @targ_xfer_val : new Xfer params of last pdrv + * @targ_xfer_rsvd : + * @fcloop_id_chg_counter : Indicates loopid changed + * @fcloopid_pdrvid : pdrv id + * @fcloop_id0 : loopid on fc loop 0 + * @fcloop_id1 : loopid on fc loop 1 + * @fcloop_state_counter : Indicates loop state changed + * @fcloop_state0 : state of fc loop 0 + * @fcloop_state1 : state of fc loop 1 + * @fcloop_state_rsvd : + */ +typedef struct { + uint32_t global_counter; + uint8_t param_counter; + uint8_t param_id; + uint16_t param_val; + uint8_t write_config_counter; + uint8_t write_config_rsvd[3]; + uint8_t ldrv_op_counter; + uint8_t ldrv_opid; + uint8_t ldrv_opcmd; + uint8_t ldrv_opstatus; + uint8_t ldrv_state_counter; + uint8_t ldrv_state_id; + uint8_t ldrv_state_new; + uint8_t ldrv_state_old; + uint8_t pdrv_state_counter; + uint8_t pdrv_state_id; + uint8_t pdrv_state_new; + uint8_t pdrv_state_old; + uint8_t pdrv_fmt_counter; + uint8_t pdrv_fmt_id; + uint8_t pdrv_fmt_val; + uint8_t pdrv_fmt_rsvd; + uint8_t targ_xfer_counter; + uint8_t targ_xfer_id; + uint8_t targ_xfer_val; + uint8_t targ_xfer_rsvd; + uint8_t fcloop_id_chg_counter; + uint8_t fcloopid_pdrvid; + uint8_t fcloop_id0; + uint8_t fcloop_id1; + uint8_t fcloop_state_counter; + uint8_t fcloop_state0; + uint8_t fcloop_state1; + uint8_t fcloop_state_rsvd; +} __attribute__ ((packed)) mraid_notify_t; + + +/** + * mraid_inquiry3_t - enquiry for device information + * + * @data_size : current size in bytes (not including resvd) + * @notify : + * @notify_rsvd : + * @rebuild_rate : rebuild rate (0% - 100%) + * @cache_flush_int : cache flush interval in seconds + * @sense_alert : + * @drive_insert_count : drive insertion count + * @battery_status : + * @num_ldrv : no. of Log Drives configured + * @recon_state : state of reconstruct + * @ldrv_op_status : logdrv Status + * @ldrv_size : size of each log drv + * @ldrv_prop : + * @ldrv_state : state of log drives + * @pdrv_state : state of phys drvs. + * @pdrv_format : + * @targ_xfer : phys device transfer rate + * @pad1k : 761 + 263reserved = 1024 bytes total size + */ +#define MAX_NOTIFY_SIZE 0x80 +#define CUR_NOTIFY_SIZE sizeof(mraid_notify_t) + +typedef struct { + uint32_t data_size; + + mraid_notify_t notify; + + uint8_t notify_rsvd[MAX_NOTIFY_SIZE - CUR_NOTIFY_SIZE]; + + uint8_t rebuild_rate; + uint8_t cache_flush_int; + uint8_t sense_alert; + uint8_t drive_insert_count; + + uint8_t battery_status; + uint8_t num_ldrv; + uint8_t recon_state[MAX_LOGICAL_DRIVES_40LD / 8]; + uint16_t ldrv_op_status[MAX_LOGICAL_DRIVES_40LD / 8]; + + uint32_t ldrv_size[MAX_LOGICAL_DRIVES_40LD]; + uint8_t ldrv_prop[MAX_LOGICAL_DRIVES_40LD]; + uint8_t ldrv_state[MAX_LOGICAL_DRIVES_40LD]; + uint8_t pdrv_state[FC_MAX_PHYSICAL_DEVICES]; + uint16_t pdrv_format[FC_MAX_PHYSICAL_DEVICES / 16]; + + uint8_t targ_xfer[80]; + uint8_t pad1k[263]; +} __attribute__ ((packed)) mraid_inquiry3_t; + + +/** + * mraid_adapinfo_t - information about the adapter + * @max_commands : max concurrent commands supported + * @rebuild_rate : rebuild rate - 0% thru 100% + * @max_targ_per_chan : max targ per channel + * @nchannels : number of channels on HBA + * @fw_version : firmware version + * @age_of_flash : number of times FW has been flashed + * @chip_set_value : contents of 0xC0000832 + * @dram_size : in MB + * @cache_flush_interval : in seconds + * @bios_version : + * @board_type : + * @sense_alert : + * @write_config_count : increase with every configuration change + * @drive_inserted_count : increase with every drive inserted + * @inserted_drive : channel:Id of inserted drive + * @battery_status : bit 0: battery module missing + * bit 1: VBAD + * bit 2: temprature high + * bit 3: battery pack missing + * bit 4,5: + * 00 - charge complete + * 01 - fast charge in progress + * 10 - fast charge fail + * 11 - undefined + * bit 6: counter > 1000 + * bit 7: Undefined + * @dec_fault_bus_info : + */ +typedef struct { + uint8_t max_commands; + uint8_t rebuild_rate; + uint8_t max_targ_per_chan; + uint8_t nchannels; + uint8_t fw_version[4]; + uint16_t age_of_flash; + uint8_t chip_set_value; + uint8_t dram_size; + uint8_t cache_flush_interval; + uint8_t bios_version[4]; + uint8_t board_type; + uint8_t sense_alert; + uint8_t write_config_count; + uint8_t battery_status; + uint8_t dec_fault_bus_info; +} __attribute__ ((packed)) mraid_adapinfo_t; + + +/** + * mraid_ldrv_info_t - information about the logical drives + * @nldrv : Number of logical drives configured + * @rsvd : + * @size : size of each logical drive + * @prop : + * @state : state of each logical drive + */ +typedef struct { + uint8_t nldrv; + uint8_t rsvd[3]; + uint32_t size[MAX_LOGICAL_DRIVES_8LD]; + uint8_t prop[MAX_LOGICAL_DRIVES_8LD]; + uint8_t state[MAX_LOGICAL_DRIVES_8LD]; +} __attribute__ ((packed)) mraid_ldrv_info_t; + + +/** + * mraid_pdrv_info_t - information about the physical drives + * @pdrv_state : state of each physical drive + */ +typedef struct { + uint8_t pdrv_state[MBOX_MAX_PHYSICAL_DRIVES]; + uint8_t rsvd; +} __attribute__ ((packed)) mraid_pdrv_info_t; + + +/** + * mraid_inquiry_t - RAID inquiry, mailbox command 0x05 + * @mraid_adapinfo_t : adapter information + * @mraid_ldrv_info_t : logical drives information + * @mraid_pdrv_info_t : physical drives information + */ +typedef struct { + mraid_adapinfo_t adapter_info; + mraid_ldrv_info_t logdrv_info; + mraid_pdrv_info_t pdrv_info; +} __attribute__ ((packed)) mraid_inquiry_t; + + +/** + * mraid_extinq_t - RAID extended inquiry, mailbox command 0x04 + * + * @raid_inq : raid inquiry + * @phys_drv_format : + * @stack_attn : + * @modem_status : + * @rsvd : + */ +typedef struct { + mraid_inquiry_t raid_inq; + uint16_t phys_drv_format[MAX_MBOX_CHANNELS]; + uint8_t stack_attn; + uint8_t modem_status; + uint8_t rsvd[2]; +} __attribute__ ((packed)) mraid_extinq_t; + + +/** + * adap_device_t - device information + * @channel : channel fpor the device + * @target : target ID of the device + */ +typedef struct { + uint8_t channel; + uint8_t target; +}__attribute__ ((packed)) adap_device_t; + + +/** + * adap_span_40ld_t - 40LD span + * @start_blk : starting block + * @num_blks : number of blocks + */ +typedef struct { + uint32_t start_blk; + uint32_t num_blks; + adap_device_t device[MAX_ROW_SIZE_40LD]; +}__attribute__ ((packed)) adap_span_40ld_t; + + +/** + * adap_span_8ld_t - 8LD span + * @start_blk : starting block + * @num_blks : number of blocks + */ +typedef struct { + uint32_t start_blk; + uint32_t num_blks; + adap_device_t device[MAX_ROW_SIZE_8LD]; +}__attribute__ ((packed)) adap_span_8ld_t; + + +/** + * logdrv_param_t - logical drives parameters + * + * @span_depth : total number of spans + * @level : RAID level + * @read_ahead : read ahead, no read ahead, adaptive read ahead + * @stripe_sz : encoded stripe size + * @status : status of the logical drive + * @write_mode : write mode, write_through/write_back + * @direct_io : direct io or through cache + * @row_size : number of stripes in a row + */ +typedef struct { + uint8_t span_depth; + uint8_t level; + uint8_t read_ahead; + uint8_t stripe_sz; + uint8_t status; + uint8_t write_mode; + uint8_t direct_io; + uint8_t row_size; +} __attribute__ ((packed)) logdrv_param_t; + + +/** + * logdrv_40ld_t - logical drive definition for 40LD controllers + * @lparam : logical drives parameters + * @span : span + */ +typedef struct { + logdrv_param_t lparam; + adap_span_40ld_t span[SPAN_DEPTH_8_SPANS]; +}__attribute__ ((packed)) logdrv_40ld_t; + + +/** + * logdrv_8ld_span8_t - logical drive definition for 8LD controllers + * @lparam : logical drives parameters + * @span : span + * + * 8-LD logical drive with upto 8 spans + */ +typedef struct { + logdrv_param_t lparam; + adap_span_8ld_t span[SPAN_DEPTH_8_SPANS]; +}__attribute__ ((packed)) logdrv_8ld_span8_t; + + +/** + * logdrv_8ld_span4_t - logical drive definition for 8LD controllers + * @lparam : logical drives parameters + * @span : span + * + * 8-LD logical drive with upto 4 spans + */ +typedef struct { + logdrv_param_t lparam; + adap_span_8ld_t span[SPAN_DEPTH_4_SPANS]; +}__attribute__ ((packed)) logdrv_8ld_span4_t; + + +/** + * phys_drive_t - physical device information + * @type : Type of the device + * @cur_status : current status of the device + * @tag_depth : Level of tagging + * @sync_neg : sync negotiation - ENABLE or DISBALE + * @size : configurable size in terms of 512 byte + */ +typedef struct { + uint8_t type; + uint8_t cur_status; + uint8_t tag_depth; + uint8_t sync_neg; + uint32_t size; +}__attribute__ ((packed)) phys_drive_t; + + +/** + * disk_array_40ld_t - disk array for 40LD controllers + * @numldrv : number of logical drives + * @resvd : + * @ldrv : logical drives information + * @pdrv : physical drives information + */ +typedef struct { + uint8_t numldrv; + uint8_t resvd[3]; + logdrv_40ld_t ldrv[MAX_LOGICAL_DRIVES_40LD]; + phys_drive_t pdrv[MBOX_MAX_PHYSICAL_DRIVES]; +}__attribute__ ((packed)) disk_array_40ld_t; + + +/** + * disk_array_8ld_span8_t - disk array for 8LD controllers + * @numldrv : number of logical drives + * @resvd : + * @ldrv : logical drives information + * @pdrv : physical drives information + * + * Disk array for 8LD logical drives with upto 8 spans + */ +typedef struct { + uint8_t numldrv; + uint8_t resvd[3]; + logdrv_8ld_span8_t ldrv[MAX_LOGICAL_DRIVES_8LD]; + phys_drive_t pdrv[MBOX_MAX_PHYSICAL_DRIVES]; +}__attribute__ ((packed)) disk_array_8ld_span8_t; + + +/** + * disk_array_8ld_span4_t - disk array for 8LD controllers + * @numldrv : number of logical drives + * @resvd : + * @ldrv : logical drives information + * @pdrv : physical drives information + * + * Disk array for 8LD logical drives with upto 4 spans + */ +typedef struct { + uint8_t numldrv; + uint8_t resvd[3]; + logdrv_8ld_span4_t ldrv[MAX_LOGICAL_DRIVES_8LD]; + phys_drive_t pdrv[MBOX_MAX_PHYSICAL_DRIVES]; +}__attribute__ ((packed)) disk_array_8ld_span4_t; + + +/** + * private_bios_data - bios private data for boot devices + * @geometry : bits 0-3 - BIOS geometry, 0x0001 - 1GB, 0x0010 - 2GB, + * 0x1000 - 8GB, Others values are invalid + * @unused : bits 4-7 are unused + * @boot_drv : logical drive set as boot drive, 0..7 - for 8LD cards, + * 0..39 - for 40LD cards + * @cksum : 0-(sum of first 13 bytes of this structure) + */ +struct private_bios_data { + uint8_t geometry :4; + uint8_t unused :4; + uint8_t boot_drv; + uint8_t rsvd[12]; + uint16_t cksum; +} __attribute__ ((packed)); + + +/** + * mbox_sgl64 - 64-bit scatter list for mailbox based controllers + * @address : address of the buffer + * @length : data transfer length + */ +typedef struct { + uint64_t address; + uint32_t length; +} __attribute__ ((packed)) mbox_sgl64; + +/** + * mbox_sgl32 - 32-bit scatter list for mailbox based controllers + * @address : address of the buffer + * @length : data transfer length + */ +typedef struct { + uint32_t address; + uint32_t length; +} __attribute__ ((packed)) mbox_sgl32; + +#endif // _MRAID_MBOX_DEFS_H_ + +/* vim: set ts=8 sw=8 tw=78: */ diff --git a/drivers/scsi/megaraid/mega_common.h b/drivers/scsi/megaraid/mega_common.h new file mode 100644 index 000000000..e15117529 --- /dev/null +++ b/drivers/scsi/megaraid/mega_common.h @@ -0,0 +1,283 @@ +/* + * + * Linux MegaRAID device driver + * + * Copyright (c) 2003-2004 LSI Logic Corporation. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + * + * FILE : mega_common.h + * + * Libaray of common routine used by all low-level megaraid drivers + */ + +#ifndef _MEGA_COMMON_H_ +#define _MEGA_COMMON_H_ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +#define LSI_MAX_CHANNELS 16 +#define LSI_MAX_LOGICAL_DRIVES_64LD (64+1) + + +/** + * scb_t - scsi command control block + * @param ccb : command control block for individual driver + * @param list : list of control blocks + * @param gp : general purpose field for LLDs + * @param sno : all SCBs have a serial number + * @param scp : associated scsi command + * @param state : current state of scb + * @param dma_dir : direction of data transfer + * @param dma_type : transfer with sg list, buffer, or no data transfer + * @param dev_channel : actual channel on the device + * @param dev_target : actual target on the device + * @param status : completion status + * + * This is our central data structure to issue commands the each driver. + * Driver specific data structures are maintained in the ccb field. + * scb provides a field 'gp', which can be used by LLD for its own purposes + * + * dev_channel and dev_target must be initialized with the actual channel and + * target on the controller. + */ +typedef struct { + caddr_t ccb; + struct list_head list; + unsigned long gp; + unsigned int sno; + struct scsi_cmnd *scp; + uint32_t state; + uint32_t dma_direction; + uint32_t dma_type; + uint16_t dev_channel; + uint16_t dev_target; + uint32_t status; +} scb_t; + +/* + * SCB states as it transitions from one state to another + */ +#define SCB_FREE 0x0000 /* on the free list */ +#define SCB_ACTIVE 0x0001 /* off the free list */ +#define SCB_PENDQ 0x0002 /* on the pending queue */ +#define SCB_ISSUED 0x0004 /* issued - owner f/w */ +#define SCB_ABORT 0x0008 /* Got an abort for this one */ +#define SCB_RESET 0x0010 /* Got a reset for this one */ + +/* + * DMA types for scb + */ +#define MRAID_DMA_NONE 0x0000 /* no data transfer for this command */ +#define MRAID_DMA_WSG 0x0001 /* data transfer using a sg list */ +#define MRAID_DMA_WBUF 0x0002 /* data transfer using a contiguous buffer */ + + +/** + * struct adapter_t - driver's initialization structure + * @param dpc_h : tasklet handle + * @param pdev : pci configuration pointer for kernel + * @param host : pointer to host structure of mid-layer + * @param host_lock : pointer to appropriate lock + * @param lock : synchronization lock for mid-layer and driver + * @param quiescent : driver is quiescent for now. + * @param outstanding_cmds : number of commands pending in the driver + * @param kscb_list : pointer to the bulk of SCBs pointers for IO + * @param kscb_pool : pool of free scbs for IO + * @param kscb_pool_lock : lock for pool of free scbs + * @param pend_list : pending commands list + * @param pend_list_lock : exlusion lock for pending commands list + * @param completed_list : list of completed commands + * @param completed_list_lock : exclusion lock for list of completed commands + * @param sglen : max sg elements supported + * @param device_ids : to convert kernel device addr to our devices. + * @param raid_device : raid adapter specific pointer + * @param max_channel : maximum channel number supported - inclusive + * @param max_target : max target supported - inclusive + * @param max_lun : max lun supported - inclusive + * @param unique_id : unique identifier for each adapter + * @param irq : IRQ for this adapter + * @param ito : internal timeout value, (-1) means no timeout + * @param ibuf : buffer to issue internal commands + * @param ibuf_dma_h : dma handle for the above buffer + * @param uscb_list : SCB pointers for user cmds, common mgmt module + * @param uscb_pool : pool of SCBs for user commands + * @param uscb_pool_lock : exclusion lock for these SCBs + * @param max_cmds : max outstanding commands + * @param fw_version : firmware version + * @param bios_version : bios version + * @param max_cdb_sz : biggest CDB size supported. + * @param ha : is high availability present - clustering + * @param init_id : initiator ID, the default value should be 7 + * @param max_sectors : max sectors per request + * @param cmd_per_lun : max outstanding commands per LUN + * @param being_detached : set when unloading, no more mgmt calls + * + * + * mraid_setup_device_map() can be called anytime after the device map is + * available and MRAID_GET_DEVICE_MAP() can be called whenever the mapping is + * required, usually from LLD's queue entry point. The formar API sets up the + * MRAID_IS_LOGICAL(adapter_t *, struct scsi_cmnd *) to find out if the + * device in question is a logical drive. + * + * quiescent flag should be set by the driver if it is not accepting more + * commands + * + * NOTE: The fields of this structures are placed to minimize cache misses + */ + +// amount of space required to store the bios and firmware version strings +#define VERSION_SIZE 16 + +typedef struct { + struct tasklet_struct dpc_h; + struct pci_dev *pdev; + struct Scsi_Host *host; + spinlock_t *host_lock; + spinlock_t lock; + uint8_t quiescent; + int outstanding_cmds; + scb_t *kscb_list; + struct list_head kscb_pool; + spinlock_t kscb_pool_lock; + struct list_head pend_list; + spinlock_t pend_list_lock; + struct list_head completed_list; + spinlock_t completed_list_lock; + uint16_t sglen; + int device_ids[LSI_MAX_CHANNELS] + [LSI_MAX_LOGICAL_DRIVES_64LD]; + caddr_t raid_device; + uint8_t max_channel; + uint16_t max_target; + uint8_t max_lun; + + uint32_t unique_id; + uint8_t irq; + uint8_t ito; + caddr_t ibuf; + dma_addr_t ibuf_dma_h; + scb_t *uscb_list; + struct list_head uscb_pool; + spinlock_t uscb_pool_lock; + int max_cmds; + uint8_t fw_version[VERSION_SIZE]; + uint8_t bios_version[VERSION_SIZE]; + uint8_t max_cdb_sz; + uint8_t ha; + uint16_t init_id; + uint16_t max_sectors; + uint16_t cmd_per_lun; + atomic_t being_detached; +} adapter_t; + +#define SCSI_FREE_LIST_LOCK(adapter) (&adapter->kscb_pool_lock) +#define USER_FREE_LIST_LOCK(adapter) (&adapter->uscb_pool_lock) +#define PENDING_LIST_LOCK(adapter) (&adapter->pend_list_lock) +#define COMPLETED_LIST_LOCK(adapter) (&adapter->completed_list_lock) + + +// conversion from scsi command +#define SCP2HOST(scp) (scp)->device->host // to host +#define SCP2HOSTDATA(scp) SCP2HOST(scp)->hostdata // to soft state +#define SCP2CHANNEL(scp) (scp)->device->channel // to channel +#define SCP2TARGET(scp) (scp)->device->id // to target +#define SCP2LUN(scp) (scp)->device->lun // to LUN + +// generic macro to convert scsi command and host to controller's soft state +#define SCSIHOST2ADAP(host) (((caddr_t *)(host->hostdata))[0]) +#define SCP2ADAPTER(scp) (adapter_t *)SCSIHOST2ADAP(SCP2HOST(scp)) + + +/** + * MRAID_GET_DEVICE_MAP - device ids + * @param adp - Adapter's soft state + * @param scp - mid-layer scsi command pointer + * @param p_chan - physical channel on the controller + * @param target - target id of the device or logical drive number + * @param islogical - set if the command is for the logical drive + * + * Macro to retrieve information about device class, logical or physical and + * the corresponding physical channel and target or logical drive number + **/ +#define MRAID_IS_LOGICAL(adp, scp) \ + (SCP2CHANNEL(scp) == (adp)->max_channel) ? 1 : 0 + +#define MRAID_GET_DEVICE_MAP(adp, scp, p_chan, target, islogical) \ + /* \ + * Is the request coming for the virtual channel \ + */ \ + islogical = MRAID_IS_LOGICAL(adp, scp); \ + \ + /* \ + * Get an index into our table of drive ids mapping \ + */ \ + if (islogical) { \ + p_chan = 0xFF; \ + target = \ + (adp)->device_ids[(adp)->max_channel][SCP2TARGET(scp)]; \ + } \ + else { \ + p_chan = ((adp)->device_ids[SCP2CHANNEL(scp)] \ + [SCP2TARGET(scp)] >> 8) & 0xFF; \ + target = ((adp)->device_ids[SCP2CHANNEL(scp)] \ + [SCP2TARGET(scp)] & 0xFF); \ + } + +/* + * ### Helper routines ### + */ +#define LSI_DBGLVL mraid_debug_level // each LLD must define a global + // mraid_debug_level + +#ifdef DEBUG +#if defined (_ASSERT_PANIC) +#define ASSERT_ACTION panic +#else +#define ASSERT_ACTION printk +#endif + +#define ASSERT(expression) \ + if (!(expression)) { \ + ASSERT_ACTION("assertion failed:(%s), file: %s, line: %d:%s\n", \ + #expression, __FILE__, __LINE__, __FUNCTION__); \ + } +#else +#define ASSERT(expression) +#endif + +/* + * struct mraid_pci_blk - structure holds DMA memory block info + * @param vaddr : virtual address to a memory block + * @param dma_addr : DMA handle to a memory block + * + * This structure is filled up for the caller. It is the responsibilty of the + * caller to allocate this array big enough to store addresses for all + * requested elements + */ +struct mraid_pci_blk { + caddr_t vaddr; + dma_addr_t dma_addr; +}; + +#endif // _MEGA_COMMON_H_ + +// vim: set ts=8 sw=8 tw=78: diff --git a/drivers/scsi/megaraid/megaraid_ioctl.h b/drivers/scsi/megaraid/megaraid_ioctl.h new file mode 100644 index 000000000..44584ca08 --- /dev/null +++ b/drivers/scsi/megaraid/megaraid_ioctl.h @@ -0,0 +1,291 @@ +/* + * + * Linux MegaRAID device driver + * + * Copyright (c) 2003-2004 LSI Logic Corporation. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + * + * FILE : megaraid_ioctl.h + * + * Definitions to interface with user level applications + */ + +#ifndef _MEGARAID_IOCTL_H_ +#define _MEGARAID_IOCTL_H_ + +#include +#include + +#include "mbox_defs.h" + +/** + * con_log() - console log routine + * @param level : indicates the severity of the message. + * @fparam mt : format string + * + * con_log displays the error messages on the console based on the current + * debug level. Also it attaches the appropriate kernel severity level with + * the message. + * + * + * consolge messages debug levels + */ +#define CL_ANN 0 /* print unconditionally, announcements */ +#define CL_DLEVEL1 1 /* debug level 1, informative */ +#define CL_DLEVEL2 2 /* debug level 2, verbose */ +#define CL_DLEVEL3 3 /* debug level 3, very verbose */ + +#define con_log(level, fmt) if (LSI_DBGLVL >= level) printk fmt; + +/* + * Definitions & Declarations needed to use common management module + */ + +#define MEGAIOC_MAGIC 'm' +#define MEGAIOCCMD _IOWR(MEGAIOC_MAGIC, 0, mimd_t) + +#define MEGAIOC_QNADAP 'm' /* Query # of adapters */ +#define MEGAIOC_QDRVRVER 'e' /* Query driver version */ +#define MEGAIOC_QADAPINFO 'g' /* Query adapter information */ + +#define USCSICMD 0x80 +#define UIOC_RD 0x00001 +#define UIOC_WR 0x00002 + +#define MBOX_CMD 0x00000 +#define GET_DRIVER_VER 0x10000 +#define GET_N_ADAP 0x20000 +#define GET_ADAP_INFO 0x30000 +#define GET_CAP 0x40000 +#define GET_STATS 0x50000 +#define GET_IOCTL_VERSION 0x01 + +#define EXT_IOCTL_SIGN_SZ 16 +#define EXT_IOCTL_SIGN "$$_EXTD_IOCTL_$$" + +#define MBOX_LEGACY 0x00 /* ioctl has legacy mbox*/ +#define MBOX_HPE 0x01 /* ioctl has hpe mbox */ + +#define APPTYPE_MIMD 0x00 /* old existing apps */ +#define APPTYPE_UIOC 0x01 /* new apps using uioc */ + +#define IOCTL_ISSUE 0x00000001 /* Issue ioctl */ +#define IOCTL_ABORT 0x00000002 /* Abort previous ioctl */ + +#define DRVRTYPE_MBOX 0x00000001 /* regular mbox driver */ +#define DRVRTYPE_HPE 0x00000002 /* new hpe driver */ + +#define MKADAP(adapno) (MEGAIOC_MAGIC << 8 | (adapno) ) +#define GETADAP(mkadap) ((mkadap) ^ MEGAIOC_MAGIC << 8) + +#define MAX_DMA_POOLS 5 /* 4k, 8k, 16k, 32k, 64k*/ + + +/** + * struct uioc_t - the common ioctl packet structure + * + * @signature : Must be "$$_EXTD_IOCTL_$$" + * @mb_type : Type of the mail box (MB_LEGACY or MB_HPE) + * @app_type : Type of the issuing application (existing or new) + * @opcode : Opcode of the command + * @adapno : Adapter number + * @cmdbuf : Pointer to buffer - can point to mbox or plain data buffer + * @xferlen : xferlen for DCMD and non mailbox commands + * @data_dir : Direction of the data transfer + * @status : Status from the driver + * @reserved : reserved bytes for future expansion + * + * @user_data : user data transfer address is saved in this + * @user_data_len: length of the data buffer sent by user app + * @user_pthru : user passthru address is saves in this (null if DCMD) + * @pthru32 : kernel address passthru (allocated per kioc) + * @pthru32_h : physicall address of @pthru32 + * @list : for kioc free pool list maintenance + * @done : call back routine for llds to call when kioc is completed + * @buf_vaddr : dma pool buffer attached to kioc for data transfer + * @buf_paddr : physical address of the dma pool buffer + * @pool_index : index of the dma pool that @buf_vaddr is taken from + * @free_buf : indicates if buffer needs to be freed after kioc completes + * + * Note : All LSI drivers understand only this packet. Any other + * : format sent by applications would be converted to this. + */ +typedef struct uioc { + +/* User Apps: */ + + uint8_t signature[EXT_IOCTL_SIGN_SZ]; + uint16_t mb_type; + uint16_t app_type; + uint32_t opcode; + uint32_t adapno; + uint64_t cmdbuf; + uint32_t xferlen; + uint32_t data_dir; + int32_t status; + uint8_t reserved[128]; + +/* Driver Data: */ + void __user * user_data; + uint32_t user_data_len; + mraid_passthru_t __user *user_pthru; + + mraid_passthru_t *pthru32; + dma_addr_t pthru32_h; + + struct list_head list; + void (*done)(struct uioc*); + + caddr_t buf_vaddr; + dma_addr_t buf_paddr; + uint8_t pool_index; + uint8_t free_buf; + +} __attribute__ ((aligned(1024),packed)) uioc_t; + + +/** + * struct mraid_hba_info - information about the controller + * + * @param pci_vendor_id : PCI vendor id + * @param pci_device_id : PCI device id + * @param subsystem_vendor_id : PCI subsystem vendor id + * @param subsystem_device_id : PCI subsystem device id + * @param baseport : base port of hba memory + * @param pci_bus : PCI bus + * @param pci_dev_fn : PCI device/function values + * @param irq : interrupt vector for the device + * + * Extended information of 256 bytes about the controller. Align on the single + * byte boundary so that 32-bit applications can be run on 64-bit platform + * drivers withoug re-compilation. + * NOTE: reduce the number of reserved bytes whenever new field are added, so + * that total size of the structure remains 256 bytes. + */ +typedef struct mraid_hba_info { + + uint16_t pci_vendor_id; + uint16_t pci_device_id; + uint16_t subsys_vendor_id; + uint16_t subsys_device_id; + + uint64_t baseport; + uint8_t pci_bus; + uint8_t pci_dev_fn; + uint8_t pci_slot; + uint8_t irq; + + uint32_t unique_id; + uint32_t host_no; + + uint8_t num_ldrv; +} __attribute__ ((aligned(256), packed)) mraid_hba_info_t; + + +/** + * mcontroller : adapter info structure for old mimd_t apps + * + * @base : base address + * @irq : irq number + * @numldrv : number of logical drives + * @pcibus : pci bus + * @pcidev : pci device + * @pcifun : pci function + * @pciid : pci id + * @pcivendor : vendor id + * @pcislot : slot number + * @uid : unique id + */ +typedef struct mcontroller { + + uint64_t base; + uint8_t irq; + uint8_t numldrv; + uint8_t pcibus; + uint16_t pcidev; + uint8_t pcifun; + uint16_t pciid; + uint16_t pcivendor; + uint8_t pcislot; + uint32_t uid; + +} __attribute__ ((packed)) mcontroller_t; + + +/** + * mm_dmapool_t : Represents one dma pool with just one buffer + * + * @vaddr : Virtual address + * @paddr : DMA physicall address + * @bufsize : In KB - 4 = 4k, 8 = 8k etc. + * @handle : Handle to the dma pool + * @lock : lock to synchronize access to the pool + * @in_use : If pool already in use, attach new block + */ +typedef struct mm_dmapool { + caddr_t vaddr; + dma_addr_t paddr; + uint32_t buf_size; + struct dma_pool *handle; + spinlock_t lock; + uint8_t in_use; +} mm_dmapool_t; + + +/** + * mraid_mmadp_t: Structure that drivers pass during (un)registration + * + * @unique_id : Any unique id (usually PCI bus+dev+fn) + * @drvr_type : megaraid or hpe (DRVRTYPE_MBOX or DRVRTYPE_HPE) + * @drv_data : Driver specific; not touched by the common module + * @timeout : timeout for issued kiocs + * @max_kioc : Maximum ioctl packets acceptable by the lld + * @pdev : pci dev; used for allocating dma'ble memory + * @issue_uioc : Driver supplied routine to issue uioc_t commands + * : issue_uioc(drvr_data, kioc, ISSUE/ABORT, uioc_done) + * @list : attach with the global list of adapters + * @kioc_list : block of mem for @max_kioc number of kiocs + * @kioc_pool : pool of free kiocs + * @kioc_pool_lock : protection for free pool + * @kioc_semaphore : so as not to exceed @max_kioc parallel ioctls + * @mbox_list : block of mem for @max_kioc number of mboxes + * @pthru_dma_pool : DMA pool to allocate passthru packets + * @dma_pool_list : array of dma pools + */ + +typedef struct mraid_mmadp { + +/* Filled by driver */ + + uint32_t unique_id; + uint32_t drvr_type; + unsigned long drvr_data; + uint8_t timeout; + uint8_t max_kioc; + + struct pci_dev *pdev; + + int(*issue_uioc)(unsigned long, uioc_t *, uint32_t); + +/* Maintained by common module */ + + struct list_head list; + uioc_t *kioc_list; + struct list_head kioc_pool; + spinlock_t kioc_pool_lock; + struct semaphore kioc_semaphore; + + mbox64_t *mbox_list; + struct dma_pool *pthru_dma_pool; + mm_dmapool_t dma_pool_list[MAX_DMA_POOLS]; + +} mraid_mmadp_t; + +int mraid_mm_register_adp(mraid_mmadp_t *); +int mraid_mm_unregister_adp(uint32_t); + +#endif /* _MEGARAID_IOCTL_H_ */ diff --git a/drivers/scsi/megaraid/megaraid_mbox.c b/drivers/scsi/megaraid/megaraid_mbox.c new file mode 100644 index 000000000..7afd6a5f1 --- /dev/null +++ b/drivers/scsi/megaraid/megaraid_mbox.c @@ -0,0 +1,3891 @@ +/* + * + * Linux MegaRAID device driver + * + * Copyright (c) 2003-2004 LSI Logic Corporation. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + * + * FILE : megaraid_mbox.c + * Version : v2.20.4 (September 27 2004) + * + * Authors: + * Atul Mukker + * Sreenivas Bagalkote + * Manoj Jose + * + * List of supported controllers + * + * OEM Product Name VID DID SSVID SSID + * --- ------------ --- --- ---- ---- + * Dell PERC3/QC 101E 1960 1028 0471 + * Dell PERC3/DC 101E 1960 1028 0493 + * Dell PERC3/SC 101E 1960 1028 0475 + * Dell PERC3/Di 1028 1960 1028 0123 + * Dell PERC4/SC 1000 1960 1028 0520 + * Dell PERC4/DC 1000 1960 1028 0518 + * Dell PERC4/QC 1000 0407 1028 0531 + * Dell PERC4/Di 1028 000F 1028 014A + * Dell PERC 4e/Si 1028 0013 1028 016c + * Dell PERC 4e/Di 1028 0013 1028 016d + * Dell PERC 4e/Di 1028 0013 1028 016e + * Dell PERC 4e/Di 1028 0013 1028 016f + * Dell PERC 4e/Di 1028 0013 1028 0170 + * Dell PERC 4e/DC 1000 0408 1028 0002 + * Dell PERC 4e/SC 1000 0408 1028 0001 + * + * + * LSI MegaRAID SCSI 320-0 1000 1960 1000 A520 + * LSI MegaRAID SCSI 320-1 1000 1960 1000 0520 + * LSI MegaRAID SCSI 320-2 1000 1960 1000 0518 + * LSI MegaRAID SCSI 320-0X 1000 0407 1000 0530 + * LSI MegaRAID SCSI 320-2X 1000 0407 1000 0532 + * LSI MegaRAID SCSI 320-4X 1000 0407 1000 0531 + * LSI MegaRAID SCSI 320-1E 1000 0408 1000 0001 + * LSI MegaRAID SCSI 320-2E 1000 0408 1000 0002 + * LSI MegaRAID SATA 150-4 1000 1960 1000 4523 + * LSI MegaRAID SATA 150-6 1000 1960 1000 0523 + * LSI MegaRAID SATA 300-4X 1000 0409 1000 3004 + * LSI MegaRAID SATA 300-8X 1000 0409 1000 3008 + * + * INTEL RAID Controller SRCU42X 1000 0407 8086 0532 + * INTEL RAID Controller SRCS16 1000 1960 8086 0523 + * INTEL RAID Controller SRCU42E 1000 0408 8086 0002 + * INTEL RAID Controller SRCZCRX 1000 0407 8086 0530 + * INTEL RAID Controller SRCS28X 1000 0409 8086 3008 + * INTEL RAID Controller SROMBU42E 1000 0408 8086 3431 + * INTEL RAID Controller SROMBU42E 1000 0408 8086 3499 + * INTEL RAID Controller SRCU51L 1000 1960 8086 0520 + * + * + * FSC MegaRAID PCI Express ROMB 1000 0408 1734 1065 + * + * + * ACER MegaRAID ROMB-2E 1000 0408 1025 004D + * + * + * For history of changes, see Documentation/ChangeLog.megaraid + */ + +#include "megaraid_mbox.h" + +static int megaraid_init(void); +static void megaraid_exit(void); + +static int megaraid_probe_one(struct pci_dev*, const struct pci_device_id *); +static void megaraid_detach_one(struct pci_dev *); +static void megaraid_mbox_shutdown(struct device *); + +static int megaraid_io_attach(adapter_t *); +static void megaraid_io_detach(adapter_t *); + +static int megaraid_init_mbox(adapter_t *); +static void megaraid_fini_mbox(adapter_t *); + +static int megaraid_alloc_cmd_packets(adapter_t *); +static void megaraid_free_cmd_packets(adapter_t *); + +static int megaraid_mbox_setup_dma_pools(adapter_t *); +static void megaraid_mbox_teardown_dma_pools(adapter_t *); + +static int megaraid_abort_handler(struct scsi_cmnd *); +static int megaraid_reset_handler(struct scsi_cmnd *); + +static int mbox_post_sync_cmd(adapter_t *, uint8_t []); +static int mbox_post_sync_cmd_fast(adapter_t *, uint8_t []); +static int megaraid_busywait_mbox(mraid_device_t *); +static int megaraid_mbox_product_info(adapter_t *); +static int megaraid_mbox_extended_cdb(adapter_t *); +static int megaraid_mbox_support_ha(adapter_t *, uint16_t *); +static int megaraid_mbox_support_random_del(adapter_t *); +static int megaraid_mbox_get_max_sg(adapter_t *); +static void megaraid_mbox_enum_raid_scsi(adapter_t *); +static void megaraid_mbox_flush_cache(adapter_t *); + +static void megaraid_mbox_display_scb(adapter_t *, scb_t *); +static void megaraid_mbox_setup_device_map(adapter_t *); + +static int megaraid_queue_command(struct scsi_cmnd *, + void (*)(struct scsi_cmnd *)); +static scb_t *megaraid_mbox_build_cmd(adapter_t *, struct scsi_cmnd *, int *); +static void megaraid_mbox_runpendq(adapter_t *, scb_t *); +static void megaraid_mbox_prepare_pthru(adapter_t *, scb_t *, + struct scsi_cmnd *); +static void megaraid_mbox_prepare_epthru(adapter_t *, scb_t *, + struct scsi_cmnd *); + +static irqreturn_t megaraid_isr(int, void *, struct pt_regs *); + +static void megaraid_mbox_dpc(unsigned long); + +static int megaraid_cmm_register(adapter_t *); +static int megaraid_cmm_unregister(adapter_t *); +static int megaraid_mbox_mm_handler(unsigned long, uioc_t *, uint32_t); +static int megaraid_mbox_mm_command(adapter_t *, uioc_t *); +static void megaraid_mbox_mm_done(adapter_t *, scb_t *); +static int gather_hbainfo(adapter_t *, mraid_hba_info_t *); +static int wait_till_fw_empty(adapter_t *); + + + +MODULE_AUTHOR("LSI Logic Corporation"); +MODULE_DESCRIPTION("LSI Logic MegaRAID Mailbox Driver"); +MODULE_LICENSE("GPL"); +MODULE_VERSION(MEGARAID_VERSION); + +/* + * ### modules parameters for driver ### + */ + +/** + * Set to enable driver to expose unconfigured disk to kernel + */ +static int megaraid_expose_unconf_disks = 0; +module_param_named(unconf_disks, megaraid_expose_unconf_disks, int, 0); +MODULE_PARM_DESC(unconf_disks, + "Set to expose unconfigured disks to kernel (default=0)"); + +/** + * driver wait time if the adapter's mailbox is busy + */ +static unsigned int max_mbox_busy_wait = MBOX_BUSY_WAIT; +module_param_named(busy_wait, max_mbox_busy_wait, int, 0); +MODULE_PARM_DESC(busy_wait, + "Max wait for mailbox in microseconds if busy (default=10)"); + +/** + * number of sectors per IO command + */ +static unsigned int megaraid_max_sectors = MBOX_MAX_SECTORS; +module_param_named(max_sectors, megaraid_max_sectors, int, 0); +MODULE_PARM_DESC(max_sectors, + "Maximum number of sectors per IO command (default=128)"); + +/** + * number of commands per logical unit + */ +static unsigned int megaraid_cmd_per_lun = MBOX_DEF_CMD_PER_LUN; +module_param_named(cmd_per_lun, megaraid_cmd_per_lun, int, 0); +MODULE_PARM_DESC(cmd_per_lun, + "Maximum number of commands per logical unit (default=64)"); + + +/** + * Fast driver load option, skip scanning for physical devices during load. + * This would result in non-disk devices being skipped during driver load + * time. These can be later added though, using /proc/scsi/scsi + */ +static unsigned int megaraid_fast_load = 0; +module_param_named(fast_load, megaraid_fast_load, int, 0); +MODULE_PARM_DESC(fast_load, + "Faster loading of the driver, skips physical devices! (default=0)"); + + +/** + * mraid_debug level - threshold for amount of information to be displayed by + * the driver. This level can be changed through modules parameters, ioctl or + * sysfs/proc interface. By default, print the announcement messages only. + */ +int mraid_debug_level = CL_ANN; +module_param_named(debug_level, mraid_debug_level, int, 0); +MODULE_PARM_DESC(debug_level, "Debug level for driver (default=0)"); + +/* + * ### global data ### + */ +static uint8_t megaraid_mbox_version[8] = + { 0x02, 0x20, 0x04, 0x00, 9, 27, 20, 4 }; + + +/* + * PCI table for all supported controllers. + */ +static struct pci_device_id pci_id_table_g[] = { + { + PCI_VENDOR_ID_DELL, + PCI_DEVICE_ID_PERC4_DI_DISCOVERY, + PCI_VENDOR_ID_DELL, + PCI_SUBSYS_ID_PERC4_DI_DISCOVERY, + }, + { + PCI_VENDOR_ID_LSI_LOGIC, + PCI_DEVICE_ID_PERC4_SC, + PCI_VENDOR_ID_DELL, + PCI_SUBSYS_ID_PERC4_SC, + }, + { + PCI_VENDOR_ID_LSI_LOGIC, + PCI_DEVICE_ID_PERC4_DC, + PCI_VENDOR_ID_DELL, + PCI_SUBSYS_ID_PERC4_DC, + }, + { + PCI_VENDOR_ID_LSI_LOGIC, + PCI_DEVICE_ID_PERC4_QC, + PCI_VENDOR_ID_DELL, + PCI_SUBSYS_ID_PERC4_QC, + }, + { + PCI_VENDOR_ID_DELL, + PCI_DEVICE_ID_PERC4_DI_EVERGLADES, + PCI_VENDOR_ID_DELL, + PCI_SUBSYS_ID_PERC4_DI_EVERGLADES, + }, + { + PCI_VENDOR_ID_DELL, + PCI_DEVICE_ID_PERC4E_SI_BIGBEND, + PCI_VENDOR_ID_DELL, + PCI_SUBSYS_ID_PERC4E_SI_BIGBEND, + }, + { + PCI_VENDOR_ID_DELL, + PCI_DEVICE_ID_PERC4E_DI_KOBUK, + PCI_VENDOR_ID_DELL, + PCI_SUBSYS_ID_PERC4E_DI_KOBUK, + }, + { + PCI_VENDOR_ID_DELL, + PCI_DEVICE_ID_PERC4E_DI_CORVETTE, + PCI_VENDOR_ID_DELL, + PCI_SUBSYS_ID_PERC4E_DI_CORVETTE, + }, + { + PCI_VENDOR_ID_DELL, + PCI_DEVICE_ID_PERC4E_DI_EXPEDITION, + PCI_VENDOR_ID_DELL, + PCI_SUBSYS_ID_PERC4E_DI_EXPEDITION, + }, + { + PCI_VENDOR_ID_DELL, + PCI_DEVICE_ID_PERC4E_DI_GUADALUPE, + PCI_VENDOR_ID_DELL, + PCI_SUBSYS_ID_PERC4E_DI_GUADALUPE, + }, + { + PCI_VENDOR_ID_LSI_LOGIC, + PCI_DEVICE_ID_PERC4E_DC_320_2E, + PCI_VENDOR_ID_DELL, + PCI_SUBSYS_ID_PERC4E_DC_320_2E, + }, + { + PCI_VENDOR_ID_LSI_LOGIC, + PCI_DEVICE_ID_PERC4E_SC_320_1E, + PCI_VENDOR_ID_DELL, + PCI_SUBSYS_ID_PERC4E_SC_320_1E, + }, + { + PCI_VENDOR_ID_AMI, + PCI_DEVICE_ID_AMI_MEGARAID3, + PCI_VENDOR_ID_DELL, + PCI_SUBSYS_ID_PERC3_QC, + }, + { + PCI_VENDOR_ID_AMI, + PCI_DEVICE_ID_AMI_MEGARAID3, + PCI_VENDOR_ID_DELL, + PCI_SUBSYS_ID_PERC3_DC, + }, + { + PCI_VENDOR_ID_AMI, + PCI_DEVICE_ID_AMI_MEGARAID3, + PCI_VENDOR_ID_DELL, + PCI_SUBSYS_ID_PERC3_SC, + }, + { + PCI_VENDOR_ID_LSI_LOGIC, + PCI_DEVICE_ID_MEGARAID_SCSI_320_0, + PCI_VENDOR_ID_LSI_LOGIC, + PCI_SUBSYS_ID_MEGARAID_SCSI_320_0, + }, + { + PCI_VENDOR_ID_LSI_LOGIC, + PCI_DEVICE_ID_MEGARAID_SCSI_320_1, + PCI_VENDOR_ID_LSI_LOGIC, + PCI_SUBSYS_ID_MEGARAID_SCSI_320_1, + }, + { + PCI_VENDOR_ID_LSI_LOGIC, + PCI_DEVICE_ID_MEGARAID_SCSI_320_2, + PCI_VENDOR_ID_LSI_LOGIC, + PCI_SUBSYS_ID_MEGARAID_SCSI_320_2, + }, + { + PCI_VENDOR_ID_LSI_LOGIC, + PCI_DEVICE_ID_MEGARAID_SCSI_320_0x, + PCI_VENDOR_ID_LSI_LOGIC, + PCI_SUBSYS_ID_MEGARAID_SCSI_320_0x, + }, + { + PCI_VENDOR_ID_LSI_LOGIC, + PCI_DEVICE_ID_MEGARAID_SCSI_320_2x, + PCI_VENDOR_ID_LSI_LOGIC, + PCI_SUBSYS_ID_MEGARAID_SCSI_320_2x, + }, + { + PCI_VENDOR_ID_LSI_LOGIC, + PCI_DEVICE_ID_MEGARAID_SCSI_320_4x, + PCI_VENDOR_ID_LSI_LOGIC, + PCI_SUBSYS_ID_MEGARAID_SCSI_320_4x, + }, + { + PCI_VENDOR_ID_LSI_LOGIC, + PCI_DEVICE_ID_MEGARAID_SCSI_320_1E, + PCI_VENDOR_ID_LSI_LOGIC, + PCI_SUBSYS_ID_MEGARAID_SCSI_320_1E, + }, + { + PCI_VENDOR_ID_LSI_LOGIC, + PCI_DEVICE_ID_MEGARAID_SCSI_320_2E, + PCI_VENDOR_ID_LSI_LOGIC, + PCI_SUBSYS_ID_MEGARAID_SCSI_320_2E, + }, + { + PCI_VENDOR_ID_LSI_LOGIC, + PCI_DEVICE_ID_MEGARAID_I4_133_RAID, + PCI_VENDOR_ID_LSI_LOGIC, + PCI_SUBSYS_ID_MEGARAID_I4_133_RAID, + }, + { + PCI_VENDOR_ID_LSI_LOGIC, + PCI_DEVICE_ID_MEGARAID_SATA_150_4, + PCI_VENDOR_ID_LSI_LOGIC, + PCI_SUBSYS_ID_MEGARAID_SATA_150_4, + }, + { + PCI_VENDOR_ID_LSI_LOGIC, + PCI_DEVICE_ID_MEGARAID_SATA_150_6, + PCI_VENDOR_ID_LSI_LOGIC, + PCI_SUBSYS_ID_MEGARAID_SATA_150_6, + }, + { + PCI_VENDOR_ID_LSI_LOGIC, + PCI_DEVICE_ID_MEGARAID_SATA_300_4x, + PCI_VENDOR_ID_LSI_LOGIC, + PCI_SUBSYS_ID_MEGARAID_SATA_300_4x, + }, + { + PCI_VENDOR_ID_LSI_LOGIC, + PCI_DEVICE_ID_MEGARAID_SATA_300_8x, + PCI_VENDOR_ID_LSI_LOGIC, + PCI_SUBSYS_ID_MEGARAID_SATA_300_8x, + }, + { + PCI_VENDOR_ID_LSI_LOGIC, + PCI_DEVICE_ID_INTEL_RAID_SRCU42X, + PCI_VENDOR_ID_INTEL, + PCI_SUBSYS_ID_INTEL_RAID_SRCU42X, + }, + { + PCI_VENDOR_ID_LSI_LOGIC, + PCI_DEVICE_ID_INTEL_RAID_SRCS16, + PCI_VENDOR_ID_INTEL, + PCI_SUBSYS_ID_INTEL_RAID_SRCS16, + }, + { + PCI_VENDOR_ID_LSI_LOGIC, + PCI_DEVICE_ID_INTEL_RAID_SRCU42E, + PCI_VENDOR_ID_INTEL, + PCI_SUBSYS_ID_INTEL_RAID_SRCU42E, + }, + { + PCI_VENDOR_ID_LSI_LOGIC, + PCI_DEVICE_ID_INTEL_RAID_SRCZCRX, + PCI_VENDOR_ID_INTEL, + PCI_SUBSYS_ID_INTEL_RAID_SRCZCRX, + }, + { + PCI_VENDOR_ID_LSI_LOGIC, + PCI_DEVICE_ID_INTEL_RAID_SRCS28X, + PCI_VENDOR_ID_INTEL, + PCI_SUBSYS_ID_INTEL_RAID_SRCS28X, + }, + { + PCI_VENDOR_ID_LSI_LOGIC, + PCI_DEVICE_ID_INTEL_RAID_SROMBU42E_ALIEF, + PCI_VENDOR_ID_INTEL, + PCI_SUBSYS_ID_INTEL_RAID_SROMBU42E_ALIEF, + }, + { + PCI_VENDOR_ID_LSI_LOGIC, + PCI_DEVICE_ID_INTEL_RAID_SROMBU42E_HARWICH, + PCI_VENDOR_ID_INTEL, + PCI_SUBSYS_ID_INTEL_RAID_SROMBU42E_HARWICH, + }, + { + PCI_VENDOR_ID_LSI_LOGIC, + PCI_DEVICE_ID_INTEL_RAID_SRCU41L_LAKE_SHETEK, + PCI_VENDOR_ID_INTEL, + PCI_SUBSYS_ID_INTEL_RAID_SRCU41L_LAKE_SHETEK, + }, + { + PCI_VENDOR_ID_LSI_LOGIC, + PCI_DEVICE_ID_FSC_MEGARAID_PCI_EXPRESS_ROMB, + PCI_SUBSYS_ID_FSC, + PCI_SUBSYS_ID_FSC_MEGARAID_PCI_EXPRESS_ROMB, + }, + { + PCI_VENDOR_ID_LSI_LOGIC, + PCI_DEVICE_ID_MEGARAID_ACER_ROMB_2E, + PCI_VENDOR_ID_AI, + PCI_SUBSYS_ID_MEGARAID_ACER_ROMB_2E, + }, + {0} /* Terminating entry */ +}; +MODULE_DEVICE_TABLE(pci, pci_id_table_g); + + +static struct pci_driver megaraid_pci_driver_g = { + .name = "megaraid", + .id_table = pci_id_table_g, + .probe = megaraid_probe_one, + .remove = __devexit_p(megaraid_detach_one), + .driver = { + .shutdown = megaraid_mbox_shutdown, + } +}; + + +/* + * Scsi host template for megaraid unified driver + */ +static struct scsi_host_template megaraid_template_g = { + .module = THIS_MODULE, + .name = "LSI Logic MegaRAID driver", + .proc_name = "megaraid", + .queuecommand = megaraid_queue_command, + .eh_abort_handler = megaraid_abort_handler, + .eh_device_reset_handler = megaraid_reset_handler, + .eh_bus_reset_handler = megaraid_reset_handler, + .eh_host_reset_handler = megaraid_reset_handler, + .use_clustering = ENABLE_CLUSTERING, +}; + + +/** + * megaraid_init - module load hook + * + * We register ourselves as hotplug enabled module and let PCI subsystem + * discover our adaters + **/ +static int __init +megaraid_init(void) +{ + int rval; + + // Announce the driver version + con_log(CL_ANN, (KERN_INFO "megaraid: %s %s\n", MEGARAID_VERSION, + MEGARAID_EXT_VERSION)); + + // check validity of module parameters + if (megaraid_cmd_per_lun > MBOX_MAX_SCSI_CMDS) { + + con_log(CL_ANN, (KERN_WARNING + "megaraid mailbox: max commands per lun reset to %d\n", + MBOX_MAX_SCSI_CMDS)); + + megaraid_cmd_per_lun = MBOX_MAX_SCSI_CMDS; + } + + + // register as a PCI hot-plug driver module + if ((rval = pci_module_init(&megaraid_pci_driver_g))) { + con_log(CL_ANN, (KERN_WARNING + "megaraid: could not register hotplug support.\n")); + } + + return rval; +} + + +/** + * megaraid_exit - driver unload entry point + * + * We simply unwrap the megaraid_init routine here + */ +static void __exit +megaraid_exit(void) +{ + con_log(CL_DLEVEL1, (KERN_NOTICE "megaraid: unloading framework\n")); + + // unregister as PCI hotplug driver + pci_unregister_driver(&megaraid_pci_driver_g); + + return; +} + + +/** + * megaraid_probe_one - PCI hotplug entry point + * @param pdev : handle to this controller's PCI configuration space + * @param id : pci device id of the class of controllers + * + * This routine should be called whenever a new adapter is detected by the + * PCI hotplug susbsytem. + **/ +static int __devinit +megaraid_probe_one(struct pci_dev *pdev, const struct pci_device_id *id) +{ + adapter_t *adapter; + + + // detected a new controller + con_log(CL_ANN, (KERN_INFO + "megaraid: probe new device %#4.04x:%#4.04x:%#4.04x:%#4.04x: ", + pdev->vendor, pdev->device, pdev->subsystem_vendor, + pdev->subsystem_device)); + + con_log(CL_ANN, ("bus %d:slot %d:func %d\n", pdev->bus->number, + PCI_SLOT(pdev->devfn), PCI_FUNC(pdev->devfn))); + + if (pci_enable_device(pdev)) { + con_log(CL_ANN, (KERN_WARNING + "megaraid: pci_enable_device failed\n")); + + return -ENODEV; + } + + // Enable bus-mastering on this controller + pci_set_master(pdev); + + // Allocate the per driver initialization structure + adapter = kmalloc(sizeof(adapter_t), GFP_KERNEL); + + if (adapter == NULL) { + con_log(CL_ANN, (KERN_WARNING + "megaraid: out of memory, %s %d.\n", __FUNCTION__, __LINE__)); + + goto out_probe_one; + } + memset(adapter, 0, sizeof(adapter_t)); + + + // set up PCI related soft state and other pre-known parameters + adapter->unique_id = pdev->bus->number << 8 | pdev->devfn; + adapter->irq = pdev->irq; + adapter->pdev = pdev; + + atomic_set(&adapter->being_detached, 0); + + // Setup the default DMA mask. This would be changed later on + // depending on hardware capabilities + if (pci_set_dma_mask(adapter->pdev, 0xFFFFFFFF) != 0) { + + con_log(CL_ANN, (KERN_WARNING + "megaraid: pci_set_dma_mask failed:%d\n", __LINE__)); + + goto out_free_adapter; + } + + + // Initialize the synchronization lock for kernel and LLD + spin_lock_init(&adapter->lock); + adapter->host_lock = &adapter->lock; + + + // Initialize the command queues: the list of free SCBs and the list + // of pending SCBs. + INIT_LIST_HEAD(&adapter->kscb_pool); + spin_lock_init(SCSI_FREE_LIST_LOCK(adapter)); + + INIT_LIST_HEAD(&adapter->pend_list); + spin_lock_init(PENDING_LIST_LOCK(adapter)); + + INIT_LIST_HEAD(&adapter->completed_list); + spin_lock_init(COMPLETED_LIST_LOCK(adapter)); + + + // Start the mailbox based controller + if (megaraid_init_mbox(adapter) != 0) { + con_log(CL_ANN, (KERN_WARNING + "megaraid: maibox adapter did not initialize\n")); + + goto out_free_adapter; + } + + // Register with LSI Common Management Module + if (megaraid_cmm_register(adapter) != 0) { + + con_log(CL_ANN, (KERN_WARNING + "megaraid: could not register with management module\n")); + + goto out_fini_mbox; + } + + // setup adapter handle in PCI soft state + pci_set_drvdata(pdev, adapter); + + // attach with scsi mid-layer + if (megaraid_io_attach(adapter) != 0) { + + con_log(CL_ANN, (KERN_WARNING "megaraid: io attach failed\n")); + + goto out_cmm_unreg; + } + + return 0; + +out_cmm_unreg: + pci_set_drvdata(pdev, NULL); + megaraid_cmm_unregister(adapter); +out_fini_mbox: + megaraid_fini_mbox(adapter); +out_free_adapter: + kfree(adapter); +out_probe_one: + pci_disable_device(pdev); + + return -ENODEV; +} + + +/** + * megaraid_detach_one - release the framework resources and call LLD release + * routine + * @param pdev : handle for our PCI cofiguration space + * + * This routine is called during driver unload. We free all the allocated + * resources and call the corresponding LLD so that it can also release all + * its resources. + * + * This routine is also called from the PCI hotplug system + **/ +static void +megaraid_detach_one(struct pci_dev *pdev) +{ + adapter_t *adapter; + struct Scsi_Host *host; + + + // Start a rollback on this adapter + adapter = pci_get_drvdata(pdev); + + if (!adapter) { + con_log(CL_ANN, (KERN_CRIT + "megaraid: Invalid detach on %#4.04x:%#4.04x:%#4.04x:%#4.04x\n", + pdev->vendor, pdev->device, pdev->subsystem_vendor, + pdev->subsystem_device)); + + return; + } + else { + con_log(CL_ANN, (KERN_NOTICE + "megaraid: detaching device %#4.04x:%#4.04x:%#4.04x:%#4.04x\n", + pdev->vendor, pdev->device, pdev->subsystem_vendor, + pdev->subsystem_device)); + } + + + host = adapter->host; + + // do not allow any more requests from the management module for this + // adapter. + // FIXME: How do we account for the request which might still be + // pending with us? + atomic_set(&adapter->being_detached, 1); + + // detach from the IO sub-system + megaraid_io_detach(adapter); + + // reset the device state in the PCI structure. We check this + // condition when we enter here. If the device state is NULL, + // that would mean the device has already been removed + pci_set_drvdata(pdev, NULL); + + // Unregister from common management module + // + // FIXME: this must return success or failure for conditions if there + // is a command pending with LLD or not. + megaraid_cmm_unregister(adapter); + + // finalize the mailbox based controller and release all resources + megaraid_fini_mbox(adapter); + + kfree(adapter); + + scsi_host_put(host); + + pci_disable_device(pdev); + + return; +} + + +/** + * megaraid_mbox_shutdown - PCI shutdown for megaraid HBA + * @param device : generice driver model device + * + * Shutdown notification, perform flush cache + */ +static void +megaraid_mbox_shutdown(struct device *device) +{ + adapter_t *adapter = pci_get_drvdata(to_pci_dev(device)); + static int counter; + + if (!adapter) { + con_log(CL_ANN, (KERN_WARNING + "megaraid: null device in shutdown\n")); + return; + } + + // flush caches now + con_log(CL_ANN, (KERN_INFO "megaraid: flushing adapter %d...", + counter++)); + + megaraid_mbox_flush_cache(adapter); + + con_log(CL_ANN, ("done\n")); +} + + +/** + * megaraid_io_attach - attach a device with the IO subsystem + * @param adapter : controller's soft state + * + * Attach this device with the IO subsystem + **/ +static int +megaraid_io_attach(adapter_t *adapter) +{ + struct Scsi_Host *host; + + // Initialize SCSI Host structure + host = scsi_host_alloc(&megaraid_template_g, 8); + if (!host) { + con_log(CL_ANN, (KERN_WARNING + "megaraid mbox: scsi_register failed\n")); + + return -1; + } + + SCSIHOST2ADAP(host) = (caddr_t)adapter; + adapter->host = host; + + // export the parameters required by the mid-layer + scsi_assign_lock(host, adapter->host_lock); + scsi_set_device(host, &adapter->pdev->dev); + + host->irq = adapter->irq; + host->unique_id = adapter->unique_id; + host->can_queue = adapter->max_cmds; + host->this_id = adapter->init_id; + host->sg_tablesize = adapter->sglen; + host->max_sectors = adapter->max_sectors; + host->cmd_per_lun = adapter->cmd_per_lun; + host->max_channel = adapter->max_channel; + host->max_id = adapter->max_target; + host->max_lun = adapter->max_lun; + + + // notify mid-layer about the new controller + if (scsi_add_host(host, &adapter->pdev->dev)) { + + con_log(CL_ANN, (KERN_WARNING + "megaraid mbox: scsi_add_host failed\n")); + + scsi_host_put(host); + + return -1; + } + + scsi_scan_host(host); + + return 0; +} + + +/** + * megaraid_io_detach - detach a device from the IO subsystem + * @param adapter : controller's soft state + * + * Detach this device from the IO subsystem + **/ +static void +megaraid_io_detach(adapter_t *adapter) +{ + struct Scsi_Host *host; + + con_log(CL_DLEVEL1, (KERN_INFO "megaraid: io detach\n")); + + host = adapter->host; + + scsi_remove_host(host); + + return; +} + + +/* + * START: Mailbox Low Level Driver + * + * This is section specific to the single mailbox based controllers + */ + +/** + * megaraid_init_mbox - initialize controller + * @param adapter - our soft state + * + * . Allocate 16-byte aligned mailbox memory for firmware handshake + * . Allocate controller's memory resources + * . Find out all initialization data + * . Allocate memory required for all the commands + * . Use internal library of FW routines, build up complete soft state + */ +static int __init +megaraid_init_mbox(adapter_t *adapter) +{ + struct pci_dev *pdev; + mraid_device_t *raid_dev; + int i; + + + adapter->ito = MBOX_TIMEOUT; + pdev = adapter->pdev; + + /* + * Allocate and initialize the init data structure for mailbox + * controllers + */ + raid_dev = kmalloc(sizeof(mraid_device_t), GFP_KERNEL); + if (raid_dev == NULL) return -1; + + memset(raid_dev, 0, sizeof(mraid_device_t)); + + /* + * Attach the adapter soft state to raid device soft state + */ + adapter->raid_device = (caddr_t)raid_dev; + raid_dev->fast_load = megaraid_fast_load; + + + // our baseport + raid_dev->baseport = pci_resource_start(pdev, 0); + + if (pci_request_regions(pdev, "MegaRAID: LSI Logic Corporation") != 0) { + + con_log(CL_ANN, (KERN_WARNING + "megaraid: mem region busy\n")); + + goto out_free_raid_dev; + } + + raid_dev->baseaddr = (unsigned long) + ioremap_nocache(raid_dev->baseport, 128); + + if (!raid_dev->baseaddr) { + + con_log(CL_ANN, (KERN_WARNING + "megaraid: could not map hba memory\n") ); + + goto out_release_regions; + } + + // + // Setup the rest of the soft state using the library of FW routines + // + + // request IRQ and register the interrupt service routine + if (request_irq(adapter->irq, megaraid_isr, SA_SHIRQ, "megaraid", + adapter)) { + + con_log(CL_ANN, (KERN_WARNING + "megaraid: Couldn't register IRQ %d!\n", adapter->irq)); + + goto out_iounmap; + } + + + // initialize the mutual exclusion lock for the mailbox + spin_lock_init(&raid_dev->mailbox_lock); + + // allocate memory required for commands + if (megaraid_alloc_cmd_packets(adapter) != 0) { + goto out_free_irq; + } + + // Product info + if (megaraid_mbox_product_info(adapter) != 0) { + goto out_alloc_cmds; + } + + // Do we support extended CDBs + adapter->max_cdb_sz = 10; + if (megaraid_mbox_extended_cdb(adapter) == 0) { + adapter->max_cdb_sz = 16; + } + + /* + * Do we support cluster environment, if we do, what is the initiator + * id. + * NOTE: In a non-cluster aware firmware environment, the LLD should + * return 7 as initiator id. + */ + adapter->ha = 0; + adapter->init_id = -1; + if (megaraid_mbox_support_ha(adapter, &adapter->init_id) == 0) { + adapter->ha = 1; + } + + /* + * Prepare the device ids array to have the mapping between the kernel + * device address and megaraid device address. + * We export the physical devices on their actual addresses. The + * logical drives are exported on a virtual SCSI channel + */ + megaraid_mbox_setup_device_map(adapter); + + // If the firmware supports random deletion, update the device id map + if (megaraid_mbox_support_random_del(adapter)) { + + // Change the logical drives numbers in device_ids array one + // slot in device_ids is reserved for target id, that's why + // "<=" below + for (i = 0; i <= MAX_LOGICAL_DRIVES_40LD; i++) { + adapter->device_ids[adapter->max_channel][i] += 0x80; + } + adapter->device_ids[adapter->max_channel][adapter->init_id] = + 0xFF; + } + + /* + * find out the maximum number of scatter-gather elements supported by + * this firmware + */ + adapter->sglen = megaraid_mbox_get_max_sg(adapter); + + // enumerate RAID and SCSI channels so that all devices on SCSI + // channels can later be exported, including disk devices + megaraid_mbox_enum_raid_scsi(adapter); + + /* + * Other parameters required by upper layer + * + * maximum number of sectors per IO command + */ + adapter->max_sectors = megaraid_max_sectors; + + /* + * number of queued commands per LUN. + */ + adapter->cmd_per_lun = megaraid_cmd_per_lun; + + // Set the DMA mask to 64-bit. All supported controllers as capable of + // DMA in this range + if (pci_set_dma_mask(adapter->pdev, 0xFFFFFFFFFFFFFFFFULL) != 0) { + + con_log(CL_ANN, (KERN_WARNING + "megaraid: could not set DMA mask for 64-bit.\n")); + + goto out_alloc_cmds; + } + + // setup tasklet for DPC + tasklet_init(&adapter->dpc_h, megaraid_mbox_dpc, + (unsigned long)adapter); + + con_log(CL_DLEVEL1, (KERN_INFO + "megaraid mbox hba successfully initialized\n")); + + return 0; + +out_alloc_cmds: + megaraid_free_cmd_packets(adapter); +out_free_irq: + free_irq(adapter->irq, adapter); +out_iounmap: + iounmap((caddr_t)raid_dev->baseaddr); +out_release_regions: + pci_release_regions(pdev); +out_free_raid_dev: + kfree(raid_dev); + + return -1; +} + + +/** + * megaraid_fini_mbox - undo controller initialization + * @param adapter : our soft state + */ +static void +megaraid_fini_mbox(adapter_t *adapter) +{ + mraid_device_t *raid_dev = ADAP2RAIDDEV(adapter); + + // flush all caches + megaraid_mbox_flush_cache(adapter); + + tasklet_kill(&adapter->dpc_h); + + megaraid_free_cmd_packets(adapter); + + free_irq(adapter->irq, adapter); + + iounmap((caddr_t)raid_dev->baseaddr); + + pci_release_regions(adapter->pdev); + + kfree(raid_dev); + + return; +} + + +/** + * megaraid_alloc_cmd_packets - allocate shared mailbox + * @param adapter : soft state of the raid controller + * + * Allocate and align the shared mailbox. This maibox is used to issue + * all the commands. For IO based controllers, the mailbox is also regsitered + * with the FW. Allocate memory for all commands as well. + * This is our big allocator + */ +static int +megaraid_alloc_cmd_packets(adapter_t *adapter) +{ + mraid_device_t *raid_dev = ADAP2RAIDDEV(adapter); + struct pci_dev *pdev; + unsigned long align; + scb_t *scb; + mbox_ccb_t *ccb; + struct mraid_pci_blk *epthru_pci_blk; + struct mraid_pci_blk *sg_pci_blk; + struct mraid_pci_blk *mbox_pci_blk; + int i; + + pdev = adapter->pdev; + + /* + * Setup the mailbox + * Allocate the common 16-byte aligned memory for the handshake + * mailbox. + */ + raid_dev->una_mbox64 = pci_alloc_consistent(adapter->pdev, + sizeof(mbox64_t), &raid_dev->una_mbox64_dma); + + if (!raid_dev->una_mbox64) { + con_log(CL_ANN, (KERN_WARNING + "megaraid: out of memory, %s %d\n", __FUNCTION__, + __LINE__)); + return -1; + } + memset(raid_dev->una_mbox64, 0, sizeof(mbox64_t)); + + /* + * Align the mailbox at 16-byte boundary + */ + raid_dev->mbox = &raid_dev->una_mbox64->mbox32; + + raid_dev->mbox = (mbox_t *)((((unsigned long)raid_dev->mbox) + 15) & + (~0UL ^ 0xFUL)); + + raid_dev->mbox64 = (mbox64_t *)(((unsigned long)raid_dev->mbox) - 8); + + align = ((void *)raid_dev->mbox - + ((void *)&raid_dev->una_mbox64->mbox32)); + + raid_dev->mbox_dma = (unsigned long)raid_dev->una_mbox64_dma + 8 + + align; + + // Allocate memory for commands issued internally + adapter->ibuf = pci_alloc_consistent(pdev, MBOX_IBUF_SIZE, + &adapter->ibuf_dma_h); + if (!adapter->ibuf) { + + con_log(CL_ANN, (KERN_WARNING + "megaraid: out of memory, %s %d\n", __FUNCTION__, + __LINE__)); + + goto out_free_common_mbox; + } + memset(adapter->ibuf, 0, MBOX_IBUF_SIZE); + + // Allocate memory for our SCSI Command Blocks and their associated + // memory + + /* + * Allocate memory for the base list of scb. Later allocate memory for + * CCBs and embedded components of each CCB and point the pointers in + * scb to the allocated components + * NOTE: The code to allocate SCB will be duplicated in all the LLD + * since the calling routine does not yet know the number of available + * commands. + */ + adapter->kscb_list = kmalloc(sizeof(scb_t) * MBOX_MAX_SCSI_CMDS, + GFP_KERNEL); + + if (adapter->kscb_list == NULL) { + con_log(CL_ANN, (KERN_WARNING + "megaraid: out of memory, %s %d\n", __FUNCTION__, + __LINE__)); + goto out_free_ibuf; + } + memset(adapter->kscb_list, 0, sizeof(scb_t) * MBOX_MAX_SCSI_CMDS); + + // memory allocation for our command packets + if (megaraid_mbox_setup_dma_pools(adapter) != 0) { + con_log(CL_ANN, (KERN_WARNING + "megaraid: out of memory, %s %d\n", __FUNCTION__, + __LINE__)); + goto out_free_scb_list; + } + + // Adjust the scb pointers and link in the free pool + epthru_pci_blk = raid_dev->epthru_pool; + sg_pci_blk = raid_dev->sg_pool; + mbox_pci_blk = raid_dev->mbox_pool; + + for (i = 0; i < MBOX_MAX_SCSI_CMDS; i++) { + scb = adapter->kscb_list + i; + ccb = raid_dev->ccb_list + i; + + ccb->mbox = (mbox_t *)(mbox_pci_blk[i].vaddr + 16); + ccb->raw_mbox = (uint8_t *)ccb->mbox; + ccb->mbox64 = (mbox64_t *)(mbox_pci_blk[i].vaddr + 8); + ccb->mbox_dma_h = (unsigned long)mbox_pci_blk[i].dma_addr + 16; + + // make sure the mailbox is aligned properly + if (ccb->mbox_dma_h & 0x0F) { + con_log(CL_ANN, (KERN_CRIT + "megaraid mbox: not aligned on 16-bytes\n")); + + goto out_teardown_dma_pools; + } + + ccb->epthru = (mraid_epassthru_t *) + epthru_pci_blk[i].vaddr; + ccb->epthru_dma_h = epthru_pci_blk[i].dma_addr; + ccb->pthru = (mraid_passthru_t *)ccb->epthru; + ccb->pthru_dma_h = ccb->epthru_dma_h; + + + ccb->sgl64 = (mbox_sgl64 *)sg_pci_blk[i].vaddr; + ccb->sgl_dma_h = sg_pci_blk[i].dma_addr; + ccb->sgl32 = (mbox_sgl32 *)ccb->sgl64; + + scb->ccb = (caddr_t)ccb; + scb->gp = 0; + + scb->sno = i; // command index + + scb->scp = NULL; + scb->state = SCB_FREE; + scb->dma_direction = PCI_DMA_NONE; + scb->dma_type = MRAID_DMA_NONE; + scb->dev_channel = -1; + scb->dev_target = -1; + + // put scb in the free pool + list_add_tail(&scb->list, &adapter->kscb_pool); + } + + return 0; + +out_teardown_dma_pools: + megaraid_mbox_teardown_dma_pools(adapter); +out_free_scb_list: + kfree(adapter->kscb_list); +out_free_ibuf: + pci_free_consistent(pdev, MBOX_IBUF_SIZE, (void *)adapter->ibuf, + adapter->ibuf_dma_h); +out_free_common_mbox: + pci_free_consistent(adapter->pdev, sizeof(mbox64_t), + (caddr_t)raid_dev->una_mbox64, raid_dev->una_mbox64_dma); + + return -1; +} + + +/** + * megaraid_free_cmd_packets - free memory + * @param adapter : soft state of the raid controller + * + * Release memory resources allocated for commands + */ +static void +megaraid_free_cmd_packets(adapter_t *adapter) +{ + mraid_device_t *raid_dev = ADAP2RAIDDEV(adapter); + + megaraid_mbox_teardown_dma_pools(adapter); + + kfree(adapter->kscb_list); + + pci_free_consistent(adapter->pdev, MBOX_IBUF_SIZE, + (void *)adapter->ibuf, adapter->ibuf_dma_h); + + pci_free_consistent(adapter->pdev, sizeof(mbox64_t), + (caddr_t)raid_dev->una_mbox64, raid_dev->una_mbox64_dma); + return; +} + + +/** + * megaraid_mbox_setup_dma_pools - setup dma pool for command packets + * @param adapter : HBA soft state + * + * setup the dma pools for mailbox, passthru and extended passthru structures, + * and scatter-gather lists + */ +static int +megaraid_mbox_setup_dma_pools(adapter_t *adapter) +{ + mraid_device_t *raid_dev = ADAP2RAIDDEV(adapter); + struct mraid_pci_blk *epthru_pci_blk; + struct mraid_pci_blk *sg_pci_blk; + struct mraid_pci_blk *mbox_pci_blk; + int i; + + + + // Allocate memory for 16-bytes aligned mailboxes + raid_dev->mbox_pool_handle = pci_pool_create("megaraid mbox pool", + adapter->pdev, + sizeof(mbox64_t) + 16, + 16, 0); + + if (raid_dev->mbox_pool_handle == NULL) { + goto fail_setup_dma_pool; + } + + mbox_pci_blk = raid_dev->mbox_pool; + for (i = 0; i < MBOX_MAX_SCSI_CMDS; i++) { + mbox_pci_blk[i].vaddr = pci_pool_alloc( + raid_dev->mbox_pool_handle, + GFP_KERNEL, + &mbox_pci_blk[i].dma_addr); + if (!mbox_pci_blk[i].vaddr) { + goto fail_setup_dma_pool; + } + } + + /* + * Allocate memory for each embedded passthru strucuture pointer + * Request for a 128 bytes aligned structure for each passthru command + * structure + * Since passthru and extended passthru commands are exclusive, they + * share common memory pool. Passthru structures piggyback on memory + * allocted to extended passthru since passthru is smaller of the two + */ + raid_dev->epthru_pool_handle = pci_pool_create("megaraid mbox pthru", + adapter->pdev, sizeof(mraid_epassthru_t), 128, 0); + + if (raid_dev->epthru_pool_handle == NULL) { + goto fail_setup_dma_pool; + } + + epthru_pci_blk = raid_dev->epthru_pool; + for (i = 0; i < MBOX_MAX_SCSI_CMDS; i++) { + epthru_pci_blk[i].vaddr = pci_pool_alloc( + raid_dev->epthru_pool_handle, + GFP_KERNEL, + &epthru_pci_blk[i].dma_addr); + if (!epthru_pci_blk[i].vaddr) { + goto fail_setup_dma_pool; + } + } + + + // Allocate memory for each scatter-gather list. Request for 512 bytes + // alignment for each sg list + raid_dev->sg_pool_handle = pci_pool_create("megaraid mbox sg", + adapter->pdev, + sizeof(mbox_sgl64) * MBOX_MAX_SG_SIZE, + 512, 0); + + if (raid_dev->sg_pool_handle == NULL) { + goto fail_setup_dma_pool; + } + + sg_pci_blk = raid_dev->sg_pool; + for (i = 0; i < MBOX_MAX_SCSI_CMDS; i++) { + sg_pci_blk[i].vaddr = pci_pool_alloc( + raid_dev->sg_pool_handle, + GFP_KERNEL, + &sg_pci_blk[i].dma_addr); + if (!sg_pci_blk[i].vaddr) { + goto fail_setup_dma_pool; + } + } + + return 0; + +fail_setup_dma_pool: + megaraid_mbox_teardown_dma_pools(adapter); + return -1; +} + + +/** + * megaraid_mbox_teardown_dma_pools - teardown dma pools for command packets + * @param adapter : HBA soft state + * + * teardown the dma pool for mailbox, passthru and extended passthru + * structures, and scatter-gather lists + */ +static void +megaraid_mbox_teardown_dma_pools(adapter_t *adapter) +{ + mraid_device_t *raid_dev = ADAP2RAIDDEV(adapter); + struct mraid_pci_blk *epthru_pci_blk; + struct mraid_pci_blk *sg_pci_blk; + struct mraid_pci_blk *mbox_pci_blk; + int i; + + + sg_pci_blk = raid_dev->sg_pool; + for (i = 0; i < MBOX_MAX_SCSI_CMDS && sg_pci_blk[i].vaddr; i++) { + pci_pool_free(raid_dev->sg_pool_handle, sg_pci_blk[i].vaddr, + sg_pci_blk[i].dma_addr); + } + if (raid_dev->sg_pool_handle) + pci_pool_destroy(raid_dev->sg_pool_handle); + + + epthru_pci_blk = raid_dev->epthru_pool; + for (i = 0; i < MBOX_MAX_SCSI_CMDS && epthru_pci_blk[i].vaddr; i++) { + pci_pool_free(raid_dev->epthru_pool_handle, + epthru_pci_blk[i].vaddr, epthru_pci_blk[i].dma_addr); + } + if (raid_dev->epthru_pool_handle) + pci_pool_destroy(raid_dev->epthru_pool_handle); + + + mbox_pci_blk = raid_dev->mbox_pool; + for (i = 0; i < MBOX_MAX_SCSI_CMDS && mbox_pci_blk[i].vaddr; i++) { + pci_pool_free(raid_dev->mbox_pool_handle, + mbox_pci_blk[i].vaddr, mbox_pci_blk[i].dma_addr); + } + if (raid_dev->mbox_pool_handle) + pci_pool_destroy(raid_dev->mbox_pool_handle); + + return; +} + + +/** + * megaraid_alloc_scb - detach and return a scb from the free list + * @adapter : controller's soft state + * + * return the scb from the head of the free list. NULL if there are none + * available + **/ +static inline scb_t * +megaraid_alloc_scb(adapter_t *adapter, struct scsi_cmnd *scp) +{ + struct list_head *head = &adapter->kscb_pool; + scb_t *scb = NULL; + unsigned long flags; + + // detach scb from free pool + spin_lock_irqsave(SCSI_FREE_LIST_LOCK(adapter), flags); + + if (list_empty(head)) { + spin_unlock_irqrestore(SCSI_FREE_LIST_LOCK(adapter), flags); + return NULL; + } + + scb = list_entry(head->next, scb_t, list); + list_del_init(&scb->list); + + spin_unlock_irqrestore(SCSI_FREE_LIST_LOCK(adapter), flags); + + scb->state = SCB_ACTIVE; + scb->scp = scp; + scb->dma_type = MRAID_DMA_NONE; + + return scb; +} + + +/** + * megaraid_dealloc_scb - return the scb to the free pool + * @adapter : controller's soft state + * @scb : scb to be freed + * + * return the scb back to the free list of scbs. The caller must 'flush' the + * SCB before calling us. E.g., performing pci_unamp and/or pci_sync etc. + * NOTE NOTE: Make sure the scb is not on any list before calling this + * routine. + **/ +static inline void +megaraid_dealloc_scb(adapter_t *adapter, scb_t *scb) +{ + unsigned long flags; + + // put scb in the free pool + scb->state = SCB_FREE; + scb->scp = NULL; + spin_lock_irqsave(SCSI_FREE_LIST_LOCK(adapter), flags); + + list_add(&scb->list, &adapter->kscb_pool); + + spin_unlock_irqrestore(SCSI_FREE_LIST_LOCK(adapter), flags); + + return; +} + + +/** + * megaraid_mbox_mksgl - make the scatter-gather list + * @adapter - controller's soft state + * @scb - scsi control block + * + * prepare the scatter-gather list + */ +static inline int +megaraid_mbox_mksgl(adapter_t *adapter, scb_t *scb) +{ + struct scatterlist *sgl; + mbox_ccb_t *ccb; + struct page *page; + unsigned long offset; + struct scsi_cmnd *scp; + int sgcnt; + int i; + + + scp = scb->scp; + ccb = (mbox_ccb_t *)scb->ccb; + + // no mapping required if no data to be transferred + if (!scp->request_buffer || !scp->request_bufflen) + return 0; + + if (!scp->use_sg) { /* scatter-gather list not used */ + + page = virt_to_page(scp->request_buffer); + + offset = ((unsigned long)scp->request_buffer & ~PAGE_MASK); + + ccb->buf_dma_h = pci_map_page(adapter->pdev, page, offset, + scp->request_bufflen, + scb->dma_direction); + scb->dma_type = MRAID_DMA_WBUF; + + /* + * We need to handle special 64-bit commands that need a + * minimum of 1 SG + */ + sgcnt = 1; + ccb->sgl64[0].address = ccb->buf_dma_h; + ccb->sgl64[0].length = scp->request_bufflen; + + return sgcnt; + } + + sgl = (struct scatterlist *)scp->request_buffer; + + // The number of sg elements returned must not exceed our limit + sgcnt = pci_map_sg(adapter->pdev, sgl, scp->use_sg, + scb->dma_direction); + + if (sgcnt > adapter->sglen) { + con_log(CL_ANN, (KERN_CRIT + "megaraid critical: too many sg elements:%d\n", + sgcnt)); + BUG(); + } + + scb->dma_type = MRAID_DMA_WSG; + + for (i = 0; i < sgcnt; i++, sgl++) { + ccb->sgl64[i].address = sg_dma_address(sgl); + ccb->sgl64[i].length = sg_dma_len(sgl); + } + + // Return count of SG nodes + return sgcnt; +} + + +/** + * mbox_post_cmd - issue a mailbox command + * @adapter - controller's soft state + * @scb - command to be issued + * + * post the command to the controller if mailbox is availble. + */ +static inline int +mbox_post_cmd(adapter_t *adapter, scb_t *scb) +{ + mraid_device_t *raid_dev = ADAP2RAIDDEV(adapter); + mbox64_t *mbox64; + mbox_t *mbox; + mbox_ccb_t *ccb; + unsigned long flags; + unsigned int i = 0; + + + ccb = (mbox_ccb_t *)scb->ccb; + mbox = raid_dev->mbox; + mbox64 = raid_dev->mbox64; + + /* + * Check for busy mailbox. If it is, return failure - the caller + * should retry later. + */ + spin_lock_irqsave(MAILBOX_LOCK(raid_dev), flags); + + if (unlikely(mbox->busy)) { + do { + udelay(1); + i++; + rmb(); + } while(mbox->busy && (i < max_mbox_busy_wait)); + + if (mbox->busy) { + + spin_unlock_irqrestore(MAILBOX_LOCK(raid_dev), flags); + + return -1; + } + } + + + // Copy this command's mailbox data into "adapter's" mailbox + memcpy((caddr_t)mbox64, (caddr_t)ccb->mbox64, 22); + mbox->cmdid = scb->sno; + + adapter->outstanding_cmds++; + + if (scb->dma_direction == PCI_DMA_TODEVICE) { + if (!scb->scp->use_sg) { // sg list not used + pci_dma_sync_single(adapter->pdev, ccb->buf_dma_h, + scb->scp->request_bufflen, + PCI_DMA_TODEVICE); + } + else { + pci_dma_sync_sg(adapter->pdev, scb->scp->request_buffer, + scb->scp->use_sg, PCI_DMA_TODEVICE); + } + } + + mbox->busy = 1; // Set busy + mbox->poll = 0; + mbox->ack = 0; + wmb(); + + WRINDOOR(raid_dev, raid_dev->mbox_dma | 0x1); + + spin_unlock_irqrestore(MAILBOX_LOCK(raid_dev), flags); + + return 0; +} + + +/** + * megaraid_queue_command - generic queue entry point for all LLDs + * @scp : pointer to the scsi command to be executed + * @done : callback routine to be called after the cmd has be completed + * + * Queue entry point for mailbox based controllers. + */ +static int +megaraid_queue_command(struct scsi_cmnd *scp, void (* done)(struct scsi_cmnd *)) +{ + adapter_t *adapter; + scb_t *scb; + int if_busy; + + adapter = SCP2ADAPTER(scp); + scp->scsi_done = done; + scp->result = 0; + + ASSERT(spin_is_locked(adapter->host_lock)); + + spin_unlock(adapter->host_lock); + + /* + * Allocate and build a SCB request + * if_busy flag will be set if megaraid_mbox_build_cmd() command could + * not allocate scb. We will return non-zero status in that case. + * NOTE: scb can be null even though certain commands completed + * successfully, e.g., MODE_SENSE and TEST_UNIT_READY, it would + * return 0 in that case, and we would do the callback right away. + */ + if_busy = 0; + scb = megaraid_mbox_build_cmd(adapter, scp, &if_busy); + + if (scb) { + megaraid_mbox_runpendq(adapter, scb); + } + + spin_lock(adapter->host_lock); + + if (!scb) { // command already completed + done(scp); + } + + return if_busy; +} + + +/** + * megaraid_mbox_build_cmd - transform the mid-layer scsi command to megaraid + * firmware lingua + * @adapter - controller's soft state + * @scp - mid-layer scsi command pointer + * @busy - set if request could not be completed because of lack of + * resources + * + * convert the command issued by mid-layer to format understood by megaraid + * firmware. We also complete certain command without sending them to firmware + */ +static scb_t * +megaraid_mbox_build_cmd(adapter_t *adapter, struct scsi_cmnd *scp, int *busy) +{ + mraid_device_t *rdev = ADAP2RAIDDEV(adapter); + int channel; + int target; + int islogical; + mbox_ccb_t *ccb; + mraid_passthru_t *pthru; + mbox64_t *mbox64; + mbox_t *mbox; + scb_t *scb; + char skip[] = "skipping"; + char scan[] = "scanning"; + char *ss; + + + /* + * Get the appropriate device map for the device this command is + * intended for + */ + MRAID_GET_DEVICE_MAP(adapter, scp, channel, target, islogical); + + /* + * Logical drive commands + */ + if (islogical) { + switch (scp->cmnd[0]) { + case TEST_UNIT_READY: + /* + * Do we support clustering and is the support enabled + * If no, return success always + */ + if (!adapter->ha) { + scp->result = (DID_OK << 16); + return NULL; + } + + if (!(scb = megaraid_alloc_scb(adapter, scp))) { + scp->result = (DID_ERROR << 16); + *busy = 1; + return NULL; + } + + scb->dma_direction = scp->sc_data_direction; + scb->dev_channel = 0xFF; + scb->dev_target = target; + ccb = (mbox_ccb_t *)scb->ccb; + + /* + * The command id will be provided by the command + * issuance routine + */ + ccb->raw_mbox[0] = CLUSTER_CMD; + ccb->raw_mbox[2] = RESERVATION_STATUS; + ccb->raw_mbox[3] = target; + + return scb; + + case MODE_SENSE: + if (scp->use_sg) { + struct scatterlist *sgl; + caddr_t vaddr; + + sgl = (struct scatterlist *)scp->request_buffer; + if (sgl->page) { + vaddr = (caddr_t) + (page_address((&sgl[0])->page) + + (&sgl[0])->offset); + + memset(vaddr, 0, scp->cmnd[4]); + } + else { + con_log(CL_ANN, (KERN_WARNING + "megaraid mailbox: invalid sg:%d\n", + __LINE__)); + } + } + else { + memset(scp->request_buffer, 0, scp->cmnd[4]); + } + scp->result = (DID_OK << 16); + return NULL; + + case INQUIRY: + /* + * Display the channel scan for logical drives + * Do not display scan for a channel if already done. + */ + if (!(rdev->last_disp & (1L << SCP2CHANNEL(scp)))) { + + con_log(CL_ANN, (KERN_INFO + "scsi[%d]: scanning scsi channel %d", + adapter->host->host_no, + SCP2CHANNEL(scp))); + + con_log(CL_ANN, ( + " [virtual] for logical drives\n")); + + rdev->last_disp |= (1L << SCP2CHANNEL(scp)); + } + + /* Fall through */ + + case READ_CAPACITY: + /* + * Do not allow LUN > 0 for logical drives and + * requests for more than 40 logical drives + */ + if (SCP2LUN(scp)) { + scp->result = (DID_BAD_TARGET << 16); + return NULL; + } + if ((target % 0x80) >= MAX_LOGICAL_DRIVES_40LD) { + scp->result = (DID_BAD_TARGET << 16); + return NULL; + } + + + /* Allocate a SCB and initialize passthru */ + if (!(scb = megaraid_alloc_scb(adapter, scp))) { + scp->result = (DID_ERROR << 16); + *busy = 1; + return NULL; + } + + ccb = (mbox_ccb_t *)scb->ccb; + scb->dev_channel = 0xFF; + scb->dev_target = target; + pthru = ccb->pthru; + mbox = ccb->mbox; + mbox64 = ccb->mbox64; + + pthru->timeout = 0; + pthru->ars = 1; + pthru->reqsenselen = 14; + pthru->islogical = 1; + pthru->logdrv = target; + pthru->cdblen = scp->cmd_len; + memcpy(pthru->cdb, scp->cmnd, scp->cmd_len); + + mbox->cmd = MBOXCMD_PASSTHRU64; + scb->dma_direction = scp->sc_data_direction; + + pthru->dataxferlen = scp->request_bufflen; + pthru->dataxferaddr = ccb->sgl_dma_h; + pthru->numsge = megaraid_mbox_mksgl(adapter, + scb); + + mbox->xferaddr = 0xFFFFFFFF; + mbox64->xferaddr_lo = (uint32_t )ccb->pthru_dma_h; + mbox64->xferaddr_hi = 0; + + return scb; + + case READ_6: + case WRITE_6: + case READ_10: + case WRITE_10: + case READ_12: + case WRITE_12: + + /* + * Allocate a SCB and initialize mailbox + */ + if (!(scb = megaraid_alloc_scb(adapter, scp))) { + scp->result = (DID_ERROR << 16); + *busy = 1; + return NULL; + } + ccb = (mbox_ccb_t *)scb->ccb; + scb->dev_channel = 0xFF; + scb->dev_target = target; + mbox = ccb->mbox; + mbox64 = ccb->mbox64; + mbox->logdrv = target; + + /* + * A little HACK: 2nd bit is zero for all scsi read + * commands and is set for all scsi write commands + */ + mbox->cmd = (scp->cmnd[0] & 0x02) ? MBOXCMD_LWRITE64: + MBOXCMD_LREAD64 ; + + /* + * 6-byte READ(0x08) or WRITE(0x0A) cdb + */ + if (scp->cmd_len == 6) { + mbox->numsectors = (uint32_t)scp->cmnd[4]; + mbox->lba = + ((uint32_t)scp->cmnd[1] << 16) | + ((uint32_t)scp->cmnd[2] << 8) | + (uint32_t)scp->cmnd[3]; + + mbox->lba &= 0x1FFFFF; + } + + /* + * 10-byte READ(0x28) or WRITE(0x2A) cdb + */ + else if (scp->cmd_len == 10) { + mbox->numsectors = + (uint32_t)scp->cmnd[8] | + ((uint32_t)scp->cmnd[7] << 8); + mbox->lba = + ((uint32_t)scp->cmnd[2] << 24) | + ((uint32_t)scp->cmnd[3] << 16) | + ((uint32_t)scp->cmnd[4] << 8) | + (uint32_t)scp->cmnd[5]; + } + + /* + * 12-byte READ(0xA8) or WRITE(0xAA) cdb + */ + else if (scp->cmd_len == 12) { + mbox->lba = + ((uint32_t)scp->cmnd[2] << 24) | + ((uint32_t)scp->cmnd[3] << 16) | + ((uint32_t)scp->cmnd[4] << 8) | + (uint32_t)scp->cmnd[5]; + + mbox->numsectors = + ((uint32_t)scp->cmnd[6] << 24) | + ((uint32_t)scp->cmnd[7] << 16) | + ((uint32_t)scp->cmnd[8] << 8) | + (uint32_t)scp->cmnd[9]; + } + else { + con_log(CL_ANN, (KERN_WARNING + "megaraid: unsupported CDB length\n")); + + megaraid_dealloc_scb(adapter, scb); + + scp->result = (DID_ERROR << 16); + return NULL; + } + + scb->dma_direction = scp->sc_data_direction; + + // Calculate Scatter-Gather info + mbox64->xferaddr_lo = (uint32_t )ccb->sgl_dma_h; + mbox->numsge = megaraid_mbox_mksgl(adapter, + scb); + mbox->xferaddr = 0xFFFFFFFF; + mbox64->xferaddr_hi = 0; + + return scb; + + case RESERVE: + case RELEASE: + /* + * Do we support clustering and is the support enabled + */ + if (!adapter->ha) { + scp->result = (DID_BAD_TARGET << 16); + return NULL; + } + + /* + * Allocate a SCB and initialize mailbox + */ + if (!(scb = megaraid_alloc_scb(adapter, scp))) { + scp->result = (DID_ERROR << 16); + *busy = 1; + return NULL; + } + + ccb = (mbox_ccb_t *)scb->ccb; + scb->dev_channel = 0xFF; + scb->dev_target = target; + ccb->raw_mbox[0] = CLUSTER_CMD; + ccb->raw_mbox[2] = (scp->cmnd[0] == RESERVE) ? + RESERVE_LD : RELEASE_LD; + + ccb->raw_mbox[3] = target; + scb->dma_direction = scp->sc_data_direction; + + return scb; + + default: + scp->result = (DID_BAD_TARGET << 16); + return NULL; + } + } + else { // Passthru device commands + + // Do not allow access to target id > 15 or LUN > 7 + if (target > 15 || SCP2LUN(scp) > 7) { + scp->result = (DID_BAD_TARGET << 16); + return NULL; + } + + // if fast load option was set and scan for last device is + // over, reset the fast_load flag so that during a possible + // next scan, devices can be made available + if (rdev->fast_load && (target == 15) && + (SCP2CHANNEL(scp) == adapter->max_channel -1)) { + + con_log(CL_ANN, (KERN_INFO + "megaraid[%d]: physical device scan re-enabled\n", + adapter->host->host_no)); + rdev->fast_load = 0; + } + + /* + * Display the channel scan for physical devices + */ + if (!(rdev->last_disp & (1L << SCP2CHANNEL(scp)))) { + + ss = rdev->fast_load ? skip : scan; + + con_log(CL_ANN, (KERN_INFO + "scsi[%d]: %s scsi channel %d [Phy %d]", + adapter->host->host_no, ss, SCP2CHANNEL(scp), + channel)); + + con_log(CL_ANN, ( + " for non-raid devices\n")); + + rdev->last_disp |= (1L << SCP2CHANNEL(scp)); + } + + // disable channel sweep if fast load option given + if (rdev->fast_load) { + scp->result = (DID_BAD_TARGET << 16); + return NULL; + } + + // Allocate a SCB and initialize passthru + if (!(scb = megaraid_alloc_scb(adapter, scp))) { + scp->result = (DID_ERROR << 16); + *busy = 1; + return NULL; + } + + ccb = (mbox_ccb_t *)scb->ccb; + scb->dev_channel = channel; + scb->dev_target = target; + scb->dma_direction = scp->sc_data_direction; + mbox = ccb->mbox; + mbox64 = ccb->mbox64; + + // Does this firmware support extended CDBs + if (adapter->max_cdb_sz == 16) { + mbox->cmd = MBOXCMD_EXTPTHRU; + + megaraid_mbox_prepare_epthru(adapter, scb, scp); + + mbox64->xferaddr_lo = (uint32_t)ccb->epthru_dma_h; + mbox64->xferaddr_hi = 0; + mbox->xferaddr = 0xFFFFFFFF; + } + else { + mbox->cmd = MBOXCMD_PASSTHRU64; + + megaraid_mbox_prepare_pthru(adapter, scb, scp); + + mbox64->xferaddr_lo = (uint32_t)ccb->pthru_dma_h; + mbox64->xferaddr_hi = 0; + mbox->xferaddr = 0xFFFFFFFF; + } + return scb; + } + + // NOT REACHED +} + + +/** + * megaraid_mbox_runpendq - execute commands queued in the pending queue + * @adapter : controller's soft state + * @scb : SCB to be queued in the pending list + * + * scan the pending list for commands which are not yet issued and try to + * post to the controller. The SCB can be a null pointer, which would indicate + * no SCB to be queue, just try to execute the ones in the pending list. + * + * NOTE: We do not actually traverse the pending list. The SCBs are plucked + * out from the head of the pending list. If it is successfully issued, the + * next SCB is at the head now. + */ +static void +megaraid_mbox_runpendq(adapter_t *adapter, scb_t *scb_q) +{ + scb_t *scb; + unsigned long flags; + + spin_lock_irqsave(PENDING_LIST_LOCK(adapter), flags); + + if (scb_q) { + scb_q->state = SCB_PENDQ; + list_add_tail(&scb_q->list, &adapter->pend_list); + } + + // if the adapter in not in quiescent mode, post the commands to FW + if (adapter->quiescent) { + spin_unlock_irqrestore(PENDING_LIST_LOCK(adapter), flags); + return; + } + + while (!list_empty(&adapter->pend_list)) { + + ASSERT(spin_is_locked(PENDING_LIST_LOCK(adapter))); + + scb = list_entry(adapter->pend_list.next, scb_t, list); + + // remove the scb from the pending list and try to + // issue. If we are unable to issue it, put back in + // the pending list and return + + list_del_init(&scb->list); + + spin_unlock_irqrestore(PENDING_LIST_LOCK(adapter), flags); + + // if mailbox was busy, return SCB back to pending + // list. Make sure to add at the head, since that's + // where it would have been removed from + + scb->state = SCB_ISSUED; + + if (mbox_post_cmd(adapter, scb) != 0) { + + spin_lock_irqsave(PENDING_LIST_LOCK(adapter), flags); + + scb->state = SCB_PENDQ; + + list_add(&scb->list, &adapter->pend_list); + + spin_unlock_irqrestore(PENDING_LIST_LOCK(adapter), + flags); + + return; + } + + spin_lock_irqsave(PENDING_LIST_LOCK(adapter), flags); + } + + spin_unlock_irqrestore(PENDING_LIST_LOCK(adapter), flags); + + + return; +} + + +/** + * megaraid_mbox_prepare_pthru - prepare a command for physical devices + * @adapter - pointer to controller's soft state + * @scb - scsi control block + * @scp - scsi command from the mid-layer + * + * prepare a command for the scsi physical devices + */ +static void +megaraid_mbox_prepare_pthru(adapter_t *adapter, scb_t *scb, + struct scsi_cmnd *scp) +{ + mbox_ccb_t *ccb; + mraid_passthru_t *pthru; + uint8_t channel; + uint8_t target; + + ccb = (mbox_ccb_t *)scb->ccb; + pthru = ccb->pthru; + channel = scb->dev_channel; + target = scb->dev_target; + + pthru->timeout = 1; // 0=6sec, 1=60sec, 2=10min, 3=3hrs + pthru->ars = 1; + pthru->islogical = 0; + pthru->channel = 0; + pthru->target = (channel << 4) | target; + pthru->logdrv = SCP2LUN(scp); + pthru->reqsenselen = 14; + pthru->cdblen = scp->cmd_len; + + memcpy(pthru->cdb, scp->cmnd, scp->cmd_len); + + if (scp->request_bufflen) { + pthru->dataxferlen = scp->request_bufflen; + pthru->dataxferaddr = ccb->sgl_dma_h; + pthru->numsge = megaraid_mbox_mksgl(adapter, scb); + } + else { + pthru->dataxferaddr = 0; + pthru->dataxferlen = 0; + pthru->numsge = 0; + } + return; +} + + +/** + * megaraid_mbox_prepare_epthru - prepare a command for physical devices + * @adapter - pointer to controller's soft state + * @scb - scsi control block + * @scp - scsi command from the mid-layer + * + * prepare a command for the scsi physical devices. This rountine prepares + * commands for devices which can take extended CDBs (>10 bytes) + */ +static void +megaraid_mbox_prepare_epthru(adapter_t *adapter, scb_t *scb, + struct scsi_cmnd *scp) +{ + mbox_ccb_t *ccb; + mraid_epassthru_t *epthru; + uint8_t channel; + uint8_t target; + + ccb = (mbox_ccb_t *)scb->ccb; + epthru = ccb->epthru; + channel = scb->dev_channel; + target = scb->dev_target; + + epthru->timeout = 1; // 0=6sec, 1=60sec, 2=10min, 3=3hrs + epthru->ars = 1; + epthru->islogical = 0; + epthru->channel = 0; + epthru->target = (channel << 4) | target; + epthru->logdrv = SCP2LUN(scp); + epthru->reqsenselen = 14; + epthru->cdblen = scp->cmd_len; + + memcpy(epthru->cdb, scp->cmnd, scp->cmd_len); + + if (scp->request_bufflen) { + epthru->dataxferlen = scp->request_bufflen; + epthru->dataxferaddr = ccb->sgl_dma_h; + epthru->numsge = megaraid_mbox_mksgl(adapter, scb); + } + else { + epthru->dataxferaddr = 0; + epthru->dataxferlen = 0; + epthru->numsge = 0; + } + return; +} + + +/** + * megaraid_ack_sequence - interrupt ack sequence for memory mapped HBAs + * @adapter - controller's soft state + * + * Interrupt ackrowledgement sequence for memory mapped HBAs. Find out the + * completed command and put them on the completed list for later processing. + * + * Returns: 1 if the interrupt is valid, 0 otherwise + */ +static inline int +megaraid_ack_sequence(adapter_t *adapter) +{ + mraid_device_t *raid_dev = ADAP2RAIDDEV(adapter); + mbox_t *mbox; + scb_t *scb; + uint8_t nstatus; + uint8_t completed[MBOX_MAX_FIRMWARE_STATUS]; + struct list_head clist; + int handled; + uint32_t dword; + unsigned long flags; + int i, j; + + + mbox = raid_dev->mbox; + + // move the SCBs from the firmware completed array to our local list + INIT_LIST_HEAD(&clist); + + // loop till F/W has more commands for us to complete + handled = 0; + spin_lock_irqsave(MAILBOX_LOCK(raid_dev), flags); + do { + /* + * Check if a valid interrupt is pending. If found, force the + * interrupt line low. + */ + dword = RDOUTDOOR(raid_dev); + if (dword != 0x10001234) break; + + handled = 1; + + WROUTDOOR(raid_dev, 0x10001234); + + nstatus = 0; + // wait for valid numstatus to post + for (i = 0; i < 0xFFFFF; i++) { + if (mbox->numstatus != 0xFF) { + nstatus = mbox->numstatus; + break; + } + rmb(); + } + mbox->numstatus = 0xFF; + + adapter->outstanding_cmds -= nstatus; + + for (i = 0; i < nstatus; i++) { + + // wait for valid command index to post + for (j = 0; j < 0xFFFFF; j++) { + if (mbox->completed[i] != 0xFF) break; + rmb(); + } + completed[i] = mbox->completed[i]; + mbox->completed[i] = 0xFF; + + if (completed[i] == 0xFF) { + con_log(CL_ANN, (KERN_CRIT + "megaraid: command posting timed out\n")); + + BUG(); + continue; + } + + // Get SCB associated with this command id + if (completed[i] >= MBOX_MAX_SCSI_CMDS) { + // a cmm command + scb = adapter->uscb_list + (completed[i] - + MBOX_MAX_SCSI_CMDS); + } + else { + // an os command + scb = adapter->kscb_list + completed[i]; + } + + scb->status = mbox->status; + list_add_tail(&scb->list, &clist); + } + + // Acknowledge interrupt + WRINDOOR(raid_dev, 0x02); + + } while(1); + + spin_unlock_irqrestore(MAILBOX_LOCK(raid_dev), flags); + + + // put the completed commands in the completed list. DPC would + // complete these commands later + spin_lock_irqsave(COMPLETED_LIST_LOCK(adapter), flags); + + list_splice(&clist, &adapter->completed_list); + + spin_unlock_irqrestore(COMPLETED_LIST_LOCK(adapter), flags); + + + // schedule the DPC if there is some work for it + if (handled) + tasklet_schedule(&adapter->dpc_h); + + return handled; +} + + +/** + * megaraid_isr - isr for memory based mailbox based controllers + * @irq - irq + * @devp - pointer to our soft state + * @regs - unused + * + * Interrupt service routine for memory-mapped mailbox controllers. + */ +static irqreturn_t +megaraid_isr(int irq, void *devp, struct pt_regs *regs) +{ + adapter_t *adapter = devp; + int handled; + + handled = megaraid_ack_sequence(adapter); + + /* Loop through any pending requests */ + if (!adapter->quiescent) { + megaraid_mbox_runpendq(adapter, NULL); + } + + return IRQ_RETVAL(handled); +} + + +/** + * megaraid_mbox_sync_scb - sync kernel buffers + * @adapter : controller's soft state + * @scb : pointer to the resource packet + * + * DMA sync if required. + */ +static inline void +megaraid_mbox_sync_scb(adapter_t *adapter, scb_t *scb) +{ + mbox_ccb_t *ccb; + + ccb = (mbox_ccb_t *)scb->ccb; + + switch (scb->dma_type) { + + case MRAID_DMA_WBUF: + if (scb->dma_direction == PCI_DMA_FROMDEVICE) { + pci_dma_sync_single(adapter->pdev, + ccb->buf_dma_h, + scb->scp->request_bufflen, + PCI_DMA_FROMDEVICE); + } + + pci_unmap_page(adapter->pdev, ccb->buf_dma_h, + scb->scp->request_bufflen, scb->dma_direction); + + break; + + case MRAID_DMA_WSG: + if (scb->dma_direction == PCI_DMA_FROMDEVICE) { + pci_dma_sync_sg(adapter->pdev, + scb->scp->request_buffer, + scb->scp->use_sg, PCI_DMA_FROMDEVICE); + } + + pci_unmap_sg(adapter->pdev, scb->scp->request_buffer, + scb->scp->use_sg, scb->dma_direction); + + break; + + default: + break; + } + + return; +} + + +/** + * megaraid_mbox_dpc - the tasklet to complete the commands from completed list + * @devp : pointer to HBA soft state + * + * Pick up the commands from the completed list and send back to the owners. + * This is a reentrant function and does not assume any locks are held while + * it is being called. + */ +static void +megaraid_mbox_dpc(unsigned long devp) +{ + adapter_t *adapter = (adapter_t *)devp; + mraid_device_t *raid_dev; + struct list_head clist; + struct scatterlist *sgl; + scb_t *scb; + scb_t *tmp; + struct scsi_cmnd *scp; + mraid_passthru_t *pthru; + mraid_epassthru_t *epthru; + mbox_ccb_t *ccb; + int islogical; + int pdev_index; + int pdev_state; + mbox_t *mbox; + unsigned long flags; + uint8_t c; + int status; + + + if (!adapter) return; + + raid_dev = ADAP2RAIDDEV(adapter); + + // move the SCBs from the completed list to our local list + INIT_LIST_HEAD(&clist); + + spin_lock_irqsave(COMPLETED_LIST_LOCK(adapter), flags); + + list_splice_init(&adapter->completed_list, &clist); + + spin_unlock_irqrestore(COMPLETED_LIST_LOCK(adapter), flags); + + + list_for_each_entry_safe(scb, tmp, &clist, list) { + + status = scb->status; + scp = scb->scp; + ccb = (mbox_ccb_t *)scb->ccb; + pthru = ccb->pthru; + epthru = ccb->epthru; + mbox = ccb->mbox; + + // Make sure f/w has completed a valid command + if (scb->state != SCB_ISSUED) { + con_log(CL_ANN, (KERN_CRIT + "megaraid critical err: invalid command %d:%d:%p\n", + scb->sno, scb->state, scp)); + BUG(); + continue; // Must never happen! + } + + // check for the management command and complete it right away + if (scb->sno >= MBOX_MAX_SCSI_CMDS) { + scb->state = SCB_FREE; + scb->status = status; + + // remove from local clist + list_del_init(&scb->list); + + megaraid_mbox_mm_done(adapter, scb); + + continue; + } + + // Was an abort issued for this command earlier + if (scb->state & SCB_ABORT) { + con_log(CL_ANN, (KERN_NOTICE + "megaraid: aborted cmd %lx[%x] completed\n", + scp->serial_number, scb->sno)); + } + + /* + * If the inquiry came of a disk drive which is not part of + * any RAID array, expose it to the kernel. For this to be + * enabled, user must set the "megaraid_expose_unconf_disks" + * flag to 1 by specifying it on module parameter list. + * This would enable data migration off drives from other + * configurations. + */ + islogical = MRAID_IS_LOGICAL(adapter, scp); + if (scp->cmnd[0] == INQUIRY && status == 0 && islogical == 0 + && IS_RAID_CH(raid_dev, scb->dev_channel)) { + + if (scp->use_sg) { + sgl = (struct scatterlist *) + scp->request_buffer; + + if (sgl->page) { + c = *(unsigned char *) + (page_address((&sgl[0])->page) + + (&sgl[0])->offset); + } + else { + con_log(CL_ANN, (KERN_WARNING + "megaraid mailbox: invalid sg:%d\n", + __LINE__)); + c = 0; + } + } + else { + c = *(uint8_t *)scp->request_buffer; + } + + if ((c & 0x1F ) == TYPE_DISK) { + pdev_index = (scb->dev_channel * 16) + + scb->dev_target; + pdev_state = + raid_dev->pdrv_state[pdev_index] & 0x0F; + + if (pdev_state == PDRV_ONLINE || + pdev_state == PDRV_FAILED || + pdev_state == PDRV_RBLD || + pdev_state == PDRV_HOTSPARE || + megaraid_expose_unconf_disks == 0) { + + status = 0xF0; + } + } + } + + // Convert MegaRAID status to Linux error code + switch (status) { + + case 0x00: + + scp->result = (DID_OK << 16); + break; + + case 0x02: + + /* set sense_buffer and result fields */ + if (mbox->cmd == MBOXCMD_PASSTHRU || + mbox->cmd == MBOXCMD_PASSTHRU64) { + + memcpy(scp->sense_buffer, pthru->reqsensearea, + 14); + + scp->result = DRIVER_SENSE << 24 | + DID_OK << 16 | CHECK_CONDITION << 1; + } + else { + if (mbox->cmd == MBOXCMD_EXTPTHRU) { + + memcpy(scp->sense_buffer, + epthru->reqsensearea, 14); + + scp->result = DRIVER_SENSE << 24 | + DID_OK << 16 | + CHECK_CONDITION << 1; + } else { + scp->sense_buffer[0] = 0x70; + scp->sense_buffer[2] = ABORTED_COMMAND; + scp->result = CHECK_CONDITION << 1; + } + } + break; + + case 0x08: + + scp->result = DID_BUS_BUSY << 16 | status; + break; + + default: + + /* + * If TEST_UNIT_READY fails, we know RESERVATION_STATUS + * failed + */ + if (scp->cmnd[0] == TEST_UNIT_READY) { + scp->result = DID_ERROR << 16 | + RESERVATION_CONFLICT << 1; + } + else + /* + * Error code returned is 1 if Reserve or Release + * failed or the input parameter is invalid + */ + if (status == 1 && (scp->cmnd[0] == RESERVE || + scp->cmnd[0] == RELEASE)) { + + scp->result = DID_ERROR << 16 | + RESERVATION_CONFLICT << 1; + } + else { + scp->result = DID_BAD_TARGET << 16 | status; + } + } + + // print a debug message for all failed commands + if (status) { + megaraid_mbox_display_scb(adapter, scb); + } + + // Free our internal resources and call the mid-layer callback + // routine + megaraid_mbox_sync_scb(adapter, scb); + + // remove from local clist + list_del_init(&scb->list); + + // put back in free list + megaraid_dealloc_scb(adapter, scb); + + // send the scsi packet back to kernel + spin_lock(adapter->host_lock); + scp->scsi_done(scp); + spin_unlock(adapter->host_lock); + } + + return; +} + + +/** + * megaraid_abort_handler - abort the scsi command + * @scp : command to be aborted + * + * Abort a previous SCSI request. Only commands on the pending list can be + * aborted. All the commands issued to the F/W must complete. + **/ +static int +megaraid_abort_handler(struct scsi_cmnd *scp) +{ + adapter_t *adapter; + mraid_device_t *raid_dev; + scb_t *scb; + scb_t *tmp; + int found; + unsigned long flags; + int i; + + + adapter = SCP2ADAPTER(scp); + raid_dev = ADAP2RAIDDEV(adapter); + + ASSERT(spin_is_locked(adapter->host_lock)); + + con_log(CL_ANN, (KERN_WARNING + "megaraid: aborting-%ld cmd=%x \n", + scp->serial_number, scp->cmnd[0], SCP2CHANNEL(scp), + SCP2TARGET(scp), SCP2LUN(scp))); + + // If FW has stopped responding, simply return failure + if (raid_dev->hw_error) { + con_log(CL_ANN, (KERN_NOTICE + "megaraid: hw error, not aborting\n")); + return FAILED; + } + + // There might a race here, where the command was completed by the + // firmware and now it is on the completed list. Before we could + // complete the command to the kernel in dpc, the abort came. + // Find out if this is the case to avoid the race. + scb = NULL; + spin_lock_irqsave(COMPLETED_LIST_LOCK(adapter), flags); + list_for_each_entry_safe(scb, tmp, &adapter->completed_list, list) { + + if (scb->scp == scp) { // Found command + + list_del_init(&scb->list); // from completed list + + con_log(CL_ANN, (KERN_WARNING + "megaraid: %ld:%d[%d:%d], abort from completed list\n", + scp->serial_number, scb->sno, + scb->dev_channel, scb->dev_target)); + + scp->result = (DID_ABORT << 16); + scp->scsi_done(scp); + + megaraid_dealloc_scb(adapter, scb); + + spin_unlock_irqrestore(COMPLETED_LIST_LOCK(adapter), + flags); + + return SUCCESS; + } + } + spin_unlock_irqrestore(COMPLETED_LIST_LOCK(adapter), flags); + + + // Find out if this command is still on the pending list. If it is and + // was never issued, abort and return success. If the command is owned + // by the firmware, we must wait for it to complete by the FW. + spin_lock_irqsave(PENDING_LIST_LOCK(adapter), flags); + list_for_each_entry_safe(scb, tmp, &adapter->pend_list, list) { + + if (scb->scp == scp) { // Found command + + list_del_init(&scb->list); // from pending list + + ASSERT(!(scb->state & SCB_ISSUED)); + + con_log(CL_ANN, (KERN_WARNING + "megaraid abort: %ld[%d:%d], driver owner\n", + scp->serial_number, scb->dev_channel, + scb->dev_target)); + + scp->result = (DID_ABORT << 16); + scp->scsi_done(scp); + + megaraid_dealloc_scb(adapter, scb); + + spin_unlock_irqrestore(PENDING_LIST_LOCK(adapter), + flags); + + return SUCCESS; + } + } + spin_unlock_irqrestore(PENDING_LIST_LOCK(adapter), flags); + + + // Check do we even own this command, in which case this would be + // owned by the firmware. The only way to locate the FW scb is to + // traverse through the list of all SCB, since driver does not + // maintain these SCBs on any list + found = 0; + for (i = 0; i < MBOX_MAX_SCSI_CMDS; i++) { + scb = adapter->kscb_list + i; + + if (scb->scp == scp) { + + found = 1; + + if (!(scb->state & SCB_ISSUED)) { + con_log(CL_ANN, (KERN_WARNING + "megaraid abort: %ld%d[%d:%d], invalid state\n", + scp->serial_number, scb->sno, scb->dev_channel, + scb->dev_target)); + BUG(); + } + else { + con_log(CL_ANN, (KERN_WARNING + "megaraid abort: %ld:%d[%d:%d], fw owner\n", + scp->serial_number, scb->sno, scb->dev_channel, + scb->dev_target)); + } + } + } + + if (!found) { + con_log(CL_ANN, (KERN_WARNING + "megaraid abort: scsi cmd:%ld, do now own\n", + scp->serial_number)); + + // FIXME: Should there be a callback for this command? + return SUCCESS; + } + + // We cannot actually abort a command owned by firmware, return + // failure and wait for reset. In host reset handler, we will find out + // if the HBA is still live + return FAILED; +} + + +/** + * megaraid_reset_handler - device reset hadler for mailbox based driver + * @scp : reference command + * + * Reset handler for the mailbox based controller. First try to find out if + * the FW is still live, in which case the outstanding commands counter mut go + * down to 0. If that happens, also issue the reservation reset command to + * relinquish (possible) reservations on the logical drives connected to this + * host + **/ +static int +megaraid_reset_handler(struct scsi_cmnd *scp) +{ + adapter_t *adapter; + scb_t *scb; + scb_t *tmp; + mraid_device_t *raid_dev; + unsigned long flags; + uint8_t raw_mbox[sizeof(mbox_t)]; + int rval; + int recovery_window; + int recovering; + int i; + + adapter = SCP2ADAPTER(scp); + raid_dev = ADAP2RAIDDEV(adapter); + + ASSERT(spin_is_locked(adapter->host_lock)); + + con_log(CL_ANN, (KERN_WARNING "megaraid: reseting the host...\n")); + + // return failure if adapter is not responding + if (raid_dev->hw_error) { + con_log(CL_ANN, (KERN_NOTICE + "megaraid: hw error, cannot reset\n")); + return FAILED; + } + + + // Under exceptional conditions, FW can take up to 3 minutes to + // complete command processing. Wait for additional 2 minutes for the + // pending commands counter to go down to 0. If it doesn't, let the + // controller be marked offline + // Also, reset all the commands currently owned by the driver + spin_lock_irqsave(PENDING_LIST_LOCK(adapter), flags); + list_for_each_entry_safe(scb, tmp, &adapter->pend_list, list) { + + list_del_init(&scb->list); // from pending list + + con_log(CL_ANN, (KERN_WARNING + "megaraid: %ld:%d[%d:%d], reset from pending list\n", + scp->serial_number, scb->sno, + scb->dev_channel, scb->dev_target)); + + scp->result = (DID_RESET << 16); + scp->scsi_done(scp); + + megaraid_dealloc_scb(adapter, scb); + } + spin_unlock_irqrestore(PENDING_LIST_LOCK(adapter), flags); + + if (adapter->outstanding_cmds) { + con_log(CL_ANN, (KERN_NOTICE + "megaraid: %d outstanding commands. Max wait %d sec\n", + adapter->outstanding_cmds, MBOX_RESET_WAIT)); + } + + spin_unlock(adapter->host_lock); + + recovery_window = MBOX_RESET_WAIT + MBOX_RESET_EXT_WAIT; + + recovering = adapter->outstanding_cmds; + + for (i = 0; i < recovery_window && adapter->outstanding_cmds; i++) { + + megaraid_ack_sequence(adapter); + + // print a message once every 5 seconds only + if (!(i % 5)) { + con_log(CL_ANN, ( + "megaraid mbox: Wait for %d commands to complete:%d\n", + adapter->outstanding_cmds, + MBOX_RESET_WAIT - i)); + } + + // bailout if no recovery happended in reset time + if ((i == MBOX_RESET_WAIT) && + (recovering == adapter->outstanding_cmds)) { + break; + } + + msleep(1000); + } + + spin_lock(adapter->host_lock); + + // If still outstanding commands, bail out + if (adapter->outstanding_cmds) { + con_log(CL_ANN, (KERN_WARNING + "megaraid mbox: critical hardware error!\n")); + + raid_dev->hw_error = 1; + + return FAILED; + } + else { + con_log(CL_ANN, (KERN_NOTICE + "megaraid mbox: reset sequence completed sucessfully\n")); + } + + + // If the controller supports clustering, reset reservations + if (!adapter->ha) return SUCCESS; + + // clear reservations if any + raw_mbox[0] = CLUSTER_CMD; + raw_mbox[2] = RESET_RESERVATIONS; + + rval = SUCCESS; + if (mbox_post_sync_cmd_fast(adapter, raw_mbox) == 0) { + con_log(CL_ANN, + (KERN_INFO "megaraid: reservation reset\n")); + } + else { + rval = FAILED; + con_log(CL_ANN, (KERN_WARNING + "megaraid: reservation reset failed\n")); + } + + return rval; +} + + +/* + * START: internal commands library + * + * This section of the driver has the common routine used by the driver and + * also has all the FW routines + */ + +/** + * mbox_post_sync_cmd() - blocking command to the mailbox based controllers + * @adapter - controller's soft state + * @raw_mbox - the mailbox + * + * Issue a scb in synchronous and non-interrupt mode for mailbox based + * controllers + */ +static int +mbox_post_sync_cmd(adapter_t *adapter, uint8_t raw_mbox[]) +{ + mraid_device_t *raid_dev = ADAP2RAIDDEV(adapter); + mbox64_t *mbox64; + mbox_t *mbox; + uint8_t status; + int i; + + + mbox64 = raid_dev->mbox64; + mbox = raid_dev->mbox; + + /* + * Wait until mailbox is free + */ + if (megaraid_busywait_mbox(raid_dev) != 0) + goto blocked_mailbox; + + /* + * Copy mailbox data into host structure + */ + memcpy((caddr_t)mbox, (caddr_t)raw_mbox, 16); + mbox->cmdid = 0xFE; + mbox->busy = 1; + mbox->poll = 0; + mbox->ack = 0; + mbox->numstatus = 0xFF; + mbox->status = 0xFF; + + wmb(); + WRINDOOR(raid_dev, raid_dev->mbox_dma | 0x1); + + // wait for maximum 1 second for status to post. If the status is not + // available within 1 second, assume FW is initializing and wait + // for an extended amount of time + if (mbox->numstatus == 0xFF) { // status not yet available + udelay(25);; + + for (i = 0; mbox->numstatus == 0xFF && i < 1000; i++) { + rmb(); + msleep(1); + } + + + if (i == 1000) { + con_log(CL_ANN, (KERN_NOTICE + "megaraid mailbox: wait for FW to boot ")); + + for (i = 0; (mbox->numstatus == 0xFF) && + (i < MBOX_RESET_WAIT); i++) { + rmb(); + con_log(CL_ANN, ("\b\b\b\b\b[%03d]", + MBOX_RESET_WAIT - i)); + msleep(1000); + } + + if (i == MBOX_RESET_WAIT) { + + con_log(CL_ANN, ( + "\nmegaraid mailbox: status not available\n")); + + return -1; + } + con_log(CL_ANN, ("\b\b\b\b\b[ok] \n")); + } + } + + // wait for maximum 1 second for poll semaphore + if (mbox->poll != 0x77) { + udelay(25); + + for (i = 0; (mbox->poll != 0x77) && (i < 1000); i++) { + rmb(); + msleep(1); + } + + if (i == 1000) { + con_log(CL_ANN, (KERN_WARNING + "megaraid mailbox: could not get poll semaphore\n")); + return -1; + } + } + + WRINDOOR(raid_dev, raid_dev->mbox_dma | 0x2); + wmb(); + + // wait for maximum 1 second for acknowledgement + if (RDINDOOR(raid_dev) & 0x2) { + udelay(25); + + for (i = 0; (RDINDOOR(raid_dev) & 0x2) && (i < 1000); i++) { + rmb(); + msleep(1); + } + + if (i == 1000) { + con_log(CL_ANN, (KERN_WARNING + "megaraid mailbox: could not acknowledge\n")); + return -1; + } + } + mbox->poll = 0; + mbox->ack = 0x77; + + status = mbox->status; + + // invalidate the completed command id array. After command + // completion, firmware would write the valid id. + mbox->numstatus = 0xFF; + mbox->status = 0xFF; + for (i = 0; i < MBOX_MAX_FIRMWARE_STATUS; i++) { + mbox->completed[i] = 0xFF; + } + + return status; + +blocked_mailbox: + + con_log(CL_ANN, (KERN_WARNING "megaraid: blocked mailbox\n") ); + return -1; +} + + +/** + * mbox_post_sync_cmd_fast - blocking command to the mailbox based controllers + * @adapter - controller's soft state + * @raw_mbox - the mailbox + * + * Issue a scb in synchronous and non-interrupt mode for mailbox based + * controllers. This is a faster version of the synchronous command and + * therefore can be called in interrupt-context as well + */ +static int +mbox_post_sync_cmd_fast(adapter_t *adapter, uint8_t raw_mbox[]) +{ + mraid_device_t *raid_dev = ADAP2RAIDDEV(adapter); + mbox_t *mbox; + long i; + + + mbox = raid_dev->mbox; + + // return immediately if the mailbox is busy + if (mbox->busy) return -1; + + // Copy mailbox data into host structure + memcpy((caddr_t)mbox, (caddr_t)raw_mbox, 14); + mbox->cmdid = 0xFE; + mbox->busy = 1; + mbox->poll = 0; + mbox->ack = 0; + mbox->numstatus = 0xFF; + mbox->status = 0xFF; + + wmb(); + WRINDOOR(raid_dev, raid_dev->mbox_dma | 0x1); + + for (i = 0; i < 0xFFFFF; i++) { + if (mbox->numstatus != 0xFF) break; + } + + if (i == 0xFFFFF) { + // We may need to re-calibrate the counter + con_log(CL_ANN, (KERN_CRIT + "megaraid: fast sync command timed out\n")); + } + + WRINDOOR(raid_dev, raid_dev->mbox_dma | 0x2); + wmb(); + + return mbox->status; +} + + +/** + * megaraid_busywait_mbox() - Wait until the controller's mailbox is available + * @raid_dev - RAID device (HBA) soft state + * + * wait until the controller's mailbox is available to accept more commands. + * wait for at most 1 second + */ +static int +megaraid_busywait_mbox(mraid_device_t *raid_dev) +{ + mbox_t *mbox = raid_dev->mbox; + int i = 0; + + if (mbox->busy) { + udelay(25); + for (i = 0; mbox->busy && i < 1000; i++) + msleep(1); + } + + if (i < 1000) return 0; + else return -1; +} + + +/** + * megaraid_mbox_product_info - some static information about the controller + * @adapter - our soft state + * + * issue commands to the controller to grab some parameters required by our + * caller. + */ +static int +megaraid_mbox_product_info(adapter_t *adapter) +{ + mraid_device_t *raid_dev = ADAP2RAIDDEV(adapter); + mbox_t *mbox; + uint8_t raw_mbox[sizeof(mbox_t)]; + mraid_pinfo_t *pinfo; + dma_addr_t pinfo_dma_h; + mraid_inquiry3_t *mraid_inq3; + int i; + + + memset((caddr_t)raw_mbox, 0, sizeof(raw_mbox)); + mbox = (mbox_t *)raw_mbox; + + /* + * Issue an ENQUIRY3 command to find out certain adapter parameters, + * e.g., max channels, max commands etc. + */ + pinfo = pci_alloc_consistent(adapter->pdev, sizeof(mraid_pinfo_t), + &pinfo_dma_h); + + if (pinfo == NULL) { + con_log(CL_ANN, (KERN_WARNING + "megaraid: out of memory, %s %d\n", __FUNCTION__, + __LINE__)); + + return -1; + } + memset(pinfo, 0, sizeof(mraid_pinfo_t)); + + mbox->xferaddr = (uint32_t)adapter->ibuf_dma_h; + memset((void *)adapter->ibuf, 0, MBOX_IBUF_SIZE); + + raw_mbox[0] = FC_NEW_CONFIG; + raw_mbox[2] = NC_SUBOP_ENQUIRY3; + raw_mbox[3] = ENQ3_GET_SOLICITED_FULL; + + // Issue the command + if (mbox_post_sync_cmd(adapter, raw_mbox) != 0) { + + con_log(CL_ANN, (KERN_WARNING "megaraid: Inquiry3 failed\n")); + + pci_free_consistent(adapter->pdev, sizeof(mraid_pinfo_t), + pinfo, pinfo_dma_h); + + return -1; + } + + /* + * Collect information about state of each physical drive + * attached to the controller. We will expose all the disks + * which are not part of RAID + */ + mraid_inq3 = (mraid_inquiry3_t *)adapter->ibuf; + for (i = 0; i < MBOX_MAX_PHYSICAL_DRIVES; i++) { + raid_dev->pdrv_state[i] = mraid_inq3->pdrv_state[i]; + } + + /* + * Get product info for information like number of channels, + * maximum commands supported. + */ + memset((caddr_t)raw_mbox, 0, sizeof(raw_mbox)); + mbox->xferaddr = (uint32_t)pinfo_dma_h; + + raw_mbox[0] = FC_NEW_CONFIG; + raw_mbox[2] = NC_SUBOP_PRODUCT_INFO; + + if (mbox_post_sync_cmd(adapter, raw_mbox) != 0) { + + con_log(CL_ANN, (KERN_WARNING + "megaraid: product info failed\n")); + + pci_free_consistent(adapter->pdev, sizeof(mraid_pinfo_t), + pinfo, pinfo_dma_h); + + return -1; + } + + /* + * Setup some parameters for host, as required by our caller + */ + adapter->max_channel = pinfo->nchannels; + + /* + * we will export all the logical drives on a single channel. + * Add 1 since inquires do not come for inititor ID + */ + adapter->max_target = MAX_LOGICAL_DRIVES_40LD + 1; + adapter->max_lun = 8; // up to 8 LUNs for non-disk devices + + /* + * These are the maximum outstanding commands for the scsi-layer + */ + adapter->max_cmds = MBOX_MAX_SCSI_CMDS; + + memset(adapter->fw_version, 0, VERSION_SIZE); + memset(adapter->bios_version, 0, VERSION_SIZE); + + memcpy(adapter->fw_version, pinfo->fw_version, 4); + adapter->fw_version[4] = 0; + + memcpy(adapter->bios_version, pinfo->bios_version, 4); + adapter->bios_version[4] = 0; + + con_log(CL_ANN, (KERN_NOTICE + "megaraid: fw version:[%s] bios version:[%s]\n", + adapter->fw_version, adapter->bios_version)); + + pci_free_consistent(adapter->pdev, sizeof(mraid_pinfo_t), pinfo, + pinfo_dma_h); + + return 0; +} + + + +/** + * megaraid_mbox_extended_cdb - check for support for extended CDBs + * @adapter - soft state for the controller + * + * this routine check whether the controller in question supports extended + * ( > 10 bytes ) CDBs + */ +static int +megaraid_mbox_extended_cdb(adapter_t *adapter) +{ + mbox_t *mbox; + uint8_t raw_mbox[sizeof(mbox_t)]; + int rval; + + mbox = (mbox_t *)raw_mbox; + + memset((caddr_t)raw_mbox, 0, sizeof(raw_mbox)); + mbox->xferaddr = (uint32_t)adapter->ibuf_dma_h; + + memset((void *)adapter->ibuf, 0, MBOX_IBUF_SIZE); + + raw_mbox[0] = MAIN_MISC_OPCODE; + raw_mbox[2] = SUPPORT_EXT_CDB; + + /* + * Issue the command + */ + rval = 0; + if (mbox_post_sync_cmd(adapter, raw_mbox) != 0) { + rval = -1; + } + + return rval; +} + + +/** + * megaraid_mbox_support_ha - Do we support clustering + * @adapter - soft state for the controller + * @init_id - ID of the initiator + * + * Determine if the firmware supports clustering and the ID of the initiator. + */ +static int +megaraid_mbox_support_ha(adapter_t *adapter, uint16_t *init_id) +{ + mbox_t *mbox; + uint8_t raw_mbox[sizeof(mbox_t)]; + int rval; + + + mbox = (mbox_t *)raw_mbox; + + memset((caddr_t)raw_mbox, 0, sizeof(raw_mbox)); + + mbox->xferaddr = (uint32_t)adapter->ibuf_dma_h; + + memset((void *)adapter->ibuf, 0, MBOX_IBUF_SIZE); + + raw_mbox[0] = GET_TARGET_ID; + + // Issue the command + *init_id = 7; + rval = -1; + if (mbox_post_sync_cmd(adapter, raw_mbox) == 0) { + + *init_id = *(uint8_t *)adapter->ibuf; + + con_log(CL_ANN, (KERN_INFO + "megaraid: cluster firmware, initiator ID: %d\n", + *init_id)); + + rval = 0; + } + + return rval; +} + + +/** + * megaraid_mbox_support_random_del - Do we support random deletion + * @adapter - soft state for the controller + * + * Determine if the firmware supports random deletion + * Return: 1 is operation supported, 0 otherwise + */ +static int +megaraid_mbox_support_random_del(adapter_t *adapter) +{ + mbox_t *mbox; + uint8_t raw_mbox[sizeof(mbox_t)]; + int rval; + + + mbox = (mbox_t *)raw_mbox; + + memset((caddr_t)raw_mbox, 0, sizeof(mbox_t)); + + raw_mbox[0] = FC_DEL_LOGDRV; + raw_mbox[0] = OP_SUP_DEL_LOGDRV; + + // Issue the command + rval = 0; + if (mbox_post_sync_cmd(adapter, raw_mbox) == 0) { + + con_log(CL_DLEVEL1, ("megaraid: supports random deletion\n")); + + rval = 1; + } + + return rval; +} + + +/** + * megaraid_mbox_get_max_sg - maximum sg elements supported by the firmware + * @adapter - soft state for the controller + * + * Find out the maximum number of scatter-gather elements supported by the + * firmware + */ +static int +megaraid_mbox_get_max_sg(adapter_t *adapter) +{ + mbox_t *mbox; + uint8_t raw_mbox[sizeof(mbox_t)]; + int nsg; + + + mbox = (mbox_t *)raw_mbox; + + memset((caddr_t)raw_mbox, 0, sizeof(mbox_t)); + + mbox->xferaddr = (uint32_t)adapter->ibuf_dma_h; + + memset((void *)adapter->ibuf, 0, MBOX_IBUF_SIZE); + + raw_mbox[0] = MAIN_MISC_OPCODE; + raw_mbox[2] = GET_MAX_SG_SUPPORT; + + // Issue the command + if (mbox_post_sync_cmd(adapter, raw_mbox) == 0) { + nsg = *(uint8_t *)adapter->ibuf; + } + else { + nsg = MBOX_DEFAULT_SG_SIZE; + } + + if (nsg > MBOX_MAX_SG_SIZE) nsg = MBOX_MAX_SG_SIZE; + + return nsg; +} + + +/** + * megaraid_mbox_enum_raid_scsi - enumerate the RAID and SCSI channels + * @adapter - soft state for the controller + * + * Enumerate the RAID and SCSI channels for ROMB platoforms so that channels + * can be exported as regular SCSI channels + */ +static void +megaraid_mbox_enum_raid_scsi(adapter_t *adapter) +{ + mraid_device_t *raid_dev = ADAP2RAIDDEV(adapter); + mbox_t *mbox; + uint8_t raw_mbox[sizeof(mbox_t)]; + + + mbox = (mbox_t *)raw_mbox; + + memset((caddr_t)raw_mbox, 0, sizeof(mbox_t)); + + mbox->xferaddr = (uint32_t)adapter->ibuf_dma_h; + + memset((void *)adapter->ibuf, 0, MBOX_IBUF_SIZE); + + raw_mbox[0] = CHNL_CLASS; + raw_mbox[2] = GET_CHNL_CLASS; + + // Issue the command. If the command fails, all channels are RAID + // channels + raid_dev->channel_class = 0xFF; + if (mbox_post_sync_cmd(adapter, raw_mbox) == 0) { + raid_dev->channel_class = *(uint8_t *)adapter->ibuf; + } + + return; +} + + +/** + * megaraid_mbox_flush_cache - flush adapter and disks cache + * @param adapter : soft state for the controller + * + * Flush adapter cache followed by disks cache + */ +static void +megaraid_mbox_flush_cache(adapter_t *adapter) +{ + mbox_t *mbox; + uint8_t raw_mbox[sizeof(mbox_t)]; + + + mbox = (mbox_t *)raw_mbox; + + memset((caddr_t)raw_mbox, 0, sizeof(mbox_t)); + + raw_mbox[0] = FLUSH_ADAPTER; + + if (mbox_post_sync_cmd(adapter, raw_mbox) != 0) { + con_log(CL_ANN, ("megaraid: flush adapter failed\n")); + } + + raw_mbox[0] = FLUSH_SYSTEM; + + if (mbox_post_sync_cmd(adapter, raw_mbox) != 0) { + con_log(CL_ANN, ("megaraid: flush disks cache failed\n")); + } + + return; +} + + +/** + * megaraid_mbox_display_scb - display SCB information, mostly debug purposes + * @param adapter : controllers' soft state + * @param scb : SCB to be displayed + * @param level : debug level for console print + * + * Diplay information about the given SCB iff the current debug level is + * verbose + */ +static void +megaraid_mbox_display_scb(adapter_t *adapter, scb_t *scb) +{ + mbox_ccb_t *ccb; + struct scsi_cmnd *scp; + mbox_t *mbox; + int level; + int i; + + + ccb = (mbox_ccb_t *)scb->ccb; + scp = scb->scp; + mbox = ccb->mbox; + + level = CL_DLEVEL3; + + con_log(level, (KERN_NOTICE + "megaraid mailbox: status:%#x cmd:%#x id:%#x ", scb->status, + mbox->cmd, scb->sno)); + + con_log(level, ("sec:%#x lba:%#x addr:%#x ld:%d sg:%d\n", + mbox->numsectors, mbox->lba, mbox->xferaddr, mbox->logdrv, + mbox->numsge)); + + if (!scp) return; + + con_log(level, (KERN_NOTICE "scsi cmnd: ")); + + for (i = 0; i < scp->cmd_len; i++) { + con_log(level, ("%#2.02x ", scp->cmnd[i])); + } + + con_log(level, ("\n")); + + return; +} + + +/** + * megaraid_mbox_setup_device_map - manage device ids + * @adapter : Driver's soft state + * + * Manange the device ids to have an appropraite mapping between the kernel + * scsi addresses and megaraid scsi and logical drive addresses. We export + * scsi devices on their actual addresses, whereas the logical drives are + * exported on a virtual scsi channel. + **/ +static void +megaraid_mbox_setup_device_map(adapter_t *adapter) +{ + uint8_t c; + uint8_t t; + + /* + * First fill the values on the logical drive channel + */ + for (t = 0; t < LSI_MAX_LOGICAL_DRIVES_64LD; t++) + adapter->device_ids[adapter->max_channel][t] = + (t < adapter->init_id) ? t : t - 1; + + adapter->device_ids[adapter->max_channel][adapter->init_id] = 0xFF; + + /* + * Fill the values on the physical devices channels + */ + for (c = 0; c < adapter->max_channel; c++) + for (t = 0; t < LSI_MAX_LOGICAL_DRIVES_64LD; t++) + adapter->device_ids[c][t] = (c << 8) | t; +} + + +/* + * END: internal commands library + */ + +/* + * START: Interface for the common management module + * + * This is the module, which interfaces with the common mangement module to + * provide support for ioctl and sysfs + */ + +/** + * megaraid_cmm_register - register with the mangement module + * @param adapter : HBA soft state + * + * Register with the management module, which allows applications to issue + * ioctl calls to the drivers. This interface is used by the management module + * to setup sysfs support as well. + */ +static int +megaraid_cmm_register(adapter_t *adapter) +{ + mraid_device_t *raid_dev = ADAP2RAIDDEV(adapter); + mraid_mmadp_t adp; + scb_t *scb; + mbox_ccb_t *ccb; + int rval; + int i; + + // Allocate memory for the base list of scb for management module. + adapter->uscb_list = kmalloc(sizeof(scb_t) * MBOX_MAX_USER_CMDS, + GFP_KERNEL); + + if (adapter->uscb_list == NULL) { + con_log(CL_ANN, (KERN_WARNING + "megaraid: out of memory, %s %d\n", __FUNCTION__, + __LINE__)); + return -1; + } + memset(adapter->uscb_list, 0, sizeof(scb_t) * MBOX_MAX_USER_CMDS); + + + // Initialize the synchronization parameters for resources for + // commands for management module + INIT_LIST_HEAD(&adapter->uscb_pool); + + spin_lock_init(USER_FREE_LIST_LOCK(adapter)); + + + + // link all the packets. Note, CCB for commands, coming from the + // commom management module, mailbox physical address are already + // setup by it. We just need placeholder for that in our local command + // control blocks + for (i = 0; i < MBOX_MAX_USER_CMDS; i++) { + + scb = adapter->uscb_list + i; + ccb = raid_dev->uccb_list + i; + + scb->ccb = (caddr_t)ccb; + ccb->mbox64 = raid_dev->umbox64 + i; + ccb->mbox = &ccb->mbox64->mbox32; + ccb->raw_mbox = (uint8_t *)ccb->mbox; + + scb->gp = 0; + + // COMMAND ID 0 - (MBOX_MAX_SCSI_CMDS-1) ARE RESERVED FOR + // COMMANDS COMING FROM IO SUBSYSTEM (MID-LAYER) + scb->sno = i + MBOX_MAX_SCSI_CMDS; + + scb->scp = NULL; + scb->state = SCB_FREE; + scb->dma_direction = PCI_DMA_NONE; + scb->dma_type = MRAID_DMA_NONE; + scb->dev_channel = -1; + scb->dev_target = -1; + + // put scb in the free pool + list_add_tail(&scb->list, &adapter->uscb_pool); + } + + adp.unique_id = adapter->unique_id; + adp.drvr_type = DRVRTYPE_MBOX; + adp.drvr_data = (unsigned long)adapter; + adp.pdev = adapter->pdev; + adp.issue_uioc = megaraid_mbox_mm_handler; + adp.timeout = 30; + adp.max_kioc = MBOX_MAX_USER_CMDS; + + if ((rval = mraid_mm_register_adp(&adp)) != 0) { + + con_log(CL_ANN, (KERN_WARNING + "megaraid mbox: did not register with CMM\n")); + + kfree(adapter->uscb_list); + } + + return rval; +} + + +/** + * megaraid_cmm_unregister - un-register with the mangement module + * @param adapter : HBA soft state + * + * Un-register with the management module. + * FIXME: mgmt module must return failure for unregister if it has pending + * commands in LLD + */ +static int +megaraid_cmm_unregister(adapter_t *adapter) +{ + kfree(adapter->uscb_list); + mraid_mm_unregister_adp(adapter->unique_id); + return 0; +} + + +/** + * megaraid_mbox_mm_handler - interface for CMM to issue commands to LLD + * @param drvr_data : LLD specific data + * @param kioc : CMM interface packet + * @param action : command action + * + * This routine is invoked whenever the Common Mangement Module (CMM) has a + * command for us. The 'action' parameter specifies if this is a new command + * or otherwise. + */ +static int +megaraid_mbox_mm_handler(unsigned long drvr_data, uioc_t *kioc, uint32_t action) +{ + adapter_t *adapter; + + if (action != IOCTL_ISSUE) { + con_log(CL_ANN, (KERN_WARNING + "megaraid: unsupported management action:%#2x\n", + action)); + return (-ENOTSUPP); + } + + adapter = (adapter_t *)drvr_data; + + // make sure this adapter is not being detached right now. + if (atomic_read(&adapter->being_detached)) { + con_log(CL_ANN, (KERN_WARNING + "megaraid: reject management request, detaching\n")); + return (-ENODEV); + } + + switch (kioc->opcode) { + + case GET_ADAP_INFO: + + kioc->status = gather_hbainfo(adapter, (mraid_hba_info_t *) + (unsigned long)kioc->buf_vaddr); + + kioc->done(kioc); + + return kioc->status; + + case MBOX_CMD: + + return megaraid_mbox_mm_command(adapter, kioc); + + default: + kioc->status = (-EINVAL); + kioc->done(kioc); + return (-EINVAL); + } + + return 0; // not reached +} + +/** + * megaraid_mbox_mm_command - issues commands routed through CMM + * @param adapter : HBA soft state + * @param kioc : management command packet + * + * Issues commands, which are routed through the management module. + */ +static int +megaraid_mbox_mm_command(adapter_t *adapter, uioc_t *kioc) +{ + struct list_head *head = &adapter->uscb_pool; + mbox64_t *mbox64; + uint8_t *raw_mbox; + scb_t *scb; + mbox_ccb_t *ccb; + unsigned long flags; + + // detach one scb from free pool + spin_lock_irqsave(USER_FREE_LIST_LOCK(adapter), flags); + + if (list_empty(head)) { // should never happen because of CMM + + con_log(CL_ANN, (KERN_WARNING + "megaraid mbox: bug in cmm handler, lost resources\n")); + + spin_unlock_irqrestore(USER_FREE_LIST_LOCK(adapter), flags); + + return (-EINVAL); + } + + scb = list_entry(head->next, scb_t, list); + list_del_init(&scb->list); + + spin_unlock_irqrestore(USER_FREE_LIST_LOCK(adapter), flags); + + scb->state = SCB_ACTIVE; + scb->dma_type = MRAID_DMA_NONE; + + ccb = (mbox_ccb_t *)scb->ccb; + mbox64 = (mbox64_t *)(unsigned long)kioc->cmdbuf; + raw_mbox = (uint8_t *)&mbox64->mbox32; + + memcpy(ccb->mbox64, mbox64, sizeof(mbox64_t)); + + scb->gp = (unsigned long)kioc; + + /* + * If it is a logdrv random delete operation, we have to wait till + * there are no outstanding cmds at the fw and then issue it directly + */ + if (raw_mbox[0] == FC_DEL_LOGDRV && raw_mbox[2] == OP_DEL_LOGDRV) { + + if (wait_till_fw_empty(adapter)) { + con_log(CL_ANN, (KERN_NOTICE + "megaraid mbox: LD delete, timed out\n")); + + kioc->status = -ETIME; + + scb->status = -1; + + megaraid_mbox_mm_done(adapter, scb); + + return (-ETIME); + } + + INIT_LIST_HEAD(&scb->list); + + scb->state = SCB_ISSUED; + if (mbox_post_cmd(adapter, scb) != 0) { + + con_log(CL_ANN, (KERN_NOTICE + "megaraid mbox: LD delete, mailbox busy\n")); + + kioc->status = -EBUSY; + + scb->status = -1; + + megaraid_mbox_mm_done(adapter, scb); + + return (-EBUSY); + } + + return 0; + } + + // put the command on the pending list and execute + megaraid_mbox_runpendq(adapter, scb); + + return 0; +} + + +static int +wait_till_fw_empty(adapter_t *adapter) +{ + unsigned long flags = 0; + int i; + + + /* + * Set the quiescent flag to stop issuing cmds to FW. + */ + spin_lock_irqsave(adapter->host_lock, flags); + adapter->quiescent++; + spin_unlock_irqrestore(adapter->host_lock, flags); + + /* + * Wait till there are no more cmds outstanding at FW. Try for at most + * 60 seconds + */ + for (i = 0; i < 60 && adapter->outstanding_cmds; i++) { + con_log(CL_DLEVEL1, (KERN_INFO + "megaraid: FW has %d pending commands\n", + adapter->outstanding_cmds)); + + msleep(1000); + } + + return adapter->outstanding_cmds; +} + + +/** + * megaraid_mbox_mm_done - callback for CMM commands + * @adapter : HBA soft state + * @scb : completed command + * + * Callback routine for internal commands originated from the management + * module. + */ +static void +megaraid_mbox_mm_done(adapter_t *adapter, scb_t *scb) +{ + uioc_t *kioc; + mbox64_t *mbox64; + uint8_t *raw_mbox; + unsigned long flags; + + kioc = (uioc_t *)scb->gp; + kioc->status = 0; + mbox64 = (mbox64_t *)(unsigned long)kioc->cmdbuf; + mbox64->mbox32.status = scb->status; + raw_mbox = (uint8_t *)&mbox64->mbox32; + + + // put scb in the free pool + scb->state = SCB_FREE; + scb->scp = NULL; + + spin_lock_irqsave(USER_FREE_LIST_LOCK(adapter), flags); + + list_add(&scb->list, &adapter->uscb_pool); + + spin_unlock_irqrestore(USER_FREE_LIST_LOCK(adapter), flags); + + // if a delete logical drive operation succeeded, restart the + // controller + if (raw_mbox[0] == FC_DEL_LOGDRV && raw_mbox[2] == OP_DEL_LOGDRV) { + + adapter->quiescent--; + + megaraid_mbox_runpendq(adapter, NULL); + } + + kioc->done(kioc); + + return; +} + + +/** + * gather_hbainfo - HBA characteristics for the applications + * @param adapter : HBA soft state + * @param hinfo : pointer to the caller's host info strucuture + */ +static int +gather_hbainfo(adapter_t *adapter, mraid_hba_info_t *hinfo) +{ + uint8_t dmajor; + + dmajor = megaraid_mbox_version[0]; + + hinfo->pci_vendor_id = adapter->pdev->vendor; + hinfo->pci_device_id = adapter->pdev->device; + hinfo->subsys_vendor_id = adapter->pdev->subsystem_vendor; + hinfo->subsys_device_id = adapter->pdev->subsystem_device; + + hinfo->pci_bus = adapter->pdev->bus->number; + hinfo->pci_dev_fn = adapter->pdev->devfn; + hinfo->pci_slot = PCI_SLOT(adapter->pdev->devfn); + hinfo->irq = adapter->host->irq; + hinfo->baseport = ADAP2RAIDDEV(adapter)->baseport; + + hinfo->unique_id = (hinfo->pci_bus << 8) | adapter->pdev->devfn; + hinfo->host_no = adapter->host->host_no; + + return 0; +} + +/* + * END: Interface for the common management module + */ + + +/* + * END: Mailbox Low Level Driver + */ +module_init(megaraid_init); +module_exit(megaraid_exit); + +/* vim: set ts=8 sw=8 tw=78 ai si: */ diff --git a/drivers/scsi/megaraid/megaraid_mbox.h b/drivers/scsi/megaraid/megaraid_mbox.h new file mode 100644 index 000000000..2f195abc6 --- /dev/null +++ b/drivers/scsi/megaraid/megaraid_mbox.h @@ -0,0 +1,268 @@ +/* + * + * Linux MegaRAID device driver + * + * Copyright (c) 2003-2004 LSI Logic Corporation. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + * + * FILE : megaraid_mbox.h + */ + +#ifndef _MEGARAID_H_ +#define _MEGARAID_H_ + + +#include "mega_common.h" +#include "mbox_defs.h" +#include "megaraid_ioctl.h" + + +#define MEGARAID_VERSION "2.20.4.0" +#define MEGARAID_EXT_VERSION "(Release Date: Mon Sep 27 22:15:07 EDT 2004)" + + +/* + * Define some PCI values here until they are put in the kernel + */ +#define PCI_DEVICE_ID_PERC4_DI_DISCOVERY 0x000E +#define PCI_SUBSYS_ID_PERC4_DI_DISCOVERY 0x0123 + +#define PCI_DEVICE_ID_PERC4_SC 0x1960 +#define PCI_SUBSYS_ID_PERC4_SC 0x0520 + +#define PCI_DEVICE_ID_PERC4_DC 0x1960 +#define PCI_SUBSYS_ID_PERC4_DC 0x0518 + +#define PCI_DEVICE_ID_PERC4_QC 0x0407 +#define PCI_SUBSYS_ID_PERC4_QC 0x0531 + +#define PCI_DEVICE_ID_PERC4_DI_EVERGLADES 0x000F +#define PCI_SUBSYS_ID_PERC4_DI_EVERGLADES 0x014A + +#define PCI_DEVICE_ID_PERC4E_SI_BIGBEND 0x0013 +#define PCI_SUBSYS_ID_PERC4E_SI_BIGBEND 0x016c + +#define PCI_DEVICE_ID_PERC4E_DI_KOBUK 0x0013 +#define PCI_SUBSYS_ID_PERC4E_DI_KOBUK 0x016d + +#define PCI_DEVICE_ID_PERC4E_DI_CORVETTE 0x0013 +#define PCI_SUBSYS_ID_PERC4E_DI_CORVETTE 0x016e + +#define PCI_DEVICE_ID_PERC4E_DI_EXPEDITION 0x0013 +#define PCI_SUBSYS_ID_PERC4E_DI_EXPEDITION 0x016f + +#define PCI_DEVICE_ID_PERC4E_DI_GUADALUPE 0x0013 +#define PCI_SUBSYS_ID_PERC4E_DI_GUADALUPE 0x0170 + +#define PCI_DEVICE_ID_PERC4E_DC_320_2E 0x0408 +#define PCI_SUBSYS_ID_PERC4E_DC_320_2E 0x0002 + +#define PCI_DEVICE_ID_PERC4E_SC_320_1E 0x0408 +#define PCI_SUBSYS_ID_PERC4E_SC_320_1E 0x0001 + +#define PCI_DEVICE_ID_MEGARAID_SCSI_320_0 0x1960 +#define PCI_SUBSYS_ID_MEGARAID_SCSI_320_0 0xA520 + +#define PCI_DEVICE_ID_MEGARAID_SCSI_320_1 0x1960 +#define PCI_SUBSYS_ID_MEGARAID_SCSI_320_1 0x0520 + +#define PCI_DEVICE_ID_MEGARAID_SCSI_320_2 0x1960 +#define PCI_SUBSYS_ID_MEGARAID_SCSI_320_2 0x0518 + +#define PCI_DEVICE_ID_MEGARAID_SCSI_320_0x 0x0407 +#define PCI_SUBSYS_ID_MEGARAID_SCSI_320_0x 0x0530 + +#define PCI_DEVICE_ID_MEGARAID_SCSI_320_2x 0x0407 +#define PCI_SUBSYS_ID_MEGARAID_SCSI_320_2x 0x0532 + +#define PCI_DEVICE_ID_MEGARAID_SCSI_320_4x 0x0407 +#define PCI_SUBSYS_ID_MEGARAID_SCSI_320_4x 0x0531 + +#define PCI_DEVICE_ID_MEGARAID_SCSI_320_1E 0x0408 +#define PCI_SUBSYS_ID_MEGARAID_SCSI_320_1E 0x0001 + +#define PCI_DEVICE_ID_MEGARAID_SCSI_320_2E 0x0408 +#define PCI_SUBSYS_ID_MEGARAID_SCSI_320_2E 0x0002 + +#define PCI_DEVICE_ID_MEGARAID_I4_133_RAID 0x1960 +#define PCI_SUBSYS_ID_MEGARAID_I4_133_RAID 0x0522 + +#define PCI_DEVICE_ID_MEGARAID_SATA_150_4 0x1960 +#define PCI_SUBSYS_ID_MEGARAID_SATA_150_4 0x4523 + +#define PCI_DEVICE_ID_MEGARAID_SATA_150_6 0x1960 +#define PCI_SUBSYS_ID_MEGARAID_SATA_150_6 0x0523 + +#define PCI_DEVICE_ID_MEGARAID_SATA_300_4x 0x0409 +#define PCI_SUBSYS_ID_MEGARAID_SATA_300_4x 0x3004 + +#define PCI_DEVICE_ID_MEGARAID_SATA_300_8x 0x0409 +#define PCI_SUBSYS_ID_MEGARAID_SATA_300_8x 0x3008 + +#define PCI_DEVICE_ID_INTEL_RAID_SRCU42X 0x0407 +#define PCI_SUBSYS_ID_INTEL_RAID_SRCU42X 0x0532 + +#define PCI_DEVICE_ID_INTEL_RAID_SRCS16 0x1960 +#define PCI_SUBSYS_ID_INTEL_RAID_SRCS16 0x0523 + +#define PCI_DEVICE_ID_INTEL_RAID_SRCU42E 0x0408 +#define PCI_SUBSYS_ID_INTEL_RAID_SRCU42E 0x0002 + +#define PCI_DEVICE_ID_INTEL_RAID_SRCZCRX 0x0407 +#define PCI_SUBSYS_ID_INTEL_RAID_SRCZCRX 0x0530 + +#define PCI_DEVICE_ID_INTEL_RAID_SRCS28X 0x0409 +#define PCI_SUBSYS_ID_INTEL_RAID_SRCS28X 0x3008 + +#define PCI_DEVICE_ID_INTEL_RAID_SROMBU42E_ALIEF 0x0408 +#define PCI_SUBSYS_ID_INTEL_RAID_SROMBU42E_ALIEF 0x3431 + +#define PCI_DEVICE_ID_INTEL_RAID_SROMBU42E_HARWICH 0x0408 +#define PCI_SUBSYS_ID_INTEL_RAID_SROMBU42E_HARWICH 0x3499 + +#define PCI_DEVICE_ID_INTEL_RAID_SRCU41L_LAKE_SHETEK 0x1960 +#define PCI_SUBSYS_ID_INTEL_RAID_SRCU41L_LAKE_SHETEK 0x0520 + +#define PCI_DEVICE_ID_FSC_MEGARAID_PCI_EXPRESS_ROMB 0x0408 +#define PCI_SUBSYS_ID_FSC_MEGARAID_PCI_EXPRESS_ROMB 0x1065 + +#define PCI_DEVICE_ID_MEGARAID_ACER_ROMB_2E 0x0408 +#define PCI_SUBSYS_ID_MEGARAID_ACER_ROMB_2E 0x004D + +#define PCI_SUBSYS_ID_PERC3_QC 0x0471 +#define PCI_SUBSYS_ID_PERC3_DC 0x0493 +#define PCI_SUBSYS_ID_PERC3_SC 0x0475 + +#ifndef PCI_SUBSYS_ID_FSC +#define PCI_SUBSYS_ID_FSC 0x1734 +#endif + +#define MBOX_MAX_SCSI_CMDS 128 // number of cmds reserved for kernel +#define MBOX_MAX_USER_CMDS 32 // number of cmds for applications +#define MBOX_DEF_CMD_PER_LUN 64 // default commands per lun +#define MBOX_DEFAULT_SG_SIZE 26 // default sg size supported by all fw +#define MBOX_MAX_SG_SIZE 32 // maximum scatter-gather list size +#define MBOX_MAX_SECTORS 128 // maximum sectors per IO +#define MBOX_TIMEOUT 30 // timeout value for internal cmds +#define MBOX_BUSY_WAIT 10 // max usec to wait for busy mailbox +#define MBOX_RESET_WAIT 180 // wait these many seconds in reset +#define MBOX_RESET_EXT_WAIT 120 // extended wait reset + +/* + * maximum transfer that can happen through the firmware commands issued + * internnaly from the driver. + */ +#define MBOX_IBUF_SIZE 4096 + + +/** + * mbox_ccb_t - command control block specific to mailbox based controllers + * @raw_mbox : raw mailbox pointer + * @mbox : mailbox + * @mbox64 : extended mailbox + * @mbox_dma_h : maibox dma address + * @sgl64 : 64-bit scatter-gather list + * @sgl32 : 32-bit scatter-gather list + * @sgl_dma_h : dma handle for the scatter-gather list + * @pthru : passthru structure + * @pthru_dma_h : dma handle for the passthru structure + * @epthru : extended passthru structure + * @epthru_dma_h : dma handle for extended passthru structure + * @buf_dma_h : dma handle for buffers w/o sg list + * + * command control block specific to the mailbox based controllers + */ +typedef struct { + uint8_t *raw_mbox; + mbox_t *mbox; + mbox64_t *mbox64; + dma_addr_t mbox_dma_h; + mbox_sgl64 *sgl64; + mbox_sgl32 *sgl32; + dma_addr_t sgl_dma_h; + mraid_passthru_t *pthru; + dma_addr_t pthru_dma_h; + mraid_epassthru_t *epthru; + dma_addr_t epthru_dma_h; + dma_addr_t buf_dma_h; +} mbox_ccb_t; + + +/** + * mraid_device_t - adapter soft state structure for mailbox controllers + * @param una_mbox64 : 64-bit mbox - unaligned + * @param una_mbox64_dma : mbox dma addr - unaligned + * @param mbox : 32-bit mbox - aligned + * @param mbox64 : 64-bit mbox - aligned + * @param mbox_dma : mbox dma addr - aligned + * @param mailbox_lock : exclusion lock for the mailbox + * @param baseport : base port of hba memory + * @param baseaddr : mapped addr of hba memory + * @param mbox_pool : pool of mailboxes + * @param mbox_pool_handle : handle for the mailbox pool memory + * @param epthru_pool : a pool for extended passthru commands + * @param epthru_pool_handle : handle to the pool above + * @param sg_pool : pool of scatter-gather lists for this driver + * @param sg_pool_handle : handle to the pool above + * @param ccb_list : list of our command control blocks + * @param uccb_list : list of cmd control blocks for mgmt module + * @param umbox64 : array of mailbox for user commands (cmm) + * @param pdrv_state : array for state of each physical drive. + * @param last_disp : flag used to show device scanning + * @param hw_error : set if FW not responding + * @param fast_load : If set, skip physical device scanning + * @channel_class : channel class, RAID or SCSI + * + * Initialization structure for mailbox controllers: memory based and IO based + * All the fields in this structure are LLD specific and may be discovered at + * init() or start() time. + * + * NOTE: The fields of this structures are placed to minimize cache misses + */ +typedef struct { + mbox64_t *una_mbox64; + dma_addr_t una_mbox64_dma; + mbox_t *mbox; + mbox64_t *mbox64; + dma_addr_t mbox_dma; + spinlock_t mailbox_lock; + unsigned long baseport; + unsigned long baseaddr; + struct mraid_pci_blk mbox_pool[MBOX_MAX_SCSI_CMDS]; + struct dma_pool *mbox_pool_handle; + struct mraid_pci_blk epthru_pool[MBOX_MAX_SCSI_CMDS]; + struct dma_pool *epthru_pool_handle; + struct mraid_pci_blk sg_pool[MBOX_MAX_SCSI_CMDS]; + struct dma_pool *sg_pool_handle; + mbox_ccb_t ccb_list[MBOX_MAX_SCSI_CMDS]; + mbox_ccb_t uccb_list[MBOX_MAX_USER_CMDS]; + mbox64_t umbox64[MBOX_MAX_USER_CMDS]; + + uint8_t pdrv_state[MBOX_MAX_PHYSICAL_DRIVES]; + uint32_t last_disp; + int hw_error; + int fast_load; + uint8_t channel_class; +} mraid_device_t; + +// route to raid device from adapter +#define ADAP2RAIDDEV(adp) ((mraid_device_t *)((adp)->raid_device)) + +#define MAILBOX_LOCK(rdev) (&(rdev)->mailbox_lock) + +// Find out if this channel is a RAID or SCSI +#define IS_RAID_CH(rdev, ch) (((rdev)->channel_class >> (ch)) & 0x01) + + +#define RDINDOOR(rdev) readl((rdev)->baseaddr + 0x20) +#define RDOUTDOOR(rdev) readl((rdev)->baseaddr + 0x2C) +#define WRINDOOR(rdev, value) writel(value, (rdev)->baseaddr + 0x20) +#define WROUTDOOR(rdev, value) writel(value, (rdev)->baseaddr + 0x2C) + +#endif // _MEGARAID_H_ + +// vim: set ts=8 sw=8 tw=78: diff --git a/drivers/scsi/megaraid/megaraid_mm.c b/drivers/scsi/megaraid/megaraid_mm.c new file mode 100644 index 000000000..8a6bc9391 --- /dev/null +++ b/drivers/scsi/megaraid/megaraid_mm.c @@ -0,0 +1,1160 @@ +/* + * + * Linux MegaRAID device driver + * + * Copyright (c) 2003-2004 LSI Logic Corporation. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + * + * FILE : megaraid_mm.c + * Version : v2.20.2.0 (August 19 2004) + * + * Common management module + */ + +#include "megaraid_mm.h" + + +// Entry points for char node driver +static int mraid_mm_open(struct inode *, struct file *); +static int mraid_mm_ioctl(struct inode *, struct file *, uint, unsigned long); + + +// routines to convert to and from the old the format +static int mimd_to_kioc(mimd_t __user *, mraid_mmadp_t *, uioc_t *); +static int kioc_to_mimd(uioc_t *, mimd_t __user *); + + +// Helper functions +static int handle_drvrcmd(void __user *, uint8_t, int *); +static int lld_ioctl(mraid_mmadp_t *, uioc_t *); +static void ioctl_done(uioc_t *); +static void lld_timedout(unsigned long); +static void hinfo_to_cinfo(mraid_hba_info_t *, mcontroller_t *); +static mraid_mmadp_t *mraid_mm_get_adapter(mimd_t __user *, int *); +static uioc_t *mraid_mm_alloc_kioc(mraid_mmadp_t *); +static void mraid_mm_dealloc_kioc(mraid_mmadp_t *, uioc_t *); +static int mraid_mm_attach_buf(mraid_mmadp_t *, uioc_t *, int); +static int mraid_mm_setup_dma_pools(mraid_mmadp_t *); +static void mraid_mm_free_adp_resources(mraid_mmadp_t *); +static void mraid_mm_teardown_dma_pools(mraid_mmadp_t *); + +#ifdef CONFIG_COMPAT +static int mraid_mm_compat_ioctl(unsigned int, unsigned int, unsigned long, + struct file *); +#endif + +MODULE_AUTHOR("LSI Logic Corporation"); +MODULE_DESCRIPTION("LSI Logic Management Module"); +MODULE_LICENSE("GPL"); +MODULE_VERSION(LSI_COMMON_MOD_VERSION); + +static int dbglevel = CL_ANN; +module_param_named(dlevel, dbglevel, int, 0); +MODULE_PARM_DESC(dlevel, "Debug level (default=0)"); + +EXPORT_SYMBOL(mraid_mm_register_adp); +EXPORT_SYMBOL(mraid_mm_unregister_adp); + +static int majorno; +static uint32_t drvr_ver = 0x02200100; + +static int adapters_count_g; +static struct list_head adapters_list_g; + +wait_queue_head_t wait_q; + +static struct file_operations lsi_fops = { + .open = mraid_mm_open, + .ioctl = mraid_mm_ioctl, + .owner = THIS_MODULE, +}; + +/** + * mraid_mm_open - open routine for char node interface + * @inod : unused + * @filep : unused + * + * allow ioctl operations by apps only if they superuser privilege + */ +static int +mraid_mm_open(struct inode *inode, struct file *filep) +{ + /* + * Only allow superuser to access private ioctl interface + */ + if (!capable(CAP_SYS_ADMIN)) return (-EACCES); + + return 0; +} + +/** + * mraid_mm_ioctl - module entry-point for ioctls + * @inode : inode (ignored) + * @filep : file operations pointer (ignored) + * @cmd : ioctl command + * @arg : user ioctl packet + */ +static int +mraid_mm_ioctl(struct inode *inode, struct file *filep, unsigned int cmd, + unsigned long arg) +{ + uioc_t *kioc; + char signature[EXT_IOCTL_SIGN_SZ] = {0}; + int rval; + mraid_mmadp_t *adp; + uint8_t old_ioctl; + int drvrcmd_rval; + void __user *argp = (void __user *)arg; + + /* + * Make sure only USCSICMD are issued through this interface. + * MIMD application would still fire different command. + */ + + if ((_IOC_TYPE(cmd) != MEGAIOC_MAGIC) && (cmd != USCSICMD)) { + return (-EINVAL); + } + + /* + * Look for signature to see if this is the new or old ioctl format. + */ + if (copy_from_user(signature, argp, EXT_IOCTL_SIGN_SZ)) { + con_log(CL_ANN, (KERN_WARNING + "megaraid cmm: copy from usr addr failed\n")); + return (-EFAULT); + } + + if (memcmp(signature, EXT_IOCTL_SIGN, EXT_IOCTL_SIGN_SZ) == 0) + old_ioctl = 0; + else + old_ioctl = 1; + + /* + * At present, we don't support the new ioctl packet + */ + if (!old_ioctl ) + return (-EINVAL); + + /* + * If it is a driver ioctl (as opposed to fw ioctls), then we can + * handle the command locally. rval > 0 means it is not a drvr cmd + */ + rval = handle_drvrcmd(argp, old_ioctl, &drvrcmd_rval); + + if (rval < 0) + return rval; + else if (rval == 0) + return drvrcmd_rval; + + rval = 0; + if ((adp = mraid_mm_get_adapter(argp, &rval)) == NULL) { + return rval; + } + + /* + * The following call will block till a kioc is available + */ + kioc = mraid_mm_alloc_kioc(adp); + + /* + * User sent the old mimd_t ioctl packet. Convert it to uioc_t. + */ + if ((rval = mimd_to_kioc(argp, adp, kioc))) { + mraid_mm_dealloc_kioc(adp, kioc); + return rval; + } + + kioc->done = ioctl_done; + + /* + * Issue the IOCTL to the low level driver + */ + if ((rval = lld_ioctl(adp, kioc))) { + mraid_mm_dealloc_kioc(adp, kioc); + return rval; + } + + /* + * Convert the kioc back to user space + */ + rval = kioc_to_mimd(kioc, argp); + + /* + * Return the kioc to free pool + */ + mraid_mm_dealloc_kioc(adp, kioc); + + return rval; +} + + +/** + * mraid_mm_get_adapter - Returns corresponding adapters for the mimd packet + * @umimd : User space mimd_t ioctl packet + * @adapter : pointer to the adapter (OUT) + */ +static mraid_mmadp_t * +mraid_mm_get_adapter(mimd_t __user *umimd, int *rval) +{ + mraid_mmadp_t *adapter; + mimd_t mimd; + uint32_t adapno; + int iterator; + + + if (copy_from_user(&mimd, umimd, sizeof(mimd_t))) { + *rval = -EFAULT; + return NULL; + } + + adapno = GETADAP(mimd.ui.fcs.adapno); + + if (adapno >= adapters_count_g) { + *rval = -ENODEV; + return NULL; + } + + adapter = NULL; + iterator = 0; + + list_for_each_entry(adapter, &adapters_list_g, list) { + if (iterator++ == adapno) break; + } + + if (!adapter) { + *rval = -ENODEV; + return NULL; + } + + return adapter; +} + +/* + * handle_drvrcmd - This routine checks if the opcode is a driver + * cmd and if it is, handles it. + * @arg : packet sent by the user app + * @old_ioctl : mimd if 1; uioc otherwise + */ +static int +handle_drvrcmd(void __user *arg, uint8_t old_ioctl, int *rval) +{ + mimd_t __user *umimd; + mimd_t kmimd; + uint8_t opcode; + uint8_t subopcode; + + if (old_ioctl) + goto old_packet; + else + goto new_packet; + +new_packet: + return (-ENOTSUPP); + +old_packet: + *rval = 0; + umimd = arg; + + if (copy_from_user(&kmimd, umimd, sizeof(mimd_t))) + return (-EFAULT); + + opcode = kmimd.ui.fcs.opcode; + subopcode = kmimd.ui.fcs.subopcode; + + /* + * If the opcode is 0x82 and the subopcode is either GET_DRVRVER or + * GET_NUMADP, then we can handle. Otherwise we should return 1 to + * indicate that we cannot handle this. + */ + if (opcode != 0x82) + return 1; + + switch (subopcode) { + + case MEGAIOC_QDRVRVER: + + if (copy_to_user(kmimd.data, &drvr_ver, sizeof(uint32_t))) + return (-EFAULT); + + return 0; + + case MEGAIOC_QNADAP: + + *rval = adapters_count_g; + + if (copy_to_user(kmimd.data, &adapters_count_g, + sizeof(uint32_t))) + return (-EFAULT); + + return 0; + + default: + /* cannot handle */ + return 1; + } + + return 0; +} + + +/** + * mimd_to_kioc - Converter from old to new ioctl format + * + * @umimd : user space old MIMD IOCTL + * @kioc : kernel space new format IOCTL + * + * Routine to convert MIMD interface IOCTL to new interface IOCTL packet. The + * new packet is in kernel space so that driver can perform operations on it + * freely. + */ + +static int +mimd_to_kioc(mimd_t __user *umimd, mraid_mmadp_t *adp, uioc_t *kioc) +{ + mbox64_t *mbox64; + mbox_t *mbox; + mraid_passthru_t *pthru32; + uint32_t adapno; + uint8_t opcode; + uint8_t subopcode; + mimd_t mimd; + + if (copy_from_user(&mimd, umimd, sizeof(mimd_t))) + return (-EFAULT); + + /* + * Applications are not allowed to send extd pthru + */ + if ((mimd.mbox[0] == MBOXCMD_PASSTHRU64) || + (mimd.mbox[0] == MBOXCMD_EXTPTHRU)) + return (-EINVAL); + + opcode = mimd.ui.fcs.opcode; + subopcode = mimd.ui.fcs.subopcode; + adapno = GETADAP(mimd.ui.fcs.adapno); + + if (adapno >= adapters_count_g) + return (-ENODEV); + + kioc->adapno = adapno; + kioc->mb_type = MBOX_LEGACY; + kioc->app_type = APPTYPE_MIMD; + + switch (opcode) { + + case 0x82: + + if (subopcode == MEGAIOC_QADAPINFO) { + + kioc->opcode = GET_ADAP_INFO; + kioc->data_dir = UIOC_RD; + kioc->xferlen = sizeof(mraid_hba_info_t); + + if (mraid_mm_attach_buf(adp, kioc, kioc->xferlen)) + return (-ENOMEM); + } + else { + con_log(CL_ANN, (KERN_WARNING + "megaraid cmm: Invalid subop\n")); + return (-EINVAL); + } + + break; + + case 0x81: + + kioc->opcode = MBOX_CMD; + kioc->xferlen = mimd.ui.fcs.length; + kioc->user_data_len = kioc->xferlen; + kioc->user_data = mimd.ui.fcs.buffer; + + if (mraid_mm_attach_buf(adp, kioc, kioc->xferlen)) + return (-ENOMEM); + + if (mimd.outlen) kioc->data_dir = UIOC_RD; + if (mimd.inlen) kioc->data_dir |= UIOC_WR; + + break; + + case 0x80: + + kioc->opcode = MBOX_CMD; + kioc->xferlen = (mimd.outlen > mimd.inlen) ? + mimd.outlen : mimd.inlen; + kioc->user_data_len = kioc->xferlen; + kioc->user_data = mimd.data; + + if (mraid_mm_attach_buf(adp, kioc, kioc->xferlen)) + return (-ENOMEM); + + if (mimd.outlen) kioc->data_dir = UIOC_RD; + if (mimd.inlen) kioc->data_dir |= UIOC_WR; + + break; + + default: + return (-EINVAL); + } + + /* + * If driver command, nothing else to do + */ + if (opcode == 0x82) + return 0; + + /* + * This is a mailbox cmd; copy the mailbox from mimd + */ + mbox64 = (mbox64_t *)((unsigned long)kioc->cmdbuf); + mbox = &mbox64->mbox32; + memcpy(mbox, mimd.mbox, 14); + + if (mbox->cmd != MBOXCMD_PASSTHRU) { // regular DCMD + + mbox->xferaddr = (uint32_t)kioc->buf_paddr; + + if (kioc->data_dir & UIOC_WR) { + if (copy_from_user(kioc->buf_vaddr, kioc->user_data, + kioc->xferlen)) { + return (-EFAULT); + } + } + + return 0; + } + + /* + * This is a regular 32-bit pthru cmd; mbox points to pthru struct. + * Just like in above case, the beginning for memblk is treated as + * a mailbox. The passthru will begin at next 1K boundary. And the + * data will start 1K after that. + */ + pthru32 = kioc->pthru32; + kioc->user_pthru = &umimd->pthru; + mbox->xferaddr = (uint32_t)kioc->pthru32_h; + + if (copy_from_user(pthru32, kioc->user_pthru, + sizeof(mraid_passthru_t))) { + return (-EFAULT); + } + + pthru32->dataxferaddr = kioc->buf_paddr; + if (kioc->data_dir & UIOC_WR) { + if (copy_from_user(kioc->buf_vaddr, kioc->user_data, + pthru32->dataxferlen)) { + return (-EFAULT); + } + } + + return 0; +} + +/** + * mraid_mm_attch_buf - Attach a free dma buffer for required size + * + * @adp : Adapter softstate + * @kioc : kioc that the buffer needs to be attached to + * @xferlen : required length for buffer + * + * First we search for a pool with smallest buffer that is >= @xferlen. If + * that pool has no free buffer, we will try for the next bigger size. If none + * is available, we will try to allocate the smallest buffer that is >= + * @xferlen and attach it the pool. + */ +static int +mraid_mm_attach_buf(mraid_mmadp_t *adp, uioc_t *kioc, int xferlen) +{ + mm_dmapool_t *pool; + int right_pool = -1; + unsigned long flags; + int i; + + kioc->pool_index = -1; + kioc->buf_vaddr = NULL; + kioc->buf_paddr = 0; + kioc->free_buf = 0; + + /* + * We need xferlen amount of memory. See if we can get it from our + * dma pools. If we don't get exact size, we will try bigger buffer + */ + + for (i = 0; i < MAX_DMA_POOLS; i++) { + + pool = &adp->dma_pool_list[i]; + + if (xferlen > pool->buf_size) + continue; + + if (right_pool == -1) + right_pool = i; + + spin_lock_irqsave(&pool->lock, flags); + + if (!pool->in_use) { + + pool->in_use = 1; + kioc->pool_index = i; + kioc->buf_vaddr = pool->vaddr; + kioc->buf_paddr = pool->paddr; + + spin_unlock_irqrestore(&pool->lock, flags); + return 0; + } + else { + spin_unlock_irqrestore(&pool->lock, flags); + continue; + } + } + + /* + * If xferlen doesn't match any of our pools, return error + */ + if (right_pool == -1) + return -EINVAL; + + /* + * We did not get any buffer from the preallocated pool. Let us try + * to allocate one new buffer. NOTE: This is a blocking call. + */ + pool = &adp->dma_pool_list[right_pool]; + + spin_lock_irqsave(&pool->lock, flags); + + kioc->pool_index = right_pool; + kioc->free_buf = 1; + kioc->buf_vaddr = pci_pool_alloc(pool->handle, GFP_KERNEL, + &kioc->buf_paddr); + spin_unlock_irqrestore(&pool->lock, flags); + + if (!kioc->buf_vaddr) + return -ENOMEM; + + return 0; +} + +/** + * mraid_mm_alloc_kioc - Returns a uioc_t from free list + * @adp : Adapter softstate for this module + * + * The kioc_semaphore is initialized with number of kioc nodes in the + * free kioc pool. If the kioc pool is empty, this function blocks till + * a kioc becomes free. + */ +static uioc_t * +mraid_mm_alloc_kioc(mraid_mmadp_t *adp) +{ + uioc_t *kioc; + struct list_head* head; + unsigned long flags; + + down(&adp->kioc_semaphore); + + spin_lock_irqsave(&adp->kioc_pool_lock, flags); + + head = &adp->kioc_pool; + + if (list_empty(head)) { + up(&adp->kioc_semaphore); + spin_unlock_irqrestore(&adp->kioc_pool_lock, flags); + + con_log(CL_ANN, ("megaraid cmm: kioc list empty!\n")); + return NULL; + } + + kioc = list_entry(head->next, uioc_t, list); + list_del_init(&kioc->list); + + spin_unlock_irqrestore(&adp->kioc_pool_lock, flags); + + memset((caddr_t)(unsigned long)kioc->cmdbuf, 0, sizeof(mbox64_t)); + memset((caddr_t) kioc->pthru32, 0, sizeof(mraid_passthru_t)); + + kioc->buf_vaddr = NULL; + kioc->buf_paddr = 0; + kioc->pool_index =-1; + kioc->free_buf = 0; + kioc->user_data = NULL; + kioc->user_data_len = 0; + kioc->user_pthru = NULL; + + return kioc; +} + +/** + * mraid_mm_dealloc_kioc - Return kioc to free pool + * + * @adp : Adapter softstate + * @kioc : uioc_t node to be returned to free pool + */ +static void +mraid_mm_dealloc_kioc(mraid_mmadp_t *adp, uioc_t *kioc) +{ + mm_dmapool_t *pool; + unsigned long flags; + + pool = &adp->dma_pool_list[kioc->pool_index]; + + /* This routine may be called in non-isr context also */ + spin_lock_irqsave(&pool->lock, flags); + + /* + * While attaching the dma buffer, if we didn't get the required + * buffer from the pool, we would have allocated it at the run time + * and set the free_buf flag. We must free that buffer. Otherwise, + * just mark that the buffer is not in use + */ + if (kioc->free_buf == 1) + pci_pool_free(pool->handle, kioc->buf_vaddr, kioc->buf_paddr); + else + pool->in_use = 0; + + spin_unlock_irqrestore(&pool->lock, flags); + + /* Return the kioc to the free pool */ + spin_lock_irqsave(&adp->kioc_pool_lock, flags); + list_add(&kioc->list, &adp->kioc_pool); + spin_unlock_irqrestore(&adp->kioc_pool_lock, flags); + + /* increment the free kioc count */ + up(&adp->kioc_semaphore); + + return; +} + +/** + * lld_ioctl - Routine to issue ioctl to low level drvr + * + * @adp : The adapter handle + * @kioc : The ioctl packet with kernel addresses + */ +static int +lld_ioctl(mraid_mmadp_t *adp, uioc_t *kioc) +{ + int rval; + struct timer_list timer; + struct timer_list *tp = NULL; + + kioc->status = -ENODATA; + rval = adp->issue_uioc(adp->drvr_data, kioc, IOCTL_ISSUE); + + if (rval) return rval; + + /* + * Start the timer + */ + if (adp->timeout > 0) { + tp = &timer; + init_timer(tp); + + tp->function = lld_timedout; + tp->data = (unsigned long)kioc; + tp->expires = jiffies + adp->timeout * HZ; + + add_timer(tp); + } + + /* + * Wait till the low level driver completes the ioctl. After this + * call, the ioctl either completed successfully or timedout. + */ + wait_event(wait_q, (kioc->status != -ENODATA)); + if (tp) { + del_timer_sync(tp); + } + + return kioc->status; +} + + +/** + * ioctl_done - callback from the low level driver + * + * @kioc : completed ioctl packet + */ +static void +ioctl_done(uioc_t *kioc) +{ + /* + * When the kioc returns from driver, make sure it still doesn't + * have ENODATA in status. Otherwise, driver will hang on wait_event + * forever + */ + if (kioc->status == -ENODATA) { + con_log(CL_ANN, (KERN_WARNING + "megaraid cmm: lld didn't change status!\n")); + + kioc->status = -EINVAL; + } + + wake_up(&wait_q); +} + + +/* + * lld_timedout : callback from the expired timer + * + * @ptr : ioctl packet that timed out + */ +static void +lld_timedout(unsigned long ptr) +{ + uioc_t *kioc = (uioc_t *)ptr; + + kioc->status = -ETIME; + + con_log(CL_ANN, (KERN_WARNING "megaraid cmm: ioctl timed out\n")); + + wake_up(&wait_q); +} + + +/** + * kioc_to_mimd : Converter from new back to old format + * + * @kioc : Kernel space IOCTL packet (successfully issued) + * @mimd : User space MIMD packet + */ +static int +kioc_to_mimd(uioc_t *kioc, mimd_t __user *mimd) +{ + mimd_t kmimd; + uint8_t opcode; + uint8_t subopcode; + + mbox64_t *mbox64; + mraid_passthru_t __user *upthru32; + mraid_passthru_t *kpthru32; + mcontroller_t cinfo; + mraid_hba_info_t *hinfo; + + + if (copy_from_user(&kmimd, mimd, sizeof(mimd_t))) + return (-EFAULT); + + opcode = kmimd.ui.fcs.opcode; + subopcode = kmimd.ui.fcs.subopcode; + + if (opcode == 0x82) { + switch (subopcode) { + + case MEGAIOC_QADAPINFO: + + hinfo = (mraid_hba_info_t *)(unsigned long) + kioc->buf_vaddr; + + hinfo_to_cinfo(hinfo, &cinfo); + + if (copy_to_user(kmimd.data, &cinfo, sizeof(cinfo))) + return (-EFAULT); + + return 0; + + default: + return (-EINVAL); + } + + return 0; + } + + mbox64 = (mbox64_t *)(unsigned long)kioc->cmdbuf; + + if (kioc->user_pthru) { + + upthru32 = kioc->user_pthru; + kpthru32 = kioc->pthru32; + + if (copy_to_user(&upthru32->scsistatus, + &kpthru32->scsistatus, + sizeof(uint8_t))) { + return (-EFAULT); + } + } + + if (kioc->user_data) { + if (copy_to_user(kioc->user_data, kioc->buf_vaddr, + kioc->user_data_len)) { + return (-EFAULT); + } + } + + if (copy_to_user(&mimd->mbox[17], + &mbox64->mbox32.status, sizeof(uint8_t))) { + return (-EFAULT); + } + + return 0; +} + + +/** + * hinfo_to_cinfo - Convert new format hba info into old format + * + * @hinfo : New format, more comprehensive adapter info + * @cinfo : Old format adapter info to support mimd_t apps + */ +static void +hinfo_to_cinfo(mraid_hba_info_t *hinfo, mcontroller_t *cinfo) +{ + if (!hinfo || !cinfo) + return; + + cinfo->base = hinfo->baseport; + cinfo->irq = hinfo->irq; + cinfo->numldrv = hinfo->num_ldrv; + cinfo->pcibus = hinfo->pci_bus; + cinfo->pcidev = hinfo->pci_slot; + cinfo->pcifun = PCI_FUNC(hinfo->pci_dev_fn); + cinfo->pciid = hinfo->pci_device_id; + cinfo->pcivendor = hinfo->pci_vendor_id; + cinfo->pcislot = hinfo->pci_slot; + cinfo->uid = hinfo->unique_id; +} + + +/* + * mraid_mm_register_adp - Registration routine for low level drvrs + * + * @adp : Adapter objejct + */ +int +mraid_mm_register_adp(mraid_mmadp_t *lld_adp) +{ + mraid_mmadp_t *adapter; + mbox64_t *mbox_list; + uioc_t *kioc; + uint32_t rval; + int i; + + + if (lld_adp->drvr_type != DRVRTYPE_MBOX) + return (-EINVAL); + + adapter = kmalloc(sizeof(mraid_mmadp_t), GFP_KERNEL); + + if (!adapter) { + rval = -ENOMEM; + goto memalloc_error; + } + + memset(adapter, 0, sizeof(mraid_mmadp_t)); + + adapter->unique_id = lld_adp->unique_id; + adapter->drvr_type = lld_adp->drvr_type; + adapter->drvr_data = lld_adp->drvr_data; + adapter->pdev = lld_adp->pdev; + adapter->issue_uioc = lld_adp->issue_uioc; + adapter->timeout = lld_adp->timeout; + adapter->max_kioc = lld_adp->max_kioc; + + /* + * Allocate single blocks of memory for all required kiocs, + * mailboxes and passthru structures. + */ + adapter->kioc_list = kmalloc(sizeof(uioc_t) * lld_adp->max_kioc, + GFP_KERNEL); + adapter->mbox_list = kmalloc(sizeof(mbox64_t) * lld_adp->max_kioc, + GFP_KERNEL); + adapter->pthru_dma_pool = pci_pool_create("megaraid mm pthru pool", + adapter->pdev, + sizeof(mraid_passthru_t), + 16, 0); + + if (!adapter->kioc_list || !adapter->mbox_list || + !adapter->pthru_dma_pool) { + + con_log(CL_ANN, (KERN_WARNING + "megaraid cmm: out of memory, %s %d\n", __FUNCTION__, + __LINE__)); + + rval = (-ENOMEM); + + goto memalloc_error; + } + + /* + * Slice kioc_list and make a kioc_pool with the individiual kiocs + */ + INIT_LIST_HEAD(&adapter->kioc_pool); + spin_lock_init(&adapter->kioc_pool_lock); + sema_init(&adapter->kioc_semaphore, lld_adp->max_kioc); + + mbox_list = (mbox64_t *)adapter->mbox_list; + + for (i = 0; i < lld_adp->max_kioc; i++) { + + kioc = adapter->kioc_list + i; + kioc->cmdbuf = (uint64_t)(unsigned long)(mbox_list + i); + kioc->pthru32 = pci_pool_alloc(adapter->pthru_dma_pool, + GFP_KERNEL, &kioc->pthru32_h); + + if (!kioc->pthru32) { + + con_log(CL_ANN, (KERN_WARNING + "megaraid cmm: out of memory, %s %d\n", + __FUNCTION__, __LINE__)); + + rval = (-ENOMEM); + + goto pthru_dma_pool_error; + } + + list_add_tail(&kioc->list, &adapter->kioc_pool); + } + + // Setup the dma pools for data buffers + if ((rval = mraid_mm_setup_dma_pools(adapter)) != 0) { + goto dma_pool_error; + } + + list_add_tail(&adapter->list, &adapters_list_g); + + adapters_count_g++; + + return 0; + +dma_pool_error: + /* Do nothing */ + +pthru_dma_pool_error: + + for (i = 0; i < lld_adp->max_kioc; i++) { + kioc = adapter->kioc_list + i; + if (kioc->pthru32) { + pci_pool_free(adapter->pthru_dma_pool, kioc->pthru32, + kioc->pthru32_h); + } + } + +memalloc_error: + + if (adapter->kioc_list) + kfree(adapter->kioc_list); + + if (adapter->mbox_list) + kfree(adapter->mbox_list); + + if (adapter->pthru_dma_pool) + pci_pool_destroy(adapter->pthru_dma_pool); + + if (adapter) + kfree(adapter); + + return rval; +} + +/** + * mraid_mm_setup_dma_pools - Set up dma buffer pools per adapter + * + * @adp : Adapter softstate + * + * We maintain a pool of dma buffers per each adapter. Each pool has one + * buffer. E.g, we may have 5 dma pools - one each for 4k, 8k ... 64k buffers. + * We have just one 4k buffer in 4k pool, one 8k buffer in 8k pool etc. We + * dont' want to waste too much memory by allocating more buffers per each + * pool. + */ +static int +mraid_mm_setup_dma_pools(mraid_mmadp_t *adp) +{ + mm_dmapool_t *pool; + int bufsize; + int i; + + /* + * Create MAX_DMA_POOLS number of pools + */ + bufsize = MRAID_MM_INIT_BUFF_SIZE; + + for (i = 0; i < MAX_DMA_POOLS; i++){ + + pool = &adp->dma_pool_list[i]; + + pool->buf_size = bufsize; + spin_lock_init(&pool->lock); + + pool->handle = pci_pool_create("megaraid mm data buffer", + adp->pdev, bufsize, 16, 0); + + if (!pool->handle) { + goto dma_pool_setup_error; + } + + pool->vaddr = pci_pool_alloc(pool->handle, GFP_KERNEL, + &pool->paddr); + + if (!pool->vaddr) + goto dma_pool_setup_error; + + bufsize = bufsize * 2; + } + + return 0; + +dma_pool_setup_error: + + mraid_mm_teardown_dma_pools(adp); + return (-ENOMEM); +} + + +/* + * mraid_mm_unregister_adp - Unregister routine for low level drivers + * Assume no outstanding ioctls to llds. + * + * @unique_id : UID of the adpater + */ +int +mraid_mm_unregister_adp(uint32_t unique_id) +{ + mraid_mmadp_t *adapter; + mraid_mmadp_t *tmp; + + list_for_each_entry_safe(adapter, tmp, &adapters_list_g, list) { + + + if (adapter->unique_id == unique_id) { + + adapters_count_g--; + + list_del_init(&adapter->list); + + mraid_mm_free_adp_resources(adapter); + + kfree(adapter); + + con_log(CL_ANN, ( + "megaraid cmm: Unregistered one adapter:%#x\n", + unique_id)); + + return 0; + } + } + + return (-ENODEV); +} + +/** + * mraid_mm_free_adp_resources - Free adapter softstate + * + * @adp : Adapter softstate + */ +static void +mraid_mm_free_adp_resources(mraid_mmadp_t *adp) +{ + uioc_t *kioc; + int i; + + mraid_mm_teardown_dma_pools(adp); + + for (i = 0; i < adp->max_kioc; i++) { + + kioc = adp->kioc_list + i; + + pci_pool_free(adp->pthru_dma_pool, kioc->pthru32, + kioc->pthru32_h); + } + + kfree(adp->kioc_list); + + kfree(adp->mbox_list); + + pci_pool_destroy(adp->pthru_dma_pool); + + + return; +} + + +/** + * mraid_mm_teardown_dma_pools - Free all per adapter dma buffers + * + * @adp : Adapter softstate + */ +static void +mraid_mm_teardown_dma_pools(mraid_mmadp_t *adp) +{ + int i; + mm_dmapool_t *pool; + + for (i = 0; i < MAX_DMA_POOLS; i++) { + + pool = &adp->dma_pool_list[i]; + + if (pool->handle) { + + if (pool->vaddr) + pci_pool_free(pool->handle, pool->vaddr, + pool->paddr); + + pci_pool_destroy(pool->handle); + pool->handle = NULL; + } + } + + return; +} + +/** + * mraid_mm_init : Module entry point + */ +static int __init +mraid_mm_init(void) +{ + // Announce the driver version + con_log(CL_ANN, (KERN_INFO "megaraid cmm: %s %s\n", + LSI_COMMON_MOD_VERSION, LSI_COMMON_MOD_EXT_VERSION)); + + majorno = register_chrdev(0, "megadev", &lsi_fops); + + if (majorno < 0) { + con_log(CL_ANN, ("megaraid cmm: cannot get major\n")); + return majorno; + } + + init_waitqueue_head(&wait_q); + + INIT_LIST_HEAD(&adapters_list_g); + +#ifdef CONFIG_COMPAT + register_ioctl32_conversion(MEGAIOCCMD, mraid_mm_compat_ioctl); +#endif + + return 0; +} + + +/** + * mraid_mm_compat_ioctl : 32bit to 64bit ioctl conversion routine + */ +#ifdef CONFIG_COMPAT +static int +mraid_mm_compat_ioctl(unsigned int fd, unsigned int cmd, + unsigned long arg, struct file *filep) +{ + struct inode *inode = filep->f_dentry->d_inode; + + return mraid_mm_ioctl(inode, filep, cmd, arg); +} +#endif + +/** + * mraid_mm_exit : Module exit point + */ +static void __exit +mraid_mm_exit(void) +{ + con_log(CL_DLEVEL1 , ("exiting common mod\n")); + + unregister_chrdev(majorno, "megadev"); + unregister_ioctl32_conversion(MEGAIOCCMD); +} + +module_init(mraid_mm_init); +module_exit(mraid_mm_exit); + +/* vi: set ts=8 sw=8 tw=78: */ diff --git a/drivers/scsi/megaraid/megaraid_mm.h b/drivers/scsi/megaraid/megaraid_mm.h new file mode 100644 index 000000000..effc23a63 --- /dev/null +++ b/drivers/scsi/megaraid/megaraid_mm.h @@ -0,0 +1,102 @@ +/* + * + * Linux MegaRAID device driver + * + * Copyright (c) 2003-2004 LSI Logic Corporation. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + * + * FILE : megaraid_mm.h + */ + +#ifndef MEGARAID_MM_H +#define MEGARAID_MM_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "mbox_defs.h" +#include "megaraid_ioctl.h" + + +#define LSI_COMMON_MOD_VERSION "2.20.2.0" +#define LSI_COMMON_MOD_EXT_VERSION \ + "(Release Date: Thu Aug 19 09:58:33 EDT 2004)" + + +#define LSI_DBGLVL dbglevel + +// The smallest dma pool +#define MRAID_MM_INIT_BUFF_SIZE 4096 + +/** + * mimd_t : Old style ioctl packet structure (deprecated) + * + * @inlen : + * @outlen : + * @fca : + * @opcode : + * @subopcode : + * @adapno : + * @buffer : + * @pad : + * @length : + * @mbox : + * @pthru : + * @data : + * @pad : + * + * Note : This structure is DEPRECATED. New applications must use + * : uioc_t structure instead. All new hba drivers use the new + * : format. If we get this mimd packet, we will convert it into + * : new uioc_t format and send it to the hba drivers. + */ + +typedef struct mimd { + + uint32_t inlen; + uint32_t outlen; + + union { + uint8_t fca[16]; + struct { + uint8_t opcode; + uint8_t subopcode; + uint16_t adapno; +#if BITS_PER_LONG == 32 + uint8_t __user *buffer; + uint8_t pad[4]; +#endif +#if BITS_PER_LONG == 64 + uint8_t __user *buffer; +#endif + uint32_t length; + } __attribute__ ((packed)) fcs; + } __attribute__ ((packed)) ui; + + uint8_t mbox[18]; /* 16 bytes + 2 status bytes */ + mraid_passthru_t pthru; + +#if BITS_PER_LONG == 32 + char __user *data; /* buffer <= 4096 for 0x80 commands */ + char pad[4]; +#endif +#if BITS_PER_LONG == 64 + char __user *data; +#endif + +} __attribute__ ((packed))mimd_t; + +#endif // MEGARAID_MM_H + +// vi: set ts=8 sw=8 tw=78: diff --git a/drivers/scsi/sata_nv.c b/drivers/scsi/sata_nv.c index 29248d608..10bebc97c 100644 --- a/drivers/scsi/sata_nv.c +++ b/drivers/scsi/sata_nv.c @@ -20,6 +20,10 @@ * If you do not delete the provisions above, a recipient may use your * version of this file under either the OSL or the GPL. * + * 0.03 + * - Fixed a bug where the hotplug handlers for non-CK804/MCP04 were using + * mmio_base, which is only set for the CK804/MCP04 case. + * * 0.02 * - Added support for CK804 SATA controller. * @@ -40,13 +44,12 @@ #include #define DRV_NAME "sata_nv" -#define DRV_VERSION "0.02" +#define DRV_VERSION "0.03" #define NV_PORTS 2 #define NV_PIO_MASK 0x1f +#define NV_MWDMA_MASK 0x07 #define NV_UDMA_MASK 0x7f -#define NV_PORT0_BMDMA_REG_OFFSET 0x00 -#define NV_PORT1_BMDMA_REG_OFFSET 0x08 #define NV_PORT0_SCR_REG_OFFSET 0x00 #define NV_PORT1_SCR_REG_OFFSET 0x40 @@ -177,11 +180,12 @@ static struct pci_driver nv_pci_driver = { static Scsi_Host_Template nv_sht = { .module = THIS_MODULE, .name = DRV_NAME, + .ioctl = ata_scsi_ioctl, .queuecommand = ata_scsi_queuecmd, .eh_strategy_handler = ata_scsi_error, .can_queue = ATA_DEF_QUEUE, .this_id = ATA_SHT_THIS_ID, - .sg_tablesize = ATA_MAX_PRD, + .sg_tablesize = LIBATA_MAX_PRD, .max_sectors = ATA_MAX_SECTORS, .cmd_per_lun = ATA_SHT_CMD_PER_LUN, .emulated = ATA_SHT_EMULATED, @@ -194,13 +198,14 @@ static Scsi_Host_Template nv_sht = { static struct ata_port_operations nv_ops = { .port_disable = ata_port_disable, - .tf_load = ata_tf_load_pio, - .tf_read = ata_tf_read_pio, - .exec_command = ata_exec_command_pio, - .check_status = ata_check_status_pio, + .tf_load = ata_tf_load, + .tf_read = ata_tf_read, + .exec_command = ata_exec_command, + .check_status = ata_check_status, + .dev_select = ata_std_dev_select, .phy_reset = sata_phy_reset, - .bmdma_setup = ata_bmdma_setup_pio, - .bmdma_start = ata_bmdma_start_pio, + .bmdma_setup = ata_bmdma_setup, + .bmdma_start = ata_bmdma_start, .qc_prep = ata_qc_prep, .qc_issue = ata_qc_issue_prot, .eng_timeout = ata_eng_timeout, @@ -213,10 +218,23 @@ static struct ata_port_operations nv_ops = { .host_stop = nv_host_stop, }; +static struct ata_port_info nv_port_info = { + .sht = &nv_sht, + .host_flags = ATA_FLAG_SATA | + ATA_FLAG_SATA_RESET | + ATA_FLAG_SRST | + ATA_FLAG_NO_LEGACY, + .pio_mask = NV_PIO_MASK, + .mwdma_mask = NV_MWDMA_MASK, + .udma_mask = NV_UDMA_MASK, + .port_ops = &nv_ops, +}; + MODULE_AUTHOR("NVIDIA"); MODULE_DESCRIPTION("low-level driver for NVIDIA nForce SATA controller"); MODULE_LICENSE("GPL"); MODULE_DEVICE_TABLE(pci, nv_pci_tbl); +MODULE_VERSION(DRV_VERSION); irqreturn_t nv_interrupt (int irq, void *dev_instance, struct pt_regs *regs) { @@ -293,7 +311,8 @@ static int nv_init_one (struct pci_dev *pdev, const struct pci_device_id *ent) { static int printed_version = 0; struct nv_host *host; - struct ata_probe_ent *probe_ent = NULL; + struct ata_port_info *ppi; + struct ata_probe_ent *probe_ent; int rc; if (!printed_version++) @@ -301,11 +320,11 @@ static int nv_init_one (struct pci_dev *pdev, const struct pci_device_id *ent) rc = pci_enable_device(pdev); if (rc) - return rc; + goto err_out; rc = pci_request_regions(pdev, DRV_NAME); if (rc) - goto err_out; + goto err_out_disable; rc = pci_set_dma_mask(pdev, ATA_DMA_MASK); if (rc) @@ -314,53 +333,19 @@ static int nv_init_one (struct pci_dev *pdev, const struct pci_device_id *ent) if (rc) goto err_out_regions; - probe_ent = kmalloc(sizeof(*probe_ent), GFP_KERNEL); - if (!probe_ent) { - rc = -ENOMEM; + rc = -ENOMEM; + + ppi = &nv_port_info; + probe_ent = ata_pci_init_native_mode(pdev, &ppi); + if (!probe_ent) goto err_out_regions; - } host = kmalloc(sizeof(struct nv_host), GFP_KERNEL); - if (!host) { - rc = -ENOMEM; + if (!host) goto err_out_free_ent; - } host->host_desc = &nv_device_tbl[ent->driver_data]; - memset(probe_ent, 0, sizeof(*probe_ent)); - INIT_LIST_HEAD(&probe_ent->node); - - probe_ent->pdev = pdev; - probe_ent->sht = &nv_sht; - probe_ent->host_flags = ATA_FLAG_SATA | - ATA_FLAG_SATA_RESET | - ATA_FLAG_SRST | - ATA_FLAG_NO_LEGACY; - - probe_ent->port_ops = &nv_ops; - probe_ent->n_ports = NV_PORTS; - probe_ent->irq = pdev->irq; - probe_ent->irq_flags = SA_SHIRQ; - probe_ent->pio_mask = NV_PIO_MASK; - probe_ent->udma_mask = NV_UDMA_MASK; - - probe_ent->port[0].cmd_addr = pci_resource_start(pdev, 0); - ata_std_ports(&probe_ent->port[0]); - probe_ent->port[0].altstatus_addr = - probe_ent->port[0].ctl_addr = - pci_resource_start(pdev, 1) | ATA_PCI_CTL_OFS; - probe_ent->port[0].bmdma_addr = - pci_resource_start(pdev, 4) | NV_PORT0_BMDMA_REG_OFFSET; - - probe_ent->port[1].cmd_addr = pci_resource_start(pdev, 2); - ata_std_ports(&probe_ent->port[1]); - probe_ent->port[1].altstatus_addr = - probe_ent->port[1].ctl_addr = - pci_resource_start(pdev, 3) | ATA_PCI_CTL_OFS; - probe_ent->port[1].bmdma_addr = - pci_resource_start(pdev, 4) | NV_PORT1_BMDMA_REG_OFFSET; - probe_ent->private_data = host; if (host->host_desc->host_flags & NV_HOST_FLAGS_SCR_MMIO) { @@ -368,8 +353,10 @@ static int nv_init_one (struct pci_dev *pdev, const struct pci_device_id *ent) probe_ent->mmio_base = ioremap(pci_resource_start(pdev, 5), pci_resource_len(pdev, 5)); - if (probe_ent->mmio_base == NULL) - goto err_out_free_ent; + if (probe_ent->mmio_base == NULL) { + rc = -EIO; + goto err_out_free_host; + } base = (unsigned long)probe_ent->mmio_base; @@ -387,26 +374,30 @@ static int nv_init_one (struct pci_dev *pdev, const struct pci_device_id *ent) pci_set_master(pdev); + rc = ata_device_add(probe_ent); + if (rc != NV_PORTS) + goto err_out_iounmap; + // Enable hotplug event interrupts. if (host->host_desc->enable_hotplug) host->host_desc->enable_hotplug(probe_ent); - rc = ata_device_add(probe_ent); - if (rc != NV_PORTS) - goto err_out_free_ent; - kfree(probe_ent); return 0; +err_out_iounmap: + if (host->host_desc->host_flags & NV_HOST_FLAGS_SCR_MMIO) + iounmap(probe_ent->mmio_base); +err_out_free_host: + kfree(host); err_out_free_ent: kfree(probe_ent); - err_out_regions: pci_release_regions(pdev); - -err_out: +err_out_disable: pci_disable_device(pdev); +err_out: return rc; } @@ -415,33 +406,33 @@ static void nv_enable_hotplug(struct ata_probe_ent *probe_ent) u8 intr_mask; outb(NV_INT_STATUS_HOTPLUG, - (unsigned long)probe_ent->mmio_base + NV_INT_STATUS); + probe_ent->port[0].scr_addr + NV_INT_STATUS); - intr_mask = inb((unsigned long)probe_ent->mmio_base + NV_INT_ENABLE); + intr_mask = inb(probe_ent->port[0].scr_addr + NV_INT_ENABLE); intr_mask |= NV_INT_ENABLE_HOTPLUG; - outb(intr_mask, (unsigned long)probe_ent->mmio_base + NV_INT_ENABLE); + outb(intr_mask, probe_ent->port[0].scr_addr + NV_INT_ENABLE); } static void nv_disable_hotplug(struct ata_host_set *host_set) { u8 intr_mask; - intr_mask = inb((unsigned long)host_set->mmio_base + NV_INT_ENABLE); + intr_mask = inb(host_set->ports[0]->ioaddr.scr_addr + NV_INT_ENABLE); intr_mask &= ~(NV_INT_ENABLE_HOTPLUG); - outb(intr_mask, (unsigned long)host_set->mmio_base + NV_INT_ENABLE); + outb(intr_mask, host_set->ports[0]->ioaddr.scr_addr + NV_INT_ENABLE); } static void nv_check_hotplug(struct ata_host_set *host_set) { u8 intr_status; - intr_status = inb((unsigned long)host_set->mmio_base + NV_INT_STATUS); + intr_status = inb(host_set->ports[0]->ioaddr.scr_addr + NV_INT_STATUS); // Clear interrupt status. - outb(0xff, (unsigned long)host_set->mmio_base + NV_INT_STATUS); + outb(0xff, host_set->ports[0]->ioaddr.scr_addr + NV_INT_STATUS); if (intr_status & NV_INT_STATUS_HOTPLUG) { if (intr_status & NV_INT_STATUS_PDEV_ADDED) diff --git a/drivers/scsi/sata_sx4.c b/drivers/scsi/sata_sx4.c index b54fd87e3..9c8e5ccc1 100644 --- a/drivers/scsi/sata_sx4.c +++ b/drivers/scsi/sata_sx4.c @@ -146,8 +146,6 @@ struct pdc_host_priv { static int pdc_sata_init_one (struct pci_dev *pdev, const struct pci_device_id *ent); -static void pdc20621_dma_setup(struct ata_queued_cmd *qc); -static void pdc20621_dma_start(struct ata_queued_cmd *qc); static irqreturn_t pdc20621_interrupt (int irq, void *dev_instance, struct pt_regs *regs); static void pdc_eng_timeout(struct ata_port *ap); static void pdc_20621_phy_reset (struct ata_port *ap); @@ -157,8 +155,6 @@ static void pdc20621_qc_prep(struct ata_queued_cmd *qc); static void pdc_tf_load_mmio(struct ata_port *ap, struct ata_taskfile *tf); static void pdc_exec_command_mmio(struct ata_port *ap, struct ata_taskfile *tf); static void pdc20621_host_stop(struct ata_host_set *host_set); -static inline void pdc_dma_complete (struct ata_port *ap, - struct ata_queued_cmd *qc, int have_err); static unsigned int pdc20621_dimm_init(struct ata_probe_ent *pe); static int pdc20621_detect_dimm(struct ata_probe_ent *pe); static unsigned int pdc20621_i2c_read(struct ata_probe_ent *pe, @@ -172,11 +168,13 @@ static void pdc20621_get_from_dimm(struct ata_probe_ent *pe, static void pdc20621_put_to_dimm(struct ata_probe_ent *pe, void *psource, u32 offset, u32 size); static void pdc20621_irq_clear(struct ata_port *ap); +static int pdc20621_qc_issue_prot(struct ata_queued_cmd *qc); static Scsi_Host_Template pdc_sata_sht = { .module = THIS_MODULE, .name = DRV_NAME, + .ioctl = ata_scsi_ioctl, .queuecommand = ata_scsi_queuecmd, .eh_strategy_handler = ata_scsi_error, .can_queue = ATA_DEF_QUEUE, @@ -195,14 +193,13 @@ static Scsi_Host_Template pdc_sata_sht = { static struct ata_port_operations pdc_20621_ops = { .port_disable = ata_port_disable, .tf_load = pdc_tf_load_mmio, - .tf_read = ata_tf_read_mmio, - .check_status = ata_check_status_mmio, + .tf_read = ata_tf_read, + .check_status = ata_check_status, .exec_command = pdc_exec_command_mmio, + .dev_select = ata_std_dev_select, .phy_reset = pdc_20621_phy_reset, - .bmdma_setup = pdc20621_dma_setup, - .bmdma_start = pdc20621_dma_start, .qc_prep = pdc20621_qc_prep, - .qc_issue = ata_qc_issue_prot, + .qc_issue = pdc20621_qc_issue_prot, .eng_timeout = pdc_eng_timeout, .irq_handler = pdc20621_interrupt, .irq_clear = pdc20621_irq_clear, @@ -217,7 +214,8 @@ static struct ata_port_info pdc_port_info[] = { .sht = &pdc_sata_sht, .host_flags = ATA_FLAG_SATA | ATA_FLAG_NO_LEGACY | ATA_FLAG_SRST | ATA_FLAG_MMIO, - .pio_mask = 0x03, /* pio3-4 */ + .pio_mask = 0x1f, /* pio0-4 */ + .mwdma_mask = 0x07, /* mwdma0-2 */ .udma_mask = 0x7f, /* udma0-6 ; FIXME */ .port_ops = &pdc_20621_ops, }, @@ -377,7 +375,10 @@ static inline unsigned int pdc20621_ata_pkt(struct ata_taskfile *tf, /* dimm dma S/G, and next-pkt */ dw = i >> 2; - buf32[dw] = cpu_to_le32(dimm_sg); + if (tf->protocol == ATA_PROT_NODATA) + buf32[dw] = 0; + else + buf32[dw] = cpu_to_le32(dimm_sg); buf32[dw + 1] = 0; i += 8; @@ -437,7 +438,7 @@ static inline void pdc20621_host_pkt(struct ata_taskfile *tf, u8 *buf, buf32[dw + 3]); } -static void pdc20621_qc_prep(struct ata_queued_cmd *qc) +static void pdc20621_dma_prep(struct ata_queued_cmd *qc) { struct scatterlist *sg = qc->sg; struct ata_port *ap = qc->ap; @@ -449,8 +450,7 @@ static void pdc20621_qc_prep(struct ata_queued_cmd *qc) unsigned int i, last, idx, total_len = 0, sgt_len; u32 *buf = (u32 *) &pp->dimm_buf[PDC_DIMM_HEADER_SZ]; - if (!(qc->flags & ATA_QCFLAG_DMAMAP)) - return; + assert(qc->flags & ATA_QCFLAG_DMAMAP); VPRINTK("ata%u: ENTER\n", ap->id); @@ -501,6 +501,56 @@ static void pdc20621_qc_prep(struct ata_queued_cmd *qc) VPRINTK("ata pkt buf ofs %u, prd size %u, mmio copied\n", i, sgt_len); } +static void pdc20621_nodata_prep(struct ata_queued_cmd *qc) +{ + struct ata_port *ap = qc->ap; + struct pdc_port_priv *pp = ap->private_data; + void *mmio = ap->host_set->mmio_base; + struct pdc_host_priv *hpriv = ap->host_set->private_data; + void *dimm_mmio = hpriv->dimm_mmio; + unsigned int portno = ap->port_no; + unsigned int i; + + VPRINTK("ata%u: ENTER\n", ap->id); + + /* hard-code chip #0 */ + mmio += PDC_CHIP0_OFS; + + i = pdc20621_ata_pkt(&qc->tf, qc->dev->devno, &pp->dimm_buf[0], portno); + + if (qc->tf.flags & ATA_TFLAG_LBA48) + i = pdc_prep_lba48(&qc->tf, &pp->dimm_buf[0], i); + else + i = pdc_prep_lba28(&qc->tf, &pp->dimm_buf[0], i); + + pdc_pkt_footer(&qc->tf, &pp->dimm_buf[0], i); + + /* copy three S/G tables and two packets to DIMM MMIO window */ + memcpy_toio(dimm_mmio + (portno * PDC_DIMM_WINDOW_STEP), + &pp->dimm_buf, PDC_DIMM_HEADER_SZ); + + /* force host FIFO dump */ + writel(0x00000001, mmio + PDC_20621_GENERAL_CTL); + + readl(dimm_mmio); /* MMIO PCI posting flush */ + + VPRINTK("ata pkt buf ofs %u, mmio copied\n", i); +} + +static void pdc20621_qc_prep(struct ata_queued_cmd *qc) +{ + switch (qc->tf.protocol) { + case ATA_PROT_DMA: + pdc20621_dma_prep(qc); + break; + case ATA_PROT_NODATA: + pdc20621_nodata_prep(qc); + break; + default: + break; + } +} + static void __pdc20621_push_hdma(struct ata_queued_cmd *qc, unsigned int seq, u32 pkt_ofs) @@ -576,13 +626,7 @@ static void pdc20621_dump_hdma(struct ata_queued_cmd *qc) static inline void pdc20621_dump_hdma(struct ata_queued_cmd *qc) { } #endif /* ATA_VERBOSE_DEBUG */ -static void pdc20621_dma_setup(struct ata_queued_cmd *qc) -{ - /* nothing for now. later, we will call standard - * code in libata-core for ATAPI here */ -} - -static void pdc20621_dma_start(struct ata_queued_cmd *qc) +static void pdc20621_packet_start(struct ata_queued_cmd *qc) { struct ata_port *ap = qc->ap; struct ata_host_set *host_set = ap->host_set; @@ -590,24 +634,21 @@ static void pdc20621_dma_start(struct ata_queued_cmd *qc) void *mmio = host_set->mmio_base; unsigned int rw = (qc->tf.flags & ATA_TFLAG_WRITE); u8 seq = (u8) (port_no + 1); - unsigned int doing_hdma = 0, port_ofs; + unsigned int port_ofs; /* hard-code chip #0 */ mmio += PDC_CHIP0_OFS; VPRINTK("ata%u: ENTER\n", ap->id); + wmb(); /* flush PRD, pkt writes */ + port_ofs = PDC_20621_DIMM_BASE + (PDC_DIMM_WINDOW_STEP * port_no); /* if writing, we (1) DMA to DIMM, then (2) do ATA command */ - if (rw) { - doing_hdma = 1; + if (rw && qc->tf.protocol == ATA_PROT_DMA) { seq += 4; - } - - wmb(); /* flush PRD, pkt writes */ - if (doing_hdma) { pdc20621_dump_hdma(qc); pdc20621_push_hdma(qc, seq, port_ofs + PDC_DIMM_HOST_PKT); VPRINTK("queued ofs 0x%x (%u), seq %u\n", @@ -628,6 +669,25 @@ static void pdc20621_dma_start(struct ata_queued_cmd *qc) } } +static int pdc20621_qc_issue_prot(struct ata_queued_cmd *qc) +{ + switch (qc->tf.protocol) { + case ATA_PROT_DMA: + case ATA_PROT_NODATA: + pdc20621_packet_start(qc); + return 0; + + case ATA_PROT_ATAPI_DMA: + BUG(); + break; + + default: + break; + } + + return ata_qc_issue_prot(qc); +} + static inline unsigned int pdc20621_host_intr( struct ata_port *ap, struct ata_queued_cmd *qc, unsigned int doing_hdma, @@ -648,7 +708,8 @@ static inline unsigned int pdc20621_host_intr( struct ata_port *ap, if (doing_hdma) { VPRINTK("ata%u: read hdma, 0x%x 0x%x\n", ap->id, readl(mmio + 0x104), readl(mmio + PDC_HDMA_CTLSTAT)); - pdc_dma_complete(ap, qc, 0); + /* get drive status; clear intr; complete txn */ + ata_qc_complete(qc, ata_wait_idle(ap)); pdc20621_pop_hdma(qc); } @@ -685,7 +746,8 @@ static inline unsigned int pdc20621_host_intr( struct ata_port *ap, else { VPRINTK("ata%u: write ata, 0x%x 0x%x\n", ap->id, readl(mmio + 0x104), readl(mmio + PDC_HDMA_CTLSTAT)); - pdc_dma_complete(ap, qc, 0); + /* get drive status; clear intr; complete txn */ + ata_qc_complete(qc, ata_wait_idle(ap)); pdc20621_pop_hdma(qc); } handled = 1; @@ -779,16 +841,6 @@ static irqreturn_t pdc20621_interrupt (int irq, void *dev_instance, struct pt_re return IRQ_RETVAL(handled); } -static inline void pdc_dma_complete (struct ata_port *ap, - struct ata_queued_cmd *qc, - int have_err) -{ - u8 err_bit = have_err ? ATA_ERR : 0; - - /* get drive status; clear intr; complete txn */ - ata_qc_complete(qc, ata_wait_idle(ap) | err_bit); -} - static void pdc_eng_timeout(struct ata_port *ap) { u8 drv_stat; @@ -813,17 +865,9 @@ static void pdc_eng_timeout(struct ata_port *ap) switch (qc->tf.protocol) { case ATA_PROT_DMA: - printk(KERN_ERR "ata%u: DMA timeout\n", ap->id); - ata_qc_complete(qc, ata_wait_idle(ap) | ATA_ERR); - break; - case ATA_PROT_NODATA: - drv_stat = ata_busy_wait(ap, ATA_BUSY | ATA_DRQ, 1000); - - printk(KERN_ERR "ata%u: command 0x%x timeout, stat 0x%x\n", - ap->id, qc->tf.command, drv_stat); - - ata_qc_complete(qc, drv_stat); + printk(KERN_ERR "ata%u: command timeout\n", ap->id); + ata_qc_complete(qc, ata_wait_idle(ap) | ATA_ERR); break; default: @@ -842,15 +886,17 @@ out: static void pdc_tf_load_mmio(struct ata_port *ap, struct ata_taskfile *tf) { - if (tf->protocol != ATA_PROT_DMA) - ata_tf_load_mmio(ap, tf); + WARN_ON (tf->protocol == ATA_PROT_DMA || + tf->protocol == ATA_PROT_NODATA); + ata_tf_load(ap, tf); } static void pdc_exec_command_mmio(struct ata_port *ap, struct ata_taskfile *tf) { - if (tf->protocol != ATA_PROT_DMA) - ata_exec_command_mmio(ap, tf); + WARN_ON (tf->protocol == ATA_PROT_DMA || + tf->protocol == ATA_PROT_NODATA); + ata_exec_command(ap, tf); } @@ -1144,8 +1190,7 @@ static unsigned int pdc20621_prog_dimm_global(struct ata_probe_ent *pe) error = 0; break; } - set_current_state(TASK_UNINTERRUPTIBLE); - schedule_timeout((i * 100) * HZ / 1000 + 1); + msleep(i*100); } return error; } @@ -1178,8 +1223,7 @@ static unsigned int pdc20621_dimm_init(struct ata_probe_ent *pe) readl(mmio + PDC_TIME_CONTROL); /* Wait 3 seconds */ - set_current_state(TASK_UNINTERRUPTIBLE); - schedule_timeout(3 * HZ); + msleep(3000); /* When timer is enabled, counter is decreased every internal @@ -1384,6 +1428,7 @@ static int pdc_sata_init_one (struct pci_dev *pdev, const struct pci_device_id * probe_ent->sht = pdc_port_info[board_idx].sht; probe_ent->host_flags = pdc_port_info[board_idx].host_flags; probe_ent->pio_mask = pdc_port_info[board_idx].pio_mask; + probe_ent->mwdma_mask = pdc_port_info[board_idx].mwdma_mask; probe_ent->udma_mask = pdc_port_info[board_idx].udma_mask; probe_ent->port_ops = pdc_port_info[board_idx].port_ops; @@ -1394,21 +1439,11 @@ static int pdc_sata_init_one (struct pci_dev *pdev, const struct pci_device_id * probe_ent->private_data = hpriv; base += PDC_CHIP0_OFS; + probe_ent->n_ports = 4; pdc_sata_setup_port(&probe_ent->port[0], base + 0x200); pdc_sata_setup_port(&probe_ent->port[1], base + 0x280); - - /* notice 4-port boards */ - switch (board_idx) { - case board_20621: - probe_ent->n_ports = 4; - - pdc_sata_setup_port(&probe_ent->port[2], base + 0x300); - pdc_sata_setup_port(&probe_ent->port[3], base + 0x380); - break; - default: - BUG(); - break; - } + pdc_sata_setup_port(&probe_ent->port[2], base + 0x300); + pdc_sata_setup_port(&probe_ent->port[3], base + 0x380); pci_set_master(pdev); @@ -1457,6 +1492,7 @@ MODULE_AUTHOR("Jeff Garzik"); MODULE_DESCRIPTION("Promise SATA low-level driver"); MODULE_LICENSE("GPL"); MODULE_DEVICE_TABLE(pci, pdc_sata_pci_tbl); +MODULE_VERSION(DRV_VERSION); module_init(pdc_sata_init); module_exit(pdc_sata_exit); diff --git a/drivers/scsi/sata_uli.c b/drivers/scsi/sata_uli.c new file mode 100644 index 000000000..1becb3691 --- /dev/null +++ b/drivers/scsi/sata_uli.c @@ -0,0 +1,283 @@ +/* + * sata_uli.c - ULi Electronics SATA + * + * The contents of this file are subject to the Open + * Software License version 1.1 that can be found at + * http://www.opensource.org/licenses/osl-1.1.txt and is included herein + * by reference. + * + * Alternatively, the contents of this file may be used under the terms + * of the GNU General Public License version 2 (the "GPL") as distributed + * in the kernel source COPYING file, 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 OSL, 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 OSL or the GPL. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include "scsi.h" +#include +#include + +#define DRV_NAME "sata_uli" +#define DRV_VERSION "0.11" + +enum { + uli_5289 = 0, + uli_5287 = 1, + + /* PCI configuration registers */ + ULI_SCR_BASE = 0x90, /* sata0 phy SCR registers */ + ULI_SATA1_OFS = 0x10, /* offset from sata0->sata1 phy regs */ + +}; + +static int uli_init_one (struct pci_dev *pdev, const struct pci_device_id *ent); +static u32 uli_scr_read (struct ata_port *ap, unsigned int sc_reg); +static void uli_scr_write (struct ata_port *ap, unsigned int sc_reg, u32 val); + +static struct pci_device_id uli_pci_tbl[] = { + { PCI_VENDOR_ID_AL, 0x5289, PCI_ANY_ID, PCI_ANY_ID, 0, 0, uli_5289 }, + { PCI_VENDOR_ID_AL, 0x5287, PCI_ANY_ID, PCI_ANY_ID, 0, 0, uli_5287 }, + { } /* terminate list */ +}; + + +static struct pci_driver uli_pci_driver = { + .name = DRV_NAME, + .id_table = uli_pci_tbl, + .probe = uli_init_one, + .remove = ata_pci_remove_one, +}; + +static Scsi_Host_Template uli_sht = { + .module = THIS_MODULE, + .name = DRV_NAME, + .ioctl = ata_scsi_ioctl, + .queuecommand = ata_scsi_queuecmd, + .eh_strategy_handler = ata_scsi_error, + .can_queue = ATA_DEF_QUEUE, + .this_id = ATA_SHT_THIS_ID, + .sg_tablesize = LIBATA_MAX_PRD, + .max_sectors = ATA_MAX_SECTORS, + .cmd_per_lun = ATA_SHT_CMD_PER_LUN, + .emulated = ATA_SHT_EMULATED, + .use_clustering = ATA_SHT_USE_CLUSTERING, + .proc_name = DRV_NAME, + .dma_boundary = ATA_DMA_BOUNDARY, + .slave_configure = ata_scsi_slave_config, + .bios_param = ata_std_bios_param, +}; + +static struct ata_port_operations uli_ops = { + .port_disable = ata_port_disable, + + .tf_load = ata_tf_load, + .tf_read = ata_tf_read, + .check_status = ata_check_status, + .exec_command = ata_exec_command, + .dev_select = ata_std_dev_select, + + .phy_reset = sata_phy_reset, + + .bmdma_setup = ata_bmdma_setup, + .bmdma_start = ata_bmdma_start, + .qc_prep = ata_qc_prep, + .qc_issue = ata_qc_issue_prot, + + .eng_timeout = ata_eng_timeout, + + .irq_handler = ata_interrupt, + .irq_clear = ata_bmdma_irq_clear, + + .scr_read = uli_scr_read, + .scr_write = uli_scr_write, + + .port_start = ata_port_start, + .port_stop = ata_port_stop, +}; + +static struct ata_port_info uli_port_info = { + .sht = &uli_sht, + .host_flags = ATA_FLAG_SATA | ATA_FLAG_SATA_RESET | + ATA_FLAG_NO_LEGACY, + .pio_mask = 0x03, //support pio mode 4 (FIXME) + .udma_mask = 0x7f, //support udma mode 6 + .port_ops = &uli_ops, +}; + + +MODULE_AUTHOR("Peer Chen"); +MODULE_DESCRIPTION("low-level driver for ULi Electronics SATA controller"); +MODULE_LICENSE("GPL"); +MODULE_DEVICE_TABLE(pci, uli_pci_tbl); +MODULE_VERSION(DRV_VERSION); + +static unsigned int get_scr_cfg_addr(unsigned int port_no, unsigned int sc_reg) +{ + unsigned int addr = ULI_SCR_BASE + (4 * sc_reg); + + switch (port_no) { + case 0: + break; + case 1: + addr += ULI_SATA1_OFS; + break; + case 2: + addr += ULI_SATA1_OFS*4; + break; + case 3: + addr += ULI_SATA1_OFS*5; + break; + default: + BUG(); + break; + } + return addr; +} + +static u32 uli_scr_cfg_read (struct ata_port *ap, unsigned int sc_reg) +{ + unsigned int cfg_addr = get_scr_cfg_addr(ap->port_no, sc_reg); + u32 val; + + pci_read_config_dword(ap->host_set->pdev, cfg_addr, &val); + return val; +} + +static void uli_scr_cfg_write (struct ata_port *ap, unsigned int scr, u32 val) +{ + unsigned int cfg_addr = get_scr_cfg_addr(ap->port_no, scr); + + pci_write_config_dword(ap->host_set->pdev, cfg_addr, val); +} + +static u32 uli_scr_read (struct ata_port *ap, unsigned int sc_reg) +{ + if (sc_reg > SCR_CONTROL) + return 0xffffffffU; + + return uli_scr_cfg_read(ap, sc_reg); +} + +static void uli_scr_write (struct ata_port *ap, unsigned int sc_reg, u32 val) +{ + if (sc_reg > SCR_CONTROL) //SCR_CONTROL=2, SCR_ERROR=1, SCR_STATUS=0 + return; + + uli_scr_cfg_write(ap, sc_reg, val); +} + +/* move to PCI layer, integrate w/ MSI stuff */ +static void pci_enable_intx(struct pci_dev *pdev) +{ + u16 pci_command; + + pci_read_config_word(pdev, PCI_COMMAND, &pci_command); + if (pci_command & PCI_COMMAND_INTX_DISABLE) { + pci_command &= ~PCI_COMMAND_INTX_DISABLE; + pci_write_config_word(pdev, PCI_COMMAND, pci_command); + } +} + +static int uli_init_one (struct pci_dev *pdev, const struct pci_device_id *ent) +{ + struct ata_probe_ent *probe_ent; + struct ata_port_info *ppi; + int rc; + unsigned int board_idx = (unsigned int) ent->driver_data; + + rc = pci_enable_device(pdev); + if (rc) + return rc; + + rc = pci_request_regions(pdev, DRV_NAME); + if (rc) + goto err_out; + + rc = pci_set_dma_mask(pdev, ATA_DMA_MASK); + if (rc) + goto err_out_regions; + rc = pci_set_consistent_dma_mask(pdev, ATA_DMA_MASK); + if (rc) + goto err_out_regions; + + ppi = &uli_port_info; + probe_ent = ata_pci_init_native_mode(pdev, &ppi); + if (!probe_ent) { + rc = -ENOMEM; + goto err_out_regions; + } + + switch (board_idx) { + case uli_5287: + probe_ent->n_ports = 4; + + probe_ent->port[2].cmd_addr = pci_resource_start(pdev, 0) + 8; + probe_ent->port[2].altstatus_addr = + probe_ent->port[2].ctl_addr = + (pci_resource_start(pdev, 1) | ATA_PCI_CTL_OFS) + 4; + probe_ent->port[2].bmdma_addr = pci_resource_start(pdev, 4) + 16; + + probe_ent->port[3].cmd_addr = pci_resource_start(pdev, 2) + 8; + probe_ent->port[3].altstatus_addr = + probe_ent->port[3].ctl_addr = + (pci_resource_start(pdev, 3) | ATA_PCI_CTL_OFS) + 4; + probe_ent->port[3].bmdma_addr = pci_resource_start(pdev, 4) + 24; + + ata_std_ports(&probe_ent->port[2]); + ata_std_ports(&probe_ent->port[3]); + break; + + case uli_5289: + /* do nothing; ata_pci_init_native_mode did it all */ + break; + + default: + BUG(); + break; + } + + pci_set_master(pdev); + pci_enable_intx(pdev); + + /* FIXME: check ata_device_add return value */ + ata_device_add(probe_ent); + kfree(probe_ent); + + return 0; + +err_out_regions: + pci_release_regions(pdev); + +err_out: + pci_disable_device(pdev); + return rc; + +} + +static int __init uli_init(void) +{ + return pci_module_init(&uli_pci_driver); +} + +static void __exit uli_exit(void) +{ + pci_unregister_driver(&uli_pci_driver); +} + + +module_init(uli_init); +module_exit(uli_exit); diff --git a/drivers/scsi/sym53c8xx_2/sym_defs.h b/drivers/scsi/sym53c8xx_2/sym_defs.h index 1241104b6..09f861a03 100644 --- a/drivers/scsi/sym53c8xx_2/sym_defs.h +++ b/drivers/scsi/sym53c8xx_2/sym_defs.h @@ -53,6 +53,9 @@ #ifndef SYM_DEFS_H #define SYM_DEFS_H +#define SYM_VERSION "2.1.18j" +#define SYM_DRIVER_NAME "sym-" SYM_VERSION + /* * Vendor. */ diff --git a/drivers/scsi/sym53c8xx_2/sym_glue.c b/drivers/scsi/sym53c8xx_2/sym_glue.c index 22238315d..0d9d1b42b 100644 --- a/drivers/scsi/sym53c8xx_2/sym_glue.c +++ b/drivers/scsi/sym53c8xx_2/sym_glue.c @@ -142,13 +142,6 @@ m_addr_t __vtobus(m_pool_ident_t dev_dmat, void *m) return b; } -/* - * Driver host data structure. - */ -struct host_data { - struct sym_hcb *ncb; -}; - /* * Used by the eh thread to wait for command completion. * It is allocated on the eh thread stack. @@ -220,47 +213,12 @@ static int __map_scsi_sg_data(struct pci_dev *pdev, struct scsi_cmnd *cmd) return use_sg; } -static void __sync_scsi_data_for_cpu(struct pci_dev *pdev, struct scsi_cmnd *cmd) -{ - int dma_dir = cmd->sc_data_direction; - - switch(SYM_UCMD_PTR(cmd)->data_mapped) { - case 2: - pci_dma_sync_sg_for_cpu(pdev, cmd->buffer, cmd->use_sg, dma_dir); - break; - case 1: - pci_dma_sync_single_for_cpu(pdev, SYM_UCMD_PTR(cmd)->data_mapping, - cmd->request_bufflen, dma_dir); - break; - } -} - -static void __sync_scsi_data_for_device(struct pci_dev *pdev, struct scsi_cmnd *cmd) -{ - int dma_dir = cmd->sc_data_direction; - - switch(SYM_UCMD_PTR(cmd)->data_mapped) { - case 2: - pci_dma_sync_sg_for_device(pdev, cmd->buffer, cmd->use_sg, dma_dir); - break; - case 1: - pci_dma_sync_single_for_device(pdev, SYM_UCMD_PTR(cmd)->data_mapping, - cmd->request_bufflen, dma_dir); - break; - } -} - #define unmap_scsi_data(np, cmd) \ __unmap_scsi_data(np->s.device, cmd) #define map_scsi_single_data(np, cmd) \ __map_scsi_single_data(np->s.device, cmd) #define map_scsi_sg_data(np, cmd) \ __map_scsi_sg_data(np->s.device, cmd) -#define sync_scsi_data_for_cpu(np, cmd) \ - __sync_scsi_data_for_cpu(np->s.device, cmd) -#define sync_scsi_data_for_device(np, cmd) \ - __sync_scsi_data_for_device(np->s.device, cmd) - /* * Complete a pending CAM CCB. */ @@ -416,27 +374,6 @@ void sym_set_cam_result_error(struct sym_hcb *np, struct sym_ccb *cp, int resid) } -/* - * Called on successfull INQUIRY response. - */ -void sym_sniff_inquiry(struct sym_hcb *np, struct scsi_cmnd *cmd, int resid) -{ - int retv; - - if (!cmd || cmd->use_sg) - return; - - sync_scsi_data_for_cpu(np, cmd); - retv = __sym_sniff_inquiry(np, cmd->device->id, cmd->device->lun, - (u_char *) cmd->request_buffer, - cmd->request_bufflen - resid); - sync_scsi_data_for_device(np, cmd); - if (retv < 0) - return; - else if (retv) - sym_update_trans_settings(np, &np->target[cmd->device->id]); -} - /* * Build the scatter/gather array for an I/O. */ @@ -730,14 +667,15 @@ void sym_log_bus_error(struct sym_hcb *np) */ static void sym_requeue_awaiting_cmds(struct sym_hcb *np) { - struct scsi_cmnd *cmd; - struct sym_ucmd *ucp = SYM_UCMD_PTR(cmd); + struct sym_ucmd *ucp; SYM_QUEHEAD tmp_cmdq; int sts; sym_que_move(&np->s.wait_cmdq, &tmp_cmdq); while ((ucp = (struct sym_ucmd *) sym_remque_head(&tmp_cmdq)) != 0) { + struct scsi_cmnd *cmd; + sym_insque_tail(&ucp->link_cmdq, &np->s.busy_cmdq); cmd = SYM_SCMD_PTR(ucp); sts = sym_queue_command(np, cmd); @@ -1118,12 +1056,7 @@ static int sym53c8xx_slave_configure(struct scsi_device *device) np = ((struct host_data *) host->hostdata)->ncb; tp = &np->target[device->id]; - - /* - * Get user settings for transfer parameters. - */ - tp->inq_byte7_valid = (INQ7_SYNC|INQ7_WIDE16); - sym_update_trans_settings(np, tp); + tp->sdev = device; /* * Allocate the LCB if not yet. @@ -2283,6 +2216,7 @@ static int sym_detach(struct sym_hcb *np) } MODULE_LICENSE("Dual BSD/GPL"); +MODULE_VERSION(SYM_VERSION); /* * Driver host template. @@ -2383,13 +2317,6 @@ static void sym2_set_offset(struct scsi_device *sdev, int offset) struct sym_hcb *np = ((struct host_data *)sdev->host->hostdata)->ncb; struct sym_tcb *tp = &np->target[sdev->id]; - if (tp->tinfo.curr.options & PPR_OPT_DT) { - if (offset > np->maxoffs_dt) - offset = np->maxoffs_dt; - } else { - if (offset > np->maxoffs) - offset = np->maxoffs; - } tp->tinfo.goal.offset = offset; } @@ -2407,23 +2334,11 @@ static void sym2_set_period(struct scsi_device *sdev, int period) struct sym_hcb *np = ((struct host_data *)sdev->host->hostdata)->ncb; struct sym_tcb *tp = &np->target[sdev->id]; - if (period <= 9 && np->minsync_dt) { - if (period < np->minsync_dt) - period = np->minsync_dt; - tp->tinfo.goal.options = PPR_OPT_DT; - tp->tinfo.goal.period = period; - if (!tp->tinfo.curr.offset || - tp->tinfo.curr.offset > np->maxoffs_dt) - tp->tinfo.goal.offset = np->maxoffs_dt; - } else { - if (period < np->minsync) - period = np->minsync; - tp->tinfo.goal.options = 0; - tp->tinfo.goal.period = period; - if (!tp->tinfo.curr.offset || - tp->tinfo.curr.offset > np->maxoffs) - tp->tinfo.goal.offset = np->maxoffs; - } + /* have to have DT for these transfers */ + if (period <= np->minsync) + tp->tinfo.goal.options |= PPR_OPT_DT; + + tp->tinfo.goal.period = period; } static void sym2_get_width(struct scsi_device *sdev) @@ -2439,6 +2354,10 @@ static void sym2_set_width(struct scsi_device *sdev, int width) struct sym_hcb *np = ((struct host_data *)sdev->host->hostdata)->ncb; struct sym_tcb *tp = &np->target[sdev->id]; + /* It is illegal to have DT set on narrow transfers */ + if (width == 0) + tp->tinfo.goal.options &= ~PPR_OPT_DT; + tp->tinfo.goal.width = width; } @@ -2455,17 +2374,10 @@ static void sym2_set_dt(struct scsi_device *sdev, int dt) struct sym_hcb *np = ((struct host_data *)sdev->host->hostdata)->ncb; struct sym_tcb *tp = &np->target[sdev->id]; - if (!dt) { - /* if clearing DT, then we may need to reduce the - * period and the offset */ - if (tp->tinfo.curr.period < np->minsync) - tp->tinfo.goal.period = np->minsync; - if (tp->tinfo.curr.offset > np->maxoffs) - tp->tinfo.goal.offset = np->maxoffs; - tp->tinfo.goal.options &= ~PPR_OPT_DT; - } else { + if (dt) tp->tinfo.goal.options |= PPR_OPT_DT; - } + else + tp->tinfo.goal.options &= ~PPR_OPT_DT; } diff --git a/drivers/scsi/sym53c8xx_2/sym_glue.h b/drivers/scsi/sym53c8xx_2/sym_glue.h index 60d48b750..5dd15fc0f 100644 --- a/drivers/scsi/sym53c8xx_2/sym_glue.h +++ b/drivers/scsi/sym53c8xx_2/sym_glue.h @@ -90,7 +90,6 @@ #define SYM_OPT_HANDLE_DIR_UNKNOWN #define SYM_OPT_HANDLE_DEVICE_QUEUEING #define SYM_OPT_NVRAM_PRE_READ -#define SYM_OPT_SNIFF_INQUIRY #define SYM_OPT_LIMIT_COMMAND_REORDERING #define SYM_OPT_ANNOUNCE_TRANSFER_RATE @@ -289,32 +288,32 @@ typedef struct scsi_cmnd *cam_scsiio_p;/* SCSI I/O */ * MEMORY mapped IO input / output */ -#define INB_OFF(o) readb((char *)np->s.mmio_va + sym_offb(o)) -#define OUTB_OFF(o, val) writeb((val), (char *)np->s.mmio_va + sym_offb(o)) +#define INB_OFF(o) readb(np->s.mmio_va + sym_offb(o)) +#define OUTB_OFF(o, val) writeb((val), np->s.mmio_va + sym_offb(o)) #if defined(__BIG_ENDIAN) && !defined(SYM_CONF_CHIP_BIG_ENDIAN) -#define INW_OFF(o) readw_l2b((char *)np->s.mmio_va + sym_offw(o)) -#define INL_OFF(o) readl_l2b((char *)np->s.mmio_va + (o)) +#define INW_OFF(o) readw_l2b(np->s.mmio_va + sym_offw(o)) +#define INL_OFF(o) readl_l2b(np->s.mmio_va + (o)) -#define OUTW_OFF(o, val) writew_b2l((val), (char *)np->s.mmio_va + sym_offw(o)) -#define OUTL_OFF(o, val) writel_b2l((val), (char *)np->s.mmio_va + (o)) +#define OUTW_OFF(o, val) writew_b2l((val), np->s.mmio_va + sym_offw(o)) +#define OUTL_OFF(o, val) writel_b2l((val), np->s.mmio_va + (o)) #elif defined(__LITTLE_ENDIAN) && defined(SYM_CONF_CHIP_BIG_ENDIAN) -#define INW_OFF(o) readw_b2l((char *)np->s.mmio_va + sym_offw(o)) -#define INL_OFF(o) readl_b2l((char *)np->s.mmio_va + (o)) +#define INW_OFF(o) readw_b2l(np->s.mmio_va + sym_offw(o)) +#define INL_OFF(o) readl_b2l(np->s.mmio_va + (o)) -#define OUTW_OFF(o, val) writew_l2b((val), (char *)np->s.mmio_va + sym_offw(o)) -#define OUTL_OFF(o, val) writel_l2b((val), (char *)np->s.mmio_va + (o)) +#define OUTW_OFF(o, val) writew_l2b((val), np->s.mmio_va + sym_offw(o)) +#define OUTL_OFF(o, val) writel_l2b((val), np->s.mmio_va + (o)) #else -#define INW_OFF(o) readw_raw((char *)np->s.mmio_va + sym_offw(o)) -#define INL_OFF(o) readl_raw((char *)np->s.mmio_va + (o)) +#define INW_OFF(o) readw_raw(np->s.mmio_va + sym_offw(o)) +#define INL_OFF(o) readl_raw(np->s.mmio_va + (o)) -#define OUTW_OFF(o, val) writew_raw((val), (char *)np->s.mmio_va + sym_offw(o)) -#define OUTL_OFF(o, val) writel_raw((val), (char *)np->s.mmio_va + (o)) +#define OUTW_OFF(o, val) writew_raw((val), np->s.mmio_va + sym_offw(o)) +#define OUTL_OFF(o, val) writel_raw((val), np->s.mmio_va + (o)) #endif @@ -391,8 +390,8 @@ struct sym_shcb { struct Scsi_Host *host; - void * mmio_va; /* MMIO kernel virtual address */ - void * ram_va; /* RAM kernel virtual address */ + void __iomem * mmio_va; /* MMIO kernel virtual address */ + void __iomem * ram_va; /* RAM kernel virtual address */ u_long io_port; /* IO port address cookie */ u_short io_ws; /* IO window size */ int irq; /* IRQ number */ @@ -424,7 +423,7 @@ struct sym_slot { int irq; /* port and address fields to fit INB, OUTB macros */ u_long io_port; - void * mmio_va; + void __iomem * mmio_va; char inst_name[16]; }; @@ -439,6 +438,13 @@ struct sym_device { u_char host_id; }; +/* + * Driver host data structure. + */ +struct host_data { + struct sym_hcb *ncb; +}; + /* * The driver definitions (sym_hipd.h) must know about a * couple of things related to the memory allocator. diff --git a/drivers/scsi/sym53c8xx_2/sym_hipd.c b/drivers/scsi/sym53c8xx_2/sym_hipd.c index 25a27f998..dddf88b87 100644 --- a/drivers/scsi/sym53c8xx_2/sym_hipd.c +++ b/drivers/scsi/sym53c8xx_2/sym_hipd.c @@ -49,10 +49,6 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ - -#define SYM_VERSION "2.1.18j" -#define SYM_DRIVER_NAME "sym-" SYM_VERSION - #include "sym_glue.h" #include "sym_nvram.h" @@ -1042,28 +1038,11 @@ static int sym_prepare_setting(hcb_p np, struct sym_nvram *nvram) for (i = 0 ; i < SYM_CONF_MAX_TARGET ; i++) { tcb_p tp = &np->target[i]; - tp->tinfo.user.scsi_version = tp->tinfo.curr.scsi_version= 2; - tp->tinfo.user.spi_version = tp->tinfo.curr.spi_version = 2; - tp->tinfo.user.period = np->minsync; - tp->tinfo.user.offset = np->maxoffs; - tp->tinfo.user.width = np->maxwide ? BUS_16_BIT : BUS_8_BIT; tp->usrflags |= (SYM_DISC_ENABLED | SYM_TAGS_ENABLED); tp->usrtags = SYM_SETUP_MAX_TAG; sym_nvram_setup_target (np, i, nvram); - /* - * Some single-ended devices may crash on receiving a - * PPR negotiation attempt. Only try PPR if we're in - * LVD mode. - */ - if (np->features & FE_ULTRA3) { - tp->tinfo.user.options |= PPR_OPT_DT; - tp->tinfo.user.period = np->minsync_dt; - tp->tinfo.user.offset = np->maxoffs_dt; - tp->tinfo.user.spi_version = 3; - } - if (!tp->usrtags) tp->usrflags &= ~SYM_TAGS_ENABLED; } @@ -1497,6 +1476,55 @@ static void sym_update_dmap_regs(hcb_p np) } #endif +static void sym_check_goals(struct scsi_device *sdev) +{ + struct sym_hcb *np = ((struct host_data *)sdev->host->hostdata)->ncb; + struct sym_trans *st = &np->target[sdev->id].tinfo.goal; + + /* here we enforce all the fiddly SPI rules */ + + if (!scsi_device_wide(sdev)) + st->width = 0; + + if (!scsi_device_sync(sdev)) { + st->options = 0; + st->period = 0; + st->offset = 0; + return; + } + + if (scsi_device_dt(sdev)) { + if (scsi_device_dt_only(sdev)) + st->options |= PPR_OPT_DT; + + if (st->offset == 0) + st->options &= ~PPR_OPT_DT; + } else { + st->options &= ~PPR_OPT_DT; + } + + if (!(np->features & FE_ULTRA3)) + st->options &= ~PPR_OPT_DT; + + if (st->options & PPR_OPT_DT) { + /* all DT transfers must be wide */ + st->width = 1; + if (st->offset > np->maxoffs_dt) + st->offset = np->maxoffs_dt; + if (st->period < np->minsync_dt) + st->period = np->minsync_dt; + if (st->period > np->maxsync_dt) + st->period = np->maxsync_dt; + } else { + if (st->offset > np->maxoffs) + st->offset = np->maxoffs; + if (st->period < np->minsync) + st->period = np->minsync; + if (st->period > np->maxsync) + st->period = np->maxsync; + } +} + /* * Prepare the next negotiation message if needed. * @@ -1508,6 +1536,10 @@ static int sym_prepare_nego(hcb_p np, ccb_p cp, int nego, u_char *msgptr) { tcb_p tp = &np->target[cp->target]; int msglen = 0; + struct scsi_device *sdev = tp->sdev; + + if (likely(sdev)) + sym_check_goals(sdev); /* * Early C1010 chips need a work-around for DT @@ -1518,19 +1550,21 @@ static int sym_prepare_nego(hcb_p np, ccb_p cp, int nego, u_char *msgptr) /* * negotiate using PPR ? */ - if (tp->tinfo.goal.options & PPR_OPT_MASK) + if (scsi_device_dt(sdev)) { nego = NS_PPR; - /* - * negotiate wide transfers ? - */ - else if (tp->tinfo.curr.width != tp->tinfo.goal.width) - nego = NS_WIDE; - /* - * negotiate synchronous transfers? - */ - else if (tp->tinfo.curr.period != tp->tinfo.goal.period || - tp->tinfo.curr.offset != tp->tinfo.goal.offset) - nego = NS_SYNC; + } else { + /* + * negotiate wide transfers ? + */ + if (tp->tinfo.curr.width != tp->tinfo.goal.width) + nego = NS_WIDE; + /* + * negotiate synchronous transfers? + */ + else if (tp->tinfo.curr.period != tp->tinfo.goal.period || + tp->tinfo.curr.offset != tp->tinfo.goal.offset) + nego = NS_SYNC; + } switch (nego) { case NS_SYNC: @@ -3999,7 +4033,6 @@ int sym_compute_residual(hcb_p np, ccb_p cp) static int sym_sync_nego_check(hcb_p np, int req, int target) { - tcb_p tp = &np->target[target]; u_char chg, ofs, per, fak, div; if (DEBUG_FLAGS & DEBUG_NEGO) { @@ -4019,19 +4052,11 @@ sym_sync_nego_check(hcb_p np, int req, int target) if (ofs) { if (ofs > np->maxoffs) {chg = 1; ofs = np->maxoffs;} - if (req) { - if (ofs > tp->tinfo.user.offset) - {chg = 1; ofs = tp->tinfo.user.offset;} - } } if (ofs) { if (per < np->minsync) {chg = 1; per = np->minsync;} - if (req) { - if (per < tp->tinfo.user.period) - {chg = 1; per = tp->tinfo.user.period;} - } } /* @@ -4151,10 +4176,6 @@ sym_ppr_nego_check(hcb_p np, int req, int target) } if (!wide || !(np->features & FE_ULTRA3)) dt &= ~PPR_OPT_DT; - if (req) { - if (wide > tp->tinfo.user.width) - {chg = 1; wide = tp->tinfo.user.width;} - } if (!(np->features & FE_U3EN)) /* Broken U3EN bit not supported */ dt &= ~PPR_OPT_DT; @@ -4168,10 +4189,6 @@ sym_ppr_nego_check(hcb_p np, int req, int target) } else if (ofs > np->maxoffs) {chg = 1; ofs = np->maxoffs;} - if (req) { - if (ofs > tp->tinfo.user.offset) - {chg = 1; ofs = tp->tinfo.user.offset;} - } } if (ofs) { @@ -4181,10 +4198,6 @@ sym_ppr_nego_check(hcb_p np, int req, int target) } else if (per < np->minsync) {chg = 1; per = np->minsync;} - if (req) { - if (per < tp->tinfo.user.period) - {chg = 1; per = tp->tinfo.user.period;} - } } /* @@ -4286,7 +4299,6 @@ reject_it: static int sym_wide_nego_check(hcb_p np, int req, int target) { - tcb_p tp = &np->target[target]; u_char chg, wide; if (DEBUG_FLAGS & DEBUG_NEGO) { @@ -4306,10 +4318,6 @@ sym_wide_nego_check(hcb_p np, int req, int target) chg = 1; wide = np->maxwide; } - if (req) { - if (wide > tp->tinfo.user.width) - {chg = 1; wide = tp->tinfo.user.width;} - } if (DEBUG_FLAGS & DEBUG_NEGO) { PRINT_TARGET(np, target); diff --git a/drivers/scsi/sym53c8xx_2/sym_hipd.h b/drivers/scsi/sym53c8xx_2/sym_hipd.h index aa246dd0f..04b5411ea 100644 --- a/drivers/scsi/sym53c8xx_2/sym_hipd.h +++ b/drivers/scsi/sym53c8xx_2/sym_hipd.h @@ -69,11 +69,6 @@ * When this option is set, the driver will use a queue per * device and handle QUEUE FULL status requeuing internally. * - * SYM_OPT_SNIFF_INQUIRY - * When this option is set, the driver sniff out successful - * INQUIRY response and performs negotiations accordingly. - * (set for Linux) - * * SYM_OPT_LIMIT_COMMAND_REORDERING * When this option is set, the driver tries to limit tagged * command reordering to some reasonnable value. @@ -82,7 +77,6 @@ #if 0 #define SYM_OPT_HANDLE_DIR_UNKNOWN #define SYM_OPT_HANDLE_DEVICE_QUEUEING -#define SYM_OPT_SNIFF_INQUIRY #define SYM_OPT_LIMIT_COMMAND_REORDERING #endif @@ -364,7 +358,6 @@ struct sym_trans { struct sym_tinfo { struct sym_trans curr; struct sym_trans goal; - struct sym_trans user; #ifdef SYM_OPT_ANNOUNCE_TRANSFER_RATE struct sym_trans prev; #endif @@ -465,18 +458,7 @@ struct sym_tcb { */ u_char usrflags; u_short usrtags; - -#ifdef SYM_OPT_SNIFF_INQUIRY - /* - * Some minimal information from INQUIRY response. - */ - u32 cmdq_map[(SYM_CONF_MAX_LUN+31)/32]; - u_char inq_version; - u_char inq_byte7; - u_char inq_byte56; - u_char inq_byte7_valid; -#endif - + struct scsi_device *sdev; }; /* @@ -1168,26 +1150,6 @@ void sym_clock(hcb_p np); void sym_announce_transfer_rate(hcb_p np, int target); #endif -/* - * Optionnaly, the driver may sniff inquiry data. - */ -#ifdef SYM_OPT_SNIFF_INQUIRY -#define INQ7_CMDQ (0x02) -#define INQ7_SYNC (0x10) -#define INQ7_WIDE16 (0x20) - -#define INQ56_CLOCKING (3<<2) -#define INQ56_ST_ONLY (0<<2) -#define INQ56_DT_ONLY (1<<2) -#define INQ56_ST_DT (3<<2) - -void sym_update_trans_settings(hcb_p np, tcb_p tp); -int -__sym_sniff_inquiry(hcb_p np, u_char tn, u_char ln, - u_char *inq_data, int inq_len); -#endif - - /* * Build a scatter/gather entry. * diff --git a/drivers/scsi/sym53c8xx_2/sym_misc.c b/drivers/scsi/sym53c8xx_2/sym_misc.c index ee2a7b28a..6b3c8b84c 100644 --- a/drivers/scsi/sym53c8xx_2/sym_misc.c +++ b/drivers/scsi/sym53c8xx_2/sym_misc.c @@ -216,121 +216,3 @@ void sym_announce_transfer_rate(hcb_p np, int target) #undef __tprev #undef __tcurr #endif /* SYM_OPT_ANNOUNCE_TRANSFER_RATE */ - - -#ifdef SYM_OPT_SNIFF_INQUIRY -/* - * Update transfer settings according to user settings - * and bits sniffed out from INQUIRY response. - */ -void sym_update_trans_settings(hcb_p np, tcb_p tp) -{ - memcpy(&tp->tinfo.goal, &tp->tinfo.user, sizeof(tp->tinfo.goal)); - - if (tp->inq_version >= 4) { - switch(tp->inq_byte56 & INQ56_CLOCKING) { - case INQ56_ST_ONLY: - tp->tinfo.goal.options = 0; - break; - case INQ56_DT_ONLY: - case INQ56_ST_DT: - default: - break; - } - } - - if (!((tp->inq_byte7 & tp->inq_byte7_valid) & INQ7_WIDE16)) { - tp->tinfo.goal.width = 0; - tp->tinfo.goal.options = 0; - } - - if (!((tp->inq_byte7 & tp->inq_byte7_valid) & INQ7_SYNC)) { - tp->tinfo.goal.offset = 0; - tp->tinfo.goal.options = 0; - } - - if (tp->tinfo.goal.options & PPR_OPT_DT) { - if (tp->tinfo.goal.offset > np->maxoffs_dt) - tp->tinfo.goal.offset = np->maxoffs_dt; - } - else { - if (tp->tinfo.goal.offset > np->maxoffs) - tp->tinfo.goal.offset = np->maxoffs; - } -} - -/* - * Snoop target capabilities from INQUIRY response. - * We only believe device versions >= SCSI-2 that use - * appropriate response data format (2). But it seems - * that some CCS devices also support SYNC (?). - */ -int -__sym_sniff_inquiry(hcb_p np, u_char tn, u_char ln, - u_char *inq_data, int inq_len) -{ - tcb_p tp = &np->target[tn]; - u_char inq_version; - u_char inq_byte7; - u_char inq_byte56; - - if (!inq_data || inq_len < 2) - return -1; - - /* - * Check device type and qualifier. - */ - if ((inq_data[0] & 0xe0) == 0x60) - return -1; - - /* - * Get SPC version. - */ - if (inq_len <= 2) - return -1; - inq_version = inq_data[2] & 0x7; - - /* - * Get SYNC/WIDE16 capabilities. - */ - inq_byte7 = tp->inq_byte7; - if (inq_version >= 2 && (inq_data[3] & 0xf) == 2) { - if (inq_len > 7) - inq_byte7 = inq_data[7]; - } - else if (inq_version == 1 && (inq_data[3] & 0xf) == 1) - inq_byte7 = INQ7_SYNC; - - /* - * Get Tagged Command Queuing capability. - */ - if (inq_byte7 & INQ7_CMDQ) - sym_set_bit(tp->cmdq_map, ln); - else - sym_clr_bit(tp->cmdq_map, ln); - inq_byte7 &= ~INQ7_CMDQ; - - /* - * Get CLOCKING capability. - */ - inq_byte56 = tp->inq_byte56; - if (inq_version >= 4 && inq_len > 56) - inq_byte56 = inq_data[56]; -#if 0 -printf("XXXXXX [%d] inq_version=%x inq_byte7=%x inq_byte56=%x XXXXX\n", - inq_len, inq_version, inq_byte7, inq_byte56); -#endif - /* - * Trigger a negotiation if needed. - */ - if (tp->inq_version != inq_version || - tp->inq_byte7 != inq_byte7 || - tp->inq_byte56 != inq_byte56) { - tp->inq_version = inq_version; - tp->inq_byte7 = inq_byte7; - tp->inq_byte56 = inq_byte56; - return 1; - } - return 0; -} -#endif /* SYM_OPT_SNIFF_INQUIRY */ diff --git a/drivers/scsi/sym53c8xx_2/sym_nvram.c b/drivers/scsi/sym53c8xx_2/sym_nvram.c index 2bb4b3f3e..ca857ffc6 100644 --- a/drivers/scsi/sym53c8xx_2/sym_nvram.c +++ b/drivers/scsi/sym53c8xx_2/sym_nvram.c @@ -53,11 +53,6 @@ #include "sym_glue.h" #include "sym_nvram.h" -/* - * Some poor and bogus sync table that refers to Tekram NVRAM layout. - */ -static u_char Tekram_sync[16] = - {25,31,37,43, 50,62,75,125, 12,15,18,21, 6,7,9,10}; #ifdef SYM_CONF_DEBUG_NVRAM static u_char Tekram_boot_delay[7] = {3, 5, 10, 20, 30, 60, 120}; #endif @@ -100,8 +95,6 @@ sym_Symbios_setup_target(struct sym_hcb *np, int target, Symbios_nvram *nvram) struct sym_tcb *tp = &np->target[target]; Symbios_target *tn = &nvram->target[target]; - tp->tinfo.user.period = tn->sync_period ? (tn->sync_period + 3) / 4 : 0; - tp->tinfo.user.width = tn->bus_width == 0x10 ? BUS_16_BIT : BUS_8_BIT; tp->usrtags = (tn->flags & SYMBIOS_QUEUE_TAGS_ENABLED)? SYM_SETUP_MAX_TAG : 0; @@ -121,15 +114,6 @@ sym_Tekram_setup_target(struct sym_hcb *np, int target, Tekram_nvram *nvram) { struct sym_tcb *tp = &np->target[target]; struct Tekram_target *tn = &nvram->target[target]; - int i; - - if (tn->flags & TEKRAM_SYNC_NEGO) { - i = tn->sync_index & 0xf; - tp->tinfo.user.period = Tekram_sync[i]; - } - - tp->tinfo.user.width = (tn->flags & TEKRAM_WIDE_NEGO) ? - BUS_16_BIT : BUS_8_BIT; if (tn->flags & TEKRAM_TAGGED_COMMANDS) { tp->usrtags = 2 << nvram->max_tags_index; diff --git a/drivers/serial/68328serial.c b/drivers/serial/68328serial.c index 5c499be24..6dd522466 100644 --- a/drivers/serial/68328serial.c +++ b/drivers/serial/68328serial.c @@ -435,10 +435,7 @@ static void do_softint(void *private) return; #if 0 if (clear_bit(RS_EVENT_WRITE_WAKEUP, &info->event)) { - if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && - tty->ldisc.write_wakeup) - (tty->ldisc.write_wakeup)(tty); - wake_up_interruptible(&tty->write_wait); + tty_wakeup(tty); } #endif } @@ -858,10 +855,7 @@ static void rs_flush_buffer(struct tty_struct *tty) cli(); info->xmit_cnt = info->xmit_head = info->xmit_tail = 0; sti(); - wake_up_interruptible(&tty->write_wait); - if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && - tty->ldisc.write_wakeup) - (tty->ldisc.write_wakeup)(tty); + tty_wakeup(tty); } /* @@ -1185,11 +1179,13 @@ static void rs_close(struct tty_struct *tty, struct file * filp) shutdown(info); if (tty->driver->flush_buffer) tty->driver->flush_buffer(tty); - if (tty->ldisc.flush_buffer) - tty->ldisc.flush_buffer(tty); + + tty_ldisc_flush(tty); tty->closing = 0; info->event = 0; info->tty = 0; +#warning "This is not and has never been valid so fix it" +#if 0 if (tty->ldisc.num != ldiscs[N_TTY].num) { if (tty->ldisc.close) (tty->ldisc.close)(tty); @@ -1198,6 +1194,7 @@ static void rs_close(struct tty_struct *tty, struct file * filp) if (tty->ldisc.open) (tty->ldisc.open)(tty); } +#endif if (info->blocked_open) { if (info->close_delay) { current->state = TASK_INTERRUPTIBLE; diff --git a/drivers/serial/8250.h b/drivers/serial/8250.h index da0ad9b3d..79c036852 100644 --- a/drivers/serial/8250.h +++ b/drivers/serial/8250.h @@ -33,6 +33,20 @@ struct old_serial_port { unsigned short iomem_reg_shift; }; +/* + * This replaces serial_uart_config in include/linux/serial.h + */ +struct serial8250_config { + const char *name; + unsigned int fifo_size; + unsigned int tx_loadsz; + unsigned int flags; +}; + +#define UART_CAP_FIFO (1 << 8) /* UART has FIFO */ +#define UART_CAP_EFR (1 << 9) /* UART has EFR */ +#define UART_CAP_SLEEP (1 << 10) /* UART has IER sleep */ + #undef SERIAL_DEBUG_PCI #if defined(__i386__) && (defined(CONFIG_M386) || defined(CONFIG_M486)) diff --git a/drivers/serial/icom.c b/drivers/serial/icom.c new file mode 100644 index 000000000..c15c8a0aa --- /dev/null +++ b/drivers/serial/icom.c @@ -0,0 +1,1702 @@ +/* + * icom.c + * + * Copyright (C) 2001 IBM Corporation. All rights reserved. + * + * Serial device driver. + * + * Based on code from serial.c + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You 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 SERIAL_DO_RESTART +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "icom.h" + +/*#define ICOM_TRACE enable port trace capabilities */ + +#define ICOM_DRIVER_NAME "icom" +#define ICOM_VERSION_STR "1.3.1" +#define NR_PORTS 128 +#define ICOM_PORT ((struct icom_port *)port) +#define to_icom_adapter(d) container_of(d, struct icom_adapter, kobj) + +static const struct pci_device_id icom_pci_table[] = { + { + .vendor = PCI_VENDOR_ID_IBM, + .device = PCI_DEVICE_ID_IBM_ICOM_DEV_ID_1, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .driver_data = ADAPTER_V1, + }, + { + .vendor = PCI_VENDOR_ID_IBM, + .device = PCI_DEVICE_ID_IBM_ICOM_DEV_ID_2, + .subvendor = PCI_VENDOR_ID_IBM, + .subdevice = PCI_DEVICE_ID_IBM_ICOM_V2_TWO_PORTS_RVX, + .driver_data = ADAPTER_V2, + }, + { + .vendor = PCI_VENDOR_ID_IBM, + .device = PCI_DEVICE_ID_IBM_ICOM_DEV_ID_2, + .subvendor = PCI_VENDOR_ID_IBM, + .subdevice = PCI_DEVICE_ID_IBM_ICOM_V2_ONE_PORT_RVX_ONE_PORT_MDM, + .driver_data = ADAPTER_V2, + }, + { + .vendor = PCI_VENDOR_ID_IBM, + .device = PCI_DEVICE_ID_IBM_ICOM_DEV_ID_2, + .subvendor = PCI_VENDOR_ID_IBM, + .subdevice = PCI_DEVICE_ID_IBM_ICOM_FOUR_PORT_MODEL, + .driver_data = ADAPTER_V2, + }, + {} +}; + +struct lookup_proc_table start_proc[4] = { + {0, ICOM_CONTROL_START_A}, + {0, ICOM_CONTROL_START_B}, + {0, ICOM_CONTROL_START_C}, + {0, ICOM_CONTROL_START_D} +}; + + +struct lookup_proc_table stop_proc[4] = { + {0, ICOM_CONTROL_STOP_A}, + {0, ICOM_CONTROL_STOP_B}, + {0, ICOM_CONTROL_STOP_C}, + {0, ICOM_CONTROL_STOP_D} +}; + +struct lookup_int_table int_mask_tbl[4] = { + {0, ICOM_INT_MASK_PRC_A}, + {0, ICOM_INT_MASK_PRC_B}, + {0, ICOM_INT_MASK_PRC_C}, + {0, ICOM_INT_MASK_PRC_D}, +}; + + +MODULE_DEVICE_TABLE(pci, icom_pci_table); + +static LIST_HEAD(icom_adapter_head); + +/* spinlock for adapter initialization and changing adapter operations */ +static spinlock_t icom_lock; + +#ifdef ICOM_TRACE +static inline void trace(struct icom_port *, char *, unsigned long) {}; +#else +static inline void trace(struct icom_port *icom_port, char *trace_pt, unsigned long trace_data) {}; +#endif + +static void msleep(unsigned long msecs) +{ + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout(MSECS_TO_JIFFIES(msecs)); +} + +static void free_port_memory(struct icom_port *icom_port) +{ + struct pci_dev *dev = icom_port->adapter->pci_dev; + + trace(icom_port, "RET_PORT_MEM", 0); + if (icom_port->recv_buf) { + pci_free_consistent(dev, 4096, icom_port->recv_buf, + icom_port->recv_buf_pci); + icom_port->recv_buf = 0; + } + if (icom_port->xmit_buf) { + pci_free_consistent(dev, 4096, icom_port->xmit_buf, + icom_port->xmit_buf_pci); + icom_port->xmit_buf = 0; + } + if (icom_port->statStg) { + pci_free_consistent(dev, 4096, icom_port->statStg, + icom_port->statStg_pci); + icom_port->statStg = 0; + } + + if (icom_port->xmitRestart) { + pci_free_consistent(dev, 4096, icom_port->xmitRestart, + icom_port->xmitRestart_pci); + icom_port->xmitRestart = 0; + } +} + +static int __init get_port_memory(struct icom_port *icom_port) +{ + int index; + unsigned long stgAddr; + unsigned long startStgAddr; + unsigned long offset; + struct pci_dev *dev = icom_port->adapter->pci_dev; + + icom_port->xmit_buf = + pci_alloc_consistent(dev, 4096, &icom_port->xmit_buf_pci); + if (!icom_port->xmit_buf) { + dev_err(&dev->dev, "Can not allocate Transmit buffer\n"); + return -ENOMEM; + } + + trace(icom_port, "GET_PORT_MEM", + (unsigned long) icom_port->xmit_buf); + + icom_port->recv_buf = + pci_alloc_consistent(dev, 4096, &icom_port->recv_buf_pci); + if (!icom_port->recv_buf) { + dev_err(&dev->dev, "Can not allocate Receive buffer\n"); + free_port_memory(icom_port); + return -ENOMEM; + } + trace(icom_port, "GET_PORT_MEM", + (unsigned long) icom_port->recv_buf); + + icom_port->statStg = + pci_alloc_consistent(dev, 4096, &icom_port->statStg_pci); + if (!icom_port->statStg) { + dev_err(&dev->dev, "Can not allocate Status buffer\n"); + free_port_memory(icom_port); + return -ENOMEM; + } + trace(icom_port, "GET_PORT_MEM", + (unsigned long) icom_port->statStg); + + icom_port->xmitRestart = + pci_alloc_consistent(dev, 4096, &icom_port->xmitRestart_pci); + if (!icom_port->xmitRestart) { + dev_err(&dev->dev, + "Can not allocate xmit Restart buffer\n"); + free_port_memory(icom_port); + return -ENOMEM; + } + + memset(icom_port->statStg, 0, 4096); + + /* FODs: Frame Out Descriptor Queue, this is a FIFO queue that + indicates that frames are to be transmitted + */ + + stgAddr = (unsigned long) icom_port->statStg; + for (index = 0; index < NUM_XBUFFS; index++) { + trace(icom_port, "FOD_ADDR", stgAddr); + stgAddr = stgAddr + sizeof(icom_port->statStg->xmit[0]); + if (index < (NUM_XBUFFS - 1)) { + memset(&icom_port->statStg->xmit[index], 0, sizeof(struct xmit_status_area)); + icom_port->statStg->xmit[index].leLengthASD = + (unsigned short int) cpu_to_le16(XMIT_BUFF_SZ); + trace(icom_port, "FOD_ADDR", stgAddr); + trace(icom_port, "FOD_XBUFF", + (unsigned long) icom_port->xmit_buf); + icom_port->statStg->xmit[index].leBuffer = + cpu_to_le32(icom_port->xmit_buf_pci); + } else if (index == (NUM_XBUFFS - 1)) { + memset(&icom_port->statStg->xmit[index], 0, sizeof(struct xmit_status_area)); + icom_port->statStg->xmit[index].leLengthASD = + (unsigned short int) cpu_to_le16(XMIT_BUFF_SZ); + trace(icom_port, "FOD_XBUFF", + (unsigned long) icom_port->xmit_buf); + icom_port->statStg->xmit[index].leBuffer = + cpu_to_le32(icom_port->xmit_buf_pci); + } else { + memset(&icom_port->statStg->xmit[index], 0, sizeof(struct xmit_status_area)); + } + } + /* FIDs */ + startStgAddr = stgAddr; + + /* fill in every entry, even if no buffer */ + for (index = 0; index < NUM_RBUFFS; index++) { + trace(icom_port, "FID_ADDR", stgAddr); + stgAddr = stgAddr + sizeof(icom_port->statStg->rcv[0]); + icom_port->statStg->rcv[index].leLength = 0; + icom_port->statStg->rcv[index].WorkingLength = + (unsigned short int) cpu_to_le16(RCV_BUFF_SZ); + if (index < (NUM_RBUFFS - 1) ) { + offset = stgAddr - (unsigned long) icom_port->statStg; + icom_port->statStg->rcv[index].leNext = + cpu_to_le32(icom_port-> statStg_pci + offset); + trace(icom_port, "FID_RBUFF", + (unsigned long) icom_port->recv_buf); + icom_port->statStg->rcv[index].leBuffer = + cpu_to_le32(icom_port->recv_buf_pci); + } else if (index == (NUM_RBUFFS -1) ) { + offset = startStgAddr - (unsigned long) icom_port->statStg; + icom_port->statStg->rcv[index].leNext = + cpu_to_le32(icom_port-> statStg_pci + offset); + trace(icom_port, "FID_RBUFF", + (unsigned long) icom_port->recv_buf + 2048); + icom_port->statStg->rcv[index].leBuffer = + cpu_to_le32(icom_port->recv_buf_pci + 2048); + } else { + icom_port->statStg->rcv[index].leNext = 0; + icom_port->statStg->rcv[index].leBuffer = 0; + } + } + + return 0; +} + +static void stop_processor(struct icom_port *icom_port) +{ + unsigned long temp; + unsigned long flags; + int port; + + spin_lock_irqsave(&icom_lock, flags); + + port = icom_port->port; + if (port == 0 || port == 1) + stop_proc[port].global_control_reg = &icom_port->global_reg->control; + else + stop_proc[port].global_control_reg = &icom_port->global_reg->control_2; + + + if (port < 4) { + temp = readl(stop_proc[port].global_control_reg); + temp = + (temp & ~start_proc[port].processor_id) | stop_proc[port].processor_id; + writel(temp, stop_proc[port].global_control_reg); + + /* write flush */ + readl(stop_proc[port].global_control_reg); + } else { + dev_err(&icom_port->adapter->pci_dev->dev, + "Invalid port assignment\n"); + } + + spin_unlock_irqrestore(&icom_lock, flags); +} + +static void start_processor(struct icom_port *icom_port) +{ + unsigned long temp; + unsigned long flags; + int port; + + spin_lock_irqsave(&icom_lock, flags); + + port = icom_port->port; + if (port == 0 || port == 1) + start_proc[port].global_control_reg = &icom_port->global_reg->control; + else + start_proc[port].global_control_reg = &icom_port->global_reg->control_2; + if (port < 4) { + temp = readl(start_proc[port].global_control_reg); + temp = + (temp & ~stop_proc[port].processor_id) | start_proc[port].processor_id; + writel(temp, start_proc[port].global_control_reg); + + /* write flush */ + readl(start_proc[port].global_control_reg); + } else { + dev_err(&icom_port->adapter->pci_dev->dev, + "Invalid port assignment\n"); + } + + spin_unlock_irqrestore(&icom_lock, flags); +} + +static void load_code(struct icom_port *icom_port) +{ + const struct firmware *fw; + char *iram_ptr; + int index; + int status = 0; + char *dram_ptr = (char *) icom_port->dram; + dma_addr_t temp_pci; + unsigned char *new_page = NULL; + unsigned char cable_id = NO_CABLE; + struct pci_dev *dev = icom_port->adapter->pci_dev; + + /* Clear out any pending interrupts */ + writew(0x3FFF, (void *) icom_port->int_reg); + + trace(icom_port, "CLEAR_INTERRUPTS", 0); + + /* Stop processor */ + stop_processor(icom_port); + + /* Zero out DRAM */ + memset_io(dram_ptr, 0, 512); + + /* Load Call Setup into Adapter */ + if (request_firmware(&fw, "icom_call_setup.bin", &dev->dev) < 0) { + dev_err(&dev->dev,"Unable to load icom_call_setup.bin firmware image\n"); + status = -1; + goto load_code_exit; + } + + if (fw->size > ICOM_DCE_IRAM_OFFSET) { + dev_err(&dev->dev, "Invalid firmware image for icom_call_setup.bin found.\n"); + release_firmware(fw); + status = -1; + goto load_code_exit; + } + + iram_ptr = (char *) icom_port->dram + ICOM_IRAM_OFFSET; + for (index = 0; index < fw->size; index++) + writeb(fw->data[index], &iram_ptr[index]); + + release_firmware(fw); + + /* Load Resident DCE portion of Adapter */ + if (request_firmware(&fw, "icom_res_dce.bin", &dev->dev) < 0) { + dev_err(&dev->dev,"Unable to load icom_res_dce.bin firmware image\n"); + status = -1; + goto load_code_exit; + } + + if (fw->size > ICOM_IRAM_SIZE) { + dev_err(&dev->dev, "Invalid firmware image for icom_res_dce.bin found.\n"); + release_firmware(fw); + status = -1; + goto load_code_exit; + } + + iram_ptr = (char *) icom_port->dram + ICOM_IRAM_OFFSET; + for (index = ICOM_DCE_IRAM_OFFSET; index < fw->size; index++) + writeb(fw->data[index], &iram_ptr[index]); + + release_firmware(fw); + + /* Set Hardware level */ + if ((icom_port->adapter->version | ADAPTER_V2) == ADAPTER_V2) + writeb(V2_HARDWARE, &(icom_port->dram->misc_flags)); + + /* Start the processor in Adapter */ + start_processor(icom_port); + + writeb((HDLC_PPP_PURE_ASYNC | HDLC_FF_FILL), + &(icom_port->dram->HDLCConfigReg)); + writeb(0x04, &(icom_port->dram->FlagFillIdleTimer)); /* 0.5 seconds */ + writeb(0x00, &(icom_port->dram->CmdReg)); + writeb(0x10, &(icom_port->dram->async_config3)); + writeb((ICOM_ACFG_DRIVE1 | ICOM_ACFG_NO_PARITY | ICOM_ACFG_8BPC | + ICOM_ACFG_1STOP_BIT), &(icom_port->dram->async_config2)); + + /*Set up data in icom DRAM to indicate where personality + *code is located and its length. + */ + new_page = pci_alloc_consistent(dev, 4096, &temp_pci); + + if (!new_page) { + dev_err(&dev->dev, "Can not allocate DMA buffer\n"); + status = -1; + goto load_code_exit; + } + + if (request_firmware(&fw, "icom_asc.bin", &dev->dev) < 0) { + dev_err(&dev->dev,"Unable to load icom_asc.bin firmware image\n"); + status = -1; + goto load_code_exit; + } + + if (fw->size > ICOM_DCE_IRAM_OFFSET) { + dev_err(&dev->dev, "Invalid firmware image for icom_asc.bin found.\n"); + release_firmware(fw); + status = -1; + goto load_code_exit; + } + + for (index = 0; index < fw->size; index++) + new_page[index] = fw->data[index]; + + release_firmware(fw); + + writeb((char) ((fw->size + 16)/16), &icom_port->dram->mac_length); + writel(temp_pci, &icom_port->dram->mac_load_addr); + + /*Setting the syncReg to 0x80 causes adapter to start downloading + the personality code into adapter instruction RAM. + Once code is loaded, it will begin executing and, based on + information provided above, will start DMAing data from + shared memory to adapter DRAM. + */ + /* the wait loop below verifies this write operation has been done + and processed + */ + writeb(START_DOWNLOAD, &icom_port->dram->sync); + + /* Wait max 1 Sec for data download and processor to start */ + for (index = 0; index < 10; index++) { + msleep(100); + if (readb(&icom_port->dram->misc_flags) & ICOM_HDW_ACTIVE) + break; + } + + if (index == 10) + status = -1; + + /* + * check Cable ID + */ + cable_id = readb(&icom_port->dram->cable_id); + + if (cable_id & ICOM_CABLE_ID_VALID) { + /* Get cable ID into the lower 4 bits (standard form) */ + cable_id = (cable_id & ICOM_CABLE_ID_MASK) >> 4; + icom_port->cable_id = cable_id; + } else { + dev_err(&dev->dev,"Invalid or no cable attached\n"); + icom_port->cable_id = NO_CABLE; + } + + load_code_exit: + + if (status != 0) { + /* Clear out any pending interrupts */ + writew(0x3FFF, (void *) icom_port->int_reg); + + /* Turn off port */ + writeb(ICOM_DISABLE, &(icom_port->dram->disable)); + + /* Stop processor */ + stop_processor(icom_port); + + dev_err(&icom_port->adapter->pci_dev->dev,"Port not opertional\n"); + } + + if (new_page != NULL) + pci_free_consistent(dev, 4096, new_page, temp_pci); +} + +static int startup(struct icom_port *icom_port) +{ + unsigned long temp; + unsigned char cable_id, raw_cable_id; + unsigned long flags; + int port; + + trace(icom_port, "STARTUP", 0); + + if (icom_port->dram == 0x00000000) { + /* should NEVER be zero */ + dev_err(&icom_port->adapter->pci_dev->dev, + "Unusable Port, port configuration missing\n"); + return -ENODEV; + } + + /* + * check Cable ID + */ + raw_cable_id = readb(&icom_port->dram->cable_id); + trace(icom_port, "CABLE_ID", raw_cable_id); + + /* Get cable ID into the lower 4 bits (standard form) */ + cable_id = (raw_cable_id & ICOM_CABLE_ID_MASK) >> 4; + + /* Check for valid Cable ID */ + if (!(raw_cable_id & ICOM_CABLE_ID_VALID) || + (cable_id != icom_port->cable_id)) { + + /* reload adapter code, pick up any potential changes in cable id */ + load_code(icom_port); + + /* still no sign of cable, error out */ + raw_cable_id = readb(&icom_port->dram->cable_id); + cable_id = (raw_cable_id & ICOM_CABLE_ID_MASK) >> 4; + if (!(raw_cable_id & ICOM_CABLE_ID_VALID) || + (icom_port->cable_id == NO_CABLE)) + return -EIO; + } + + /* + * Finally, clear and enable interrupts + */ + spin_lock_irqsave(&icom_lock, flags); + port = icom_port->port; + if (port == 0 || port == 1) + int_mask_tbl[port].global_int_mask = &icom_port->global_reg->int_mask; + else + int_mask_tbl[port].global_int_mask = &icom_port->global_reg->int_mask_2; + + if (port == 0 || port == 2) + writew(0x00FF,(void *) icom_port->int_reg); + else + writew(0x3F00,(void *) icom_port->int_reg); + if (port < 4) { + temp = readl(int_mask_tbl[port].global_int_mask); + writel(temp & ~int_mask_tbl[port].processor_id, int_mask_tbl[port].global_int_mask); + + /* write flush */ + readl(int_mask_tbl[port].global_int_mask); + } else { + dev_err(&icom_port->adapter->pci_dev->dev, + "Invalid port assignment\n"); + } + + spin_unlock_irqrestore(&icom_lock, flags); + return 0; +} + +static void shutdown(struct icom_port *icom_port) +{ + unsigned long temp; + unsigned char cmdReg; + unsigned long flags; + int port; + + spin_lock_irqsave(&icom_lock, flags); + trace(icom_port, "SHUTDOWN", 0); + + /* + * disable all interrupts + */ + port = icom_port->port; + if (port == 0 || port == 1) + int_mask_tbl[port].global_int_mask = &icom_port->global_reg->int_mask; + else + int_mask_tbl[port].global_int_mask = &icom_port->global_reg->int_mask_2; + + if (port < 4) { + temp = readl(int_mask_tbl[port].global_int_mask); + writel(temp | int_mask_tbl[port].processor_id, int_mask_tbl[port].global_int_mask); + + /* write flush */ + readl(int_mask_tbl[port].global_int_mask); + } else { + dev_err(&icom_port->adapter->pci_dev->dev, + "Invalid port assignment\n"); + } + spin_unlock_irqrestore(&icom_lock, flags); + + /* + * disable break condition + */ + cmdReg = readb(&icom_port->dram->CmdReg); + if ((cmdReg | CMD_SND_BREAK) == CMD_SND_BREAK) { + writeb(cmdReg & ~CMD_SND_BREAK, &icom_port->dram->CmdReg); + } +} + +static int icom_write(struct uart_port *port) +{ + unsigned long data_count; + unsigned char cmdReg; + unsigned long offset; + int temp_tail = port->info->xmit.tail; + + trace(ICOM_PORT, "WRITE", 0); + + if (cpu_to_le16(ICOM_PORT->statStg->xmit[0].flags) & + SA_FLAGS_READY_TO_XMIT) { + trace(ICOM_PORT, "WRITE_FULL", 0); + return 0; + } + + data_count = 0; + while ((port->info->xmit.head != temp_tail) && + (data_count <= XMIT_BUFF_SZ)) { + + ICOM_PORT->xmit_buf[data_count++] = + port->info->xmit.buf[temp_tail]; + + temp_tail++; + temp_tail &= (UART_XMIT_SIZE - 1); + } + + if (data_count) { + ICOM_PORT->statStg->xmit[0].flags = + cpu_to_le16(SA_FLAGS_READY_TO_XMIT); + ICOM_PORT->statStg->xmit[0].leLength = + cpu_to_le16(data_count); + offset = + (unsigned long) &ICOM_PORT->statStg->xmit[0] - + (unsigned long) ICOM_PORT->statStg; + *ICOM_PORT->xmitRestart = + cpu_to_le32(ICOM_PORT->statStg_pci + offset); + cmdReg = readb(&ICOM_PORT->dram->CmdReg); + writeb(cmdReg | CMD_XMIT_RCV_ENABLE, + &ICOM_PORT->dram->CmdReg); + writeb(START_XMIT, &ICOM_PORT->dram->StartXmitCmd); + trace(ICOM_PORT, "WRITE_START", data_count); + /* write flush */ + readb(&ICOM_PORT->dram->StartXmitCmd); + } + + return data_count; +} + +static inline void check_modem_status(struct icom_port *icom_port) +{ + static char old_status = 0; + char delta_status; + unsigned char status; + + spin_lock(&icom_port->uart_port.lock); + + /*modem input register */ + status = readb(&icom_port->dram->isr); + trace(icom_port, "CHECK_MODEM", status); + delta_status = status ^ old_status; + if (delta_status) { + if (delta_status & ICOM_RI) + icom_port->uart_port.icount.rng++; + if (delta_status & ICOM_DSR) + icom_port->uart_port.icount.dsr++; + if (delta_status & ICOM_DCD) + uart_handle_dcd_change(&icom_port->uart_port, + delta_status & ICOM_DCD); + if (delta_status & ICOM_CTS) + uart_handle_cts_change(&icom_port->uart_port, + delta_status & ICOM_CTS); + + wake_up_interruptible(&icom_port->uart_port.info-> + delta_msr_wait); + old_status = status; + } + spin_unlock(&icom_port->uart_port.lock); +} + +static void xmit_interrupt(u16 port_int_reg, struct icom_port *icom_port) +{ + unsigned short int count; + int i; + + if (port_int_reg & (INT_XMIT_COMPLETED)) { + trace(icom_port, "XMIT_COMPLETE", 0); + + /* clear buffer in use bit */ + icom_port->statStg->xmit[0].flags &= + cpu_to_le16(~SA_FLAGS_READY_TO_XMIT); + + count = (unsigned short int) + cpu_to_le16(icom_port->statStg->xmit[0].leLength); + icom_port->uart_port.icount.tx += count; + + for (i=0; iuart_port.info->xmit); i++) { + + icom_port->uart_port.info->xmit.tail++; + icom_port->uart_port.info->xmit.tail &= + (UART_XMIT_SIZE - 1); + } + + if (!icom_write(&icom_port->uart_port)) + /* activate write queue */ + uart_write_wakeup(&icom_port->uart_port); + } else + trace(icom_port, "XMIT_DISABLED", 0); +} + +static void recv_interrupt(u16 port_int_reg, struct icom_port *icom_port) +{ + short int count, rcv_buff; + struct tty_struct *tty = icom_port->uart_port.info->tty; + unsigned short int status; + struct uart_icount *icount; + unsigned long offset; + + trace(icom_port, "RCV_COMPLETE", 0); + rcv_buff = icom_port->next_rcv; + + status = cpu_to_le16(icom_port->statStg->rcv[rcv_buff].flags); + while (status & SA_FL_RCV_DONE) { + + trace(icom_port, "FID_STATUS", status); + count = cpu_to_le16(icom_port->statStg->rcv[rcv_buff].leLength); + + trace(icom_port, "RCV_COUNT", count); + if (count > (TTY_FLIPBUF_SIZE - tty->flip.count)) + count = TTY_FLIPBUF_SIZE - tty->flip.count; + + trace(icom_port, "REAL_COUNT", count); + + offset = + cpu_to_le32(icom_port->statStg->rcv[rcv_buff].leBuffer) - + icom_port->recv_buf_pci; + + memcpy(tty->flip.char_buf_ptr,(unsigned char *) + ((unsigned long)icom_port->recv_buf + offset), count); + + if (count > 0) { + tty->flip.count += count - 1; + tty->flip.char_buf_ptr += count - 1; + + memset(tty->flip.flag_buf_ptr, 0, count); + tty->flip.flag_buf_ptr += count - 1; + } + + icount = &icom_port->uart_port.icount; + icount->rx += count; + + /* Break detect logic */ + if ((status & SA_FLAGS_FRAME_ERROR) + && (tty->flip.char_buf_ptr[0] == 0x00)) { + status &= ~SA_FLAGS_FRAME_ERROR; + status |= SA_FLAGS_BREAK_DET; + trace(icom_port, "BREAK_DET", 0); + } + + if (status & + (SA_FLAGS_BREAK_DET | SA_FLAGS_PARITY_ERROR | + SA_FLAGS_FRAME_ERROR | SA_FLAGS_OVERRUN)) { + + if (status & SA_FLAGS_BREAK_DET) + icount->brk++; + if (status & SA_FLAGS_PARITY_ERROR) + icount->parity++; + if (status & SA_FLAGS_FRAME_ERROR) + icount->frame++; + if (status & SA_FLAGS_OVERRUN) + icount->overrun++; + + /* + * Now check to see if character should be + * ignored, and mask off conditions which + * should be ignored. + */ + if (status & icom_port->ignore_status_mask) { + trace(icom_port, "IGNORE_CHAR", 0); + goto ignore_char; + } + + status &= icom_port->read_status_mask; + + if (status & SA_FLAGS_BREAK_DET) { + *tty->flip.flag_buf_ptr = TTY_BREAK; + } else if (status & SA_FLAGS_PARITY_ERROR) { + trace(icom_port, "PARITY_ERROR", 0); + *tty->flip.flag_buf_ptr = TTY_PARITY; + } else if (status & SA_FLAGS_FRAME_ERROR) + *tty->flip.flag_buf_ptr = TTY_FRAME; + + if (status & SA_FLAGS_OVERRUN) { + /* + * Overrun is special, since it's + * reported immediately, and doesn't + * affect the current character + */ + if (tty->flip.count < TTY_FLIPBUF_SIZE) { + tty->flip.count++; + tty->flip.flag_buf_ptr++; + tty->flip.char_buf_ptr++; + *tty->flip.flag_buf_ptr = TTY_OVERRUN; + } + } + } + + tty->flip.flag_buf_ptr++; + tty->flip.char_buf_ptr++; + tty->flip.count++; + ignore_char: + icom_port->statStg->rcv[rcv_buff].flags = 0; + icom_port->statStg->rcv[rcv_buff].leLength = 0; + icom_port->statStg->rcv[rcv_buff].WorkingLength = + (unsigned short int) cpu_to_le16(RCV_BUFF_SZ); + + rcv_buff++; + if (rcv_buff == NUM_RBUFFS) + rcv_buff = 0; + + status = cpu_to_le16(icom_port->statStg->rcv[rcv_buff].flags); + } + icom_port->next_rcv = rcv_buff; + tty_flip_buffer_push(tty); +} + +static void process_interrupt(u16 port_int_reg, + struct icom_port *icom_port) +{ + + spin_lock(&icom_port->uart_port.lock); + trace(icom_port, "INTERRUPT", port_int_reg); + + if (port_int_reg & (INT_XMIT_COMPLETED | INT_XMIT_DISABLED)) + xmit_interrupt(port_int_reg, icom_port); + + if (port_int_reg & INT_RCV_COMPLETED) + recv_interrupt(port_int_reg, icom_port); + + spin_unlock(&icom_port->uart_port.lock); +} + +static irqreturn_t icom_interrupt(int irq, void *dev_id, + struct pt_regs *regs) +{ + unsigned long int_reg; + u32 adapter_interrupts; + u16 port_int_reg; + struct icom_adapter *icom_adapter; + struct icom_port *icom_port; + + /* find icom_port for this interrupt */ + icom_adapter = (struct icom_adapter *) dev_id; + + if ((icom_adapter->version | ADAPTER_V2) == ADAPTER_V2) { + int_reg = icom_adapter->base_addr + 0x8024; + + adapter_interrupts = readl((void *) int_reg); + + if (adapter_interrupts & 0x00003FFF) { + /* port 2 interrupt, NOTE: for all ADAPTER_V2, port 2 will be active */ + icom_port = &icom_adapter->port_info[2]; + port_int_reg = (u16) adapter_interrupts; + process_interrupt(port_int_reg, icom_port); + check_modem_status(icom_port); + } + if (adapter_interrupts & 0x3FFF0000) { + /* port 3 interrupt */ + icom_port = &icom_adapter->port_info[3]; + if (icom_port->status == ICOM_PORT_ACTIVE) { + port_int_reg = + (u16) (adapter_interrupts >> 16); + process_interrupt(port_int_reg, icom_port); + check_modem_status(icom_port); + } + } + + /* Clear out any pending interrupts */ + writel(adapter_interrupts, (void *) int_reg); + + int_reg = icom_adapter->base_addr + 0x8004; + } else { + int_reg = icom_adapter->base_addr + 0x4004; + } + + adapter_interrupts = readl((void *) int_reg); + + if (adapter_interrupts & 0x00003FFF) { + /* port 0 interrupt, NOTE: for all adapters, port 0 will be active */ + icom_port = &icom_adapter->port_info[0]; + port_int_reg = (u16) adapter_interrupts; + process_interrupt(port_int_reg, icom_port); + check_modem_status(icom_port); + } + if (adapter_interrupts & 0x3FFF0000) { + /* port 1 interrupt */ + icom_port = &icom_adapter->port_info[1]; + if (icom_port->status == ICOM_PORT_ACTIVE) { + port_int_reg = (u16) (adapter_interrupts >> 16); + process_interrupt(port_int_reg, icom_port); + check_modem_status(icom_port); + } + } + + /* Clear out any pending interrupts */ + writel(adapter_interrupts, (void *) int_reg); + + /* flush the write */ + adapter_interrupts = readl((void *) int_reg); + + return IRQ_HANDLED; +} + +/* + * ------------------------------------------------------------------ + * Begin serial-core API + * ------------------------------------------------------------------ + */ +static unsigned int icom_tx_empty(struct uart_port *port) +{ + int ret; + unsigned long flags; + + spin_lock_irqsave(&port->lock, flags); + if (cpu_to_le16(ICOM_PORT->statStg->xmit[0].flags) & + SA_FLAGS_READY_TO_XMIT) + ret = TIOCSER_TEMT; + else + ret = 0; + + spin_unlock_irqrestore(&port->lock, flags); + return ret; +} + +static void icom_set_mctrl(struct uart_port *port, unsigned int mctrl) +{ + unsigned char local_osr; + + trace(ICOM_PORT, "SET_MODEM", 0); + local_osr = readb(&ICOM_PORT->dram->osr); + + if (mctrl & TIOCM_RTS) { + trace(ICOM_PORT, "RAISE_RTS", 0); + local_osr |= ICOM_RTS; + } else { + trace(ICOM_PORT, "LOWER_RTS", 0); + local_osr &= ~ICOM_RTS; + } + + if (mctrl & TIOCM_DTR) { + trace(ICOM_PORT, "RAISE_DTR", 0); + local_osr |= ICOM_DTR; + } else { + trace(ICOM_PORT, "LOWER_DTR", 0); + local_osr &= ~ICOM_DTR; + } + + writeb(local_osr, &ICOM_PORT->dram->osr); +} + +static unsigned int icom_get_mctrl(struct uart_port *port) +{ + unsigned char status; + unsigned int result; + + trace(ICOM_PORT, "GET_MODEM", 0); + + status = readb(&ICOM_PORT->dram->isr); + + result = ((status & ICOM_DCD) ? TIOCM_CAR : 0) + | ((status & ICOM_RI) ? TIOCM_RNG : 0) + | ((status & ICOM_DSR) ? TIOCM_DSR : 0) + | ((status & ICOM_CTS) ? TIOCM_CTS : 0); + return result; +} + +static void icom_stop_tx(struct uart_port *port, unsigned int tty_stop) +{ + unsigned char cmdReg; + + if (tty_stop) { + trace(ICOM_PORT, "STOP", 0); + cmdReg = readb(&ICOM_PORT->dram->CmdReg); + writeb(cmdReg | CMD_HOLD_XMIT, &ICOM_PORT->dram->CmdReg); + } +} + +static void icom_start_tx(struct uart_port *port, unsigned int tty_start) +{ + unsigned char cmdReg; + + trace(ICOM_PORT, "START", 0); + cmdReg = readb(&ICOM_PORT->dram->CmdReg); + if ((cmdReg & CMD_HOLD_XMIT) == CMD_HOLD_XMIT) + writeb(cmdReg & ~CMD_HOLD_XMIT, + &ICOM_PORT->dram->CmdReg); + + icom_write(port); +} + +static void icom_send_xchar(struct uart_port *port, char ch) +{ + unsigned char xdata; + int index; + unsigned long flags; + + trace(ICOM_PORT, "SEND_XCHAR", ch); + + /* wait .1 sec to send char */ + for (index = 0; index < 10; index++) { + spin_lock_irqsave(&port->lock, flags); + xdata = readb(&ICOM_PORT->dram->xchar); + if (xdata == 0x00) { + trace(ICOM_PORT, "QUICK_WRITE", 0); + writeb(ch, &ICOM_PORT->dram->xchar); + + /* flush write operation */ + xdata = readb(&ICOM_PORT->dram->xchar); + spin_unlock_irqrestore(&port->lock, flags); + break; + } + spin_unlock_irqrestore(&port->lock, flags); + msleep(10); + } +} + +static void icom_stop_rx(struct uart_port *port) +{ + unsigned char cmdReg; + + cmdReg = readb(&ICOM_PORT->dram->CmdReg); + writeb(cmdReg & ~CMD_RCV_ENABLE, &ICOM_PORT->dram->CmdReg); +} + +static void icom_enable_ms(struct uart_port *port) +{ + /* no-op */ +} + +static void icom_break(struct uart_port *port, int break_state) +{ + unsigned char cmdReg; + unsigned long flags; + + spin_lock_irqsave(&port->lock, flags); + trace(ICOM_PORT, "BREAK", 0); + cmdReg = readb(&ICOM_PORT->dram->CmdReg); + if (break_state == -1) { + writeb(cmdReg | CMD_SND_BREAK, &ICOM_PORT->dram->CmdReg); + } else { + writeb(cmdReg & ~CMD_SND_BREAK, &ICOM_PORT->dram->CmdReg); + } + spin_unlock_irqrestore(&port->lock, flags); +} + +static int icom_open(struct uart_port *port) +{ + int retval; + + kobject_get(&ICOM_PORT->adapter->kobj); + retval = startup(ICOM_PORT); + + if (retval) { + kobject_put(&ICOM_PORT->adapter->kobj); + trace(ICOM_PORT, "STARTUP_ERROR", 0); + return retval; + } + + return 0; +} + +static void icom_close(struct uart_port *port) +{ + unsigned char cmdReg; + + trace(ICOM_PORT, "CLOSE", 0); + + /* stop receiver */ + cmdReg = readb(&ICOM_PORT->dram->CmdReg); + writeb(cmdReg & (unsigned char) ~CMD_RCV_ENABLE, + &ICOM_PORT->dram->CmdReg); + + shutdown(ICOM_PORT); + + kobject_put(&ICOM_PORT->adapter->kobj); +} + +static void icom_set_termios(struct uart_port *port, + struct termios *termios, + struct termios *old_termios) +{ + int baud; + unsigned cflag, iflag; + int bits; + char new_config2; + char new_config3 = 0; + char tmp_byte; + int index; + int rcv_buff, xmit_buff; + unsigned long offset; + unsigned long flags; + + spin_lock_irqsave(&port->lock, flags); + trace(ICOM_PORT, "CHANGE_SPEED", 0); + + cflag = termios->c_cflag; + iflag = termios->c_iflag; + + new_config2 = ICOM_ACFG_DRIVE1; + + /* byte size and parity */ + switch (cflag & CSIZE) { + case CS5: /* 5 bits/char */ + new_config2 |= ICOM_ACFG_5BPC; + bits = 7; + break; + case CS6: /* 6 bits/char */ + new_config2 |= ICOM_ACFG_6BPC; + bits = 8; + break; + case CS7: /* 7 bits/char */ + new_config2 |= ICOM_ACFG_7BPC; + bits = 9; + break; + case CS8: /* 8 bits/char */ + new_config2 |= ICOM_ACFG_8BPC; + bits = 10; + break; + default: + bits = 10; + break; + } + if (cflag & CSTOPB) { + /* 2 stop bits */ + new_config2 |= ICOM_ACFG_2STOP_BIT; + bits++; + } + if (cflag & PARENB) { + /* parity bit enabled */ + new_config2 |= ICOM_ACFG_PARITY_ENAB; + trace(ICOM_PORT, "PARENB", 0); + bits++; + } + if (cflag & PARODD) { + /* odd parity */ + new_config2 |= ICOM_ACFG_PARITY_ODD; + trace(ICOM_PORT, "PARODD", 0); + } + + /* Determine divisor based on baud rate */ + baud = uart_get_baud_rate(port, termios, old_termios, + icom_acfg_baud[0], + icom_acfg_baud[BAUD_TABLE_LIMIT]); + if (!baud) + baud = 9600; /* B0 transition handled in rs_set_termios */ + + for (index = 0; index < BAUD_TABLE_LIMIT; index++) { + if (icom_acfg_baud[index] == baud) { + new_config3 = index; + break; + } + } + + uart_update_timeout(port, cflag, baud); + + /* CTS flow control flag and modem status interrupts */ + tmp_byte = readb(&(ICOM_PORT->dram->HDLCConfigReg)); + if (cflag & CRTSCTS) + tmp_byte |= HDLC_HDW_FLOW; + else + tmp_byte &= ~HDLC_HDW_FLOW; + writeb(tmp_byte, &(ICOM_PORT->dram->HDLCConfigReg)); + + /* + * Set up parity check flag + */ + ICOM_PORT->read_status_mask = SA_FLAGS_OVERRUN | SA_FL_RCV_DONE; + if (iflag & INPCK) + ICOM_PORT->read_status_mask |= + SA_FLAGS_FRAME_ERROR | SA_FLAGS_PARITY_ERROR; + + if ((iflag & BRKINT) || (iflag & PARMRK)) + ICOM_PORT->read_status_mask |= SA_FLAGS_BREAK_DET; + + /* + * Characters to ignore + */ + ICOM_PORT->ignore_status_mask = 0; + if (iflag & IGNPAR) + ICOM_PORT->ignore_status_mask |= + SA_FLAGS_PARITY_ERROR | SA_FLAGS_FRAME_ERROR; + if (iflag & IGNBRK) { + ICOM_PORT->ignore_status_mask |= SA_FLAGS_BREAK_DET; + /* + * If we're ignore parity and break indicators, ignore + * overruns too. (For real raw support). + */ + if (iflag & IGNPAR) + ICOM_PORT->ignore_status_mask |= SA_FLAGS_OVERRUN; + } + + /* + * !!! ignore all characters if CREAD is not set + */ + if ((cflag & CREAD) == 0) + ICOM_PORT->ignore_status_mask |= SA_FL_RCV_DONE; + + /* Turn off Receiver to prepare for reset */ + writeb(CMD_RCV_DISABLE, &ICOM_PORT->dram->CmdReg); + + for (index = 0; index < 10; index++) { + if (readb(&ICOM_PORT->dram->PrevCmdReg) == 0x00) { + break; + } + } + + /* clear all current buffers of data */ + for (rcv_buff = 0; rcv_buff < NUM_RBUFFS; rcv_buff++) { + ICOM_PORT->statStg->rcv[rcv_buff].flags = 0; + ICOM_PORT->statStg->rcv[rcv_buff].leLength = 0; + ICOM_PORT->statStg->rcv[rcv_buff].WorkingLength = + (unsigned short int) cpu_to_le16(RCV_BUFF_SZ); + } + + for (xmit_buff = 0; xmit_buff < NUM_XBUFFS; xmit_buff++) { + ICOM_PORT->statStg->xmit[xmit_buff].flags = 0; + } + + /* activate changes and start xmit and receiver here */ + /* Enable the receiver */ + writeb(new_config3, &(ICOM_PORT->dram->async_config3)); + writeb(new_config2, &(ICOM_PORT->dram->async_config2)); + tmp_byte = readb(&(ICOM_PORT->dram->HDLCConfigReg)); + tmp_byte |= HDLC_PPP_PURE_ASYNC | HDLC_FF_FILL; + writeb(tmp_byte, &(ICOM_PORT->dram->HDLCConfigReg)); + writeb(0x04, &(ICOM_PORT->dram->FlagFillIdleTimer)); /* 0.5 seconds */ + writeb(0xFF, &(ICOM_PORT->dram->ier)); /* enable modem signal interrupts */ + + /* reset processor */ + writeb(CMD_RESTART, &ICOM_PORT->dram->CmdReg); + + for (index = 0; index < 10; index++) { + if (readb(&ICOM_PORT->dram->CmdReg) == 0x00) { + break; + } + } + + /* Enable Transmitter and Reciever */ + offset = + (unsigned long) &ICOM_PORT->statStg->rcv[0] - + (unsigned long) ICOM_PORT->statStg; + writel(ICOM_PORT->statStg_pci + offset, + &ICOM_PORT->dram->RcvStatusAddr); + ICOM_PORT->next_rcv = 0; + ICOM_PORT->put_length = 0; + *ICOM_PORT->xmitRestart = 0; + writel(ICOM_PORT->xmitRestart_pci, + &ICOM_PORT->dram->XmitStatusAddr); + trace(ICOM_PORT, "XR_ENAB", 0); + writeb(CMD_XMIT_RCV_ENABLE, &ICOM_PORT->dram->CmdReg); + + spin_unlock_irqrestore(&port->lock, flags); +} + +static const char *icom_type(struct uart_port *port) +{ + return "icom"; +} + +static void icom_release_port(struct uart_port *port) +{ +} + +static int icom_request_port(struct uart_port *port) +{ + return 0; +} + +static void icom_config_port(struct uart_port *port, int flags) +{ + port->type = PORT_ICOM; +} + +static struct uart_ops icom_ops = { + .tx_empty = icom_tx_empty, + .set_mctrl = icom_set_mctrl, + .get_mctrl = icom_get_mctrl, + .stop_tx = icom_stop_tx, + .start_tx = icom_start_tx, + .send_xchar = icom_send_xchar, + .stop_rx = icom_stop_rx, + .enable_ms = icom_enable_ms, + .break_ctl = icom_break, + .startup = icom_open, + .shutdown = icom_close, + .set_termios = icom_set_termios, + .type = icom_type, + .release_port = icom_release_port, + .request_port = icom_request_port, + .config_port = icom_config_port, +}; + +#define ICOM_CONSOLE NULL + +static struct uart_driver icom_uart_driver = { + .owner = THIS_MODULE, + .driver_name = ICOM_DRIVER_NAME, + .dev_name = "ttyA", + .major = ICOM_MAJOR, + .minor = ICOM_MINOR_START, + .nr = NR_PORTS, + .cons = ICOM_CONSOLE, +}; + +static int __devinit icom_init_ports(struct icom_adapter *icom_adapter) +{ + u32 subsystem_id = icom_adapter->subsystem_id; + int retval = 0; + int i; + struct icom_port *icom_port; + + if (icom_adapter->version == ADAPTER_V1) { + icom_adapter->numb_ports = 2; + + for (i = 0; i < 2; i++) { + icom_port = &icom_adapter->port_info[i]; + icom_port->port = i; + icom_port->status = ICOM_PORT_ACTIVE; + icom_port->imbed_modem = ICOM_UNKNOWN; + } + } else { + if (subsystem_id == PCI_DEVICE_ID_IBM_ICOM_FOUR_PORT_MODEL) { + icom_adapter->numb_ports = 4; + + for (i = 0; i < 4; i++) { + icom_port = &icom_adapter->port_info[i]; + + icom_port->port = i; + icom_port->status = ICOM_PORT_ACTIVE; + icom_port->imbed_modem = ICOM_IMBED_MODEM; + } + } else { + icom_adapter->numb_ports = 4; + + icom_adapter->port_info[0].port = 0; + icom_adapter->port_info[0].status = ICOM_PORT_ACTIVE; + + if (subsystem_id == + PCI_DEVICE_ID_IBM_ICOM_V2_ONE_PORT_RVX_ONE_PORT_MDM) { + icom_adapter->port_info[0].imbed_modem = ICOM_IMBED_MODEM; + } else { + icom_adapter->port_info[0].imbed_modem = ICOM_RVX; + } + + icom_adapter->port_info[1].status = ICOM_PORT_OFF; + + icom_adapter->port_info[2].port = 2; + icom_adapter->port_info[2].status = ICOM_PORT_ACTIVE; + icom_adapter->port_info[2].imbed_modem = ICOM_RVX; + icom_adapter->port_info[3].status = ICOM_PORT_OFF; + } + } + + return retval; +} + +static void icom_port_active(struct icom_port *icom_port, struct icom_adapter *icom_adapter, int port_num) +{ + if (icom_adapter->version == ADAPTER_V1) { + icom_port->global_reg = (struct icom_regs *) ((char *) + icom_adapter->base_addr + 0x4000); + icom_port->int_reg = (unsigned long) icom_adapter->base_addr + + 0x4004 + 2 - 2 * port_num; + } else { + icom_port->global_reg = (struct icom_regs *) ((char *) + icom_adapter->base_addr + 0x8000); + if (icom_port->port < 2) + icom_port->int_reg = (unsigned long) icom_adapter->base_addr + + 0x8004 + 2 - 2 * icom_port->port; + else + icom_port->int_reg = (unsigned long) icom_adapter->base_addr + + 0x8024 + 2 - 2 * (icom_port->port - 2); + } +} +static int __init icom_load_ports(struct icom_adapter *icom_adapter) +{ + struct icom_port *icom_port; + int port_num; + int retval; + + for (port_num = 0; port_num < icom_adapter->numb_ports; port_num++) { + + icom_port = &icom_adapter->port_info[port_num]; + + if (icom_port->status == ICOM_PORT_ACTIVE) { + icom_port_active(icom_port, icom_adapter, port_num); + icom_port->dram = (struct func_dram *) ((char *) + icom_adapter->base_addr + + 0x2000 * icom_port->port); + + icom_port->adapter = icom_adapter; + + /* get port memory */ + if ((retval = get_port_memory(icom_port)) != 0) { + dev_err(&icom_port->adapter->pci_dev->dev, + "Memory allocation for port FAILED\n"); + } + } + } + return 0; +} + +static int __devinit icom_alloc_adapter(struct icom_adapter + **icom_adapter_ref) +{ + int adapter_count = 0; + struct icom_adapter *icom_adapter; + struct icom_adapter *cur_adapter_entry; + struct list_head *tmp; + + icom_adapter = (struct icom_adapter *) + kmalloc(sizeof(struct icom_adapter), GFP_KERNEL); + + if (!icom_adapter) { + return -ENOMEM; + } + + memset(icom_adapter, 0, sizeof(struct icom_adapter)); + + list_for_each(tmp, &icom_adapter_head) { + cur_adapter_entry = + list_entry(tmp, struct icom_adapter, + icom_adapter_entry); + if (cur_adapter_entry->index != adapter_count) { + break; + } + adapter_count++; + } + + icom_adapter->index = adapter_count; + list_add_tail(&icom_adapter->icom_adapter_entry, tmp); + + *icom_adapter_ref = icom_adapter; + return 0; +} + +static void icom_free_adapter(struct icom_adapter *icom_adapter) +{ + list_del(&icom_adapter->icom_adapter_entry); + kfree(icom_adapter); +} + +static void icom_remove_adapter(struct icom_adapter *icom_adapter) +{ + struct icom_port *icom_port; + int index; + + for (index = 0; index < icom_adapter->numb_ports; index++) { + icom_port = &icom_adapter->port_info[index]; + + if (icom_port->status == ICOM_PORT_ACTIVE) { + dev_info(&icom_adapter->pci_dev->dev, + "Device removed\n"); + + uart_remove_one_port(&icom_uart_driver, + &icom_port->uart_port); + + /* be sure that DTR and RTS are dropped */ + writeb(0x00, &icom_port->dram->osr); + + /* Wait 0.1 Sec for simple Init to complete */ + msleep(100); + + /* Stop proccessor */ + stop_processor(icom_port); + + free_port_memory(icom_port); + } + } + + free_irq(icom_adapter->irq_number, (void *) icom_adapter); + iounmap((void *) icom_adapter->base_addr); + icom_free_adapter(icom_adapter); + pci_release_regions(icom_adapter->pci_dev); +} + +static void icom_kobj_release(struct kobject *kobj) +{ + struct icom_adapter *icom_adapter; + + icom_adapter = to_icom_adapter(kobj); + icom_remove_adapter(icom_adapter); +} + +static struct kobj_type icom_kobj_type = { + .release = icom_kobj_release, +}; + +static int __devinit icom_probe(struct pci_dev *dev, + const struct pci_device_id *ent) +{ + int index; + unsigned int command_reg; + int retval; + struct icom_adapter *icom_adapter; + struct icom_port *icom_port; + + retval = pci_enable_device(dev); + if (retval) { + dev_err(&dev->dev, "Device enable FAILED\n"); + return retval; + } + + if ( (retval = pci_request_regions(dev, "icom"))) { + dev_err(&dev->dev, "pci_request_region FAILED\n"); + pci_disable_device(dev); + return retval; + } + + pci_set_master(dev); + + if ( (retval = pci_read_config_dword(dev, PCI_COMMAND, &command_reg))) { + dev_err(&dev->dev, "PCI Config read FAILED\n"); + return retval; + } + + pci_write_config_dword(dev, PCI_COMMAND, + command_reg | PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER + | PCI_COMMAND_PARITY | PCI_COMMAND_SERR); + + if (ent->driver_data == ADAPTER_V1) { + pci_write_config_dword(dev, 0x44, 0x8300830A); + } else { + pci_write_config_dword(dev, 0x44, 0x42004200); + pci_write_config_dword(dev, 0x48, 0x42004200); + } + + + retval = icom_alloc_adapter(&icom_adapter); + if (retval) { + dev_err(&dev->dev, "icom_alloc_adapter FAILED\n"); + retval = -EIO; + goto probe_exit0; + } + + icom_adapter->base_addr_pci = pci_resource_start(dev, 0); + icom_adapter->irq_number = dev->irq; + icom_adapter->pci_dev = dev; + icom_adapter->version = ent->driver_data; + icom_adapter->subsystem_id = ent->subdevice; + + + retval = icom_init_ports(icom_adapter); + if (retval) { + dev_err(&dev->dev, "Port configuration failed\n"); + goto probe_exit1; + } + + icom_adapter->base_addr = + (unsigned long) ioremap(icom_adapter->base_addr_pci, + pci_resource_len(dev, 0)); + + if (!icom_adapter->base_addr) + goto probe_exit1; + + /* save off irq and request irq line */ + if ( (retval = request_irq(dev->irq, icom_interrupt, + SA_INTERRUPT | SA_SHIRQ, ICOM_DRIVER_NAME, + (void *) icom_adapter))) { + goto probe_exit2; + } + + retval = icom_load_ports(icom_adapter); + + for (index = 0; index < icom_adapter->numb_ports; index++) { + icom_port = &icom_adapter->port_info[index]; + + if (icom_port->status == ICOM_PORT_ACTIVE) { + icom_port->uart_port.irq = icom_port->adapter->irq_number; + icom_port->uart_port.type = PORT_ICOM; + icom_port->uart_port.iotype = UPIO_MEM; + icom_port->uart_port.membase = + (char *) icom_adapter->base_addr_pci; + icom_port->uart_port.fifosize = 16; + icom_port->uart_port.ops = &icom_ops; + icom_port->uart_port.line = + icom_port->port + icom_adapter->index * 4; + if (uart_add_one_port (&icom_uart_driver, &icom_port->uart_port)) { + icom_port->status = ICOM_PORT_OFF; + dev_err(&dev->dev, "Device add failed\n"); + } else + dev_info(&dev->dev, "Device added\n"); + } + } + + kobject_init(&icom_adapter->kobj); + icom_adapter->kobj.ktype = &icom_kobj_type; + return 0; + +probe_exit2: + iounmap((void *) icom_adapter->base_addr); +probe_exit1: + icom_free_adapter(icom_adapter); + +probe_exit0: + pci_release_regions(dev); + pci_disable_device(dev); + + return retval; + + +} + +static void __devexit icom_remove(struct pci_dev *dev) +{ + struct icom_adapter *icom_adapter; + struct list_head *tmp; + + list_for_each(tmp, &icom_adapter_head) { + icom_adapter = list_entry(tmp, struct icom_adapter, + icom_adapter_entry); + if (icom_adapter->pci_dev == dev) { + kobject_put(&icom_adapter->kobj); + return; + } + } + + dev_err(&dev->dev, "Unable to find device to remove\n"); +} + +static struct pci_driver icom_pci_driver = { + .name = ICOM_DRIVER_NAME, + .id_table = icom_pci_table, + .probe = icom_probe, + .remove = __devexit_p(icom_remove), +}; + +static int __init icom_init(void) +{ + int ret; + + spin_lock_init(&icom_lock); + icom_lock = (spinlock_t) SPIN_LOCK_UNLOCKED; + + ret = uart_register_driver(&icom_uart_driver); + if (ret) + return ret; + + ret = pci_register_driver(&icom_pci_driver); + + if (ret < 0) + uart_unregister_driver(&icom_uart_driver); + + return ret; +} + +static void __exit icom_exit(void) +{ + pci_unregister_driver(&icom_pci_driver); + uart_unregister_driver(&icom_uart_driver); +} + +module_init(icom_init); +module_exit(icom_exit); + +#ifdef ICOM_TRACE +static inline void trace(struct icom_port *icom_port, char *trace_pt, + unsigned long trace_data) +{ + dev_info(&icom_port->adapter->pci_dev->dev, ":%d:%s - %lx\n", + icom_port->port, trace_pt, trace_data); +} +#endif + +MODULE_AUTHOR("Michael Anderson "); +MODULE_DESCRIPTION("IBM iSeries Serial IOA driver"); +MODULE_SUPPORTED_DEVICE + ("IBM iSeries 2745, 2771, 2772, 2742, 2793 and 2805 Communications adapters"); +MODULE_LICENSE("GPL"); + diff --git a/drivers/serial/icom.h b/drivers/serial/icom.h new file mode 100644 index 000000000..479b52d36 --- /dev/null +++ b/drivers/serial/icom.h @@ -0,0 +1,290 @@ +/* + * icom.h + * + * Copyright (C) 2001 Michael Anderson, IBM Corporation + * + * Serial device driver include file. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the 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 + +#define BAUD_TABLE_LIMIT ((sizeof(icom_acfg_baud)/sizeof(int)) - 1) +static int icom_acfg_baud[] = { + 300, + 600, + 900, + 1200, + 1800, + 2400, + 3600, + 4800, + 7200, + 9600, + 14400, + 19200, + 28800, + 38400, + 57600, + 76800, + 115200, + 153600, + 230400, + 307200, + 460800, +}; + +struct icom_regs { + u32 control; /* Adapter Control Register */ + u32 interrupt; /* Adapter Interrupt Register */ + u32 int_mask; /* Adapter Interrupt Mask Reg */ + u32 int_pri; /* Adapter Interrupt Priority r */ + u32 int_reg_b; /* Adapter non-masked Interrupt */ + u32 resvd01; + u32 resvd02; + u32 resvd03; + u32 control_2; /* Adapter Control Register 2 */ + u32 interrupt_2; /* Adapter Interrupt Register 2 */ + u32 int_mask_2; /* Adapter Interrupt Mask 2 */ + u32 int_pri_2; /* Adapter Interrupt Prior 2 */ + u32 int_reg_2b; /* Adapter non-masked 2 */ +}; + +struct func_dram { + u32 reserved[108]; /* 0-1B0 reserved by personality code */ + u32 RcvStatusAddr; /* 1B0-1B3 Status Address for Next rcv */ + u8 RcvStnAddr; /* 1B4 Receive Station Addr */ + u8 IdleState; /* 1B5 Idle State */ + u8 IdleMonitor; /* 1B6 Idle Monitor */ + u8 FlagFillIdleTimer; /* 1B7 Flag Fill Idle Timer */ + u32 XmitStatusAddr; /* 1B8-1BB Transmit Status Address */ + u8 StartXmitCmd; /* 1BC Start Xmit Command */ + u8 HDLCConfigReg; /* 1BD Reserved */ + u8 CauseCode; /* 1BE Cause code for fatal error */ + u8 xchar; /* 1BF High priority send */ + u32 reserved3; /* 1C0-1C3 Reserved */ + u8 PrevCmdReg; /* 1C4 Reserved */ + u8 CmdReg; /* 1C5 Command Register */ + u8 async_config2; /* 1C6 Async Config Byte 2 */ + u8 async_config3; /* 1C7 Async Config Byte 3 */ + u8 dce_resvd[20]; /* 1C8-1DB DCE Rsvd */ + u8 dce_resvd21; /* 1DC DCE Rsvd (21st byte */ + u8 misc_flags; /* 1DD misc flags */ +#define V2_HARDWARE 0x40 +#define ICOM_HDW_ACTIVE 0x01 + u8 call_length; /* 1DE Phone #/CFI buff ln */ + u8 call_length2; /* 1DF Upper byte (unused) */ + u32 call_addr; /* 1E0-1E3 Phn #/CFI buff addr */ + u16 timer_value; /* 1E4-1E5 general timer value */ + u8 timer_command; /* 1E6 general timer cmd */ + u8 dce_command; /* 1E7 dce command reg */ + u8 dce_cmd_status; /* 1E8 dce command stat */ + u8 x21_r1_ioff; /* 1E9 dce ready counter */ + u8 x21_r0_ioff; /* 1EA dce not ready ctr */ + u8 x21_ralt_ioff; /* 1EB dce CNR counter */ + u8 x21_r1_ion; /* 1EC dce ready I on ctr */ + u8 rsvd_ier; /* 1ED Rsvd for IER (if ne */ + u8 ier; /* 1EE Interrupt Enable */ + u8 isr; /* 1EF Input Signal Reg */ + u8 osr; /* 1F0 Output Signal Reg */ + u8 reset; /* 1F1 Reset/Reload Reg */ + u8 disable; /* 1F2 Disable Reg */ + u8 sync; /* 1F3 Sync Reg */ + u8 error_stat; /* 1F4 Error Status */ + u8 cable_id; /* 1F5 Cable ID */ + u8 cs_length; /* 1F6 CS Load Length */ + u8 mac_length; /* 1F7 Mac Load Length */ + u32 cs_load_addr; /* 1F8-1FB Call Load PCI Addr */ + u32 mac_load_addr; /* 1FC-1FF Mac Load PCI Addr */ +}; + +/* + * adapter defines and structures + */ +#define ICOM_CONTROL_START_A 0x00000008 +#define ICOM_CONTROL_STOP_A 0x00000004 +#define ICOM_CONTROL_START_B 0x00000002 +#define ICOM_CONTROL_STOP_B 0x00000001 +#define ICOM_CONTROL_START_C 0x00000008 +#define ICOM_CONTROL_STOP_C 0x00000004 +#define ICOM_CONTROL_START_D 0x00000002 +#define ICOM_CONTROL_STOP_D 0x00000001 +#define ICOM_IRAM_OFFSET 0x1000 +#define ICOM_IRAM_SIZE 0x0C00 +#define ICOM_DCE_IRAM_OFFSET 0x0A00 +#define ICOM_CABLE_ID_VALID 0x01 +#define ICOM_CABLE_ID_MASK 0xF0 +#define ICOM_DISABLE 0x80 +#define CMD_XMIT_RCV_ENABLE 0xC0 +#define CMD_XMIT_ENABLE 0x40 +#define CMD_RCV_DISABLE 0x00 +#define CMD_RCV_ENABLE 0x80 +#define CMD_RESTART 0x01 +#define CMD_HOLD_XMIT 0x02 +#define CMD_SND_BREAK 0x04 +#define RS232_CABLE 0x06 +#define V24_CABLE 0x0E +#define V35_CABLE 0x0C +#define V36_CABLE 0x02 +#define NO_CABLE 0x00 +#define START_DOWNLOAD 0x80 +#define ICOM_INT_MASK_PRC_A 0x00003FFF +#define ICOM_INT_MASK_PRC_B 0x3FFF0000 +#define ICOM_INT_MASK_PRC_C 0x00003FFF +#define ICOM_INT_MASK_PRC_D 0x3FFF0000 +#define INT_RCV_COMPLETED 0x1000 +#define INT_XMIT_COMPLETED 0x2000 +#define INT_IDLE_DETECT 0x0800 +#define INT_RCV_DISABLED 0x0400 +#define INT_XMIT_DISABLED 0x0200 +#define INT_RCV_XMIT_SHUTDOWN 0x0100 +#define INT_FATAL_ERROR 0x0080 +#define INT_CABLE_PULL 0x0020 +#define INT_SIGNAL_CHANGE 0x0010 +#define HDLC_PPP_PURE_ASYNC 0x02 +#define HDLC_FF_FILL 0x00 +#define HDLC_HDW_FLOW 0x01 +#define START_XMIT 0x80 +#define ICOM_ACFG_DRIVE1 0x20 +#define ICOM_ACFG_NO_PARITY 0x00 +#define ICOM_ACFG_PARITY_ENAB 0x02 +#define ICOM_ACFG_PARITY_ODD 0x01 +#define ICOM_ACFG_8BPC 0x00 +#define ICOM_ACFG_7BPC 0x04 +#define ICOM_ACFG_6BPC 0x08 +#define ICOM_ACFG_5BPC 0x0C +#define ICOM_ACFG_1STOP_BIT 0x00 +#define ICOM_ACFG_2STOP_BIT 0x10 +#define ICOM_DTR 0x80 +#define ICOM_RTS 0x40 +#define ICOM_RI 0x08 +#define ICOM_DSR 0x80 +#define ICOM_DCD 0x20 +#define ICOM_CTS 0x40 + +#define NUM_XBUFFS 1 +#define NUM_RBUFFS 2 +#define RCV_BUFF_SZ 0x0200 +#define XMIT_BUFF_SZ 0x1000 +struct statusArea { + /**********************************************/ + /* Transmit Status Area */ + /**********************************************/ + struct xmit_status_area{ + u32 leNext; /* Next entry in Little Endian on Adapter */ + u32 leNextASD; + u32 leBuffer; /* Buffer for entry in LE for Adapter */ + u16 leLengthASD; + u16 leOffsetASD; + u16 leLength; /* Length of data in segment */ + u16 flags; +#define SA_FLAGS_DONE 0x0080 /* Done with Segment */ +#define SA_FLAGS_CONTINUED 0x8000 /* More Segments */ +#define SA_FLAGS_IDLE 0x4000 /* Mark IDLE after frm */ +#define SA_FLAGS_READY_TO_XMIT 0x0800 +#define SA_FLAGS_STAT_MASK 0x007F + } xmit[NUM_XBUFFS]; + + /**********************************************/ + /* Receive Status Area */ + /**********************************************/ + struct { + u32 leNext; /* Next entry in Little Endian on Adapter */ + u32 leNextASD; + u32 leBuffer; /* Buffer for entry in LE for Adapter */ + u16 WorkingLength; /* size of segment */ + u16 reserv01; + u16 leLength; /* Length of data in segment */ + u16 flags; +#define SA_FL_RCV_DONE 0x0010 /* Data ready */ +#define SA_FLAGS_OVERRUN 0x0040 +#define SA_FLAGS_PARITY_ERROR 0x0080 +#define SA_FLAGS_FRAME_ERROR 0x0001 +#define SA_FLAGS_FRAME_TRUNC 0x0002 +#define SA_FLAGS_BREAK_DET 0x0004 /* set conditionally by device driver, not hardware */ +#define SA_FLAGS_RCV_MASK 0xFFE6 + } rcv[NUM_RBUFFS]; +}; + +struct icom_adapter; + + +#define ICOM_MAJOR 243 +#define ICOM_MINOR_START 0 + +struct icom_port { + struct uart_port uart_port; + u8 imbed_modem; +#define ICOM_UNKNOWN 1 +#define ICOM_RVX 2 +#define ICOM_IMBED_MODEM 3 + unsigned char cable_id; + unsigned char read_status_mask; + unsigned char ignore_status_mask; + unsigned long int_reg; + struct icom_regs *global_reg; + struct func_dram *dram; + int port; + struct statusArea *statStg; + dma_addr_t statStg_pci; + u32 *xmitRestart; + dma_addr_t xmitRestart_pci; + unsigned char *xmit_buf; + dma_addr_t xmit_buf_pci; + unsigned char *recv_buf; + dma_addr_t recv_buf_pci; + int next_rcv; + int put_length; + int status; +#define ICOM_PORT_ACTIVE 1 /* Port exists. */ +#define ICOM_PORT_OFF 0 /* Port does not exist. */ + int load_in_progress; + struct icom_adapter *adapter; +}; + +struct icom_adapter { + unsigned long base_addr; + unsigned long base_addr_pci; + unsigned char irq_number; + struct pci_dev *pci_dev; + struct icom_port port_info[4]; + int index; + int version; +#define ADAPTER_V1 0x0001 +#define ADAPTER_V2 0x0002 + u32 subsystem_id; +#define FOUR_PORT_MODEL 0x0252 +#define V2_TWO_PORTS_RVX 0x021A +#define V2_ONE_PORT_RVX_ONE_PORT_IMBED_MDM 0x0251 + int numb_ports; + struct list_head icom_adapter_entry; + struct kobject kobj; +}; + +/* prototype */ +extern void iCom_sercons_init(void); + +struct lookup_proc_table { + u32 *global_control_reg; + unsigned long processor_id; +}; + +struct lookup_int_table { + u32 *global_int_mask; + unsigned long processor_id; +}; + +#define MSECS_TO_JIFFIES(ms) (((ms)*HZ+999)/1000) diff --git a/drivers/serial/mcfserial.c b/drivers/serial/mcfserial.c index dfed45204..7f1b9ee7e 100644 --- a/drivers/serial/mcfserial.c +++ b/drivers/serial/mcfserial.c @@ -424,11 +424,7 @@ static void mcfrs_offintr(void *private) tty = info->tty; if (!tty) return; - - if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && - tty->ldisc.write_wakeup) - (tty->ldisc.write_wakeup)(tty); - wake_up_interruptible(&tty->write_wait); + tty_wakeup(tty); } @@ -835,10 +831,7 @@ static void mcfrs_flush_buffer(struct tty_struct *tty) info->xmit_cnt = info->xmit_head = info->xmit_tail = 0; local_irq_restore(flags); - wake_up_interruptible(&tty->write_wait); - if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && - tty->ldisc.write_wakeup) - (tty->ldisc.write_wakeup)(tty); + tty_wakeup(tty); } /* @@ -1232,11 +1225,12 @@ static void mcfrs_close(struct tty_struct *tty, struct file * filp) shutdown(info); if (tty->driver->flush_buffer) tty->driver->flush_buffer(tty); - if (tty->ldisc.flush_buffer) - tty->ldisc.flush_buffer(tty); + tty_ldisc_flush(tty); + tty->closing = 0; info->event = 0; info->tty = 0; +#if 0 if (tty->ldisc.num != ldiscs[N_TTY].num) { if (tty->ldisc.close) (tty->ldisc.close)(tty); @@ -1245,6 +1239,7 @@ static void mcfrs_close(struct tty_struct *tty, struct file * filp) if (tty->ldisc.open) (tty->ldisc.open)(tty); } +#endif if (info->blocked_open) { if (info->close_delay) { current->state = TASK_INTERRUPTIBLE; diff --git a/drivers/serial/s3c2410.c b/drivers/serial/s3c2410.c index 22a02bd69..70d2f8333 100644 --- a/drivers/serial/s3c2410.c +++ b/drivers/serial/s3c2410.c @@ -26,8 +26,6 @@ #include #include -#include - #if 0 #include #define dbg(x...) llprintk(x) @@ -677,7 +675,7 @@ serial_s3c2410_get_options(struct uart_port *port, int *baud, default: case S3C2410_LCON_PNONE: - /* nothing */ + *parity = 'n'; } /* now calculate the baud rate */ diff --git a/drivers/serial/serial_cs.c b/drivers/serial/serial_cs.c index c3e6eb244..8557ed1ce 100644 --- a/drivers/serial/serial_cs.c +++ b/drivers/serial/serial_cs.c @@ -363,9 +363,10 @@ next_tuple(client_handle_t handle, tuple_t * tuple, cisparse_t * parse) /*====================================================================*/ -static int simple_config(dev_link_t * link) +static int simple_config(dev_link_t *link) { static ioaddr_t base[5] = { 0x3f8, 0x2f8, 0x3e8, 0x2e8, 0x0 }; + static int size_table[2] = { 8, 16 }; client_handle_t handle = link->handle; struct serial_info *info = link->priv; tuple_t tuple; @@ -374,6 +375,7 @@ static int simple_config(dev_link_t * link) cistpl_cftable_entry_t *cf = &parse.cftable_entry; config_info_t config; int i, j, try; + int s; /* If the card is already configured, look up the port and irq */ i = pcmcia_get_configuration_info(handle, &config); @@ -399,29 +401,30 @@ static int simple_config(dev_link_t * link) tuple.Attributes = 0; tuple.DesiredTuple = CISTPL_CFTABLE_ENTRY; /* Two tries: without IO aliases, then with aliases */ - for (try = 0; try < 2; try++) { - i = first_tuple(handle, &tuple, &parse); - while (i != CS_NO_MORE_ITEMS) { - if (i != CS_SUCCESS) - goto next_entry; - if (cf->vpp1.present & (1 << CISTPL_POWER_VNOM)) - link->conf.Vpp1 = link->conf.Vpp2 = - cf->vpp1.param[CISTPL_POWER_VNOM] / 10000; - if ((cf->io.nwin > 0) && (cf->io.win[0].len == 8) && - (cf->io.win[0].base != 0)) { - link->conf.ConfigIndex = cf->index; - link->io.BasePort1 = cf->io.win[0].base; - link->io.IOAddrLines = (try == 0) ? - 16 : cf->io.flags & CISTPL_IO_LINES_MASK; - i = pcmcia_request_io(link->handle, &link->io); - if (i == CS_SUCCESS) - goto found_port; + for (s = 0; s < 2; s++) { + for (try = 0; try < 2; try++) { + i = first_tuple(handle, &tuple, &parse); + while (i != CS_NO_MORE_ITEMS) { + if (i != CS_SUCCESS) + goto next_entry; + if (cf->vpp1.present & (1 << CISTPL_POWER_VNOM)) + link->conf.Vpp1 = link->conf.Vpp2 = + cf->vpp1.param[CISTPL_POWER_VNOM] / 10000; + if ((cf->io.nwin > 0) && (cf->io.win[0].len == size_table[s]) && + (cf->io.win[0].base != 0)) { + link->conf.ConfigIndex = cf->index; + link->io.BasePort1 = cf->io.win[0].base; + link->io.IOAddrLines = (try == 0) ? + 16 : cf->io.flags & CISTPL_IO_LINES_MASK; + i = pcmcia_request_io(link->handle, &link->io); + if (i == CS_SUCCESS) + goto found_port; + } +next_entry: + i = next_tuple(handle, &tuple, &parse); } - next_entry: - i = next_tuple(handle, &tuple, &parse); } } - /* Second pass: try to find an entry that isn't picky about its base address, then try to grab any standard serial port address, and finally try to get any free port. */ diff --git a/drivers/serial/sn_console.c b/drivers/serial/sn_console.c index 98b96c546..92e69e24d 100644 --- a/drivers/serial/sn_console.c +++ b/drivers/serial/sn_console.c @@ -50,8 +50,8 @@ #include #include +#include #include -#include #include /* number of characters we can transmit to the SAL console at a time */ @@ -70,16 +70,22 @@ /* To use dynamic numbers only and not use the assigned major and minor, * define the following.. */ -/* #define USE_DYNAMIC_MINOR 1 */ /* use dynamic minor number */ -#define USE_DYNAMIC_MINOR 0 /* Don't rely on misc_register dynamic minor */ + /* #define USE_DYNAMIC_MINOR 1 *//* use dynamic minor number */ +#define USE_DYNAMIC_MINOR 0 /* Don't rely on misc_register dynamic minor */ /* Device name we're using */ #define DEVICE_NAME "ttySG" -#define DEVICE_NAME_DYNAMIC "ttySG0" /* need full name for misc_register */ +#define DEVICE_NAME_DYNAMIC "ttySG0" /* need full name for misc_register */ /* The major/minor we are using, ignored for USE_DYNAMIC_MINOR */ #define DEVICE_MAJOR 204 #define DEVICE_MINOR 40 +#ifdef CONFIG_MAGIC_SYSRQ +static char sysrq_serial_str[] = "\eSYS"; +static char *sysrq_serial_ptr = sysrq_serial_str; +static unsigned long sysrq_requested; +#endif /* CONFIG_MAGIC_SYSRQ */ + /* * Port definition - this kinda drives it all */ @@ -100,9 +106,8 @@ struct sn_cons_port { static struct sn_cons_port sal_console_port; /* Only used if USE_DYNAMIC_MINOR is set to 1 */ -static struct miscdevice misc; /* used with misc_register for dynamic */ +static struct miscdevice misc; /* used with misc_register for dynamic */ -extern u64 master_node_bedrock_address; extern void early_sn_setup(void); #undef DEBUG @@ -118,9 +123,6 @@ static int snt_hw_puts_raw(const char *, int); static int snt_hw_puts_buffered(const char *, int); static int snt_poll_getc(void); static int snt_poll_input_pending(void); -static int snt_sim_puts(const char *, int); -static int snt_sim_getc(void); -static int snt_sim_input_pending(void); static int snt_intr_getc(void); static int snt_intr_input_pending(void); static void sn_transmit_chars(struct sn_cons_port *, int); @@ -134,14 +136,6 @@ static struct sn_sal_ops poll_ops = { .sal_input_pending = snt_poll_input_pending }; -/* A table for the simulator */ -static struct sn_sal_ops sim_ops = { - .sal_puts_raw = snt_sim_puts, - .sal_puts = snt_sim_puts, - .sal_getc = snt_sim_getc, - .sal_input_pending = snt_sim_input_pending -}; - /* A table for interrupts enabled */ static struct sn_sal_ops intr_ops = { .sal_puts_raw = snt_hw_puts_raw, @@ -162,15 +156,13 @@ static struct sn_sal_ops intr_ops = { * output is buffered and sent to the SAL asynchronously (either by * timer callback or by UART interrupt) */ - /* routines for running the console in polling mode */ /** * snt_poll_getc - Get a character from the console in polling mode * */ -static int -snt_poll_getc(void) +static int snt_poll_getc(void) { int ch; @@ -182,8 +174,7 @@ snt_poll_getc(void) * snt_poll_input_pending - Check if any input is waiting - polling mode. * */ -static int -snt_poll_input_pending(void) +static int snt_poll_input_pending(void) { int status, input; @@ -191,64 +182,13 @@ snt_poll_input_pending(void) return !status && input; } -/* routines for running the console on the simulator */ - -/** - * snt_sim_puts - send to the console, used in simulator mode - * @str: String to send - * @count: length of string - * - */ -static int -snt_sim_puts(const char *str, int count) -{ - int counter = count; - -#ifdef FLAG_DIRECT_CONSOLE_WRITES - /* This is an easy way to pre-pend the output to know whether the output - * was done via sal or directly */ - writeb('[', master_node_bedrock_address + (UART_TX << 3)); - writeb('+', master_node_bedrock_address + (UART_TX << 3)); - writeb(']', master_node_bedrock_address + (UART_TX << 3)); - writeb(' ', master_node_bedrock_address + (UART_TX << 3)); -#endif /* FLAG_DIRECT_CONSOLE_WRITES */ - while (counter > 0) { - writeb(*str, master_node_bedrock_address + (UART_TX << 3)); - counter--; - str++; - } - return count; -} - -/** - * snt_sim_getc - Get character from console in simulator mode - * - */ -static int -snt_sim_getc(void) -{ - return readb(master_node_bedrock_address + (UART_RX << 3)); -} - -/** - * snt_sim_input_pending - Check if there is input pending in simulator mode - * - */ -static int -snt_sim_input_pending(void) -{ - return readb(master_node_bedrock_address + - (UART_LSR << 3)) & UART_LSR_DR; -} - /* routines for an interrupt driven console (normal) */ /** * snt_intr_getc - Get a character from the console, interrupt mode * */ -static int -snt_intr_getc(void) +static int snt_intr_getc(void) { return ia64_sn_console_readc(); } @@ -257,8 +197,7 @@ snt_intr_getc(void) * snt_intr_input_pending - Check if input is pending, interrupt mode * */ -static int -snt_intr_input_pending(void) +static int snt_intr_input_pending(void) { return ia64_sn_console_intr_status() & SAL_CONSOLE_INTR_RECV; } @@ -271,8 +210,7 @@ snt_intr_input_pending(void) * @len: Length * */ -static int -snt_hw_puts_raw(const char *s, int len) +static int snt_hw_puts_raw(const char *s, int len) { /* this will call the PROM and not return until this is done */ return ia64_sn_console_putb(s, len); @@ -284,8 +222,7 @@ snt_hw_puts_raw(const char *s, int len) * @len: Length * */ -static int -snt_hw_puts_buffered(const char *s, int len) +static int snt_hw_puts_buffered(const char *s, int len) { /* queue data to the PROM */ return ia64_sn_console_xmit_chars((char *)s, len); @@ -303,8 +240,7 @@ snt_hw_puts_buffered(const char *s, int len) * @port: Port to operate with (we ignore since we only have one port) * */ -static const char * -snp_type(struct uart_port *port) +static const char *snp_type(struct uart_port *port) { return ("SGI SN L1"); } @@ -314,8 +250,7 @@ snp_type(struct uart_port *port) * @port: Port to operate on (we ignore since we only have one port) * */ -static unsigned int -snp_tx_empty(struct uart_port *port) +static unsigned int snp_tx_empty(struct uart_port *port) { return 1; } @@ -326,8 +261,7 @@ snp_tx_empty(struct uart_port *port) * @tty_stop: Set to 1 if called via uart_stop * */ -static void -snp_stop_tx(struct uart_port *port, unsigned int tty_stop) +static void snp_stop_tx(struct uart_port *port, unsigned int tty_stop) { } @@ -336,8 +270,7 @@ snp_stop_tx(struct uart_port *port, unsigned int tty_stop) * @port: Port to operate on - we ignore - no-op function * */ -static void -snp_release_port(struct uart_port *port) +static void snp_release_port(struct uart_port *port) { } @@ -346,8 +279,7 @@ snp_release_port(struct uart_port *port) * @port: Port to operate on - we ignore - no-op function * */ -static void -snp_enable_ms(struct uart_port *port) +static void snp_enable_ms(struct uart_port *port) { } @@ -356,8 +288,7 @@ snp_enable_ms(struct uart_port *port) * @port: Port to shut down - we ignore * */ -static void -snp_shutdown(struct uart_port *port) +static void snp_shutdown(struct uart_port *port) { } @@ -367,8 +298,7 @@ snp_shutdown(struct uart_port *port) * @mctrl: Lines to set/unset - we ignore * */ -static void -snp_set_mctrl(struct uart_port *port, unsigned int mctrl) +static void snp_set_mctrl(struct uart_port *port, unsigned int mctrl) { } @@ -377,8 +307,7 @@ snp_set_mctrl(struct uart_port *port, unsigned int mctrl) * @port: port to operate on - we only have one port so we ignore this * */ -static unsigned int -snp_get_mctrl(struct uart_port *port) +static unsigned int snp_get_mctrl(struct uart_port *port) { return TIOCM_CAR | TIOCM_RNG | TIOCM_DSR | TIOCM_CTS; } @@ -388,8 +317,7 @@ snp_get_mctrl(struct uart_port *port) * @port: Port to operate on - we ignore * */ -static void -snp_stop_rx(struct uart_port *port) +static void snp_stop_rx(struct uart_port *port) { } @@ -399,11 +327,11 @@ snp_stop_rx(struct uart_port *port) * @tty_stop: Set to 1 if called via uart_start * */ -static void -snp_start_tx(struct uart_port *port, unsigned int tty_stop) +static void snp_start_tx(struct uart_port *port, unsigned int tty_stop) { if (sal_console_port.sc_ops->sal_wakeup_transmit) - sal_console_port.sc_ops->sal_wakeup_transmit(&sal_console_port, TRANSMIT_BUFFERED); + sal_console_port.sc_ops->sal_wakeup_transmit(&sal_console_port, + TRANSMIT_BUFFERED); } @@ -413,8 +341,7 @@ snp_start_tx(struct uart_port *port, unsigned int tty_stop) * @break_state: Break state * */ -static void -snp_break_ctl(struct uart_port *port, int break_state) +static void snp_break_ctl(struct uart_port *port, int break_state) { } @@ -423,8 +350,7 @@ snp_break_ctl(struct uart_port *port, int break_state) * @port: Port to operate on * */ -static int -snp_startup(struct uart_port *port) +static int snp_startup(struct uart_port *port) { return 0; } @@ -447,8 +373,7 @@ snp_set_termios(struct uart_port *port, struct termios *termios, * @port: port to operate on * */ -static int -snp_request_port(struct uart_port *port) +static int snp_request_port(struct uart_port *port) { return 0; } @@ -459,8 +384,7 @@ snp_request_port(struct uart_port *port) * @flags: flags used for port setup * */ -static void -snp_config_port(struct uart_port *port, int flags) +static void snp_config_port(struct uart_port *port, int flags) { } @@ -498,22 +422,17 @@ static struct uart_ops sn_console_ops = { * itself may be broken. * */ -static int -sn_debug_printf(const char *fmt, ...) +static int sn_debug_printf(const char *fmt, ...) { static char printk_buf[1024]; int printed_len; va_list args; va_start(args, fmt); - printed_len = vsnprintf(printk_buf, sizeof (printk_buf), fmt, args); + printed_len = vsnprintf(printk_buf, sizeof(printk_buf), fmt, args); if (!sal_console_port.sc_ops) { - if (IS_RUNNING_ON_SIMULATOR()) - sal_console_port.sc_ops = &sim_ops; - else - sal_console_port.sc_ops = &poll_ops; - + sal_console_port.sc_ops = &poll_ops; early_sn_setup(); } sal_console_port.sc_ops->sal_puts_raw(printk_buf, printed_len); @@ -521,24 +440,25 @@ sn_debug_printf(const char *fmt, ...) va_end(args); return printed_len; } -#endif /* DEBUG */ +#endif /* DEBUG */ /* * Interrupt handling routines. */ - /** * sn_receive_chars - Grab characters, pass them to tty layer * @port: Port to operate on * @regs: Saved registers (needed by uart_handle_sysrq_char) + * @flags: irq flags * * Note: If we're not registered with the serial core infrastructure yet, * we don't try to send characters to it... * */ static void -sn_receive_chars(struct sn_cons_port *port, struct pt_regs *regs) +sn_receive_chars(struct sn_cons_port *port, struct pt_regs *regs, + unsigned long flags) { int ch; struct tty_struct *tty; @@ -569,10 +489,34 @@ sn_receive_chars(struct sn_cons_port *port, struct pt_regs *regs) "obtaining data from the console (0x%0x)\n", ch); break; } -#if defined(CONFIG_SERIAL_SGI_L1_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ) - if (uart_handle_sysrq_char(&port->sc_port, ch, regs)) - continue; -#endif /* CONFIG_SERIAL_SGI_L1_CONSOLE && CONFIG_MAGIC_SYSRQ */ +#ifdef CONFIG_MAGIC_SYSRQ + if (sysrq_requested) { + unsigned long sysrq_timeout = sysrq_requested + HZ*5; + + sysrq_requested = 0; + if (ch && time_before(jiffies, sysrq_timeout)) { + spin_unlock_irqrestore(&port->sc_port.lock, flags); + handle_sysrq(ch, regs, NULL); + spin_lock_irqsave(&port->sc_port.lock, flags); + /* ignore actual sysrq command char */ + continue; + } + } + if (ch == *sysrq_serial_ptr) { + if (!(*++sysrq_serial_ptr)) { + sysrq_requested = jiffies; + sysrq_serial_ptr = sysrq_serial_str; + } + /* + * ignore the whole sysrq string except for the + * leading escape + */ + if (ch != '\e') + continue; + } + else + sysrq_serial_ptr = sysrq_serial_str; +#endif /* CONFIG_MAGIC_SYSRQ */ /* record the character to pass up to the tty layer */ if (tty) { @@ -583,8 +527,6 @@ sn_receive_chars(struct sn_cons_port *port, struct pt_regs *regs) if (tty->flip.count == TTY_FLIPBUF_SIZE) break; } - else { - } port->sc_port.icount.rx++; } @@ -604,8 +546,7 @@ sn_receive_chars(struct sn_cons_port *port, struct pt_regs *regs) * ignore them until we register with the serial core stuffs. * */ -static void -sn_transmit_chars(struct sn_cons_port *port, int raw) +static void sn_transmit_chars(struct sn_cons_port *port, int raw) { int xmit_count, tail, head, loops, ii; int result; @@ -620,8 +561,7 @@ sn_transmit_chars(struct sn_cons_port *port, int raw) if (port->sc_port.info) { /* We're initilized, using serial core infrastructure */ xmit = &port->sc_port.info->xmit; - } - else { + } else { /* Probably sn_sal_switch_to_asynch has been run but serial core isn't * initilized yet. Just return. Writes are going through * sn_sal_console_write (due to register_console) at this time. @@ -673,7 +613,7 @@ sn_transmit_chars(struct sn_cons_port *port, int raw) uart_write_wakeup(&port->sc_port); if (uart_circ_empty(xmit)) - snp_stop_tx(&port->sc_port, 0); /* no-op for us */ + snp_stop_tx(&port->sc_port, 0); /* no-op for us */ } /** @@ -683,10 +623,9 @@ sn_transmit_chars(struct sn_cons_port *port, int raw) * @regs: Saved registers, used by sn_receive_chars for uart_handle_sysrq_char * */ -static irqreturn_t -sn_sal_interrupt(int irq, void *dev_id, struct pt_regs *regs) +static irqreturn_t sn_sal_interrupt(int irq, void *dev_id, struct pt_regs *regs) { - struct sn_cons_port *port = (struct sn_cons_port *) dev_id; + struct sn_cons_port *port = (struct sn_cons_port *)dev_id; unsigned long flags; int status = ia64_sn_console_intr_status(); @@ -695,7 +634,7 @@ sn_sal_interrupt(int irq, void *dev_id, struct pt_regs *regs) spin_lock_irqsave(&port->sc_port.lock, flags); if (status & SAL_CONSOLE_INTR_RECV) { - sn_receive_chars(port, regs); + sn_receive_chars(port, regs, flags); } if (status & SAL_CONSOLE_INTR_XMIT) { sn_transmit_chars(port, TRANSMIT_BUFFERED); @@ -711,10 +650,10 @@ sn_sal_interrupt(int irq, void *dev_id, struct pt_regs *regs) * returns the console irq if interrupt is successfully registered, else 0 * */ -static int -sn_sal_connect_interrupt(struct sn_cons_port *port) +static int sn_sal_connect_interrupt(struct sn_cons_port *port) { - if (request_irq(SGI_UART_VECTOR, sn_sal_interrupt, SA_INTERRUPT, + if (request_irq(SGI_UART_VECTOR, sn_sal_interrupt, + SA_INTERRUPT | SA_SHIRQ, "SAL console driver", port) >= 0) { return SGI_UART_VECTOR; } @@ -732,10 +671,9 @@ sn_sal_connect_interrupt(struct sn_cons_port *port) * Obviously not used in interrupt mode * */ -static void -sn_sal_timer_poll(unsigned long data) +static void sn_sal_timer_poll(unsigned long data) { - struct sn_cons_port *port = (struct sn_cons_port *) data; + struct sn_cons_port *port = (struct sn_cons_port *)data; unsigned long flags; if (!port) @@ -743,7 +681,7 @@ sn_sal_timer_poll(unsigned long data) if (!port->sc_port.irq) { spin_lock_irqsave(&port->sc_port.lock, flags); - sn_receive_chars(port, NULL); + sn_receive_chars(port, NULL, flags); sn_transmit_chars(port, TRANSMIT_RAW); spin_unlock_irqrestore(&port->sc_port.lock, flags); mod_timer(&port->sc_timer, @@ -765,8 +703,7 @@ sn_sal_timer_poll(unsigned long data) * if we didn't already come through here via sn_sal_serial_console_init. * */ -static void __init -sn_sal_switch_to_asynch(struct sn_cons_port *port) +static void __init sn_sal_switch_to_asynch(struct sn_cons_port *port) { unsigned long flags; @@ -781,12 +718,8 @@ sn_sal_switch_to_asynch(struct sn_cons_port *port) spin_lock_irqsave(&port->sc_port.lock, flags); /* early_printk invocation may have done this for us */ - if (!port->sc_ops) { - if (IS_RUNNING_ON_SIMULATOR()) - port->sc_ops = &sim_ops; - else - port->sc_ops = &poll_ops; - } + if (!port->sc_ops) + port->sc_ops = &poll_ops; /* we can't turn on the console interrupt (as request_irq * calls kmalloc, which isn't set up yet), so we rely on a @@ -795,7 +728,7 @@ sn_sal_switch_to_asynch(struct sn_cons_port *port) */ init_timer(&port->sc_timer); port->sc_timer.function = sn_sal_timer_poll; - port->sc_timer.data = (unsigned long) port; + port->sc_timer.data = (unsigned long)port; if (IS_RUNNING_ON_SIMULATOR()) port->sc_interrupt_timeout = 6; @@ -822,8 +755,7 @@ sn_sal_switch_to_asynch(struct sn_cons_port *port) * We attempt to switch to interrupt mode here by calling * sn_sal_connect_interrupt. If that works out, we enable receive interrupts. */ -static void __init -sn_sal_switch_to_interrupts(struct sn_cons_port *port) +static void __init sn_sal_switch_to_interrupts(struct sn_cons_port *port) { int irq; unsigned long flags; @@ -851,7 +783,6 @@ sn_sal_switch_to_interrupts(struct sn_cons_port *port) * Kernel console definitions */ -#ifdef CONFIG_SERIAL_SGI_L1_CONSOLE static void sn_sal_console_write(struct console *, const char *, unsigned); static int __init sn_sal_console_setup(struct console *, char *); extern struct uart_driver sal_console_uart; @@ -862,22 +793,19 @@ static struct console sal_console = { .write = sn_sal_console_write, .device = uart_console_device, .setup = sn_sal_console_setup, - .index = -1, /* unspecified */ + .index = -1, /* unspecified */ .data = &sal_console_uart, }; #define SAL_CONSOLE &sal_console -#else -#define SAL_CONSOLE 0 -#endif /* CONFIG_SERIAL_SGI_L1_CONSOLE */ static struct uart_driver sal_console_uart = { .owner = THIS_MODULE, .driver_name = "sn_console", .dev_name = DEVICE_NAME, - .major = 0, /* major/minor set at registration time per USE_DYNAMIC_MINOR */ + .major = 0, /* major/minor set at registration time per USE_DYNAMIC_MINOR */ .minor = 0, - .nr = 1, /* one port */ + .nr = 1, /* one port */ .cons = SAL_CONSOLE, }; @@ -890,38 +818,38 @@ static struct uart_driver sal_console_uart = { * core and try to enable interrupt driven mode. * */ -static int __init -sn_sal_module_init(void) +static int __init sn_sal_module_init(void) { int retval; - printk(KERN_INFO "sn_console: Console driver init\n"); - if (!ia64_platform_is("sn2")) return -ENODEV; + printk(KERN_INFO "sn_console: Console driver init\n"); + if (USE_DYNAMIC_MINOR == 1) { misc.minor = MISC_DYNAMIC_MINOR; misc.name = DEVICE_NAME_DYNAMIC; retval = misc_register(&misc); if (retval != 0) { - printk("Failed to register console device using misc_register.\n"); + printk + ("Failed to register console device using misc_register.\n"); return -ENODEV; } sal_console_uart.major = MISC_MAJOR; sal_console_uart.minor = misc.minor; - } - else { + } else { sal_console_uart.major = DEVICE_MAJOR; sal_console_uart.minor = DEVICE_MINOR; } /* We register the driver and the port before switching to interrupts - * or async above so the proper uart structures are populated */ + * or async above so the proper uart structures are populated */ if (uart_register_driver(&sal_console_uart) < 0) { - printk("ERROR sn_sal_module_init failed uart_register_driver, line %d\n", - __LINE__); + printk + ("ERROR sn_sal_module_init failed uart_register_driver, line %d\n", + __LINE__); return -ENODEV; } @@ -957,8 +885,7 @@ sn_sal_module_init(void) * sn_sal_module_exit - When we're unloaded, remove the driver/port * */ -static void __exit -sn_sal_module_exit(void) +static void __exit sn_sal_module_exit(void) { del_timer_sync(&sal_console_port.sc_timer); uart_remove_one_port(&sal_console_uart, &sal_console_port.sc_port); @@ -969,8 +896,6 @@ sn_sal_module_exit(void) module_init(sn_sal_module_init); module_exit(sn_sal_module_exit); -#ifdef CONFIG_SERIAL_SGI_L1_CONSOLE - /** * puts_raw_fixed - sn_sal_console_write helper for adding \r's as required * @puts_raw : puts function to do the writing @@ -982,7 +907,8 @@ module_exit(sn_sal_module_exit); * */ -static void puts_raw_fixed(int (*puts_raw) (const char *s, int len), const char *s, int count) +static void puts_raw_fixed(int (*puts_raw) (const char *s, int len), + const char *s, int count) { const char *s1; @@ -1030,7 +956,7 @@ sn_sal_console_write(struct console *co, const char *s, unsigned count) if (port->sc_port.info) { /* somebody really wants this output, might be an - * oops, kdb, panic, etc. make sure they get it. */ + * oops, kdb, panic, etc. make sure they get it. */ #if defined(CONFIG_SMP) || defined(CONFIG_PREEMPT) if (spin_is_locked(&port->sc_port.lock)) { int lhead = port->sc_port.info->xmit.head; @@ -1038,29 +964,39 @@ sn_sal_console_write(struct console *co, const char *s, unsigned count) int counter, got_lock = 0; /* - * We attempt to determine if someone has died with the - * lock. We wait ~20 secs after the head and tail ptrs - * stop moving and assume the lock holder is not functional - * and plow ahead. If the lock is freed within the time out - * period we re-get the lock and go ahead normally. We also - * remember if we have plowed ahead so that we don't have + * We attempt to determine if someone has died with the + * lock. We wait ~20 secs after the head and tail ptrs + * stop moving and assume the lock holder is not functional + * and plow ahead. If the lock is freed within the time out + * period we re-get the lock and go ahead normally. We also + * remember if we have plowed ahead so that we don't have * to wait out the time out period again - the asumption - * is that we will time out again. - */ + * is that we will time out again. + */ for (counter = 0; counter < 150; mdelay(125), counter++) { - if (!spin_is_locked(&port->sc_port.lock) || stole_lock) { + if (!spin_is_locked(&port->sc_port.lock) + || stole_lock) { if (!stole_lock) { - spin_lock_irqsave(&port->sc_port.lock, flags); + spin_lock_irqsave(&port-> + sc_port.lock, + flags); got_lock = 1; } break; - } - else { + } else { /* still locked */ - if ((lhead != port->sc_port.info->xmit.head) || (ltail != port->sc_port.info->xmit.tail)) { - lhead = port->sc_port.info->xmit.head; - ltail = port->sc_port.info->xmit.tail; + if ((lhead != + port->sc_port.info->xmit.head) + || (ltail != + port->sc_port.info->xmit. + tail)) { + lhead = + port->sc_port.info->xmit. + head; + ltail = + port->sc_port.info->xmit. + tail; counter = 0; } } @@ -1068,16 +1004,15 @@ sn_sal_console_write(struct console *co, const char *s, unsigned count) /* flush anything in the serial core xmit buffer, raw */ sn_transmit_chars(port, 1); if (got_lock) { - spin_unlock_irqrestore(&port->sc_port.lock, flags); + spin_unlock_irqrestore(&port->sc_port.lock, + flags); stole_lock = 0; - } - else { + } else { /* fell thru */ stole_lock = 1; } puts_raw_fixed(port->sc_ops->sal_puts_raw, s, count); - } - else { + } else { stole_lock = 0; #endif spin_lock_irqsave(&port->sc_port.lock, flags); @@ -1085,7 +1020,9 @@ sn_sal_console_write(struct console *co, const char *s, unsigned count) spin_unlock_irqrestore(&port->sc_port.lock, flags); puts_raw_fixed(port->sc_ops->sal_puts_raw, s, count); +#if defined(CONFIG_SMP) || defined(CONFIG_PREEMPT) } +#endif } else { /* Not yet registered with serial core - simple case */ @@ -1106,8 +1043,7 @@ sn_sal_console_write(struct console *co, const char *s, unsigned count) * here so providing it is easier. * */ -static int __init -sn_sal_console_setup(struct console *co, char *options) +static int __init sn_sal_console_setup(struct console *co, char *options) { return 0; } @@ -1135,7 +1071,7 @@ static struct console sal_console_early __initdata = { .name = "sn_sal", .write = sn_sal_console_write_early, .flags = CON_PRINTBUFFER, - .index = -1, + .index = -1, }; /** @@ -1147,24 +1083,18 @@ static struct console sal_console_early __initdata = { * sn_sal_serial_console_init is called, this console is unregistered * and a new one registered. */ -int __init -sn_serial_console_early_setup(void) +int __init sn_serial_console_early_setup(void) { if (!ia64_platform_is("sn2")) return -1; - if (IS_RUNNING_ON_SIMULATOR()) - sal_console_port.sc_ops = &sim_ops; - else - sal_console_port.sc_ops = &poll_ops; - - early_sn_setup(); /* Find SAL entry points */ + sal_console_port.sc_ops = &poll_ops; + early_sn_setup(); /* Find SAL entry points */ register_console(&sal_console_early); return 0; } - /** * sn_sal_serial_console_init - Early console output - set up for register * @@ -1177,12 +1107,11 @@ sn_serial_console_early_setup(void) * it here doesn't hurt anything. * */ -static int __init -sn_sal_serial_console_init(void) +static int __init sn_sal_serial_console_init(void) { if (ia64_platform_is("sn2")) { sn_sal_switch_to_asynch(&sal_console_port); - DPRINTF ("sn_sal_serial_console_init : register console\n"); + DPRINTF("sn_sal_serial_console_init : register console\n"); register_console(&sal_console); unregister_console(&sal_console_early); } @@ -1190,5 +1119,3 @@ sn_sal_serial_console_init(void) } console_initcall(sn_sal_serial_console_init); - -#endif /* CONFIG_SERIAL_SGI_L1_CONSOLE */ diff --git a/drivers/usb/atm/Kconfig b/drivers/usb/atm/Kconfig new file mode 100644 index 000000000..0d9f5379b --- /dev/null +++ b/drivers/usb/atm/Kconfig @@ -0,0 +1,30 @@ +# +# USB ATM driver configuration +# +comment "USB ATM/DSL drivers" + depends on USB + +config USB_ATM + tristate "Generic USB ATM/DSL core I/O support" + depends on USB && ATM + select CRC32 + default n + help + This provides a library which is used for packet I/O by USB DSL + modems, such as the SpeedTouch driver below. + + To compile this driver as a module, choose M here: the + module will be called usb_atm. + +config USB_SPEEDTOUCH + tristate "Alcatel Speedtouch USB support" + depends on USB && ATM + select USB_ATM + help + Say Y here if you have an Alcatel SpeedTouch USB or SpeedTouch 330 + modem. In order to use your modem you will need to install the + two parts of the firmware, extracted by the user space tools; see + for details. + + To compile this driver as a module, choose M here: the + module will be called speedtch. diff --git a/drivers/usb/atm/Makefile b/drivers/usb/atm/Makefile new file mode 100644 index 000000000..9213b8b97 --- /dev/null +++ b/drivers/usb/atm/Makefile @@ -0,0 +1,7 @@ +# +# Makefile for the rest of the USB drivers +# (the ones that don't fit into any other categories) +# + +obj-$(CONFIG_USB_ATM) += usb_atm.o +obj-$(CONFIG_USB_SPEEDTOUCH) += speedtch.o diff --git a/drivers/usb/atm/speedtch.c b/drivers/usb/atm/speedtch.c new file mode 100644 index 000000000..c06a3c1b0 --- /dev/null +++ b/drivers/usb/atm/speedtch.c @@ -0,0 +1,863 @@ +/****************************************************************************** + * speedtch.c - Alcatel SpeedTouch USB xDSL modem driver + * + * Copyright (C) 2001, Alcatel + * Copyright (C) 2003, Duncan Sands + * Copyright (C) 2004, David Woodhouse + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "usb_atm.h" + +/* +#define DEBUG +#define VERBOSE_DEBUG +*/ + +#if !defined (DEBUG) && defined (CONFIG_USB_DEBUG) +# define DEBUG +#endif + +#include + +#if defined(CONFIG_FW_LOADER) || defined(CONFIG_FW_LOADER_MODULE) +# define USE_FW_LOADER +#endif + +#ifdef VERBOSE_DEBUG +static int udsl_print_packet(const unsigned char *data, int len); +#define PACKETDEBUG(arg...) udsl_print_packet (arg) +#define vdbg(arg...) dbg (arg) +#else +#define PACKETDEBUG(arg...) +#define vdbg(arg...) +#endif + +#define DRIVER_AUTHOR "Johan Verrept, Duncan Sands " +#define DRIVER_VERSION "1.8" +#define DRIVER_DESC "Alcatel SpeedTouch USB driver version " DRIVER_VERSION + +static const char speedtch_driver_name[] = "speedtch"; + +#define SPEEDTOUCH_VENDORID 0x06b9 +#define SPEEDTOUCH_PRODUCTID 0x4061 + +/* Timeout in jiffies */ +#define CTRL_TIMEOUT (2*HZ) +#define DATA_TIMEOUT (2*HZ) + +#define OFFSET_7 0 /* size 1 */ +#define OFFSET_b 1 /* size 8 */ +#define OFFSET_d 9 /* size 4 */ +#define OFFSET_e 13 /* size 1 */ +#define OFFSET_f 14 /* size 1 */ +#define TOTAL 15 + +#define SIZE_7 1 +#define SIZE_b 8 +#define SIZE_d 4 +#define SIZE_e 1 +#define SIZE_f 1 + +static int dl_512_first = 0; +static int sw_buffering = 0; + +module_param(dl_512_first, bool, 0444); +MODULE_PARM_DESC(dl_512_first, "Read 512 bytes before sending firmware"); + +module_param(sw_buffering, uint, 0444); +MODULE_PARM_DESC(sw_buffering, "Enable software buffering"); + +#define UDSL_IOCTL_LINE_UP 1 +#define UDSL_IOCTL_LINE_DOWN 2 + +#define SPEEDTCH_ENDPOINT_INT 0x81 +#define SPEEDTCH_ENDPOINT_DATA 0x07 +#define SPEEDTCH_ENDPOINT_FIRMWARE 0x05 + +#define hex2int(c) ( (c >= '0') && (c <= '9') ? (c - '0') : ((c & 0xf) + 9) ) + +static struct usb_device_id speedtch_usb_ids[] = { + {USB_DEVICE(SPEEDTOUCH_VENDORID, SPEEDTOUCH_PRODUCTID)}, + {} +}; + +MODULE_DEVICE_TABLE(usb, speedtch_usb_ids); + +struct speedtch_instance_data { + struct udsl_instance_data u; + + /* Status */ + struct urb *int_urb; + unsigned char int_data[16]; + struct work_struct poll_work; + struct timer_list poll_timer; +}; +/* USB */ + +static int speedtch_usb_probe(struct usb_interface *intf, + const struct usb_device_id *id); +static void speedtch_usb_disconnect(struct usb_interface *intf); +static int speedtch_usb_ioctl(struct usb_interface *intf, unsigned int code, + void *user_data); +static void speedtch_handle_int(struct urb *urb, struct pt_regs *regs); +static void speedtch_poll_status(struct speedtch_instance_data *instance); + +static struct usb_driver speedtch_usb_driver = { + .owner = THIS_MODULE, + .name = speedtch_driver_name, + .probe = speedtch_usb_probe, + .disconnect = speedtch_usb_disconnect, + .ioctl = speedtch_usb_ioctl, + .id_table = speedtch_usb_ids, +}; + +/*************** +** firmware ** +***************/ + +static void speedtch_got_firmware(struct speedtch_instance_data *instance, + int got_it) +{ + int err; + struct usb_interface *intf; + + down(&instance->u.serialize); /* vs self, speedtch_firmware_start */ + if (instance->u.status == UDSL_LOADED_FIRMWARE) + goto out; + if (!got_it) { + instance->u.status = UDSL_NO_FIRMWARE; + goto out; + } + if ((err = usb_set_interface(instance->u.usb_dev, 1, 1)) < 0) { + dbg("speedtch_got_firmware: usb_set_interface returned %d!", err); + instance->u.status = UDSL_NO_FIRMWARE; + goto out; + } + + /* Set up interrupt endpoint */ + intf = usb_ifnum_to_if(instance->u.usb_dev, 0); + if (intf && !usb_driver_claim_interface(&speedtch_usb_driver, intf, NULL)) { + + instance->int_urb = usb_alloc_urb(0, GFP_KERNEL); + if (instance->int_urb) { + + usb_fill_int_urb(instance->int_urb, instance->u.usb_dev, + usb_rcvintpipe(instance->u.usb_dev, SPEEDTCH_ENDPOINT_INT), + instance->int_data, + sizeof(instance->int_data), + speedtch_handle_int, instance, 50); + err = usb_submit_urb(instance->int_urb, GFP_KERNEL); + if (err) { + /* Doesn't matter; we'll poll anyway */ + dbg("speedtch_got_firmware: Submission of interrupt URB failed %d", err); + usb_free_urb(instance->int_urb); + instance->int_urb = NULL; + usb_driver_release_interface(&speedtch_usb_driver, intf); + } + } + } + /* Start status polling */ + mod_timer(&instance->poll_timer, jiffies + (1 * HZ)); + + instance->u.status = UDSL_LOADED_FIRMWARE; + tasklet_schedule(&instance->u.receive_tasklet); + out: + up(&instance->u.serialize); + wake_up_interruptible(&instance->u.firmware_waiters); +} + +static int speedtch_set_swbuff(struct speedtch_instance_data *instance, + int state) +{ + struct usb_device *dev = instance->u.usb_dev; + int ret; + + ret = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), + 0x32, 0x40, state ? 0x01 : 0x00, + 0x00, NULL, 0, 100); + if (ret < 0) { + printk("Warning: %sabling SW buffering: usb_control_msg returned %d\n", + state ? "En" : "Dis", ret); + return ret; + } + + dbg("speedtch_set_swbuff: %sbled SW buffering", state ? "En" : "Dis"); + return 0; +} + +static void speedtch_test_sequence(struct speedtch_instance_data *instance) +{ + struct usb_device *dev = instance->u.usb_dev; + unsigned char buf[10]; + int ret; + + /* URB 147 */ + buf[0] = 0x1c; + buf[1] = 0x50; + ret = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), + 0x01, 0x40, 0x0b, 0x00, buf, 2, 100); + if (ret < 0) + printk(KERN_WARNING "%s failed on URB147: %d\n", __func__, ret); + + /* URB 148 */ + buf[0] = 0x32; + buf[1] = 0x00; + ret = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), + 0x01, 0x40, 0x02, 0x00, buf, 2, 100); + if (ret < 0) + printk(KERN_WARNING "%s failed on URB148: %d\n", __func__, ret); + + /* URB 149 */ + buf[0] = 0x01; + buf[1] = 0x00; + buf[2] = 0x01; + ret = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), + 0x01, 0x40, 0x03, 0x00, buf, 3, 100); + if (ret < 0) + printk(KERN_WARNING "%s failed on URB149: %d\n", __func__, ret); + + /* URB 150 */ + buf[0] = 0x01; + buf[1] = 0x00; + buf[2] = 0x01; + ret = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), + 0x01, 0x40, 0x04, 0x00, buf, 3, 100); + if (ret < 0) + printk(KERN_WARNING "%s failed on URB150: %d\n", __func__, ret); +} + +static int speedtch_start_synchro(struct speedtch_instance_data *instance) +{ + struct usb_device *dev = instance->u.usb_dev; + unsigned char buf[2]; + int ret; + + ret = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), + 0x12, 0xc0, 0x04, 0x00, + buf, sizeof(buf), CTRL_TIMEOUT); + if (ret < 0) { + printk(KERN_WARNING "SpeedTouch: Failed to start ADSL synchronisation: %d\n", ret); + return ret; + } + + dbg("speedtch_start_synchro: modem prodded. %d Bytes returned: %02x %02x", ret, buf[0], buf[1]); + return 0; +} + +static void speedtch_handle_int(struct urb *urb, struct pt_regs *regs) +{ + struct speedtch_instance_data *instance = urb->context; + unsigned int count = urb->actual_length; + int ret; + + /* The magic interrupt for "up state" */ + const static unsigned char up_int[6] = { 0xa1, 0x00, 0x01, 0x00, 0x00, 0x00 }; + /* The magic interrupt for "down state" */ + const static unsigned char down_int[6] = { 0xa1, 0x00, 0x00, 0x00, 0x00, 0x00 }; + + switch (urb->status) { + case 0: + /* success */ + break; + case -ECONNRESET: + case -ENOENT: + case -ESHUTDOWN: + /* this urb is terminated; clean up */ + dbg("%s - urb shutting down with status: %d", __func__, urb->status); + return; + default: + dbg("%s - nonzero urb status received: %d", __func__, urb->status); + goto exit; + } + + if (count < 6) { + dbg("%s - int packet too short", __func__); + goto exit; + } + + if (!memcmp(up_int, instance->int_data, 6)) { + del_timer(&instance->poll_timer); + printk(KERN_NOTICE "DSL line goes up\n"); + } else if (!memcmp(down_int, instance->int_data, 6)) { + printk(KERN_NOTICE "DSL line goes down\n"); + } else { + int i; + + printk(KERN_DEBUG "Unknown interrupt packet of %d bytes:", count); + for (i = 0; i < count; i++) + printk(" %02x", instance->int_data[i]); + printk("\n"); + } + schedule_work(&instance->poll_work); + + exit: + rmb(); + if (!instance->int_urb) + return; + + ret = usb_submit_urb(urb, GFP_ATOMIC); + if (ret) + err("%s - usb_submit_urb failed with result %d", __func__, ret); +} + +static int speedtch_get_status(struct speedtch_instance_data *instance, + unsigned char *buf) +{ + struct usb_device *dev = instance->u.usb_dev; + int ret; + + memset(buf, 0, TOTAL); + + ret = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), + 0x12, 0xc0, 0x07, 0x00, buf + OFFSET_7, SIZE_7, + CTRL_TIMEOUT); + if (ret < 0) { + dbg("MSG 7 failed"); + return ret; + } + + ret = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), + 0x12, 0xc0, 0x0b, 0x00, buf + OFFSET_b, SIZE_b, + CTRL_TIMEOUT); + if (ret < 0) { + dbg("MSG B failed"); + return ret; + } + + ret = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), + 0x12, 0xc0, 0x0d, 0x00, buf + OFFSET_d, SIZE_d, + CTRL_TIMEOUT); + if (ret < 0) { + dbg("MSG D failed"); + return ret; + } + + ret = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), + 0x01, 0xc0, 0x0e, 0x00, buf + OFFSET_e, SIZE_e, + CTRL_TIMEOUT); + if (ret < 0) { + dbg("MSG E failed"); + return ret; + } + + ret = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), + 0x01, 0xc0, 0x0f, 0x00, buf + OFFSET_f, SIZE_f, + CTRL_TIMEOUT); + if (ret < 0) { + dbg("MSG F failed"); + return ret; + } + + return 0; +} + +static void speedtch_poll_status(struct speedtch_instance_data *instance) +{ + unsigned char buf[TOTAL]; + int ret; + + ret = speedtch_get_status(instance, buf); + if (ret) { + printk(KERN_WARNING + "SpeedTouch: Error %d fetching device status\n", ret); + return; + } + + dbg("Line state %02x", buf[OFFSET_7]); + + switch (buf[OFFSET_7]) { + case 0: + if (instance->u.atm_dev->signal != ATM_PHY_SIG_LOST) { + instance->u.atm_dev->signal = ATM_PHY_SIG_LOST; + printk(KERN_NOTICE "ADSL line is down\n"); + } + break; + + case 0x08: + if (instance->u.atm_dev->signal != ATM_PHY_SIG_UNKNOWN) { + instance->u.atm_dev->signal = ATM_PHY_SIG_UNKNOWN; + printk(KERN_NOTICE "ADSL line is blocked?\n"); + } + break; + + case 0x10: + if (instance->u.atm_dev->signal != ATM_PHY_SIG_LOST) { + instance->u.atm_dev->signal = ATM_PHY_SIG_LOST; + printk(KERN_NOTICE "ADSL line is synchronising\n"); + } + break; + + case 0x20: + if (instance->u.atm_dev->signal != ATM_PHY_SIG_FOUND) { + int down_speed = buf[OFFSET_b] | (buf[OFFSET_b + 1] << 8) + | (buf[OFFSET_b + 2] << 16) | (buf[OFFSET_b + 3] << 24); + int up_speed = buf[OFFSET_b + 4] | (buf[OFFSET_b + 5] << 8) + | (buf[OFFSET_b + 6] << 16) | (buf[OFFSET_b + 7] << 24); + + if (!(down_speed & 0x0000ffff) && + !(up_speed & 0x0000ffff)) { + down_speed >>= 16; + up_speed >>= 16; + } + instance->u.atm_dev->link_rate = down_speed * 1000 / 424; + instance->u.atm_dev->signal = ATM_PHY_SIG_FOUND; + + printk(KERN_NOTICE + "ADSL line is up (%d Kib/s down | %d Kib/s up)\n", + down_speed, up_speed); + } + break; + + default: + if (instance->u.atm_dev->signal != ATM_PHY_SIG_UNKNOWN) { + instance->u.atm_dev->signal = ATM_PHY_SIG_UNKNOWN; + printk(KERN_NOTICE "Unknown line state %02x\n", buf[OFFSET_7]); + } + break; + } +} + +static void speedtch_timer_poll(unsigned long data) +{ + struct speedtch_instance_data *instance = (void *)data; + + schedule_work(&instance->poll_work); + mod_timer(&instance->poll_timer, jiffies + (5 * HZ)); +} + +#ifdef USE_FW_LOADER +static void speedtch_upload_firmware(struct speedtch_instance_data *instance, + const struct firmware *fw1, + const struct firmware *fw2) +{ + unsigned char *buffer; + struct usb_device *usb_dev = instance->u.usb_dev; + struct usb_interface *intf; + int actual_length, ret; + int offset; + + dbg("speedtch_upload_firmware"); + + if (!(intf = usb_ifnum_to_if(usb_dev, 2))) { + dbg("speedtch_upload_firmware: interface not found!"); + goto fail; + } + + if (!(buffer = (unsigned char *)__get_free_page(GFP_KERNEL))) { + dbg("speedtch_upload_firmware: no memory for buffer!"); + goto fail; + } + + /* A user-space firmware loader may already have claimed interface #2 */ + if ((ret = + usb_driver_claim_interface(&speedtch_usb_driver, intf, NULL)) < 0) { + dbg("speedtch_upload_firmware: interface in use (%d)!", ret); + goto fail_free; + } + + /* URB 7 */ + if (dl_512_first) { /* some modems need a read before writing the firmware */ + ret = usb_bulk_msg(usb_dev, usb_rcvbulkpipe(usb_dev, SPEEDTCH_ENDPOINT_FIRMWARE), + buffer, 0x200, &actual_length, 2 * HZ); + + if (ret < 0 && ret != -ETIMEDOUT) + dbg("speedtch_upload_firmware: read BLOCK0 from modem failed (%d)!", ret); + else + dbg("speedtch_upload_firmware: BLOCK0 downloaded (%d bytes)", ret); + } + + /* URB 8 : both leds are static green */ + for (offset = 0; offset < fw1->size; offset += PAGE_SIZE) { + int thislen = min_t(int, PAGE_SIZE, fw1->size - offset); + memcpy(buffer, fw1->data + offset, thislen); + + ret = usb_bulk_msg(usb_dev, usb_sndbulkpipe(usb_dev, SPEEDTCH_ENDPOINT_FIRMWARE), + buffer, thislen, &actual_length, DATA_TIMEOUT); + + if (ret < 0) { + dbg("speedtch_upload_firmware: write BLOCK1 to modem failed (%d)!", ret); + goto fail_release; + } + dbg("speedtch_upload_firmware: BLOCK1 uploaded (%d bytes)", fw1->size); + } + + /* USB led blinking green, ADSL led off */ + + /* URB 11 */ + ret = usb_bulk_msg(usb_dev, usb_rcvbulkpipe(usb_dev, SPEEDTCH_ENDPOINT_FIRMWARE), + buffer, 0x200, &actual_length, DATA_TIMEOUT); + + if (ret < 0) { + dbg("speedtch_upload_firmware: read BLOCK2 from modem failed (%d)!", ret); + goto fail_release; + } + dbg("speedtch_upload_firmware: BLOCK2 downloaded (%d bytes)", actual_length); + + /* URBs 12 to 139 - USB led blinking green, ADSL led off */ + for (offset = 0; offset < fw2->size; offset += PAGE_SIZE) { + int thislen = min_t(int, PAGE_SIZE, fw2->size - offset); + memcpy(buffer, fw2->data + offset, thislen); + + ret = usb_bulk_msg(usb_dev, usb_sndbulkpipe(usb_dev, SPEEDTCH_ENDPOINT_FIRMWARE), + buffer, thislen, &actual_length, DATA_TIMEOUT); + + if (ret < 0) { + dbg("speedtch_upload_firmware: write BLOCK3 to modem failed (%d)!", ret); + goto fail_release; + } + } + dbg("speedtch_upload_firmware: BLOCK3 uploaded (%d bytes)", fw2->size); + + /* USB led static green, ADSL led static red */ + + /* URB 142 */ + ret = usb_bulk_msg(usb_dev, usb_rcvbulkpipe(usb_dev, SPEEDTCH_ENDPOINT_FIRMWARE), + buffer, 0x200, &actual_length, DATA_TIMEOUT); + + if (ret < 0) { + dbg("speedtch_upload_firmware: read BLOCK4 from modem failed (%d)!", ret); + goto fail_release; + } + + /* success */ + dbg("speedtch_upload_firmware: BLOCK4 downloaded (%d bytes)", actual_length); + + /* Delay to allow firmware to start up. We can do this here + because we're in our own kernel thread anyway. */ + msleep(1000); + + /* Enable software buffering, if requested */ + if (sw_buffering) + speedtch_set_swbuff(instance, 1); + + /* Magic spell; don't ask us what this does */ + speedtch_test_sequence(instance); + + /* Start modem synchronisation */ + if (speedtch_start_synchro(instance)) + dbg("speedtch_start_synchro: failed"); + + speedtch_got_firmware(instance, 1); + + free_page((unsigned long)buffer); + return; + + fail_release: + /* Only release interface #2 if uploading failed; we don't release it + we succeeded. This prevents the userspace tools from trying to load + the firmware themselves */ + usb_driver_release_interface(&speedtch_usb_driver, intf); + fail_free: + free_page((unsigned long)buffer); + fail: + speedtch_got_firmware(instance, 0); +} + +static int speedtch_find_firmware(struct speedtch_instance_data + *instance, int phase, + const struct firmware **fw_p) +{ + char buf[24]; + const u16 bcdDevice = instance->u.usb_dev->descriptor.bcdDevice; + const u8 major_revision = bcdDevice >> 8; + const u8 minor_revision = bcdDevice & 0xff; + + sprintf(buf, "speedtch-%d.bin.%x.%02x", phase, major_revision, minor_revision); + dbg("speedtch_find_firmware: looking for %s", buf); + + if (request_firmware(fw_p, buf, &instance->u.usb_dev->dev)) { + sprintf(buf, "speedtch-%d.bin.%x", phase, major_revision); + dbg("speedtch_find_firmware: looking for %s", buf); + + if (request_firmware(fw_p, buf, &instance->u.usb_dev->dev)) { + sprintf(buf, "speedtch-%d.bin", phase); + dbg("speedtch_find_firmware: looking for %s", buf); + + if (request_firmware(fw_p, buf, &instance->u.usb_dev->dev)) { + dev_warn(&instance->u.usb_dev->dev, "no stage %d firmware found!", phase); + return -ENOENT; + } + } + } + + dev_info(&instance->u.usb_dev->dev, "found stage %d firmware %s\n", phase, buf); + + return 0; +} + +static int speedtch_load_firmware(void *arg) +{ + const struct firmware *fw1, *fw2; + struct speedtch_instance_data *instance = arg; + + BUG_ON(!instance); + + daemonize("firmware/speedtch"); + + if (!speedtch_find_firmware(instance, 1, &fw1)) { + if (!speedtch_find_firmware(instance, 2, &fw2)) { + speedtch_upload_firmware(instance, fw1, fw2); + release_firmware(fw2); + } + release_firmware(fw1); + } + + /* In case we failed, set state back to NO_FIRMWARE so that + another later attempt may work. Otherwise, we never actually + manage to recover if, for example, the firmware is on /usr and + we look for it too early. */ + speedtch_got_firmware(instance, 0); + + module_put(THIS_MODULE); + udsl_put_instance(&instance->u); + return 0; +} +#endif /* USE_FW_LOADER */ + +static void speedtch_firmware_start(struct speedtch_instance_data *instance) +{ +#ifdef USE_FW_LOADER + int ret; +#endif + + dbg("speedtch_firmware_start"); + + down(&instance->u.serialize); /* vs self, speedtch_got_firmware */ + + if (instance->u.status >= UDSL_LOADING_FIRMWARE) { + up(&instance->u.serialize); + return; + } + + instance->u.status = UDSL_LOADING_FIRMWARE; + up(&instance->u.serialize); + +#ifdef USE_FW_LOADER + udsl_get_instance(&instance->u); + try_module_get(THIS_MODULE); + + ret = kernel_thread(speedtch_load_firmware, instance, + CLONE_FS | CLONE_FILES); + + if (ret >= 0) + return; /* OK */ + + dbg("speedtch_firmware_start: kernel_thread failed (%d)!", ret); + + module_put(THIS_MODULE); + udsl_put_instance(&instance->u); + /* Just pretend it never happened... hope modem_run happens */ +#endif /* USE_FW_LOADER */ + + speedtch_got_firmware(instance, 0); +} + +static int speedtch_firmware_wait(struct udsl_instance_data *instance) +{ + speedtch_firmware_start((void *)instance); + + if (wait_event_interruptible(instance->firmware_waiters, instance->status != UDSL_LOADING_FIRMWARE) < 0) + return -ERESTARTSYS; + + return (instance->status == UDSL_LOADED_FIRMWARE) ? 0 : -EAGAIN; +} + +/********** +** USB ** +**********/ + +static int speedtch_usb_ioctl(struct usb_interface *intf, unsigned int code, + void *user_data) +{ + struct speedtch_instance_data *instance = usb_get_intfdata(intf); + + dbg("speedtch_usb_ioctl entered"); + + if (!instance) { + dbg("speedtch_usb_ioctl: NULL instance!"); + return -ENODEV; + } + + switch (code) { + case UDSL_IOCTL_LINE_UP: + instance->u.atm_dev->signal = ATM_PHY_SIG_FOUND; + speedtch_got_firmware(instance, 1); + return (instance->u.status == UDSL_LOADED_FIRMWARE) ? 0 : -EIO; + case UDSL_IOCTL_LINE_DOWN: + instance->u.atm_dev->signal = ATM_PHY_SIG_LOST; + return 0; + default: + return -ENOTTY; + } +} + +static int speedtch_usb_probe(struct usb_interface *intf, + const struct usb_device_id *id) +{ + struct usb_device *dev = interface_to_usbdev(intf); + int ifnum = intf->altsetting->desc.bInterfaceNumber; + struct speedtch_instance_data *instance; + unsigned char mac_str[13]; + int ret, i; + char buf7[SIZE_7]; + + dbg("speedtch_usb_probe: trying device with vendor=0x%x, product=0x%x, ifnum %d", dev->descriptor.idVendor, dev->descriptor.idProduct, ifnum); + + if ((dev->descriptor.bDeviceClass != USB_CLASS_VENDOR_SPEC) || + (dev->descriptor.idVendor != SPEEDTOUCH_VENDORID) || + (dev->descriptor.idProduct != SPEEDTOUCH_PRODUCTID) || (ifnum != 1)) + return -ENODEV; + + dbg("speedtch_usb_probe: device accepted"); + + /* instance init */ + instance = kmalloc(sizeof(*instance), GFP_KERNEL); + if (!instance) { + dbg("speedtch_usb_probe: no memory for instance data!"); + return -ENOMEM; + } + + memset(instance, 0, sizeof(struct speedtch_instance_data)); + + if ((ret = usb_set_interface(dev, 0, 0)) < 0) + goto fail; + + if ((ret = usb_set_interface(dev, 2, 0)) < 0) + goto fail; + + instance->u.data_endpoint = SPEEDTCH_ENDPOINT_DATA; + instance->u.firmware_wait = speedtch_firmware_wait; + instance->u.driver_name = speedtch_driver_name; + + ret = udsl_instance_setup(dev, &instance->u); + if (ret) + goto fail; + + init_timer(&instance->poll_timer); + instance->poll_timer.function = speedtch_timer_poll; + instance->poll_timer.data = (unsigned long)instance; + + INIT_WORK(&instance->poll_work, (void *)speedtch_poll_status, instance); + + /* set MAC address, it is stored in the serial number */ + memset(instance->u.atm_dev->esi, 0, sizeof(instance->u.atm_dev->esi)); + if (usb_string(dev, dev->descriptor.iSerialNumber, mac_str, sizeof(mac_str)) == 12) { + for (i = 0; i < 6; i++) + instance->u.atm_dev->esi[i] = + (hex2int(mac_str[i * 2]) * 16) + (hex2int(mac_str[i * 2 + 1])); + } + + /* First check whether the modem already seems to be alive */ + ret = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), + 0x12, 0xc0, 0x07, 0x00, buf7, SIZE_7, HZ / 2); + + if (ret == SIZE_7) { + dbg("firmware appears to be already loaded"); + speedtch_got_firmware(instance, 1); + speedtch_poll_status(instance); + } else { + speedtch_firmware_start(instance); + } + + usb_set_intfdata(intf, instance); + + return 0; + + fail: + kfree(instance); + + return -ENOMEM; +} + +static void speedtch_usb_disconnect(struct usb_interface *intf) +{ + struct speedtch_instance_data *instance = usb_get_intfdata(intf); + + dbg("speedtch_usb_disconnect entered"); + + if (!instance) { + dbg("speedtch_usb_disconnect: NULL instance!"); + return; + } + + if (instance->int_urb) { + struct urb *int_urb = instance->int_urb; + instance->int_urb = NULL; + wmb(); + usb_unlink_urb(int_urb); + usb_free_urb(int_urb); + } + + instance->int_data[0] = 1; + del_timer_sync(&instance->poll_timer); + wmb(); + flush_scheduled_work(); + + udsl_instance_disconnect(&instance->u); + + /* clean up */ + usb_set_intfdata(intf, NULL); + udsl_put_instance(&instance->u); +} + +/*********** +** init ** +***********/ + +static int __init speedtch_usb_init(void) +{ + dbg("speedtch_usb_init: driver version " DRIVER_VERSION); + + return usb_register(&speedtch_usb_driver); +} + +static void __exit speedtch_usb_cleanup(void) +{ + dbg("speedtch_usb_cleanup entered"); + + usb_deregister(&speedtch_usb_driver); +} + +module_init(speedtch_usb_init); +module_exit(speedtch_usb_cleanup); + +MODULE_AUTHOR(DRIVER_AUTHOR); +MODULE_DESCRIPTION(DRIVER_DESC); +MODULE_LICENSE("GPL"); +MODULE_VERSION(DRIVER_VERSION); diff --git a/drivers/usb/atm/usb_atm.c b/drivers/usb/atm/usb_atm.c new file mode 100644 index 000000000..9180dda04 --- /dev/null +++ b/drivers/usb/atm/usb_atm.c @@ -0,0 +1,1201 @@ +/****************************************************************************** + * usb_atm.c - Generic USB xDSL driver core + * + * Copyright (C) 2001, Alcatel + * Copyright (C) 2003, Duncan Sands, SolNegro, Josep Comas + * Copyright (C) 2004, David Woodhouse + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the 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. + * + ******************************************************************************/ + +/* + * Written by Johan Verrept, maintained by Duncan Sands (duncan.sands@free.fr) + * + * 1.7+: - See the check-in logs + * + * 1.6: - No longer opens a connection if the firmware is not loaded + * - Added support for the speedtouch 330 + * - Removed the limit on the number of devices + * - Module now autoloads on device plugin + * - Merged relevant parts of sarlib + * - Replaced the kernel thread with a tasklet + * - New packet transmission code + * - Changed proc file contents + * - Fixed all known SMP races + * - Many fixes and cleanups + * - Various fixes by Oliver Neukum (oliver@neukum.name) + * + * 1.5A: - Version for inclusion in 2.5 series kernel + * - Modifications by Richard Purdie (rpurdie@rpsys.net) + * - made compatible with kernel 2.5.6 onwards by changing + * udsl_usb_send_data_context->urb to a pointer and adding code + * to alloc and free it + * - remove_wait_queue() added to udsl_atm_processqueue_thread() + * + * 1.5: - fixed memory leak when atmsar_decode_aal5 returned NULL. + * (reported by stephen.robinson@zen.co.uk) + * + * 1.4: - changed the spin_lock() under interrupt to spin_lock_irqsave() + * - unlink all active send urbs of a vcc that is being closed. + * + * 1.3.1: - added the version number + * + * 1.3: - Added multiple send urb support + * - fixed memory leak and vcc->tx_inuse starvation bug + * when not enough memory left in vcc. + * + * 1.2: - Fixed race condition in udsl_usb_send_data() + * 1.1: - Turned off packet debugging + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "usb_atm.h" + +/* +#define DEBUG +#define VERBOSE_DEBUG +*/ + +#if !defined (DEBUG) && defined (CONFIG_USB_DEBUG) +# define DEBUG +#endif + +#include + +#ifdef DEBUG +#define UDSL_ASSERT(x) BUG_ON(!(x)) +#else +#define UDSL_ASSERT(x) do { if (!(x)) warn("failed assertion '" #x "' at line %d", __LINE__); } while(0) +#endif + +#ifdef VERBOSE_DEBUG +static int udsl_print_packet(const unsigned char *data, int len); +#define PACKETDEBUG(arg...) udsl_print_packet (arg) +#define vdbg(arg...) dbg (arg) +#else +#define PACKETDEBUG(arg...) +#define vdbg(arg...) +#endif + +#define DRIVER_AUTHOR "Johan Verrept, Duncan Sands " +#define DRIVER_VERSION "1.8" +#define DRIVER_DESC "Alcatel SpeedTouch USB driver version " DRIVER_VERSION + +static unsigned int num_rcv_urbs = UDSL_DEFAULT_RCV_URBS; +static unsigned int num_snd_urbs = UDSL_DEFAULT_SND_URBS; +static unsigned int num_rcv_bufs = UDSL_DEFAULT_RCV_BUFS; +static unsigned int num_snd_bufs = UDSL_DEFAULT_SND_BUFS; +static unsigned int rcv_buf_size = UDSL_DEFAULT_RCV_BUF_SIZE; +static unsigned int snd_buf_size = UDSL_DEFAULT_SND_BUF_SIZE; + +module_param(num_rcv_urbs, uint, 0444); +MODULE_PARM_DESC(num_rcv_urbs, + "Number of urbs used for reception (range: 0-" + __MODULE_STRING(UDSL_MAX_RCV_URBS) ", default: " + __MODULE_STRING(UDSL_DEFAULT_RCV_URBS) ")"); + +module_param(num_snd_urbs, uint, 0444); +MODULE_PARM_DESC(num_snd_urbs, + "Number of urbs used for transmission (range: 0-" + __MODULE_STRING(UDSL_MAX_SND_URBS) ", default: " + __MODULE_STRING(UDSL_DEFAULT_SND_URBS) ")"); + +module_param(num_rcv_bufs, uint, 0444); +MODULE_PARM_DESC(num_rcv_bufs, + "Number of buffers used for reception (range: 0-" + __MODULE_STRING(UDSL_MAX_RCV_BUFS) ", default: " + __MODULE_STRING(UDSL_DEFAULT_RCV_BUFS) ")"); + +module_param(num_snd_bufs, uint, 0444); +MODULE_PARM_DESC(num_snd_bufs, + "Number of buffers used for transmission (range: 0-" + __MODULE_STRING(UDSL_MAX_SND_BUFS) ", default: " + __MODULE_STRING(UDSL_DEFAULT_SND_BUFS) ")"); + +module_param(rcv_buf_size, uint, 0444); +MODULE_PARM_DESC(rcv_buf_size, + "Size of the buffers used for reception (range: 0-" + __MODULE_STRING(UDSL_MAX_RCV_BUF_SIZE) ", default: " + __MODULE_STRING(UDSL_DEFAULT_RCV_BUF_SIZE) ")"); + +module_param(snd_buf_size, uint, 0444); +MODULE_PARM_DESC(snd_buf_size, + "Size of the buffers used for transmission (range: 0-" + __MODULE_STRING(UDSL_MAX_SND_BUF_SIZE) ", default: " + __MODULE_STRING(UDSL_DEFAULT_SND_BUF_SIZE) ")"); + +/* ATM */ + +static void udsl_atm_dev_close(struct atm_dev *dev); +static int udsl_atm_open(struct atm_vcc *vcc); +static void udsl_atm_close(struct atm_vcc *vcc); +static int udsl_atm_ioctl(struct atm_dev *dev, unsigned int cmd, void __user * arg); +static int udsl_atm_send(struct atm_vcc *vcc, struct sk_buff *skb); +static int udsl_atm_proc_read(struct atm_dev *atm_dev, loff_t * pos, char *page); + +static struct atmdev_ops udsl_atm_devops = { + .dev_close = udsl_atm_dev_close, + .open = udsl_atm_open, + .close = udsl_atm_close, + .ioctl = udsl_atm_ioctl, + .send = udsl_atm_send, + .proc_read = udsl_atm_proc_read, + .owner = THIS_MODULE, +}; + +/*********** +** misc ** +***********/ + +static inline void udsl_pop(struct atm_vcc *vcc, struct sk_buff *skb) +{ + if (vcc->pop) + vcc->pop(vcc, skb); + else + dev_kfree_skb(skb); +} + +/************* +** decode ** +*************/ + +static inline struct udsl_vcc_data *udsl_find_vcc(struct udsl_instance_data *instance, + short vpi, int vci) +{ + struct udsl_vcc_data *vcc; + + list_for_each_entry(vcc, &instance->vcc_list, list) + if ((vcc->vci == vci) && (vcc->vpi == vpi)) + return vcc; + return NULL; +} + +static void udsl_extract_cells(struct udsl_instance_data *instance, + unsigned char *source, unsigned int howmany) +{ + struct udsl_vcc_data *cached_vcc = NULL; + struct atm_vcc *vcc; + struct sk_buff *sarb; + struct udsl_vcc_data *vcc_data; + int cached_vci = 0; + unsigned int i; + int pti; + int vci; + short cached_vpi = 0; + short vpi; + + for (i = 0; i < howmany; + i++, source += ATM_CELL_SIZE + instance->rcv_padding) { + vpi = ((source[0] & 0x0f) << 4) | (source[1] >> 4); + vci = ((source[1] & 0x0f) << 12) | (source[2] << 4) | (source[3] >> 4); + pti = (source[3] & 0x2) != 0; + + vdbg("udsl_extract_cells: vpi %hd, vci %d, pti %d", vpi, vci, pti); + + if (cached_vcc && (vci == cached_vci) && (vpi == cached_vpi)) + vcc_data = cached_vcc; + else if ((vcc_data = udsl_find_vcc(instance, vpi, vci))) { + cached_vcc = vcc_data; + cached_vpi = vpi; + cached_vci = vci; + } else { + dbg("udsl_extract_cells: unknown vpi/vci (%hd/%d)!", vpi, vci); + continue; + } + + vcc = vcc_data->vcc; + sarb = vcc_data->sarb; + + if (sarb->tail + ATM_CELL_PAYLOAD > sarb->end) { + dbg("udsl_extract_cells: buffer overrun (sarb->len %u, vcc: 0x%p)!", sarb->len, vcc); + /* discard cells already received */ + skb_trim(sarb, 0); + } + + memcpy(sarb->tail, source + ATM_CELL_HEADER, ATM_CELL_PAYLOAD); + __skb_put(sarb, ATM_CELL_PAYLOAD); + + if (pti) { + struct sk_buff *skb; + unsigned int length; + unsigned int pdu_length; + + length = (source[ATM_CELL_SIZE - 6] << 8) + source[ATM_CELL_SIZE - 5]; + + /* guard against overflow */ + if (length > ATM_MAX_AAL5_PDU) { + dbg("udsl_extract_cells: bogus length %u (vcc: 0x%p)!", length, vcc); + atomic_inc(&vcc->stats->rx_err); + goto out; + } + + pdu_length = UDSL_NUM_CELLS(length) * ATM_CELL_PAYLOAD; + + if (sarb->len < pdu_length) { + dbg("udsl_extract_cells: bogus pdu_length %u (sarb->len: %u, vcc: 0x%p)!", pdu_length, sarb->len, vcc); + atomic_inc(&vcc->stats->rx_err); + goto out; + } + + if (crc32_be(~0, sarb->tail - pdu_length, pdu_length) != 0xc704dd7b) { + dbg("udsl_extract_cells: packet failed crc check (vcc: 0x%p)!", vcc); + atomic_inc(&vcc->stats->rx_err); + goto out; + } + + vdbg("udsl_extract_cells: got packet (length: %u, pdu_length: %u, vcc: 0x%p)", length, pdu_length, vcc); + + if (!(skb = dev_alloc_skb(length))) { + dbg("udsl_extract_cells: no memory for skb (length: %u)!", length); + atomic_inc(&vcc->stats->rx_drop); + goto out; + } + + vdbg("udsl_extract_cells: allocated new sk_buff (skb: 0x%p, skb->truesize: %u)", skb, skb->truesize); + + if (!atm_charge(vcc, skb->truesize)) { + dbg("udsl_extract_cells: failed atm_charge (skb->truesize: %u)!", skb->truesize); + dev_kfree_skb(skb); + goto out; /* atm_charge increments rx_drop */ + } + + memcpy(skb->data, sarb->tail - pdu_length, length); + __skb_put(skb, length); + + vdbg("udsl_extract_cells: sending skb 0x%p, skb->len %u, skb->truesize %u", skb, skb->len, skb->truesize); + + PACKETDEBUG(skb->data, skb->len); + + vcc->push(vcc, skb); + + atomic_inc(&vcc->stats->rx); + out: + skb_trim(sarb, 0); + } + } +} + +/************* +** encode ** +*************/ + +static const unsigned char zeros[ATM_CELL_PAYLOAD]; + +static void udsl_groom_skb(struct atm_vcc *vcc, struct sk_buff *skb) +{ + struct udsl_control *ctrl = UDSL_SKB(skb); + unsigned int zero_padding; + u32 crc; + + ctrl->atm_data.vcc = vcc; + ctrl->cell_header[0] = vcc->vpi >> 4; + ctrl->cell_header[1] = (vcc->vpi << 4) | (vcc->vci >> 12); + ctrl->cell_header[2] = vcc->vci >> 4; + ctrl->cell_header[3] = vcc->vci << 4; + ctrl->cell_header[4] = 0xec; + + ctrl->num_cells = UDSL_NUM_CELLS(skb->len); + ctrl->num_entire = skb->len / ATM_CELL_PAYLOAD; + + zero_padding = ctrl->num_cells * ATM_CELL_PAYLOAD - skb->len - ATM_AAL5_TRAILER; + + if (ctrl->num_entire + 1 < ctrl->num_cells) + ctrl->pdu_padding = zero_padding - (ATM_CELL_PAYLOAD - ATM_AAL5_TRAILER); + else + ctrl->pdu_padding = zero_padding; + + ctrl->aal5_trailer[0] = 0; /* UU = 0 */ + ctrl->aal5_trailer[1] = 0; /* CPI = 0 */ + ctrl->aal5_trailer[2] = skb->len >> 8; + ctrl->aal5_trailer[3] = skb->len; + + crc = crc32_be(~0, skb->data, skb->len); + crc = crc32_be(crc, zeros, zero_padding); + crc = crc32_be(crc, ctrl->aal5_trailer, 4); + crc = ~crc; + + ctrl->aal5_trailer[4] = crc >> 24; + ctrl->aal5_trailer[5] = crc >> 16; + ctrl->aal5_trailer[6] = crc >> 8; + ctrl->aal5_trailer[7] = crc; +} + +static unsigned int udsl_write_cells(struct udsl_instance_data *instance, + unsigned int howmany, struct sk_buff *skb, + unsigned char **target_p) +{ + struct udsl_control *ctrl = UDSL_SKB(skb); + unsigned char *target = *target_p; + unsigned int nc, ne, i; + + vdbg("udsl_write_cells: howmany=%u, skb->len=%d, num_cells=%u, num_entire=%u, pdu_padding=%u", howmany, skb->len, ctrl->num_cells, ctrl->num_entire, ctrl->pdu_padding); + + nc = ctrl->num_cells; + ne = min(howmany, ctrl->num_entire); + + for (i = 0; i < ne; i++) { + memcpy(target, ctrl->cell_header, ATM_CELL_HEADER); + target += ATM_CELL_HEADER; + memcpy(target, skb->data, ATM_CELL_PAYLOAD); + target += ATM_CELL_PAYLOAD; + if (instance->snd_padding) { + memset(target, 0, instance->snd_padding); + target += instance->snd_padding; + } + __skb_pull(skb, ATM_CELL_PAYLOAD); + } + + ctrl->num_entire -= ne; + + if (!(ctrl->num_cells -= ne) || !(howmany -= ne)) + goto out; + + if (instance->snd_padding) { + memset(target, 0, instance->snd_padding); + target += instance->snd_padding; + } + memcpy(target, ctrl->cell_header, ATM_CELL_HEADER); + target += ATM_CELL_HEADER; + memcpy(target, skb->data, skb->len); + target += skb->len; + __skb_pull(skb, skb->len); + memset(target, 0, ctrl->pdu_padding); + target += ctrl->pdu_padding; + + if (--ctrl->num_cells) { + if (!--howmany) { + ctrl->pdu_padding = ATM_CELL_PAYLOAD - ATM_AAL5_TRAILER; + goto out; + } + + memcpy(target, ctrl->cell_header, ATM_CELL_HEADER); + target += ATM_CELL_HEADER; + memset(target, 0, ATM_CELL_PAYLOAD - ATM_AAL5_TRAILER); + target += ATM_CELL_PAYLOAD - ATM_AAL5_TRAILER; + + --ctrl->num_cells; + UDSL_ASSERT(!ctrl->num_cells); + } + + memcpy(target, ctrl->aal5_trailer, ATM_AAL5_TRAILER); + target += ATM_AAL5_TRAILER; + /* set pti bit in last cell */ + *(target + 3 - ATM_CELL_SIZE) |= 0x2; + if (instance->snd_padding) { + memset(target, 0, instance->snd_padding); + target += instance->snd_padding; + } + out: + *target_p = target; + return nc - ctrl->num_cells; +} + +/************** +** receive ** +**************/ + +static void udsl_complete_receive(struct urb *urb, struct pt_regs *regs) +{ + struct udsl_receive_buffer *buf; + struct udsl_instance_data *instance; + struct udsl_receiver *rcv; + unsigned long flags; + + if (!urb || !(rcv = urb->context)) { + dbg("udsl_complete_receive: bad urb!"); + return; + } + + instance = rcv->instance; + buf = rcv->buffer; + + buf->filled_cells = urb->actual_length / (ATM_CELL_SIZE + instance->rcv_padding); + + vdbg("udsl_complete_receive: urb 0x%p, status %d, actual_length %d, filled_cells %u, rcv 0x%p, buf 0x%p", urb, urb->status, urb->actual_length, buf->filled_cells, rcv, buf); + + UDSL_ASSERT(buf->filled_cells <= rcv_buf_size); + + /* may not be in_interrupt() */ + spin_lock_irqsave(&instance->receive_lock, flags); + list_add(&rcv->list, &instance->spare_receivers); + list_add_tail(&buf->list, &instance->filled_receive_buffers); + if (likely(!urb->status)) + tasklet_schedule(&instance->receive_tasklet); + spin_unlock_irqrestore(&instance->receive_lock, flags); +} + +static void udsl_process_receive(unsigned long data) +{ + struct udsl_receive_buffer *buf; + struct udsl_instance_data *instance = (struct udsl_instance_data *)data; + struct udsl_receiver *rcv; + int err; + + made_progress: + while (!list_empty(&instance->spare_receive_buffers)) { + spin_lock_irq(&instance->receive_lock); + if (list_empty(&instance->spare_receivers)) { + spin_unlock_irq(&instance->receive_lock); + break; + } + rcv = list_entry(instance->spare_receivers.next, + struct udsl_receiver, list); + list_del(&rcv->list); + spin_unlock_irq(&instance->receive_lock); + + buf = list_entry(instance->spare_receive_buffers.next, + struct udsl_receive_buffer, list); + list_del(&buf->list); + + rcv->buffer = buf; + + usb_fill_bulk_urb(rcv->urb, instance->usb_dev, + usb_rcvbulkpipe(instance->usb_dev, instance->data_endpoint), + buf->base, + rcv_buf_size * (ATM_CELL_SIZE + instance->rcv_padding), + udsl_complete_receive, rcv); + + vdbg("udsl_process_receive: sending urb 0x%p, rcv 0x%p, buf 0x%p", + rcv->urb, rcv, buf); + + if ((err = usb_submit_urb(rcv->urb, GFP_ATOMIC)) < 0) { + dbg("udsl_process_receive: urb submission failed (%d)!", err); + list_add(&buf->list, &instance->spare_receive_buffers); + spin_lock_irq(&instance->receive_lock); + list_add(&rcv->list, &instance->spare_receivers); + spin_unlock_irq(&instance->receive_lock); + break; + } + } + + spin_lock_irq(&instance->receive_lock); + if (list_empty(&instance->filled_receive_buffers)) { + spin_unlock_irq(&instance->receive_lock); + return; /* done - no more buffers */ + } + buf = list_entry(instance->filled_receive_buffers.next, + struct udsl_receive_buffer, list); + list_del(&buf->list); + spin_unlock_irq(&instance->receive_lock); + + vdbg("udsl_process_receive: processing buf 0x%p", buf); + udsl_extract_cells(instance, buf->base, buf->filled_cells); + list_add(&buf->list, &instance->spare_receive_buffers); + goto made_progress; +} + +/*********** +** send ** +***********/ + +static void udsl_complete_send(struct urb *urb, struct pt_regs *regs) +{ + struct udsl_instance_data *instance; + struct udsl_sender *snd; + unsigned long flags; + + if (!urb || !(snd = urb->context) || !(instance = snd->instance)) { + dbg("udsl_complete_send: bad urb!"); + return; + } + + vdbg("udsl_complete_send: urb 0x%p, status %d, snd 0x%p, buf 0x%p", urb, + urb->status, snd, snd->buffer); + + /* may not be in_interrupt() */ + spin_lock_irqsave(&instance->send_lock, flags); + list_add(&snd->list, &instance->spare_senders); + list_add(&snd->buffer->list, &instance->spare_send_buffers); + tasklet_schedule(&instance->send_tasklet); + spin_unlock_irqrestore(&instance->send_lock, flags); +} + +static void udsl_process_send(unsigned long data) +{ + struct udsl_send_buffer *buf; + struct udsl_instance_data *instance = (struct udsl_instance_data *)data; + struct sk_buff *skb; + struct udsl_sender *snd; + int err; + unsigned int num_written; + + made_progress: + spin_lock_irq(&instance->send_lock); + while (!list_empty(&instance->spare_senders)) { + if (!list_empty(&instance->filled_send_buffers)) { + buf = list_entry(instance->filled_send_buffers.next, + struct udsl_send_buffer, list); + list_del(&buf->list); + } else if ((buf = instance->current_buffer)) { + instance->current_buffer = NULL; + } else /* all buffers empty */ + break; + + snd = list_entry(instance->spare_senders.next, + struct udsl_sender, list); + list_del(&snd->list); + spin_unlock_irq(&instance->send_lock); + + snd->buffer = buf; + usb_fill_bulk_urb(snd->urb, instance->usb_dev, + usb_sndbulkpipe(instance->usb_dev, instance->data_endpoint), + buf->base, + (snd_buf_size - buf->free_cells) * (ATM_CELL_SIZE + instance->snd_padding), + udsl_complete_send, snd); + + vdbg("udsl_process_send: submitting urb 0x%p (%d cells), snd 0x%p, buf 0x%p", + snd->urb, snd_buf_size - buf->free_cells, snd, buf); + + if ((err = usb_submit_urb(snd->urb, GFP_ATOMIC)) < 0) { + dbg("udsl_process_send: urb submission failed (%d)!", err); + spin_lock_irq(&instance->send_lock); + list_add(&snd->list, &instance->spare_senders); + spin_unlock_irq(&instance->send_lock); + list_add(&buf->list, &instance->filled_send_buffers); + return; /* bail out */ + } + + spin_lock_irq(&instance->send_lock); + } /* while */ + spin_unlock_irq(&instance->send_lock); + + if (!instance->current_skb) + instance->current_skb = skb_dequeue(&instance->sndqueue); + if (!instance->current_skb) + return; /* done - no more skbs */ + + skb = instance->current_skb; + + if (!(buf = instance->current_buffer)) { + spin_lock_irq(&instance->send_lock); + if (list_empty(&instance->spare_send_buffers)) { + instance->current_buffer = NULL; + spin_unlock_irq(&instance->send_lock); + return; /* done - no more buffers */ + } + buf = list_entry(instance->spare_send_buffers.next, + struct udsl_send_buffer, list); + list_del(&buf->list); + spin_unlock_irq(&instance->send_lock); + + buf->free_start = buf->base; + buf->free_cells = snd_buf_size; + + instance->current_buffer = buf; + } + + num_written = udsl_write_cells(instance, buf->free_cells, skb, &buf->free_start); + + vdbg("udsl_process_send: wrote %u cells from skb 0x%p to buffer 0x%p", + num_written, skb, buf); + + if (!(buf->free_cells -= num_written)) { + list_add_tail(&buf->list, &instance->filled_send_buffers); + instance->current_buffer = NULL; + } + + vdbg("udsl_process_send: buffer contains %d cells, %d left", + snd_buf_size - buf->free_cells, buf->free_cells); + + if (!UDSL_SKB(skb)->num_cells) { + struct atm_vcc *vcc = UDSL_SKB(skb)->atm_data.vcc; + + udsl_pop(vcc, skb); + instance->current_skb = NULL; + + atomic_inc(&vcc->stats->tx); + } + + goto made_progress; +} + +static void udsl_cancel_send(struct udsl_instance_data *instance, + struct atm_vcc *vcc) +{ + struct sk_buff *skb, *n; + + dbg("udsl_cancel_send entered"); + spin_lock_irq(&instance->sndqueue.lock); + for (skb = instance->sndqueue.next, n = skb->next; + skb != (struct sk_buff *)&instance->sndqueue; + skb = n, n = skb->next) + if (UDSL_SKB(skb)->atm_data.vcc == vcc) { + dbg("udsl_cancel_send: popping skb 0x%p", skb); + __skb_unlink(skb, &instance->sndqueue); + udsl_pop(vcc, skb); + } + spin_unlock_irq(&instance->sndqueue.lock); + + tasklet_disable(&instance->send_tasklet); + if ((skb = instance->current_skb) && (UDSL_SKB(skb)->atm_data.vcc == vcc)) { + dbg("udsl_cancel_send: popping current skb (0x%p)", skb); + instance->current_skb = NULL; + udsl_pop(vcc, skb); + } + tasklet_enable(&instance->send_tasklet); + dbg("udsl_cancel_send done"); +} + +static int udsl_atm_send(struct atm_vcc *vcc, struct sk_buff *skb) +{ + struct udsl_instance_data *instance = vcc->dev->dev_data; + int err; + + vdbg("udsl_atm_send called (skb 0x%p, len %u)", skb, skb->len); + + if (!instance) { + dbg("udsl_atm_send: NULL data!"); + err = -ENODEV; + goto fail; + } + + if (vcc->qos.aal != ATM_AAL5) { + dbg("udsl_atm_send: unsupported ATM type %d!", vcc->qos.aal); + err = -EINVAL; + goto fail; + } + + if (skb->len > ATM_MAX_AAL5_PDU) { + dbg("udsl_atm_send: packet too long (%d vs %d)!", skb->len, + ATM_MAX_AAL5_PDU); + err = -EINVAL; + goto fail; + } + + PACKETDEBUG(skb->data, skb->len); + + udsl_groom_skb(vcc, skb); + skb_queue_tail(&instance->sndqueue, skb); + tasklet_schedule(&instance->send_tasklet); + + return 0; + + fail: + udsl_pop(vcc, skb); + return err; +} + +/******************** +** bean counting ** +********************/ + +static void udsl_destroy_instance(struct kref *kref) +{ + struct udsl_instance_data *instance = + container_of(kref, struct udsl_instance_data, refcount); + + tasklet_kill(&instance->receive_tasklet); + tasklet_kill(&instance->send_tasklet); + usb_put_dev(instance->usb_dev); + kfree(instance); +} + +void udsl_get_instance(struct udsl_instance_data *instance) +{ + kref_get(&instance->refcount); +} + +void udsl_put_instance(struct udsl_instance_data *instance) +{ + kref_put(&instance->refcount, udsl_destroy_instance); +} + +/********** +** ATM ** +**********/ + +static void udsl_atm_dev_close(struct atm_dev *dev) +{ + struct udsl_instance_data *instance = dev->dev_data; + + dev->dev_data = NULL; + udsl_put_instance(instance); +} + +static int udsl_atm_proc_read(struct atm_dev *atm_dev, loff_t * pos, char *page) +{ + struct udsl_instance_data *instance = atm_dev->dev_data; + int left = *pos; + + if (!instance) { + dbg("udsl_atm_proc_read: NULL instance!"); + return -ENODEV; + } + + if (!left--) + 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]); + + if (!left--) + return sprintf(page, + "AAL5: tx %d ( %d err ), rx %d ( %d err, %d drop )\n", + atomic_read(&atm_dev->stats.aal5.tx), + atomic_read(&atm_dev->stats.aal5.tx_err), + atomic_read(&atm_dev->stats.aal5.rx), + atomic_read(&atm_dev->stats.aal5.rx_err), + atomic_read(&atm_dev->stats.aal5.rx_drop)); + + if (!left--) { + switch (atm_dev->signal) { + case ATM_PHY_SIG_FOUND: + sprintf(page, "Line up"); + break; + case ATM_PHY_SIG_LOST: + sprintf(page, "Line down"); + break; + default: + sprintf(page, "Line state unknown"); + break; + } + + if (instance->usb_dev->state == USB_STATE_NOTATTACHED) + strcat(page, ", disconnected\n"); + else { + if (instance->status == UDSL_LOADED_FIRMWARE) + strcat(page, ", firmware loaded\n"); + else if (instance->status == UDSL_LOADING_FIRMWARE) + strcat(page, ", firmware loading\n"); + else + strcat(page, ", no firmware\n"); + } + + return strlen(page); + } + + return 0; +} + +static int udsl_atm_open(struct atm_vcc *vcc) +{ + struct udsl_instance_data *instance = vcc->dev->dev_data; + struct udsl_vcc_data *new; + unsigned int max_pdu; + int vci = vcc->vci; + short vpi = vcc->vpi; + int err; + + dbg("udsl_atm_open: vpi %hd, vci %d", vpi, vci); + + if (!instance) { + dbg("udsl_atm_open: NULL data!"); + return -ENODEV; + } + + /* only support AAL5 */ + if ((vcc->qos.aal != ATM_AAL5) || (vcc->qos.rxtp.max_sdu < 0) + || (vcc->qos.rxtp.max_sdu > ATM_MAX_AAL5_PDU)) { + dbg("udsl_atm_open: unsupported ATM type %d!", vcc->qos.aal); + return -EINVAL; + } + + if (instance->firmware_wait && + (err = instance->firmware_wait(instance)) < 0) { + dbg("udsl_atm_open: firmware not loaded (%d)!", err); + return err; + } + + down(&instance->serialize); /* vs self, udsl_atm_close */ + + if (udsl_find_vcc(instance, vpi, vci)) { + dbg("udsl_atm_open: %hd/%d already in use!", vpi, vci); + up(&instance->serialize); + return -EADDRINUSE; + } + + if (!(new = kmalloc(sizeof(struct udsl_vcc_data), GFP_KERNEL))) { + dbg("udsl_atm_open: no memory for vcc_data!"); + up(&instance->serialize); + return -ENOMEM; + } + + memset(new, 0, sizeof(struct udsl_vcc_data)); + new->vcc = vcc; + new->vpi = vpi; + new->vci = vci; + + /* udsl_extract_cells requires at least one cell */ + max_pdu = max(1, UDSL_NUM_CELLS(vcc->qos.rxtp.max_sdu)) * ATM_CELL_PAYLOAD; + if (!(new->sarb = alloc_skb(max_pdu, GFP_KERNEL))) { + dbg("udsl_atm_open: no memory for SAR buffer!"); + kfree(new); + up(&instance->serialize); + return -ENOMEM; + } + + vcc->dev_data = new; + + tasklet_disable(&instance->receive_tasklet); + list_add(&new->list, &instance->vcc_list); + tasklet_enable(&instance->receive_tasklet); + + set_bit(ATM_VF_ADDR, &vcc->flags); + set_bit(ATM_VF_PARTIAL, &vcc->flags); + set_bit(ATM_VF_READY, &vcc->flags); + + up(&instance->serialize); + + tasklet_schedule(&instance->receive_tasklet); + + dbg("udsl_atm_open: allocated vcc data 0x%p (max_pdu: %u)", new, max_pdu); + + return 0; +} + +static void udsl_atm_close(struct atm_vcc *vcc) +{ + struct udsl_instance_data *instance = vcc->dev->dev_data; + struct udsl_vcc_data *vcc_data = vcc->dev_data; + + dbg("udsl_atm_close called"); + + if (!instance || !vcc_data) { + dbg("udsl_atm_close: NULL data!"); + return; + } + + dbg("udsl_atm_close: deallocating vcc 0x%p with vpi %d vci %d", + vcc_data, vcc_data->vpi, vcc_data->vci); + + udsl_cancel_send(instance, vcc); + + down(&instance->serialize); /* vs self, udsl_atm_open */ + + tasklet_disable(&instance->receive_tasklet); + list_del(&vcc_data->list); + tasklet_enable(&instance->receive_tasklet); + + kfree_skb(vcc_data->sarb); + vcc_data->sarb = NULL; + + kfree(vcc_data); + vcc->dev_data = NULL; + + vcc->vpi = ATM_VPI_UNSPEC; + vcc->vci = ATM_VCI_UNSPEC; + clear_bit(ATM_VF_READY, &vcc->flags); + clear_bit(ATM_VF_PARTIAL, &vcc->flags); + clear_bit(ATM_VF_ADDR, &vcc->flags); + + up(&instance->serialize); + + dbg("udsl_atm_close successful"); +} + +static int udsl_atm_ioctl(struct atm_dev *dev, unsigned int cmd, + void __user * arg) +{ + switch (cmd) { + case ATM_QUERYLOOP: + return put_user(ATM_LM_NONE, (int __user *)arg) ? -EFAULT : 0; + default: + return -ENOIOCTLCMD; + } +} + +/********** +** USB ** +**********/ + +int udsl_instance_setup(struct usb_device *dev, + struct udsl_instance_data *instance) +{ + char *buf; + int i, length; + + kref_init(&instance->refcount); /* one for USB */ + udsl_get_instance(instance); /* one for ATM */ + + init_MUTEX(&instance->serialize); + + instance->usb_dev = dev; + + INIT_LIST_HEAD(&instance->vcc_list); + + instance->status = UDSL_NO_FIRMWARE; + init_waitqueue_head(&instance->firmware_waiters); + + spin_lock_init(&instance->receive_lock); + INIT_LIST_HEAD(&instance->spare_receivers); + INIT_LIST_HEAD(&instance->filled_receive_buffers); + + tasklet_init(&instance->receive_tasklet, udsl_process_receive, (unsigned long)instance); + INIT_LIST_HEAD(&instance->spare_receive_buffers); + + skb_queue_head_init(&instance->sndqueue); + + spin_lock_init(&instance->send_lock); + INIT_LIST_HEAD(&instance->spare_senders); + INIT_LIST_HEAD(&instance->spare_send_buffers); + + tasklet_init(&instance->send_tasklet, udsl_process_send, + (unsigned long)instance); + INIT_LIST_HEAD(&instance->filled_send_buffers); + + /* receive init */ + for (i = 0; i < num_rcv_urbs; i++) { + struct udsl_receiver *rcv = &(instance->receivers[i]); + + if (!(rcv->urb = usb_alloc_urb(0, GFP_KERNEL))) { + dbg("udsl_usb_probe: no memory for receive urb %d!", i); + goto fail; + } + + rcv->instance = instance; + + list_add(&rcv->list, &instance->spare_receivers); + } + + for (i = 0; i < num_rcv_bufs; i++) { + struct udsl_receive_buffer *buf = + &(instance->receive_buffers[i]); + + buf->base = kmalloc(rcv_buf_size * (ATM_CELL_SIZE + instance->rcv_padding), + GFP_KERNEL); + if (!buf->base) { + dbg("udsl_usb_probe: no memory for receive buffer %d!", i); + goto fail; + } + + list_add(&buf->list, &instance->spare_receive_buffers); + } + + /* send init */ + for (i = 0; i < num_snd_urbs; i++) { + struct udsl_sender *snd = &(instance->senders[i]); + + if (!(snd->urb = usb_alloc_urb(0, GFP_KERNEL))) { + dbg("udsl_usb_probe: no memory for send urb %d!", i); + goto fail; + } + + snd->instance = instance; + + list_add(&snd->list, &instance->spare_senders); + } + + for (i = 0; i < num_snd_bufs; i++) { + struct udsl_send_buffer *buf = &(instance->send_buffers[i]); + + buf->base = kmalloc(snd_buf_size * (ATM_CELL_SIZE + instance->snd_padding), + GFP_KERNEL); + if (!buf->base) { + dbg("udsl_usb_probe: no memory for send buffer %d!", i); + goto fail; + } + + list_add(&buf->list, &instance->spare_send_buffers); + } + + /* ATM init */ + instance->atm_dev = atm_dev_register(instance->driver_name, + &udsl_atm_devops, -1, NULL); + if (!instance->atm_dev) { + dbg("udsl_usb_probe: failed to register ATM device!"); + goto fail; + } + + instance->atm_dev->ci_range.vpi_bits = ATM_CI_MAX; + instance->atm_dev->ci_range.vci_bits = ATM_CI_MAX; + instance->atm_dev->signal = ATM_PHY_SIG_UNKNOWN; + + /* temp init ATM device, set to 128kbit */ + instance->atm_dev->link_rate = 128 * 1000 / 424; + + /* device description */ + buf = instance->description; + length = sizeof(instance->description); + + if ((i = usb_string(dev, dev->descriptor.iProduct, buf, length)) < 0) + goto finish; + + buf += i; + length -= i; + + i = scnprintf(buf, length, " ("); + buf += i; + length -= i; + + if (length <= 0 || (i = usb_make_path(dev, buf, length)) < 0) + goto finish; + + buf += i; + length -= i; + + snprintf(buf, length, ")"); + + finish: + /* ready for ATM callbacks */ + wmb(); + instance->atm_dev->dev_data = instance; + + usb_get_dev(dev); + + return 0; + + fail: + for (i = 0; i < num_snd_bufs; i++) + kfree(instance->send_buffers[i].base); + + for (i = 0; i < num_snd_urbs; i++) + usb_free_urb(instance->senders[i].urb); + + for (i = 0; i < num_rcv_bufs; i++) + kfree(instance->receive_buffers[i].base); + + for (i = 0; i < num_rcv_urbs; i++) + usb_free_urb(instance->receivers[i].urb); + + return -ENOMEM; +} + +void udsl_instance_disconnect(struct udsl_instance_data *instance) +{ + int i; + + dbg("udsl_instance_disconnect entered"); + + if (!instance) { + dbg("udsl_instance_disconnect: NULL instance!"); + return; + } + + /* receive finalize */ + tasklet_disable(&instance->receive_tasklet); + + for (i = 0; i < num_rcv_urbs; i++) + usb_kill_urb(instance->receivers[i].urb); + + /* no need to take the spinlock */ + INIT_LIST_HEAD(&instance->filled_receive_buffers); + INIT_LIST_HEAD(&instance->spare_receive_buffers); + + tasklet_enable(&instance->receive_tasklet); + + for (i = 0; i < num_rcv_urbs; i++) + usb_free_urb(instance->receivers[i].urb); + + for (i = 0; i < num_rcv_bufs; i++) + kfree(instance->receive_buffers[i].base); + + /* send finalize */ + tasklet_disable(&instance->send_tasklet); + + for (i = 0; i < num_snd_urbs; i++) + usb_kill_urb(instance->senders[i].urb); + + /* no need to take the spinlock */ + INIT_LIST_HEAD(&instance->spare_senders); + INIT_LIST_HEAD(&instance->spare_send_buffers); + instance->current_buffer = NULL; + + tasklet_enable(&instance->send_tasklet); + + for (i = 0; i < num_snd_urbs; i++) + usb_free_urb(instance->senders[i].urb); + + for (i = 0; i < num_snd_bufs; i++) + kfree(instance->send_buffers[i].base); + + /* ATM finalize */ + shutdown_atm_dev(instance->atm_dev); +} + +EXPORT_SYMBOL_GPL(udsl_get_instance); +EXPORT_SYMBOL_GPL(udsl_put_instance); +EXPORT_SYMBOL_GPL(udsl_instance_setup); +EXPORT_SYMBOL_GPL(udsl_instance_disconnect); + +/*********** +** init ** +***********/ + +static int __init udsl_usb_init(void) +{ + dbg("udsl_usb_init: driver version " DRIVER_VERSION); + + if (sizeof(struct udsl_control) > sizeof(((struct sk_buff *) 0)->cb)) { + printk(KERN_ERR __FILE__ ": unusable with this kernel!\n"); + return -EIO; + } + + if ((num_rcv_urbs > UDSL_MAX_RCV_URBS) + || (num_snd_urbs > UDSL_MAX_SND_URBS) + || (num_rcv_bufs > UDSL_MAX_RCV_BUFS) + || (num_snd_bufs > UDSL_MAX_SND_BUFS) + || (rcv_buf_size > UDSL_MAX_RCV_BUF_SIZE) + || (snd_buf_size > UDSL_MAX_SND_BUF_SIZE)) + return -EINVAL; + + return 0; +} + +static void __exit udsl_usb_exit(void) +{ +} + +module_init(udsl_usb_init); +module_exit(udsl_usb_exit); + +MODULE_AUTHOR(DRIVER_AUTHOR); +MODULE_DESCRIPTION(DRIVER_DESC); +MODULE_LICENSE("GPL"); +MODULE_VERSION(DRIVER_VERSION); + +/************ +** debug ** +************/ + +#ifdef VERBOSE_DEBUG +static int udsl_print_packet(const unsigned char *data, int len) +{ + unsigned char buffer[256]; + int i = 0, j = 0; + + for (i = 0; i < len;) { + buffer[0] = '\0'; + sprintf(buffer, "%.3d :", i); + for (j = 0; (j < 16) && (i < len); j++, i++) { + sprintf(buffer, "%s %2.2x", buffer, data[i]); + } + dbg("%s", buffer); + } + return i; +} +#endif diff --git a/drivers/usb/atm/usb_atm.h b/drivers/usb/atm/usb_atm.h new file mode 100644 index 000000000..188e91710 --- /dev/null +++ b/drivers/usb/atm/usb_atm.h @@ -0,0 +1,160 @@ +/****************************************************************************** + * usb_atm.h - Generic USB xDSL driver core + * + * Copyright (C) 2001, Alcatel + * Copyright (C) 2003, Duncan Sands, SolNegro, Josep Comas + * Copyright (C) 2004, David Woodhouse + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the 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 +#include +#include +#include +#include +#include + +#define UDSL_MAX_RCV_URBS 4 +#define UDSL_MAX_SND_URBS 4 +#define UDSL_MAX_RCV_BUFS 8 +#define UDSL_MAX_SND_BUFS 8 +#define UDSL_MAX_RCV_BUF_SIZE 1024 /* ATM cells */ +#define UDSL_MAX_SND_BUF_SIZE 1024 /* ATM cells */ +#define UDSL_DEFAULT_RCV_URBS 2 +#define UDSL_DEFAULT_SND_URBS 2 +#define UDSL_DEFAULT_RCV_BUFS 4 +#define UDSL_DEFAULT_SND_BUFS 4 +#define UDSL_DEFAULT_RCV_BUF_SIZE 64 /* ATM cells */ +#define UDSL_DEFAULT_SND_BUF_SIZE 64 /* ATM cells */ + +#define ATM_CELL_HEADER (ATM_CELL_SIZE - ATM_CELL_PAYLOAD) +#define UDSL_NUM_CELLS(x) (((x) + ATM_AAL5_TRAILER + ATM_CELL_PAYLOAD - 1) / ATM_CELL_PAYLOAD) + +/* receive */ + +struct udsl_receive_buffer { + struct list_head list; + unsigned char *base; + unsigned int filled_cells; +}; + +struct udsl_receiver { + struct list_head list; + struct udsl_receive_buffer *buffer; + struct urb *urb; + struct udsl_instance_data *instance; +}; + +struct udsl_vcc_data { + /* vpi/vci lookup */ + struct list_head list; + short vpi; + int vci; + struct atm_vcc *vcc; + + /* raw cell reassembly */ + struct sk_buff *sarb; +}; + +/* send */ + +struct udsl_send_buffer { + struct list_head list; + unsigned char *base; + unsigned char *free_start; + unsigned int free_cells; +}; + +struct udsl_sender { + struct list_head list; + struct udsl_send_buffer *buffer; + struct urb *urb; + struct udsl_instance_data *instance; +}; + +struct udsl_control { + struct atm_skb_data atm_data; + unsigned int num_cells; + unsigned int num_entire; + unsigned int pdu_padding; + unsigned char cell_header[ATM_CELL_HEADER]; + unsigned char aal5_trailer[ATM_AAL5_TRAILER]; +}; + +#define UDSL_SKB(x) ((struct udsl_control *)(x)->cb) + +/* main driver data */ + +enum udsl_status { + UDSL_NO_FIRMWARE, + UDSL_LOADING_FIRMWARE, + UDSL_LOADED_FIRMWARE +}; + +struct udsl_instance_data { + struct kref refcount; + struct semaphore serialize; + + /* USB device part */ + struct usb_device *usb_dev; + char description[64]; + int data_endpoint; + int snd_padding; + int rcv_padding; + const char *driver_name; + + /* ATM device part */ + struct atm_dev *atm_dev; + struct list_head vcc_list; + + /* firmware */ + int (*firmware_wait) (struct udsl_instance_data *); + enum udsl_status status; + wait_queue_head_t firmware_waiters; + + /* receive */ + struct udsl_receiver receivers[UDSL_MAX_RCV_URBS]; + struct udsl_receive_buffer receive_buffers[UDSL_MAX_RCV_BUFS]; + + spinlock_t receive_lock; + struct list_head spare_receivers; + struct list_head filled_receive_buffers; + + struct tasklet_struct receive_tasklet; + struct list_head spare_receive_buffers; + + /* send */ + struct udsl_sender senders[UDSL_MAX_SND_URBS]; + struct udsl_send_buffer send_buffers[UDSL_MAX_SND_BUFS]; + + struct sk_buff_head sndqueue; + + spinlock_t send_lock; + struct list_head spare_senders; + struct list_head spare_send_buffers; + + struct tasklet_struct send_tasklet; + struct sk_buff *current_skb; /* being emptied */ + struct udsl_send_buffer *current_buffer; /* being filled */ + struct list_head filled_send_buffers; +}; + +extern int udsl_instance_setup(struct usb_device *dev, + struct udsl_instance_data *instance); +extern void udsl_instance_disconnect(struct udsl_instance_data *instance); +extern void udsl_get_instance(struct udsl_instance_data *instance); +extern void udsl_put_instance(struct udsl_instance_data *instance); diff --git a/drivers/usb/class/cdc-acm.h b/drivers/usb/class/cdc-acm.h index bf72f278b..cc26d9517 100644 --- a/drivers/usb/class/cdc-acm.h +++ b/drivers/usb/class/cdc-acm.h @@ -70,7 +70,7 @@ */ struct acm_line { - __u32 speed; + __le32 speed; __u8 stopbits; __u8 parity; __u8 databits; diff --git a/drivers/usb/core/Kconfig b/drivers/usb/core/Kconfig index 050b078e3..1a9ff6184 100644 --- a/drivers/usb/core/Kconfig +++ b/drivers/usb/core/Kconfig @@ -60,3 +60,40 @@ config USB_DYNAMIC_MINORS If you are unsure about this, say N here. +config USB_SUSPEND + bool "USB suspend/resume (EXPERIMENTAL)" + depends on USB && PM && EXPERIMENTAL + help + If you say Y here, you can use driver calls or the sysfs + "power/state" file to suspend or resume individual USB + peripherals. There are many related features, such as + remote wakeup and driver-specific suspend processing, that + may not yet work as expected. + + If you are unsure about this, say N here. + + +config USB_OTG + bool + depends on USB && EXPERIMENTAL + select USB_SUSPEND + default n + + +config USB_OTG_WHITELIST + bool "Rely on OTG Targeted Peripherals List" + depends on USB_OTG + default y + help + If you say Y here, the "otg_whitelist.h" file will be used as a + product whitelist, so USB peripherals not listed there will be + rejected during enumeration. This behavior is required by the + USB OTG specification for all devices not on your product's + "Targeted Peripherals List". + + Otherwise, peripherals not listed there will only generate a + warning and enumeration will continue. That's more like what + normal Linux-USB hosts do (other than the warning), and is + convenient for many stages of product development. + + diff --git a/drivers/usb/core/otg_whitelist.h b/drivers/usb/core/otg_whitelist.h new file mode 100644 index 000000000..8bf4e8111 --- /dev/null +++ b/drivers/usb/core/otg_whitelist.h @@ -0,0 +1,112 @@ +/* + * drivers/usb/core/otg_whitelist.h + * + * Copyright (C) 2004 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. + */ + +/* + * This OTG Whitelist is the OTG "Targeted Peripheral List". It should + * mostly use of USB_DEVICE() or USB_DEVICE_VER() entries.. + * + * YOU _SHOULD_ CHANGE THIS LIST TO MATCH YOUR PRODUCT AND ITS TESTING! + */ + +static struct usb_device_id whitelist_table [] = { + +/* hubs are optional in OTG, but very handy ... */ +{ USB_DEVICE_INFO(USB_CLASS_HUB, 0, 0), }, +{ USB_DEVICE_INFO(USB_CLASS_HUB, 0, 1), }, + +#ifdef CONFIG_USB_PRINTER /* ignoring nonstatic linkage! */ +/* FIXME actually, printers are NOT supposed to use device classes; + * they're supposed to use interface classes... + */ +{ USB_DEVICE_INFO(7, 1, 1) }, +{ USB_DEVICE_INFO(7, 1, 2) }, +{ USB_DEVICE_INFO(7, 1, 3) }, +#endif + +#ifdef CONFIG_USB_CDCETHER +/* Linux-USB CDC Ethernet gadget */ +{ USB_DEVICE(0x0525, 0xa4a1), }, +/* Linux-USB CDC Ethernet + RNDIS gadget */ +{ USB_DEVICE(0x0525, 0xa4a2), }, +#endif + +#if defined(CONFIG_USB_TEST) || defined(CONFIG_USB_TEST_MODULE) +/* gadget zero, for testing */ +{ USB_DEVICE(0x0525, 0xa4a0), }, +#endif + +{ } /* Terminating entry */ +}; + +static int is_targeted(struct usb_device *dev) +{ + struct usb_device_id *id = whitelist_table; + + /* possible in developer configs only! */ + if (!dev->bus->otg_port) + return 1; + + /* HNP test device is _never_ targeted (see OTG spec 6.6.6) */ + if (dev->descriptor.idVendor == 0x1a0a + && dev->descriptor.idProduct == 0xbadd) + return 0; + + /* NOTE: can't use usb_match_id() since interface caches + * aren't set up yet. this is cut/paste from that code. + */ + for (id = whitelist_table; id->match_flags; id++) { + if ((id->match_flags & USB_DEVICE_ID_MATCH_VENDOR) && + id->idVendor != dev->descriptor.idVendor) + continue; + + if ((id->match_flags & USB_DEVICE_ID_MATCH_PRODUCT) && + id->idProduct != dev->descriptor.idProduct) + continue; + + /* No need to test id->bcdDevice_lo != 0, since 0 is never + greater than any unsigned number. */ + if ((id->match_flags & USB_DEVICE_ID_MATCH_DEV_LO) && + (id->bcdDevice_lo > dev->descriptor.bcdDevice)) + continue; + + if ((id->match_flags & USB_DEVICE_ID_MATCH_DEV_HI) && + (id->bcdDevice_hi < dev->descriptor.bcdDevice)) + continue; + + if ((id->match_flags & USB_DEVICE_ID_MATCH_DEV_CLASS) && + (id->bDeviceClass != dev->descriptor.bDeviceClass)) + continue; + + if ((id->match_flags & USB_DEVICE_ID_MATCH_DEV_SUBCLASS) && + (id->bDeviceSubClass!= dev->descriptor.bDeviceSubClass)) + continue; + + if ((id->match_flags & USB_DEVICE_ID_MATCH_DEV_PROTOCOL) && + (id->bDeviceProtocol != dev->descriptor.bDeviceProtocol)) + continue; + + return 1; + } + + /* add other match criteria here ... */ + + + /* OTG MESSAGE: report errors here, customize to match your product */ + dev_err(&dev->dev, "device v%04x p%04x is not supported\n", + dev->descriptor.idVendor, + dev->descriptor.idProduct); +#ifdef CONFIG_USB_OTG_WHITELIST + return 0; +#else + return 1; +#endif +} + diff --git a/drivers/usb/gadget/Makefile b/drivers/usb/gadget/Makefile index 645d15cf5..03c639bfd 100644 --- a/drivers/usb/gadget/Makefile +++ b/drivers/usb/gadget/Makefile @@ -5,13 +5,15 @@ obj-$(CONFIG_USB_DUMMY_HCD) += dummy_hcd.o obj-$(CONFIG_USB_NET2280) += net2280.o obj-$(CONFIG_USB_PXA2XX) += pxa2xx_udc.o obj-$(CONFIG_USB_GOKU) += goku_udc.o +obj-$(CONFIG_USB_OMAP) += omap_udc.o +obj-$(CONFIG_USB_LH7A40X) += lh7a40x_udc.o # # USB gadget drivers # g_zero-objs := zero.o usbstring.o config.o epautoconf.o g_ether-objs := ether.o usbstring.o config.o epautoconf.o -g_serial-objs := serial.o usbstring.o +g_serial-objs := serial.o usbstring.o epautoconf.o gadgetfs-objs := inode.o g_file_storage-objs := file_storage.o usbstring.o config.o \ epautoconf.o diff --git a/drivers/usb/gadget/lh7a40x_udc.c b/drivers/usb/gadget/lh7a40x_udc.c new file mode 100644 index 000000000..772627e97 --- /dev/null +++ b/drivers/usb/gadget/lh7a40x_udc.c @@ -0,0 +1,2168 @@ +/* + * linux/drivers/usb/gadget/lh7a40x_udc.c + * Sharp LH7A40x on-chip full speed USB device controllers + * + * Copyright (C) 2004 Mikko Lahteenmaki, Nordic ID + * Copyright (C) 2004 Bo Henriksen, Nordic ID + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the 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 "lh7a40x_udc.h" + +//#define DEBUG printk +//#define DEBUG_EP0 printk +//#define DEBUG_SETUP printk + +#ifndef DEBUG_EP0 +# define DEBUG_EP0(fmt,args...) +#endif +#ifndef DEBUG_SETUP +# define DEBUG_SETUP(fmt,args...) +#endif +#ifndef DEBUG +# define NO_STATES +# define DEBUG(fmt,args...) +#endif + +#define DRIVER_DESC "LH7A40x USB Device Controller" +#define DRIVER_VERSION __DATE__ + +#ifndef _BIT /* FIXME - what happended to _BIT in 2.6.7bk18? */ +#define _BIT(x) (1<<(x)) +#endif + +struct lh7a40x_udc *the_controller; + +static const char driver_name[] = "lh7a40x_udc"; +static const char driver_desc[] = DRIVER_DESC; +static const char ep0name[] = "ep0-control"; + +/* + Local definintions. +*/ +#define UDC_PROC_FILE + +#ifndef NO_STATES +static char *state_names[] = { + "WAIT_FOR_SETUP", + "DATA_STATE_XMIT", + "DATA_STATE_NEED_ZLP", + "WAIT_FOR_OUT_STATUS", + "DATA_STATE_RECV" +}; +#endif + +/* + Local declarations. +*/ +static int lh7a40x_ep_enable(struct usb_ep *ep, + const struct usb_endpoint_descriptor *); +static int lh7a40x_ep_disable(struct usb_ep *ep); +static struct usb_request *lh7a40x_alloc_request(struct usb_ep *ep, int); +static void lh7a40x_free_request(struct usb_ep *ep, struct usb_request *); +static void *lh7a40x_alloc_buffer(struct usb_ep *ep, unsigned, dma_addr_t *, + int); +static void lh7a40x_free_buffer(struct usb_ep *ep, void *, dma_addr_t, + unsigned); +static int lh7a40x_queue(struct usb_ep *ep, struct usb_request *, int); +static int lh7a40x_dequeue(struct usb_ep *ep, struct usb_request *); +static int lh7a40x_set_halt(struct usb_ep *ep, int); +static int lh7a40x_fifo_status(struct usb_ep *ep); +static int lh7a40x_fifo_status(struct usb_ep *ep); +static void lh7a40x_fifo_flush(struct usb_ep *ep); +static void lh7a40x_ep0_kick(struct lh7a40x_udc *dev, struct lh7a40x_ep *ep); +static void lh7a40x_handle_ep0(struct lh7a40x_udc *dev, u32 intr); + +static void done(struct lh7a40x_ep *ep, struct lh7a40x_request *req, + int status); +static void pio_irq_enable(int bEndpointAddress); +static void pio_irq_disable(int bEndpointAddress); +static void stop_activity(struct lh7a40x_udc *dev, + struct usb_gadget_driver *driver); +static void flush(struct lh7a40x_ep *ep); +static void udc_enable(struct lh7a40x_udc *dev); +static void udc_set_address(struct lh7a40x_udc *dev, unsigned char address); + +static struct usb_ep_ops lh7a40x_ep_ops = { + .enable = lh7a40x_ep_enable, + .disable = lh7a40x_ep_disable, + + .alloc_request = lh7a40x_alloc_request, + .free_request = lh7a40x_free_request, + + .alloc_buffer = lh7a40x_alloc_buffer, + .free_buffer = lh7a40x_free_buffer, + + .queue = lh7a40x_queue, + .dequeue = lh7a40x_dequeue, + + .set_halt = lh7a40x_set_halt, + .fifo_status = lh7a40x_fifo_status, + .fifo_flush = lh7a40x_fifo_flush, +}; + +/* Inline code */ + +static __inline__ int write_packet(struct lh7a40x_ep *ep, + struct lh7a40x_request *req, int max) +{ + u8 *buf; + int length, count; + volatile u32 *fifo = (volatile u32 *)ep->fifo; + + buf = req->req.buf + req->req.actual; + prefetch(buf); + + length = req->req.length - req->req.actual; + length = min(length, max); + req->req.actual += length; + + DEBUG("Write %d (max %d), fifo %p\n", length, max, fifo); + + count = length; + while (count--) { + *fifo = *buf++; + } + + return length; +} + +static __inline__ void usb_set_index(u32 ep) +{ + *(volatile u32 *)io_p2v(USB_INDEX) = ep; +} + +static __inline__ u32 usb_read(u32 port) +{ + return *(volatile u32 *)io_p2v(port); +} + +static __inline__ void usb_write(u32 val, u32 port) +{ + *(volatile u32 *)io_p2v(port) = val; +} + +static __inline__ void usb_set(u32 val, u32 port) +{ + volatile u32 *ioport = (volatile u32 *)io_p2v(port); + u32 after = (*ioport) | val; + *ioport = after; +} + +static __inline__ void usb_clear(u32 val, u32 port) +{ + volatile u32 *ioport = (volatile u32 *)io_p2v(port); + u32 after = (*ioport) & ~val; + *ioport = after; +} + +/*-------------------------------------------------------------------------*/ + +#define GPIO_PORTC_DR (0x80000E08) +#define GPIO_PORTC_DDR (0x80000E18) +#define GPIO_PORTC_PDR (0x80000E70) + +/* get port C pin data register */ +#define get_portc_pdr(bit) ((usb_read(GPIO_PORTC_PDR) & _BIT(bit)) != 0) +/* get port C data direction register */ +#define get_portc_ddr(bit) ((usb_read(GPIO_PORTC_DDR) & _BIT(bit)) != 0) +/* set port C data register */ +#define set_portc_dr(bit, val) (val ? usb_set(_BIT(bit), GPIO_PORTC_DR) : usb_clear(_BIT(bit), GPIO_PORTC_DR)) +/* set port C data direction register */ +#define set_portc_ddr(bit, val) (val ? usb_set(_BIT(bit), GPIO_PORTC_DDR) : usb_clear(_BIT(bit), GPIO_PORTC_DDR)) + +/* + * LPD7A404 GPIO's: + * Port C bit 1 = USB Port 1 Power Enable + * Port C bit 2 = USB Port 1 Data Carrier Detect + */ +#define is_usb_connected() get_portc_pdr(2) + +#ifdef UDC_PROC_FILE + +static const char proc_node_name[] = "driver/udc"; + +static int +udc_proc_read(char *page, char **start, off_t off, int count, + int *eof, void *_dev) +{ + char *buf = page; + struct lh7a40x_udc *dev = _dev; + char *next = buf; + unsigned size = count; + unsigned long flags; + int t; + + if (off != 0) + return 0; + + local_irq_save(flags); + + /* basic device status */ + t = scnprintf(next, size, + DRIVER_DESC "\n" + "%s version: %s\n" + "Gadget driver: %s\n" + "Host: %s\n\n", + driver_name, DRIVER_VERSION, + dev->driver ? dev->driver->driver.name : "(none)", + is_usb_connected()? "full speed" : "disconnected"); + size -= t; + next += t; + + t = scnprintf(next, size, + "GPIO:\n" + " Port C bit 1: %d, dir %d\n" + " Port C bit 2: %d, dir %d\n\n", + get_portc_pdr(1), get_portc_ddr(1), + get_portc_pdr(2), get_portc_ddr(2) + ); + size -= t; + next += t; + + t = scnprintf(next, size, + "DCP pullup: %d\n\n", + (usb_read(USB_PM) & PM_USB_DCP) != 0); + size -= t; + next += t; + + local_irq_restore(flags); + *eof = 1; + return count - size; +} + +#define create_proc_files() create_proc_read_entry(proc_node_name, 0, NULL, udc_proc_read, dev) +#define remove_proc_files() remove_proc_entry(proc_node_name, NULL) + +#else /* !UDC_PROC_FILE */ + +#define create_proc_files() do {} while (0) +#define remove_proc_files() do {} while (0) + +#endif /* UDC_PROC_FILE */ + +/* + * udc_disable - disable USB device controller + */ +static void udc_disable(struct lh7a40x_udc *dev) +{ + DEBUG("%s, %p\n", __FUNCTION__, dev); + + udc_set_address(dev, 0); + + /* Disable interrupts */ + usb_write(0, USB_IN_INT_EN); + usb_write(0, USB_OUT_INT_EN); + usb_write(0, USB_INT_EN); + + /* Disable the USB */ + usb_write(0, USB_PM); + +#ifdef CONFIG_ARCH_LH7A404 + /* Disable USB power */ + set_portc_dr(1, 0); +#endif + + /* if hardware supports it, disconnect from usb */ + /* make_usb_disappear(); */ + + dev->ep0state = WAIT_FOR_SETUP; + dev->gadget.speed = USB_SPEED_UNKNOWN; + dev->usb_address = 0; +} + +/* + * udc_reinit - initialize software state + */ +static void udc_reinit(struct lh7a40x_udc *dev) +{ + u32 i; + + DEBUG("%s, %p\n", __FUNCTION__, dev); + + /* device/ep0 records init */ + INIT_LIST_HEAD(&dev->gadget.ep_list); + INIT_LIST_HEAD(&dev->gadget.ep0->ep_list); + dev->ep0state = WAIT_FOR_SETUP; + + /* basic endpoint records init */ + for (i = 0; i < UDC_MAX_ENDPOINTS; i++) { + struct lh7a40x_ep *ep = &dev->ep[i]; + + if (i != 0) + list_add_tail(&ep->ep.ep_list, &dev->gadget.ep_list); + + ep->desc = 0; + ep->stopped = 0; + INIT_LIST_HEAD(&ep->queue); + ep->pio_irqs = 0; + } + + /* the rest was statically initialized, and is read-only */ +} + +#define BYTES2MAXP(x) (x / 8) +#define MAXP2BYTES(x) (x * 8) + +/* until it's enabled, this UDC should be completely invisible + * to any USB host. + */ +static void udc_enable(struct lh7a40x_udc *dev) +{ + int ep; + + DEBUG("%s, %p\n", __FUNCTION__, dev); + + dev->gadget.speed = USB_SPEED_UNKNOWN; + +#ifdef CONFIG_ARCH_LH7A404 + /* Set Port C bit 1 & 2 as output */ + set_portc_ddr(1, 1); + set_portc_ddr(2, 1); + + /* Enable USB power */ + set_portc_dr(1, 0); +#endif + + /* + * C.f Chapter 18.1.3.1 Initializing the USB + */ + + /* Disable the USB */ + usb_clear(PM_USB_ENABLE, USB_PM); + + /* Reset APB & I/O sides of the USB */ + usb_set(USB_RESET_APB | USB_RESET_IO, USB_RESET); + mdelay(5); + usb_clear(USB_RESET_APB | USB_RESET_IO, USB_RESET); + + /* Set MAXP values for each */ + for (ep = 0; ep < UDC_MAX_ENDPOINTS; ep++) { + struct lh7a40x_ep *ep_reg = &dev->ep[ep]; + u32 csr; + + usb_set_index(ep); + + switch (ep_reg->ep_type) { + case ep_bulk_in: + case ep_interrupt: + usb_clear(USB_IN_CSR2_USB_DMA_EN | USB_IN_CSR2_AUTO_SET, + ep_reg->csr2); + /* Fall through */ + case ep_control: + usb_write(BYTES2MAXP(ep_maxpacket(ep_reg)), + USB_IN_MAXP); + break; + case ep_bulk_out: + usb_clear(USB_OUT_CSR2_USB_DMA_EN | + USB_OUT_CSR2_AUTO_CLR, ep_reg->csr2); + usb_write(BYTES2MAXP(ep_maxpacket(ep_reg)), + USB_OUT_MAXP); + break; + } + + /* Read & Write CSR1, just in case */ + csr = usb_read(ep_reg->csr1); + usb_write(csr, ep_reg->csr1); + + flush(ep_reg); + } + + /* Disable interrupts */ + usb_write(0, USB_IN_INT_EN); + usb_write(0, USB_OUT_INT_EN); + usb_write(0, USB_INT_EN); + + /* Enable interrupts */ + usb_set(USB_IN_INT_EP0, USB_IN_INT_EN); + usb_set(USB_INT_RESET_INT | USB_INT_RESUME_INT, USB_INT_EN); + /* Dont enable rest of the interrupts */ + /* usb_set(USB_IN_INT_EP3 | USB_IN_INT_EP1 | USB_IN_INT_EP0, USB_IN_INT_EN); + usb_set(USB_OUT_INT_EP2, USB_OUT_INT_EN); */ + + /* Enable SUSPEND */ + usb_set(PM_ENABLE_SUSPEND, USB_PM); + + /* Enable the USB */ + usb_set(PM_USB_ENABLE, USB_PM); + +#ifdef CONFIG_ARCH_LH7A404 + /* NOTE: DOES NOT WORK! */ + /* Let host detect UDC: + * Software must write a 0 to the PMR:DCP_CTRL bit to turn this + * transistor on and pull the USBDP pin HIGH. + */ + /* usb_clear(PM_USB_DCP, USB_PM); + usb_set(PM_USB_DCP, USB_PM); */ +#endif +} + +/* + Register entry point for the peripheral controller driver. +*/ +int usb_gadget_register_driver(struct usb_gadget_driver *driver) +{ + struct lh7a40x_udc *dev = the_controller; + int retval; + + DEBUG("%s: %s\n", __FUNCTION__, driver->driver.name); + + if (!driver + || driver->speed != USB_SPEED_FULL + || !driver->bind + || !driver->unbind || !driver->disconnect || !driver->setup) + return -EINVAL; + if (!dev) + return -ENODEV; + if (dev->driver) + return -EBUSY; + + /* first hook up the driver ... */ + dev->driver = driver; + dev->gadget.dev.driver = &driver->driver; + + device_add(&dev->gadget.dev); + retval = driver->bind(&dev->gadget); + if (retval) { + printk("%s: bind to driver %s --> error %d\n", dev->gadget.name, + driver->driver.name, retval); + device_del(&dev->gadget.dev); + + dev->driver = 0; + dev->gadget.dev.driver = 0; + return retval; + } + + /* ... then enable host detection and ep0; and we're ready + * for set_configuration as well as eventual disconnect. + * NOTE: this shouldn't power up until later. + */ + printk("%s: registered gadget driver '%s'\n", dev->gadget.name, + driver->driver.name); + + udc_enable(dev); + + return 0; +} + +EXPORT_SYMBOL(usb_gadget_register_driver); + +/* + Unregister entry point for the peripheral controller driver. +*/ +int usb_gadget_unregister_driver(struct usb_gadget_driver *driver) +{ + struct lh7a40x_udc *dev = the_controller; + unsigned long flags; + + if (!dev) + return -ENODEV; + if (!driver || driver != dev->driver) + return -EINVAL; + + spin_lock_irqsave(&dev->lock, flags); + dev->driver = 0; + stop_activity(dev, driver); + spin_unlock_irqrestore(&dev->lock, flags); + + driver->unbind(&dev->gadget); + device_del(&dev->gadget.dev); + + udc_disable(dev); + + DEBUG("unregistered gadget driver '%s'\n", driver->driver.name); + return 0; +} + +EXPORT_SYMBOL(usb_gadget_unregister_driver); + +/*-------------------------------------------------------------------------*/ + +/** Write request to FIFO (max write == maxp size) + * Return: 0 = still running, 1 = completed, negative = errno + * NOTE: INDEX register must be set for EP + */ +static int write_fifo(struct lh7a40x_ep *ep, struct lh7a40x_request *req) +{ + u32 max; + u32 csr; + + max = le16_to_cpu(ep->desc->wMaxPacketSize); + + csr = usb_read(ep->csr1); + DEBUG("CSR: %x %d\n", csr, csr & USB_IN_CSR1_FIFO_NOT_EMPTY); + + if (!(csr & USB_IN_CSR1_FIFO_NOT_EMPTY)) { + unsigned count; + int is_last, is_short; + + count = write_packet(ep, req, max); + usb_set(USB_IN_CSR1_IN_PKT_RDY, ep->csr1); + + /* last packet is usually short (or a zlp) */ + if (unlikely(count != max)) + is_last = is_short = 1; + else { + if (likely(req->req.length != req->req.actual) + || req->req.zero) + is_last = 0; + else + is_last = 1; + /* interrupt/iso maxpacket may not fill the fifo */ + is_short = unlikely(max < ep_maxpacket(ep)); + } + + DEBUG("%s: wrote %s %d bytes%s%s %d left %p\n", __FUNCTION__, + ep->ep.name, count, + is_last ? "/L" : "", is_short ? "/S" : "", + req->req.length - req->req.actual, req); + + /* requests complete when all IN data is in the FIFO */ + if (is_last) { + done(ep, req, 0); + if (list_empty(&ep->queue)) { + pio_irq_disable(ep_index(ep)); + } + return 1; + } + } else { + DEBUG("Hmm.. %d ep FIFO is not empty!\n", ep_index(ep)); + } + + return 0; +} + +/** Read to request from FIFO (max read == bytes in fifo) + * Return: 0 = still running, 1 = completed, negative = errno + * NOTE: INDEX register must be set for EP + */ +static int read_fifo(struct lh7a40x_ep *ep, struct lh7a40x_request *req) +{ + u32 csr; + u8 *buf; + unsigned bufferspace, count, is_short; + volatile u32 *fifo = (volatile u32 *)ep->fifo; + + /* make sure there's a packet in the FIFO. */ + csr = usb_read(ep->csr1); + if (!(csr & USB_OUT_CSR1_OUT_PKT_RDY)) { + DEBUG("%s: Packet NOT ready!\n", __FUNCTION__); + return -EINVAL; + } + + buf = req->req.buf + req->req.actual; + prefetchw(buf); + bufferspace = req->req.length - req->req.actual; + + /* read all bytes from this packet */ + count = usb_read(USB_OUT_FIFO_WC1); + req->req.actual += min(count, bufferspace); + + is_short = (count < ep->ep.maxpacket); + DEBUG("read %s %02x, %d bytes%s req %p %d/%d\n", + ep->ep.name, csr, count, + is_short ? "/S" : "", req, req->req.actual, req->req.length); + + while (likely(count-- != 0)) { + u8 byte = (u8) (*fifo & 0xff); + + if (unlikely(bufferspace == 0)) { + /* this happens when the driver's buffer + * is smaller than what the host sent. + * discard the extra data. + */ + if (req->req.status != -EOVERFLOW) + printk("%s overflow %d\n", ep->ep.name, count); + req->req.status = -EOVERFLOW; + } else { + *buf++ = byte; + bufferspace--; + } + } + + usb_clear(USB_OUT_CSR1_OUT_PKT_RDY, ep->csr1); + + /* completion */ + if (is_short || req->req.actual == req->req.length) { + done(ep, req, 0); + usb_set(USB_OUT_CSR1_FIFO_FLUSH, ep->csr1); + + if (list_empty(&ep->queue)) + pio_irq_disable(ep_index(ep)); + return 1; + } + + /* finished that packet. the next one may be waiting... */ + return 0; +} + +/* + * done - retire a request; caller blocked irqs + * INDEX register is preserved to keep same + */ +static void done(struct lh7a40x_ep *ep, struct lh7a40x_request *req, int status) +{ + unsigned int stopped = ep->stopped; + u32 index; + + DEBUG("%s, %p\n", __FUNCTION__, ep); + list_del_init(&req->queue); + + if (likely(req->req.status == -EINPROGRESS)) + req->req.status = status; + else + status = req->req.status; + + if (status && status != -ESHUTDOWN) + DEBUG("complete %s req %p stat %d len %u/%u\n", + ep->ep.name, &req->req, status, + req->req.actual, req->req.length); + + /* don't modify queue heads during completion callback */ + ep->stopped = 1; + /* Read current index (completion may modify it) */ + index = usb_read(USB_INDEX); + + spin_unlock(&ep->dev->lock); + req->req.complete(&ep->ep, &req->req); + spin_lock(&ep->dev->lock); + + /* Restore index */ + usb_set_index(index); + ep->stopped = stopped; +} + +/** Enable EP interrupt */ +static void pio_irq_enable(int ep) +{ + DEBUG("%s: %d\n", __FUNCTION__, ep); + + switch (ep) { + case 1: + usb_set(USB_IN_INT_EP1, USB_IN_INT_EN); + break; + case 2: + usb_set(USB_OUT_INT_EP2, USB_OUT_INT_EN); + break; + case 3: + usb_set(USB_IN_INT_EP3, USB_IN_INT_EN); + break; + default: + DEBUG("Unknown endpoint: %d\n", ep); + break; + } +} + +/** Disable EP interrupt */ +static void pio_irq_disable(int ep) +{ + DEBUG("%s: %d\n", __FUNCTION__, ep); + + switch (ep) { + case 1: + usb_clear(USB_IN_INT_EP1, USB_IN_INT_EN); + break; + case 2: + usb_clear(USB_OUT_INT_EP2, USB_OUT_INT_EN); + break; + case 3: + usb_clear(USB_IN_INT_EP3, USB_IN_INT_EN); + break; + default: + DEBUG("Unknown endpoint: %d\n", ep); + break; + } +} + +/* + * nuke - dequeue ALL requests + */ +void nuke(struct lh7a40x_ep *ep, int status) +{ + struct lh7a40x_request *req; + + DEBUG("%s, %p\n", __FUNCTION__, ep); + + /* Flush FIFO */ + flush(ep); + + /* called with irqs blocked */ + while (!list_empty(&ep->queue)) { + req = list_entry(ep->queue.next, struct lh7a40x_request, queue); + done(ep, req, status); + } + + /* Disable IRQ if EP is enabled (has decriptor) */ + if (ep->desc) + pio_irq_disable(ep_index(ep)); +} + +/* +void nuke_all(struct lh7a40x_udc *dev) +{ + int n; + for(n=0; nep[n]; + usb_set_index(n); + nuke(ep, 0); + } +}*/ + +/* +static void flush_all(struct lh7a40x_udc *dev) +{ + int n; + for (n = 0; n < UDC_MAX_ENDPOINTS; n++) + { + struct lh7a40x_ep *ep = &dev->ep[n]; + flush(ep); + } +} +*/ + +/** Flush EP + * NOTE: INDEX register must be set before this call + */ +static void flush(struct lh7a40x_ep *ep) +{ + DEBUG("%s, %p\n", __FUNCTION__, ep); + + switch (ep->ep_type) { + case ep_control: + /* check, by implication c.f. 15.1.2.11 */ + break; + + case ep_bulk_in: + case ep_interrupt: + /* if(csr & USB_IN_CSR1_IN_PKT_RDY) */ + usb_set(USB_IN_CSR1_FIFO_FLUSH, ep->csr1); + break; + + case ep_bulk_out: + /* if(csr & USB_OUT_CSR1_OUT_PKT_RDY) */ + usb_set(USB_OUT_CSR1_FIFO_FLUSH, ep->csr1); + break; + } +} + +/** + * lh7a40x_in_epn - handle IN interrupt + */ +static void lh7a40x_in_epn(struct lh7a40x_udc *dev, u32 ep_idx, u32 intr) +{ + u32 csr; + struct lh7a40x_ep *ep = &dev->ep[ep_idx]; + struct lh7a40x_request *req; + + usb_set_index(ep_idx); + + csr = usb_read(ep->csr1); + DEBUG("%s: %d, csr %x\n", __FUNCTION__, ep_idx, csr); + + if (csr & USB_IN_CSR1_SENT_STALL) { + DEBUG("USB_IN_CSR1_SENT_STALL\n"); + usb_set(USB_IN_CSR1_SENT_STALL /*|USB_IN_CSR1_SEND_STALL */ , + ep->csr1); + return; + } + + if (!ep->desc) { + DEBUG("%s: NO EP DESC\n", __FUNCTION__); + return; + } + + if (list_empty(&ep->queue)) + req = 0; + else + req = list_entry(ep->queue.next, struct lh7a40x_request, queue); + + DEBUG("req: %p\n", req); + + if (!req) + return; + + write_fifo(ep, req); +} + +/* ********************************************************************************************* */ +/* Bulk OUT (recv) + */ + +static void lh7a40x_out_epn(struct lh7a40x_udc *dev, u32 ep_idx, u32 intr) +{ + struct lh7a40x_ep *ep = &dev->ep[ep_idx]; + struct lh7a40x_request *req; + + DEBUG("%s: %d\n", __FUNCTION__, ep_idx); + + usb_set_index(ep_idx); + + if (ep->desc) { + u32 csr; + csr = usb_read(ep->csr1); + + while ((csr = + usb_read(ep-> + csr1)) & (USB_OUT_CSR1_OUT_PKT_RDY | + USB_OUT_CSR1_SENT_STALL)) { + DEBUG("%s: %x\n", __FUNCTION__, csr); + + if (csr & USB_OUT_CSR1_SENT_STALL) { + DEBUG("%s: stall sent, flush fifo\n", + __FUNCTION__); + /* usb_set(USB_OUT_CSR1_FIFO_FLUSH, ep->csr1); */ + flush(ep); + } else if (csr & USB_OUT_CSR1_OUT_PKT_RDY) { + if (list_empty(&ep->queue)) + req = 0; + else + req = + list_entry(ep->queue.next, + struct lh7a40x_request, + queue); + + if (!req) { + printk("%s: NULL REQ %d\n", + __FUNCTION__, ep_idx); + flush(ep); + break; + } else { + read_fifo(ep, req); + } + } + + } + + } else { + /* Throw packet away.. */ + printk("%s: No descriptor?!?\n", __FUNCTION__); + flush(ep); + } +} + +static void stop_activity(struct lh7a40x_udc *dev, + struct usb_gadget_driver *driver) +{ + int i; + + /* don't disconnect drivers more than once */ + if (dev->gadget.speed == USB_SPEED_UNKNOWN) + driver = 0; + dev->gadget.speed = USB_SPEED_UNKNOWN; + + /* prevent new request submissions, kill any outstanding requests */ + for (i = 0; i < UDC_MAX_ENDPOINTS; i++) { + struct lh7a40x_ep *ep = &dev->ep[i]; + ep->stopped = 1; + + usb_set_index(i); + nuke(ep, -ESHUTDOWN); + } + + /* report disconnect; the driver is already quiesced */ + if (driver) { + spin_unlock(&dev->lock); + driver->disconnect(&dev->gadget); + spin_lock(&dev->lock); + } + + /* re-init driver-visible data structures */ + udc_reinit(dev); +} + +/** Handle USB RESET interrupt + */ +static void lh7a40x_reset_intr(struct lh7a40x_udc *dev) +{ +#if 0 /* def CONFIG_ARCH_LH7A404 */ + /* Does not work always... */ + + DEBUG("%s: %d\n", __FUNCTION__, dev->usb_address); + + if (!dev->usb_address) { + /*usb_set(USB_RESET_IO, USB_RESET); + mdelay(5); + usb_clear(USB_RESET_IO, USB_RESET); */ + return; + } + /* Put the USB controller into reset. */ + usb_set(USB_RESET_IO, USB_RESET); + + /* Set Device ID to 0 */ + udc_set_address(dev, 0); + + /* Let PLL2 settle down */ + mdelay(5); + + /* Release the USB controller from reset */ + usb_clear(USB_RESET_IO, USB_RESET); + + /* Re-enable UDC */ + udc_enable(dev); + +#endif + dev->gadget.speed = USB_SPEED_FULL; +} + +/* + * lh7a40x usb client interrupt handler. + */ +static irqreturn_t lh7a40x_udc_irq(int irq, void *_dev, struct pt_regs *r) +{ + struct lh7a40x_udc *dev = _dev; + + DEBUG("\n\n"); + + spin_lock(&dev->lock); + + for (;;) { + u32 intr_in = usb_read(USB_IN_INT); + u32 intr_out = usb_read(USB_OUT_INT); + u32 intr_int = usb_read(USB_INT); + + /* Test also against enable bits.. (lh7a40x errata).. Sigh.. */ + u32 in_en = usb_read(USB_IN_INT_EN); + u32 out_en = usb_read(USB_OUT_INT_EN); + + if (!intr_out && !intr_in && !intr_int) + break; + + DEBUG("%s (on state %s)\n", __FUNCTION__, + state_names[dev->ep0state]); + DEBUG("intr_out = %x\n", intr_out); + DEBUG("intr_in = %x\n", intr_in); + DEBUG("intr_int = %x\n", intr_int); + + if (intr_in) { + usb_write(intr_in, USB_IN_INT); + + if ((intr_in & USB_IN_INT_EP1) + && (in_en & USB_IN_INT_EP1)) { + DEBUG("USB_IN_INT_EP1\n"); + lh7a40x_in_epn(dev, 1, intr_in); + } + if ((intr_in & USB_IN_INT_EP3) + && (in_en & USB_IN_INT_EP3)) { + DEBUG("USB_IN_INT_EP3\n"); + lh7a40x_in_epn(dev, 3, intr_in); + } + if (intr_in & USB_IN_INT_EP0) { + DEBUG("USB_IN_INT_EP0 (control)\n"); + lh7a40x_handle_ep0(dev, intr_in); + } + } + + if (intr_out) { + usb_write(intr_out, USB_OUT_INT); + + if ((intr_out & USB_OUT_INT_EP2) + && (out_en & USB_OUT_INT_EP2)) { + DEBUG("USB_OUT_INT_EP2\n"); + lh7a40x_out_epn(dev, 2, intr_out); + } + } + + if (intr_int) { + usb_write(intr_int, USB_INT); + + if (intr_int & USB_INT_RESET_INT) { + lh7a40x_reset_intr(dev); + } + + if (intr_int & USB_INT_RESUME_INT) { + DEBUG("USB resume\n"); + + if (dev->gadget.speed != USB_SPEED_UNKNOWN + && dev->driver + && dev->driver->resume + && is_usb_connected()) { + dev->driver->resume(&dev->gadget); + } + } + + if (intr_int & USB_INT_SUSPEND_INT) { + DEBUG("USB suspend%s\n", + is_usb_connected()? "" : "+disconnect"); + if (!is_usb_connected()) { + stop_activity(dev, dev->driver); + } else if (dev->gadget.speed != + USB_SPEED_UNKNOWN && dev->driver + && dev->driver->suspend) { + dev->driver->suspend(&dev->gadget); + } + } + + } + } + + spin_unlock(&dev->lock); + + return IRQ_HANDLED; +} + +static int lh7a40x_ep_enable(struct usb_ep *_ep, + const struct usb_endpoint_descriptor *desc) +{ + struct lh7a40x_ep *ep; + struct lh7a40x_udc *dev; + unsigned long flags; + + DEBUG("%s, %p\n", __FUNCTION__, _ep); + + ep = container_of(_ep, struct lh7a40x_ep, ep); + if (!_ep || !desc || ep->desc || _ep->name == ep0name + || desc->bDescriptorType != USB_DT_ENDPOINT + || ep->bEndpointAddress != desc->bEndpointAddress + || ep_maxpacket(ep) < le16_to_cpu(desc->wMaxPacketSize)) { + DEBUG("%s, bad ep or descriptor\n", __FUNCTION__); + return -EINVAL; + } + + /* xfer types must match, except that interrupt ~= bulk */ + if (ep->bmAttributes != desc->bmAttributes + && ep->bmAttributes != USB_ENDPOINT_XFER_BULK + && desc->bmAttributes != USB_ENDPOINT_XFER_INT) { + DEBUG("%s, %s type mismatch\n", __FUNCTION__, _ep->name); + return -EINVAL; + } + + /* hardware _could_ do smaller, but driver doesn't */ + if ((desc->bmAttributes == USB_ENDPOINT_XFER_BULK + && le16_to_cpu(desc->wMaxPacketSize) != ep_maxpacket(ep)) + || !desc->wMaxPacketSize) { + DEBUG("%s, bad %s maxpacket\n", __FUNCTION__, _ep->name); + return -ERANGE; + } + + dev = ep->dev; + if (!dev->driver || dev->gadget.speed == USB_SPEED_UNKNOWN) { + DEBUG("%s, bogus device state\n", __FUNCTION__); + return -ESHUTDOWN; + } + + spin_lock_irqsave(&ep->dev->lock, flags); + + ep->stopped = 0; + ep->desc = desc; + ep->pio_irqs = 0; + ep->ep.maxpacket = le16_to_cpu(desc->wMaxPacketSize); + + /* Reset halt state (does flush) */ + lh7a40x_set_halt(_ep, 0); + + spin_unlock_irqrestore(&ep->dev->lock, flags); + + DEBUG("%s: enabled %s\n", __FUNCTION__, _ep->name); + return 0; +} + +/** Disable EP + * NOTE: Sets INDEX register + */ +static int lh7a40x_ep_disable(struct usb_ep *_ep) +{ + struct lh7a40x_ep *ep; + unsigned long flags; + + DEBUG("%s, %p\n", __FUNCTION__, _ep); + + ep = container_of(_ep, struct lh7a40x_ep, ep); + if (!_ep || !ep->desc) { + DEBUG("%s, %s not enabled\n", __FUNCTION__, + _ep ? ep->ep.name : NULL); + return -EINVAL; + } + + spin_lock_irqsave(&ep->dev->lock, flags); + + usb_set_index(ep_index(ep)); + + /* Nuke all pending requests (does flush) */ + nuke(ep, -ESHUTDOWN); + + /* Disable ep IRQ */ + pio_irq_disable(ep_index(ep)); + + ep->desc = 0; + ep->stopped = 1; + + spin_unlock_irqrestore(&ep->dev->lock, flags); + + DEBUG("%s: disabled %s\n", __FUNCTION__, _ep->name); + return 0; +} + +static struct usb_request *lh7a40x_alloc_request(struct usb_ep *ep, + int gfp_flags) +{ + struct lh7a40x_request *req; + + DEBUG("%s, %p\n", __FUNCTION__, ep); + + req = kmalloc(sizeof *req, gfp_flags); + if (!req) + return 0; + + memset(req, 0, sizeof *req); + INIT_LIST_HEAD(&req->queue); + + return &req->req; +} + +static void lh7a40x_free_request(struct usb_ep *ep, struct usb_request *_req) +{ + struct lh7a40x_request *req; + + DEBUG("%s, %p\n", __FUNCTION__, ep); + + req = container_of(_req, struct lh7a40x_request, req); + WARN_ON(!list_empty(&req->queue)); + kfree(req); +} + +static void *lh7a40x_alloc_buffer(struct usb_ep *ep, unsigned bytes, + dma_addr_t * dma, int gfp_flags) +{ + char *retval; + + DEBUG("%s (%p, %d, %d)\n", __FUNCTION__, ep, bytes, gfp_flags); + + retval = kmalloc(bytes, gfp_flags & ~(__GFP_DMA | __GFP_HIGHMEM)); + if (retval) + *dma = virt_to_bus(retval); + return retval; +} + +static void lh7a40x_free_buffer(struct usb_ep *ep, void *buf, dma_addr_t dma, + unsigned bytes) +{ + DEBUG("%s, %p\n", __FUNCTION__, ep); + kfree(buf); +} + +/** Queue one request + * Kickstart transfer if needed + * NOTE: Sets INDEX register + */ +static int lh7a40x_queue(struct usb_ep *_ep, struct usb_request *_req, + int gfp_flags) +{ + struct lh7a40x_request *req; + struct lh7a40x_ep *ep; + struct lh7a40x_udc *dev; + unsigned long flags; + + DEBUG("\n\n\n%s, %p\n", __FUNCTION__, _ep); + + req = container_of(_req, struct lh7a40x_request, req); + if (unlikely + (!_req || !_req->complete || !_req->buf + || !list_empty(&req->queue))) { + DEBUG("%s, bad params\n", __FUNCTION__); + return -EINVAL; + } + + ep = container_of(_ep, struct lh7a40x_ep, ep); + if (unlikely(!_ep || (!ep->desc && ep->ep.name != ep0name))) { + DEBUG("%s, bad ep\n", __FUNCTION__); + return -EINVAL; + } + + dev = ep->dev; + if (unlikely(!dev->driver || dev->gadget.speed == USB_SPEED_UNKNOWN)) { + DEBUG("%s, bogus device state %p\n", __FUNCTION__, dev->driver); + return -ESHUTDOWN; + } + + DEBUG("%s queue req %p, len %d buf %p\n", _ep->name, _req, _req->length, + _req->buf); + + spin_lock_irqsave(&dev->lock, flags); + + _req->status = -EINPROGRESS; + _req->actual = 0; + + /* kickstart this i/o queue? */ + DEBUG("Add to %d Q %d %d\n", ep_index(ep), list_empty(&ep->queue), + ep->stopped); + if (list_empty(&ep->queue) && likely(!ep->stopped)) { + u32 csr; + + if (unlikely(ep_index(ep) == 0)) { + /* EP0 */ + list_add_tail(&req->queue, &ep->queue); + lh7a40x_ep0_kick(dev, ep); + req = 0; + } else if (ep_is_in(ep)) { + /* EP1 & EP3 */ + usb_set_index(ep_index(ep)); + csr = usb_read(ep->csr1); + pio_irq_enable(ep_index(ep)); + if ((csr & USB_IN_CSR1_FIFO_NOT_EMPTY) == 0) { + if (write_fifo(ep, req) == 1) + req = 0; + } + } else { + /* EP2 */ + usb_set_index(ep_index(ep)); + csr = usb_read(ep->csr1); + pio_irq_enable(ep_index(ep)); + if (!(csr & USB_OUT_CSR1_FIFO_FULL)) { + if (read_fifo(ep, req) == 1) + req = 0; + } + } + } + + /* pio or dma irq handler advances the queue. */ + if (likely(req != 0)) + list_add_tail(&req->queue, &ep->queue); + + spin_unlock_irqrestore(&dev->lock, flags); + + return 0; +} + +/* dequeue JUST ONE request */ +static int lh7a40x_dequeue(struct usb_ep *_ep, struct usb_request *_req) +{ + struct lh7a40x_ep *ep; + struct lh7a40x_request *req; + unsigned long flags; + + DEBUG("%s, %p\n", __FUNCTION__, _ep); + + ep = container_of(_ep, struct lh7a40x_ep, ep); + if (!_ep || ep->ep.name == ep0name) + return -EINVAL; + + spin_lock_irqsave(&ep->dev->lock, flags); + + /* make sure it's actually queued on this endpoint */ + list_for_each_entry(req, &ep->queue, queue) { + if (&req->req == _req) + break; + } + if (&req->req != _req) { + spin_unlock_irqrestore(&ep->dev->lock, flags); + return -EINVAL; + } + + done(ep, req, -ECONNRESET); + + spin_unlock_irqrestore(&ep->dev->lock, flags); + return 0; +} + +/** Halt specific EP + * Return 0 if success + * NOTE: Sets INDEX register to EP ! + */ +static int lh7a40x_set_halt(struct usb_ep *_ep, int value) +{ + struct lh7a40x_ep *ep; + unsigned long flags; + + ep = container_of(_ep, struct lh7a40x_ep, ep); + if (unlikely(!_ep || (!ep->desc && ep->ep.name != ep0name))) { + DEBUG("%s, bad ep\n", __FUNCTION__); + return -EINVAL; + } + + usb_set_index(ep_index(ep)); + + DEBUG("%s, ep %d, val %d\n", __FUNCTION__, ep_index(ep), value); + + spin_lock_irqsave(&ep->dev->lock, flags); + + if (ep_index(ep) == 0) { + /* EP0 */ + usb_set(EP0_SEND_STALL, ep->csr1); + } else if (ep_is_in(ep)) { + u32 csr = usb_read(ep->csr1); + if (value && ((csr & USB_IN_CSR1_FIFO_NOT_EMPTY) + || !list_empty(&ep->queue))) { + /* + * Attempts to halt IN endpoints will fail (returning -EAGAIN) + * if any transfer requests are still queued, or if the controller + * FIFO still holds bytes that the host hasnÂ’t collected. + */ + spin_unlock_irqrestore(&ep->dev->lock, flags); + DEBUG + ("Attempt to halt IN endpoint failed (returning -EAGAIN) %d %d\n", + (csr & USB_IN_CSR1_FIFO_NOT_EMPTY), + !list_empty(&ep->queue)); + return -EAGAIN; + } + flush(ep); + if (value) + usb_set(USB_IN_CSR1_SEND_STALL, ep->csr1); + else { + usb_clear(USB_IN_CSR1_SEND_STALL, ep->csr1); + usb_set(USB_IN_CSR1_CLR_DATA_TOGGLE, ep->csr1); + } + + } else { + + flush(ep); + if (value) + usb_set(USB_OUT_CSR1_SEND_STALL, ep->csr1); + else { + usb_clear(USB_OUT_CSR1_SEND_STALL, ep->csr1); + usb_set(USB_OUT_CSR1_CLR_DATA_REG, ep->csr1); + } + } + + if (value) { + ep->stopped = 1; + } else { + ep->stopped = 0; + } + + spin_unlock_irqrestore(&ep->dev->lock, flags); + + DEBUG("%s %s halted\n", _ep->name, value == 0 ? "NOT" : "IS"); + + return 0; +} + +/** Return bytes in EP FIFO + * NOTE: Sets INDEX register to EP + */ +static int lh7a40x_fifo_status(struct usb_ep *_ep) +{ + u32 csr; + int count = 0; + struct lh7a40x_ep *ep; + + ep = container_of(_ep, struct lh7a40x_ep, ep); + if (!_ep) { + DEBUG("%s, bad ep\n", __FUNCTION__); + return -ENODEV; + } + + DEBUG("%s, %d\n", __FUNCTION__, ep_index(ep)); + + /* LPD can't report unclaimed bytes from IN fifos */ + if (ep_is_in(ep)) + return -EOPNOTSUPP; + + usb_set_index(ep_index(ep)); + + csr = usb_read(ep->csr1); + if (ep->dev->gadget.speed != USB_SPEED_UNKNOWN || + csr & USB_OUT_CSR1_OUT_PKT_RDY) { + count = usb_read(USB_OUT_FIFO_WC1); + } + + return count; +} + +/** Flush EP FIFO + * NOTE: Sets INDEX register to EP + */ +static void lh7a40x_fifo_flush(struct usb_ep *_ep) +{ + struct lh7a40x_ep *ep; + + ep = container_of(_ep, struct lh7a40x_ep, ep); + if (unlikely(!_ep || (!ep->desc && ep->ep.name != ep0name))) { + DEBUG("%s, bad ep\n", __FUNCTION__); + return; + } + + usb_set_index(ep_index(ep)); + flush(ep); +} + +/****************************************************************/ +/* End Point 0 related functions */ +/****************************************************************/ + +/* return: 0 = still running, 1 = completed, negative = errno */ +static int write_fifo_ep0(struct lh7a40x_ep *ep, struct lh7a40x_request *req) +{ + u32 max; + unsigned count; + int is_last; + + max = ep_maxpacket(ep); + + DEBUG_EP0("%s\n", __FUNCTION__); + + count = write_packet(ep, req, max); + + /* last packet is usually short (or a zlp) */ + if (unlikely(count != max)) + is_last = 1; + else { + if (likely(req->req.length != req->req.actual) || req->req.zero) + is_last = 0; + else + is_last = 1; + } + + DEBUG_EP0("%s: wrote %s %d bytes%s %d left %p\n", __FUNCTION__, + ep->ep.name, count, + is_last ? "/L" : "", req->req.length - req->req.actual, req); + + /* requests complete when all IN data is in the FIFO */ + if (is_last) { + done(ep, req, 0); + return 1; + } + + return 0; +} + +static __inline__ int lh7a40x_fifo_read(struct lh7a40x_ep *ep, + unsigned char *cp, int max) +{ + int bytes; + int count = usb_read(USB_OUT_FIFO_WC1); + volatile u32 *fifo = (volatile u32 *)ep->fifo; + + if (count > max) + count = max; + bytes = count; + while (count--) + *cp++ = *fifo & 0xFF; + return bytes; +} + +static __inline__ void lh7a40x_fifo_write(struct lh7a40x_ep *ep, + unsigned char *cp, int count) +{ + volatile u32 *fifo = (volatile u32 *)ep->fifo; + DEBUG_EP0("fifo_write: %d %d\n", ep_index(ep), count); + while (count--) + *fifo = *cp++; +} + +static int read_fifo_ep0(struct lh7a40x_ep *ep, struct lh7a40x_request *req) +{ + u32 csr; + u8 *buf; + unsigned bufferspace, count, is_short; + volatile u32 *fifo = (volatile u32 *)ep->fifo; + + DEBUG_EP0("%s\n", __FUNCTION__); + + csr = usb_read(USB_EP0_CSR); + if (!(csr & USB_OUT_CSR1_OUT_PKT_RDY)) + return 0; + + buf = req->req.buf + req->req.actual; + prefetchw(buf); + bufferspace = req->req.length - req->req.actual; + + /* read all bytes from this packet */ + if (likely(csr & EP0_OUT_PKT_RDY)) { + count = usb_read(USB_OUT_FIFO_WC1); + req->req.actual += min(count, bufferspace); + } else /* zlp */ + count = 0; + + is_short = (count < ep->ep.maxpacket); + DEBUG_EP0("read %s %02x, %d bytes%s req %p %d/%d\n", + ep->ep.name, csr, count, + is_short ? "/S" : "", req, req->req.actual, req->req.length); + + while (likely(count-- != 0)) { + u8 byte = (u8) (*fifo & 0xff); + + if (unlikely(bufferspace == 0)) { + /* this happens when the driver's buffer + * is smaller than what the host sent. + * discard the extra data. + */ + if (req->req.status != -EOVERFLOW) + DEBUG_EP0("%s overflow %d\n", ep->ep.name, + count); + req->req.status = -EOVERFLOW; + } else { + *buf++ = byte; + bufferspace--; + } + } + + /* completion */ + if (is_short || req->req.actual == req->req.length) { + done(ep, req, 0); + return 1; + } + + /* finished that packet. the next one may be waiting... */ + return 0; +} + +/** + * udc_set_address - set the USB address for this device + * @address: + * + * Called from control endpoint function after it decodes a set address setup packet. + */ +static void udc_set_address(struct lh7a40x_udc *dev, unsigned char address) +{ + DEBUG_EP0("%s: %d\n", __FUNCTION__, address); + /* c.f. 15.1.2.2 Table 15-4 address will be used after DATA_END is set */ + dev->usb_address = address; + usb_set((address & USB_FA_FUNCTION_ADDR), USB_FA); + usb_set(USB_FA_ADDR_UPDATE | (address & USB_FA_FUNCTION_ADDR), USB_FA); + /* usb_read(USB_FA); */ +} + +/* + * DATA_STATE_RECV (OUT_PKT_RDY) + * - if error + * set EP0_CLR_OUT | EP0_DATA_END | EP0_SEND_STALL bits + * - else + * set EP0_CLR_OUT bit + if last set EP0_DATA_END bit + */ +static void lh7a40x_ep0_out(struct lh7a40x_udc *dev, u32 csr) +{ + struct lh7a40x_request *req; + struct lh7a40x_ep *ep = &dev->ep[0]; + int ret; + + DEBUG_EP0("%s: %x\n", __FUNCTION__, csr); + + if (list_empty(&ep->queue)) + req = 0; + else + req = list_entry(ep->queue.next, struct lh7a40x_request, queue); + + if (req) { + + if (req->req.length == 0) { + DEBUG_EP0("ZERO LENGTH OUT!\n"); + usb_set((EP0_CLR_OUT | EP0_DATA_END), USB_EP0_CSR); + dev->ep0state = WAIT_FOR_SETUP; + return; + } + ret = read_fifo_ep0(ep, req); + if (ret) { + /* Done! */ + DEBUG_EP0("%s: finished, waiting for status\n", + __FUNCTION__); + + usb_set((EP0_CLR_OUT | EP0_DATA_END), USB_EP0_CSR); + dev->ep0state = WAIT_FOR_SETUP; + } else { + /* Not done yet.. */ + DEBUG_EP0("%s: not finished\n", __FUNCTION__); + usb_set(EP0_CLR_OUT, USB_EP0_CSR); + } + } else { + DEBUG_EP0("NO REQ??!\n"); + } +} + +/* + * DATA_STATE_XMIT + */ +static int lh7a40x_ep0_in(struct lh7a40x_udc *dev, u32 csr) +{ + struct lh7a40x_request *req; + struct lh7a40x_ep *ep = &dev->ep[0]; + int ret, need_zlp = 0; + + DEBUG_EP0("%s: %x\n", __FUNCTION__, csr); + + if (list_empty(&ep->queue)) + req = 0; + else + req = list_entry(ep->queue.next, struct lh7a40x_request, queue); + + if (!req) { + DEBUG_EP0("%s: NULL REQ\n", __FUNCTION__); + return 0; + } + + if (req->req.length == 0) { + + usb_set((EP0_IN_PKT_RDY | EP0_DATA_END), USB_EP0_CSR); + dev->ep0state = WAIT_FOR_SETUP; + return 1; + } + + if (req->req.length - req->req.actual == EP0_PACKETSIZE) { + /* Next write will end with the packet size, */ + /* so we need Zero-length-packet */ + need_zlp = 1; + } + + ret = write_fifo_ep0(ep, req); + + if (ret == 1 && !need_zlp) { + /* Last packet */ + DEBUG_EP0("%s: finished, waiting for status\n", __FUNCTION__); + + usb_set((EP0_IN_PKT_RDY | EP0_DATA_END), USB_EP0_CSR); + dev->ep0state = WAIT_FOR_SETUP; + } else { + DEBUG_EP0("%s: not finished\n", __FUNCTION__); + usb_set(EP0_IN_PKT_RDY, USB_EP0_CSR); + } + + if (need_zlp) { + DEBUG_EP0("%s: Need ZLP!\n", __FUNCTION__); + usb_set(EP0_IN_PKT_RDY, USB_EP0_CSR); + dev->ep0state = DATA_STATE_NEED_ZLP; + } + + return 1; +} + +static int lh7a40x_handle_get_status(struct lh7a40x_udc *dev, + struct usb_ctrlrequest *ctrl) +{ + struct lh7a40x_ep *ep0 = &dev->ep[0]; + struct lh7a40x_ep *qep; + int reqtype = (ctrl->bRequestType & USB_RECIP_MASK); + u16 val = 0; + + if (reqtype == USB_RECIP_INTERFACE) { + /* This is not supported. + * And according to the USB spec, this one does nothing.. + * Just return 0 + */ + DEBUG_SETUP("GET_STATUS: USB_RECIP_INTERFACE\n"); + } else if (reqtype == USB_RECIP_DEVICE) { + DEBUG_SETUP("GET_STATUS: USB_RECIP_DEVICE\n"); + val |= (1 << 0); /* Self powered */ + /*val |= (1<<1); *//* Remote wakeup */ + } else if (reqtype == USB_RECIP_ENDPOINT) { + int ep_num = (ctrl->wIndex & ~USB_DIR_IN); + + DEBUG_SETUP + ("GET_STATUS: USB_RECIP_ENDPOINT (%d), ctrl->wLength = %d\n", + ep_num, ctrl->wLength); + + if (ctrl->wLength > 2 || ep_num > 3) + return -EOPNOTSUPP; + + qep = &dev->ep[ep_num]; + if (ep_is_in(qep) != ((ctrl->wIndex & USB_DIR_IN) ? 1 : 0) + && ep_index(qep) != 0) { + return -EOPNOTSUPP; + } + + usb_set_index(ep_index(qep)); + + /* Return status on next IN token */ + switch (qep->ep_type) { + case ep_control: + val = + (usb_read(qep->csr1) & EP0_SEND_STALL) == + EP0_SEND_STALL; + break; + case ep_bulk_in: + case ep_interrupt: + val = + (usb_read(qep->csr1) & USB_IN_CSR1_SEND_STALL) == + USB_IN_CSR1_SEND_STALL; + break; + case ep_bulk_out: + val = + (usb_read(qep->csr1) & USB_OUT_CSR1_SEND_STALL) == + USB_OUT_CSR1_SEND_STALL; + break; + } + + /* Back to EP0 index */ + usb_set_index(0); + + DEBUG_SETUP("GET_STATUS, ep: %d (%x), val = %d\n", ep_num, + ctrl->wIndex, val); + } else { + DEBUG_SETUP("Unknown REQ TYPE: %d\n", reqtype); + return -EOPNOTSUPP; + } + + /* Clear "out packet ready" */ + usb_set((EP0_CLR_OUT), USB_EP0_CSR); + /* Put status to FIFO */ + lh7a40x_fifo_write(ep0, (u8 *) & val, sizeof(val)); + /* Issue "In packet ready" */ + usb_set((EP0_IN_PKT_RDY | EP0_DATA_END), USB_EP0_CSR); + + return 0; +} + +/* + * WAIT_FOR_SETUP (OUT_PKT_RDY) + * - read data packet from EP0 FIFO + * - decode command + * - if error + * set EP0_CLR_OUT | EP0_DATA_END | EP0_SEND_STALL bits + * - else + * set EP0_CLR_OUT | EP0_DATA_END bits + */ +static void lh7a40x_ep0_setup(struct lh7a40x_udc *dev, u32 csr) +{ + struct lh7a40x_ep *ep = &dev->ep[0]; + struct usb_ctrlrequest ctrl; + int i, bytes, is_in; + + DEBUG_SETUP("%s: %x\n", __FUNCTION__, csr); + + /* Nuke all previous transfers */ + nuke(ep, -EPROTO); + + /* read control req from fifo (8 bytes) */ + bytes = lh7a40x_fifo_read(ep, (unsigned char *)&ctrl, 8); + + DEBUG_SETUP("Read CTRL REQ %d bytes\n", bytes); + DEBUG_SETUP("CTRL.bRequestType = %d (is_in %d)\n", ctrl.bRequestType, + ctrl.bRequestType == USB_DIR_IN); + DEBUG_SETUP("CTRL.bRequest = %d\n", ctrl.bRequest); + DEBUG_SETUP("CTRL.wLength = %d\n", ctrl.wLength); + DEBUG_SETUP("CTRL.wValue = %d (%d)\n", ctrl.wValue, ctrl.wValue >> 8); + DEBUG_SETUP("CTRL.wIndex = %d\n", ctrl.wIndex); + + /* Set direction of EP0 */ + if (likely(ctrl.bRequestType & USB_DIR_IN)) { + ep->bEndpointAddress |= USB_DIR_IN; + is_in = 1; + } else { + ep->bEndpointAddress &= ~USB_DIR_IN; + is_in = 0; + } + + dev->req_pending = 1; + + /* Handle some SETUP packets ourselves */ + switch (ctrl.bRequest) { + case USB_REQ_SET_ADDRESS: + if (ctrl.bRequestType != (USB_TYPE_STANDARD | USB_RECIP_DEVICE)) + break; + + DEBUG_SETUP("USB_REQ_SET_ADDRESS (%d)\n", ctrl.wValue); + udc_set_address(dev, ctrl.wValue); + usb_set((EP0_CLR_OUT | EP0_DATA_END), USB_EP0_CSR); + return; + + case USB_REQ_GET_STATUS:{ + if (lh7a40x_handle_get_status(dev, &ctrl) == 0) + return; + + case USB_REQ_CLEAR_FEATURE: + case USB_REQ_SET_FEATURE: + if (ctrl.bRequestType == USB_RECIP_ENDPOINT) { + struct lh7a40x_ep *qep; + int ep_num = (ctrl.wIndex & 0x0f); + + /* Support only HALT feature */ + if (ctrl.wValue != 0 || ctrl.wLength != 0 + || ep_num > 3 || ep_num < 1) + break; + + qep = &dev->ep[ep_num]; + if (ctrl.bRequest == USB_REQ_SET_FEATURE) { + DEBUG_SETUP("SET_FEATURE (%d)\n", + ep_num); + lh7a40x_set_halt(&qep->ep, 1); + } else { + DEBUG_SETUP("CLR_FEATURE (%d)\n", + ep_num); + lh7a40x_set_halt(&qep->ep, 0); + } + usb_set_index(0); + + /* Reply with a ZLP on next IN token */ + usb_set((EP0_CLR_OUT | EP0_DATA_END), + USB_EP0_CSR); + return; + } + break; + } + + default: + break; + } + + if (likely(dev->driver)) { + /* device-2-host (IN) or no data setup command, process immediately */ + spin_unlock(&dev->lock); + i = dev->driver->setup(&dev->gadget, &ctrl); + spin_lock(&dev->lock); + + if (i < 0) { + /* setup processing failed, force stall */ + DEBUG_SETUP + (" --> ERROR: gadget setup FAILED (stalling), setup returned %d\n", + i); + usb_set_index(0); + usb_set((EP0_CLR_OUT | EP0_DATA_END | EP0_SEND_STALL), + USB_EP0_CSR); + + /* ep->stopped = 1; */ + dev->ep0state = WAIT_FOR_SETUP; + } + } +} + +/* + * DATA_STATE_NEED_ZLP + */ +static void lh7a40x_ep0_in_zlp(struct lh7a40x_udc *dev, u32 csr) +{ + DEBUG_EP0("%s: %x\n", __FUNCTION__, csr); + + /* c.f. Table 15-14 */ + usb_set((EP0_IN_PKT_RDY | EP0_DATA_END), USB_EP0_CSR); + dev->ep0state = WAIT_FOR_SETUP; +} + +/* + * handle ep0 interrupt + */ +static void lh7a40x_handle_ep0(struct lh7a40x_udc *dev, u32 intr) +{ + struct lh7a40x_ep *ep = &dev->ep[0]; + u32 csr; + + /* Set index 0 */ + usb_set_index(0); + csr = usb_read(USB_EP0_CSR); + + DEBUG_EP0("%s: csr = %x\n", __FUNCTION__, csr); + + /* + * For overview of what we should be doing see c.f. Chapter 18.1.2.4 + * We will follow that outline here modified by our own global state + * indication which provides hints as to what we think should be + * happening.. + */ + + /* + * if SENT_STALL is set + * - clear the SENT_STALL bit + */ + if (csr & EP0_SENT_STALL) { + DEBUG_EP0("%s: EP0_SENT_STALL is set: %x\n", __FUNCTION__, csr); + usb_clear((EP0_SENT_STALL | EP0_SEND_STALL), USB_EP0_CSR); + nuke(ep, -ECONNABORTED); + dev->ep0state = WAIT_FOR_SETUP; + return; + } + + /* + * if a transfer is in progress && IN_PKT_RDY and OUT_PKT_RDY are clear + * - fill EP0 FIFO + * - if last packet + * - set IN_PKT_RDY | DATA_END + * - else + * set IN_PKT_RDY + */ + if (!(csr & (EP0_IN_PKT_RDY | EP0_OUT_PKT_RDY))) { + DEBUG_EP0("%s: IN_PKT_RDY and OUT_PKT_RDY are clear\n", + __FUNCTION__); + + switch (dev->ep0state) { + case DATA_STATE_XMIT: + DEBUG_EP0("continue with DATA_STATE_XMIT\n"); + lh7a40x_ep0_in(dev, csr); + return; + case DATA_STATE_NEED_ZLP: + DEBUG_EP0("continue with DATA_STATE_NEED_ZLP\n"); + lh7a40x_ep0_in_zlp(dev, csr); + return; + default: + /* Stall? */ + DEBUG_EP0("Odd state!! state = %s\n", + state_names[dev->ep0state]); + dev->ep0state = WAIT_FOR_SETUP; + /* nuke(ep, 0); */ + /* usb_set(EP0_SEND_STALL, ep->csr1); */ + break; + } + } + + /* + * if SETUP_END is set + * - abort the last transfer + * - set SERVICED_SETUP_END_BIT + */ + if (csr & EP0_SETUP_END) { + DEBUG_EP0("%s: EP0_SETUP_END is set: %x\n", __FUNCTION__, csr); + + usb_set(EP0_CLR_SETUP_END, USB_EP0_CSR); + + nuke(ep, 0); + dev->ep0state = WAIT_FOR_SETUP; + } + + /* + * if EP0_OUT_PKT_RDY is set + * - read data packet from EP0 FIFO + * - decode command + * - if error + * set SERVICED_OUT_PKT_RDY | DATA_END bits | SEND_STALL + * - else + * set SERVICED_OUT_PKT_RDY | DATA_END bits + */ + if (csr & EP0_OUT_PKT_RDY) { + + DEBUG_EP0("%s: EP0_OUT_PKT_RDY is set: %x\n", __FUNCTION__, + csr); + + switch (dev->ep0state) { + case WAIT_FOR_SETUP: + DEBUG_EP0("WAIT_FOR_SETUP\n"); + lh7a40x_ep0_setup(dev, csr); + break; + + case DATA_STATE_RECV: + DEBUG_EP0("DATA_STATE_RECV\n"); + lh7a40x_ep0_out(dev, csr); + break; + + default: + /* send stall? */ + DEBUG_EP0("strange state!! 2. send stall? state = %d\n", + dev->ep0state); + break; + } + } +} + +static void lh7a40x_ep0_kick(struct lh7a40x_udc *dev, struct lh7a40x_ep *ep) +{ + u32 csr; + + usb_set_index(0); + csr = usb_read(USB_EP0_CSR); + + DEBUG_EP0("%s: %x\n", __FUNCTION__, csr); + + /* Clear "out packet ready" */ + usb_set(EP0_CLR_OUT, USB_EP0_CSR); + + if (ep_is_in(ep)) { + dev->ep0state = DATA_STATE_XMIT; + lh7a40x_ep0_in(dev, csr); + } else { + dev->ep0state = DATA_STATE_RECV; + lh7a40x_ep0_out(dev, csr); + } +} + +/* --------------------------------------------------------------------------- + * device-scoped parts of the api to the usb controller hardware + * --------------------------------------------------------------------------- + */ + +static int lh7a40x_udc_get_frame(struct usb_gadget *_gadget) +{ + u32 frame1 = usb_read(USB_FRM_NUM1); /* Least significant 8 bits */ + u32 frame2 = usb_read(USB_FRM_NUM2); /* Most significant 3 bits */ + DEBUG("%s, %p\n", __FUNCTION__, _gadget); + return ((frame2 & 0x07) << 8) | (frame1 & 0xff); +} + +static int lh7a40x_udc_wakeup(struct usb_gadget *_gadget) +{ + /* host may not have enabled remote wakeup */ + /*if ((UDCCS0 & UDCCS0_DRWF) == 0) + return -EHOSTUNREACH; + udc_set_mask_UDCCR(UDCCR_RSM); */ + return -ENOTSUPP; +} + +static const struct usb_gadget_ops lh7a40x_udc_ops = { + .get_frame = lh7a40x_udc_get_frame, + .wakeup = lh7a40x_udc_wakeup, + /* current versions must always be self-powered */ +}; + +static void nop_release(struct device *dev) +{ + DEBUG("%s %s\n", __FUNCTION__, dev->bus_id); +} + +static struct lh7a40x_udc memory = { + .usb_address = 0, + + .gadget = { + .ops = &lh7a40x_udc_ops, + .ep0 = &memory.ep[0].ep, + .name = driver_name, + .dev = { + .bus_id = "gadget", + .release = nop_release, + }, + }, + + /* control endpoint */ + .ep[0] = { + .ep = { + .name = ep0name, + .ops = &lh7a40x_ep_ops, + .maxpacket = EP0_PACKETSIZE, + }, + .dev = &memory, + + .bEndpointAddress = 0, + .bmAttributes = 0, + + .ep_type = ep_control, + .fifo = io_p2v(USB_EP0_FIFO), + .csr1 = USB_EP0_CSR, + .csr2 = USB_EP0_CSR, + }, + + /* first group of endpoints */ + .ep[1] = { + .ep = { + .name = "ep1in-bulk", + .ops = &lh7a40x_ep_ops, + .maxpacket = 64, + }, + .dev = &memory, + + .bEndpointAddress = USB_DIR_IN | 1, + .bmAttributes = USB_ENDPOINT_XFER_BULK, + + .ep_type = ep_bulk_in, + .fifo = io_p2v(USB_EP1_FIFO), + .csr1 = USB_IN_CSR1, + .csr2 = USB_IN_CSR2, + }, + + .ep[2] = { + .ep = { + .name = "ep2out-bulk", + .ops = &lh7a40x_ep_ops, + .maxpacket = 64, + }, + .dev = &memory, + + .bEndpointAddress = 2, + .bmAttributes = USB_ENDPOINT_XFER_BULK, + + .ep_type = ep_bulk_out, + .fifo = io_p2v(USB_EP2_FIFO), + .csr1 = USB_OUT_CSR1, + .csr2 = USB_OUT_CSR2, + }, + + .ep[3] = { + .ep = { + .name = "ep3in-int", + .ops = &lh7a40x_ep_ops, + .maxpacket = 64, + }, + .dev = &memory, + + .bEndpointAddress = USB_DIR_IN | 3, + .bmAttributes = USB_ENDPOINT_XFER_INT, + + .ep_type = ep_interrupt, + .fifo = io_p2v(USB_EP3_FIFO), + .csr1 = USB_IN_CSR1, + .csr2 = USB_IN_CSR2, + }, +}; + +/* + * probe - binds to the platform device + */ +static int lh7a40x_udc_probe(struct device *_dev) +{ + struct lh7a40x_udc *dev = &memory; + int retval; + + DEBUG("%s: %p\n", __FUNCTION__, _dev); + + spin_lock_init(&dev->lock); + dev->dev = _dev; + + device_initialize(&dev->gadget.dev); + dev->gadget.dev.parent = _dev; + + the_controller = dev; + dev_set_drvdata(_dev, dev); + + udc_disable(dev); + udc_reinit(dev); + + /* irq setup after old hardware state is cleaned up */ + retval = + request_irq(IRQ_USBINTR, lh7a40x_udc_irq, SA_INTERRUPT, driver_name, + dev); + if (retval != 0) { + DEBUG(KERN_ERR "%s: can't get irq %i, err %d\n", driver_name, + IRQ_USBINTR, retval); + return -EBUSY; + } + + create_proc_files(); + + return retval; +} + +static int lh7a40x_udc_remove(struct device *_dev) +{ + struct lh7a40x_udc *dev = _dev->driver_data; + + DEBUG("%s: %p\n", __FUNCTION__, dev); + + udc_disable(dev); + remove_proc_files(); + usb_gadget_unregister_driver(dev->driver); + + free_irq(IRQ_USBINTR, dev); + + dev_set_drvdata(_dev, 0); + + the_controller = 0; + + return 0; +} + +/*-------------------------------------------------------------------------*/ + +static struct device_driver udc_driver = { + .name = (char *)driver_name, + .bus = &platform_bus_type, + .probe = lh7a40x_udc_probe, + .remove = lh7a40x_udc_remove + /* FIXME power management support */ + /* .suspend = ... disable UDC */ + /* .resume = ... re-enable UDC */ +}; + +static int __init udc_init(void) +{ + DEBUG("%s: %s version %s\n", __FUNCTION__, driver_name, DRIVER_VERSION); + return driver_register(&udc_driver); +} + +static void __exit udc_exit(void) +{ + driver_unregister(&udc_driver); +} + +module_init(udc_init); +module_exit(udc_exit); + +MODULE_DESCRIPTION(DRIVER_DESC); +MODULE_AUTHOR("Mikko Lahteenmaki, Bo Henriksen"); +MODULE_LICENSE("GPL"); diff --git a/drivers/usb/gadget/lh7a40x_udc.h b/drivers/usb/gadget/lh7a40x_udc.h new file mode 100644 index 000000000..1bb455c04 --- /dev/null +++ b/drivers/usb/gadget/lh7a40x_udc.h @@ -0,0 +1,261 @@ +/* + * linux/drivers/usb/gadget/lh7a40x_udc.h + * Sharp LH7A40x on-chip full speed USB device controllers + * + * Copyright (C) 2004 Mikko Lahteenmaki, Nordic ID + * Copyright (C) 2004 Bo Henriksen, Nordic ID + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the 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 __LH7A40X_H_ +#define __LH7A40X_H_ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +/* + * Memory map + */ + +#define USB_FA 0x80000200 // function address register +#define USB_PM 0x80000204 // power management register + +#define USB_IN_INT 0x80000208 // IN interrupt register bank (EP0-EP3) +#define USB_OUT_INT 0x80000210 // OUT interrupt register bank (EP2) +#define USB_INT 0x80000218 // interrupt register bank + +#define USB_IN_INT_EN 0x8000021C // IN interrupt enable register bank +#define USB_OUT_INT_EN 0x80000224 // OUT interrupt enable register bank +#define USB_INT_EN 0x8000022C // USB interrupt enable register bank + +#define USB_FRM_NUM1 0x80000230 // Frame number1 register +#define USB_FRM_NUM2 0x80000234 // Frame number2 register +#define USB_INDEX 0x80000238 // index register + +#define USB_IN_MAXP 0x80000240 // IN MAXP register +#define USB_IN_CSR1 0x80000244 // IN CSR1 register/EP0 CSR register +#define USB_EP0_CSR 0x80000244 // IN CSR1 register/EP0 CSR register +#define USB_IN_CSR2 0x80000248 // IN CSR2 register +#define USB_OUT_MAXP 0x8000024C // OUT MAXP register + +#define USB_OUT_CSR1 0x80000250 // OUT CSR1 register +#define USB_OUT_CSR2 0x80000254 // OUT CSR2 register +#define USB_OUT_FIFO_WC1 0x80000258 // OUT FIFO write count1 register +#define USB_OUT_FIFO_WC2 0x8000025C // OUT FIFO write count2 register + +#define USB_RESET 0x8000044C // USB reset register + +#define USB_EP0_FIFO 0x80000280 +#define USB_EP1_FIFO 0x80000284 +#define USB_EP2_FIFO 0x80000288 +#define USB_EP3_FIFO 0x8000028c + +/* + * USB reset register + */ +#define USB_RESET_APB (1<<1) //resets USB APB control side WRITE +#define USB_RESET_IO (1<<0) //resets USB IO side WRITE + +/* + * USB function address register + */ +#define USB_FA_ADDR_UPDATE (1<<7) +#define USB_FA_FUNCTION_ADDR (0x7F) + +/* + * Power Management register + */ +#define PM_USB_DCP (1<<5) +#define PM_USB_ENABLE (1<<4) +#define PM_USB_RESET (1<<3) +#define PM_UC_RESUME (1<<2) +#define PM_SUSPEND_MODE (1<<1) +#define PM_ENABLE_SUSPEND (1<<0) + +/* + * IN interrupt register + */ +#define USB_IN_INT_EP3 (1<<3) +#define USB_IN_INT_EP1 (1<<1) +#define USB_IN_INT_EP0 (1<<0) + +/* + * OUT interrupt register + */ +#define USB_OUT_INT_EP2 (1<<2) + +/* + * USB interrupt register + */ +#define USB_INT_RESET_INT (1<<2) +#define USB_INT_RESUME_INT (1<<1) +#define USB_INT_SUSPEND_INT (1<<0) + +/* + * USB interrupt enable register + */ +#define USB_INT_EN_USB_RESET_INTER (1<<2) +#define USB_INT_EN_RESUME_INTER (1<<1) +#define USB_INT_EN_SUSPEND_INTER (1<<0) + +/* + * INCSR1 register + */ +#define USB_IN_CSR1_CLR_DATA_TOGGLE (1<<6) +#define USB_IN_CSR1_SENT_STALL (1<<5) +#define USB_IN_CSR1_SEND_STALL (1<<4) +#define USB_IN_CSR1_FIFO_FLUSH (1<<3) +#define USB_IN_CSR1_FIFO_NOT_EMPTY (1<<1) +#define USB_IN_CSR1_IN_PKT_RDY (1<<0) + +/* + * INCSR2 register + */ +#define USB_IN_CSR2_AUTO_SET (1<<7) +#define USB_IN_CSR2_USB_DMA_EN (1<<4) + +/* + * OUT CSR1 register + */ +#define USB_OUT_CSR1_CLR_DATA_REG (1<<7) +#define USB_OUT_CSR1_SENT_STALL (1<<6) +#define USB_OUT_CSR1_SEND_STALL (1<<5) +#define USB_OUT_CSR1_FIFO_FLUSH (1<<4) +#define USB_OUT_CSR1_FIFO_FULL (1<<1) +#define USB_OUT_CSR1_OUT_PKT_RDY (1<<0) + +/* + * OUT CSR2 register + */ +#define USB_OUT_CSR2_AUTO_CLR (1<<7) +#define USB_OUT_CSR2_USB_DMA_EN (1<<4) + +/* + * EP0 CSR + */ +#define EP0_CLR_SETUP_END (1<<7) /* Clear "Setup Ends" Bit (w) */ +#define EP0_CLR_OUT (1<<6) /* Clear "Out packet ready" Bit (w) */ +#define EP0_SEND_STALL (1<<5) /* Send STALL Handshake (rw) */ +#define EP0_SETUP_END (1<<4) /* Setup Ends (r) */ + +#define EP0_DATA_END (1<<3) /* Data end (rw) */ +#define EP0_SENT_STALL (1<<2) /* Sent Stall Handshake (r) */ +#define EP0_IN_PKT_RDY (1<<1) /* In packet ready (rw) */ +#define EP0_OUT_PKT_RDY (1<<0) /* Out packet ready (r) */ + +/* general CSR */ +#define OUT_PKT_RDY (1<<0) +#define IN_PKT_RDY (1<<0) + +/* + * IN/OUT MAXP register + */ +#define USB_OUT_MAXP_MAXP (0xF) +#define USB_IN_MAXP_MAXP (0xF) + +// Max packet size +//#define EP0_PACKETSIZE 0x10 +#define EP0_PACKETSIZE 0x8 +#define EP0_MAXPACKETSIZE 0x10 + +#define UDC_MAX_ENDPOINTS 4 + +#define WAIT_FOR_SETUP 0 +#define DATA_STATE_XMIT 1 +#define DATA_STATE_NEED_ZLP 2 +#define WAIT_FOR_OUT_STATUS 3 +#define DATA_STATE_RECV 4 + +/* ********************************************************************************************* */ +/* IO + */ + +typedef enum ep_type { + ep_control, ep_bulk_in, ep_bulk_out, ep_interrupt +} ep_type_t; + +struct lh7a40x_ep { + struct usb_ep ep; + struct lh7a40x_udc *dev; + + const struct usb_endpoint_descriptor *desc; + struct list_head queue; + unsigned long pio_irqs; + + u8 stopped; + u8 bEndpointAddress; + u8 bmAttributes; + + ep_type_t ep_type; + u32 fifo; + u32 csr1; + u32 csr2; +}; + +struct lh7a40x_request { + struct usb_request req; + struct list_head queue; +}; + +struct lh7a40x_udc { + struct usb_gadget gadget; + struct usb_gadget_driver *driver; + struct device *dev; + spinlock_t lock; + + int ep0state; + struct lh7a40x_ep ep[UDC_MAX_ENDPOINTS]; + + unsigned char usb_address; + + unsigned req_pending:1, req_std:1, req_config:1; +}; + +extern struct lh7a40x_udc *the_controller; + +#define ep_is_in(EP) (((EP)->bEndpointAddress&USB_DIR_IN)==USB_DIR_IN) +#define ep_index(EP) ((EP)->bEndpointAddress&0xF) +#define ep_maxpacket(EP) ((EP)->ep.maxpacket) + +#endif diff --git a/drivers/usb/gadget/net2280.h b/drivers/usb/gadget/net2280.h index ec9d6f7e7..8033c4eec 100644 --- a/drivers/usb/gadget/net2280.h +++ b/drivers/usb/gadget/net2280.h @@ -446,7 +446,7 @@ struct net2280_ep_regs { /* [11.9] */ */ static inline u32 -get_idx_reg (struct net2280_regs *regs, u32 index) +get_idx_reg (struct net2280_regs __iomem *regs, u32 index) { writel (index, ®s->idxaddr); /* NOTE: synchs device/cpu memory views */ @@ -454,7 +454,7 @@ get_idx_reg (struct net2280_regs *regs, u32 index) } static inline void -set_idx_reg (struct net2280_regs *regs, u32 index, u32 value) +set_idx_reg (struct net2280_regs __iomem *regs, u32 index, u32 value) { writel (index, ®s->idxaddr); writel (value, ®s->idxdata); @@ -507,8 +507,8 @@ struct net2280_dma { struct net2280_ep { struct usb_ep ep; - struct net2280_ep_regs *regs; - struct net2280_dma_regs *dma; + struct net2280_ep_regs __iomem *regs; + struct net2280_dma_regs __iomem *dma; struct net2280_dma *dummy; dma_addr_t td_dma; /* of dummy */ struct net2280 *dev; @@ -539,7 +539,7 @@ static inline void allow_status (struct net2280_ep *ep) /* count (<= 4) bytes in the next fifo write will be valid */ static inline void set_fifo_bytecount (struct net2280_ep *ep, unsigned count) { - writeb (count, 2 + (u8 *) &ep->regs->ep_cfg); + writeb (count, 2 + (u8 __iomem *) &ep->regs->ep_cfg); } struct net2280_request { @@ -559,18 +559,19 @@ struct net2280 { struct usb_gadget_driver *driver; unsigned enabled : 1, protocol_stall : 1, + softconnect : 1, got_irq : 1, region : 1; u16 chiprev; /* pci state used to access those endpoints */ struct pci_dev *pdev; - struct net2280_regs *regs; - struct net2280_usb_regs *usb; - struct net2280_pci_regs *pci; - struct net2280_dma_regs *dma; - struct net2280_dep_regs *dep; - struct net2280_ep_regs *epregs; + struct net2280_regs __iomem *regs; + struct net2280_usb_regs __iomem *usb; + struct net2280_pci_regs __iomem *pci; + struct net2280_dma_regs __iomem *dma; + struct net2280_dep_regs __iomem *dep; + struct net2280_ep_regs __iomem *epregs; struct pci_pool *requests; // statistics... diff --git a/drivers/usb/gadget/omap_udc.c b/drivers/usb/gadget/omap_udc.c new file mode 100644 index 000000000..e40089d79 --- /dev/null +++ b/drivers/usb/gadget/omap_udc.c @@ -0,0 +1,2695 @@ +/* + * omap_udc.c -- for OMAP 1610 udc, with OTG support + * + * Copyright (C) 2004 Texas Instruments, Inc. + * Copyright (C) 2004 David Brownell + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You 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 + */ + +#undef DEBUG +#undef VERBOSE + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "omap_udc.h" + +#undef USB_TRACE + +/* OUT-dma seems to be behaving */ +#define USE_DMA + +/* ISO too */ +#define USE_ISO + + +#define DRIVER_DESC "OMAP UDC driver" +#define DRIVER_VERSION "24 August 2004" + +#define DMA_ADDR_INVALID (~(dma_addr_t)0) + + +/* + * The OMAP UDC needs _very_ early endpoint setup: before enabling the + * D+ pullup to allow enumeration. That's too early for the gadget + * framework to use from usb_endpoint_enable(), which happens after + * enumeration as part of activating an interface. (But if we add an + * optional new "UDC not yet running" state to the gadget driver model, + * even just during driver binding, the endpoint autoconfig logic is the + * natural spot to manufacture new endpoints.) + * + * So instead of using endpoint enable calls to control the hardware setup, + * this driver defines a "fifo mode" parameter. It's used during driver + * initialization to choose among a set of pre-defined endpoint configs. + * See omap_udc_setup() for available modes, or to add others. That code + * lives in an init section, so use this driver as a module if you need + * to change the fifo mode after the kernel boots. + * + * Gadget drivers normally ignore endpoints they don't care about, and + * won't include them in configuration descriptors. That means only + * misbehaving hosts would even notice they exist. + */ +#ifdef USE_ISO +static unsigned fifo_mode = 3; +#else +static unsigned fifo_mode = 0; +#endif + +/* "modprobe omap_udc fifo_mode=42", or else as a kernel + * boot parameter "omap_udc:fifo_mode=42" + */ +module_param (fifo_mode, uint, 0); +MODULE_PARM_DESC (fifo_mode, "endpoint setup (0 == default)"); + + +#ifdef USE_DMA +static unsigned use_dma = 1; + +/* "modprobe omap_udc use_dma=y", or else as a kernel + * boot parameter "omap_udc:use_dma=y" + */ +module_param (use_dma, bool, 0); +MODULE_PARM_DESC (use_dma, "enable/disable DMA"); +#else /* !USE_DMA */ + +/* save a bit of code */ +#define use_dma 0 +#endif /* !USE_DMA */ + + +static const char driver_name [] = "omap_udc"; +static const char driver_desc [] = DRIVER_DESC; + +/*-------------------------------------------------------------------------*/ + +/* there's a notion of "current endpoint" for modifying endpoint + * state, and PIO access to its FIFO. + */ + +static void use_ep(struct omap_ep *ep, u16 select) +{ + u16 num = ep->bEndpointAddress & 0x0f; + + if (ep->bEndpointAddress & USB_DIR_IN) + num |= UDC_EP_DIR; + UDC_EP_NUM_REG = num | select; + /* when select, MUST deselect later !! */ +} + +static inline void deselect_ep(void) +{ + UDC_EP_NUM_REG &= ~UDC_EP_SEL; + /* 6 wait states before TX will happen */ +} + +static void dma_channel_claim(struct omap_ep *ep, unsigned preferred); + +/*-------------------------------------------------------------------------*/ + +static int omap_ep_enable(struct usb_ep *_ep, + const struct usb_endpoint_descriptor *desc) +{ + struct omap_ep *ep = container_of(_ep, struct omap_ep, ep); + struct omap_udc *udc; + unsigned long flags; + u16 maxp; + + /* catch various bogus parameters */ + if (!_ep || !desc || ep->desc + || desc->bDescriptorType != USB_DT_ENDPOINT + || ep->bEndpointAddress != desc->bEndpointAddress + || ep->maxpacket < le16_to_cpu + (desc->wMaxPacketSize)) { + DBG("%s, bad ep or descriptor\n", __FUNCTION__); + return -EINVAL; + } + maxp = le16_to_cpu (desc->wMaxPacketSize); + if ((desc->bmAttributes == USB_ENDPOINT_XFER_BULK + && maxp != ep->maxpacket) + || desc->wMaxPacketSize > ep->maxpacket + || !desc->wMaxPacketSize) { + DBG("%s, bad %s maxpacket\n", __FUNCTION__, _ep->name); + return -ERANGE; + } + +#ifdef USE_ISO + if ((desc->bmAttributes == USB_ENDPOINT_XFER_ISOC + && desc->bInterval != 1)) { + /* hardware wants period = 1; USB allows 2^(Interval-1) */ + DBG("%s, unsupported ISO period %dms\n", _ep->name, + 1 << (desc->bInterval - 1)); + return -EDOM; + } +#else + if (desc->bmAttributes == USB_ENDPOINT_XFER_ISOC) { + DBG("%s, ISO nyet\n", _ep->name); + return -EDOM; + } +#endif + + /* xfer types must match, except that interrupt ~= bulk */ + if (ep->bmAttributes != desc->bmAttributes + && ep->bmAttributes != USB_ENDPOINT_XFER_BULK + && desc->bmAttributes != USB_ENDPOINT_XFER_INT) { + DBG("%s, %s type mismatch\n", __FUNCTION__, _ep->name); + return -EINVAL; + } + + udc = ep->udc; + if (!udc->driver || udc->gadget.speed == USB_SPEED_UNKNOWN) { + DBG("%s, bogus device state\n", __FUNCTION__); + return -ESHUTDOWN; + } + + spin_lock_irqsave(&udc->lock, flags); + + ep->desc = desc; + ep->irqs = 0; + ep->stopped = 0; + ep->ep.maxpacket = maxp; + + /* set endpoint to initial state */ + ep->dma_channel = 0; + ep->has_dma = 0; + ep->lch = -1; + use_ep(ep, UDC_EP_SEL); + UDC_CTRL_REG = UDC_RESET_EP; + ep->ackwait = 0; + deselect_ep(); + + if (ep->bmAttributes == USB_ENDPOINT_XFER_ISOC) + list_add(&ep->iso, &udc->iso); + + /* maybe assign a DMA channel to this endpoint */ + if (use_dma && desc->bmAttributes == USB_ENDPOINT_XFER_BULK + && !(ep->bEndpointAddress & USB_DIR_IN)) + /* FIXME ISO can dma, but prefers first channel. + * IN can dma, but lacks debugging. + */ + dma_channel_claim(ep, 0); + + /* PIO OUT may RX packets */ + if (desc->bmAttributes != USB_ENDPOINT_XFER_ISOC + && !ep->has_dma + && !(ep->bEndpointAddress & USB_DIR_IN)) + UDC_CTRL_REG = UDC_SET_FIFO_EN; + + spin_unlock_irqrestore(&udc->lock, flags); + VDBG("%s enabled\n", _ep->name); + return 0; +} + +static void nuke(struct omap_ep *, int status); + +static int omap_ep_disable(struct usb_ep *_ep) +{ + struct omap_ep *ep = container_of(_ep, struct omap_ep, ep); + unsigned long flags; + + if (!_ep || !ep->desc) { + DBG("%s, %s not enabled\n", __FUNCTION__, + _ep ? ep->ep.name : NULL); + return -EINVAL; + } + + spin_lock_irqsave(&ep->udc->lock, flags); + ep->desc = 0; + nuke (ep, -ESHUTDOWN); + ep->ep.maxpacket = ep->maxpacket; + ep->has_dma = 0; + UDC_CTRL_REG = UDC_SET_HALT; + list_del_init(&ep->iso); + + spin_unlock_irqrestore(&ep->udc->lock, flags); + + VDBG("%s disabled\n", _ep->name); + return 0; +} + +/*-------------------------------------------------------------------------*/ + +static struct usb_request * +omap_alloc_request(struct usb_ep *ep, int gfp_flags) +{ + struct omap_req *req; + + req = kmalloc(sizeof *req, gfp_flags); + if (req) { + memset (req, 0, sizeof *req); + req->req.dma = DMA_ADDR_INVALID; + INIT_LIST_HEAD (&req->queue); + } + return &req->req; +} + +static void +omap_free_request(struct usb_ep *ep, struct usb_request *_req) +{ + struct omap_req *req = container_of(_req, struct omap_req, req); + + if (_req) + kfree (req); +} + +/*-------------------------------------------------------------------------*/ + +static void * +omap_alloc_buffer( + struct usb_ep *_ep, + unsigned bytes, + dma_addr_t *dma, + int gfp_flags +) +{ + void *retval; + struct omap_ep *ep; + + ep = container_of(_ep, struct omap_ep, ep); + if (use_dma && ep->has_dma) { + static int warned; + if (!warned && bytes < PAGE_SIZE) { + dev_warn(ep->udc->gadget.dev.parent, + "using dma_alloc_coherent for " + "small allocations wastes memory\n"); + warned++; + } + return dma_alloc_coherent(ep->udc->gadget.dev.parent, + bytes, dma, gfp_flags); + } + + retval = kmalloc(bytes, gfp_flags); + if (retval) + *dma = virt_to_phys(retval); + return retval; +} + +static void omap_free_buffer( + struct usb_ep *_ep, + void *buf, + dma_addr_t dma, + unsigned bytes +) +{ + struct omap_ep *ep; + + ep = container_of(_ep, struct omap_ep, ep); + if (use_dma && _ep && ep->has_dma) + dma_free_coherent(ep->udc->gadget.dev.parent, bytes, buf, dma); + else + kfree (buf); +} + +/*-------------------------------------------------------------------------*/ + +static void +done(struct omap_ep *ep, struct omap_req *req, int status) +{ + unsigned stopped = ep->stopped; + + list_del_init(&req->queue); + + if (req->req.status == -EINPROGRESS) + req->req.status = status; + else + status = req->req.status; + + if (use_dma && ep->has_dma) { + if (req->mapped) { + dma_unmap_single(ep->udc->gadget.dev.parent, + req->req.dma, req->req.length, + (ep->bEndpointAddress & USB_DIR_IN) + ? DMA_TO_DEVICE + : DMA_FROM_DEVICE); + req->req.dma = DMA_ADDR_INVALID; + req->mapped = 0; + } else + dma_sync_single_for_cpu(ep->udc->gadget.dev.parent, + req->req.dma, req->req.length, + (ep->bEndpointAddress & USB_DIR_IN) + ? DMA_TO_DEVICE + : DMA_FROM_DEVICE); + } + +#ifndef USB_TRACE + if (status && status != -ESHUTDOWN) +#endif + VDBG("complete %s req %p stat %d len %u/%u\n", + ep->ep.name, &req->req, status, + req->req.actual, req->req.length); + + /* don't modify queue heads during completion callback */ + ep->stopped = 1; + spin_unlock(&ep->udc->lock); + req->req.complete(&ep->ep, &req->req); + spin_lock(&ep->udc->lock); + ep->stopped = stopped; +} + +/*-------------------------------------------------------------------------*/ + +#define FIFO_FULL (UDC_NON_ISO_FIFO_FULL | UDC_ISO_FIFO_FULL) +#define FIFO_UNWRITABLE (UDC_EP_HALTED | FIFO_FULL) + +#define FIFO_EMPTY (UDC_NON_ISO_FIFO_EMPTY | UDC_ISO_FIFO_EMPTY) +#define FIFO_UNREADABLE (UDC_EP_HALTED | FIFO_EMPTY) + +static inline int +write_packet(u8 *buf, struct omap_req *req, unsigned max) +{ + unsigned len; + u16 *wp; + + len = min(req->req.length - req->req.actual, max); + req->req.actual += len; + + max = len; + if (likely((((int)buf) & 1) == 0)) { + wp = (u16 *)buf; + while (max >= 2) { + UDC_DATA_REG = *wp++; + max -= 2; + } + buf = (u8 *)wp; + } + while (max--) + *(volatile u8 *)&UDC_DATA_REG = *buf++; + return len; +} + +// FIXME change r/w fifo calling convention + + +// return: 0 = still running, 1 = completed, negative = errno +static int write_fifo(struct omap_ep *ep, struct omap_req *req) +{ + u8 *buf; + unsigned count; + int is_last; + u16 ep_stat; + + buf = req->req.buf + req->req.actual; + prefetch(buf); + + /* PIO-IN isn't double buffered except for iso */ + ep_stat = UDC_STAT_FLG_REG; + if (ep_stat & FIFO_UNWRITABLE) + return 0; + + count = ep->ep.maxpacket; + count = write_packet(buf, req, count); + UDC_CTRL_REG = UDC_SET_FIFO_EN; + ep->ackwait = 1; + + /* last packet is often short (sometimes a zlp) */ + if (count != ep->ep.maxpacket) + is_last = 1; + else if (req->req.length == req->req.actual + && !req->req.zero) + is_last = 1; + else + is_last = 0; + + /* NOTE: requests complete when all IN data is in a + * FIFO (or sometimes later, if a zlp was needed). + * Use usb_ep_fifo_status() where needed. + */ + if (is_last) + done(ep, req, 0); + return is_last; +} + +static inline int +read_packet(u8 *buf, struct omap_req *req, unsigned avail) +{ + unsigned len; + u16 *wp; + + len = min(req->req.length - req->req.actual, avail); + req->req.actual += len; + avail = len; + + if (likely((((int)buf) & 1) == 0)) { + wp = (u16 *)buf; + while (avail >= 2) { + *wp++ = UDC_DATA_REG; + avail -= 2; + } + buf = (u8 *)wp; + } + while (avail--) + *buf++ = *(volatile u8 *)&UDC_DATA_REG; + return len; +} + +// return: 0 = still running, 1 = queue empty, negative = errno +static int read_fifo(struct omap_ep *ep, struct omap_req *req) +{ + u8 *buf; + unsigned count, avail; + int is_last; + + buf = req->req.buf + req->req.actual; + prefetchw(buf); + + for (;;) { + u16 ep_stat = UDC_STAT_FLG_REG; + + is_last = 0; + if (ep_stat & FIFO_UNREADABLE) + break; + + if (ep_stat & (UDC_NON_ISO_FIFO_FULL|UDC_ISO_FIFO_FULL)) + avail = ep->ep.maxpacket; + else + avail = UDC_RXFSTAT_REG; + count = read_packet(buf, req, avail); + + // FIXME double buffered PIO OUT wasn't behaving... + + /* partial packet reads may not be errors */ + if (count < ep->ep.maxpacket) { + is_last = 1; + /* overflowed this request? flush extra data */ + if (count != avail) { + req->req.status = -EOVERFLOW; + avail -= count; + while (avail--) + (void) *(volatile u8 *)&UDC_DATA_REG; + } + } else if (req->req.length == req->req.actual) + is_last = 1; + else + is_last = 0; + + if (!ep->bEndpointAddress) + break; + if (!ep->double_buf) { + UDC_CTRL_REG = UDC_SET_FIFO_EN; + if (!is_last) + break; + } + + if (is_last) { + done(ep, req, 0); + if (list_empty(&ep->queue) || !ep->double_buf) + break; + req = container_of(ep->queue.next, + struct omap_req, queue); + is_last = 0; + } + } + return is_last; +} + +/*-------------------------------------------------------------------------*/ + +/* Each USB transfer request using DMA maps to one or more DMA transfers. + * When DMA completion isn't request completion, the UDC continues with + * the next DMA transfer for that USB transfer. + */ + +static void next_in_dma(struct omap_ep *ep, struct omap_req *req) +{ + u16 txdma_ctrl; + unsigned length = req->req.length - req->req.actual; + + /* measure length in either bytes or packets */ + if (length <= (UDC_TXN_TSC + 1)) { + txdma_ctrl = UDC_TXN_EOT | length; + omap_set_dma_transfer_params(ep->lch, OMAP_DMA_DATA_TYPE_S8, + length, 1, OMAP_DMA_SYNC_ELEMENT); + } else { + length = max(length / ep->maxpacket, + (unsigned) UDC_TXN_TSC + 1); + txdma_ctrl = length; + omap_set_dma_transfer_params(ep->lch, OMAP_DMA_DATA_TYPE_S8, + ep->ep.maxpacket, length, + OMAP_DMA_SYNC_ELEMENT); + length *= ep->maxpacket; + } + + omap_set_dma_src_params(ep->lch, OMAP_DMA_PORT_EMIFF, + OMAP_DMA_AMODE_POST_INC, req->req.dma + req->req.actual); + + omap_start_dma(ep->lch); + UDC_DMA_IRQ_EN_REG |= UDC_TX_DONE_IE(ep->dma_channel); + UDC_TXDMA_REG(ep->dma_channel) = UDC_TXN_START | txdma_ctrl; + req->dma_bytes = length; +} + +static void finish_in_dma(struct omap_ep *ep, struct omap_req *req, int status) +{ + if (status == 0) { + req->req.actual += req->dma_bytes; + + /* return if this request needs to send data or zlp */ + if (req->req.actual < req->req.length) + return; + if (req->req.zero + && req->dma_bytes != 0 + && (req->req.actual % ep->maxpacket) == 0) + return; + } else { + u32 last; + + // FIXME this surely isn't #bytes transferred + last = (omap_readw(OMAP_DMA_CSSA_U(ep->lch)) << 16) + | omap_readw(OMAP_DMA_CSSA_L(ep->lch)); + req->req.actual = last - req->req.dma; + } + + /* tx completion */ + omap_stop_dma(ep->lch); + UDC_DMA_IRQ_EN_REG &= ~UDC_TX_DONE_IE(ep->dma_channel); + done(ep, req, status); +} + +static void next_out_dma(struct omap_ep *ep, struct omap_req *req) +{ + unsigned packets; + + /* NOTE: we filtered out "short reads" before, so we know + * the buffer has only whole numbers of packets. + */ + + /* set up this DMA transfer, enable the fifo, start */ + packets = (req->req.length - req->req.actual) / ep->ep.maxpacket; + packets = min(packets, (unsigned)UDC_RXN_TC + 1); + req->dma_bytes = packets * ep->ep.maxpacket; + omap_set_dma_transfer_params(ep->lch, OMAP_DMA_DATA_TYPE_S8, + ep->ep.maxpacket, packets, + OMAP_DMA_SYNC_ELEMENT); + omap_set_dma_dest_params(ep->lch, OMAP_DMA_PORT_EMIFF, + OMAP_DMA_AMODE_POST_INC, req->req.dma + req->req.actual); + + UDC_RXDMA_REG(ep->dma_channel) = UDC_RXN_STOP | (packets - 1); + UDC_DMA_IRQ_EN_REG |= UDC_RX_EOT_IE(ep->dma_channel); + UDC_EP_NUM_REG = (ep->bEndpointAddress & 0xf); + UDC_CTRL_REG = UDC_SET_FIFO_EN; + + omap_start_dma(ep->lch); +} + +static void +finish_out_dma(struct omap_ep *ep, struct omap_req *req, int status) +{ + u16 count; + + /* FIXME must be a better way to see how much dma + * happened, even when it never got going... + */ + count = omap_readw(OMAP_DMA_CDAC(ep->lch)); + count -= 0xffff & (req->req.dma + req->req.actual); + count += req->req.actual; + if (count <= req->req.length) + req->req.actual = count; + + if (count != req->dma_bytes || status) + omap_stop_dma(ep->lch); + + /* if this wasn't short, request may need another transfer */ + else if (req->req.actual < req->req.length) + return; + + /* rx completion */ + UDC_DMA_IRQ_EN_REG &= ~UDC_RX_EOT_IE(ep->dma_channel); + done(ep, req, status); +} + +static void dma_irq(struct omap_udc *udc, u16 irq_src) +{ + u16 dman_stat = UDC_DMAN_STAT_REG; + struct omap_ep *ep; + struct omap_req *req; + + /* IN dma: tx to host */ + if (irq_src & UDC_TXN_DONE) { + ep = &udc->ep[16 + UDC_DMA_TX_SRC(dman_stat)]; + ep->irqs++; + /* can see TXN_DONE after dma abort */ + if (!list_empty(&ep->queue)) { + req = container_of(ep->queue.next, + struct omap_req, queue); + finish_in_dma(ep, req, 0); + } + UDC_IRQ_SRC_REG = UDC_TXN_DONE; + + if (!list_empty (&ep->queue)) { + req = container_of(ep->queue.next, + struct omap_req, queue); + next_in_dma(ep, req); + } + } + + /* OUT dma: rx from host */ + if (irq_src & UDC_RXN_EOT) { + ep = &udc->ep[UDC_DMA_RX_SRC(dman_stat)]; + ep->irqs++; + /* can see RXN_EOT after dma abort */ + if (!list_empty(&ep->queue)) { + req = container_of(ep->queue.next, + struct omap_req, queue); + finish_out_dma(ep, req, 0); + } + UDC_IRQ_SRC_REG = UDC_RXN_EOT; + + if (!list_empty (&ep->queue)) { + req = container_of(ep->queue.next, + struct omap_req, queue); + next_out_dma(ep, req); + } + } + + if (irq_src & UDC_RXN_CNT) { + ep = &udc->ep[UDC_DMA_RX_SRC(dman_stat)]; + DBG("%s, RX_CNT irq?\n", ep->ep.name); + UDC_IRQ_SRC_REG = UDC_RXN_CNT; + } +} + +static void dma_error(int lch, u16 ch_status, void *data) +{ + struct omap_ep *ep = data; + + /* if ch_status & OMAP_DMA_DROP_IRQ ... */ + /* if ch_status & OMAP_DMA_TOUT_IRQ ... */ + ERR("%s dma error, lch %d status %02x\n", ep->ep.name, lch, ch_status); + + /* complete current transfer ... */ +} + +static void dma_channel_claim(struct omap_ep *ep, unsigned channel) +{ + u16 reg; + int status, restart, is_in; + + is_in = ep->bEndpointAddress & USB_DIR_IN; + if (is_in) + reg = UDC_TXDMA_CFG_REG; + else + reg = UDC_RXDMA_CFG_REG; + reg |= 1 << 12; /* "pulse" activated */ + + ep->dma_channel = 0; + ep->lch = -1; + if (channel == 0 || channel > 3) { + if ((reg & 0x0f00) == 0) + channel = 3; + else if ((reg & 0x00f0) == 0) + channel = 2; + else if ((reg & 0x000f) == 0) /* preferred for ISO */ + channel = 1; + else { + status = -EMLINK; + goto just_restart; + } + } + reg |= (0x0f & ep->bEndpointAddress) << (4 * (channel - 1)); + ep->dma_channel = channel; + + if (is_in) { + status = omap_request_dma(OMAP_DMA_USB_W2FC_TX0 - 1 + channel, + ep->ep.name, dma_error, ep, &ep->lch); + if (status == 0) { + UDC_TXDMA_CFG_REG = reg; + omap_set_dma_dest_params(ep->lch, + OMAP_DMA_PORT_TIPB, + OMAP_DMA_AMODE_CONSTANT, + (unsigned long) io_v2p((u32)&UDC_DATA_DMA_REG)); + } + } else { + status = omap_request_dma(OMAP_DMA_USB_W2FC_RX0 - 1 + channel, + ep->ep.name, dma_error, ep, &ep->lch); + if (status == 0) { + UDC_RXDMA_CFG_REG = reg; + omap_set_dma_src_params(ep->lch, + OMAP_DMA_PORT_TIPB, + OMAP_DMA_AMODE_CONSTANT, + (unsigned long) io_v2p((u32)&UDC_DATA_DMA_REG)); + } + } + if (status) + ep->dma_channel = 0; + else { + ep->has_dma = 1; + omap_disable_dma_irq(ep->lch, OMAP_DMA_BLOCK_IRQ); + + /* channel type P: hw synch (fifo) */ + omap_writew(2, OMAP_DMA_LCH_CTRL(ep->lch)); + } + +just_restart: + /* restart any queue, even if the claim failed */ + restart = !ep->stopped && !list_empty(&ep->queue); + + if (status) + DBG("%s no dma channel: %d%s\n", ep->ep.name, status, + restart ? " (restart)" : ""); + else + DBG("%s claimed %cxdma%d lch %d%s\n", ep->ep.name, + is_in ? 't' : 'r', + ep->dma_channel - 1, ep->lch, + restart ? " (restart)" : ""); + + if (restart) { + struct omap_req *req; + req = container_of(ep->queue.next, struct omap_req, queue); + if (ep->has_dma) + (is_in ? next_in_dma : next_out_dma)(ep, req); + else { + use_ep(ep, UDC_EP_SEL); + (is_in ? write_fifo : read_fifo)(ep, req); + deselect_ep(); + /* IN: 6 wait states before it'll tx */ + } + } +} + +static void dma_channel_release(struct omap_ep *ep) +{ + int shift = 4 * (ep->dma_channel - 1); + u16 mask = 0x0f << shift; + struct omap_req *req; + int active; + + /* abort any active usb transfer request */ + if (!list_empty(&ep->queue)) + req = container_of(ep->queue.next, struct omap_req, queue); + else + req = 0; + + active = ((1 << 7) & omap_readl(OMAP_DMA_CCR(ep->lch))) != 0; + + DBG("%s release %s %cxdma%d %p\n", ep->ep.name, + active ? "active" : "idle", + (ep->bEndpointAddress & USB_DIR_IN) ? 't' : 'r', + ep->dma_channel - 1, req); + + /* wait till current packet DMA finishes, and fifo empties */ + if (ep->bEndpointAddress & USB_DIR_IN) { + UDC_TXDMA_CFG_REG &= ~mask; + + if (req) { + if (active) + udelay(50); + finish_in_dma(ep, req, -ECONNRESET); + if (UDC_TXDMA_CFG_REG & mask) + WARN("%s, SPIN abort TX dma\n", ep->ep.name); + } + + /* host may empty the fifo (or not...) */ + while (UDC_TXDMA_CFG_REG & mask) + udelay(10); + + } else { + UDC_RXDMA_CFG_REG &= ~mask; + + /* dma empties the fifo */ + while (active && (UDC_RXDMA_CFG_REG & mask)) + udelay(10); + omap_stop_dma(ep->lch); + if (req) + finish_out_dma(ep, req, -ECONNRESET); + } + omap_free_dma(ep->lch); + ep->dma_channel = 0; + ep->lch = -1; + /* has_dma still set, till endpoint is fully quiesced */ +} + + +/*-------------------------------------------------------------------------*/ + +static int +omap_ep_queue(struct usb_ep *_ep, struct usb_request *_req, int gfp_flags) +{ + struct omap_ep *ep = container_of(_ep, struct omap_ep, ep); + struct omap_req *req = container_of(_req, struct omap_req, req); + struct omap_udc *udc; + unsigned long flags; + int is_iso = 0; + + /* catch various bogus parameters */ + if (!_req || !req->req.complete || !req->req.buf + || !list_empty(&req->queue)) { + DBG("%s, bad params\n", __FUNCTION__); + return -EINVAL; + } + if (!_ep || (!ep->desc && ep->bEndpointAddress)) { + DBG("%s, bad ep\n", __FUNCTION__); + return -EINVAL; + } + if (ep->bmAttributes == USB_ENDPOINT_XFER_ISOC) { + if (req->req.length > ep->ep.maxpacket) + return -EMSGSIZE; + is_iso = 1; + } + + /* this isn't bogus, but OMAP DMA isn't the only hardware to + * have a hard time with partial packet reads... reject it. + */ + if (use_dma + && ep->has_dma + && ep->bEndpointAddress != 0 + && (ep->bEndpointAddress & USB_DIR_IN) == 0 + && (req->req.length % ep->ep.maxpacket) != 0) { + DBG("%s, no partial packet OUT reads\n", __FUNCTION__); + return -EMSGSIZE; + } + + udc = ep->udc; + if (!udc->driver || udc->gadget.speed == USB_SPEED_UNKNOWN) + return -ESHUTDOWN; + + if (use_dma && ep->has_dma) { + if (req->req.dma == DMA_ADDR_INVALID) { + req->req.dma = dma_map_single( + ep->udc->gadget.dev.parent, + req->req.buf, + req->req.length, + (ep->bEndpointAddress & USB_DIR_IN) + ? DMA_TO_DEVICE + : DMA_FROM_DEVICE); + req->mapped = 1; + } else { + dma_sync_single_for_device( + ep->udc->gadget.dev.parent, + req->req.dma, req->req.length, + (ep->bEndpointAddress & USB_DIR_IN) + ? DMA_TO_DEVICE + : DMA_FROM_DEVICE); + req->mapped = 0; + } + } + + VDBG("%s queue req %p, len %d buf %p\n", + ep->ep.name, _req, _req->length, _req->buf); + + spin_lock_irqsave(&udc->lock, flags); + + req->req.status = -EINPROGRESS; + req->req.actual = 0; + + /* maybe kickstart non-iso i/o queues */ + if (is_iso) + UDC_IRQ_EN_REG |= UDC_SOF_IE; + else if (list_empty(&ep->queue) && !ep->stopped && !ep->ackwait) { + int is_in; + + if (ep->bEndpointAddress == 0) { + if (!udc->ep0_pending || !list_empty (&ep->queue)) { + spin_unlock_irqrestore(&udc->lock, flags); + return -EL2HLT; + } + + /* empty DATA stage? */ + is_in = udc->ep0_in; + if (!req->req.length) { + + /* chip became CONFIGURED or ADDRESSED + * earlier; drivers may already have queued + * requests to non-control endpoints + */ + if (udc->ep0_set_config) { + u16 irq_en = UDC_IRQ_EN_REG; + + irq_en |= UDC_DS_CHG_IE | UDC_EP0_IE; + if (!udc->ep0_reset_config) + irq_en |= UDC_EPN_RX_IE + | UDC_EPN_TX_IE; + UDC_IRQ_EN_REG = irq_en; + } + + /* STATUS is reverse direction */ + UDC_EP_NUM_REG = is_in + ? UDC_EP_SEL + : (UDC_EP_SEL|UDC_EP_DIR); + UDC_CTRL_REG = UDC_CLR_EP; + UDC_CTRL_REG = UDC_SET_FIFO_EN; + UDC_EP_NUM_REG = udc->ep0_in ? 0 : UDC_EP_DIR; + + /* cleanup */ + udc->ep0_pending = 0; + done(ep, req, 0); + req = 0; + + /* non-empty DATA stage */ + } else if (is_in) { + UDC_EP_NUM_REG = UDC_EP_SEL|UDC_EP_DIR; + } else { + if (udc->ep0_setup) + goto irq_wait; + UDC_EP_NUM_REG = UDC_EP_SEL; + } + } else { + is_in = ep->bEndpointAddress & USB_DIR_IN; + if (!ep->has_dma) + use_ep(ep, UDC_EP_SEL); + /* if ISO: SOF IRQs must be enabled/disabled! */ + } + + if (ep->has_dma) + (is_in ? next_in_dma : next_out_dma)(ep, req); + else if (req) { + if ((is_in ? write_fifo : read_fifo)(ep, req) == 1) + req = 0; + deselect_ep(); + /* IN: 6 wait states before it'll tx */ + } + } + +irq_wait: + /* irq handler advances the queue */ + if (req != 0) + list_add_tail(&req->queue, &ep->queue); + spin_unlock_irqrestore(&udc->lock, flags); + + return 0; +} + +static int omap_ep_dequeue(struct usb_ep *_ep, struct usb_request *_req) +{ + struct omap_ep *ep = container_of(_ep, struct omap_ep, ep); + struct omap_req *req; + unsigned long flags; + + if (!_ep || !_req) + return -EINVAL; + + spin_lock_irqsave(&ep->udc->lock, flags); + + /* make sure it's actually queued on this endpoint */ + list_for_each_entry (req, &ep->queue, queue) { + if (&req->req == _req) + break; + } + if (&req->req != _req) { + spin_unlock_irqrestore(&ep->udc->lock, flags); + return -EINVAL; + } + + if (use_dma && ep->dma_channel && ep->queue.next == &req->queue) { + int channel = ep->dma_channel; + + /* releasing the dma completion cancels the request, + * reclaiming the channel restarts the queue + */ + dma_channel_release(ep); + dma_channel_claim(ep, channel); + } else + done(ep, req, -ECONNRESET); + spin_unlock_irqrestore(&ep->udc->lock, flags); + return 0; +} + +/*-------------------------------------------------------------------------*/ + +static int omap_ep_set_halt(struct usb_ep *_ep, int value) +{ + struct omap_ep *ep = container_of(_ep, struct omap_ep, ep); + unsigned long flags; + int status = -EOPNOTSUPP; + + spin_lock_irqsave(&ep->udc->lock, flags); + + /* just use protocol stalls for ep0; real halts are annoying */ + if (ep->bEndpointAddress == 0) { + if (!ep->udc->ep0_pending) + status = -EINVAL; + else if (value) { + if (ep->udc->ep0_set_config) { + WARN("error changing config?\n"); + UDC_SYSCON2_REG = UDC_CLR_CFG; + } + UDC_SYSCON2_REG = UDC_STALL_CMD; + ep->udc->ep0_pending = 0; + status = 0; + } else /* NOP */ + status = 0; + + /* otherwise, all active non-ISO endpoints can halt */ + } else if (ep->bmAttributes != USB_ENDPOINT_XFER_ISOC && ep->desc) { + + /* IN endpoints must already be idle */ + if ((ep->bEndpointAddress & USB_DIR_IN) + && !list_empty(&ep->queue)) { + status = -EAGAIN; + goto done; + } + + if (value) { + int channel; + + if (use_dma && ep->dma_channel + && !list_empty(&ep->queue)) { + channel = ep->dma_channel; + dma_channel_release(ep); + } else + channel = 0; + + use_ep(ep, UDC_EP_SEL); + if (UDC_STAT_FLG_REG & UDC_NON_ISO_FIFO_EMPTY) { + UDC_CTRL_REG = UDC_SET_HALT; + status = 0; + } else + status = -EAGAIN; + deselect_ep(); + + if (channel) + dma_channel_claim(ep, channel); + } else { + use_ep(ep, 0); + UDC_CTRL_REG = UDC_RESET_EP; + ep->ackwait = 0; + if (!(ep->bEndpointAddress & USB_DIR_IN)) + UDC_CTRL_REG = UDC_SET_FIFO_EN; + } + } +done: + VDBG("%s %s halt stat %d\n", ep->ep.name, + value ? "set" : "clear", status); + + spin_unlock_irqrestore(&ep->udc->lock, flags); + return status; +} + +static struct usb_ep_ops omap_ep_ops = { + .enable = omap_ep_enable, + .disable = omap_ep_disable, + + .alloc_request = omap_alloc_request, + .free_request = omap_free_request, + + .alloc_buffer = omap_alloc_buffer, + .free_buffer = omap_free_buffer, + + .queue = omap_ep_queue, + .dequeue = omap_ep_dequeue, + + .set_halt = omap_ep_set_halt, + // fifo_status ... report bytes in fifo + // fifo_flush ... flush fifo +}; + +/*-------------------------------------------------------------------------*/ + +static int omap_get_frame(struct usb_gadget *gadget) +{ + u16 sof = UDC_SOF_REG; + return (sof & UDC_TS_OK) ? (sof & UDC_TS) : -EL2NSYNC; +} + +static int omap_wakeup(struct usb_gadget *gadget) +{ + struct omap_udc *udc; + unsigned long flags; + int retval = -EHOSTUNREACH; + + udc = container_of(gadget, struct omap_udc, gadget); + + spin_lock_irqsave(&udc->lock, flags); + if (udc->devstat & UDC_SUS) { + /* NOTE: OTG spec erratum says that OTG devices may + * issue wakeups without host enable. + */ + if (udc->devstat & (UDC_B_HNP_ENABLE|UDC_R_WK_OK)) { + DBG("remote wakeup...\n"); + UDC_SYSCON2_REG = UDC_RMT_WKP; + retval = 0; + } + + /* NOTE: non-OTG systems may use SRP TOO... */ + } else if (!(udc->devstat & UDC_ATT)) { + if (udc->transceiver) + retval = otg_start_srp(udc->transceiver); + } + spin_unlock_irqrestore(&udc->lock, flags); + + return retval; +} + +static int +omap_set_selfpowered(struct usb_gadget *gadget, int is_selfpowered) +{ + struct omap_udc *udc; + unsigned long flags; + u16 syscon1; + + udc = container_of(gadget, struct omap_udc, gadget); + spin_lock_irqsave(&udc->lock, flags); + syscon1 = UDC_SYSCON1_REG; + if (is_selfpowered) + syscon1 |= UDC_SELF_PWR; + else + syscon1 &= ~UDC_SELF_PWR; + UDC_SYSCON1_REG = syscon1; + spin_unlock_irqrestore(&udc->lock, flags); + + return 0; +} + +static int can_pullup(struct omap_udc *udc) +{ + return udc->driver && udc->softconnect && udc->vbus_active; +} + +static void pullup_enable(struct omap_udc *udc) +{ + UDC_SYSCON1_REG |= UDC_PULLUP_EN; +#ifndef CONFIG_USB_OTG + OTG_CTRL_REG |= OTG_BSESSVLD; +#endif + UDC_IRQ_EN_REG = UDC_DS_CHG_IE; +} + +static void pullup_disable(struct omap_udc *udc) +{ +#ifndef CONFIG_USB_OTG + OTG_CTRL_REG &= ~OTG_BSESSVLD; +#endif + UDC_IRQ_EN_REG = UDC_DS_CHG_IE; + UDC_SYSCON1_REG &= ~UDC_PULLUP_EN; +} + +/* + * Called by whatever detects VBUS sessions: external transceiver + * driver, or maybe GPIO0 VBUS IRQ. + */ +static int omap_vbus_session(struct usb_gadget *gadget, int is_active) +{ + struct omap_udc *udc; + unsigned long flags; + + udc = container_of(gadget, struct omap_udc, gadget); + spin_lock_irqsave(&udc->lock, flags); + VDBG("VBUS %s\n", is_active ? "on" : "off"); + udc->vbus_active = (is_active != 0); + if (can_pullup(udc)) + pullup_enable(udc); + else + pullup_disable(udc); + spin_unlock_irqrestore(&udc->lock, flags); + return 0; +} + +static int omap_vbus_draw(struct usb_gadget *gadget, unsigned mA) +{ + struct omap_udc *udc; + + udc = container_of(gadget, struct omap_udc, gadget); + if (udc->transceiver) + return otg_set_power(udc->transceiver, mA); + return -EOPNOTSUPP; +} + +static int omap_pullup(struct usb_gadget *gadget, int is_on) +{ + struct omap_udc *udc; + unsigned long flags; + + udc = container_of(gadget, struct omap_udc, gadget); + spin_lock_irqsave(&udc->lock, flags); + udc->softconnect = (is_on != 0); + if (can_pullup(udc)) + pullup_enable(udc); + else + pullup_disable(udc); + spin_unlock_irqrestore(&udc->lock, flags); + return 0; +} + +static struct usb_gadget_ops omap_gadget_ops = { + .get_frame = omap_get_frame, + .wakeup = omap_wakeup, + .set_selfpowered = omap_set_selfpowered, + .vbus_session = omap_vbus_session, + .vbus_draw = omap_vbus_draw, + .pullup = omap_pullup, +}; + +/*-------------------------------------------------------------------------*/ + +/* dequeue ALL requests; caller holds udc->lock */ +static void nuke(struct omap_ep *ep, int status) +{ + struct omap_req *req; + + ep->stopped = 1; + + if (use_dma && ep->dma_channel) + dma_channel_release(ep); + + use_ep(ep, 0); + UDC_CTRL_REG = UDC_CLR_EP; + if (ep->bEndpointAddress && ep->bmAttributes != USB_ENDPOINT_XFER_ISOC) + UDC_CTRL_REG = UDC_SET_HALT; + + while (!list_empty(&ep->queue)) { + req = list_entry(ep->queue.next, struct omap_req, queue); + done(ep, req, status); + } +} + +/* caller holds udc->lock */ +static void udc_quiesce(struct omap_udc *udc) +{ + struct omap_ep *ep; + + udc->gadget.speed = USB_SPEED_UNKNOWN; + nuke(&udc->ep[0], -ESHUTDOWN); + list_for_each_entry (ep, &udc->gadget.ep_list, ep.ep_list) + nuke(ep, -ESHUTDOWN); +} + +/*-------------------------------------------------------------------------*/ + +static void update_otg(struct omap_udc *udc) +{ + u16 devstat; + + if (!udc->gadget.is_otg) + return; + + if (OTG_CTRL_REG & OTG_ID) + devstat = UDC_DEVSTAT_REG; + else + devstat = 0; + + udc->gadget.b_hnp_enable = !!(devstat & UDC_B_HNP_ENABLE); + udc->gadget.a_hnp_support = !!(devstat & UDC_A_HNP_SUPPORT); + udc->gadget.a_alt_hnp_support = !!(devstat & UDC_A_ALT_HNP_SUPPORT); + + /* Enable HNP early, avoiding races on suspend irq path. + * ASSUMES OTG state machine B_BUS_REQ input is true. + */ + if (udc->gadget.b_hnp_enable) + OTG_CTRL_REG = (OTG_CTRL_REG | OTG_B_HNPEN | OTG_B_BUSREQ) + & ~OTG_PULLUP; +} + +static void ep0_irq(struct omap_udc *udc, u16 irq_src) +{ + struct omap_ep *ep0 = &udc->ep[0]; + struct omap_req *req = 0; + + ep0->irqs++; + + /* Clear any pending requests and then scrub any rx/tx state + * before starting to handle the SETUP request. + */ + if (irq_src & UDC_SETUP) + nuke(ep0, 0); + + /* IN/OUT packets mean we're in the DATA or STATUS stage. + * This driver uses only uses protocol stalls (ep0 never halts), + * and if we got this far the gadget driver already had a + * chance to stall. Tries to be forgiving of host oddities. + * + * NOTE: the last chance gadget drivers have to stall control + * requests is during their request completion callback. + */ + if (!list_empty(&ep0->queue)) + req = container_of(ep0->queue.next, struct omap_req, queue); + + /* IN == TX to host */ + if (irq_src & UDC_EP0_TX) { + int stat; + + UDC_IRQ_SRC_REG = UDC_EP0_TX; + UDC_EP_NUM_REG = UDC_EP_SEL|UDC_EP_DIR; + stat = UDC_STAT_FLG_REG; + if (stat & UDC_ACK) { + if (udc->ep0_in) { + /* write next IN packet from response, + * or set up the status stage. + */ + if (req) + stat = write_fifo(ep0, req); + UDC_EP_NUM_REG = UDC_EP_DIR; + if (!req && udc->ep0_pending) { + UDC_EP_NUM_REG = UDC_EP_SEL; + UDC_CTRL_REG = UDC_CLR_EP; + UDC_CTRL_REG = UDC_SET_FIFO_EN; + UDC_EP_NUM_REG = 0; + udc->ep0_pending = 0; + } /* else: 6 wait states before it'll tx */ + } else { + /* ack status stage of OUT transfer */ + UDC_EP_NUM_REG = UDC_EP_DIR; + if (req) + done(ep0, req, 0); + } + req = 0; + } else if (stat & UDC_STALL) { + UDC_CTRL_REG = UDC_CLR_HALT; + UDC_EP_NUM_REG = UDC_EP_DIR; + } else { + UDC_EP_NUM_REG = UDC_EP_DIR; + } + } + + /* OUT == RX from host */ + if (irq_src & UDC_EP0_RX) { + int stat; + + UDC_IRQ_SRC_REG = UDC_EP0_RX; + UDC_EP_NUM_REG = UDC_EP_SEL; + stat = UDC_STAT_FLG_REG; + if (stat & UDC_ACK) { + if (!udc->ep0_in) { + stat = 0; + /* read next OUT packet of request, maybe + * reactiviting the fifo; stall on errors. + */ + if (!req || (stat = read_fifo(ep0, req)) < 0) { + UDC_SYSCON2_REG = UDC_STALL_CMD; + udc->ep0_pending = 0; + stat = 0; + } else if (stat == 0) + UDC_CTRL_REG = UDC_SET_FIFO_EN; + UDC_EP_NUM_REG = 0; + + /* activate status stage */ + if (stat == 1) { + done(ep0, req, 0); + /* that may have STALLed ep0... */ + UDC_EP_NUM_REG = UDC_EP_SEL|UDC_EP_DIR; + UDC_CTRL_REG = UDC_CLR_EP; + UDC_CTRL_REG = UDC_SET_FIFO_EN; + UDC_EP_NUM_REG = UDC_EP_DIR; + udc->ep0_pending = 0; + } + } else { + /* ack status stage of IN transfer */ + UDC_EP_NUM_REG = 0; + if (req) + done(ep0, req, 0); + } + } else if (stat & UDC_STALL) { + UDC_CTRL_REG = UDC_CLR_HALT; + UDC_EP_NUM_REG = 0; + } else { + UDC_EP_NUM_REG = 0; + } + } + + /* SETUP starts all control transfers */ + if (irq_src & UDC_SETUP) { + union u { + u16 word[4]; + struct usb_ctrlrequest r; + } u; + int status = -EINVAL; + struct omap_ep *ep; + + /* read the (latest) SETUP message */ + do { + UDC_EP_NUM_REG = UDC_SETUP_SEL; + /* two bytes at a time */ + u.word[0] = UDC_DATA_REG; + u.word[1] = UDC_DATA_REG; + u.word[2] = UDC_DATA_REG; + u.word[3] = UDC_DATA_REG; + UDC_EP_NUM_REG = 0; + } while (UDC_IRQ_SRC_REG & UDC_SETUP); + le16_to_cpus (&u.r.wValue); + le16_to_cpus (&u.r.wIndex); + le16_to_cpus (&u.r.wLength); + + /* Delegate almost all control requests to the gadget driver, + * except for a handful of ch9 status/feature requests that + * hardware doesn't autodecode _and_ the gadget API hides. + */ + udc->ep0_in = (u.r.bRequestType & USB_DIR_IN) != 0; + udc->ep0_set_config = 0; + udc->ep0_pending = 1; + ep0->stopped = 0; + ep0->ackwait = 0; + switch (u.r.bRequest) { + case USB_REQ_SET_CONFIGURATION: + /* udc needs to know when ep != 0 is valid */ + if (u.r.bRequestType != USB_RECIP_DEVICE) + goto delegate; + if (u.r.wLength != 0) + goto do_stall; + udc->ep0_set_config = 1; + udc->ep0_reset_config = (u.r.wValue == 0); + VDBG("set config %d\n", u.r.wValue); + + /* update udc NOW since gadget driver may start + * queueing requests immediately; clear config + * later if it fails the request. + */ + if (udc->ep0_reset_config) + UDC_SYSCON2_REG = UDC_CLR_CFG; + else + UDC_SYSCON2_REG = UDC_DEV_CFG; + update_otg(udc); + goto delegate; + case USB_REQ_CLEAR_FEATURE: + /* clear endpoint halt */ + if (u.r.bRequestType != USB_RECIP_ENDPOINT) + goto delegate; + if (u.r.wValue != USB_ENDPOINT_HALT + || u.r.wLength != 0) + goto do_stall; + ep = &udc->ep[u.r.wIndex & 0xf]; + if (ep != ep0) { + if (u.r.wIndex & USB_DIR_IN) + ep += 16; + if (ep->bmAttributes == USB_ENDPOINT_XFER_ISOC + || !ep->desc) + goto do_stall; + use_ep(ep, 0); + UDC_CTRL_REG = UDC_RESET_EP; + ep->ackwait = 0; + if (!(ep->bEndpointAddress & USB_DIR_IN)) + UDC_CTRL_REG = UDC_SET_FIFO_EN; + } + VDBG("%s halt cleared by host\n", ep->name); + goto ep0out_status_stage; + case USB_REQ_SET_FEATURE: + /* set endpoint halt */ + if (u.r.bRequestType != USB_RECIP_ENDPOINT) + goto delegate; + if (u.r.wValue != USB_ENDPOINT_HALT + || u.r.wLength != 0) + goto do_stall; + ep = &udc->ep[u.r.wIndex & 0xf]; + if (u.r.wIndex & USB_DIR_IN) + ep += 16; + if (ep->bmAttributes == USB_ENDPOINT_XFER_ISOC + || ep == ep0 || !ep->desc) + goto do_stall; + if (use_dma && ep->has_dma) { + /* this has rude side-effects (aborts) and + * can't really work if DMA-IN is active + */ + DBG("%s host set_halt, NYET \n", ep->name); + goto do_stall; + } + use_ep(ep, 0); + /* can't halt if fifo isn't empty... */ + UDC_CTRL_REG = UDC_CLR_EP; + UDC_CTRL_REG = UDC_SET_HALT; + VDBG("%s halted by host\n", ep->name); +ep0out_status_stage: + status = 0; + UDC_EP_NUM_REG = UDC_EP_SEL|UDC_EP_DIR; + UDC_CTRL_REG = UDC_CLR_EP; + UDC_CTRL_REG = UDC_SET_FIFO_EN; + UDC_EP_NUM_REG = UDC_EP_DIR; + udc->ep0_pending = 0; + break; + case USB_REQ_GET_STATUS: + /* return interface status. if we were pedantic, + * we'd detect non-existent interfaces, and stall. + */ + if (u.r.bRequestType + != (USB_DIR_IN|USB_RECIP_INTERFACE)) + goto delegate; + /* return two zero bytes */ + UDC_EP_NUM_REG = UDC_EP_SEL|UDC_EP_DIR; + UDC_DATA_REG = 0; + UDC_CTRL_REG = UDC_SET_FIFO_EN; + UDC_EP_NUM_REG = UDC_EP_DIR; + status = 0; + VDBG("GET_STATUS, interface %d\n", u.r.wIndex); + /* next, status stage */ + break; + default: +delegate: + /* activate the ep0out fifo right away */ + if (!udc->ep0_in && u.r.wLength) { + UDC_EP_NUM_REG = 0; + UDC_CTRL_REG = UDC_SET_FIFO_EN; + } + + /* gadget drivers see class/vendor specific requests, + * {SET,GET}_{INTERFACE,DESCRIPTOR,CONFIGURATION}, + * and more + */ + VDBG("SETUP %02x.%02x v%04x i%04x l%04x\n", + u.r.bRequestType, u.r.bRequest, + u.r.wValue, u.r.wIndex, u.r.wLength); + + /* The gadget driver may return an error here, + * causing an immediate protocol stall. + * + * Else it must issue a response, either queueing a + * response buffer for the DATA stage, or halting ep0 + * (causing a protocol stall, not a real halt). A + * zero length buffer means no DATA stage. + * + * It's fine to issue that response after the setup() + * call returns, and this IRQ was handled. + */ + udc->ep0_setup = 1; + spin_unlock(&udc->lock); + status = udc->driver->setup (&udc->gadget, &u.r); + spin_lock(&udc->lock); + udc->ep0_setup = 0; + } + + if (status < 0) { +do_stall: + VDBG("req %02x.%02x protocol STALL; stat %d\n", + u.r.bRequestType, u.r.bRequest, status); + if (udc->ep0_set_config) { + if (udc->ep0_reset_config) + WARN("error resetting config?\n"); + else + UDC_SYSCON2_REG = UDC_CLR_CFG; + } + UDC_SYSCON2_REG = UDC_STALL_CMD; + udc->ep0_pending = 0; + } + } +} + +/*-------------------------------------------------------------------------*/ + +#define OTG_FLAGS (UDC_B_HNP_ENABLE|UDC_A_HNP_SUPPORT|UDC_A_ALT_HNP_SUPPORT) + +static void devstate_irq(struct omap_udc *udc, u16 irq_src) +{ + u16 devstat, change; + + devstat = UDC_DEVSTAT_REG; + change = devstat ^ udc->devstat; + udc->devstat = devstat; + + if (change & (UDC_USB_RESET|UDC_ATT)) { + udc_quiesce(udc); + + if (change & UDC_ATT) { + /* driver for any external transceiver will + * have called omap_vbus_session() already + */ + if (devstat & UDC_ATT) { + udc->gadget.speed = USB_SPEED_FULL; + VDBG("connect\n"); + if (!udc->transceiver) + pullup_enable(udc); + // if (driver->connect) call it + } else if (udc->gadget.speed != USB_SPEED_UNKNOWN) { + udc->gadget.speed = USB_SPEED_UNKNOWN; + if (!udc->transceiver) + pullup_disable(udc); + DBG("disconnect, gadget %s\n", + udc->driver->driver.name); + if (udc->driver->disconnect) { + spin_unlock(&udc->lock); + udc->driver->disconnect(&udc->gadget); + spin_lock(&udc->lock); + } + } + change &= ~UDC_ATT; + } + + if (change & UDC_USB_RESET) { + if (devstat & UDC_USB_RESET) { + VDBG("RESET=1\n"); + } else { + udc->gadget.speed = USB_SPEED_FULL; + INFO("USB reset done, gadget %s\n", + udc->driver->driver.name); + /* ep0 traffic is legal from now on */ + UDC_IRQ_EN_REG = UDC_DS_CHG_IE | UDC_EP0_IE; + } + change &= ~UDC_USB_RESET; + } + } + if (change & UDC_SUS) { + if (udc->gadget.speed != USB_SPEED_UNKNOWN) { + // FIXME tell isp1301 to suspend/resume (?) + if (devstat & UDC_SUS) { + VDBG("suspend\n"); + update_otg(udc); + /* HNP could be under way already */ + if (udc->gadget.speed == USB_SPEED_FULL + && udc->driver->suspend) { + spin_unlock(&udc->lock); + udc->driver->suspend(&udc->gadget); + spin_lock(&udc->lock); + } + } else { + VDBG("resume\n"); + if (udc->gadget.speed == USB_SPEED_FULL + && udc->driver->resume) { + spin_unlock(&udc->lock); + udc->driver->resume(&udc->gadget); + spin_lock(&udc->lock); + } + } + } + change &= ~UDC_SUS; + } + if (change & OTG_FLAGS) { + update_otg(udc); + change &= ~OTG_FLAGS; + } + + change &= ~(UDC_CFG|UDC_DEF|UDC_ADD); + if (change) + VDBG("devstat %03x, ignore change %03x\n", + devstat, change); + + UDC_IRQ_SRC_REG = UDC_DS_CHG; +} + +static irqreturn_t +omap_udc_irq(int irq, void *_udc, struct pt_regs *r) +{ + struct omap_udc *udc = _udc; + u16 irq_src; + irqreturn_t status = IRQ_NONE; + unsigned long flags; + + spin_lock_irqsave(&udc->lock, flags); + irq_src = UDC_IRQ_SRC_REG; + + /* Device state change (usb ch9 stuff) */ + if (irq_src & UDC_DS_CHG) { + devstate_irq(_udc, irq_src); + status = IRQ_HANDLED; + irq_src &= ~UDC_DS_CHG; + } + + /* EP0 control transfers */ + if (irq_src & (UDC_EP0_RX|UDC_SETUP|UDC_EP0_TX)) { + ep0_irq(_udc, irq_src); + status = IRQ_HANDLED; + irq_src &= ~(UDC_EP0_RX|UDC_SETUP|UDC_EP0_TX); + } + + /* DMA transfer completion */ + if (use_dma && (irq_src & (UDC_TXN_DONE|UDC_RXN_CNT|UDC_RXN_EOT))) { + dma_irq(_udc, irq_src); + status = IRQ_HANDLED; + irq_src &= ~(UDC_TXN_DONE|UDC_RXN_CNT|UDC_RXN_EOT); + } + + irq_src &= ~(UDC_SOF|UDC_EPN_TX|UDC_EPN_RX); + if (irq_src) + DBG("udc_irq, unhandled %03x\n", irq_src); + spin_unlock_irqrestore(&udc->lock, flags); + + return status; +} + +static irqreturn_t +omap_udc_pio_irq(int irq, void *_dev, struct pt_regs *r) +{ + u16 epn_stat, irq_src; + irqreturn_t status = IRQ_NONE; + struct omap_ep *ep; + int epnum; + struct omap_udc *udc = _dev; + struct omap_req *req; + unsigned long flags; + + spin_lock_irqsave(&udc->lock, flags); + epn_stat = UDC_EPN_STAT_REG; + irq_src = UDC_IRQ_SRC_REG; + + /* handle OUT first, to avoid some wasteful NAKs */ + if (irq_src & UDC_EPN_RX) { + epnum = (epn_stat >> 8) & 0x0f; + UDC_IRQ_SRC_REG = UDC_EPN_RX; + status = IRQ_HANDLED; + ep = &udc->ep[epnum]; + ep->irqs++; + + if (!list_empty(&ep->queue)) { + UDC_EP_NUM_REG = epnum | UDC_EP_SEL; + if ((UDC_STAT_FLG_REG & UDC_ACK)) { + int stat; + req = container_of(ep->queue.next, + struct omap_req, queue); + stat = read_fifo(ep, req); + // FIXME double buffered PIO OUT should work + } + UDC_EP_NUM_REG = epnum; + } + } + + /* then IN transfers */ + if (irq_src & UDC_EPN_TX) { + epnum = epn_stat & 0x0f; + UDC_IRQ_SRC_REG = UDC_EPN_TX; + status = IRQ_HANDLED; + ep = &udc->ep[16 + epnum]; + ep->irqs++; + ep->ackwait = 0; + + if (!list_empty(&ep->queue)) { + UDC_EP_NUM_REG = epnum | UDC_EP_DIR | UDC_EP_SEL; + if ((UDC_STAT_FLG_REG & UDC_ACK)) { + req = container_of(ep->queue.next, + struct omap_req, queue); + (void) write_fifo(ep, req); + } + UDC_EP_NUM_REG = epnum | UDC_EP_DIR; + /* 6 wait states before it'll tx */ + } + } + + spin_unlock_irqrestore(&udc->lock, flags); + return status; +} + +#ifdef USE_ISO +static irqreturn_t +omap_udc_iso_irq(int irq, void *_dev, struct pt_regs *r) +{ + struct omap_udc *udc = _dev; + struct omap_ep *ep; + int pending = 0; + unsigned long flags; + + spin_lock_irqsave(&udc->lock, flags); + + /* handle all non-DMA ISO transfers */ + list_for_each_entry (ep, &udc->iso, iso) { + u16 stat; + struct omap_req *req; + + if (ep->has_dma || list_empty(&ep->queue)) + continue; + req = list_entry(ep->queue.next, struct omap_req, queue); + + use_ep(ep, UDC_EP_SEL); + stat = UDC_STAT_FLG_REG; + + /* NOTE: like the other controller drivers, this isn't + * currently reporting lost or damaged frames. + */ + if (ep->bEndpointAddress & USB_DIR_IN) { + if (stat & UDC_MISS_IN) + /* done(ep, req, -EPROTO) */; + else + write_fifo(ep, req); + } else { + int status = 0; + + if (stat & UDC_NO_RXPACKET) + status = -EREMOTEIO; + else if (stat & UDC_ISO_ERR) + status = -EILSEQ; + else if (stat & UDC_DATA_FLUSH) + status = -ENOSR; + + if (status) + /* done(ep, req, status) */; + else + read_fifo(ep, req); + } + deselect_ep(); + /* 6 wait states before next EP */ + + ep->irqs++; + if (!list_empty(&ep->queue)) + pending = 1; + } + if (!pending) + UDC_IRQ_EN_REG &= ~UDC_SOF_IE; + UDC_IRQ_SRC_REG = UDC_SOF; + + spin_unlock_irqrestore(&udc->lock, flags); + return IRQ_HANDLED; +} +#endif + +/*-------------------------------------------------------------------------*/ + +static struct omap_udc *udc; + +int usb_gadget_register_driver (struct usb_gadget_driver *driver) +{ + int status = -ENODEV; + struct omap_ep *ep; + unsigned long flags; + + /* basic sanity tests */ + if (!udc) + return -ENODEV; + if (!driver + // FIXME if otg, check: driver->is_otg + || driver->speed < USB_SPEED_FULL + || !driver->bind + || !driver->unbind + || !driver->setup) + return -EINVAL; + + spin_lock_irqsave(&udc->lock, flags); + if (udc->driver) { + spin_unlock_irqrestore(&udc->lock, flags); + return -EBUSY; + } + + /* reset state */ + list_for_each_entry (ep, &udc->gadget.ep_list, ep.ep_list) { + ep->irqs = 0; + if (ep->bmAttributes == USB_ENDPOINT_XFER_ISOC) + continue; + use_ep(ep, 0); + UDC_CTRL_REG = UDC_SET_HALT; + } + udc->ep0_pending = 0; + udc->ep[0].irqs = 0; + udc->softconnect = 1; + + /* hook up the driver */ + driver->driver.bus = 0; + udc->driver = driver; + udc->gadget.dev.driver = &driver->driver; + spin_unlock_irqrestore(&udc->lock, flags); + + status = driver->bind (&udc->gadget); + if (status) { + DBG("bind to %s --> %d\n", driver->driver.name, status); + udc->gadget.dev.driver = 0; + udc->driver = 0; + goto done; + } + DBG("bound to driver %s\n", driver->driver.name); + + UDC_IRQ_SRC_REG = UDC_IRQ_SRC_MASK; + + /* connect to bus through transceiver */ + if (udc->transceiver) { + status = otg_set_peripheral(udc->transceiver, &udc->gadget); + if (status < 0) { + ERR("can't bind to transceiver\n"); + driver->unbind (&udc->gadget); + udc->gadget.dev.driver = 0; + udc->driver = 0; + goto done; + } + } else { + if (can_pullup(udc)) + pullup_enable (udc); + else + pullup_disable (udc); + } + +done: + return status; +} +EXPORT_SYMBOL(usb_gadget_register_driver); + +int usb_gadget_unregister_driver (struct usb_gadget_driver *driver) +{ + unsigned long flags; + int status = -ENODEV; + + if (!udc) + return -ENODEV; + if (!driver || driver != udc->driver) + return -EINVAL; + + if (udc->transceiver) + (void) otg_set_peripheral(udc->transceiver, 0); + else + pullup_disable(udc); + + spin_lock_irqsave(&udc->lock, flags); + udc_quiesce(udc); + spin_unlock_irqrestore(&udc->lock, flags); + + driver->unbind(&udc->gadget); + udc->gadget.dev.driver = 0; + udc->driver = 0; + + + DBG("unregistered driver '%s'\n", driver->driver.name); + return status; +} +EXPORT_SYMBOL(usb_gadget_unregister_driver); + + +/*-------------------------------------------------------------------------*/ + +#ifdef CONFIG_USB_OMAP_PROC + +#include + +static const char proc_filename[] = "driver/udc"; + +#define FOURBITS "%s%s%s%s" +#define EIGHTBITS FOURBITS FOURBITS + +static void proc_ep_show(struct seq_file *s, struct omap_ep *ep) +{ + u16 stat_flg; + struct omap_req *req; + char buf[20]; + + use_ep(ep, 0); + + if (use_dma && ep->has_dma) + snprintf(buf, sizeof buf, "(%cxdma%d lch%d) ", + (ep->bEndpointAddress & USB_DIR_IN) ? 't' : 'r', + ep->dma_channel - 1, ep->lch); + else + buf[0] = 0; + + stat_flg = UDC_STAT_FLG_REG; + seq_printf(s, + "\n%s %sirqs %ld stat %04x " EIGHTBITS FOURBITS "%s\n", + ep->name, buf, ep->irqs, stat_flg, + (stat_flg & UDC_NO_RXPACKET) ? "no_rxpacket " : "", + (stat_flg & UDC_MISS_IN) ? "miss_in " : "", + (stat_flg & UDC_DATA_FLUSH) ? "data_flush " : "", + (stat_flg & UDC_ISO_ERR) ? "iso_err " : "", + (stat_flg & UDC_ISO_FIFO_EMPTY) ? "iso_fifo_empty " : "", + (stat_flg & UDC_ISO_FIFO_FULL) ? "iso_fifo_full " : "", + (stat_flg & UDC_EP_HALTED) ? "HALT " : "", + (stat_flg & UDC_STALL) ? "STALL " : "", + (stat_flg & UDC_NAK) ? "NAK " : "", + (stat_flg & UDC_ACK) ? "ACK " : "", + (stat_flg & UDC_FIFO_EN) ? "fifo_en " : "", + (stat_flg & UDC_NON_ISO_FIFO_EMPTY) ? "fifo_empty " : "", + (stat_flg & UDC_NON_ISO_FIFO_FULL) ? "fifo_full " : ""); + + if (list_empty (&ep->queue)) + seq_printf(s, "\t(queue empty)\n"); + else + list_for_each_entry (req, &ep->queue, queue) + seq_printf(s, "\treq %p len %d/%d buf %p\n", + &req->req, req->req.actual, + req->req.length, req->req.buf); +} + +static char *trx_mode(unsigned m) +{ + switch (m) { + case 3: + case 0: return "6wire"; + case 1: return "4wire"; + case 2: return "3wire"; + default: return "unknown"; + } +} + +static int proc_udc_show(struct seq_file *s, void *_) +{ + u32 tmp; + struct omap_ep *ep; + unsigned long flags; + + spin_lock_irqsave(&udc->lock, flags); + + seq_printf(s, "%s, version: " DRIVER_VERSION +#ifdef USE_ISO + " (iso)" +#endif + "%s\n", + driver_desc, + use_dma ? " (dma)" : ""); + + tmp = UDC_REV_REG & 0xff; + seq_printf(s, + "UDC rev %d.%d, OTG rev %d.%d, fifo mode %d, gadget %s\n" + "hmc %d, transceiver %08x %s\n", + tmp >> 4, tmp & 0xf, + OTG_REV_REG >> 4, OTG_REV_REG & 0xf, + fifo_mode, + udc->driver ? udc->driver->driver.name : "(none)", + HMC, USB_TRANSCEIVER_CTRL_REG, + udc->transceiver ? udc->transceiver->label : ""); + + /* OTG controller registers */ + tmp = OTG_SYSCON_1_REG; + seq_printf(s, "otg_syscon1 %08x usb2 %s, usb1 %s, usb0 %s," + FOURBITS "\n", tmp, + trx_mode(USB2_TRX_MODE(tmp)), + trx_mode(USB1_TRX_MODE(tmp)), + trx_mode(USB0_TRX_MODE(tmp)), + (tmp & OTG_IDLE_EN) ? " !otg" : "", + (tmp & HST_IDLE_EN) ? " !host" : "", + (tmp & DEV_IDLE_EN) ? " !dev" : "", + (tmp & OTG_RESET_DONE) ? " reset_done" : " reset_active"); + tmp = OTG_SYSCON_2_REG; + seq_printf(s, "otg_syscon2 %08x%s" EIGHTBITS + " b_ase_brst=%d hmc=%d\n", tmp, + (tmp & OTG_EN) ? " otg_en" : "", + (tmp & USBX_SYNCHRO) ? " synchro" : "", + // much more SRP stuff + (tmp & SRP_DATA) ? " srp_data" : "", + (tmp & SRP_VBUS) ? " srp_vbus" : "", + (tmp & OTG_PADEN) ? " otg_paden" : "", + (tmp & HMC_PADEN) ? " hmc_paden" : "", + (tmp & UHOST_EN) ? " uhost_en" : "", + (tmp & HMC_TLLSPEED) ? " tllspeed" : "", + (tmp & HMC_TLLATTACH) ? " tllattach" : "", + B_ASE_BRST(tmp), + OTG_HMC(tmp)); + tmp = OTG_CTRL_REG; + seq_printf(s, "otg_ctrl %06x" EIGHTBITS EIGHTBITS "%s\n", tmp, + (tmp & OTG_ASESSVLD) ? " asess" : "", + (tmp & OTG_BSESSEND) ? " bsess_end" : "", + (tmp & OTG_BSESSVLD) ? " bsess" : "", + (tmp & OTG_VBUSVLD) ? " vbus" : "", + (tmp & OTG_ID) ? " id" : "", + (tmp & OTG_DRIVER_SEL) ? " DEVICE" : " HOST", + (tmp & OTG_A_SETB_HNPEN) ? " a_setb_hnpen" : "", + (tmp & OTG_A_BUSREQ) ? " a_bus" : "", + (tmp & OTG_B_HNPEN) ? " b_hnpen" : "", + (tmp & OTG_B_BUSREQ) ? " b_bus" : "", + (tmp & OTG_BUSDROP) ? " busdrop" : "", + (tmp & OTG_PULLDOWN) ? " down" : "", + (tmp & OTG_PULLUP) ? " up" : "", + (tmp & OTG_DRV_VBUS) ? " drv" : "", + (tmp & OTG_PD_VBUS) ? " pd_vb" : "", + (tmp & OTG_PU_VBUS) ? " pu_vb" : "", + (tmp & OTG_PU_ID) ? " pu_id" : "" + ); + tmp = OTG_IRQ_EN_REG; + seq_printf(s, "otg_irq_en %04x" "\n", tmp); + tmp = OTG_IRQ_SRC_REG; + seq_printf(s, "otg_irq_src %04x" "\n", tmp); + tmp = OTG_OUTCTRL_REG; + seq_printf(s, "otg_outctrl %04x" "\n", tmp); + tmp = OTG_TEST_REG; + seq_printf(s, "otg_test %04x" "\n", tmp); + + tmp = UDC_SYSCON1_REG; + seq_printf(s, "\nsyscon1 %04x" EIGHTBITS "\n", tmp, + (tmp & UDC_CFG_LOCK) ? " cfg_lock" : "", + (tmp & UDC_DATA_ENDIAN) ? " data_endian" : "", + (tmp & UDC_DMA_ENDIAN) ? " dma_endian" : "", + (tmp & UDC_NAK_EN) ? " nak" : "", + (tmp & UDC_AUTODECODE_DIS) ? " autodecode_dis" : "", + (tmp & UDC_SELF_PWR) ? " self_pwr" : "", + (tmp & UDC_SOFF_DIS) ? " soff_dis" : "", + (tmp & UDC_PULLUP_EN) ? " PULLUP" : ""); + // syscon2 is write-only + + /* UDC controller registers */ + if (!(tmp & UDC_PULLUP_EN)) { + seq_printf(s, "(suspended)\n"); + spin_unlock_irqrestore(&udc->lock, flags); + return 0; + } + + tmp = UDC_DEVSTAT_REG; + seq_printf(s, "devstat %04x" EIGHTBITS "%s%s\n", tmp, + (tmp & UDC_B_HNP_ENABLE) ? " b_hnp" : "", + (tmp & UDC_A_HNP_SUPPORT) ? " a_hnp" : "", + (tmp & UDC_A_ALT_HNP_SUPPORT) ? " a_alt_hnp" : "", + (tmp & UDC_R_WK_OK) ? " r_wk_ok" : "", + (tmp & UDC_USB_RESET) ? " usb_reset" : "", + (tmp & UDC_SUS) ? " SUS" : "", + (tmp & UDC_CFG) ? " CFG" : "", + (tmp & UDC_ADD) ? " ADD" : "", + (tmp & UDC_DEF) ? " DEF" : "", + (tmp & UDC_ATT) ? " ATT" : ""); + seq_printf(s, "sof %04x\n", UDC_SOF_REG); + tmp = UDC_IRQ_EN_REG; + seq_printf(s, "irq_en %04x" FOURBITS "%s\n", tmp, + (tmp & UDC_SOF_IE) ? " sof" : "", + (tmp & UDC_EPN_RX_IE) ? " epn_rx" : "", + (tmp & UDC_EPN_TX_IE) ? " epn_tx" : "", + (tmp & UDC_DS_CHG_IE) ? " ds_chg" : "", + (tmp & UDC_EP0_IE) ? " ep0" : ""); + tmp = UDC_IRQ_SRC_REG; + seq_printf(s, "irq_src %04x" EIGHTBITS "%s%s\n", tmp, + (tmp & UDC_TXN_DONE) ? " txn_done" : "", + (tmp & UDC_RXN_CNT) ? " rxn_cnt" : "", + (tmp & UDC_RXN_EOT) ? " rxn_eot" : "", + (tmp & UDC_SOF) ? " sof" : "", + (tmp & UDC_EPN_RX) ? " epn_rx" : "", + (tmp & UDC_EPN_TX) ? " epn_tx" : "", + (tmp & UDC_DS_CHG) ? " ds_chg" : "", + (tmp & UDC_SETUP) ? " setup" : "", + (tmp & UDC_EP0_RX) ? " ep0out" : "", + (tmp & UDC_EP0_TX) ? " ep0in" : ""); + if (use_dma) { + unsigned i; + + tmp = UDC_DMA_IRQ_EN_REG; + seq_printf(s, "dma_irq_en %04x%s" EIGHTBITS "\n", tmp, + (tmp & UDC_TX_DONE_IE(3)) ? " tx2_done" : "", + (tmp & UDC_RX_CNT_IE(3)) ? " rx2_cnt" : "", + (tmp & UDC_RX_EOT_IE(3)) ? " rx2_eot" : "", + + (tmp & UDC_TX_DONE_IE(2)) ? " tx1_done" : "", + (tmp & UDC_RX_CNT_IE(2)) ? " rx1_cnt" : "", + (tmp & UDC_RX_EOT_IE(2)) ? " rx1_eot" : "", + + (tmp & UDC_TX_DONE_IE(1)) ? " tx0_done" : "", + (tmp & UDC_RX_CNT_IE(1)) ? " rx0_cnt" : "", + (tmp & UDC_RX_EOT_IE(1)) ? " rx0_eot" : ""); + + tmp = UDC_RXDMA_CFG_REG; + seq_printf(s, "rxdma_cfg %04x\n", tmp); + if (tmp) { + for (i = 0; i < 3; i++) { + if ((tmp & (0x0f << (i * 4))) == 0) + continue; + seq_printf(s, "rxdma[%d] %04x\n", i, + UDC_RXDMA_REG(i + 1)); + } + } + tmp = UDC_TXDMA_CFG_REG; + seq_printf(s, "txdma_cfg %04x\n", tmp); + if (tmp) { + for (i = 0; i < 3; i++) { + if (!(tmp & (0x0f << (i * 4)))) + continue; + seq_printf(s, "txdma[%d] %04x\n", i, + UDC_TXDMA_REG(i + 1)); + } + } + } + + tmp = UDC_DEVSTAT_REG; + if (tmp & UDC_ATT) { + proc_ep_show(s, &udc->ep[0]); + if (tmp & UDC_ADD) { + list_for_each_entry (ep, &udc->gadget.ep_list, + ep.ep_list) { + if (ep->desc) + proc_ep_show(s, ep); + } + } + } + spin_unlock_irqrestore(&udc->lock, flags); + return 0; +} + +static int proc_udc_open(struct inode *inode, struct file *file) +{ + return single_open(file, proc_udc_show, 0); +} + +static struct file_operations proc_ops = { + .open = proc_udc_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +static void create_proc_file(void) +{ + struct proc_dir_entry *pde; + + pde = create_proc_entry (proc_filename, 0, NULL); + if (pde) + pde->proc_fops = &proc_ops; +} + +static void remove_proc_file(void) +{ + remove_proc_entry(proc_filename, 0); +} + +#else + +static inline void create_proc_file(void) {} +static inline void remove_proc_file(void) {} + +#endif + +/*-------------------------------------------------------------------------*/ + +/* Before this controller can enumerate, we need to pick an endpoint + * configuration, or "fifo_mode" That involves allocating 2KB of packet + * buffer space among the endpoints we'll be operating. + */ +static unsigned __init +omap_ep_setup(char *name, u8 addr, u8 type, + unsigned buf, unsigned maxp, int dbuf) +{ + struct omap_ep *ep; + u16 epn_rxtx = 0; + + /* OUT endpoints first, then IN */ + ep = &udc->ep[addr & 0xf]; + if (addr & USB_DIR_IN) + ep += 16; + + /* in case of ep init table bugs */ + BUG_ON(ep->name[0]); + + /* chip setup ... bit values are same for IN, OUT */ + if (type == USB_ENDPOINT_XFER_ISOC) { + switch (maxp) { + case 8: epn_rxtx = 0 << 12; break; + case 16: epn_rxtx = 1 << 12; break; + case 32: epn_rxtx = 2 << 12; break; + case 64: epn_rxtx = 3 << 12; break; + case 128: epn_rxtx = 4 << 12; break; + case 256: epn_rxtx = 5 << 12; break; + case 512: epn_rxtx = 6 << 12; break; + default: BUG(); + } + epn_rxtx |= UDC_EPN_RX_ISO; + dbuf = 1; + } else { + /* pio-out could potentially double-buffer, + * as can (should!) DMA-IN + */ + if (!use_dma || (addr & USB_DIR_IN)) + dbuf = 0; + + switch (maxp) { + case 8: epn_rxtx = 0 << 12; break; + case 16: epn_rxtx = 1 << 12; break; + case 32: epn_rxtx = 2 << 12; break; + case 64: epn_rxtx = 3 << 12; break; + default: BUG(); + } + if (dbuf && addr) + epn_rxtx |= UDC_EPN_RX_DB; + } + if (addr) + epn_rxtx |= UDC_EPN_RX_VALID; + BUG_ON(buf & 0x07); + epn_rxtx |= buf >> 3; + + DBG("%s addr %02x rxtx %04x maxp %d%s buf %d\n", + name, addr, epn_rxtx, maxp, dbuf ? "x2" : "", buf); + + if (addr & USB_DIR_IN) + UDC_EP_TX_REG(addr & 0xf) = epn_rxtx; + else + UDC_EP_RX_REG(addr) = epn_rxtx; + + /* next endpoint's buffer starts after this one's */ + buf += maxp; + if (dbuf) + buf += maxp; + BUG_ON(buf > 2048); + + /* set up driver data structures */ + BUG_ON(strlen(name) >= sizeof ep->name); + strlcpy(ep->name, name, sizeof ep->name); + INIT_LIST_HEAD(&ep->queue); + INIT_LIST_HEAD(&ep->iso); + ep->bEndpointAddress = addr; + ep->bmAttributes = type; + ep->double_buf = dbuf; + ep->udc = udc; + + ep->ep.name = ep->name; + ep->ep.ops = &omap_ep_ops; + ep->ep.maxpacket = ep->maxpacket = maxp; + list_add_tail (&ep->ep.ep_list, &udc->gadget.ep_list); + + return buf; +} + +static void omap_udc_release(struct device *dev) +{ + complete(udc->done); + kfree (udc); + udc = 0; +} + +static int __init +omap_udc_setup(struct platform_device *odev, struct otg_transceiver *xceiv) +{ + unsigned tmp, buf; + + /* abolish any previous hardware state */ + UDC_SYSCON1_REG = 0; + UDC_IRQ_EN_REG = 0; + UDC_IRQ_SRC_REG = UDC_IRQ_SRC_MASK; + UDC_DMA_IRQ_EN_REG = 0; + UDC_RXDMA_CFG_REG = 0; + UDC_TXDMA_CFG_REG = 0; + + /* UDC_PULLUP_EN gates the chip clock */ + // OTG_SYSCON_1_REG |= DEV_IDLE_EN; + + udc = kmalloc (sizeof *udc, SLAB_KERNEL); + if (!udc) + return -ENOMEM; + + memset(udc, 0, sizeof *udc); + spin_lock_init (&udc->lock); + + udc->gadget.ops = &omap_gadget_ops; + udc->gadget.ep0 = &udc->ep[0].ep; + INIT_LIST_HEAD(&udc->gadget.ep_list); + INIT_LIST_HEAD(&udc->iso); + udc->gadget.speed = USB_SPEED_UNKNOWN; + udc->gadget.name = driver_name; + + device_initialize(&udc->gadget.dev); + strcpy (udc->gadget.dev.bus_id, "gadget"); + udc->gadget.dev.release = omap_udc_release; + udc->gadget.dev.parent = &odev->dev; + if (use_dma) + udc->gadget.dev.dma_mask = odev->dev.dma_mask; + + udc->transceiver = xceiv; + + /* ep0 is special; put it right after the SETUP buffer */ + buf = omap_ep_setup("ep0", 0, USB_ENDPOINT_XFER_CONTROL, + 8 /* after SETUP */, 64 /* maxpacket */, 0); + list_del_init(&udc->ep[0].ep.ep_list); + + /* initially disable all non-ep0 endpoints */ + for (tmp = 1; tmp < 15; tmp++) { + UDC_EP_RX_REG(tmp) = 0; + UDC_EP_TX_REG(tmp) = 0; + } + +#define OMAP_BULK_EP(name,addr) \ + buf = omap_ep_setup(name "-bulk", addr, \ + USB_ENDPOINT_XFER_BULK, buf, 64, 1); +#define OMAP_INT_EP(name,addr, maxp) \ + buf = omap_ep_setup(name "-int", addr, \ + USB_ENDPOINT_XFER_INT, buf, maxp, 0); +#define OMAP_ISO_EP(name,addr, maxp) \ + buf = omap_ep_setup(name "-iso", addr, \ + USB_ENDPOINT_XFER_ISOC, buf, maxp, 1); + + switch (fifo_mode) { + case 0: + OMAP_BULK_EP("ep1in", USB_DIR_IN | 1); + OMAP_BULK_EP("ep2out", USB_DIR_OUT | 2); + OMAP_INT_EP("ep3in", USB_DIR_IN | 3, 16); + break; + case 1: + OMAP_BULK_EP("ep1in", USB_DIR_IN | 1); + OMAP_BULK_EP("ep2out", USB_DIR_OUT | 2); + OMAP_BULK_EP("ep3in", USB_DIR_IN | 3); + OMAP_BULK_EP("ep4out", USB_DIR_OUT | 4); + + OMAP_BULK_EP("ep5in", USB_DIR_IN | 5); + OMAP_BULK_EP("ep5out", USB_DIR_OUT | 5); + OMAP_BULK_EP("ep6in", USB_DIR_IN | 6); + OMAP_BULK_EP("ep6out", USB_DIR_OUT | 6); + + OMAP_BULK_EP("ep7in", USB_DIR_IN | 7); + OMAP_BULK_EP("ep7out", USB_DIR_OUT | 7); + OMAP_BULK_EP("ep8in", USB_DIR_IN | 8); + OMAP_BULK_EP("ep8out", USB_DIR_OUT | 8); + + OMAP_INT_EP("ep9in", USB_DIR_IN | 9, 16); + OMAP_INT_EP("ep10out", USB_DIR_IN | 10, 16); + OMAP_INT_EP("ep11in", USB_DIR_IN | 9, 16); + OMAP_INT_EP("ep12out", USB_DIR_IN | 10, 16); + break; + +#ifdef USE_ISO + case 2: /* mixed iso/bulk */ + OMAP_ISO_EP("ep1in", USB_DIR_IN | 1, 256); + OMAP_ISO_EP("ep2out", USB_DIR_OUT | 2, 256); + OMAP_ISO_EP("ep3in", USB_DIR_IN | 3, 128); + OMAP_ISO_EP("ep4out", USB_DIR_OUT | 4, 128); + + OMAP_INT_EP("ep5in", USB_DIR_IN | 5, 16); + + OMAP_BULK_EP("ep6in", USB_DIR_IN | 6); + OMAP_BULK_EP("ep7out", USB_DIR_OUT | 7); + OMAP_INT_EP("ep8in", USB_DIR_IN | 8, 16); + break; + case 3: /* mixed bulk/iso */ + OMAP_BULK_EP("ep1in", USB_DIR_IN | 1); + OMAP_BULK_EP("ep2out", USB_DIR_OUT | 2); + OMAP_INT_EP("ep3in", USB_DIR_IN | 3, 16); + + OMAP_BULK_EP("ep4in", USB_DIR_IN | 4); + OMAP_BULK_EP("ep5out", USB_DIR_OUT | 5); + OMAP_INT_EP("ep6in", USB_DIR_IN | 6, 16); + + OMAP_ISO_EP("ep7in", USB_DIR_IN | 7, 256); + OMAP_ISO_EP("ep8out", USB_DIR_OUT | 8, 256); + OMAP_INT_EP("ep9in", USB_DIR_IN | 9, 16); + break; +#endif + + /* add more modes as needed */ + + default: + ERR("unsupported fifo_mode #%d\n", fifo_mode); + return -ENODEV; + } + UDC_SYSCON1_REG = UDC_CFG_LOCK|UDC_SELF_PWR; + INFO("fifo mode %d, %d bytes not used\n", fifo_mode, 2048 - buf); + return 0; +} + +static int __init omap_udc_probe(struct device *dev) +{ + struct platform_device *odev = to_platform_device(dev); + int status = -ENODEV; + int hmc; + struct otg_transceiver *xceiv = 0; + const char *type = 0; + struct omap_usb_config *config = dev->platform_data; + + /* NOTE: "knows" the order of the resources! */ + if (!request_mem_region(odev->resource[0].start, + odev->resource[0].end - odev->resource[0].start + 1, + driver_name)) { + DBG("request_mem_region failed\n"); + return -EBUSY; + } + + INFO("OMAP UDC rev %d.%d, OTG rev %d.%d, %s receptacle\n", + UDC_REV_REG >> 4, UDC_REV_REG & 0xf, + OTG_REV_REG >> 4, OTG_REV_REG & 0xf, + config->otg ? "Mini-AB" : "B/Mini-B"); + + /* use the mode given to us by board init code */ + hmc = HMC; + switch (hmc) { + case 3: + case 11: + case 19: + case 25: + xceiv = otg_get_transceiver(); + if (!xceiv) { + DBG("external transceiver not registered!\n"); + goto cleanup0; + } + type = xceiv->label; + break; + case 0: /* POWERUP DEFAULT == 0 */ + case 4: + case 12: + case 20: + type = "INTEGRATED"; + break; + case 21: /* internal loopback */ + type = "(loopback)"; + break; + case 14: /* transceiverless */ + type = "(none)"; + break; + + default: + ERR("unrecognized UDC HMC mode %d\n", hmc); + return -ENODEV; + } + INFO("hmc mode %d, transceiver %s\n", hmc, type); + + /* a "gadget" abstracts/virtualizes the controller */ + status = omap_udc_setup(odev, xceiv); + if (status) { + goto cleanup0; + } + xceiv = 0; + // "udc" is now valid + pullup_disable(udc); + udc->gadget.is_otg = (config->otg != 0); + + /* USB general purpose IRQ: ep0, state changes, dma, etc */ + status = request_irq(odev->resource[1].start, omap_udc_irq, + SA_SAMPLE_RANDOM, driver_name, udc); + if (status != 0) { + ERR( "can't get irq %ld, err %d\n", + odev->resource[1].start, status); + goto cleanup1; + } + + /* USB "non-iso" IRQ (PIO for all but ep0) */ + status = request_irq(odev->resource[2].start, omap_udc_pio_irq, + SA_SAMPLE_RANDOM, "omap_udc pio", udc); + if (status != 0) { + ERR( "can't get irq %ld, err %d\n", + odev->resource[2].start, status); + goto cleanup2; + } +#ifdef USE_ISO + status = request_irq(odev->resource[3].start, omap_udc_iso_irq, + SA_INTERRUPT, "omap_udc iso", udc); + if (status != 0) { + ERR("can't get irq %ld, err %d\n", + odev->resource[3].start, status); + goto cleanup3; + } +#endif + + create_proc_file(); + device_add(&udc->gadget.dev); + return 0; + +#ifdef USE_ISO +cleanup3: + free_irq(odev->resource[2].start, udc); +#endif + +cleanup2: + free_irq(odev->resource[1].start, udc); + +cleanup1: + kfree (udc); + udc = 0; + +cleanup0: + if (xceiv) + put_device(xceiv->dev); + release_mem_region(odev->resource[0].start, + odev->resource[0].end - odev->resource[0].start + 1); + return status; +} + +static int __exit omap_udc_remove(struct device *dev) +{ + struct platform_device *odev = to_platform_device(dev); + DECLARE_COMPLETION(done); + + if (!udc) + return -ENODEV; + + udc->done = &done; + + pullup_disable(udc); + if (udc->transceiver) { + put_device(udc->transceiver->dev); + udc->transceiver = 0; + } + UDC_SYSCON1_REG = 0; + + remove_proc_file(); + +#ifdef USE_ISO + free_irq(odev->resource[3].start, udc); +#endif + free_irq(odev->resource[2].start, udc); + free_irq(odev->resource[1].start, udc); + + release_mem_region(odev->resource[0].start, + odev->resource[0].end - odev->resource[0].start + 1); + + device_unregister(&udc->gadget.dev); + wait_for_completion(&done); + + return 0; +} + +/* suspend/resume/wakeup from sysfs (echo > power/state) */ + +static int omap_udc_suspend(struct device *dev, u32 state, u32 level) +{ + if (level != 0) + return 0; + + DBG("suspend, state %d\n", state); + omap_pullup(&udc->gadget, 0); + udc->gadget.dev.power.power_state = 3; + udc->gadget.dev.parent->power.power_state = 3; + return 0; +} + +static int omap_udc_resume(struct device *dev, u32 level) +{ + if (level != 0) + return 0; + + DBG("resume + wakeup/SRP\n"); + udc->gadget.dev.parent->power.power_state = 0; + udc->gadget.dev.power.power_state = 0; + omap_pullup(&udc->gadget, 1); + + /* maybe the host would enumerate us if we nudged it */ + msleep(100); + return omap_wakeup(&udc->gadget); +} + +/*-------------------------------------------------------------------------*/ + +static struct device_driver udc_driver = { + .name = (char *) driver_name, + .bus = &platform_bus_type, + .probe = omap_udc_probe, + .remove = __exit_p(omap_udc_remove), + .suspend = omap_udc_suspend, + .resume = omap_udc_resume, +}; + +static int __init udc_init(void) +{ + /* should work on many OMAP systems with at most minor changes, + * but the 1510 doesn't have an OTG controller. + */ + if (cpu_is_omap1510()) { + DBG("no OMAP1510 support yet\n"); + return -ENODEV; + } + INFO("%s, version: " DRIVER_VERSION "%s\n", driver_desc, + use_dma ? " (dma)" : ""); + return driver_register(&udc_driver); +} +module_init(udc_init); + +static void __exit udc_exit(void) +{ + driver_unregister(&udc_driver); +} +module_exit(udc_exit); + +MODULE_DESCRIPTION(DRIVER_DESC); +MODULE_LICENSE("GPL"); + diff --git a/drivers/usb/gadget/omap_udc.h b/drivers/usb/gadget/omap_udc.h new file mode 100644 index 000000000..bd5420cd0 --- /dev/null +++ b/drivers/usb/gadget/omap_udc.h @@ -0,0 +1,199 @@ +/* + * omap_udc.h -- for omap 3.2 udc, with OTG support + * + * 2004 (C) Texas Instruments, Inc. + * 2004 (C) David Brownell + */ + +/* + * USB device/endpoint management registers + */ +#define UDC_REG(offset) __REG16(UDC_BASE + (offset)) + +#define UDC_REV_REG UDC_REG(0x0) /* Revision */ +#define UDC_EP_NUM_REG UDC_REG(0x4) /* Which endpoint */ +# define UDC_SETUP_SEL (1 << 6) +# define UDC_EP_SEL (1 << 5) +# define UDC_EP_DIR (1 << 4) + /* low 4 bits for endpoint number */ +#define UDC_DATA_REG UDC_REG(0x08) /* Endpoint FIFO */ +#define UDC_CTRL_REG UDC_REG(0x0C) /* Endpoint control */ +# define UDC_CLR_HALT (1 << 7) +# define UDC_SET_HALT (1 << 6) +# define UDC_SET_FIFO_EN (1 << 2) +# define UDC_CLR_EP (1 << 1) +# define UDC_RESET_EP (1 << 0) +#define UDC_STAT_FLG_REG UDC_REG(0x10) /* Endpoint status */ +# define UDC_NO_RXPACKET (1 << 15) +# define UDC_MISS_IN (1 << 14) +# define UDC_DATA_FLUSH (1 << 13) +# define UDC_ISO_ERR (1 << 12) +# define UDC_ISO_FIFO_EMPTY (1 << 9) +# define UDC_ISO_FIFO_FULL (1 << 8) +# define UDC_EP_HALTED (1 << 6) +# define UDC_STALL (1 << 5) +# define UDC_NAK (1 << 4) +# define UDC_ACK (1 << 3) +# define UDC_FIFO_EN (1 << 2) +# define UDC_NON_ISO_FIFO_EMPTY (1 << 1) +# define UDC_NON_ISO_FIFO_FULL (1 << 0) +#define UDC_RXFSTAT_REG UDC_REG(0x14) /* OUT bytecount */ +#define UDC_SYSCON1_REG UDC_REG(0x18) /* System config 1 */ +# define UDC_CFG_LOCK (1 << 8) +# define UDC_DATA_ENDIAN (1 << 7) +# define UDC_DMA_ENDIAN (1 << 6) +# define UDC_NAK_EN (1 << 4) +# define UDC_AUTODECODE_DIS (1 << 3) +# define UDC_SELF_PWR (1 << 2) +# define UDC_SOFF_DIS (1 << 1) +# define UDC_PULLUP_EN (1 << 0) +#define UDC_SYSCON2_REG UDC_REG(0x1C) /* System config 2 */ +# define UDC_RMT_WKP (1 << 6) +# define UDC_STALL_CMD (1 << 5) +# define UDC_DEV_CFG (1 << 3) +# define UDC_CLR_CFG (1 << 2) +#define UDC_DEVSTAT_REG UDC_REG(0x20) /* Device status */ +# define UDC_B_HNP_ENABLE (1 << 9) +# define UDC_A_HNP_SUPPORT (1 << 8) +# define UDC_A_ALT_HNP_SUPPORT (1 << 7) +# define UDC_R_WK_OK (1 << 6) +# define UDC_USB_RESET (1 << 5) +# define UDC_SUS (1 << 4) +# define UDC_CFG (1 << 3) +# define UDC_ADD (1 << 2) +# define UDC_DEF (1 << 1) +# define UDC_ATT (1 << 0) +#define UDC_SOF_REG UDC_REG(0x24) /* Start of frame */ +# define UDC_FT_LOCK (1 << 12) +# define UDC_TS_OK (1 << 11) +# define UDC_TS 0x03ff +#define UDC_IRQ_EN_REG UDC_REG(0x28) /* Interrupt enable */ +# define UDC_SOF_IE (1 << 7) +# define UDC_EPN_RX_IE (1 << 5) +# define UDC_EPN_TX_IE (1 << 4) +# define UDC_DS_CHG_IE (1 << 3) +# define UDC_EP0_IE (1 << 0) +#define UDC_DMA_IRQ_EN_REG UDC_REG(0x2C) /* DMA irq enable */ + /* rx/tx dma channels numbered 1-3 not 0-2 */ +# define UDC_TX_DONE_IE(n) (1 << (4 * (n) - 2)) +# define UDC_RX_CNT_IE(n) (1 << (4 * (n) - 3)) +# define UDC_RX_EOT_IE(n) (1 << (4 * (n) - 4)) +#define UDC_IRQ_SRC_REG UDC_REG(0x30) /* Interrupt source */ +# define UDC_TXN_DONE (1 << 10) +# define UDC_RXN_CNT (1 << 9) +# define UDC_RXN_EOT (1 << 8) +# define UDC_SOF (1 << 7) +# define UDC_EPN_RX (1 << 5) +# define UDC_EPN_TX (1 << 4) +# define UDC_DS_CHG (1 << 3) +# define UDC_SETUP (1 << 2) +# define UDC_EP0_RX (1 << 1) +# define UDC_EP0_TX (1 << 0) +# define UDC_IRQ_SRC_MASK 0x7bf +#define UDC_EPN_STAT_REG UDC_REG(0x34) /* EP irq status */ +#define UDC_DMAN_STAT_REG UDC_REG(0x38) /* DMA irq status */ +# define UDC_DMA_RX_SB (1 << 12) +# define UDC_DMA_RX_SRC(x) (((x)>>8) & 0xf) +# define UDC_DMA_TX_SRC(x) (((x)>>0) & 0xf) + + +/* DMA configuration registers: up to three channels in each direction. */ +#define UDC_RXDMA_CFG_REG UDC_REG(0x40) /* 3 eps for RX DMA */ +#define UDC_TXDMA_CFG_REG UDC_REG(0x44) /* 3 eps for TX DMA */ +#define UDC_DATA_DMA_REG UDC_REG(0x48) /* rx/tx fifo addr */ + +/* rx/tx dma control, numbering channels 1-3 not 0-2 */ +#define UDC_TXDMA_REG(chan) UDC_REG(0x50 - 4 + 4 * (chan)) +# define UDC_TXN_EOT (1 << 15) /* bytes vs packets */ +# define UDC_TXN_START (1 << 14) /* start transfer */ +# define UDC_TXN_TSC 0x03ff /* units in xfer */ +#define UDC_RXDMA_REG(chan) UDC_REG(0x60 - 4 + 4 * (chan)) +# define UDC_RXN_STOP (1 << 15) /* enable EOT irq */ +# define UDC_RXN_TC 0x00ff /* packets in xfer */ + + +/* + * Endpoint configuration registers (used before CFG_LOCK is set) + * UDC_EP_TX_REG(0) is unused + */ +#define UDC_EP_RX_REG(endpoint) UDC_REG(0x80 + (endpoint)*4) +# define UDC_EPN_RX_VALID (1 << 15) +# define UDC_EPN_RX_DB (1 << 14) + /* buffer size in bits 13, 12 */ +# define UDC_EPN_RX_ISO (1 << 11) + /* buffer pointer in low 11 bits */ +#define UDC_EP_TX_REG(endpoint) UDC_REG(0xc0 + (endpoint)*4) + /* same bitfields as in RX_REG */ + +/*-------------------------------------------------------------------------*/ + +struct omap_req { + struct usb_request req; + struct list_head queue; + unsigned dma_bytes; + unsigned mapped:1; +}; + +struct omap_ep { + struct usb_ep ep; + struct list_head queue; + unsigned long irqs; + struct list_head iso; + const struct usb_endpoint_descriptor *desc; + char name[14]; + u16 maxpacket; + u8 bEndpointAddress; + u8 bmAttributes; + unsigned double_buf:1; + unsigned stopped:1; + unsigned ackwait:1; + unsigned has_dma:1; + u8 dma_channel; + int lch; + struct omap_udc *udc; +}; + +struct omap_udc { + struct usb_gadget gadget; + struct usb_gadget_driver *driver; + spinlock_t lock; + struct omap_ep ep[32]; + u16 devstat; + struct otg_transceiver *transceiver; + struct list_head iso; + unsigned softconnect:1; + unsigned vbus_active:1; + unsigned ep0_pending:1; + unsigned ep0_in:1; + unsigned ep0_set_config:1; + unsigned ep0_reset_config:1; + unsigned ep0_setup:1; + unsigned hmc:6; + + struct completion *done; +}; + +/*-------------------------------------------------------------------------*/ + +#ifdef DEBUG +#define DBG(stuff...) printk(KERN_DEBUG "udc: " stuff) +#else +#define DBG(stuff...) do{}while(0) +#endif + +#ifdef VERBOSE +# define VDBG DBG +#else +# define VDBG(stuff...) do{}while(0) +#endif + +#define ERR(stuff...) printk(KERN_ERR "udc: " stuff) +#define WARN(stuff...) printk(KERN_WARNING "udc: " stuff) +#define INFO(stuff...) printk(KERN_INFO "udc: " stuff) + +/*-------------------------------------------------------------------------*/ + +// #define HMC_1510 ((MOD_CONF_CTRL_0_REG >> 1) & 0x3f) +#define HMC_1610 (OTG_SYSCON_2_REG & 0x3f) +#define HMC HMC_1610 + diff --git a/drivers/usb/host/Kconfig b/drivers/usb/host/Kconfig index 946898ac4..c6e6362d0 100644 --- a/drivers/usb/host/Kconfig +++ b/drivers/usb/host/Kconfig @@ -52,6 +52,7 @@ config USB_EHCI_ROOT_HUB_TT config USB_OHCI_HCD tristate "OHCI HCD support" depends on USB + select ISP1301_OMAP if MACH_OMAP_H2 ---help--- The Open Host Controller Interface (OHCI) is a standard for accessing USB 1.1 host controller hardware. It does more in hardware than Intel's diff --git a/drivers/usb/host/hc_sl811.c b/drivers/usb/host/hc_sl811.c index 5bda71207..b57f1fe82 100644 --- a/drivers/usb/host/hc_sl811.c +++ b/drivers/usb/host/hc_sl811.c @@ -106,14 +106,14 @@ int SL11StartXaction (hci_t * hci, __u8 addr, __u8 epaddr, int pid, int len, static int sofWaitCnt = 0; -MODULE_PARM (urb_debug, "i"); +module_param(urb_debug, int, 0); MODULE_PARM_DESC (urb_debug, "debug urb messages, default is 0 (no)"); -MODULE_PARM (base_addr, "i"); +module_param(base_addr, int, 0); MODULE_PARM_DESC (base_addr, "sl811 base address 0xd3800000"); -MODULE_PARM (data_reg_addr, "i"); +module_param(data_reg_addr, int, 0); MODULE_PARM_DESC (data_reg_addr, "sl811 data register address 0xd3810000"); -MODULE_PARM (irq, "i"); +module_param(irq, int, 0); MODULE_PARM_DESC (irq, "IRQ 34 (default)"); static int hc_reset (hci_t * hci); diff --git a/drivers/usb/media/pwc/ChangeLog b/drivers/usb/media/pwc/ChangeLog new file mode 100644 index 000000000..b2eb71a9a --- /dev/null +++ b/drivers/usb/media/pwc/ChangeLog @@ -0,0 +1,143 @@ +9.0.2 + +* Adding #ifdef to compile PWC before and after 2.6.5 + +9.0.1 + +9.0 + + +8.12 + +* Implement motorized pan/tilt feature for Logitech QuickCam Orbit/Spere. + +8.11.1 + +* Fix for PCVC720/40, would not be able to set videomode +* Fix for Samsung MPC models, appearantly they are based on a newer chipset + +8.11 + +* 20 dev_hints (per request) +* Hot unplugging should be better, no more dangling pointers or memory leaks +* Added reserved Logitech webcam IDs +* Device now remembers size & fps between close()/open() +* Removed palette stuff altogether + +8.10.1 + +* Added IDs for PCVC720K/40 and Creative Labs Webcam Pro + +8.10 + +* Fixed ID for QuickCam Notebook pro +* Added GREALSIZE ioctl() call +* Fixed bug in case PWCX was not loaded and invalid size was set + +8.9 + +* Merging with kernel 2.5.49 +* Adding IDs for QuickCam Zoom & QuickCam Notebook + +8.8 + +* Fixing 'leds' parameter +* Adding IDs for Logitech QuickCam Pro 4000 +* Making URB init/cleanup a little nicer + +8.7 + +* Incorporating changes in ioctl() parameter passing +* Also changes to URB mechanism + +8.6 + +* Added ID's for Visionite VCS UM100 and UC300 +* Removed YUV420-interlaced palette altogether (was confusing) +* Removed MIRROR stuff as it didn't work anyway +* Fixed a problem with the 'leds' parameter (wouldn't blink) +* Added ioctl()s for advanced features: 'extended' whitebalance ioctl()s, + CONTOUR, BACKLIGHT, FLICKER, DYNNOISE. +* VIDIOCGCAP.name now contains real camera model name instead of + 'Philips xxx webcam' +* Added PROBE ioctl (see previous point & API doc) + +8.5 + +* Adding IDs for Creative Labs Webcam 5 +* Adding IDs for SOTEC CMS-001 webcam +* Solving possible hang in VIDIOCSYNC when unplugging the cam +* Forgot to return structure in VIDIOCPWCGAWB, oops +* Time interval for the LEDs are now in milliseconds + +8.4 + +* Fixing power_save option for Vesta range +* Handling new error codes in ISOC callback +* Adding dev_hint module parameter, to specify /dev/videoX device nodes + +8.3 + +* Adding Samsung C10 and C30 cameras +* Removing palette module parameter +* Fixed typo in ID of QuickCam 3000 Pro +* Adding LED settings (blinking while in use) for ToUCam cameras. +* Turns LED off when camera is not in use. + +8.2 + +* Making module more silent when trace = 0 +* Adding QuickCam 3000 Pro IDs +* Chrominance control for the Vesta cameras +* Hopefully fixed problems on machines with BIGMEM and > 1GB of RAM +* Included Oliver Neukem's lock_kernel() patch +* Allocates less memory for image buffers +* Adds ioctl()s for the whitebalancing + +8.1 + +* Adding support for 750 +* Adding V4L GAUDIO/SAUDIO/UNIT ioctl() calls + +8.0 +* 'damage control' after inclusion in 2.4.5. +* Changed wait-queue mechanism in read/mmap/poll according to the book. +* Included YUV420P palette. +* Changed interface to decompressor module. +* Cleaned up pwc structure a bit. + +7.0 + +* Fixed bug in vcvt_420i_yuyv; extra variables on stack were misaligned. +* There is now a clear error message when an image size is selected that + is only supported using the decompressor, and the decompressor isn't + loaded. +* When the decompressor wasn't loaded, selecting large image size + would create skewed or double images. + +6.3 + +* Introduced spinlocks for the buffer pointer manipulation; a number of + reports seem to suggest the down()/up() semaphores were the cause of + lockups, since they are not suitable for interrupt/user locking. +* Separated decompressor and core code into 2 modules. + +6.2 + +* Non-integral image sizes are now padded with gray or black. +* Added SHUTTERSPEED ioctl(). +* Fixed buglet in VIDIOCPWCSAGC; the function would always return an error, + even though the call succeeded. +* Added hotplug support for 2.4.*. +* Memory: the 645/646 uses less memory now. + +6.1 + +* VIDIOCSPICT returns -EINVAL with invalid palettes. +* Added saturation control. +* Split decompressors from rest. +* Fixed bug that would reset the framerate to the default framerate if + the rate field was set to 0 (which is not what I intended, nl. do not + change the framerate!). +* VIDIOCPWCSCQUAL (setting compression quality) now takes effect immediately. +* Workaround for a bug in the 730 sensor. diff --git a/drivers/usb/media/pwc/Makefile b/drivers/usb/media/pwc/Makefile new file mode 100644 index 000000000..44bcc763a --- /dev/null +++ b/drivers/usb/media/pwc/Makefile @@ -0,0 +1,20 @@ +ifneq ($(KERNELRELEASE),) + +pwc-objs := pwc-if.o pwc-misc.o pwc-ctrl.o pwc-uncompress.o pwc-dec1.o pwc-dec23.o pwc-kiara.o pwc-timon.o + +obj-m += pwc.o + +else + +KDIR := /lib/modules/$(shell uname -r)/build +PWD := $(shell pwd) + +default: + $(MAKE) -C $(KDIR) SUBDIRS=$(PWD) modules + +endif + +clean: + rm -f *.[oas] .*.flags *.ko .*.cmd .*.d .*.tmp *.mod.c + rm -rf .tmp_versions + diff --git a/drivers/usb/media/pwc/philips.txt b/drivers/usb/media/pwc/philips.txt new file mode 100644 index 000000000..04a640d72 --- /dev/null +++ b/drivers/usb/media/pwc/philips.txt @@ -0,0 +1,236 @@ +This file contains some additional information for the Philips and OEM webcams. +E-mail: webcam@smcc.demon.nl Last updated: 2004-01-19 +Site: http://www.smcc.demon.nl/webcam/ + +As of this moment, the following cameras are supported: + * Philips PCA645 + * Philips PCA646 + * Philips PCVC675 + * Philips PCVC680 + * Philips PCVC690 + * Philips PCVC720/40 + * Philips PCVC730 + * Philips PCVC740 + * Philips PCVC750 + * Askey VC010 + * Creative Labs Webcam 5 + * Creative Labs Webcam Pro Ex + * Logitech QuickCam 3000 Pro + * Logitech QuickCam 4000 Pro + * Logitech QuickCam Notebook Pro + * Logitech QuickCam Zoom + * Logitech QuickCam Orbit + * Logitech QuickCam Sphere + * Samsung MPC-C10 + * Samsung MPC-C30 + * Sotec Afina Eye + * AME CU-001 + * Visionite VCS-UM100 + * Visionite VCS-UC300 + +The main webpage for the Philips driver is at the address above. It contains +a lot of extra information, a FAQ, and the binary plugin 'PWCX'. This plugin +contains decompression routines that allow you to use higher image sizes and +framerates; in addition the webcam uses less bandwidth on the USB bus (handy +if you want to run more than 1 camera simultaneously). These routines fall +under a NDA, and may therefor not be distributed as source; however, its use +is completely optional. + +You can build this code either into your kernel, or as a module. I recommend +the latter, since it makes troubleshooting a lot easier. The built-in +microphone is supported through the USB Audio class. + +When you load the module you can set some default settings for the +camera; some programs depend on a particular image-size or -format and +don't know how to set it properly in the driver. The options are: + +size + Can be one of 'sqcif', 'qsif', 'qcif', 'sif', 'cif' or + 'vga', for an image size of resp. 128x96, 160x120, 176x144, + 320x240, 352x288 and 640x480 (of course, only for those cameras that + support these resolutions). + +fps + Specifies the desired framerate. Is an integer in the range of 4-30. + +fbufs + This paramter specifies the number of internal buffers to use for storing + frames from the cam. This will help if the process that reads images from + the cam is a bit slow or momentarely busy. However, on slow machines it + only introduces lag, so choose carefully. The default is 3, which is + reasonable. You can set it between 2 and 5. + +mbufs + This is an integer between 1 and 10. It will tell the module the number of + buffers to reserve for mmap(), VIDIOCCGMBUF, VIDIOCMCAPTURE and friends. + The default is 2, which is adequate for most applications (double + buffering). + + Should you experience a lot of 'Dumping frame...' messages during + grabbing with a tool that uses mmap(), you might want to increase if. + However, it doesn't really buffer images, it just gives you a bit more + slack when your program is behind. But you need a multi-threaded or + forked program to really take advantage of these buffers. + + The absolute maximum is 10, but don't set it too high! Every buffer takes + up 460 KB of RAM, so unless you have a lot of memory setting this to + something more than 4 is an absolute waste. This memory is only + allocated during open(), so nothing is wasted when the camera is not in + use. + +power_save + When power_save is enabled (set to 1), the module will try to shut down + the cam on close() and re-activate on open(). This will save power and + turn off the LED. Not all cameras support this though (the 645 and 646 + don't have power saving at all), and some models don't work either (they + will shut down, but never wake up). Consider this experimental. By + default this option is disabled. + +compression (only useful with the plugin) + With this option you can control the compression factor that the camera + uses to squeeze the image through the USB bus. You can set the + parameter between 0 and 3: + 0 = prefer uncompressed images; if the requested mode is not available + in an uncompressed format, the driver will silently switch to low + compression. + 1 = low compression. + 2 = medium compression. + 3 = high compression. + + High compression takes less bandwidth of course, but it could also + introduce some unwanted artefacts. The default is 2, medium compression. + See the FAQ on the website for an overview of which modes require + compression. + + The compression parameter does not apply to the 645 and 646 cameras + and OEM models derived from those (only a few). Most cams honour this + parameter. + +leds + This settings takes 2 integers, that define the on/off time for the LED + (in milliseconds). One of the interesting things that you can do with + this is let the LED blink while the camera is in use. This: + + leds=500,500 + + will blink the LED once every second. But with: + + leds=0,0 + + the LED never goes on, making it suitable for silent surveillance. + + By default the camera's LED is on solid while in use, and turned off + when the camera is not used anymore. + + This parameter works only with the ToUCam range of cameras (720, 730, 740, + 750) and OEMs. For other cameras this command is silently ignored, and + the LED cannot be controlled. + + Finally: this parameters does not take effect UNTIL the first time you + open the camera device. Until then, the LED remains on. + +dev_hint + A long standing problem with USB devices is their dynamic nature: you + never know what device a camera gets assigned; it depends on module load + order, the hub configuration, the order in which devices are plugged in, + and the phase of the moon (i.e. it can be random). With this option you + can give the driver a hint as to what video device node (/dev/videoX) it + should use with a specific camera. This is also handy if you have two + cameras of the same model. + + A camera is specified by its type (the number from the camera model, + like PCA645, PCVC750VC, etc) and optionally the serial number (visible + in /proc/bus/usb/devices). A hint consists of a string with the following + format: + + [type[.serialnumber]:]node + + The square brackets mean that both the type and the serialnumber are + optional, but a serialnumber cannot be specified without a type (which + would be rather pointless). The serialnumber is separated from the type + by a '.'; the node number by a ':'. + + This somewhat cryptic syntax is best explained by a few examples: + + dev_hint=3,5 The first detected cam gets assigned + /dev/video3, the second /dev/video5. Any + other cameras will get the first free + available slot (see below). + + dev_hint=645:1,680:2 The PCA645 camera will get /dev/video1, + and a PCVC680 /dev/video2. + + dev_hint=645.0123:3,645.4567:0 The PCA645 camera with serialnumber + 0123 goes to /dev/video3, the same + camera model with the 4567 serial + gets /dev/video0. + + dev_hint=750:1,4,5,6 The PCVC750 camera will get /dev/video1, the + next 3 Philips cams will use /dev/video4 + through /dev/video6. + + Some points worth knowing: + - Serialnumbers are case sensitive and must be written full, including + leading zeroes (it's treated as a string). + - If a device node is already occupied, registration will fail and + the webcam is not available. + - You can have up to 64 video devices; be sure to make enough device + nodes in /dev if you want to spread the numbers (this does not apply + to devfs). After /dev/video9 comes /dev/video10 (not /dev/videoA). + - If a camera does not match any dev_hint, it will simply get assigned + the first available device node, just as it used to be. + +trace + In order to better detect problems, it is now possible to turn on a + 'trace' of some of the calls the module makes; it logs all items in your + kernel log at debug level. + + The trace variable is a bitmask; each bit represents a certain feature. + If you want to trace something, look up the bit value(s) in the table + below, add the values together and supply that to the trace variable. + + Value Value Description Default + (dec) (hex) + 1 0x1 Module initialization; this will log messages On + while loading and unloading the module + + 2 0x2 probe() and disconnect() traces On + + 4 0x4 Trace open() and close() calls Off + + 8 0x8 read(), mmap() and associated ioctl() calls Off + + 16 0x10 Memory allocation of buffers, etc. Off + + 32 0x20 Showing underflow, overflow and Dumping frame On + messages + + 64 0x40 Show viewport and image sizes Off + + 128 0x80 PWCX debugging Off + + For example, to trace the open() & read() fuctions, sum 8 + 4 = 12, + so you would supply trace=12 during insmod or modprobe. If + you want to turn the initialization and probing tracing off, set trace=0. + The default value for trace is 35 (0x23). + + + +Example: + + # modprobe pwc size=cif fps=15 power_save=1 + +The fbufs, mbufs and trace parameters are global and apply to all connected +cameras. Each camera has its own set of buffers. + +size and fps only specify defaults when you open() the device; this is to +accommodate some tools that don't set the size. You can change these +settings after open() with the Video4Linux ioctl() calls. The default of +defaults is QCIF size at 10 fps. + +The compression parameter is semiglobal; it sets the initial compression +preference for all camera's, but this parameter can be set per camera with +the VIDIOCPWCSCQUAL ioctl() call. + +All parameters are optional. + diff --git a/drivers/usb/media/pwc/pwc-ctrl.c b/drivers/usb/media/pwc/pwc-ctrl.c new file mode 100644 index 000000000..45c9323a1 --- /dev/null +++ b/drivers/usb/media/pwc/pwc-ctrl.c @@ -0,0 +1,1630 @@ +/* Driver for Philips webcam + Functions that send various control messages to the webcam, including + video modes. + (C) 1999-2003 Nemosoft Unv. + (C) 2004 Luc Saillard (luc@saillard.org) + + NOTE: this version of pwc is an unofficial (modified) release of pwc & pcwx + driver and thus may have bugs that are not present in the original version. + Please send bug reports and support requests to . + + NOTE: this version of pwc is an unofficial (modified) release of pwc & pcwx + driver and thus may have bugs that are not present in the original version. + Please send bug reports and support requests to . + The decompression routines have been implemented by reverse-engineering the + Nemosoft binary pwcx module. Caveat emptor. + + This program is free software; you can redistribute it and/or modify + it under the terms of the 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 +*/ + +/* + Changes + 2001/08/03 Alvarado Added methods for changing white balance and + red/green gains + */ + +/* Control functions for the cam; brightness, contrast, video mode, etc. */ + +#ifdef __KERNEL__ +#include +#endif +#include +#include + +#include "pwc.h" +#include "pwc-ioctl.h" +#include "pwc-uncompress.h" +#include "pwc-kiara.h" +#include "pwc-timon.h" +#include "pwc-dec1.h" +#include "pwc-dec23.h" + +/* Request types: video */ +#define SET_LUM_CTL 0x01 +#define GET_LUM_CTL 0x02 +#define SET_CHROM_CTL 0x03 +#define GET_CHROM_CTL 0x04 +#define SET_STATUS_CTL 0x05 +#define GET_STATUS_CTL 0x06 +#define SET_EP_STREAM_CTL 0x07 +#define GET_EP_STREAM_CTL 0x08 +#define SET_MPT_CTL 0x0D +#define GET_MPT_CTL 0x0E + +/* Selectors for the Luminance controls [GS]ET_LUM_CTL */ +#define AGC_MODE_FORMATTER 0x2000 +#define PRESET_AGC_FORMATTER 0x2100 +#define SHUTTER_MODE_FORMATTER 0x2200 +#define PRESET_SHUTTER_FORMATTER 0x2300 +#define PRESET_CONTOUR_FORMATTER 0x2400 +#define AUTO_CONTOUR_FORMATTER 0x2500 +#define BACK_LIGHT_COMPENSATION_FORMATTER 0x2600 +#define CONTRAST_FORMATTER 0x2700 +#define DYNAMIC_NOISE_CONTROL_FORMATTER 0x2800 +#define FLICKERLESS_MODE_FORMATTER 0x2900 +#define AE_CONTROL_SPEED 0x2A00 +#define BRIGHTNESS_FORMATTER 0x2B00 +#define GAMMA_FORMATTER 0x2C00 + +/* Selectors for the Chrominance controls [GS]ET_CHROM_CTL */ +#define WB_MODE_FORMATTER 0x1000 +#define AWB_CONTROL_SPEED_FORMATTER 0x1100 +#define AWB_CONTROL_DELAY_FORMATTER 0x1200 +#define PRESET_MANUAL_RED_GAIN_FORMATTER 0x1300 +#define PRESET_MANUAL_BLUE_GAIN_FORMATTER 0x1400 +#define COLOUR_MODE_FORMATTER 0x1500 +#define SATURATION_MODE_FORMATTER1 0x1600 +#define SATURATION_MODE_FORMATTER2 0x1700 + +/* Selectors for the Status controls [GS]ET_STATUS_CTL */ +#define SAVE_USER_DEFAULTS_FORMATTER 0x0200 +#define RESTORE_USER_DEFAULTS_FORMATTER 0x0300 +#define RESTORE_FACTORY_DEFAULTS_FORMATTER 0x0400 +#define READ_AGC_FORMATTER 0x0500 +#define READ_SHUTTER_FORMATTER 0x0600 +#define READ_RED_GAIN_FORMATTER 0x0700 +#define READ_BLUE_GAIN_FORMATTER 0x0800 +#define SENSOR_TYPE_FORMATTER1 0x0C00 +#define READ_RAW_Y_MEAN_FORMATTER 0x3100 +#define SET_POWER_SAVE_MODE_FORMATTER 0x3200 +#define MIRROR_IMAGE_FORMATTER 0x3300 +#define LED_FORMATTER 0x3400 +#define SENSOR_TYPE_FORMATTER2 0x3700 + +/* Formatters for the Video Endpoint controls [GS]ET_EP_STREAM_CTL */ +#define VIDEO_OUTPUT_CONTROL_FORMATTER 0x0100 + +/* Formatters for the motorized pan & tilt [GS]ET_MPT_CTL */ +#define PT_RELATIVE_CONTROL_FORMATTER 0x01 +#define PT_RESET_CONTROL_FORMATTER 0x02 +#define PT_STATUS_FORMATTER 0x03 + +static char *size2name[PSZ_MAX] = +{ + "subQCIF", + "QSIF", + "QCIF", + "SIF", + "CIF", + "VGA", +}; + +/********/ + +/* Entries for the Nala (645/646) camera; the Nala doesn't have compression + preferences, so you either get compressed or non-compressed streams. + + An alternate value of 0 means this mode is not available at all. + */ + +struct Nala_table_entry { + char alternate; /* USB alternate setting */ + int compressed; /* Compressed yes/no */ + + unsigned char mode[3]; /* precomputed mode table */ +}; + +static struct Nala_table_entry Nala_table[PSZ_MAX][8] = +{ +#include "pwc-nala.h" +}; + + +/****************************************************************************/ + + +#define SendControlMsg(request, value, buflen) \ + usb_control_msg(pdev->udev, usb_sndctrlpipe(pdev->udev, 0), \ + request, \ + USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, \ + value, \ + pdev->vcinterface, \ + &buf, buflen, HZ / 2) + +#define RecvControlMsg(request, value, buflen) \ + usb_control_msg(pdev->udev, usb_rcvctrlpipe(pdev->udev, 0), \ + request, \ + USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, \ + value, \ + pdev->vcinterface, \ + &buf, buflen, HZ / 2) + + +#if PWC_DEBUG +void pwc_hexdump(void *p, int len) +{ + int i; + unsigned char *s; + char buf[100], *d; + + s = (unsigned char *)p; + d = buf; + *d = '\0'; + Debug("Doing hexdump @ %p, %d bytes.\n", p, len); + for (i = 0; i < len; i++) { + d += sprintf(d, "%02X ", *s++); + if ((i & 0xF) == 0xF) { + Debug("%s\n", buf); + d = buf; + *d = '\0'; + } + } + if ((i & 0xF) != 0) + Debug("%s\n", buf); +} +#endif + +static inline int send_video_command(struct usb_device *udev, int index, void *buf, int buflen) +{ + return usb_control_msg(udev, + usb_sndctrlpipe(udev, 0), + SET_EP_STREAM_CTL, + USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + VIDEO_OUTPUT_CONTROL_FORMATTER, + index, + buf, buflen, HZ); +} + + + +static inline int set_video_mode_Nala(struct pwc_device *pdev, int size, int frames) +{ + unsigned char buf[3]; + int ret, fps; + struct Nala_table_entry *pEntry; + int frames2frames[31] = + { /* closest match of framerate */ + 0, 0, 0, 0, 4, /* 0-4 */ + 5, 5, 7, 7, 10, /* 5-9 */ + 10, 10, 12, 12, 15, /* 10-14 */ + 15, 15, 15, 20, 20, /* 15-19 */ + 20, 20, 20, 24, 24, /* 20-24 */ + 24, 24, 24, 24, 24, /* 25-29 */ + 24 /* 30 */ + }; + int frames2table[31] = + { 0, 0, 0, 0, 0, /* 0-4 */ + 1, 1, 1, 2, 2, /* 5-9 */ + 3, 3, 4, 4, 4, /* 10-14 */ + 5, 5, 5, 5, 5, /* 15-19 */ + 6, 6, 6, 6, 7, /* 20-24 */ + 7, 7, 7, 7, 7, /* 25-29 */ + 7 /* 30 */ + }; + + if (size < 0 || size > PSZ_CIF || frames < 4 || frames > 25) + return -EINVAL; + frames = frames2frames[frames]; + fps = frames2table[frames]; + pEntry = &Nala_table[size][fps]; + if (pEntry->alternate == 0) + return -EINVAL; + + if (pEntry->compressed) + return -ENOENT; /* Not supported. */ + + memcpy(buf, pEntry->mode, 3); + ret = send_video_command(pdev->udev, pdev->vendpoint, buf, 3); + if (ret < 0) { + Debug("Failed to send video command... %d\n", ret); + return ret; + } + if (pEntry->compressed && pdev->vpalette != VIDEO_PALETTE_RAW) + { + switch(pdev->type) { + case 645: + case 646: + pwc_dec1_init(pdev->type, pdev->release, buf, pdev->decompress_data); + break; + + case 675: + case 680: + case 690: + case 720: + case 730: + case 740: + case 750: + pwc_dec23_init(pdev->type, pdev->release, buf, pdev->decompress_data); + break; + } + } + + pdev->cmd_len = 3; + memcpy(pdev->cmd_buf, buf, 3); + + /* Set various parameters */ + pdev->vframes = frames; + pdev->vsize = size; + pdev->valternate = pEntry->alternate; + pdev->image = pwc_image_sizes[size]; + pdev->frame_size = (pdev->image.x * pdev->image.y * 3) / 2; + if (pEntry->compressed) { + if (pdev->release < 5) { /* 4 fold compression */ + pdev->vbandlength = 528; + pdev->frame_size /= 4; + } + else { + pdev->vbandlength = 704; + pdev->frame_size /= 3; + } + } + else + pdev->vbandlength = 0; + return 0; +} + + +static inline int set_video_mode_Timon(struct pwc_device *pdev, int size, int frames, int compression, int snapshot) +{ + unsigned char buf[13]; + const struct Timon_table_entry *pChoose; + int ret, fps; + + if (size >= PSZ_MAX || frames < 5 || frames > 30 || compression < 0 || compression > 3) + return -EINVAL; + if (size == PSZ_VGA && frames > 15) + return -EINVAL; + fps = (frames / 5) - 1; + + /* Find a supported framerate with progressively higher compression ratios + if the preferred ratio is not available. + */ + pChoose = NULL; + while (compression <= 3) { + pChoose = &Timon_table[size][fps][compression]; + if (pChoose->alternate != 0) + break; + compression++; + } + if (pChoose == NULL || pChoose->alternate == 0) + return -ENOENT; /* Not supported. */ + + memcpy(buf, pChoose->mode, 13); + if (snapshot) + buf[0] |= 0x80; + ret = send_video_command(pdev->udev, pdev->vendpoint, buf, 13); + if (ret < 0) + return ret; + + if (pChoose->bandlength > 0 && pdev->vpalette != VIDEO_PALETTE_RAW) + pwc_dec23_init(pdev->type, pdev->release, buf, pdev->decompress_data); + + pdev->cmd_len = 13; + memcpy(pdev->cmd_buf, buf, 13); + + /* Set various parameters */ + pdev->vframes = frames; + pdev->vsize = size; + pdev->vsnapshot = snapshot; + pdev->valternate = pChoose->alternate; + pdev->image = pwc_image_sizes[size]; + pdev->vbandlength = pChoose->bandlength; + if (pChoose->bandlength > 0) + pdev->frame_size = (pChoose->bandlength * pdev->image.y) / 4; + else + pdev->frame_size = (pdev->image.x * pdev->image.y * 12) / 8; + return 0; +} + + +static inline int set_video_mode_Kiara(struct pwc_device *pdev, int size, int frames, int compression, int snapshot) +{ + const struct Kiara_table_entry *pChoose = 0; + int fps, ret; + unsigned char buf[12]; + struct Kiara_table_entry RawEntry = {6, 773, 1272, {0xAD, 0xF4, 0x10, 0x27, 0xB6, 0x24, 0x96, 0x02, 0x30, 0x05, 0x03, 0x80}}; + + if (size >= PSZ_MAX || frames < 5 || frames > 30 || compression < 0 || compression > 3) + return -EINVAL; + if (size == PSZ_VGA && frames > 15) + return -EINVAL; + fps = (frames / 5) - 1; + + /* special case: VGA @ 5 fps and snapshot is raw bayer mode */ + if (size == PSZ_VGA && frames == 5 && snapshot) + { + /* Only available in case the raw palette is selected or + we have the decompressor available. This mode is + only available in compressed form + */ + if (pdev->vpalette == VIDEO_PALETTE_RAW) + { + Info("Choosing VGA/5 BAYER mode (%d).\n", pdev->vpalette); + pChoose = &RawEntry; + } + else + { + Info("VGA/5 BAYER mode _must_ have a decompressor available, or use RAW palette.\n"); + } + } + else + { + /* Find a supported framerate with progressively higher compression ratios + if the preferred ratio is not available. + Skip this step when using RAW modes. + */ + while (compression <= 3) { + pChoose = &Kiara_table[size][fps][compression]; + if (pChoose->alternate != 0) + break; + compression++; + } + } + if (pChoose == NULL || pChoose->alternate == 0) + return -ENOENT; /* Not supported. */ + + Debug("Using alternate setting %d.\n", pChoose->alternate); + + /* usb_control_msg won't take staticly allocated arrays as argument?? */ + memcpy(buf, pChoose->mode, 12); + if (snapshot) + buf[0] |= 0x80; + + /* Firmware bug: video endpoint is 5, but commands are sent to endpoint 4 */ + ret = send_video_command(pdev->udev, 4 /* pdev->vendpoint */, buf, 12); + if (ret < 0) + return ret; + + if (pChoose->bandlength > 0 && pdev->vpalette != VIDEO_PALETTE_RAW) + pwc_dec23_init(pdev->type, pdev->release, buf, pdev->decompress_data); + + pdev->cmd_len = 12; + memcpy(pdev->cmd_buf, buf, 12); + /* All set and go */ + pdev->vframes = frames; + pdev->vsize = size; + pdev->vsnapshot = snapshot; + pdev->valternate = pChoose->alternate; + pdev->image = pwc_image_sizes[size]; + pdev->vbandlength = pChoose->bandlength; + if (pdev->vbandlength > 0) + pdev->frame_size = (pdev->vbandlength * pdev->image.y) / 4; + else + pdev->frame_size = (pdev->image.x * pdev->image.y * 12) / 8; + return 0; +} + + + +/** + @pdev: device structure + @width: viewport width + @height: viewport height + @frame: framerate, in fps + @compression: preferred compression ratio + @snapshot: snapshot mode or streaming + */ +int pwc_set_video_mode(struct pwc_device *pdev, int width, int height, int frames, int compression, int snapshot) +{ + int ret, size; + + Trace(TRACE_FLOW, "set_video_mode(%dx%d @ %d, palette %d).\n", width, height, frames, pdev->vpalette); + size = pwc_decode_size(pdev, width, height); + if (size < 0) { + Debug("Could not find suitable size.\n"); + return -ERANGE; + } + Debug("decode_size = %d.\n", size); + + ret = -EINVAL; + switch(pdev->type) { + case 645: + case 646: + ret = set_video_mode_Nala(pdev, size, frames); + break; + + case 675: + case 680: + case 690: + ret = set_video_mode_Timon(pdev, size, frames, compression, snapshot); + break; + + case 720: + case 730: + case 740: + case 750: + ret = set_video_mode_Kiara(pdev, size, frames, compression, snapshot); + break; + } + if (ret < 0) { + if (ret == -ENOENT) + Info("Video mode %s@%d fps is only supported with the decompressor module (pwcx).\n", size2name[size], frames); + else { + Err("Failed to set video mode %s@%d fps; return code = %d\n", size2name[size], frames, ret); + } + return ret; + } + pdev->view.x = width; + pdev->view.y = height; + pdev->frame_total_size = pdev->frame_size + pdev->frame_header_size + pdev->frame_trailer_size; + pwc_set_image_buffer_size(pdev); + Trace(TRACE_SIZE, "Set viewport to %dx%d, image size is %dx%d.\n", width, height, pwc_image_sizes[size].x, pwc_image_sizes[size].y); + return 0; +} + + +void pwc_set_image_buffer_size(struct pwc_device *pdev) +{ + int i, factor = 0, filler = 0; + + /* for PALETTE_YUV420P */ + switch(pdev->vpalette) + { + case VIDEO_PALETTE_YUV420P: + factor = 6; + filler = 128; + break; + case VIDEO_PALETTE_RAW: + factor = 6; /* can be uncompressed YUV420P */ + filler = 0; + break; + } + + /* Set sizes in bytes */ + pdev->image.size = pdev->image.x * pdev->image.y * factor / 4; + pdev->view.size = pdev->view.x * pdev->view.y * factor / 4; + + /* Align offset, or you'll get some very weird results in + YUV420 mode... x must be multiple of 4 (to get the Y's in + place), and y even (or you'll mixup U & V). This is less of a + problem for YUV420P. + */ + pdev->offset.x = ((pdev->view.x - pdev->image.x) / 2) & 0xFFFC; + pdev->offset.y = ((pdev->view.y - pdev->image.y) / 2) & 0xFFFE; + + /* Fill buffers with gray or black */ + for (i = 0; i < MAX_IMAGES; i++) { + if (pdev->image_ptr[i] != NULL) + memset(pdev->image_ptr[i], filler, pdev->view.size); + } +} + + + +/* BRIGHTNESS */ + +int pwc_get_brightness(struct pwc_device *pdev) +{ + char buf; + int ret; + + ret = RecvControlMsg(GET_LUM_CTL, BRIGHTNESS_FORMATTER, 1); + if (ret < 0) + return ret; + return buf << 9; +} + +int pwc_set_brightness(struct pwc_device *pdev, int value) +{ + char buf; + + if (value < 0) + value = 0; + if (value > 0xffff) + value = 0xffff; + buf = (value >> 9) & 0x7f; + return SendControlMsg(SET_LUM_CTL, BRIGHTNESS_FORMATTER, 1); +} + +/* CONTRAST */ + +int pwc_get_contrast(struct pwc_device *pdev) +{ + char buf; + int ret; + + ret = RecvControlMsg(GET_LUM_CTL, CONTRAST_FORMATTER, 1); + if (ret < 0) + return ret; + return buf << 10; +} + +int pwc_set_contrast(struct pwc_device *pdev, int value) +{ + char buf; + + if (value < 0) + value = 0; + if (value > 0xffff) + value = 0xffff; + buf = (value >> 10) & 0x3f; + return SendControlMsg(SET_LUM_CTL, CONTRAST_FORMATTER, 1); +} + +/* GAMMA */ + +int pwc_get_gamma(struct pwc_device *pdev) +{ + char buf; + int ret; + + ret = RecvControlMsg(GET_LUM_CTL, GAMMA_FORMATTER, 1); + if (ret < 0) + return ret; + return buf << 11; +} + +int pwc_set_gamma(struct pwc_device *pdev, int value) +{ + char buf; + + if (value < 0) + value = 0; + if (value > 0xffff) + value = 0xffff; + buf = (value >> 11) & 0x1f; + return SendControlMsg(SET_LUM_CTL, GAMMA_FORMATTER, 1); +} + + +/* SATURATION */ + +int pwc_get_saturation(struct pwc_device *pdev) +{ + char buf; + int ret; + + if (pdev->type < 675) + return -1; + ret = RecvControlMsg(GET_CHROM_CTL, pdev->type < 730 ? SATURATION_MODE_FORMATTER2 : SATURATION_MODE_FORMATTER1, 1); + if (ret < 0) + return ret; + return 32768 + buf * 327; +} + +int pwc_set_saturation(struct pwc_device *pdev, int value) +{ + char buf; + + if (pdev->type < 675) + return -EINVAL; + if (value < 0) + value = 0; + if (value > 0xffff) + value = 0xffff; + /* saturation ranges from -100 to +100 */ + buf = (value - 32768) / 327; + return SendControlMsg(SET_CHROM_CTL, pdev->type < 730 ? SATURATION_MODE_FORMATTER2 : SATURATION_MODE_FORMATTER1, 1); +} + +/* AGC */ + +static inline int pwc_set_agc(struct pwc_device *pdev, int mode, int value) +{ + char buf; + int ret; + + if (mode) + buf = 0x0; /* auto */ + else + buf = 0xff; /* fixed */ + + ret = SendControlMsg(SET_LUM_CTL, AGC_MODE_FORMATTER, 1); + + if (!mode && ret >= 0) { + if (value < 0) + value = 0; + if (value > 0xffff) + value = 0xffff; + buf = (value >> 10) & 0x3F; + ret = SendControlMsg(SET_LUM_CTL, PRESET_AGC_FORMATTER, 1); + } + if (ret < 0) + return ret; + return 0; +} + +static inline int pwc_get_agc(struct pwc_device *pdev, int *value) +{ + unsigned char buf; + int ret; + + ret = RecvControlMsg(GET_LUM_CTL, AGC_MODE_FORMATTER, 1); + if (ret < 0) + return ret; + + if (buf != 0) { /* fixed */ + ret = RecvControlMsg(GET_LUM_CTL, PRESET_AGC_FORMATTER, 1); + if (ret < 0) + return ret; + if (buf > 0x3F) + buf = 0x3F; + *value = (buf << 10); + } + else { /* auto */ + ret = RecvControlMsg(GET_STATUS_CTL, READ_AGC_FORMATTER, 1); + if (ret < 0) + return ret; + /* Gah... this value ranges from 0x00 ... 0x9F */ + if (buf > 0x9F) + buf = 0x9F; + *value = -(48 + buf * 409); + } + + return 0; +} + +static inline int pwc_set_shutter_speed(struct pwc_device *pdev, int mode, int value) +{ + char buf[2]; + int speed, ret; + + + if (mode) + buf[0] = 0x0; /* auto */ + else + buf[0] = 0xff; /* fixed */ + + ret = SendControlMsg(SET_LUM_CTL, SHUTTER_MODE_FORMATTER, 1); + + if (!mode && ret >= 0) { + if (value < 0) + value = 0; + if (value > 0xffff) + value = 0xffff; + switch(pdev->type) { + case 675: + case 680: + case 690: + /* speed ranges from 0x0 to 0x290 (656) */ + speed = (value / 100); + buf[1] = speed >> 8; + buf[0] = speed & 0xff; + break; + case 720: + case 730: + case 740: + case 750: + /* speed seems to range from 0x0 to 0xff */ + buf[1] = 0; + buf[0] = value >> 8; + break; + } + + ret = SendControlMsg(SET_LUM_CTL, PRESET_SHUTTER_FORMATTER, 2); + } + return ret; +} + + +/* POWER */ + +int pwc_camera_power(struct pwc_device *pdev, int power) +{ + char buf; + + if (pdev->type < 675 || (pdev->type < 730 && pdev->release < 6)) + return 0; /* Not supported by Nala or Timon < release 6 */ + + if (power) + buf = 0x00; /* active */ + else + buf = 0xFF; /* power save */ + return SendControlMsg(SET_STATUS_CTL, SET_POWER_SAVE_MODE_FORMATTER, 1); +} + + + +/* private calls */ + +static inline int pwc_restore_user(struct pwc_device *pdev) +{ + char buf; /* dummy */ + return SendControlMsg(SET_STATUS_CTL, RESTORE_USER_DEFAULTS_FORMATTER, 0); +} + +static inline int pwc_save_user(struct pwc_device *pdev) +{ + char buf; /* dummy */ + return SendControlMsg(SET_STATUS_CTL, SAVE_USER_DEFAULTS_FORMATTER, 0); +} + +static inline int pwc_restore_factory(struct pwc_device *pdev) +{ + char buf; /* dummy */ + return SendControlMsg(SET_STATUS_CTL, RESTORE_FACTORY_DEFAULTS_FORMATTER, 0); +} + + /* ************************************************* */ + /* Patch by Alvarado: (not in the original version */ + + /* + * the camera recognizes modes from 0 to 4: + * + * 00: indoor (incandescant lighting) + * 01: outdoor (sunlight) + * 02: fluorescent lighting + * 03: manual + * 04: auto + */ +static inline int pwc_set_awb(struct pwc_device *pdev, int mode) +{ + char buf; + int ret; + + if (mode < 0) + mode = 0; + + if (mode > 4) + mode = 4; + + buf = mode & 0x07; /* just the lowest three bits */ + + ret = SendControlMsg(SET_CHROM_CTL, WB_MODE_FORMATTER, 1); + + if (ret < 0) + return ret; + return 0; +} + +static inline int pwc_get_awb(struct pwc_device *pdev) +{ + unsigned char buf; + int ret; + + ret = RecvControlMsg(GET_CHROM_CTL, WB_MODE_FORMATTER, 1); + + if (ret < 0) + return ret; + return buf; +} + +static inline int pwc_set_red_gain(struct pwc_device *pdev, int value) +{ + unsigned char buf; + + if (value < 0) + value = 0; + if (value > 0xffff) + value = 0xffff; + /* only the msb is considered */ + buf = value >> 8; + return SendControlMsg(SET_CHROM_CTL, PRESET_MANUAL_RED_GAIN_FORMATTER, 1); +} + +static inline int pwc_get_red_gain(struct pwc_device *pdev, int *value) +{ + unsigned char buf; + int ret; + + ret = RecvControlMsg(GET_CHROM_CTL, PRESET_MANUAL_RED_GAIN_FORMATTER, 1); + if (ret < 0) + return ret; + *value = buf << 8; + return 0; +} + + +static inline int pwc_set_blue_gain(struct pwc_device *pdev, int value) +{ + unsigned char buf; + + if (value < 0) + value = 0; + if (value > 0xffff) + value = 0xffff; + /* only the msb is considered */ + buf = value >> 8; + return SendControlMsg(SET_CHROM_CTL, PRESET_MANUAL_BLUE_GAIN_FORMATTER, 1); +} + +static inline int pwc_get_blue_gain(struct pwc_device *pdev, int *value) +{ + unsigned char buf; + int ret; + + ret = RecvControlMsg(GET_CHROM_CTL, PRESET_MANUAL_BLUE_GAIN_FORMATTER, 1); + if (ret < 0) + return ret; + *value = buf << 8; + return 0; +} + + +/* The following two functions are different, since they only read the + internal red/blue gains, which may be different from the manual + gains set or read above. + */ +static inline int pwc_read_red_gain(struct pwc_device *pdev, int *value) +{ + unsigned char buf; + int ret; + + ret = RecvControlMsg(GET_STATUS_CTL, READ_RED_GAIN_FORMATTER, 1); + if (ret < 0) + return ret; + *value = buf << 8; + return 0; +} + +static inline int pwc_read_blue_gain(struct pwc_device *pdev, int *value) +{ + unsigned char buf; + int ret; + + ret = RecvControlMsg(GET_STATUS_CTL, READ_BLUE_GAIN_FORMATTER, 1); + if (ret < 0) + return ret; + *value = buf << 8; + return 0; +} + + +static inline int pwc_set_wb_speed(struct pwc_device *pdev, int speed) +{ + unsigned char buf; + + /* useful range is 0x01..0x20 */ + buf = speed / 0x7f0; + return SendControlMsg(SET_CHROM_CTL, AWB_CONTROL_SPEED_FORMATTER, 1); +} + +static inline int pwc_get_wb_speed(struct pwc_device *pdev, int *value) +{ + unsigned char buf; + int ret; + + ret = RecvControlMsg(GET_CHROM_CTL, AWB_CONTROL_SPEED_FORMATTER, 1); + if (ret < 0) + return ret; + *value = buf * 0x7f0; + return 0; +} + + +static inline int pwc_set_wb_delay(struct pwc_device *pdev, int delay) +{ + unsigned char buf; + + /* useful range is 0x01..0x3F */ + buf = (delay >> 10); + return SendControlMsg(SET_CHROM_CTL, AWB_CONTROL_DELAY_FORMATTER, 1); +} + +static inline int pwc_get_wb_delay(struct pwc_device *pdev, int *value) +{ + unsigned char buf; + int ret; + + ret = RecvControlMsg(GET_CHROM_CTL, AWB_CONTROL_DELAY_FORMATTER, 1); + if (ret < 0) + return ret; + *value = buf << 10; + return 0; +} + + +int pwc_set_leds(struct pwc_device *pdev, int on_value, int off_value) +{ + unsigned char buf[2]; + + if (pdev->type < 730) + return 0; + on_value /= 100; + off_value /= 100; + if (on_value < 0) + on_value = 0; + if (on_value > 0xff) + on_value = 0xff; + if (off_value < 0) + off_value = 0; + if (off_value > 0xff) + off_value = 0xff; + + buf[0] = on_value; + buf[1] = off_value; + + return SendControlMsg(SET_STATUS_CTL, LED_FORMATTER, 2); +} + +int pwc_get_leds(struct pwc_device *pdev, int *on_value, int *off_value) +{ + unsigned char buf[2]; + int ret; + + if (pdev->type < 730) { + *on_value = -1; + *off_value = -1; + return 0; + } + + ret = RecvControlMsg(GET_STATUS_CTL, LED_FORMATTER, 2); + if (ret < 0) + return ret; + *on_value = buf[0] * 100; + *off_value = buf[1] * 100; + return 0; +} + +static inline int pwc_set_contour(struct pwc_device *pdev, int contour) +{ + unsigned char buf; + int ret; + + if (contour < 0) + buf = 0xff; /* auto contour on */ + else + buf = 0x0; /* auto contour off */ + ret = SendControlMsg(SET_LUM_CTL, AUTO_CONTOUR_FORMATTER, 1); + if (ret < 0) + return ret; + + if (contour < 0) + return 0; + if (contour > 0xffff) + contour = 0xffff; + + buf = (contour >> 10); /* contour preset is [0..3f] */ + ret = SendControlMsg(SET_LUM_CTL, PRESET_CONTOUR_FORMATTER, 1); + if (ret < 0) + return ret; + return 0; +} + +static inline int pwc_get_contour(struct pwc_device *pdev, int *contour) +{ + unsigned char buf; + int ret; + + ret = RecvControlMsg(GET_LUM_CTL, AUTO_CONTOUR_FORMATTER, 1); + if (ret < 0) + return ret; + + if (buf == 0) { + /* auto mode off, query current preset value */ + ret = RecvControlMsg(GET_LUM_CTL, PRESET_CONTOUR_FORMATTER, 1); + if (ret < 0) + return ret; + *contour = buf << 10; + } + else + *contour = -1; + return 0; +} + + +static inline int pwc_set_backlight(struct pwc_device *pdev, int backlight) +{ + unsigned char buf; + + if (backlight) + buf = 0xff; + else + buf = 0x0; + return SendControlMsg(SET_LUM_CTL, BACK_LIGHT_COMPENSATION_FORMATTER, 1); +} + +static inline int pwc_get_backlight(struct pwc_device *pdev, int *backlight) +{ + int ret; + unsigned char buf; + + ret = RecvControlMsg(GET_LUM_CTL, BACK_LIGHT_COMPENSATION_FORMATTER, 1); + if (ret < 0) + return ret; + *backlight = buf; + return 0; +} + + +static inline int pwc_set_flicker(struct pwc_device *pdev, int flicker) +{ + unsigned char buf; + + if (flicker) + buf = 0xff; + else + buf = 0x0; + return SendControlMsg(SET_LUM_CTL, FLICKERLESS_MODE_FORMATTER, 1); +} + +static inline int pwc_get_flicker(struct pwc_device *pdev, int *flicker) +{ + int ret; + unsigned char buf; + + ret = RecvControlMsg(GET_LUM_CTL, FLICKERLESS_MODE_FORMATTER, 1); + if (ret < 0) + return ret; + *flicker = buf; + return 0; +} + + +static inline int pwc_set_dynamic_noise(struct pwc_device *pdev, int noise) +{ + unsigned char buf; + + if (noise < 0) + noise = 0; + if (noise > 3) + noise = 3; + buf = noise; + return SendControlMsg(SET_LUM_CTL, DYNAMIC_NOISE_CONTROL_FORMATTER, 1); +} + +static inline int pwc_get_dynamic_noise(struct pwc_device *pdev, int *noise) +{ + int ret; + unsigned char buf; + + ret = RecvControlMsg(GET_LUM_CTL, DYNAMIC_NOISE_CONTROL_FORMATTER, 1); + if (ret < 0) + return ret; + *noise = buf; + return 0; +} + +int pwc_mpt_reset(struct pwc_device *pdev, int flags) +{ + unsigned char buf; + + buf = flags & 0x03; // only lower two bits are currently used + return SendControlMsg(SET_MPT_CTL, PT_RESET_CONTROL_FORMATTER, 1); +} + +static inline int pwc_mpt_set_angle(struct pwc_device *pdev, int pan, int tilt) +{ + unsigned char buf[4]; + + /* set new relative angle; angles are expressed in degrees * 100, + but cam as .5 degree resolution, hence devide by 200. Also + the angle must be multiplied by 64 before it's send to + the cam (??) + */ + pan = 64 * pan / 100; + tilt = -64 * tilt / 100; /* positive tilt is down, which is not what the user would expect */ + buf[0] = pan & 0xFF; + buf[1] = (pan >> 8) & 0xFF; + buf[2] = tilt & 0xFF; + buf[3] = (tilt >> 8) & 0xFF; + return SendControlMsg(SET_MPT_CTL, PT_RELATIVE_CONTROL_FORMATTER, 4); +} + +static inline int pwc_mpt_get_status(struct pwc_device *pdev, struct pwc_mpt_status *status) +{ + int ret; + unsigned char buf[5]; + + ret = RecvControlMsg(GET_MPT_CTL, PT_STATUS_FORMATTER, 5); + if (ret < 0) + return ret; + status->status = buf[0] & 0x7; // 3 bits are used for reporting + status->time_pan = (buf[1] << 8) + buf[2]; + status->time_tilt = (buf[3] << 8) + buf[4]; + return 0; +} + + +int pwc_get_cmos_sensor(struct pwc_device *pdev, int *sensor) +{ + unsigned char buf; + int ret = -1, request; + + if (pdev->type < 675) + request = SENSOR_TYPE_FORMATTER1; + else if (pdev->type < 730) + return -1; /* The Vesta series doesn't have this call */ + else + request = SENSOR_TYPE_FORMATTER2; + + ret = RecvControlMsg(GET_STATUS_CTL, request, 1); + if (ret < 0) + return ret; + if (pdev->type < 675) + *sensor = buf | 0x100; + else + *sensor = buf; + return 0; +} + + + /* End of Add-Ons */ + /* ************************************************* */ + +/* Linux 2.5.something and 2.6 pass direct pointers to arguments of + ioctl() calls. With 2.4, you have to do tedious copy_from_user() + and copy_to_user() calls. With these macros we circumvent this, + and let me maintain only one source file. The functionality is + exactly the same otherwise. + */ + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0) + +/* define local variable for arg */ +#define ARG_DEF(ARG_type, ARG_name)\ + ARG_type *ARG_name = arg; +/* copy arg to local variable */ +#define ARG_IN(ARG_name) /* nothing */ +/* argument itself (referenced) */ +#define ARGR(ARG_name) (*ARG_name) +/* argument address */ +#define ARGA(ARG_name) ARG_name +/* copy local variable to arg */ +#define ARG_OUT(ARG_name) /* nothing */ + +#else + +#define ARG_DEF(ARG_type, ARG_name)\ + ARG_type ARG_name; +#define ARG_IN(ARG_name)\ + if (copy_from_user(&ARG_name, arg, sizeof(ARG_name))) {\ + ret = -EFAULT;\ + break;\ + } +#define ARGR(ARG_name) ARG_name +#define ARGA(ARG_name) &ARG_name +#define ARG_OUT(ARG_name)\ + if (copy_to_user(arg, &ARG_name, sizeof(ARG_name))) {\ + ret = -EFAULT;\ + break;\ + } + +#endif + +int pwc_ioctl(struct pwc_device *pdev, unsigned int cmd, void *arg) +{ + int ret = 0; + + switch(cmd) { + case VIDIOCPWCRUSER: + { + if (pwc_restore_user(pdev)) + ret = -EINVAL; + break; + } + + case VIDIOCPWCSUSER: + { + if (pwc_save_user(pdev)) + ret = -EINVAL; + break; + } + + case VIDIOCPWCFACTORY: + { + if (pwc_restore_factory(pdev)) + ret = -EINVAL; + break; + } + + case VIDIOCPWCSCQUAL: + { + ARG_DEF(int, qual) + + ARG_IN(qual) + if (ARGR(qual) < 0 || ARGR(qual) > 3) + ret = -EINVAL; + else + ret = pwc_try_video_mode(pdev, pdev->view.x, pdev->view.y, pdev->vframes, ARGR(qual), pdev->vsnapshot); + if (ret >= 0) + pdev->vcompression = ARGR(qual); + break; + } + + case VIDIOCPWCGCQUAL: + { + ARG_DEF(int, qual) + + ARGR(qual) = pdev->vcompression; + ARG_OUT(qual) + break; + } + + case VIDIOCPWCPROBE: + { + ARG_DEF(struct pwc_probe, probe) + + strcpy(ARGR(probe).name, pdev->vdev->name); + ARGR(probe).type = pdev->type; + ARG_OUT(probe) + break; + } + + case VIDIOCPWCGSERIAL: + { + ARG_DEF(struct pwc_serial, serial) + + strcpy(ARGR(serial).serial, pdev->serial); + ARG_OUT(serial) + break; + } + + case VIDIOCPWCSAGC: + { + ARG_DEF(int, agc) + + ARG_IN(agc) + if (pwc_set_agc(pdev, ARGR(agc) < 0 ? 1 : 0, ARGR(agc))) + ret = -EINVAL; + break; + } + + case VIDIOCPWCGAGC: + { + ARG_DEF(int, agc) + + if (pwc_get_agc(pdev, ARGA(agc))) + ret = -EINVAL; + ARG_OUT(agc) + break; + } + + case VIDIOCPWCSSHUTTER: + { + ARG_DEF(int, shutter_speed) + + ARG_IN(shutter_speed) + ret = pwc_set_shutter_speed(pdev, ARGR(shutter_speed) < 0 ? 1 : 0, ARGR(shutter_speed)); + break; + } + + case VIDIOCPWCSAWB: + { + ARG_DEF(struct pwc_whitebalance, wb) + + ARG_IN(wb) + ret = pwc_set_awb(pdev, ARGR(wb).mode); + if (ret >= 0 && ARGR(wb).mode == PWC_WB_MANUAL) { + pwc_set_red_gain(pdev, ARGR(wb).manual_red); + pwc_set_blue_gain(pdev, ARGR(wb).manual_blue); + } + break; + } + + case VIDIOCPWCGAWB: + { + ARG_DEF(struct pwc_whitebalance, wb) + + memset(ARGA(wb), 0, sizeof(struct pwc_whitebalance)); + ARGR(wb).mode = pwc_get_awb(pdev); + if (ARGR(wb).mode < 0) + ret = -EINVAL; + else { + if (ARGR(wb).mode == PWC_WB_MANUAL) { + ret = pwc_get_red_gain(pdev, &ARGR(wb).manual_red); + if (ret < 0) + break; + ret = pwc_get_blue_gain(pdev, &ARGR(wb).manual_blue); + if (ret < 0) + break; + } + if (ARGR(wb).mode == PWC_WB_AUTO) { + ret = pwc_read_red_gain(pdev, &ARGR(wb).read_red); + if (ret < 0) + break; + ret =pwc_read_blue_gain(pdev, &ARGR(wb).read_blue); + if (ret < 0) + break; + } + } + ARG_OUT(wb) + break; + } + + case VIDIOCPWCSAWBSPEED: + { + ARG_DEF(struct pwc_wb_speed, wbs) + + if (ARGR(wbs).control_speed > 0) { + ret = pwc_set_wb_speed(pdev, ARGR(wbs).control_speed); + } + if (ARGR(wbs).control_delay > 0) { + ret = pwc_set_wb_delay(pdev, ARGR(wbs).control_delay); + } + break; + } + + case VIDIOCPWCGAWBSPEED: + { + ARG_DEF(struct pwc_wb_speed, wbs) + + ret = pwc_get_wb_speed(pdev, &ARGR(wbs).control_speed); + if (ret < 0) + break; + ret = pwc_get_wb_delay(pdev, &ARGR(wbs).control_delay); + if (ret < 0) + break; + ARG_OUT(wbs) + break; + } + + case VIDIOCPWCSLED: + { + ARG_DEF(struct pwc_leds, leds) + + ARG_IN(leds) + ret = pwc_set_leds(pdev, ARGR(leds).led_on, ARGR(leds).led_off); + break; + } + + + case VIDIOCPWCGLED: + { + ARG_DEF(struct pwc_leds, leds) + + ret = pwc_get_leds(pdev, &ARGR(leds).led_on, &ARGR(leds).led_off); + ARG_OUT(leds) + break; + } + + case VIDIOCPWCSCONTOUR: + { + ARG_DEF(int, contour) + + ARG_IN(contour) + ret = pwc_set_contour(pdev, ARGR(contour)); + break; + } + + case VIDIOCPWCGCONTOUR: + { + ARG_DEF(int, contour) + + ret = pwc_get_contour(pdev, ARGA(contour)); + ARG_OUT(contour) + break; + } + + case VIDIOCPWCSBACKLIGHT: + { + ARG_DEF(int, backlight) + + ARG_IN(backlight) + ret = pwc_set_backlight(pdev, ARGR(backlight)); + break; + } + + case VIDIOCPWCGBACKLIGHT: + { + ARG_DEF(int, backlight) + + ret = pwc_get_backlight(pdev, ARGA(backlight)); + ARG_OUT(backlight) + break; + } + + case VIDIOCPWCSFLICKER: + { + ARG_DEF(int, flicker) + + ARG_IN(flicker) + ret = pwc_set_flicker(pdev, ARGR(flicker)); + break; + } + + case VIDIOCPWCGFLICKER: + { + ARG_DEF(int, flicker) + + ret = pwc_get_flicker(pdev, ARGA(flicker)); + ARG_OUT(flicker) + break; + } + + case VIDIOCPWCSDYNNOISE: + { + ARG_DEF(int, dynnoise) + + ARG_IN(dynnoise) + ret = pwc_set_dynamic_noise(pdev, ARGR(dynnoise)); + break; + } + + case VIDIOCPWCGDYNNOISE: + { + ARG_DEF(int, dynnoise) + + ret = pwc_get_dynamic_noise(pdev, ARGA(dynnoise)); + ARG_OUT(dynnoise); + break; + } + + case VIDIOCPWCGREALSIZE: + { + ARG_DEF(struct pwc_imagesize, size) + + ARGR(size).width = pdev->image.x; + ARGR(size).height = pdev->image.y; + ARG_OUT(size) + break; + } + + case VIDIOCPWCMPTRESET: + { + if (pdev->features & FEATURE_MOTOR_PANTILT) + { + ARG_DEF(int, flags) + + ARG_IN(flags) + ret = pwc_mpt_reset(pdev, ARGR(flags)); + if (ret >= 0) + { + pdev->pan_angle = 0; + pdev->tilt_angle = 0; + } + } + else + { + ret = -ENXIO; + } + break; + } + + case VIDIOCPWCMPTGRANGE: + { + if (pdev->features & FEATURE_MOTOR_PANTILT) + { + ARG_DEF(struct pwc_mpt_range, range) + + ARGR(range) = pdev->angle_range; + ARG_OUT(range) + } + else + { + ret = -ENXIO; + } + break; + } + + case VIDIOCPWCMPTSANGLE: + { + int new_pan, new_tilt; + + if (pdev->features & FEATURE_MOTOR_PANTILT) + { + ARG_DEF(struct pwc_mpt_angles, angles) + + ARG_IN(angles) + /* The camera can only set relative angles, so + do some calculations when getting an absolute angle . + */ + if (ARGR(angles).absolute) + { + new_pan = ARGR(angles).pan; + new_tilt = ARGR(angles).tilt; + } + else + { + new_pan = pdev->pan_angle + ARGR(angles).pan; + new_tilt = pdev->tilt_angle + ARGR(angles).tilt; + } + /* check absolute ranges */ + if (new_pan < pdev->angle_range.pan_min || + new_pan > pdev->angle_range.pan_max || + new_tilt < pdev->angle_range.tilt_min || + new_tilt > pdev->angle_range.tilt_max) + { + ret = -ERANGE; + } + else + { + /* go to relative range, check again */ + new_pan -= pdev->pan_angle; + new_tilt -= pdev->tilt_angle; + /* angles are specified in degrees * 100, thus the limit = 36000 */ + if (new_pan < -36000 || new_pan > 36000 || new_tilt < -36000 || new_tilt > 36000) + ret = -ERANGE; + } + if (ret == 0) /* no errors so far */ + { + ret = pwc_mpt_set_angle(pdev, new_pan, new_tilt); + if (ret >= 0) + { + pdev->pan_angle += new_pan; + pdev->tilt_angle += new_tilt; + } + if (ret == -EPIPE) /* stall -> out of range */ + ret = -ERANGE; + } + } + else + { + ret = -ENXIO; + } + break; + } + + case VIDIOCPWCMPTGANGLE: + { + + if (pdev->features & FEATURE_MOTOR_PANTILT) + { + ARG_DEF(struct pwc_mpt_angles, angles) + + ARGR(angles).absolute = 1; + ARGR(angles).pan = pdev->pan_angle; + ARGR(angles).tilt = pdev->tilt_angle; + ARG_OUT(angles) + } + else + { + ret = -ENXIO; + } + break; + } + + case VIDIOCPWCMPTSTATUS: + { + if (pdev->features & FEATURE_MOTOR_PANTILT) + { + ARG_DEF(struct pwc_mpt_status, status) + + ret = pwc_mpt_get_status(pdev, ARGA(status)); + ARG_OUT(status) + } + else + { + ret = -ENXIO; + } + break; + } + + case VIDIOCPWCGVIDCMD: + { + ARG_DEF(struct pwc_video_command, cmd); + + ARGR(cmd).type = pdev->type; + ARGR(cmd).release = pdev->release; + ARGR(cmd).command_len = pdev->cmd_len; + memcpy(&ARGR(cmd).command_buf, pdev->cmd_buf, pdev->cmd_len); + ARGR(cmd).bandlength = pdev->vbandlength; + ARGR(cmd).frame_size = pdev->frame_size; + ARG_OUT(cmd) + break; + } + /* + case VIDIOCPWCGVIDTABLE: + { + ARG_DEF(struct pwc_table_init_buffer, table); + ARGR(table).len = pdev->cmd_len; + memcpy(&ARGR(table).buffer, pdev->decompress_data, pdev->decompressor->table_size); + ARG_OUT(table) + break; + } + */ + + default: + ret = -ENOIOCTLCMD; + break; + } + + if (ret > 0) + return 0; + return ret; +} + + + diff --git a/drivers/usb/media/pwc/pwc-dec1.c b/drivers/usb/media/pwc/pwc-dec1.c new file mode 100644 index 000000000..57d03d917 --- /dev/null +++ b/drivers/usb/media/pwc/pwc-dec1.c @@ -0,0 +1,42 @@ +/* Linux driver for Philips webcam + Decompression for chipset version 1 + (C) 2004 Luc Saillard (luc@saillard.org) + + NOTE: this version of pwc is an unofficial (modified) release of pwc & pcwx + driver and thus may have bugs that are not present in the original version. + Please send bug reports and support requests to . + The decompression routines have been implemented by reverse-engineering the + Nemosoft binary pwcx module. Caveat emptor. + + This program is free software; you can redistribute it and/or modify + it under the terms of the 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 "pwc-dec1.h" + + +void pwc_dec1_init(int type, int release, void *buffer, void *table) +{ + +} + +void pwc_dec1_exit(void) +{ + + + +} + diff --git a/drivers/usb/media/pwc/pwc-dec1.h b/drivers/usb/media/pwc/pwc-dec1.h new file mode 100644 index 000000000..a7ffd9c45 --- /dev/null +++ b/drivers/usb/media/pwc/pwc-dec1.h @@ -0,0 +1,36 @@ +/* Linux driver for Philips webcam + (C) 2004 Luc Saillard (luc@saillard.org) + + NOTE: this version of pwc is an unofficial (modified) release of pwc & pcwx + driver and thus may have bugs that are not present in the original version. + Please send bug reports and support requests to . + The decompression routines have been implemented by reverse-engineering the + Nemosoft binary pwcx module. Caveat emptor. + + This program is free software; you can redistribute it and/or modify + it under the terms of the 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 PWC_DEC1_H +#define PWC_DEC1_H + +void pwc_dec1_init(int type, int release, void *buffer, void *private_data); +void pwc_dec1_exit(void); + +#endif + + + diff --git a/drivers/usb/media/pwc/pwc-dec23.c b/drivers/usb/media/pwc/pwc-dec23.c new file mode 100644 index 000000000..0a7ebd00c --- /dev/null +++ b/drivers/usb/media/pwc/pwc-dec23.c @@ -0,0 +1,623 @@ +/* Linux driver for Philips webcam + Decompression for chipset version 2 et 3 + (C) 2004 Luc Saillard (luc@saillard.org) + + NOTE: this version of pwc is an unofficial (modified) release of pwc & pcwx + driver and thus may have bugs that are not present in the original version. + Please send bug reports and support requests to . + The decompression routines have been implemented by reverse-engineering the + Nemosoft binary pwcx module. Caveat emptor. + + This program is free software; you can redistribute it and/or modify + it under the terms of the 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 "pwc-timon.h" +#include "pwc-kiara.h" +#include "pwc-dec23.h" +#include "pwc-ioctl.h" + +#include + +/**** + * + * + * + */ + + +static void fill_table_a000(unsigned int *p) +{ + static unsigned int initial_values[12] = { + 0xFFAD9B00, 0xFFDDEE00, 0x00221200, 0x00526500, + 0xFFC21E00, 0x003DE200, 0xFF924B80, 0xFFD2A300, + 0x002D5D00, 0x006DB480, 0xFFED3E00, 0x0012C200 + }; + static unsigned int values_derivated[12] = { + 0x0000A4CA, 0x00004424, 0xFFFFBBDC, 0xFFFF5B36, + 0x00007BC4, 0xFFFF843C, 0x0000DB69, 0x00005ABA, + 0xFFFFA546, 0xFFFF2497, 0x00002584, 0xFFFFDA7C + }; + unsigned int temp_values[12]; + int i,j; + + memcpy(temp_values,initial_values,sizeof(initial_values)); + for (i=0;i<256;i++) + { + for (j=0;j<12;j++) + { + *p++ = temp_values[j]; + temp_values[j] += values_derivated[j]; + } + } +} + +static void fill_table_d000(unsigned char *p) +{ + int bit,byte; + + for (bit=0; bit<8; bit++) + { + unsigned char bitpower = 1<=1 && k<3) + bit=(table[0]>>15)&7; + else if (k>=3 && k<6) + bit=(table[0]>>12)&7; + else if (k>=6 && k<10) + bit=(table[0]>>9)&7; + else if (k>=10 && k<13) + bit=(table[0]>>6)&7; + else if (k>=13 && k<15) + bit=(table[0]>>3)&7; + else + bit=(table[0])&7; + if (k == 0) + *(unsigned char *)p8++ = 8; + else + *(unsigned char *)p8++ = j - bit; + *(unsigned char *)p8++ = bit; + + pw = 1<xx + pdev->yy) + * + */ +void fill_table_dc00_d800(unsigned int precision, unsigned int *pdc00, unsigned int *pd800) +{ + int i; + unsigned int offset1, offset2; + + for(i=0,offset1=0x4000, offset2=0; i<256 ; i++,offset1+=0x7BC4, offset2+=0x7BC4) + { + unsigned int msb = offset1 >> 15; + + if ( msb > 255) + { + if (msb) + msb=0; + else + msb=255; + } + + *pdc00++ = msb << precision; + *pd800++ = offset2; + } + +} + +/* + * struct { + * unsigned char op; // operation to execute + * unsigned char bits; // bits use to perform operation + * unsigned char offset1; // offset to add to access in the table_0004 % 16 + * unsigned char offset2; // offset to add to access in the table_0004 + * } + * + */ +static unsigned int table_ops[] = { +0x02,0x00,0x00,0x00, 0x00,0x03,0x01,0x00, 0x00,0x04,0x01,0x10, 0x00,0x06,0x01,0x30, +0x02,0x00,0x00,0x00, 0x00,0x03,0x01,0x40, 0x00,0x05,0x01,0x20, 0x01,0x00,0x00,0x00, +0x02,0x00,0x00,0x00, 0x00,0x03,0x01,0x00, 0x00,0x04,0x01,0x50, 0x00,0x05,0x02,0x00, +0x02,0x00,0x00,0x00, 0x00,0x03,0x01,0x40, 0x00,0x05,0x03,0x00, 0x01,0x00,0x00,0x00, +0x02,0x00,0x00,0x00, 0x00,0x03,0x01,0x00, 0x00,0x04,0x01,0x10, 0x00,0x06,0x02,0x10, +0x02,0x00,0x00,0x00, 0x00,0x03,0x01,0x40, 0x00,0x05,0x01,0x60, 0x01,0x00,0x00,0x00, +0x02,0x00,0x00,0x00, 0x00,0x03,0x01,0x00, 0x00,0x04,0x01,0x50, 0x00,0x05,0x02,0x40, +0x02,0x00,0x00,0x00, 0x00,0x03,0x01,0x40, 0x00,0x05,0x03,0x40, 0x01,0x00,0x00,0x00, +0x02,0x00,0x00,0x00, 0x00,0x03,0x01,0x00, 0x00,0x04,0x01,0x10, 0x00,0x06,0x01,0x70, +0x02,0x00,0x00,0x00, 0x00,0x03,0x01,0x40, 0x00,0x05,0x01,0x20, 0x01,0x00,0x00,0x00, +0x02,0x00,0x00,0x00, 0x00,0x03,0x01,0x00, 0x00,0x04,0x01,0x50, 0x00,0x05,0x02,0x00, +0x02,0x00,0x00,0x00, 0x00,0x03,0x01,0x40, 0x00,0x05,0x03,0x00, 0x01,0x00,0x00,0x00, +0x02,0x00,0x00,0x00, 0x00,0x03,0x01,0x00, 0x00,0x04,0x01,0x10, 0x00,0x06,0x02,0x50, +0x02,0x00,0x00,0x00, 0x00,0x03,0x01,0x40, 0x00,0x05,0x01,0x60, 0x01,0x00,0x00,0x00, +0x02,0x00,0x00,0x00, 0x00,0x03,0x01,0x00, 0x00,0x04,0x01,0x50, 0x00,0x05,0x02,0x40, +0x02,0x00,0x00,0x00, 0x00,0x03,0x01,0x40, 0x00,0x05,0x03,0x40, 0x01,0x00,0x00,0x00 +}; + +/* + * TODO: multiply by 4 all values + * + */ +static unsigned int MulIdx[256] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 1, 2, 3, 0, 1, 2, 3, 0, 1, 2, 3, 0, 1, 2, 3, + 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, + 4, 4, 4, 4, 5, 5, 5, 5, 5, 5, 5, 5, 4, 4, 4, 4, + 6, 7, 8, 9, 7,10,11, 8, 8,11,10, 7, 9, 8, 7, 6, + 4, 5, 5, 4, 4, 5, 5, 4, 4, 5, 5, 4, 4, 5, 5, 4, + 1, 3, 0, 2, 1, 3, 0, 2, 1, 3, 0, 2, 1, 3, 0, 2, + 0, 3, 3, 0, 1, 2, 2, 1, 2, 1, 1, 2, 3, 0, 0, 3, + 0, 1, 2, 3, 3, 2, 1, 0, 3, 2, 1, 0, 0, 1, 2, 3, + 1, 1, 1, 1, 3, 3, 3, 3, 0, 0, 0, 0, 2, 2, 2, 2, + 7,10,11, 8, 9, 8, 7, 6, 6, 7, 8, 9, 8,11,10, 7, + 4, 5, 5, 4, 5, 4, 4, 5, 5, 4, 4, 5, 4, 5, 5, 4, + 7, 9, 6, 8,10, 8, 7,11,11, 7, 8,10, 8, 6, 9, 7, + 1, 3, 0, 2, 2, 0, 3, 1, 2, 0, 3, 1, 1, 3, 0, 2, + 1, 2, 2, 1, 3, 0, 0, 3, 0, 3, 3, 0, 2, 1, 1, 2, +10, 8, 7,11, 8, 6, 9, 7, 7, 9, 6, 8,11, 7, 8,10 +}; + + + +void pwc_dec23_init(int type, int release, unsigned char *mode, void *data) +{ + int flags; + struct pwc_dec23_private *pdev = data; + release = release; + + switch (type) + { + case 720: + case 730: + case 740: + case 750: + flags = mode[2]&0x18; /* our: flags = 8, mode[2]==e8 */ + if (flags==8) + pdev->zz = 7; + else if (flags==0x10) + pdev->zz = 8; + else + pdev->zz = 6; + flags = mode[2]>>5; /* our: 7 */ + + fill_table_color(flags, (unsigned int *)KiaraRomTable, pdev->table_0004, pdev->table_8004); + break; + + + case 675: + case 680: + case 690: + flags = mode[2]&6; + if (flags==2) + pdev->zz = 7; + else if (flags==4) + pdev->zz = 8; + else + pdev->zz = 6; + flags = mode[2]>>3; + + fill_table_color(flags, (unsigned int *)TimonRomTable, pdev->table_0004, pdev->table_8004); + break; + + default: + /* Not supported */ + return; + } + + /* * * * ** */ + pdev->xx = 8 - pdev->zz; + pdev->yy = 15 - pdev->xx; + pdev->zzmask = 0xFF>>pdev->xx; + //pdev->zzmask = (1U<zz)-1; + + + fill_table_dc00_d800(pdev->xx + pdev->yy, pdev->table_dc00, pdev->table_d800); + fill_table_a000(pdev->table_a004); + fill_table_d000(pdev->table_d004); +} + + +/* + * To manage the stream, we keep in a 32 bits variables, + * the next bits in the stream. fill_reservoir() add to + * the reservoir at least wanted nbits. + * + * + */ +#define fill_nbits(reservoir,nbits_in_reservoir,stream,nbits_wanted) do { \ + while (nbits_in_reservoir>= nbits_wanted; \ + nbits_in_reservoir -= nbits_wanted; \ +} while(0); + + + +static void DecompressBand23(const struct pwc_dec23_private *pdev, + const unsigned char *rawyuv, + unsigned char *planar_y, + unsigned char *planar_u, + unsigned char *planar_v, + unsigned int image_x, /* aka number of pixels wanted ??? */ + unsigned int pixels_per_line, /* aka number of pixels per line */ + int flags) +{ + + + unsigned int reservoir, nbits_in_reservoir; + int first_4_bits; + unsigned int bytes_per_channel; + int line_size; /* size of the line (4Y+U+V) */ + int passes; + const unsigned char *ptable0004, *ptable8004; + + int even_line; + unsigned int temp_colors[16]; + int nblocks; + + const unsigned char *stream; + unsigned char *dest_y, *dest_u=NULL, *dest_v=NULL; + unsigned int offset_to_plane_u, offset_to_plane_v; + + int i; + + + reservoir = 0; + nbits_in_reservoir = 0; + stream = rawyuv+1; /* The first byte of the stream is skipped */ + even_line = 1; + + get_nbits(reservoir,nbits_in_reservoir,stream,4,first_4_bits); + + line_size = pixels_per_line*3; + + for (passes=0;passes<2;passes++) + { + if (passes==0) + { + bytes_per_channel = pixels_per_line; + dest_y = planar_y; + nblocks = image_x/4; + } + else + { + /* Format planar: All Y, then all U, then all V */ + bytes_per_channel = pixels_per_line/2; + dest_u = planar_u; + dest_v = planar_v; + dest_y = dest_u; + nblocks = image_x/8; + } + + offset_to_plane_u = bytes_per_channel*2; + offset_to_plane_v = bytes_per_channel*3; + /* + printf("bytes_per_channel = %d\n",bytes_per_channel); + printf("offset_to_plane_u = %d\n",offset_to_plane_u); + printf("offset_to_plane_v = %d\n",offset_to_plane_v); + */ + + while (nblocks-->0) + { + unsigned int gray_index; + + fill_nbits(reservoir,nbits_in_reservoir,stream,16); + gray_index = reservoir & pdev->zzmask; + reservoir >>= pdev->zz; + nbits_in_reservoir -= pdev->zz; + + fill_nbits(reservoir,nbits_in_reservoir,stream,2); + + if ( (reservoir & 3) == 0) + { + reservoir>>=2; + nbits_in_reservoir-=2; + for (i=0;i<16;i++) + temp_colors[i] = pdev->table_dc00[gray_index]; + + } + else + { + unsigned int channel_v, offset1; + + /* swap bit 0 and 2 of offset_OR */ + channel_v = ((reservoir & 1) << 2) | (reservoir & 2) | ((reservoir & 4)>>2); + reservoir>>=3; + nbits_in_reservoir-=3; + + for (i=0;i<16;i++) + temp_colors[i] = pdev->table_d800[gray_index]; + + ptable0004 = pdev->table_0004 + (passes*16384) + (first_4_bits*1024) + (channel_v*128); + ptable8004 = pdev->table_8004 + (passes*4096) + (first_4_bits*256) + (channel_v*32); + + offset1 = 0; + while(1) + { + unsigned int index_in_table_ops, op, rows=0; + fill_nbits(reservoir,nbits_in_reservoir,stream,16); + + /* mode is 0,1 or 2 */ + index_in_table_ops = (reservoir&0x3F); + op = table_ops[ index_in_table_ops*4 ]; + if (op == 2) + { + reservoir >>= 2; + nbits_in_reservoir -= 2; + break; /* exit the while(1) */ + } + if (op == 0) + { + unsigned int shift; + + offset1 = (offset1 + table_ops[index_in_table_ops*4+2]) & 0x0F; + shift = table_ops[ index_in_table_ops*4+1 ]; + reservoir >>= shift; + nbits_in_reservoir -= shift; + rows = ptable0004[ offset1 + table_ops[index_in_table_ops*4+3] ]; + } + if (op == 1) + { + /* 10bits [ xxxx xxxx yyyy 000 ] + * yyy => offset in the table8004 + * xxx => offset in the tabled004 + */ + unsigned int mask, shift; + unsigned int col1, row1, total_bits; + + offset1 = (offset1 + ((reservoir>>3)&0x0F)+1) & 0x0F; + + col1 = (reservoir>>7) & 0xFF; + row1 = ptable8004 [ offset1*2 ]; + + /* Bit mask table */ + mask = pdev->table_d004[ (row1<<8) + col1 ]; + shift = ptable8004 [ offset1*2 + 1]; + rows = ((mask << shift) + 0x80) & 0xFF; + + total_bits = row1 + 8; + reservoir >>= total_bits; + nbits_in_reservoir -= total_bits; + } + { + const unsigned int *table_a004 = pdev->table_a004 + rows*12; + unsigned int *poffset = MulIdx + offset1*16; /* 64/4 (int) */ + for (i=0;i<16;i++) + { + temp_colors[i] += table_a004[ *poffset ]; + poffset++; + } + } + } + } +#define USE_SIGNED_INT_FOR_COLOR +#ifdef USE_SIGNED_INT_FOR_COLOR +# define CLAMP(x) ((x)>255?255:((x)<0?0:x)) +#else +# define CLAMP(x) ((x)>255?255:x) +#endif + + if (passes == 0) + { +#ifdef USE_SIGNED_INT_FOR_COLOR + const int *c = temp_colors; +#else + const unsigned int *c = temp_colors; +#endif + unsigned char *d; + + d = dest_y; + for (i=0;i<4;i++,c++) + *d++ = CLAMP((*c) >> pdev->yy); + + d = dest_y + bytes_per_channel; + for (i=0;i<4;i++,c++) + *d++ = CLAMP((*c) >> pdev->yy); + + d = dest_y + offset_to_plane_u; + for (i=0;i<4;i++,c++) + *d++ = CLAMP((*c) >> pdev->yy); + + d = dest_y + offset_to_plane_v; + for (i=0;i<4;i++,c++) + *d++ = CLAMP((*c) >> pdev->yy); + + dest_y += 4; + } + else if (passes == 1) + { +#ifdef USE_SIGNED_INT_FOR_COLOR + int *c1 = temp_colors; + int *c2 = temp_colors+4; +#else + unsigned int *c1 = temp_colors; + unsigned int *c2 = temp_colors+4; +#endif + unsigned char *d; + + d = dest_y; + for (i=0;i<4;i++,c1++,c2++) + { + *d++ = CLAMP((*c1) >> pdev->yy); + *d++ = CLAMP((*c2) >> pdev->yy); + } + c1 = temp_colors+12; + //c2 = temp_colors+8; + d = dest_y + bytes_per_channel; + for (i=0;i<4;i++,c1++,c2++) + { + *d++ = CLAMP((*c1) >> pdev->yy); + *d++ = CLAMP((*c2) >> pdev->yy); + } + + if (even_line) /* Each line, swap u/v */ + { + even_line=0; + dest_y = dest_v; + dest_u += 8; + } + else + { + even_line=1; + dest_y = dest_u; + dest_v += 8; + } + } + + } /* end of while (nblocks-->0) */ + + } /* end of for (passes=0;passes<2;passes++) */ + +} + + +/** + * + * image: size of the image wanted + * view : size of the image returned by the camera + * offset: (x,y) to displayer image in the view + * + * src: raw data + * dst: image output + * flags: PWCX_FLAG_PLANAR + * pdev: private buffer + * bandlength: + * + */ +void pwc_dec23_decompress(const struct pwc_coord *image, + const struct pwc_coord *view, + const struct pwc_coord *offset, + const void *src, + void *dst, + int flags, + const void *data, + int bandlength) +{ + const struct pwc_dec23_private *pdev = data; + unsigned char *pout, *pout_planar_y=NULL, *pout_planar_u=NULL, *pout_planar_v=NULL; + int i,n,stride,pixel_size; + + + if (flags & PWCX_FLAG_BAYER) + { + pout = dst + (view->x * offset->y) + offset->x; + pixel_size = view->x * 4; + } + else + { + n = view->x * view->y; + + /* offset in Y plane */ + stride = view->x * offset->y; + pout_planar_y = dst + stride + offset->x; + + /* offsets in U/V planes */ + stride = (view->x * offset->y)/4 + offset->x/2; + pout_planar_u = dst + n + + stride; + pout_planar_v = dst + n + n/4 + stride; + + pixel_size = view->x * 4; + } + + + for (i=0;iy;i+=4) + { + if (flags & PWCX_FLAG_BAYER) + { + //TODO: + //DecompressBandBayer(pdev,src,pout,image.x,view->x,flags); + src += bandlength; + pout += pixel_size; + } + else + { + DecompressBand23(pdev,src,pout_planar_y,pout_planar_u,pout_planar_v,image->x,view->x,flags); + src += bandlength; + pout_planar_y += pixel_size; + pout_planar_u += view->x; + pout_planar_v += view->x; + } + } +} + +void pwc_dec23_exit(void) +{ + /* Do nothing */ + +} + diff --git a/drivers/usb/media/pwc/pwc-dec23.h b/drivers/usb/media/pwc/pwc-dec23.h new file mode 100644 index 000000000..5b2aacdef --- /dev/null +++ b/drivers/usb/media/pwc/pwc-dec23.h @@ -0,0 +1,58 @@ +/* Linux driver for Philips webcam + (C) 2004 Luc Saillard (luc@saillard.org) + + NOTE: this version of pwc is an unofficial (modified) release of pwc & pcwx + driver and thus may have bugs that are not present in the original version. + Please send bug reports and support requests to . + The decompression routines have been implemented by reverse-engineering the + Nemosoft binary pwcx module. Caveat emptor. + + This program is free software; you can redistribute it and/or modify + it under the terms of the 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 PWC_DEC23_H +#define PWC_DEC23_H + +struct pwc_dec23_private +{ + unsigned char xx,yy,zz,zzmask; + + unsigned char table_0004[2*0x4000]; + unsigned char table_8004[2*0x1000]; + unsigned int table_a004[256*12]; + + unsigned char table_d004[8*256]; + unsigned int table_d800[256]; + unsigned int table_dc00[256]; +}; + + +void pwc_dec23_init(int type, int release, unsigned char *buffer, void *private_data); +void pwc_dec23_exit(void); +void pwc_dec23_decompress(const struct pwc_coord *image, + const struct pwc_coord *view, + const struct pwc_coord *offset, + const void *src, + void *dst, + int flags, + const void *data, + int bandlength); + + + +#endif + + + diff --git a/drivers/usb/media/pwc/pwc-if.c b/drivers/usb/media/pwc/pwc-if.c new file mode 100644 index 000000000..d5c4f24f8 --- /dev/null +++ b/drivers/usb/media/pwc/pwc-if.c @@ -0,0 +1,2211 @@ +/* Linux driver for Philips webcam + USB and Video4Linux interface part. + (C) 1999-2004 Nemosoft Unv. + (C) 2004 Luc Saillard (luc@saillard.org) + + NOTE: this version of pwc is an unofficial (modified) release of pwc & pcwx + driver and thus may have bugs that are not present in the original version. + Please send bug reports and support requests to . + The decompression routines have been implemented by reverse-engineering the + Nemosoft binary pwcx module. Caveat emptor. + + This program is free software; you can redistribute it and/or modify + it under the terms of the 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 + +*/ + +/* + This code forms the interface between the USB layers and the Philips + specific stuff. Some adanved stuff of the driver falls under an + NDA, signed between me and Philips B.V., Eindhoven, the Netherlands, and + is thus not distributed in source form. The binary pwcx.o module + contains the code that falls under the NDA. + + In case you're wondering: 'pwc' stands for "Philips WebCam", but + I really didn't want to type 'philips_web_cam' every time (I'm lazy as + any Linux kernel hacker, but I don't like uncomprehensible abbreviations + without explanation). + + Oh yes, convention: to disctinguish between all the various pointers to + device-structures, I use these names for the pointer variables: + udev: struct usb_device * + vdev: struct video_device * + pdev: struct pwc_devive * +*/ + +/* Contributors: + - Alvarado: adding whitebalance code + - Alistar Moire: QuickCam 3000 Pro device/product ID + - Tony Hoyle: Creative Labs Webcam 5 device/product ID + - Mark Burazin: solving hang in VIDIOCSYNC when camera gets unplugged + - Jk Fang: Sotec Afina Eye ID + - Xavier Roche: QuickCam Pro 4000 ID + - Jens Knudsen: QuickCam Zoom ID + - J. Debert: QuickCam for Notebooks ID +*/ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "pwc.h" +#include "pwc-ioctl.h" +#include "pwc-kiara.h" +#include "pwc-timon.h" +#include "pwc-dec23.h" +#include "pwc-dec1.h" +#include "pwc-uncompress.h" + +/* Function prototypes and driver templates */ + +/* hotplug device table support */ +static struct usb_device_id pwc_device_table [] = { + { USB_DEVICE(0x0471, 0x0302) }, /* Philips models */ + { USB_DEVICE(0x0471, 0x0303) }, + { USB_DEVICE(0x0471, 0x0304) }, + { USB_DEVICE(0x0471, 0x0307) }, + { USB_DEVICE(0x0471, 0x0308) }, + { USB_DEVICE(0x0471, 0x030C) }, + { USB_DEVICE(0x0471, 0x0310) }, + { USB_DEVICE(0x0471, 0x0311) }, + { USB_DEVICE(0x0471, 0x0312) }, + { USB_DEVICE(0x0471, 0x0313) }, /* the 'new' 720K */ + { USB_DEVICE(0x069A, 0x0001) }, /* Askey */ + { USB_DEVICE(0x046D, 0x08B0) }, /* Logitech QuickCam Pro 3000 */ + { USB_DEVICE(0x046D, 0x08B1) }, /* Logitech QuickCam Notebook Pro */ + { USB_DEVICE(0x046D, 0x08B2) }, /* Logitech QuickCam Pro 4000 */ + { USB_DEVICE(0x046D, 0x08B3) }, /* Logitech QuickCam Zoom (old model) */ + { USB_DEVICE(0x046D, 0x08B4) }, /* Logitech QuickCam Zoom (new model) */ + { USB_DEVICE(0x046D, 0x08B5) }, /* Logitech QuickCam Orbit/Sphere */ + { USB_DEVICE(0x046D, 0x08B6) }, /* Logitech (reserved) */ + { USB_DEVICE(0x046D, 0x08B7) }, /* Logitech (reserved) */ + { USB_DEVICE(0x046D, 0x08B8) }, /* Logitech (reserved) */ + { USB_DEVICE(0x055D, 0x9000) }, /* Samsung */ + { USB_DEVICE(0x055D, 0x9001) }, + { USB_DEVICE(0x041E, 0x400C) }, /* Creative Webcam 5 */ + { USB_DEVICE(0x041E, 0x4011) }, /* Creative Webcam Pro Ex */ + { USB_DEVICE(0x04CC, 0x8116) }, /* Afina Eye */ + { USB_DEVICE(0x06BE, 0x8116) }, /* new Afina Eye */ + { USB_DEVICE(0x0d81, 0x1910) }, /* Visionite */ + { USB_DEVICE(0x0d81, 0x1900) }, + { } +}; +MODULE_DEVICE_TABLE(usb, pwc_device_table); + +static int usb_pwc_probe(struct usb_interface *intf, const struct usb_device_id *id); +static void usb_pwc_disconnect(struct usb_interface *intf); + +static struct usb_driver pwc_driver = { + .owner = THIS_MODULE, + .name = "Philips webcam", /* name */ + .id_table = pwc_device_table, + .probe = usb_pwc_probe, /* probe() */ + .disconnect = usb_pwc_disconnect, /* disconnect() */ +}; + +#define MAX_DEV_HINTS 20 +#define MAX_ISOC_ERRORS 20 + +static int default_size = PSZ_QCIF; +static int default_fps = 10; +static int default_fbufs = 3; /* Default number of frame buffers */ +static int default_mbufs = 2; /* Default number of mmap() buffers */ + int pwc_trace = TRACE_MODULE | TRACE_FLOW | TRACE_PWCX; +static int power_save = 0; +static int led_on = 100, led_off = 0; /* defaults to LED that is on while in use */ + int pwc_preferred_compression = 2; /* 0..3 = uncompressed..high */ +static struct { + int type; + char serial_number[30]; + int device_node; + struct pwc_device *pdev; +} device_hint[MAX_DEV_HINTS]; + +/***/ + +static int pwc_video_open(struct inode *inode, struct file *file); +static int pwc_video_close(struct inode *inode, struct file *file); +static ssize_t pwc_video_read(struct file *file, char *buf, + size_t count, loff_t *ppos); +static unsigned int pwc_video_poll(struct file *file, poll_table *wait); +static int pwc_video_ioctl(struct inode *inode, struct file *file, + unsigned int ioctlnr, unsigned long arg); +static int pwc_video_mmap(struct file *file, struct vm_area_struct *vma); + +static struct file_operations pwc_fops = { + .owner = THIS_MODULE, + .open = pwc_video_open, + .release = pwc_video_close, + .read = pwc_video_read, + .poll = pwc_video_poll, + .mmap = pwc_video_mmap, + .ioctl = pwc_video_ioctl, + .llseek = no_llseek, +}; +static struct video_device pwc_template = { + .owner = THIS_MODULE, + .name = "Philips Webcam", /* Filled in later */ + .type = VID_TYPE_CAPTURE, + .hardware = VID_HARDWARE_PWC, + .release = video_device_release, + .fops = &pwc_fops, + .minor = -1, +}; + +/***************************************************************************/ + +/* Okay, this is some magic that I worked out and the reasoning behind it... + + The biggest problem with any USB device is of course: "what to do + when the user unplugs the device while it is in use by an application?" + We have several options: + 1) Curse them with the 7 plagues when they do (requires divine intervention) + 2) Tell them not to (won't work: they'll do it anyway) + 3) Oops the kernel (this will have a negative effect on a user's uptime) + 4) Do something sensible. + + Of course, we go for option 4. + + It happens that this device will be linked to two times, once from + usb_device and once from the video_device in their respective 'private' + pointers. This is done when the device is probed() and all initialization + succeeded. The pwc_device struct links back to both structures. + + When a device is unplugged while in use it will be removed from the + list of known USB devices; I also de-register it as a V4L device, but + unfortunately I can't free the memory since the struct is still in use + by the file descriptor. This free-ing is then deferend until the first + opportunity. Crude, but it works. + + A small 'advantage' is that if a user unplugs the cam and plugs it back + in, it should get assigned the same video device minor, but unfortunately + it's non-trivial to re-link the cam back to the video device... (that + would surely be magic! :)) +*/ + +/***************************************************************************/ +/* Private functions */ + +/* Here we want the physical address of the memory. + * This is used when initializing the contents of the area. + */ +static inline unsigned long kvirt_to_pa(unsigned long adr) +{ + unsigned long kva, ret; + + kva = (unsigned long) page_address(vmalloc_to_page((void *)adr)); + kva |= adr & (PAGE_SIZE-1); /* restore the offset */ + ret = __pa(kva); + return ret; +} + +static void * rvmalloc(unsigned long size) +{ + void * mem; + unsigned long adr; + + size=PAGE_ALIGN(size); + mem=vmalloc_32(size); + if (mem) + { + memset(mem, 0, size); /* Clear the ram out, no junk to the user */ + adr=(unsigned long) mem; + while (size > 0) + { + SetPageReserved(vmalloc_to_page((void *)adr)); + adr+=PAGE_SIZE; + size-=PAGE_SIZE; + } + } + return mem; +} + +static void rvfree(void * mem, unsigned long size) +{ + unsigned long adr; + + if (mem) + { + adr=(unsigned long) mem; + while ((long) size > 0) + { + ClearPageReserved(vmalloc_to_page((void *)adr)); + adr+=PAGE_SIZE; + size-=PAGE_SIZE; + } + vfree(mem); + } +} + + + + +static int pwc_allocate_buffers(struct pwc_device *pdev) +{ + int i; + void *kbuf; + + Trace(TRACE_MEMORY, ">> pwc_allocate_buffers(pdev = 0x%p)\n", pdev); + + if (pdev == NULL) + return -ENXIO; + +#ifdef PWC_MAGIC + if (pdev->magic != PWC_MAGIC) { + Err("allocate_buffers(): magic failed.\n"); + return -ENXIO; + } +#endif + /* Allocate Isochronuous pipe buffers */ + for (i = 0; i < MAX_ISO_BUFS; i++) { + if (pdev->sbuf[i].data == NULL) { + kbuf = kmalloc(ISO_BUFFER_SIZE, GFP_KERNEL); + if (kbuf == NULL) { + Err("Failed to allocate iso buffer %d.\n", i); + return -ENOMEM; + } + Trace(TRACE_MEMORY, "Allocated iso buffer at %p.\n", kbuf); + pdev->sbuf[i].data = kbuf; + memset(kbuf, 0, ISO_BUFFER_SIZE); + } + } + + /* Allocate frame buffer structure */ + if (pdev->fbuf == NULL) { + kbuf = kmalloc(default_fbufs * sizeof(struct pwc_frame_buf), GFP_KERNEL); + if (kbuf == NULL) { + Err("Failed to allocate frame buffer structure.\n"); + return -ENOMEM; + } + Trace(TRACE_MEMORY, "Allocated frame buffer structure at %p.\n", kbuf); + pdev->fbuf = kbuf; + memset(kbuf, 0, default_fbufs * sizeof(struct pwc_frame_buf)); + } + /* create frame buffers, and make circular ring */ + for (i = 0; i < default_fbufs; i++) { + if (pdev->fbuf[i].data == NULL) { + kbuf = vmalloc(PWC_FRAME_SIZE); /* need vmalloc since frame buffer > 128K */ + if (kbuf == NULL) { + Err("Failed to allocate frame buffer %d.\n", i); + return -ENOMEM; + } + Trace(TRACE_MEMORY, "Allocated frame buffer %d at %p.\n", i, kbuf); + pdev->fbuf[i].data = kbuf; + memset(kbuf, 128, PWC_FRAME_SIZE); + } + } + + /* Allocate decompressor table space */ + kbuf = NULL; + switch (pdev->type) + { + case 675: + case 680: + case 690: + case 720: + case 730: + case 740: + case 750: + Trace(TRACE_MEMORY,"private_data(%d)\n",sizeof(struct pwc_dec23_private)); + kbuf = kmalloc(sizeof(struct pwc_dec23_private), GFP_KERNEL); /* Timon & Kiara */ + break; + case 645: + case 646: + /* TODO & FIXME */ + kbuf = kmalloc(sizeof(struct pwc_dec23_private), GFP_KERNEL); + break; + } + if (kbuf == NULL) { + Err("Failed to allocate decompress table.\n"); + return -ENOMEM; + } + pdev->decompress_data = kbuf; + + /* Allocate image buffer; double buffer for mmap() */ + kbuf = rvmalloc(default_mbufs * pdev->len_per_image); + if (kbuf == NULL) { + Err("Failed to allocate image buffer(s). needed (%d)\n",default_mbufs * pdev->len_per_image); + return -ENOMEM; + } + Trace(TRACE_MEMORY, "Allocated image buffer at %p.\n", kbuf); + pdev->image_data = kbuf; + for (i = 0; i < default_mbufs; i++) + pdev->image_ptr[i] = kbuf + i * pdev->len_per_image; + for (; i < MAX_IMAGES; i++) + pdev->image_ptr[i] = NULL; + + kbuf = NULL; + + Trace(TRACE_MEMORY, "<< pwc_allocate_buffers()\n"); + return 0; +} + +static void pwc_free_buffers(struct pwc_device *pdev) +{ + int i; + + Trace(TRACE_MEMORY, "Entering free_buffers(%p).\n", pdev); + + if (pdev == NULL) + return; +#ifdef PWC_MAGIC + if (pdev->magic != PWC_MAGIC) { + Err("free_buffers(): magic failed.\n"); + return; + } +#endif + + /* Release Iso-pipe buffers */ + for (i = 0; i < MAX_ISO_BUFS; i++) + if (pdev->sbuf[i].data != NULL) { + Trace(TRACE_MEMORY, "Freeing ISO buffer at %p.\n", pdev->sbuf[i].data); + kfree(pdev->sbuf[i].data); + pdev->sbuf[i].data = NULL; + } + + /* The same for frame buffers */ + if (pdev->fbuf != NULL) { + for (i = 0; i < default_fbufs; i++) { + if (pdev->fbuf[i].data != NULL) { + Trace(TRACE_MEMORY, "Freeing frame buffer %d at %p.\n", i, pdev->fbuf[i].data); + vfree(pdev->fbuf[i].data); + pdev->fbuf[i].data = NULL; + } + } + kfree(pdev->fbuf); + pdev->fbuf = NULL; + } + + /* Intermediate decompression buffer & tables */ + if (pdev->decompress_data != NULL) { + Trace(TRACE_MEMORY, "Freeing decompression buffer at %p.\n", pdev->decompress_data); + kfree(pdev->decompress_data); + pdev->decompress_data = NULL; + } + pdev->decompressor = NULL; + + /* Release image buffers */ + if (pdev->image_data != NULL) { + Trace(TRACE_MEMORY, "Freeing image buffer at %p.\n", pdev->image_data); + rvfree(pdev->image_data, default_mbufs * pdev->len_per_image); + } + pdev->image_data = NULL; + + Trace(TRACE_MEMORY, "Leaving free_buffers().\n"); +} + +/* The frame & image buffer mess. + + Yes, this is a mess. Well, it used to be simple, but alas... In this + module, 3 buffers schemes are used to get the data from the USB bus to + the user program. The first scheme involves the ISO buffers (called thus + since they transport ISO data from the USB controller), and not really + interesting. Suffices to say the data from this buffer is quickly + gathered in an interrupt handler (pwc_isoc_handler) and placed into the + frame buffer. + + The frame buffer is the second scheme, and is the central element here. + It collects the data from a single frame from the camera (hence, the + name). Frames are delimited by the USB camera with a short USB packet, + so that's easy to detect. The frame buffers form a list that is filled + by the camera+USB controller and drained by the user process through + either read() or mmap(). + + The image buffer is the third scheme, in which frames are decompressed + and converted into planar format. For mmap() there is more than + one image buffer available. + + The frame buffers provide the image buffering. In case the user process + is a bit slow, this introduces lag and some undesired side-effects. + The problem arises when the frame buffer is full. I used to drop the last + frame, which makes the data in the queue stale very quickly. But dropping + the frame at the head of the queue proved to be a litte bit more difficult. + I tried a circular linked scheme, but this introduced more problems than + it solved. + + Because filling and draining are completely asynchronous processes, this + requires some fiddling with pointers and mutexes. + + Eventually, I came up with a system with 2 lists: an 'empty' frame list + and a 'full' frame list: + * Initially, all frame buffers but one are on the 'empty' list; the one + remaining buffer is our initial fill frame. + * If a frame is needed for filling, we try to take it from the 'empty' + list, unless that list is empty, in which case we take the buffer at + the head of the 'full' list. + * When our fill buffer has been filled, it is appended to the 'full' + list. + * If a frame is needed by read() or mmap(), it is taken from the head of + the 'full' list, handled, and then appended to the 'empty' list. If no + buffer is present on the 'full' list, we wait. + The advantage is that the buffer that is currently being decompressed/ + converted, is on neither list, and thus not in our way (any other scheme + I tried had the problem of old data lingering in the queue). + + Whatever strategy you choose, it always remains a tradeoff: with more + frame buffers the chances of a missed frame are reduced. On the other + hand, on slower machines it introduces lag because the queue will + always be full. + */ + +/** + \brief Find next frame buffer to fill. Take from empty or full list, whichever comes first. + */ +static inline int pwc_next_fill_frame(struct pwc_device *pdev) +{ + int ret; + unsigned long flags; + + ret = 0; + spin_lock_irqsave(&pdev->ptrlock, flags); + if (pdev->fill_frame != NULL) { + /* append to 'full' list */ + if (pdev->full_frames == NULL) { + pdev->full_frames = pdev->fill_frame; + pdev->full_frames_tail = pdev->full_frames; + } + else { + pdev->full_frames_tail->next = pdev->fill_frame; + pdev->full_frames_tail = pdev->fill_frame; + } + } + if (pdev->empty_frames != NULL) { + /* We have empty frames available. That's easy */ + pdev->fill_frame = pdev->empty_frames; + pdev->empty_frames = pdev->empty_frames->next; + } + else { + /* Hmm. Take it from the full list */ +#if PWC_DEBUG + /* sanity check */ + if (pdev->full_frames == NULL) { + Err("Neither empty or full frames available!\n"); + spin_unlock_irqrestore(&pdev->ptrlock, flags); + return -EINVAL; + } +#endif + pdev->fill_frame = pdev->full_frames; + pdev->full_frames = pdev->full_frames->next; + ret = 1; + } + pdev->fill_frame->next = NULL; +#if PWC_DEBUG + Trace(TRACE_SEQUENCE, "Assigning sequence number %d.\n", pdev->sequence); + pdev->fill_frame->sequence = pdev->sequence++; +#endif + spin_unlock_irqrestore(&pdev->ptrlock, flags); + return ret; +} + + +/** + \brief Reset all buffers, pointers and lists, except for the image_used[] buffer. + + If the image_used[] buffer is cleared too, mmap()/VIDIOCSYNC will run into trouble. + */ +static void pwc_reset_buffers(struct pwc_device *pdev) +{ + int i; + unsigned long flags; + + spin_lock_irqsave(&pdev->ptrlock, flags); + pdev->full_frames = NULL; + pdev->full_frames_tail = NULL; + for (i = 0; i < default_fbufs; i++) { + pdev->fbuf[i].filled = 0; + if (i > 0) + pdev->fbuf[i].next = &pdev->fbuf[i - 1]; + else + pdev->fbuf->next = NULL; + } + pdev->empty_frames = &pdev->fbuf[default_fbufs - 1]; + pdev->empty_frames_tail = pdev->fbuf; + pdev->read_frame = NULL; + pdev->fill_frame = pdev->empty_frames; + pdev->empty_frames = pdev->empty_frames->next; + + pdev->image_read_pos = 0; + pdev->fill_image = 0; + spin_unlock_irqrestore(&pdev->ptrlock, flags); +} + + +/** + \brief Do all the handling for getting one frame: get pointer, decompress, advance pointers. + */ +static int pwc_handle_frame(struct pwc_device *pdev) +{ + int ret = 0; + unsigned long flags; + + spin_lock_irqsave(&pdev->ptrlock, flags); + /* First grab our read_frame; this is removed from all lists, so + we can release the lock after this without problems */ + if (pdev->read_frame != NULL) { + /* This can't theoretically happen */ + Err("Huh? Read frame still in use?\n"); + } + else { + if (pdev->full_frames == NULL) { + Err("Woops. No frames ready.\n"); + } + else { + pdev->read_frame = pdev->full_frames; + pdev->full_frames = pdev->full_frames->next; + pdev->read_frame->next = NULL; + } + + if (pdev->read_frame != NULL) { +#if PWC_DEBUG + Trace(TRACE_SEQUENCE, "Decompressing frame %d\n", pdev->read_frame->sequence); +#endif + /* Decompression is a lenghty process, so it's outside of the lock. + This gives the isoc_handler the opportunity to fill more frames + in the mean time. + */ + spin_unlock_irqrestore(&pdev->ptrlock, flags); + ret = pwc_decompress(pdev); + spin_lock_irqsave(&pdev->ptrlock, flags); + + /* We're done with read_buffer, tack it to the end of the empty buffer list */ + if (pdev->empty_frames == NULL) { + pdev->empty_frames = pdev->read_frame; + pdev->empty_frames_tail = pdev->empty_frames; + } + else { + pdev->empty_frames_tail->next = pdev->read_frame; + pdev->empty_frames_tail = pdev->read_frame; + } + pdev->read_frame = NULL; + } + } + spin_unlock_irqrestore(&pdev->ptrlock, flags); + return ret; +} + +/** + \brief Advance pointers of image buffer (after each user request) +*/ +static inline void pwc_next_image(struct pwc_device *pdev) +{ + pdev->image_used[pdev->fill_image] = 0; + pdev->fill_image = (pdev->fill_image + 1) % default_mbufs; +} + + +/* This gets called for the Isochronous pipe (video). This is done in + * interrupt time, so it has to be fast, not crash, and not stall. Neat. + */ +static void pwc_isoc_handler(struct urb *urb, struct pt_regs *regs) +{ + struct pwc_device *pdev; + int i, fst, flen; + int awake; + struct pwc_frame_buf *fbuf; + unsigned char *fillptr = 0, *iso_buf = 0; + + awake = 0; + pdev = (struct pwc_device *)urb->context; + if (pdev == NULL) { + Err("isoc_handler() called with NULL device?!\n"); + return; + } +#ifdef PWC_MAGIC + if (pdev->magic != PWC_MAGIC) { + Err("isoc_handler() called with bad magic!\n"); + return; + } +#endif + if (urb->status == -ENOENT || urb->status == -ECONNRESET) { + Trace(TRACE_OPEN, "pwc_isoc_handler(): URB (%p) unlinked %ssynchronuously.\n", urb, urb->status == -ENOENT ? "" : "a"); + return; + } + if (urb->status != -EINPROGRESS && urb->status != 0) { + const char *errmsg; + + errmsg = "Unknown"; + switch(urb->status) { + case -ENOSR: errmsg = "Buffer error (overrun)"; break; + case -EPIPE: errmsg = "Stalled (device not responding)"; break; + case -EOVERFLOW: errmsg = "Babble (bad cable?)"; break; + case -EPROTO: errmsg = "Bit-stuff error (bad cable?)"; break; + case -EILSEQ: errmsg = "CRC/Timeout (could be anything)"; break; + case -ETIMEDOUT: errmsg = "NAK (device does not respond)"; break; + } + Trace(TRACE_FLOW, "pwc_isoc_handler() called with status %d [%s].\n", urb->status, errmsg); + /* Give up after a number of contiguous errors on the USB bus. + Appearantly something is wrong so we simulate an unplug event. + */ + if (++pdev->visoc_errors > MAX_ISOC_ERRORS) + { + Info("Too many ISOC errors, bailing out.\n"); + pdev->error_status = EIO; + awake = 1; + wake_up_interruptible(&pdev->frameq); + } + goto handler_end; // ugly, but practical + } + + fbuf = pdev->fill_frame; + if (fbuf == NULL) { + Err("pwc_isoc_handler without valid fill frame.\n"); + awake = 1; + goto handler_end; + } + else { + fillptr = fbuf->data + fbuf->filled; + } + + /* Reset ISOC error counter. We did get here, after all. */ + pdev->visoc_errors = 0; + + /* vsync: 0 = don't copy data + 1 = sync-hunt + 2 = synched + */ + /* Compact data */ + for (i = 0; i < urb->number_of_packets; i++) { + fst = urb->iso_frame_desc[i].status; + flen = urb->iso_frame_desc[i].actual_length; + iso_buf = urb->transfer_buffer + urb->iso_frame_desc[i].offset; + if (fst == 0) { + if (flen > 0) { /* if valid data... */ + if (pdev->vsync > 0) { /* ...and we are not sync-hunting... */ + pdev->vsync = 2; + + /* ...copy data to frame buffer, if possible */ + if (flen + fbuf->filled > pdev->frame_total_size) { + Trace(TRACE_FLOW, "Frame buffer overflow (flen = %d, frame_total_size = %d).\n", flen, pdev->frame_total_size); + pdev->vsync = 0; /* Hmm, let's wait for an EOF (end-of-frame) */ + pdev->vframes_error++; + } + else { + memmove(fillptr, iso_buf, flen); + fillptr += flen; + } + } + fbuf->filled += flen; + } /* ..flen > 0 */ + + if (flen < pdev->vlast_packet_size) { + /* Shorter packet... We probably have the end of an image-frame; + wake up read() process and let select()/poll() do something. + Decompression is done in user time over there. + */ + if (pdev->vsync == 2) { + /* The ToUCam Fun CMOS sensor causes the firmware to send 2 or 3 bogus + frames on the USB wire after an exposure change. This conditition is + however detected in the cam and a bit is set in the header. + */ + if (pdev->type == 730) { + unsigned char *ptr = (unsigned char *)fbuf->data; + + if (ptr[1] == 1 && ptr[0] & 0x10) { +#if PWC_DEBUG + Debug("Hyundai CMOS sensor bug. Dropping frame %d.\n", fbuf->sequence); +#endif + pdev->drop_frames += 2; + pdev->vframes_error++; + } + if ((ptr[0] ^ pdev->vmirror) & 0x01) { + if (ptr[0] & 0x01) + Info("Snapshot button pressed.\n"); + else + Info("Snapshot button released.\n"); + } + if ((ptr[0] ^ pdev->vmirror) & 0x02) { + if (ptr[0] & 0x02) + Info("Image is mirrored.\n"); + else + Info("Image is normal.\n"); + } + pdev->vmirror = ptr[0] & 0x03; + /* Sometimes the trailer of the 730 is still sent as a 4 byte packet + after a short frame; this condition is filtered out specifically. A 4 byte + frame doesn't make sense anyway. + So we get either this sequence: + drop_bit set -> 4 byte frame -> short frame -> good frame + Or this one: + drop_bit set -> short frame -> good frame + So we drop either 3 or 2 frames in all! + */ + if (fbuf->filled == 4) + pdev->drop_frames++; + } + + /* In case we were instructed to drop the frame, do so silently. + The buffer pointers are not updated either (but the counters are reset below). + */ + if (pdev->drop_frames > 0) + pdev->drop_frames--; + else { + /* Check for underflow first */ + if (fbuf->filled < pdev->frame_total_size) { + Trace(TRACE_FLOW, "Frame buffer underflow (%d bytes); discarded.\n", fbuf->filled); + pdev->vframes_error++; + } + else { + /* Send only once per EOF */ + awake = 1; /* delay wake_ups */ + + /* Find our next frame to fill. This will always succeed, since we + * nick a frame from either empty or full list, but if we had to + * take it from the full list, it means a frame got dropped. + */ + if (pwc_next_fill_frame(pdev)) { + pdev->vframes_dumped++; + if ((pdev->vframe_count > FRAME_LOWMARK) && (pwc_trace & TRACE_FLOW)) { + if (pdev->vframes_dumped < 20) + Trace(TRACE_FLOW, "Dumping frame %d.\n", pdev->vframe_count); + if (pdev->vframes_dumped == 20) + Trace(TRACE_FLOW, "Dumping frame %d (last message).\n", pdev->vframe_count); + } + } + fbuf = pdev->fill_frame; + } + } /* !drop_frames */ + pdev->vframe_count++; + } + fbuf->filled = 0; + fillptr = fbuf->data; + pdev->vsync = 1; + } /* .. flen < last_packet_size */ + pdev->vlast_packet_size = flen; + } /* ..status == 0 */ +#if PWC_DEBUG + /* This is normally not interesting to the user, unless you are really debugging something */ + else { + static int iso_error = 0; + iso_error++; + if (iso_error < 20) + Trace(TRACE_FLOW, "Iso frame %d of USB has error %d\n", i, fst); + } +#endif + } + +handler_end: + if (awake) + wake_up_interruptible(&pdev->frameq); + + urb->dev = pdev->udev; + i = usb_submit_urb(urb, GFP_ATOMIC); + if (i != 0) + Err("Error (%d) re-submitting urb in pwc_isoc_handler.\n", i); +} + + +static int pwc_isoc_init(struct pwc_device *pdev) +{ + struct usb_device *udev; + struct urb *urb; + int i, j, ret; + + struct usb_interface *intf; + struct usb_host_interface *idesc = NULL; + + if (pdev == NULL) + return -EFAULT; + if (pdev->iso_init) + return 0; + pdev->vsync = 0; + udev = pdev->udev; + + /* Get the current alternate interface, adjust packet size */ + if (!udev->actconfig) + return -EFAULT; +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,5) + idesc = &udev->actconfig->interface[0]->altsetting[pdev->valternate]; +#else + intf = usb_ifnum_to_if(udev, 0); + if (intf) + idesc = usb_altnum_to_altsetting(intf, pdev->valternate); +#endif + + if (!idesc) + return -EFAULT; + + /* Search video endpoint */ + pdev->vmax_packet_size = -1; + for (i = 0; i < idesc->desc.bNumEndpoints; i++) + if ((idesc->endpoint[i].desc.bEndpointAddress & 0xF) == pdev->vendpoint) { + pdev->vmax_packet_size = idesc->endpoint[i].desc.wMaxPacketSize; + break; + } + + if (pdev->vmax_packet_size < 0 || pdev->vmax_packet_size > ISO_MAX_FRAME_SIZE) { + Err("Failed to find packet size for video endpoint in current alternate setting.\n"); + return -ENFILE; /* Odd error, that should be noticable */ + } + + /* Set alternate interface */ + ret = 0; + Trace(TRACE_OPEN, "Setting alternate interface %d\n", pdev->valternate); + ret = usb_set_interface(pdev->udev, 0, pdev->valternate); + if (ret < 0) + return ret; + + for (i = 0; i < MAX_ISO_BUFS; i++) { + urb = usb_alloc_urb(ISO_FRAMES_PER_DESC, GFP_KERNEL); + if (urb == NULL) { + Err("Failed to allocate urb %d\n", i); + ret = -ENOMEM; + break; + } + pdev->sbuf[i].urb = urb; + Trace(TRACE_MEMORY, "Allocated URB at 0x%p\n", urb); + } + if (ret) { + /* De-allocate in reverse order */ + while (i >= 0) { + if (pdev->sbuf[i].urb != NULL) + usb_free_urb(pdev->sbuf[i].urb); + pdev->sbuf[i].urb = NULL; + i--; + } + return ret; + } + + /* init URB structure */ + for (i = 0; i < MAX_ISO_BUFS; i++) { + urb = pdev->sbuf[i].urb; + + urb->interval = 1; // devik + urb->dev = udev; + urb->pipe = usb_rcvisocpipe(udev, pdev->vendpoint); + urb->transfer_flags = URB_ISO_ASAP; + urb->transfer_buffer = pdev->sbuf[i].data; + urb->transfer_buffer_length = ISO_BUFFER_SIZE; + urb->complete = pwc_isoc_handler; + urb->context = pdev; + urb->start_frame = 0; + urb->number_of_packets = ISO_FRAMES_PER_DESC; + for (j = 0; j < ISO_FRAMES_PER_DESC; j++) { + urb->iso_frame_desc[j].offset = j * ISO_MAX_FRAME_SIZE; + urb->iso_frame_desc[j].length = pdev->vmax_packet_size; + } + } + + /* link */ + for (i = 0; i < MAX_ISO_BUFS; i++) { + ret = usb_submit_urb(pdev->sbuf[i].urb, GFP_KERNEL); + if (ret) + Err("isoc_init() submit_urb %d failed with error %d\n", i, ret); + else + Trace(TRACE_MEMORY, "URB 0x%p submitted.\n", pdev->sbuf[i].urb); + } + + /* All is done... */ + pdev->iso_init = 1; + Trace(TRACE_OPEN, "<< pwc_isoc_init()\n"); + return 0; +} + +static void pwc_isoc_cleanup(struct pwc_device *pdev) +{ + int i; + + Trace(TRACE_OPEN, ">> pwc_isoc_cleanup()\n"); + if (pdev == NULL) + return; + + /* Unlinking ISOC buffers one by one */ + for (i = 0; i < MAX_ISO_BUFS; i++) { + struct urb *urb; + + urb = pdev->sbuf[i].urb; + if (urb != 0) { + if (pdev->iso_init) { + Trace(TRACE_MEMORY, "Unlinking URB %p\n", urb); + usb_unlink_urb(urb); + } + Trace(TRACE_MEMORY, "Freeing URB\n"); + usb_free_urb(urb); + pdev->sbuf[i].urb = NULL; + } + } + + /* Stop camera, but only if we are sure the camera is still there (unplug + is signalled by EPIPE) + */ + if (pdev->error_status && pdev->error_status != EPIPE) { + Trace(TRACE_OPEN, "Setting alternate interface 0.\n"); + usb_set_interface(pdev->udev, 0, 0); + } + + pdev->iso_init = 0; + Trace(TRACE_OPEN, "<< pwc_isoc_cleanup()\n"); +} + +int pwc_try_video_mode(struct pwc_device *pdev, int width, int height, int new_fps, int new_compression, int new_snapshot) +{ + int ret, start; + + /* Stop isoc stuff */ + pwc_isoc_cleanup(pdev); + /* Reset parameters */ + pwc_reset_buffers(pdev); + /* Try to set video mode... */ + start = ret = pwc_set_video_mode(pdev, width, height, new_fps, new_compression, new_snapshot); + if (ret) { + Trace(TRACE_FLOW, "pwc_set_video_mode attempt 1 failed.\n"); + /* That failed... restore old mode (we know that worked) */ + start = pwc_set_video_mode(pdev, pdev->view.x, pdev->view.y, pdev->vframes, pdev->vcompression, pdev->vsnapshot); + if (start) { + Trace(TRACE_FLOW, "pwc_set_video_mode attempt 2 failed.\n"); + } + } + if (start == 0) + { + if (pwc_isoc_init(pdev) < 0) + { + Info("Failed to restart ISOC transfers in pwc_try_video_mode.\n"); + ret = -EAGAIN; /* let's try again, who knows if it works a second time */ + } + } + pdev->drop_frames++; /* try to avoid garbage during switch */ + return ret; /* Return original error code */ +} + + +/***************************************************************************/ +/* Video4Linux functions */ + +static int pwc_video_open(struct inode *inode, struct file *file) +{ + int i; + struct video_device *vdev = video_devdata(file); + struct pwc_device *pdev; + + Trace(TRACE_OPEN, ">> video_open called(vdev = 0x%p).\n", vdev); + + pdev = (struct pwc_device *)vdev->priv; + if (pdev == NULL) + BUG(); + if (pdev->vopen) + return -EBUSY; + + down(&pdev->modlock); + if (!pdev->usb_init) { + Trace(TRACE_OPEN, "Doing first time initialization.\n"); + pdev->usb_init = 1; + + if (pwc_trace & TRACE_OPEN) + { + /* Query sensor type */ + const char *sensor_type = NULL; + int ret; + + ret = pwc_get_cmos_sensor(pdev, &i); + if (ret >= 0) + { + switch(i) { + case 0x00: sensor_type = "Hyundai CMOS sensor"; break; + case 0x20: sensor_type = "Sony CCD sensor + TDA8787"; break; + case 0x2E: sensor_type = "Sony CCD sensor + Exas 98L59"; break; + case 0x2F: sensor_type = "Sony CCD sensor + ADI 9804"; break; + case 0x30: sensor_type = "Sharp CCD sensor + TDA8787"; break; + case 0x3E: sensor_type = "Sharp CCD sensor + Exas 98L59"; break; + case 0x3F: sensor_type = "Sharp CCD sensor + ADI 9804"; break; + case 0x40: sensor_type = "UPA 1021 sensor"; break; + case 0x100: sensor_type = "VGA sensor"; break; + case 0x101: sensor_type = "PAL MR sensor"; break; + default: sensor_type = "unknown type of sensor"; break; + } + } + if (sensor_type != NULL) + Info("This %s camera is equipped with a %s (%d).\n", pdev->vdev->name, sensor_type, i); + } + } + + /* Turn on camera */ + if (power_save) { + i = pwc_camera_power(pdev, 1); + if (i < 0) + Info("Failed to restore power to the camera! (%d)\n", i); + } + /* Set LED on/off time */ + if (pwc_set_leds(pdev, led_on, led_off) < 0) + Info("Failed to set LED on/off time.\n"); + + pwc_construct(pdev); /* set min/max sizes correct */ + + /* So far, so good. Allocate memory. */ + i = pwc_allocate_buffers(pdev); + if (i < 0) { + Trace(TRACE_OPEN, "Failed to allocate buffer memory.\n"); + up(&pdev->modlock); + return i; + } + + /* Reset buffers & parameters */ + pwc_reset_buffers(pdev); + for (i = 0; i < default_mbufs; i++) + pdev->image_used[i] = 0; + pdev->vframe_count = 0; + pdev->vframes_dumped = 0; + pdev->vframes_error = 0; + pdev->visoc_errors = 0; + pdev->error_status = 0; +#if PWC_DEBUG + pdev->sequence = 0; +#endif + pwc_construct(pdev); /* set min/max sizes correct */ + + /* Set some defaults */ + pdev->vsnapshot = 0; + + /* Start iso pipe for video; first try the last used video size + (or the default one); if that fails try QCIF/10 or QSIF/10; + it that fails too, give up. + */ + i = pwc_set_video_mode(pdev, pwc_image_sizes[pdev->vsize].x, pwc_image_sizes[pdev->vsize].y, pdev->vframes, pdev->vcompression, 0); + if (i) { + Trace(TRACE_OPEN, "First attempt at set_video_mode failed.\n"); + if (pdev->type == 730 || pdev->type == 740 || pdev->type == 750) + i = pwc_set_video_mode(pdev, pwc_image_sizes[PSZ_QSIF].x, pwc_image_sizes[PSZ_QSIF].y, 10, pdev->vcompression, 0); + else + i = pwc_set_video_mode(pdev, pwc_image_sizes[PSZ_QCIF].x, pwc_image_sizes[PSZ_QCIF].y, 10, pdev->vcompression, 0); + } + if (i) { + Trace(TRACE_OPEN, "Second attempt at set_video_mode failed.\n"); + up(&pdev->modlock); + return i; + } + + i = pwc_isoc_init(pdev); + if (i) { + Trace(TRACE_OPEN, "Failed to init ISOC stuff = %d.\n", i); + up(&pdev->modlock); + return i; + } + + pdev->vopen++; + file->private_data = vdev; + up(&pdev->modlock); + Trace(TRACE_OPEN, "<< video_open() returns 0.\n"); + return 0; +} + +/* Note that all cleanup is done in the reverse order as in _open */ +static int pwc_video_close(struct inode *inode, struct file *file) +{ + struct video_device *vdev = file->private_data; + struct pwc_device *pdev; + int i; + + Trace(TRACE_OPEN, ">> video_close called(vdev = 0x%p).\n", vdev); + + pdev = (struct pwc_device *)vdev->priv; + if (pdev->vopen == 0) + Info("video_close() called on closed device?\n"); + + /* Dump statistics, but only if a reasonable amount of frames were + processed (to prevent endless log-entries in case of snap-shot + programs) + */ + if (pdev->vframe_count > 20) + Info("Closing video device: %d frames received, dumped %d frames, %d frames with errors.\n", pdev->vframe_count, pdev->vframes_dumped, pdev->vframes_error); + + switch (pdev->type) + { + case 675: + case 680: + case 690: + case 720: + case 730: + case 740: + case 750: + pwc_dec23_exit(); /* Timon & Kiara */ + break; + case 645: + case 646: + pwc_dec1_exit(); + break; + } + + pwc_isoc_cleanup(pdev); + pwc_free_buffers(pdev); + + /* Turn off LEDS and power down camera, but only when not unplugged */ + if (pdev->error_status != EPIPE) { + /* Turn LEDs off */ + if (pwc_set_leds(pdev, 0, 0) < 0) + Info("Failed to set LED on/off time.\n"); + if (power_save) { + i = pwc_camera_power(pdev, 0); + if (i < 0) + Err("Failed to power down camera (%d)\n", i); + } + } + pdev->vopen = 0; + Trace(TRACE_OPEN, "<< video_close()\n"); + return 0; +} + +/* + * FIXME: what about two parallel reads ???? + * ANSWER: Not supported. You can't open the device more than once, + despite what the V4L1 interface says. First, I don't see + the need, second there's no mechanism of alerting the + 2nd/3rd/... process of events like changing image size. + And I don't see the point of blocking that for the + 2nd/3rd/... process. + In multi-threaded environments reading parallel from any + device is tricky anyhow. + */ + +static ssize_t pwc_video_read(struct file *file, char *buf, + size_t count, loff_t *ppos) +{ + struct video_device *vdev = file->private_data; + struct pwc_device *pdev; + int noblock = file->f_flags & O_NONBLOCK; + DECLARE_WAITQUEUE(wait, current); + int bytes_to_read; + + Trace(TRACE_READ, "video_read(0x%p, %p, %d) called.\n", vdev, buf, count); + if (vdev == NULL) + return -EFAULT; + pdev = vdev->priv; + if (pdev == NULL) + return -EFAULT; + if (pdev->error_status) + return -pdev->error_status; /* Something happened, report what. */ + + /* In case we're doing partial reads, we don't have to wait for a frame */ + if (pdev->image_read_pos == 0) { + /* Do wait queueing according to the (doc)book */ + add_wait_queue(&pdev->frameq, &wait); + while (pdev->full_frames == NULL) { + /* Check for unplugged/etc. here */ + if (pdev->error_status) { + remove_wait_queue(&pdev->frameq, &wait); + set_current_state(TASK_RUNNING); + return -pdev->error_status ; + } + if (noblock) { + remove_wait_queue(&pdev->frameq, &wait); + set_current_state(TASK_RUNNING); + return -EWOULDBLOCK; + } + if (signal_pending(current)) { + remove_wait_queue(&pdev->frameq, &wait); + set_current_state(TASK_RUNNING); + return -ERESTARTSYS; + } + schedule(); + set_current_state(TASK_INTERRUPTIBLE); + } + remove_wait_queue(&pdev->frameq, &wait); + set_current_state(TASK_RUNNING); + + /* Decompress and release frame */ + if (pwc_handle_frame(pdev)) + return -EFAULT; + } + + Trace(TRACE_READ, "Copying data to user space.\n"); + if (pdev->vpalette == VIDEO_PALETTE_RAW) + bytes_to_read = pdev->frame_size; + else + bytes_to_read = pdev->view.size; + + /* copy bytes to user space; we allow for partial reads */ + if (count + pdev->image_read_pos > bytes_to_read) + count = bytes_to_read - pdev->image_read_pos; + if (copy_to_user(buf, pdev->image_ptr[pdev->fill_image] + pdev->image_read_pos, count)) + return -EFAULT; + pdev->image_read_pos += count; + if (pdev->image_read_pos >= bytes_to_read) { /* All data has been read */ + pdev->image_read_pos = 0; + pwc_next_image(pdev); + } + return count; +} + +static unsigned int pwc_video_poll(struct file *file, poll_table *wait) +{ + struct video_device *vdev = file->private_data; + struct pwc_device *pdev; + + if (vdev == NULL) + return -EFAULT; + pdev = vdev->priv; + if (pdev == NULL) + return -EFAULT; + + poll_wait(file, &pdev->frameq, wait); + if (pdev->error_status) + return POLLERR; + if (pdev->full_frames != NULL) /* we have frames waiting */ + return (POLLIN | POLLRDNORM); + + return 0; +} + +static int pwc_video_do_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, void *arg) +{ + struct video_device *vdev = file->private_data; + struct pwc_device *pdev; + DECLARE_WAITQUEUE(wait, current); + + if (vdev == NULL) + return -EFAULT; + pdev = vdev->priv; + if (pdev == NULL) + return -EFAULT; + + switch (cmd) { + /* Query cabapilities */ + case VIDIOCGCAP: + { + struct video_capability *caps = arg; + + strcpy(caps->name, vdev->name); + caps->type = VID_TYPE_CAPTURE; + caps->channels = 1; + caps->audios = 1; + caps->minwidth = pdev->view_min.x; + caps->minheight = pdev->view_min.y; + caps->maxwidth = pdev->view_max.x; + caps->maxheight = pdev->view_max.y; + break; + } + + /* Channel functions (simulate 1 channel) */ + case VIDIOCGCHAN: + { + struct video_channel *v = arg; + + if (v->channel != 0) + return -EINVAL; + v->flags = 0; + v->tuners = 0; + v->type = VIDEO_TYPE_CAMERA; + strcpy(v->name, "Webcam"); + return 0; + } + + case VIDIOCSCHAN: + { + /* The spec says the argument is an integer, but + the bttv driver uses a video_channel arg, which + makes sense becasue it also has the norm flag. + */ + struct video_channel *v = arg; + if (v->channel != 0) + return -EINVAL; + return 0; + } + + + /* Picture functions; contrast etc. */ + case VIDIOCGPICT: + { + struct video_picture *p = arg; + int val; + + val = pwc_get_brightness(pdev); + if (val >= 0) + p->brightness = val; + else + p->brightness = 0xffff; + val = pwc_get_contrast(pdev); + if (val >= 0) + p->contrast = val; + else + p->contrast = 0xffff; + /* Gamma, Whiteness, what's the difference? :) */ + val = pwc_get_gamma(pdev); + if (val >= 0) + p->whiteness = val; + else + p->whiteness = 0xffff; + val = pwc_get_saturation(pdev); + if (val >= 0) + p->colour = val; + else + p->colour = 0xffff; + p->depth = 24; + p->palette = pdev->vpalette; + p->hue = 0xFFFF; /* N/A */ + break; + } + + case VIDIOCSPICT: + { + struct video_picture *p = arg; + /* + * FIXME: Suppose we are mid read + ANSWER: No problem: the firmware of the camera + can handle brightness/contrast/etc + changes at _any_ time, and the palette + is used exactly once in the uncompress + routine. + */ + pwc_set_brightness(pdev, p->brightness); + pwc_set_contrast(pdev, p->contrast); + pwc_set_gamma(pdev, p->whiteness); + pwc_set_saturation(pdev, p->colour); + if (p->palette && p->palette != pdev->vpalette) { + switch (p->palette) { + case VIDEO_PALETTE_YUV420P: + case VIDEO_PALETTE_RAW: + pdev->vpalette = p->palette; + return pwc_try_video_mode(pdev, pdev->image.x, pdev->image.y, pdev->vframes, pdev->vcompression, pdev->vsnapshot); + break; + default: + return -EINVAL; + break; + } + } + break; + } + + /* Window/size parameters */ + case VIDIOCGWIN: + { + struct video_window *vw = arg; + + vw->x = 0; + vw->y = 0; + vw->width = pdev->view.x; + vw->height = pdev->view.y; + vw->chromakey = 0; + vw->flags = (pdev->vframes << PWC_FPS_SHIFT) | + (pdev->vsnapshot ? PWC_FPS_SNAPSHOT : 0); + break; + } + + case VIDIOCSWIN: + { + struct video_window *vw = arg; + int fps, snapshot, ret; + + fps = (vw->flags & PWC_FPS_FRMASK) >> PWC_FPS_SHIFT; + snapshot = vw->flags & PWC_FPS_SNAPSHOT; + if (fps == 0) + fps = pdev->vframes; + if (pdev->view.x == vw->width && pdev->view.y && fps == pdev->vframes && snapshot == pdev->vsnapshot) + return 0; + ret = pwc_try_video_mode(pdev, vw->width, vw->height, fps, pdev->vcompression, snapshot); + if (ret) + return ret; + break; + } + + /* We don't have overlay support (yet) */ + case VIDIOCGFBUF: + { + struct video_buffer *vb = arg; + + memset(vb,0,sizeof(*vb)); + break; + } + + /* mmap() functions */ + case VIDIOCGMBUF: + { + /* Tell the user program how much memory is needed for a mmap() */ + struct video_mbuf *vm = arg; + int i; + + memset(vm, 0, sizeof(*vm)); + vm->size = default_mbufs * pdev->len_per_image; + vm->frames = default_mbufs; /* double buffering should be enough for most applications */ + for (i = 0; i < default_mbufs; i++) + vm->offsets[i] = i * pdev->len_per_image; + break; + } + + case VIDIOCMCAPTURE: + { + /* Start capture into a given image buffer (called 'frame' in video_mmap structure) */ + struct video_mmap *vm = arg; + + Trace(TRACE_READ, "VIDIOCMCAPTURE: %dx%d, frame %d, format %d\n", vm->width, vm->height, vm->frame, vm->format); + if (vm->frame < 0 || vm->frame >= default_mbufs) + return -EINVAL; + + /* xawtv is nasty. It probes the available palettes + by setting a very small image size and trying + various palettes... The driver doesn't support + such small images, so I'm working around it. + */ + if (vm->format) + { + switch (vm->format) + { + case VIDEO_PALETTE_YUV420P: + case VIDEO_PALETTE_RAW: + break; + default: + return -EINVAL; + break; + } + } + + if ((vm->width != pdev->view.x || vm->height != pdev->view.y) && + (vm->width >= pdev->view_min.x && vm->height >= pdev->view_min.y)) { + int ret; + + Trace(TRACE_OPEN, "VIDIOCMCAPTURE: changing size to please xawtv :-(.\n"); + ret = pwc_try_video_mode(pdev, vm->width, vm->height, pdev->vframes, pdev->vcompression, pdev->vsnapshot); + if (ret) + return ret; + } /* ... size mismatch */ + + /* FIXME: should we lock here? */ + if (pdev->image_used[vm->frame]) + return -EBUSY; /* buffer wasn't available. Bummer */ + pdev->image_used[vm->frame] = 1; + + /* Okay, we're done here. In the SYNC call we wait until a + frame comes available, then expand image into the given + buffer. + In contrast to the CPiA cam the Philips cams deliver a + constant stream, almost like a grabber card. Also, + we have separate buffers for the rawdata and the image, + meaning we can nearly always expand into the requested buffer. + */ + Trace(TRACE_READ, "VIDIOCMCAPTURE done.\n"); + break; + } + + case VIDIOCSYNC: + { + /* The doc says: "Whenever a buffer is used it should + call VIDIOCSYNC to free this frame up and continue." + + The only odd thing about this whole procedure is + that MCAPTURE flags the buffer as "in use", and + SYNC immediately unmarks it, while it isn't + after SYNC that you know that the buffer actually + got filled! So you better not start a CAPTURE in + the same frame immediately (use double buffering). + This is not a problem for this cam, since it has + extra intermediate buffers, but a hardware + grabber card will then overwrite the buffer + you're working on. + */ + int *mbuf = arg; + int ret; + + Trace(TRACE_READ, "VIDIOCSYNC called (%d).\n", *mbuf); + + /* bounds check */ + if (*mbuf < 0 || *mbuf >= default_mbufs) + return -EINVAL; + /* check if this buffer was requested anyway */ + if (pdev->image_used[*mbuf] == 0) + return -EINVAL; + + /* Add ourselves to the frame wait-queue. + + FIXME: needs auditing for safety. + QUESTION: In what respect? I think that using the + frameq is safe now. + */ + add_wait_queue(&pdev->frameq, &wait); + while (pdev->full_frames == NULL) { + if (pdev->error_status) { + remove_wait_queue(&pdev->frameq, &wait); + set_current_state(TASK_RUNNING); + return -pdev->error_status; + } + + if (signal_pending(current)) { + remove_wait_queue(&pdev->frameq, &wait); + set_current_state(TASK_RUNNING); + return -ERESTARTSYS; + } + schedule(); + set_current_state(TASK_INTERRUPTIBLE); + } + remove_wait_queue(&pdev->frameq, &wait); + set_current_state(TASK_RUNNING); + + /* The frame is ready. Expand in the image buffer + requested by the user. I don't care if you + mmap() 5 buffers and request data in this order: + buffer 4 2 3 0 1 2 3 0 4 3 1 . . . + Grabber hardware may not be so forgiving. + */ + Trace(TRACE_READ, "VIDIOCSYNC: frame ready.\n"); + pdev->fill_image = *mbuf; /* tell in which buffer we want the image to be expanded */ + /* Decompress, etc */ + ret = pwc_handle_frame(pdev); + pdev->image_used[*mbuf] = 0; + if (ret) + return -EFAULT; + break; + } + + case VIDIOCGAUDIO: + { + struct video_audio *v = arg; + + strcpy(v->name, "Microphone"); + v->audio = -1; /* unknown audio minor */ + v->flags = 0; + v->mode = VIDEO_SOUND_MONO; + v->volume = 0; + v->bass = 0; + v->treble = 0; + v->balance = 0x8000; + v->step = 1; + break; + } + + case VIDIOCSAUDIO: + { + /* Dummy: nothing can be set */ + break; + } + + case VIDIOCGUNIT: + { + struct video_unit *vu = arg; + + vu->video = pdev->vdev->minor & 0x3F; + vu->audio = -1; /* not known yet */ + vu->vbi = -1; + vu->radio = -1; + vu->teletext = -1; + break; + } + default: + return pwc_ioctl(pdev, cmd, arg); + } /* ..switch */ + return 0; +} + +static int pwc_video_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg) +{ + return video_usercopy(inode, file, cmd, arg, pwc_video_do_ioctl); +} + + +static int pwc_video_mmap(struct file *file, struct vm_area_struct *vma) +{ + struct video_device *vdev = file->private_data; + struct pwc_device *pdev; + unsigned long start = vma->vm_start; + unsigned long size = vma->vm_end-vma->vm_start; + unsigned long page, pos; + + Trace(TRACE_MEMORY, "mmap(0x%p, 0x%lx, %lu) called.\n", vdev, start, size); + pdev = vdev->priv; + + vma->vm_flags |= VM_IO; + + pos = (unsigned long)pdev->image_data; + while (size > 0) { + page = kvirt_to_pa(pos); + if (remap_page_range(vma, start, page, PAGE_SIZE, PAGE_SHARED)) + return -EAGAIN; + + start += PAGE_SIZE; + pos += PAGE_SIZE; + if (size > PAGE_SIZE) + size -= PAGE_SIZE; + else + size = 0; + } + + return 0; +} + +/***************************************************************************/ +/* USB functions */ + +/* This function gets called when a new device is plugged in or the usb core + * is loaded. + */ + +static int usb_pwc_probe(struct usb_interface *intf, const struct usb_device_id *id) +{ + struct usb_device *udev = interface_to_usbdev(intf); + struct pwc_device *pdev = NULL; + int vendor_id, product_id, type_id; + int i, hint; + int features = 0; + int video_nr = -1; /* default: use next available device */ + char serial_number[30], *name; + + /* Check if we can handle this device */ + Trace(TRACE_PROBE, "probe() called [%04X %04X], if %d\n", + udev->descriptor.idVendor, udev->descriptor.idProduct, + intf->altsetting->desc.bInterfaceNumber); + + /* the interfaces are probed one by one. We are only interested in the + video interface (0) now. + Interface 1 is the Audio Control, and interface 2 Audio itself. + */ + if (intf->altsetting->desc.bInterfaceNumber > 0) + return -ENODEV; + + vendor_id = udev->descriptor.idVendor; + product_id = udev->descriptor.idProduct; + + if (vendor_id == 0x0471) { + switch (product_id) { + case 0x0302: + Info("Philips PCA645VC USB webcam detected.\n"); + name = "Philips 645 webcam"; + type_id = 645; + break; + case 0x0303: + Info("Philips PCA646VC USB webcam detected.\n"); + name = "Philips 646 webcam"; + type_id = 646; + break; + case 0x0304: + Info("Askey VC010 type 2 USB webcam detected.\n"); + name = "Askey VC010 webcam"; + type_id = 646; + break; + case 0x0307: + Info("Philips PCVC675K (Vesta) USB webcam detected.\n"); + name = "Philips 675 webcam"; + type_id = 675; + break; + case 0x0308: + Info("Philips PCVC680K (Vesta Pro) USB webcam detected.\n"); + name = "Philips 680 webcam"; + type_id = 680; + break; + case 0x030C: + Info("Philips PCVC690K (Vesta Pro Scan) USB webcam detected.\n"); + name = "Philips 690 webcam"; + type_id = 690; + break; + case 0x0310: + Info("Philips PCVC730K (ToUCam Fun)/PCVC830 (ToUCam II) USB webcam detected.\n"); + name = "Philips 730 webcam"; + type_id = 730; + break; + case 0x0311: + Info("Philips PCVC740K (ToUCam Pro)/PCVC840 (ToUCam II) USB webcam detected.\n"); + name = "Philips 740 webcam"; + type_id = 740; + break; + case 0x0312: + Info("Philips PCVC750K (ToUCam Pro Scan) USB webcam detected.\n"); + name = "Philips 750 webcam"; + type_id = 750; + break; + case 0x0313: + Info("Philips PCVC720K/40 (ToUCam XS) USB webcam detected.\n"); + name = "Philips 720K/40 webcam"; + type_id = 720; + break; + default: + return -ENODEV; + break; + } + } + else if (vendor_id == 0x069A) { + switch(product_id) { + case 0x0001: + Info("Askey VC010 type 1 USB webcam detected.\n"); + name = "Askey VC010 webcam"; + type_id = 645; + break; + default: + return -ENODEV; + break; + } + } + else if (vendor_id == 0x046d) { + switch(product_id) { + case 0x08b0: + Info("Logitech QuickCam Pro 3000 USB webcam detected.\n"); + name = "Logitech QuickCam Pro 3000"; + type_id = 740; /* CCD sensor */ + break; + case 0x08b1: + Info("Logitech QuickCam Notebook Pro USB webcam detected.\n"); + name = "Logitech QuickCam Notebook Pro"; + type_id = 740; /* CCD sensor */ + break; + case 0x08b2: + Info("Logitech QuickCam 4000 Pro USB webcam detected.\n"); + name = "Logitech QuickCam Pro 4000"; + type_id = 740; /* CCD sensor */ + break; + case 0x08b3: + Info("Logitech QuickCam Zoom USB webcam detected.\n"); + name = "Logitech QuickCam Zoom"; + type_id = 740; /* CCD sensor */ + break; + case 0x08B4: + Info("Logitech QuickCam Zoom (new model) USB webcam detected.\n"); + name = "Logitech QuickCam Zoom"; + type_id = 740; /* CCD sensor */ + break; + case 0x08b5: + Info("Logitech QuickCam Orbit/Sphere USB webcam detected.\n"); + name = "Logitech QuickCam Orbit"; + type_id = 740; /* CCD sensor */ + features |= FEATURE_MOTOR_PANTILT; + break; + case 0x08b6: + case 0x08b7: + case 0x08b8: + Info("Logitech QuickCam detected (reserved ID).\n"); + name = "Logitech QuickCam (res.)"; + type_id = 730; /* Assuming CMOS */ + break; + default: + return -ENODEV; + break; + } + } + else if (vendor_id == 0x055d) { + /* I don't know the difference between the C10 and the C30; + I suppose the difference is the sensor, but both cameras + work equally well with a type_id of 675 + */ + switch(product_id) { + case 0x9000: + Info("Samsung MPC-C10 USB webcam detected.\n"); + name = "Samsung MPC-C10"; + type_id = 675; + break; + case 0x9001: + Info("Samsung MPC-C30 USB webcam detected.\n"); + name = "Samsung MPC-C30"; + type_id = 675; + break; + default: + return -ENODEV; + break; + } + } + else if (vendor_id == 0x041e) { + switch(product_id) { + case 0x400c: + Info("Creative Labs Webcam 5 detected.\n"); + name = "Creative Labs Webcam 5"; + type_id = 730; + break; + case 0x4011: + Info("Creative Labs Webcam Pro Ex detected.\n"); + name = "Creative Labs Webcam Pro Ex"; + type_id = 740; + break; + default: + return -ENODEV; + break; + } + } + else if (vendor_id == 0x04cc) { + switch(product_id) { + case 0x8116: + Info("Sotec Afina Eye USB webcam detected.\n"); + name = "Sotec Afina Eye"; + type_id = 730; + break; + default: + return -ENODEV; + break; + } + } + else if (vendor_id == 0x06be) { + switch(product_id) { + case 0x8116: + /* This is essentially the same cam as the Sotec Afina Eye */ + Info("AME Co. Afina Eye USB webcam detected.\n"); + name = "AME Co. Afina Eye"; + type_id = 750; + break; + default: + return -ENODEV; + break; + } + + } + else if (vendor_id == 0x0d81) { + switch(product_id) { + case 0x1900: + Info("Visionite VCS-UC300 USB webcam detected.\n"); + name = "Visionite VCS-UC300"; + type_id = 740; /* CCD sensor */ + break; + case 0x1910: + Info("Visionite VCS-UM100 USB webcam detected.\n"); + name = "Visionite VCS-UM100"; + type_id = 730; /* CMOS sensor */ + break; + default: + return -ENODEV; + break; + } + } + else + return -ENODEV; /* Not any of the know types; but the list keeps growing. */ + + memset(serial_number, 0, 30); + usb_string(udev, udev->descriptor.iSerialNumber, serial_number, 29); + Trace(TRACE_PROBE, "Device serial number is %s\n", serial_number); + + if (udev->descriptor.bNumConfigurations > 1) + Info("Warning: more than 1 configuration available.\n"); + + /* Allocate structure, initialize pointers, mutexes, etc. and link it to the usb_device */ + pdev = kmalloc(sizeof(struct pwc_device), GFP_KERNEL); + if (pdev == NULL) { + Err("Oops, could not allocate memory for pwc_device.\n"); + return -ENOMEM; + } + memset(pdev, 0, sizeof(struct pwc_device)); + pdev->type = type_id; + pdev->vsize = default_size; + pdev->vframes = default_fps; + strcpy(pdev->serial, serial_number); + pdev->features = features; + if (vendor_id == 0x046D && product_id == 0x08B5) + { + /* Logitech QuickCam Orbit + The ranges have been determined experimentally; they may differ from cam to cam. + Also, the exact ranges left-right and up-down are different for my cam + */ + pdev->angle_range.pan_min = -7000; + pdev->angle_range.pan_max = 7000; + pdev->angle_range.tilt_min = -3000; + pdev->angle_range.tilt_max = 2500; + } + + init_MUTEX(&pdev->modlock); + pdev->ptrlock = SPIN_LOCK_UNLOCKED; + + pdev->udev = udev; + init_waitqueue_head(&pdev->frameq); + pdev->vcompression = pwc_preferred_compression; + + /* Allocate video_device structure */ + pdev->vdev = video_device_alloc(); + if (pdev->vdev == 0) + { + Err("Err, cannot allocate video_device struture. Failing probe."); + kfree(pdev); + return -ENOMEM; + } + memcpy(pdev->vdev, &pwc_template, sizeof(pwc_template)); + strcpy(pdev->vdev->name, name); + pdev->vdev->owner = THIS_MODULE; + video_set_drvdata(pdev->vdev, pdev); + + pdev->release = udev->descriptor.bcdDevice; + Trace(TRACE_PROBE, "Release: %04x\n", pdev->release); + + /* Now search device_hint[] table for a match, so we can hint a node number. */ + for (hint = 0; hint < MAX_DEV_HINTS; hint++) { + if (((device_hint[hint].type == -1) || (device_hint[hint].type == pdev->type)) && + (device_hint[hint].pdev == NULL)) { + /* so far, so good... try serial number */ + if ((device_hint[hint].serial_number[0] == '*') || !strcmp(device_hint[hint].serial_number, serial_number)) { + /* match! */ + video_nr = device_hint[hint].device_node; + Trace(TRACE_PROBE, "Found hint, will try to register as /dev/video%d\n", video_nr); + break; + } + } + } + + pdev->vdev->release = video_device_release; + i = video_register_device(pdev->vdev, VFL_TYPE_GRABBER, video_nr); + if (i < 0) { + Err("Failed to register as video device (%d).\n", i); + video_device_release(pdev->vdev); /* Drip... drip... drip... */ + kfree(pdev); /* Oops, no memory leaks please */ + return -EIO; + } + else { + Info("Registered as /dev/video%d.\n", pdev->vdev->minor & 0x3F); + } + + /* occupy slot */ + if (hint < MAX_DEV_HINTS) + device_hint[hint].pdev = pdev; + + Trace(TRACE_PROBE, "probe() function returning struct at 0x%p.\n", pdev); + usb_set_intfdata (intf, pdev); + return 0; +} + +/* The user janked out the cable... */ +static void usb_pwc_disconnect(struct usb_interface *intf) +{ + struct pwc_device *pdev; + int hint; + + lock_kernel(); + pdev = usb_get_intfdata (intf); + usb_set_intfdata (intf, NULL); + if (pdev == NULL) { + Err("pwc_disconnect() Called without private pointer.\n"); + goto disconnect_out; + } + if (pdev->udev == NULL) { + Err("pwc_disconnect() already called for %p\n", pdev); + goto disconnect_out; + } + if (pdev->udev != interface_to_usbdev(intf)) { + Err("pwc_disconnect() Woops: pointer mismatch udev/pdev.\n"); + goto disconnect_out; + } +#ifdef PWC_MAGIC + if (pdev->magic != PWC_MAGIC) { + Err("pwc_disconnect() Magic number failed. Consult your scrolls and try again.\n"); + goto disconnect_out; + } +#endif + + /* We got unplugged; this is signalled by an EPIPE error code */ + if (pdev->vopen) { + Info("Disconnected while webcam is in use!\n"); + pdev->error_status = EPIPE; + } + + /* Alert waiting processes */ + wake_up_interruptible(&pdev->frameq); + /* Wait until device is closed */ + while (pdev->vopen) + schedule(); + /* Device is now closed, so we can safely unregister it */ + Trace(TRACE_PROBE, "Unregistering video device in disconnect().\n"); + video_unregister_device(pdev->vdev); + + /* Free memory (don't set pdev to 0 just yet) */ + kfree(pdev); + +disconnect_out: + /* search device_hint[] table if we occupy a slot, by any chance */ + for (hint = 0; hint < MAX_DEV_HINTS; hint++) + if (device_hint[hint].pdev == pdev) + device_hint[hint].pdev = NULL; + + unlock_kernel(); +} + + +/* *grunt* We have to do atoi ourselves :-( */ +static int pwc_atoi(const char *s) +{ + int k = 0; + + k = 0; + while (*s != '\0' && *s >= '0' && *s <= '9') { + k = 10 * k + (*s - '0'); + s++; + } + return k; +} + + +/* + * Initialization code & module stuff + */ + +static char *size = NULL; +static int fps = 0; +static int fbufs = 0; +static int mbufs = 0; +static int trace = -1; +static int compression = -1; +static int leds[2] = { -1, -1 }; +static char *dev_hint[MAX_DEV_HINTS] = { }; + +MODULE_PARM(size, "s"); +MODULE_PARM_DESC(size, "Initial image size. One of sqcif, qsif, qcif, sif, cif, vga"); +MODULE_PARM(fps, "i"); +MODULE_PARM_DESC(fps, "Initial frames per second. Varies with model, useful range 5-30"); +MODULE_PARM(fbufs, "i"); +MODULE_PARM_DESC(fbufs, "Number of internal frame buffers to reserve"); +MODULE_PARM(mbufs, "i"); +MODULE_PARM_DESC(mbufs, "Number of external (mmap()ed) image buffers"); +MODULE_PARM(trace, "i"); +MODULE_PARM_DESC(trace, "For debugging purposes"); +MODULE_PARM(power_save, "i"); +MODULE_PARM_DESC(power_save, "Turn power save feature in camera on or off"); +MODULE_PARM(compression, "i"); +MODULE_PARM_DESC(compression, "Preferred compression quality. Range 0 (uncompressed) to 3 (high compression)"); +MODULE_PARM(leds, "2i"); +MODULE_PARM_DESC(leds, "LED on,off time in milliseconds"); +MODULE_PARM(dev_hint, "0-20s"); +MODULE_PARM_DESC(dev_hint, "Device node hints"); + +MODULE_DESCRIPTION("Philips & OEM USB webcam driver"); +MODULE_AUTHOR("Luc Saillard "); +MODULE_LICENSE("GPL"); + +static int __init usb_pwc_init(void) +{ + int i, sz; + char *sizenames[PSZ_MAX] = { "sqcif", "qsif", "qcif", "sif", "cif", "vga" }; + + Info("Philips webcam module version " PWC_VERSION " loaded.\n"); + Info("Supports Philips PCA645/646, PCVC675/680/690, PCVC720[40]/730/740/750 & PCVC830/840.\n"); + Info("Also supports the Askey VC010, various Logitech Quickcams, Samsung MPC-C10 and MPC-C30,\n"); + Info("the Creative WebCam 5 & Pro Ex, SOTEC Afina Eye and Visionite VCS-UC300 and VCS-UM100.\n"); + + if (fps) { + if (fps < 4 || fps > 30) { + Err("Framerate out of bounds (4-30).\n"); + return -EINVAL; + } + default_fps = fps; + Info("Default framerate set to %d.\n", default_fps); + } + + if (size) { + /* string; try matching with array */ + for (sz = 0; sz < PSZ_MAX; sz++) { + if (!strcmp(sizenames[sz], size)) { /* Found! */ + default_size = sz; + break; + } + } + if (sz == PSZ_MAX) { + Err("Size not recognized; try size=[sqcif | qsif | qcif | sif | cif | vga].\n"); + return -EINVAL; + } + Info("Default image size set to %s [%dx%d].\n", sizenames[default_size], pwc_image_sizes[default_size].x, pwc_image_sizes[default_size].y); + } + if (mbufs) { + if (mbufs < 1 || mbufs > MAX_IMAGES) { + Err("Illegal number of mmap() buffers; use a number between 1 and %d.\n", MAX_IMAGES); + return -EINVAL; + } + default_mbufs = mbufs; + Info("Number of image buffers set to %d.\n", default_mbufs); + } + if (fbufs) { + if (fbufs < 2 || fbufs > MAX_FRAMES) { + Err("Illegal number of frame buffers; use a number between 2 and %d.\n", MAX_FRAMES); + return -EINVAL; + } + default_fbufs = fbufs; + Info("Number of frame buffers set to %d.\n", default_fbufs); + } + if (trace >= 0) { + Info("Trace options: 0x%04x\n", trace); + pwc_trace = trace; + } + if (compression >= 0) { + if (compression > 3) { + Err("Invalid compression setting; use a number between 0 (uncompressed) and 3 (high).\n"); + return -EINVAL; + } + pwc_preferred_compression = compression; + Info("Preferred compression set to %d.\n", pwc_preferred_compression); + } + if (power_save) + Info("Enabling power save on open/close.\n"); + if (leds[0] >= 0) + led_on = leds[0]; + if (leds[1] >= 0) + led_off = leds[1]; + + /* Big device node whoopla. Basicly, it allows you to assign a + device node (/dev/videoX) to a camera, based on its type + & serial number. The format is [type[.serialnumber]:]node. + + Any camera that isn't matched by these rules gets the next + available free device node. + */ + for (i = 0; i < MAX_DEV_HINTS; i++) { + char *s, *colon, *dot; + + /* This loop also initializes the array */ + device_hint[i].pdev = NULL; + s = dev_hint[i]; + if (s != NULL && *s != '\0') { + device_hint[i].type = -1; /* wildcard */ + strcpy(device_hint[i].serial_number, "*"); + + /* parse string: chop at ':' & '/' */ + colon = dot = s; + while (*colon != '\0' && *colon != ':') + colon++; + while (*dot != '\0' && *dot != '.') + dot++; + /* Few sanity checks */ + if (*dot != '\0' && dot > colon) { + Err("Malformed camera hint: the colon must be after the dot.\n"); + return -EINVAL; + } + + if (*colon == '\0') { + /* No colon */ + if (*dot != '\0') { + Err("Malformed camera hint: no colon + device node given.\n"); + return -EINVAL; + } + else { + /* No type or serial number specified, just a number. */ + device_hint[i].device_node = pwc_atoi(s); + } + } + else { + /* There's a colon, so we have at least a type and a device node */ + device_hint[i].type = pwc_atoi(s); + device_hint[i].device_node = pwc_atoi(colon + 1); + if (*dot != '\0') { + /* There's a serial number as well */ + int k; + + dot++; + k = 0; + while (*dot != ':' && k < 29) { + device_hint[i].serial_number[k++] = *dot; + dot++; + } + device_hint[i].serial_number[k] = '\0'; + } + } +#if PWC_DEBUG + Debug("device_hint[%d]:\n", i); + Debug(" type : %d\n", device_hint[i].type); + Debug(" serial# : %s\n", device_hint[i].serial_number); + Debug(" node : %d\n", device_hint[i].device_node); +#endif + } + else + device_hint[i].type = 0; /* not filled */ + } /* ..for MAX_DEV_HINTS */ + + Trace(TRACE_PROBE, "Registering driver at address 0x%p.\n", &pwc_driver); + return usb_register(&pwc_driver); +} + +static void __exit usb_pwc_exit(void) +{ + Trace(TRACE_MODULE, "Deregistering driver.\n"); + usb_deregister(&pwc_driver); + Info("Philips webcam module removed.\n"); +} + +module_init(usb_pwc_init); +module_exit(usb_pwc_exit); + diff --git a/drivers/usb/media/pwc/pwc-ioctl.h b/drivers/usb/media/pwc/pwc-ioctl.h new file mode 100644 index 000000000..65805eaa9 --- /dev/null +++ b/drivers/usb/media/pwc/pwc-ioctl.h @@ -0,0 +1,292 @@ +#ifndef PWC_IOCTL_H +#define PWC_IOCTL_H + +/* (C) 2001-2004 Nemosoft Unv. + (C) 2004 Luc Saillard (luc@saillard.org) + + NOTE: this version of pwc is an unofficial (modified) release of pwc & pcwx + driver and thus may have bugs that are not present in the original version. + Please send bug reports and support requests to . + The decompression routines have been implemented by reverse-engineering the + Nemosoft binary pwcx module. Caveat emptor. + + This program is free software; you can redistribute it and/or modify + it under the terms of the 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 +*/ + +/* This is pwc-ioctl.h belonging to PWC 8.12.1 + It contains structures and defines to communicate from user space + directly to the driver. + */ + +/* + Changes + 2001/08/03 Alvarado Added ioctl constants to access methods for + changing white balance and red/blue gains + 2002/12/15 G. H. Fernandez-Toribio VIDIOCGREALSIZE + 2003/12/13 Nemosft Unv. Some modifications to make interfacing to + PWCX easier + */ + +/* These are private ioctl() commands, specific for the Philips webcams. + They contain functions not found in other webcams, and settings not + specified in the Video4Linux API. + + The #define names are built up like follows: + VIDIOC VIDeo IOCtl prefix + PWC Philps WebCam + G optional: Get + S optional: Set + ... the function + */ + + + /* Enumeration of image sizes */ +#define PSZ_SQCIF 0x00 +#define PSZ_QSIF 0x01 +#define PSZ_QCIF 0x02 +#define PSZ_SIF 0x03 +#define PSZ_CIF 0x04 +#define PSZ_VGA 0x05 +#define PSZ_MAX 6 + + +/* The frame rate is encoded in the video_window.flags parameter using + the upper 16 bits, since some flags are defined nowadays. The following + defines provide a mask and shift to filter out this value. + + In 'Snapshot' mode the camera freezes its automatic exposure and colour + balance controls. + */ +#define PWC_FPS_SHIFT 16 +#define PWC_FPS_MASK 0x00FF0000 +#define PWC_FPS_FRMASK 0x003F0000 +#define PWC_FPS_SNAPSHOT 0x00400000 + + +/* structure for transfering x & y coordinates */ +struct pwc_coord +{ + int x, y; /* guess what */ + int size; /* size, or offset */ +}; + + +/* Used with VIDIOCPWCPROBE */ +struct pwc_probe +{ + char name[32]; + int type; +}; + +struct pwc_serial +{ + char serial[30]; /* String with serial number. Contains terminating 0 */ +}; + +/* pwc_whitebalance.mode values */ +#define PWC_WB_INDOOR 0 +#define PWC_WB_OUTDOOR 1 +#define PWC_WB_FL 2 +#define PWC_WB_MANUAL 3 +#define PWC_WB_AUTO 4 + +/* Used with VIDIOCPWC[SG]AWB (Auto White Balance). + Set mode to one of the PWC_WB_* values above. + *red and *blue are the respective gains of these colour components inside + the camera; range 0..65535 + When 'mode' == PWC_WB_MANUAL, 'manual_red' and 'manual_blue' are set or read; + otherwise undefined. + 'read_red' and 'read_blue' are read-only. +*/ +struct pwc_whitebalance +{ + int mode; + int manual_red, manual_blue; /* R/W */ + int read_red, read_blue; /* R/O */ +}; + +/* + 'control_speed' and 'control_delay' are used in automatic whitebalance mode, + and tell the camera how fast it should react to changes in lighting, and + with how much delay. Valid values are 0..65535. +*/ +struct pwc_wb_speed +{ + int control_speed; + int control_delay; + +}; + +/* Used with VIDIOCPWC[SG]LED */ +struct pwc_leds +{ + int led_on; /* Led on-time; range = 0..25000 */ + int led_off; /* Led off-time; range = 0..25000 */ +}; + +/* Image size (used with GREALSIZE) */ +struct pwc_imagesize +{ + int width; + int height; +}; + +/* Defines and structures for Motorized Pan & Tilt */ +#define PWC_MPT_PAN 0x01 +#define PWC_MPT_TILT 0x02 +#define PWC_MPT_TIMEOUT 0x04 /* for status */ + +/* Set angles; when absolute != 0, the angle is absolute and the + driver calculates the relative offset for you. This can only + be used with VIDIOCPWCSANGLE; VIDIOCPWCGANGLE always returns + absolute angles. + */ +struct pwc_mpt_angles +{ + int absolute; /* write-only */ + int pan; /* degrees * 100 */ + int tilt; /* degress * 100 */ +}; + +/* Range of angles of the camera, both horizontally and vertically. + */ +struct pwc_mpt_range +{ + int pan_min, pan_max; /* degrees * 100 */ + int tilt_min, tilt_max; +}; + +struct pwc_mpt_status +{ + int status; + int time_pan; + int time_tilt; +}; + + +/* This is used for out-of-kernel decompression. With it, you can get + all the necessary information to initialize and use the decompressor + routines in standalone applications. + */ +struct pwc_video_command +{ + int type; /* camera type (645, 675, 730, etc.) */ + int release; /* release number */ + + int size; /* one of PSZ_* */ + int alternate; + int command_len; /* length of USB video command */ + unsigned char command_buf[13]; /* Actual USB video command */ + int bandlength; /* >0 = compressed */ + int frame_size; /* Size of one (un)compressed frame */ +}; + +/* Flags for PWCX subroutines. Not all modules honour all flags. */ +#define PWCX_FLAG_PLANAR 0x0001 +#define PWCX_FLAG_BAYER 0x0008 + + +/* IOCTL definitions */ + + /* Restore user settings */ +#define VIDIOCPWCRUSER _IO('v', 192) + /* Save user settings */ +#define VIDIOCPWCSUSER _IO('v', 193) + /* Restore factory settings */ +#define VIDIOCPWCFACTORY _IO('v', 194) + + /* You can manipulate the compression factor. A compression preference of 0 + means use uncompressed modes when available; 1 is low compression, 2 is + medium and 3 is high compression preferred. Of course, the higher the + compression, the lower the bandwidth used but more chance of artefacts + in the image. The driver automatically chooses a higher compression when + the preferred mode is not available. + */ + /* Set preferred compression quality (0 = uncompressed, 3 = highest compression) */ +#define VIDIOCPWCSCQUAL _IOW('v', 195, int) + /* Get preferred compression quality */ +#define VIDIOCPWCGCQUAL _IOR('v', 195, int) + + +/* Retrieve serial number of camera */ +#define VIDIOCPWCGSERIAL _IOR('v', 198, struct pwc_serial) + + /* This is a probe function; since so many devices are supported, it + becomes difficult to include all the names in programs that want to + check for the enhanced Philips stuff. So in stead, try this PROBE; + it returns a structure with the original name, and the corresponding + Philips type. + To use, fill the structure with zeroes, call PROBE and if that succeeds, + compare the name with that returned from VIDIOCGCAP; they should be the + same. If so, you can be assured it is a Philips (OEM) cam and the type + is valid. + */ +#define VIDIOCPWCPROBE _IOR('v', 199, struct pwc_probe) + + /* Set AGC (Automatic Gain Control); int < 0 = auto, 0..65535 = fixed */ +#define VIDIOCPWCSAGC _IOW('v', 200, int) + /* Get AGC; int < 0 = auto; >= 0 = fixed, range 0..65535 */ +#define VIDIOCPWCGAGC _IOR('v', 200, int) + /* Set shutter speed; int < 0 = auto; >= 0 = fixed, range 0..65535 */ +#define VIDIOCPWCSSHUTTER _IOW('v', 201, int) + + /* Color compensation (Auto White Balance) */ +#define VIDIOCPWCSAWB _IOW('v', 202, struct pwc_whitebalance) +#define VIDIOCPWCGAWB _IOR('v', 202, struct pwc_whitebalance) + + /* Auto WB speed */ +#define VIDIOCPWCSAWBSPEED _IOW('v', 203, struct pwc_wb_speed) +#define VIDIOCPWCGAWBSPEED _IOR('v', 203, struct pwc_wb_speed) + + /* LEDs on/off/blink; int range 0..65535 */ +#define VIDIOCPWCSLED _IOW('v', 205, struct pwc_leds) +#define VIDIOCPWCGLED _IOR('v', 205, struct pwc_leds) + + /* Contour (sharpness); int < 0 = auto, 0..65536 = fixed */ +#define VIDIOCPWCSCONTOUR _IOW('v', 206, int) +#define VIDIOCPWCGCONTOUR _IOR('v', 206, int) + + /* Backlight compensation; 0 = off, otherwise on */ +#define VIDIOCPWCSBACKLIGHT _IOW('v', 207, int) +#define VIDIOCPWCGBACKLIGHT _IOR('v', 207, int) + + /* Flickerless mode; = 0 off, otherwise on */ +#define VIDIOCPWCSFLICKER _IOW('v', 208, int) +#define VIDIOCPWCGFLICKER _IOR('v', 208, int) + + /* Dynamic noise reduction; 0 off, 3 = high noise reduction */ +#define VIDIOCPWCSDYNNOISE _IOW('v', 209, int) +#define VIDIOCPWCGDYNNOISE _IOR('v', 209, int) + + /* Real image size as used by the camera; tells you whether or not there's a gray border around the image */ +#define VIDIOCPWCGREALSIZE _IOR('v', 210, struct pwc_imagesize) + + /* Motorized pan & tilt functions */ +#define VIDIOCPWCMPTRESET _IOW('v', 211, int) +#define VIDIOCPWCMPTGRANGE _IOR('v', 211, struct pwc_mpt_range) +#define VIDIOCPWCMPTSANGLE _IOW('v', 212, struct pwc_mpt_angles) +#define VIDIOCPWCMPTGANGLE _IOR('v', 212, struct pwc_mpt_angles) +#define VIDIOCPWCMPTSTATUS _IOR('v', 213, struct pwc_mpt_status) + + /* Get the USB set-video command; needed for initializing libpwcx */ +#define VIDIOCPWCGVIDCMD _IOR('v', 215, struct pwc_video_command) +struct pwc_table_init_buffer { + int len; + char *buffer; + +}; +#define VIDIOCPWCGVIDTABLE _IOR('v', 216, struct pwc_table_init_buffer) + +#endif diff --git a/drivers/usb/media/pwc/pwc-kiara.c b/drivers/usb/media/pwc/pwc-kiara.c new file mode 100644 index 000000000..5485800ef --- /dev/null +++ b/drivers/usb/media/pwc/pwc-kiara.c @@ -0,0 +1,891 @@ +/* Linux driver for Philips webcam + (C) 2004 Luc Saillard (luc@saillard.org) + + NOTE: this version of pwc is an unofficial (modified) release of pwc & pcwx + driver and thus may have bugs that are not present in the original version. + Please send bug reports and support requests to . + The decompression routines have been implemented by reverse-engineering the + Nemosoft binary pwcx module. Caveat emptor. + + This program is free software; you can redistribute it and/or modify + it under the terms of the 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 +*/ + + +/* This tables contains entries for the 730/740/750 (Kiara) camera, with + 4 different qualities (no compression, low, medium, high). + It lists the bandwidth requirements for said mode by its alternate interface + number. An alternate of 0 means that the mode is unavailable. + + There are 6 * 4 * 4 entries: + 6 different resolutions subqcif, qsif, qcif, sif, cif, vga + 6 framerates: 5, 10, 15, 20, 25, 30 + 4 compression modi: none, low, medium, high + + When an uncompressed mode is not available, the next available compressed mode + will be chosen (unless the decompressor is absent). Sometimes there are only + 1 or 2 compressed modes available; in that case entries are duplicated. +*/ + + +#include "pwc-kiara.h" +#include "pwc-uncompress.h" + +const struct Kiara_table_entry Kiara_table[PSZ_MAX][6][4] = +{ + /* SQCIF */ + { + /* 5 fps */ + { + {0, }, + {0, }, + {0, }, + {0, }, + }, + /* 10 fps */ + { + {0, }, + {0, }, + {0, }, + {0, }, + }, + /* 15 fps */ + { + {0, }, + {0, }, + {0, }, + {0, }, + }, + /* 20 fps */ + { + {0, }, + {0, }, + {0, }, + {0, }, + }, + /* 25 fps */ + { + {0, }, + {0, }, + {0, }, + {0, }, + }, + /* 30 fps */ + { + {0, }, + {0, }, + {0, }, + {0, }, + }, + }, + /* QSIF */ + { + /* 5 fps */ + { + {1, 146, 0, {0x1D, 0xF4, 0x30, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x92, 0x00, 0x80}}, + {1, 146, 0, {0x1D, 0xF4, 0x30, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x92, 0x00, 0x80}}, + {1, 146, 0, {0x1D, 0xF4, 0x30, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x92, 0x00, 0x80}}, + {1, 146, 0, {0x1D, 0xF4, 0x30, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x92, 0x00, 0x80}}, + }, + /* 10 fps */ + { + {2, 291, 0, {0x1C, 0xF4, 0x30, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x23, 0x01, 0x80}}, + {1, 192, 630, {0x14, 0xF4, 0x30, 0x13, 0xA9, 0x12, 0xE1, 0x17, 0x08, 0xC0, 0x00, 0x80}}, + {1, 192, 630, {0x14, 0xF4, 0x30, 0x13, 0xA9, 0x12, 0xE1, 0x17, 0x08, 0xC0, 0x00, 0x80}}, + {1, 192, 630, {0x14, 0xF4, 0x30, 0x13, 0xA9, 0x12, 0xE1, 0x17, 0x08, 0xC0, 0x00, 0x80}}, + }, + /* 15 fps */ + { + {3, 437, 0, {0x1B, 0xF4, 0x30, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0xB5, 0x01, 0x80}}, + {2, 292, 640, {0x13, 0xF4, 0x30, 0x13, 0xF7, 0x13, 0x2F, 0x13, 0x20, 0x24, 0x01, 0x80}}, + {2, 292, 640, {0x13, 0xF4, 0x30, 0x13, 0xF7, 0x13, 0x2F, 0x13, 0x20, 0x24, 0x01, 0x80}}, + {1, 192, 420, {0x13, 0xF4, 0x30, 0x0D, 0x1B, 0x0C, 0x53, 0x1E, 0x18, 0xC0, 0x00, 0x80}}, + }, + /* 20 fps */ + { + {4, 589, 0, {0x1A, 0xF4, 0x30, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x4D, 0x02, 0x80}}, + {3, 448, 730, {0x12, 0xF4, 0x30, 0x16, 0xC9, 0x16, 0x01, 0x0E, 0x18, 0xC0, 0x01, 0x80}}, + {2, 292, 476, {0x12, 0xF4, 0x30, 0x0E, 0xD8, 0x0E, 0x10, 0x19, 0x18, 0x24, 0x01, 0x80}}, + {1, 192, 312, {0x12, 0xF4, 0x50, 0x09, 0xB3, 0x08, 0xEB, 0x1E, 0x18, 0xC0, 0x00, 0x80}}, + }, + /* 25 fps */ + { + {5, 703, 0, {0x19, 0xF4, 0x30, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0xBF, 0x02, 0x80}}, + {3, 447, 610, {0x11, 0xF4, 0x30, 0x13, 0x0B, 0x12, 0x43, 0x14, 0x28, 0xBF, 0x01, 0x80}}, + {2, 292, 398, {0x11, 0xF4, 0x50, 0x0C, 0x6C, 0x0B, 0xA4, 0x1E, 0x28, 0x24, 0x01, 0x80}}, + {1, 193, 262, {0x11, 0xF4, 0x50, 0x08, 0x23, 0x07, 0x5B, 0x1E, 0x28, 0xC1, 0x00, 0x80}}, + }, + /* 30 fps */ + { + {8, 874, 0, {0x18, 0xF4, 0x30, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x6A, 0x03, 0x80}}, + {5, 704, 730, {0x10, 0xF4, 0x30, 0x16, 0xC9, 0x16, 0x01, 0x0E, 0x28, 0xC0, 0x02, 0x80}}, + {3, 448, 492, {0x10, 0xF4, 0x30, 0x0F, 0x5D, 0x0E, 0x95, 0x15, 0x28, 0xC0, 0x01, 0x80}}, + {2, 292, 320, {0x10, 0xF4, 0x50, 0x09, 0xFB, 0x09, 0x33, 0x1E, 0x28, 0x24, 0x01, 0x80}}, + }, + }, + /* QCIF */ + { + /* 5 fps */ + { + {0, }, + {0, }, + {0, }, + {0, }, + }, + /* 10 fps */ + { + {0, }, + {0, }, + {0, }, + {0, }, + }, + /* 15 fps */ + { + {0, }, + {0, }, + {0, }, + {0, }, + }, + /* 20 fps */ + { + {0, }, + {0, }, + {0, }, + {0, }, + }, + /* 25 fps */ + { + {0, }, + {0, }, + {0, }, + {0, }, + }, + /* 30 fps */ + { + {0, }, + {0, }, + {0, }, + {0, }, + }, + }, + /* SIF */ + { + /* 5 fps */ + { + {4, 582, 0, {0x0D, 0xF4, 0x30, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x46, 0x02, 0x80}}, + {3, 387, 1276, {0x05, 0xF4, 0x30, 0x27, 0xD8, 0x26, 0x48, 0x03, 0x10, 0x83, 0x01, 0x80}}, + {2, 291, 960, {0x05, 0xF4, 0x30, 0x1D, 0xF2, 0x1C, 0x62, 0x04, 0x10, 0x23, 0x01, 0x80}}, + {1, 191, 630, {0x05, 0xF4, 0x50, 0x13, 0xA9, 0x12, 0x19, 0x05, 0x18, 0xBF, 0x00, 0x80}}, + }, + /* 10 fps */ + { + {0, }, + {6, 775, 1278, {0x04, 0xF4, 0x30, 0x27, 0xE8, 0x26, 0x58, 0x05, 0x30, 0x07, 0x03, 0x80}}, + {3, 447, 736, {0x04, 0xF4, 0x30, 0x16, 0xFB, 0x15, 0x6B, 0x05, 0x28, 0xBF, 0x01, 0x80}}, + {2, 292, 480, {0x04, 0xF4, 0x70, 0x0E, 0xF9, 0x0D, 0x69, 0x09, 0x28, 0x24, 0x01, 0x80}}, + }, + /* 15 fps */ + { + {0, }, + {9, 955, 1050, {0x03, 0xF4, 0x30, 0x20, 0xCF, 0x1F, 0x3F, 0x06, 0x48, 0xBB, 0x03, 0x80}}, + {4, 592, 650, {0x03, 0xF4, 0x30, 0x14, 0x44, 0x12, 0xB4, 0x08, 0x30, 0x50, 0x02, 0x80}}, + {3, 448, 492, {0x03, 0xF4, 0x50, 0x0F, 0x52, 0x0D, 0xC2, 0x09, 0x38, 0xC0, 0x01, 0x80}}, + }, + /* 20 fps */ + { + {0, }, + {9, 958, 782, {0x02, 0xF4, 0x30, 0x18, 0x6A, 0x16, 0xDA, 0x0B, 0x58, 0xBE, 0x03, 0x80}}, + {5, 703, 574, {0x02, 0xF4, 0x50, 0x11, 0xE7, 0x10, 0x57, 0x0B, 0x40, 0xBF, 0x02, 0x80}}, + {3, 446, 364, {0x02, 0xF4, 0x90, 0x0B, 0x5C, 0x09, 0xCC, 0x0E, 0x38, 0xBE, 0x01, 0x80}}, + }, + /* 25 fps */ + { + {0, }, + {9, 958, 654, {0x01, 0xF4, 0x30, 0x14, 0x66, 0x12, 0xD6, 0x0B, 0x50, 0xBE, 0x03, 0x80}}, + {6, 776, 530, {0x01, 0xF4, 0x50, 0x10, 0x8C, 0x0E, 0xFC, 0x0C, 0x48, 0x08, 0x03, 0x80}}, + {4, 592, 404, {0x01, 0xF4, 0x70, 0x0C, 0x96, 0x0B, 0x06, 0x0B, 0x48, 0x50, 0x02, 0x80}}, + }, + /* 30 fps */ + { + {0, }, + {9, 957, 526, {0x00, 0xF4, 0x50, 0x10, 0x68, 0x0E, 0xD8, 0x0D, 0x58, 0xBD, 0x03, 0x80}}, + {6, 775, 426, {0x00, 0xF4, 0x70, 0x0D, 0x48, 0x0B, 0xB8, 0x0F, 0x50, 0x07, 0x03, 0x80}}, + {4, 590, 324, {0x00, 0x7A, 0x88, 0x0A, 0x1C, 0x08, 0xB4, 0x0E, 0x50, 0x4E, 0x02, 0x80}}, + }, + }, + /* CIF */ + { + /* 5 fps */ + { + {0, }, + {0, }, + {0, }, + {0, }, + }, + /* 10 fps */ + { + {0, }, + {0, }, + {0, }, + {0, }, + }, + /* 15 fps */ + { + {0, }, + {0, }, + {0, }, + {0, }, + }, + /* 20 fps */ + { + {0, }, + {0, }, + {0, }, + {0, }, + }, + /* 25 fps */ + { + {0, }, + {0, }, + {0, }, + {0, }, + }, + /* 30 fps */ + { + {0, }, + {0, }, + {0, }, + {0, }, + }, + }, + /* VGA */ + { + /* 5 fps */ + { + {0, }, + {6, 773, 1272, {0x25, 0xF4, 0x30, 0x27, 0xB6, 0x24, 0x96, 0x02, 0x30, 0x05, 0x03, 0x80}}, + {4, 592, 976, {0x25, 0xF4, 0x50, 0x1E, 0x78, 0x1B, 0x58, 0x03, 0x30, 0x50, 0x02, 0x80}}, + {3, 448, 738, {0x25, 0xF4, 0x90, 0x17, 0x0C, 0x13, 0xEC, 0x04, 0x30, 0xC0, 0x01, 0x80}}, + }, + /* 10 fps */ + { + {0, }, + {9, 956, 788, {0x24, 0xF4, 0x70, 0x18, 0x9C, 0x15, 0x7C, 0x03, 0x48, 0xBC, 0x03, 0x80}}, + {6, 776, 640, {0x24, 0xF4, 0xB0, 0x13, 0xFC, 0x11, 0x2C, 0x04, 0x48, 0x08, 0x03, 0x80}}, + {4, 592, 488, {0x24, 0x7A, 0xE8, 0x0F, 0x3C, 0x0C, 0x6C, 0x06, 0x48, 0x50, 0x02, 0x80}}, + }, + /* 15 fps */ + { + {0, }, + {9, 957, 526, {0x23, 0x7A, 0xE8, 0x10, 0x68, 0x0D, 0x98, 0x06, 0x58, 0xBD, 0x03, 0x80}}, + {9, 957, 526, {0x23, 0x7A, 0xE8, 0x10, 0x68, 0x0D, 0x98, 0x06, 0x58, 0xBD, 0x03, 0x80}}, + {8, 895, 492, {0x23, 0x7A, 0xE8, 0x0F, 0x5D, 0x0C, 0x8D, 0x06, 0x58, 0x7F, 0x03, 0x80}}, + }, + /* 20 fps */ + { + {0, }, + {0, }, + {0, }, + {0, }, + }, + /* 25 fps */ + { + {0, }, + {0, }, + {0, }, + {0, }, + }, + /* 30 fps */ + { + {0, }, + {0, }, + {0, }, + {0, }, + }, + }, +}; + + +/* + * Rom table for kiara chips + * + * 32 roms tables (one for each resolution ?) + * 2 tables per roms (one for each passes) (Y, and U&V) + * 128 bytes per passes + */ + +const unsigned int KiaraRomTable [8][2][16][8] = +{ + { /* version 0 */ + { /* version 0, passes 0 */ + {0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000001,0x00000001}, + {0x00000000,0x00000000,0x00000009,0x00000009, + 0x00000009,0x00000009,0x00000009,0x00000009}, + {0x00000000,0x00000000,0x00000009,0x00000049, + 0x00000049,0x00000049,0x00000049,0x00000049}, + {0x00000000,0x00000000,0x00000049,0x00000049, + 0x00000049,0x00000249,0x0000024a,0x00000049}, + {0x00000000,0x00000000,0x00000049,0x00000049, + 0x00000249,0x00000249,0x0000024a,0x0000024a}, + {0x00000000,0x00000000,0x00000049,0x00000249, + 0x00000249,0x0000124a,0x0000024a,0x0000024a}, + {0x00000000,0x00000000,0x00000049,0x00000249, + 0x0000124a,0x00009252,0x00001252,0x00001252}, + {0x00000000,0x00000000,0x00000249,0x00000249, + 0x00009252,0x00009292,0x00009292,0x00009292}, + {0x00000000,0x00000000,0x00000249,0x00001249, + 0x00009292,0x00009292,0x00009493,0x000124db}, + {0x00000000,0x00000000,0x00000249,0x0000924a, + 0x00009492,0x0000a49b,0x0000a49b,0x000124db}, + {0x00000000,0x00000000,0x00001249,0x00009252, + 0x0000a493,0x000124db,0x000124db,0x000126dc}, + {0x00000000,0x00000000,0x00001249,0x00009493, + 0x000124db,0x000126dc,0x000136e4,0x000126dc}, + {0x00000000,0x00000000,0x00009292,0x0000a49b, + 0x000124db,0x000136e4,0x000136e4,0x000136e4}, + {0x00000000,0x00000000,0x00009292,0x0000a49b, + 0x000126dc,0x0001b724,0x0001b92d,0x0001b925}, + {0x00000000,0x00000000,0x00009492,0x000124db, + 0x000136e4,0x0001b925,0x0001c96e,0x0001c92d}, + {0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000} + }, + { /* version 0, passes 1 */ + {0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000}, + {0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000}, + {0x00000000,0x00000000,0x00000001,0x00000009, + 0x00000009,0x00000009,0x00000009,0x00000001}, + {0x00000000,0x00000000,0x00000009,0x00000009, + 0x00000049,0x00000049,0x00000049,0x00000049}, + {0x00000000,0x00000000,0x00000049,0x00000049, + 0x00000049,0x00000049,0x0000024a,0x0000024a}, + {0x00000000,0x00000000,0x00000049,0x00000049, + 0x00000249,0x00000249,0x0000024a,0x0000024a}, + {0x00000000,0x00000000,0x00000049,0x00000249, + 0x00000249,0x00000249,0x0000024a,0x00001252}, + {0x00000000,0x00000000,0x00000049,0x00001249, + 0x0000124a,0x0000124a,0x00001252,0x00009292}, + {0x00000000,0x00000000,0x00000249,0x00001249, + 0x00009252,0x00009252,0x00009292,0x00009493}, + {0x00000000,0x00000000,0x00000249,0x0000924a, + 0x00009292,0x00009292,0x00009292,0x00009493}, + {0x00000000,0x00000000,0x00000249,0x00009292, + 0x00009492,0x00009493,0x0000a49b,0x00009493}, + {0x00000000,0x00000000,0x00001249,0x00009292, + 0x0000a493,0x000124db,0x000126dc,0x000126dc}, + {0x00000000,0x00000000,0x0000924a,0x00009493, + 0x0000a493,0x000126dc,0x000136e4,0x000136e4}, + {0x00000000,0x00000000,0x00009252,0x00009493, + 0x000126dc,0x000126dc,0x000136e4,0x000136e4}, + {0x00000000,0x00000000,0x00009292,0x0000a49b, + 0x000136e4,0x000136e4,0x0001b725,0x0001b724}, + {0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000} + } + }, + { /* version 1 */ + { /* version 1, passes 0 */ + {0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000001}, + {0x00000000,0x00000000,0x00000009,0x00000009, + 0x00000009,0x00000009,0x00000009,0x00000009}, + {0x00000000,0x00000000,0x00000049,0x00000049, + 0x00000049,0x00000049,0x00000049,0x00000049}, + {0x00000000,0x00000000,0x00000049,0x00000049, + 0x00000049,0x00000249,0x0000024a,0x0000024a}, + {0x00000000,0x00000000,0x00000049,0x00000249, + 0x00000249,0x00000249,0x0000024a,0x00001252}, + {0x00000000,0x00000000,0x00000249,0x00000249, + 0x00000249,0x0000124a,0x00001252,0x00001252}, + {0x00000000,0x00000000,0x00000249,0x00000249, + 0x0000124a,0x0000124a,0x00009292,0x00009292}, + {0x00000000,0x00000000,0x00000249,0x00001249, + 0x0000124a,0x00009252,0x00009292,0x00009292}, + {0x00000000,0x00000000,0x00000249,0x00001249, + 0x00009252,0x00009292,0x00009292,0x00009292}, + {0x00000000,0x00000000,0x00000249,0x00001249, + 0x00009252,0x00009292,0x00009493,0x00009493}, + {0x00000000,0x00000000,0x00000249,0x0000924a, + 0x00009252,0x00009493,0x00009493,0x00009493}, + {0x00000000,0x00000000,0x00000249,0x0000924a, + 0x00009292,0x00009493,0x00009493,0x00009493}, + {0x00000000,0x00000000,0x00000249,0x00009252, + 0x00009492,0x00009493,0x0000a49b,0x0000a49b}, + {0x00000000,0x00000000,0x00001249,0x00009292, + 0x00009492,0x000124db,0x000124db,0x000124db}, + {0x00000000,0x00000000,0x0000924a,0x00009493, + 0x0000a493,0x000126dc,0x000126dc,0x000126dc}, + {0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000} + }, + { /* version 1, passes 1 */ + {0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000}, + {0x00000000,0x00000000,0x00000049,0x00000009, + 0x00000049,0x00000009,0x00000001,0x00000000}, + {0x00000000,0x00000000,0x00000049,0x00000049, + 0x00000049,0x00000049,0x00000049,0x00000000}, + {0x00000000,0x00000000,0x00000249,0x00000049, + 0x00000249,0x00000049,0x0000024a,0x00000001}, + {0x00000000,0x00000000,0x00000249,0x00000249, + 0x00000249,0x00000249,0x0000024a,0x00000001}, + {0x00000000,0x00000000,0x00000249,0x00000249, + 0x00000249,0x00000249,0x0000024a,0x00000001}, + {0x00000000,0x00000000,0x00000249,0x00000249, + 0x00000249,0x00000249,0x0000024a,0x00000009}, + {0x00000000,0x00000000,0x00000249,0x00000249, + 0x0000124a,0x0000124a,0x0000024a,0x00000009}, + {0x00000000,0x00000000,0x00000249,0x00000249, + 0x0000124a,0x0000124a,0x0000024a,0x00000009}, + {0x00000000,0x00000000,0x00001249,0x00001249, + 0x0000124a,0x00009252,0x00001252,0x00000049}, + {0x00000000,0x00000000,0x00001249,0x00001249, + 0x0000124a,0x00009292,0x00001252,0x00000049}, + {0x00000000,0x00000000,0x00001249,0x00001249, + 0x0000124a,0x00009292,0x00001252,0x00000049}, + {0x00000000,0x00000000,0x00001249,0x00001249, + 0x00009252,0x00009292,0x00001252,0x0000024a}, + {0x00000000,0x00000000,0x00001249,0x00001249, + 0x00009292,0x00009292,0x00001252,0x0000024a}, + {0x00000000,0x00000000,0x0000924a,0x0000924a, + 0x00009492,0x00009493,0x00009292,0x00001252}, + {0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000} + } + }, + { /* version 2 */ + { /* version 2, passes 0 */ + {0x00000000,0x00000000,0x00000049,0x00000049, + 0x00000049,0x00000049,0x0000024a,0x0000024a}, + {0x00000000,0x00000000,0x00000249,0x00000249, + 0x00000249,0x0000124a,0x00001252,0x00009292}, + {0x00000000,0x00000000,0x00000249,0x00000249, + 0x0000124a,0x00009252,0x00009292,0x00009292}, + {0x00000000,0x00000000,0x00000249,0x00001249, + 0x0000124a,0x00009292,0x00009493,0x00009493}, + {0x00000000,0x00000000,0x00000249,0x00001249, + 0x00009252,0x00009493,0x00009493,0x0000a49b}, + {0x00000000,0x00000000,0x00000249,0x0000924a, + 0x00009292,0x00009493,0x0000a49b,0x0000a49b}, + {0x00000000,0x00000000,0x00001249,0x0000924a, + 0x00009292,0x00009493,0x0000a49b,0x000124db}, + {0x00000000,0x00000000,0x00001249,0x00009252, + 0x00009492,0x0000a49b,0x0000a49b,0x000124db}, + {0x00000000,0x00000000,0x00001249,0x00009292, + 0x00009492,0x000124db,0x000124db,0x000126dc}, + {0x00000000,0x00000000,0x00001249,0x00009292, + 0x0000a493,0x000124db,0x000126dc,0x000126dc}, + {0x00000000,0x00000000,0x00001249,0x00009493, + 0x0000a493,0x000124db,0x000126dc,0x000136e4}, + {0x00000000,0x00000000,0x00001249,0x00009493, + 0x0000a493,0x000126dc,0x000136e4,0x000136e4}, + {0x00000000,0x00000000,0x0000924a,0x00009493, + 0x0001249b,0x000126dc,0x000136e4,0x000136e4}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x000124db,0x000136e4,0x000136e4,0x0001b724}, + {0x00000000,0x00000000,0x00009252,0x000124db, + 0x000126dc,0x0001b724,0x0001b725,0x0001b925}, + {0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000} + }, + { /* version 2, passes 1 */ + {0x00000000,0x00000000,0x00000049,0x00000049, + 0x00000049,0x00000049,0x00000049,0x00000049}, + {0x00000000,0x00000000,0x00000249,0x00000249, + 0x00000249,0x00000249,0x0000024a,0x00000049}, + {0x00000000,0x00000000,0x00001249,0x00000249, + 0x0000124a,0x0000124a,0x00001252,0x00000049}, + {0x00000000,0x00000000,0x00001249,0x00001249, + 0x0000124a,0x0000124a,0x00009292,0x0000024a}, + {0x00000000,0x00000000,0x00001249,0x00001249, + 0x00009252,0x00009292,0x00009292,0x0000024a}, + {0x00000000,0x00000000,0x00001249,0x00001249, + 0x00009252,0x00009292,0x0000a49b,0x0000024a}, + {0x00000000,0x00000000,0x00001249,0x00001249, + 0x00009292,0x00009493,0x0000a49b,0x00001252}, + {0x00000000,0x00000000,0x00001249,0x00001249, + 0x00009292,0x00009493,0x0000a49b,0x00001252}, + {0x00000000,0x00000000,0x00001249,0x0000924a, + 0x00009492,0x0000a49b,0x0000a49b,0x00001252}, + {0x00000000,0x00000000,0x00001249,0x00009252, + 0x00009492,0x0000a49b,0x0000a49b,0x00009292}, + {0x00000000,0x00000000,0x00001249,0x00009292, + 0x00009492,0x0000a49b,0x0000a49b,0x00009292}, + {0x00000000,0x00000000,0x00001249,0x00009493, + 0x0000a493,0x0000a49b,0x0000a49b,0x00009292}, + {0x00000000,0x00000000,0x00001249,0x00009493, + 0x0000a493,0x0000a49b,0x0000a49b,0x00009493}, + {0x00000000,0x00000000,0x0000924a,0x00009493, + 0x0000a493,0x000124db,0x0000a49b,0x00009493}, + {0x00000000,0x00000000,0x00009252,0x0000a49b, + 0x0001249b,0x000126dc,0x000124db,0x0000a49b}, + {0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000} + } + }, + { /* version 3 */ + { /* version 3, passes 0 */ + {0x00000000,0x00000000,0x00000249,0x00000249, + 0x0000124a,0x0000124a,0x00009292,0x00009292}, + {0x00000000,0x00000000,0x00001249,0x00001249, + 0x00009292,0x00009493,0x0000a49b,0x0000a49b}, + {0x00000000,0x00000000,0x00001249,0x0000924a, + 0x00009492,0x0000a49b,0x0000a49b,0x000124db}, + {0x00000000,0x00000000,0x00001249,0x00009292, + 0x00009492,0x000124db,0x000126dc,0x000126dc}, + {0x00000000,0x00000000,0x00001249,0x00009493, + 0x0000a493,0x000124db,0x000126dc,0x000126dc}, + {0x00000000,0x00000000,0x00001249,0x00009493, + 0x0000a493,0x000126dc,0x000136e4,0x000136e4}, + {0x00000000,0x00000000,0x00001249,0x00009493, + 0x0000a493,0x000126dc,0x000136e4,0x0001b724}, + {0x00000000,0x00000000,0x00001249,0x00009493, + 0x0001249b,0x000126dc,0x000136e4,0x0001b724}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x0001249b,0x000126dc,0x000136e4,0x0001b724}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x0001249b,0x000136e4,0x0001b725,0x0001b724}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x000124db,0x000136e4,0x0001b725,0x0001b925}, + {0x00000000,0x00000000,0x00009292,0x0000a49b, + 0x000126dc,0x000136e4,0x0001b92d,0x0001b925}, + {0x00000000,0x00000000,0x00009292,0x0000a49b, + 0x000126dc,0x0001b724,0x0001b92d,0x0001c92d}, + {0x00000000,0x00000000,0x00009492,0x000124db, + 0x000126dc,0x0001b724,0x0001c96e,0x0001c92d}, + {0x00000000,0x00000000,0x0000a492,0x000126db, + 0x000136e4,0x0001b925,0x00025bb6,0x00024b77}, + {0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000} + }, + { /* version 3, passes 1 */ + {0x00000000,0x00000000,0x00001249,0x00000249, + 0x0000124a,0x0000124a,0x00001252,0x00001252}, + {0x00000000,0x00000000,0x00001249,0x00001249, + 0x00009252,0x00009292,0x00009292,0x00001252}, + {0x00000000,0x00000000,0x00001249,0x0000924a, + 0x00009492,0x00009493,0x0000a49b,0x00001252}, + {0x00000000,0x00000000,0x00001249,0x00009252, + 0x00009492,0x0000a49b,0x0000a49b,0x00009292}, + {0x00000000,0x00000000,0x00001249,0x00009292, + 0x00009492,0x0000a49b,0x0000a49b,0x00009292}, + {0x00000000,0x00000000,0x00001249,0x00009493, + 0x0000a493,0x0000a49b,0x000126dc,0x00009292}, + {0x00000000,0x00000000,0x0000924a,0x00009493, + 0x0000a493,0x0000a49b,0x000126dc,0x00009493}, + {0x00000000,0x00000000,0x0000924a,0x00009493, + 0x0000a493,0x0000a49b,0x000126dc,0x00009493}, + {0x00000000,0x00000000,0x0000924a,0x00009493, + 0x0000a493,0x000124db,0x000126dc,0x00009493}, + {0x00000000,0x00000000,0x0000924a,0x00009493, + 0x0000a493,0x000124db,0x000126dc,0x0000a49b}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x0000a493,0x000124db,0x000126dc,0x0000a49b}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x0001249b,0x000126dc,0x000126dc,0x0000a49b}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x000124db,0x000136e4,0x000126dc,0x000124db}, + {0x00000000,0x00000000,0x00009492,0x0000a49b, + 0x000136e4,0x000136e4,0x000126dc,0x000124db}, + {0x00000000,0x00000000,0x0000a492,0x000124db, + 0x0001b724,0x0001b724,0x000136e4,0x000126dc}, + {0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000} + } + }, + { /* version 4 */ + { /* version 4, passes 0 */ + {0x00000000,0x00000000,0x00000049,0x00000049, + 0x00000049,0x00000049,0x00000049,0x00000049}, + {0x00000000,0x00000000,0x00000249,0x00000049, + 0x00000249,0x00000249,0x0000024a,0x00000049}, + {0x00000000,0x00000000,0x00000249,0x00000249, + 0x0000124a,0x00009252,0x00001252,0x0000024a}, + {0x00000000,0x00000000,0x00001249,0x00001249, + 0x00009252,0x00009292,0x00009493,0x00001252}, + {0x00000000,0x00000000,0x00001249,0x0000924a, + 0x00009292,0x00009493,0x00009493,0x00001252}, + {0x00000000,0x00000000,0x00001249,0x00009292, + 0x00009492,0x0000a49b,0x0000a49b,0x00009292}, + {0x00000000,0x00000000,0x00001249,0x00009493, + 0x0000a493,0x000124db,0x000124db,0x00009493}, + {0x00000000,0x00000000,0x0000924a,0x00009493, + 0x0000a493,0x000124db,0x000126dc,0x0000a49b}, + {0x00000000,0x00000000,0x0000924a,0x00009493, + 0x0000a493,0x000124db,0x000126dc,0x0000a49b}, + {0x00000000,0x00000000,0x0000924a,0x00009493, + 0x0001249b,0x000126dc,0x000126dc,0x000124db}, + {0x00000000,0x00000000,0x00009252,0x00009493, + 0x000124db,0x000136e4,0x000136e4,0x000126dc}, + {0x00000000,0x00000000,0x00009252,0x0000a49b, + 0x000124db,0x000136e4,0x000136e4,0x000126dc}, + {0x00000000,0x00000000,0x00009292,0x0000a49b, + 0x000126dc,0x000136e4,0x000136e4,0x000136e4}, + {0x00000000,0x00000000,0x00009492,0x0000a49b, + 0x000126dc,0x0001b724,0x0001b725,0x0001b724}, + {0x00000000,0x00000000,0x0000a492,0x000124db, + 0x000136e4,0x0001b925,0x0001b92d,0x0001b925}, + {0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000} + }, + { /* version 4, passes 1 */ + {0x00000000,0x00000000,0x00000249,0x00000049, + 0x00000009,0x00000009,0x00000009,0x00000009}, + {0x00000000,0x00000000,0x00000249,0x00000249, + 0x00000049,0x00000049,0x00000009,0x00000009}, + {0x00000000,0x00000000,0x00001249,0x00001249, + 0x0000124a,0x00000249,0x00000049,0x00000049}, + {0x00000000,0x00000000,0x00001249,0x00001249, + 0x0000124a,0x0000124a,0x00000049,0x00000049}, + {0x00000000,0x00000000,0x00001249,0x00001249, + 0x00009252,0x0000124a,0x0000024a,0x0000024a}, + {0x00000000,0x00000000,0x00001249,0x0000924a, + 0x00009252,0x0000124a,0x0000024a,0x0000024a}, + {0x00000000,0x00000000,0x00001249,0x00009292, + 0x00009492,0x00009252,0x00001252,0x00001252}, + {0x00000000,0x00000000,0x00001249,0x00009493, + 0x0000a493,0x00009292,0x00009292,0x00001252}, + {0x00000000,0x00000000,0x0000924a,0x00009493, + 0x0000a493,0x00009292,0x00009292,0x00009292}, + {0x00000000,0x00000000,0x0000924a,0x00009493, + 0x0000a493,0x00009493,0x00009493,0x00009292}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x0000a493,0x0000a49b,0x00009493,0x00009493}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x0000a493,0x0000a49b,0x0000a49b,0x00009493}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x0001249b,0x000124db,0x0000a49b,0x0000a49b}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x000136e4,0x000126dc,0x000124db,0x0000a49b}, + {0x00000000,0x00000000,0x00009252,0x000124db, + 0x0001b724,0x000136e4,0x000126dc,0x000124db}, + {0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000} + } + }, + { /* version 5 */ + { /* version 5, passes 0 */ + {0x00000000,0x00000000,0x00000249,0x00000249, + 0x00000249,0x00000249,0x00001252,0x00001252}, + {0x00000000,0x00000000,0x00001249,0x00001249, + 0x00009252,0x00009292,0x00009292,0x00001252}, + {0x00000000,0x00000000,0x00001249,0x0000924a, + 0x00009492,0x0000a49b,0x0000a49b,0x00009292}, + {0x00000000,0x00000000,0x00001249,0x00009493, + 0x0000a493,0x0000a49b,0x000124db,0x00009493}, + {0x00000000,0x00000000,0x00001249,0x00009493, + 0x0000a493,0x000124db,0x000126dc,0x00009493}, + {0x00000000,0x00000000,0x0000924a,0x00009493, + 0x0000a493,0x000126dc,0x000126dc,0x0000a49b}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x0001249b,0x000126dc,0x000136e4,0x000124db}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x000126dc,0x000136e4,0x000136e4,0x000126dc}, + {0x00000000,0x00000000,0x00009292,0x0000a49b, + 0x000126dc,0x000136e4,0x000136e4,0x000126dc}, + {0x00000000,0x00000000,0x00009292,0x0000a49b, + 0x000126dc,0x0001b724,0x0001b725,0x000136e4}, + {0x00000000,0x00000000,0x00009292,0x0000a49b, + 0x000136e4,0x0001b724,0x0001b92d,0x0001b724}, + {0x00000000,0x00000000,0x00009492,0x0000a49b, + 0x000136e4,0x0001b724,0x0001b92d,0x0001b724}, + {0x00000000,0x00000000,0x00009492,0x000124db, + 0x000136e4,0x0001b925,0x0001c96e,0x0001b925}, + {0x00000000,0x00000000,0x00009492,0x000124db, + 0x0001b724,0x0001b925,0x0001c96e,0x0001c92d}, + {0x00000000,0x00000000,0x0000a492,0x000126db, + 0x0001c924,0x0002496d,0x00025bb6,0x00024b77}, + {0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000} + }, + { /* version 5, passes 1 */ + {0x00000000,0x00000000,0x00001249,0x00000249, + 0x00000249,0x00000249,0x0000024a,0x0000024a}, + {0x00000000,0x00000000,0x00001249,0x00001249, + 0x0000124a,0x0000124a,0x0000024a,0x0000024a}, + {0x00000000,0x00000000,0x00001249,0x0000924a, + 0x00009252,0x00009252,0x0000024a,0x0000024a}, + {0x00000000,0x00000000,0x00001249,0x00009292, + 0x00009492,0x0000a49b,0x00001252,0x00001252}, + {0x00000000,0x00000000,0x0000924a,0x00009493, + 0x0000a493,0x0000a49b,0x00001252,0x00001252}, + {0x00000000,0x00000000,0x0000924a,0x00009493, + 0x0000a493,0x0000a49b,0x00009292,0x00001252}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x0000a493,0x0000a49b,0x00009292,0x00009292}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x0000a493,0x0000a49b,0x00009493,0x00009292}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x0001249b,0x000124db,0x00009493,0x00009292}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x0001249b,0x000124db,0x00009493,0x00009493}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x000124db,0x000124db,0x0000a49b,0x00009493}, + {0x00000000,0x00000000,0x0000924a,0x000124db, + 0x000126dc,0x000126dc,0x0000a49b,0x00009493}, + {0x00000000,0x00000000,0x0000924a,0x000124db, + 0x000136e4,0x000126dc,0x000124db,0x0000a49b}, + {0x00000000,0x00000000,0x00009292,0x000124db, + 0x000136e4,0x000126dc,0x000124db,0x0000a49b}, + {0x00000000,0x00000000,0x00009492,0x000126db, + 0x0001b724,0x000136e4,0x000126dc,0x000124db}, + {0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000} + } + }, + { /* version 6 */ + { /* version 6, passes 0 */ + {0x00000000,0x00000000,0x00001249,0x00001249, + 0x00009252,0x00009292,0x00009493,0x00009493}, + {0x00000000,0x00000000,0x00001249,0x00009292, + 0x0000a493,0x0000a49b,0x0000a49b,0x00009493}, + {0x00000000,0x00000000,0x00001249,0x00009493, + 0x0000a493,0x000124db,0x000124db,0x0000a49b}, + {0x00000000,0x00000000,0x0000924a,0x00009493, + 0x0000a493,0x000126dc,0x000126dc,0x0000a49b}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x0001249b,0x000126dc,0x000136e4,0x000124db}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x000126dc,0x000136e4,0x000136e4,0x000126dc}, + {0x00000000,0x00000000,0x00009292,0x0000a49b, + 0x000126dc,0x0001b724,0x0001b725,0x000126dc}, + {0x00000000,0x00000000,0x00009292,0x0000a49b, + 0x000136e4,0x0001b724,0x0001b92d,0x000136e4}, + {0x00000000,0x00000000,0x00009492,0x0000a49b, + 0x000136e4,0x0001b724,0x0001b92d,0x0001b724}, + {0x00000000,0x00000000,0x00009492,0x000124db, + 0x000136e4,0x0001b724,0x0001b92d,0x0001b724}, + {0x00000000,0x00000000,0x00009492,0x000124db, + 0x000136e4,0x0001b925,0x0001b92d,0x0001b925}, + {0x00000000,0x00000000,0x00009492,0x000124db, + 0x0001b724,0x0001b925,0x0001c96e,0x0001c92d}, + {0x00000000,0x00000000,0x0000a492,0x000124db, + 0x0001b724,0x0001c92d,0x0001c96e,0x0001c92d}, + {0x00000000,0x00000000,0x0000a492,0x000124db, + 0x0001b724,0x0001c92d,0x00024b76,0x0002496e}, + {0x00000000,0x00000000,0x00012492,0x000126db, + 0x0001c924,0x00024b6d,0x0002ddb6,0x00025bbf}, + {0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000} + }, + { /* version 6, passes 1 */ + {0x00000000,0x00000000,0x00001249,0x00001249, + 0x0000124a,0x0000124a,0x00001252,0x00001252}, + {0x00000000,0x00000000,0x00001249,0x00009292, + 0x00009492,0x00009252,0x00001252,0x00001252}, + {0x00000000,0x00000000,0x0000924a,0x00009493, + 0x0000a493,0x00009292,0x00001252,0x00001252}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x0000a493,0x0000a49b,0x00009292,0x00009292}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x0000a493,0x0000a49b,0x00009292,0x00009292}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x0001249b,0x0000a49b,0x00009493,0x00009292}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x000124db,0x000124db,0x00009493,0x00009493}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x000124db,0x000124db,0x0000a49b,0x00009493}, + {0x00000000,0x00000000,0x0000924a,0x000124db, + 0x000126dc,0x000124db,0x0000a49b,0x00009493}, + {0x00000000,0x00000000,0x0000924a,0x000124db, + 0x000126dc,0x000126dc,0x0000a49b,0x0000a49b}, + {0x00000000,0x00000000,0x0000924a,0x000124db, + 0x000136e4,0x000126dc,0x000124db,0x0000a49b}, + {0x00000000,0x00000000,0x00009492,0x000126db, + 0x000136e4,0x000126dc,0x000124db,0x0000a49b}, + {0x00000000,0x00000000,0x00009492,0x000126db, + 0x0001b724,0x000136e4,0x000126dc,0x000124db}, + {0x00000000,0x00000000,0x00009492,0x000126db, + 0x0001b724,0x000136e4,0x000126dc,0x000124db}, + {0x00000000,0x00000000,0x0000a492,0x000136db, + 0x0001c924,0x0001b724,0x000136e4,0x000126dc}, + {0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000} + } + }, + { /* version 7 */ + { /* version 7, passes 0 */ + {0x00000000,0x00000000,0x00001249,0x00001249, + 0x00009252,0x00009292,0x00009493,0x00009493}, + {0x00000000,0x00000000,0x00001249,0x00009493, + 0x0000a493,0x000124db,0x000126dc,0x00009493}, + {0x00000000,0x00000000,0x00001249,0x0000a49b, + 0x0001249b,0x000126dc,0x000126dc,0x0000a49b}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x0001249b,0x000126dc,0x000136e4,0x0000a49b}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x000126dc,0x000136e4,0x0001b725,0x000124db}, + {0x00000000,0x00000000,0x00009292,0x0000a49b, + 0x000136e4,0x0001b724,0x0001b725,0x000126dc}, + {0x00000000,0x00000000,0x00009292,0x000124db, + 0x000136e4,0x0001b724,0x0001b725,0x000126dc}, + {0x00000000,0x00000000,0x00009492,0x000124db, + 0x000136e4,0x0001b724,0x0001c96e,0x000136e4}, + {0x00000000,0x00000000,0x00009492,0x000124db, + 0x000136e4,0x0001c92d,0x0001c96e,0x0001b724}, + {0x00000000,0x00000000,0x0000a492,0x000124db, + 0x000136e4,0x0001c92d,0x0001c96e,0x0001b724}, + {0x00000000,0x00000000,0x0000a492,0x000124db, + 0x0001b724,0x0001c92d,0x0001c96e,0x0001b925}, + {0x00000000,0x00000000,0x0000a492,0x000126db, + 0x0001b724,0x0001c92d,0x00024b76,0x0001c92d}, + {0x00000000,0x00000000,0x0000a492,0x000126db, + 0x0001b924,0x0001c92d,0x00024b76,0x0001c92d}, + {0x00000000,0x00000000,0x0000a492,0x000126db, + 0x0001b924,0x0001c92d,0x00024b76,0x0002496e}, + {0x00000000,0x00000000,0x00012492,0x000136db, + 0x00024924,0x00024b6d,0x0002ddb6,0x00025bbf}, + {0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000} + }, + { /* version 7, passes 1 */ + {0x00000000,0x00000000,0x00001249,0x00001249, + 0x0000124a,0x0000124a,0x00001252,0x00001252}, + {0x00000000,0x00000000,0x0000924a,0x00009493, + 0x00009492,0x00009292,0x00001252,0x00001252}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x0000a493,0x0000a49b,0x00001252,0x00001252}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x0000a493,0x0000a49b,0x00009292,0x00009292}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x0000a493,0x0000a49b,0x00009292,0x00009292}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x000126dc,0x0000a49b,0x00009493,0x00009292}, + {0x00000000,0x00000000,0x0000924a,0x000124db, + 0x000126dc,0x000124db,0x00009493,0x00009493}, + {0x00000000,0x00000000,0x0000924a,0x000124db, + 0x000136e4,0x000124db,0x0000a49b,0x00009493}, + {0x00000000,0x00000000,0x0000924a,0x000136db, + 0x0001b724,0x000124db,0x0000a49b,0x00009493}, + {0x00000000,0x00000000,0x0000924a,0x000136db, + 0x0001b724,0x000126dc,0x0000a49b,0x0000a49b}, + {0x00000000,0x00000000,0x00009292,0x000136db, + 0x0001b724,0x000126dc,0x000124db,0x0000a49b}, + {0x00000000,0x00000000,0x00009492,0x000136db, + 0x0001b724,0x000126dc,0x000124db,0x0000a49b}, + {0x00000000,0x00000000,0x0000a492,0x000136db, + 0x0001b724,0x000136e4,0x000126dc,0x000124db}, + {0x00000000,0x00000000,0x0000a492,0x000136db, + 0x0001b724,0x000136e4,0x000126dc,0x000124db}, + {0x00000000,0x00000000,0x00012492,0x0001b6db, + 0x0001c924,0x0001b724,0x000136e4,0x000126dc}, + {0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000} + } + } +}; + diff --git a/drivers/usb/media/pwc/pwc-kiara.h b/drivers/usb/media/pwc/pwc-kiara.h new file mode 100644 index 000000000..12929abbb --- /dev/null +++ b/drivers/usb/media/pwc/pwc-kiara.h @@ -0,0 +1,45 @@ +/* Linux driver for Philips webcam + (C) 2004 Luc Saillard (luc@saillard.org) + + NOTE: this version of pwc is an unofficial (modified) release of pwc & pcwx + driver and thus may have bugs that are not present in the original version. + Please send bug reports and support requests to . + The decompression routines have been implemented by reverse-engineering the + Nemosoft binary pwcx module. Caveat emptor. + + This program is free software; you can redistribute it and/or modify + it under the terms of the 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 +*/ + +/* Entries for the Kiara (730/740/750) camera */ + +#ifndef PWC_KIARA_H +#define PWC_KIARA_H + +#include "pwc-ioctl.h" + +struct Kiara_table_entry +{ + char alternate; /* USB alternate interface */ + unsigned short packetsize; /* Normal packet size */ + unsigned short bandlength; /* Bandlength when decompressing */ + unsigned char mode[12]; /* precomputed mode settings for cam */ +}; + +const extern struct Kiara_table_entry Kiara_table[PSZ_MAX][6][4]; +const extern unsigned int KiaraRomTable[8][2][16][8]; + +#endif + + diff --git a/drivers/usb/media/pwc/pwc-misc.c b/drivers/usb/media/pwc/pwc-misc.c new file mode 100644 index 000000000..b7a4bd352 --- /dev/null +++ b/drivers/usb/media/pwc/pwc-misc.c @@ -0,0 +1,140 @@ +/* Linux driver for Philips webcam + Various miscellaneous functions and tables. + (C) 1999-2003 Nemosoft Unv. + (C) 2004 Luc Saillard (luc@saillard.org) + + NOTE: this version of pwc is an unofficial (modified) release of pwc & pcwx + driver and thus may have bugs that are not present in the original version. + Please send bug reports and support requests to . + The decompression routines have been implemented by reverse-engineering the + Nemosoft binary pwcx module. Caveat emptor. + + This program is free software; you can redistribute it and/or modify + it under the terms of the 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 + +#include "pwc.h" + +struct pwc_coord pwc_image_sizes[PSZ_MAX] = +{ + { 128, 96, 0 }, + { 160, 120, 0 }, + { 176, 144, 0 }, + { 320, 240, 0 }, + { 352, 288, 0 }, + { 640, 480, 0 }, +}; + +/* x,y -> PSZ_ */ +int pwc_decode_size(struct pwc_device *pdev, int width, int height) +{ + int i, find; + + /* Make sure we don't go beyond our max size. + NB: we have different limits for RAW and normal modes. In case + you don't have the decompressor loaded or use RAW mode, + the maximum viewable size is smaller. + */ + if (pdev->vpalette == VIDEO_PALETTE_RAW) + { + if (width > pdev->abs_max.x || height > pdev->abs_max.y) + { + Debug("VIDEO_PALETTE_RAW: going beyond abs_max.\n"); + return -1; + } + } + else + { + if (width > pdev->view_max.x || height > pdev->view_max.y) + { + Debug("VIDEO_PALETTE_ not RAW: going beyond view_max.\n"); + return -1; + } + } + + /* Find the largest size supported by the camera that fits into the + requested size. + */ + find = -1; + for (i = 0; i < PSZ_MAX; i++) { + if (pdev->image_mask & (1 << i)) { + if (pwc_image_sizes[i].x <= width && pwc_image_sizes[i].y <= height) + find = i; + } + } + return find; +} + +/* initialize variables depending on type and decompressor*/ +void pwc_construct(struct pwc_device *pdev) +{ + switch(pdev->type) { + case 645: + case 646: + pdev->view_min.x = 128; + pdev->view_min.y = 96; + pdev->view_max.x = 352; + pdev->view_max.y = 288; + pdev->abs_max.x = 352; + pdev->abs_max.y = 288; + pdev->image_mask = 1 << PSZ_SQCIF | 1 << PSZ_QCIF | 1 << PSZ_CIF; + pdev->vcinterface = 2; + pdev->vendpoint = 4; + pdev->frame_header_size = 0; + pdev->frame_trailer_size = 0; + break; + case 675: + case 680: + case 690: + pdev->view_min.x = 128; + pdev->view_min.y = 96; + /* Anthill bug #38: PWC always reports max size, even without PWCX */ + pdev->view_max.x = 640; + pdev->view_max.y = 480; + pdev->image_mask = 1 << PSZ_SQCIF | 1 << PSZ_QSIF | 1 << PSZ_QCIF | 1 << PSZ_SIF | 1 << PSZ_CIF | 1 << PSZ_VGA; + pdev->abs_max.x = 640; + pdev->abs_max.y = 480; + pdev->vcinterface = 3; + pdev->vendpoint = 4; + pdev->frame_header_size = 0; + pdev->frame_trailer_size = 0; + break; + case 720: + case 730: + case 740: + case 750: + pdev->view_min.x = 160; + pdev->view_min.y = 120; + pdev->view_max.x = 640; + pdev->view_max.y = 480; + pdev->image_mask = 1 << PSZ_QSIF | 1 << PSZ_SIF | 1 << PSZ_VGA; + pdev->abs_max.x = 640; + pdev->abs_max.y = 480; + pdev->vcinterface = 3; + pdev->vendpoint = 5; + pdev->frame_header_size = TOUCAM_HEADER_SIZE; + pdev->frame_trailer_size = TOUCAM_TRAILER_SIZE; + break; + } + Debug("type = %d\n",pdev->type); + pdev->vpalette = VIDEO_PALETTE_YUV420P; /* default */ + pdev->view_min.size = pdev->view_min.x * pdev->view_min.y; + pdev->view_max.size = pdev->view_max.x * pdev->view_max.y; + /* length of image, in YUV format; always allocate enough memory. */ + pdev->len_per_image = (pdev->abs_max.x * pdev->abs_max.y * 3) / 2; +} + + diff --git a/drivers/usb/media/pwc/pwc-nala.h b/drivers/usb/media/pwc/pwc-nala.h new file mode 100644 index 000000000..e6c5cb69d --- /dev/null +++ b/drivers/usb/media/pwc/pwc-nala.h @@ -0,0 +1,66 @@ + /* SQCIF */ + { + {0, 0, {0x04, 0x01, 0x03}}, + {8, 0, {0x05, 0x01, 0x03}}, + {7, 0, {0x08, 0x01, 0x03}}, + {7, 0, {0x0A, 0x01, 0x03}}, + {6, 0, {0x0C, 0x01, 0x03}}, + {5, 0, {0x0F, 0x01, 0x03}}, + {4, 0, {0x14, 0x01, 0x03}}, + {3, 0, {0x18, 0x01, 0x03}}, + }, + /* QSIF */ + { + {0}, + {0}, + {0}, + {0}, + {0}, + {0}, + {0}, + {0}, + }, + /* QCIF */ + { + {0, 0, {0x04, 0x01, 0x02}}, + {8, 0, {0x05, 0x01, 0x02}}, + {7, 0, {0x08, 0x01, 0x02}}, + {6, 0, {0x0A, 0x01, 0x02}}, + {5, 0, {0x0C, 0x01, 0x02}}, + {4, 0, {0x0F, 0x01, 0x02}}, + {1, 0, {0x14, 0x01, 0x02}}, + {1, 0, {0x18, 0x01, 0x02}}, + }, + /* SIF */ + { + {0}, + {0}, + {0}, + {0}, + {0}, + {0}, + {0}, + {0}, + }, + /* CIF */ + { + {4, 0, {0x04, 0x01, 0x01}}, + {7, 1, {0x05, 0x03, 0x01}}, + {6, 1, {0x08, 0x03, 0x01}}, + {4, 1, {0x0A, 0x03, 0x01}}, + {3, 1, {0x0C, 0x03, 0x01}}, + {2, 1, {0x0F, 0x03, 0x01}}, + {0}, + {0}, + }, + /* VGA */ + { + {0}, + {0}, + {0}, + {0}, + {0}, + {0}, + {0}, + {0}, + }, diff --git a/drivers/usb/media/pwc/pwc-timon.c b/drivers/usb/media/pwc/pwc-timon.c new file mode 100644 index 000000000..f950a4e5e --- /dev/null +++ b/drivers/usb/media/pwc/pwc-timon.c @@ -0,0 +1,1446 @@ +/* Linux driver for Philips webcam + (C) 2004 Luc Saillard (luc@saillard.org) + + NOTE: this version of pwc is an unofficial (modified) release of pwc & pcwx + driver and thus may have bugs that are not present in the original version. + Please send bug reports and support requests to . + The decompression routines have been implemented by reverse-engineering the + Nemosoft binary pwcx module. Caveat emptor. + + This program is free software; you can redistribute it and/or modify + it under the terms of the 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 +*/ + + +/* This tables contains entries for the 675/680/690 (Timon) camera, with + 4 different qualities (no compression, low, medium, high). + It lists the bandwidth requirements for said mode by its alternate interface + number. An alternate of 0 means that the mode is unavailable. + + There are 6 * 4 * 4 entries: + 6 different resolutions subqcif, qsif, qcif, sif, cif, vga + 6 framerates: 5, 10, 15, 20, 25, 30 + 4 compression modi: none, low, medium, high + + When an uncompressed mode is not available, the next available compressed mode + will be chosen (unless the decompressor is absent). Sometimes there are only + 1 or 2 compressed modes available; in that case entries are duplicated. +*/ + +#include "pwc-timon.h" + +const struct Timon_table_entry Timon_table[PSZ_MAX][6][4] = +{ + /* SQCIF */ + { + /* 5 fps */ + { + {1, 140, 0, {0x05, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x8C, 0xFC, 0x80, 0x02}}, + {1, 140, 0, {0x05, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x8C, 0xFC, 0x80, 0x02}}, + {1, 140, 0, {0x05, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x8C, 0xFC, 0x80, 0x02}}, + {1, 140, 0, {0x05, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x8C, 0xFC, 0x80, 0x02}}, + }, + /* 10 fps */ + { + {2, 280, 0, {0x04, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x18, 0xA9, 0x80, 0x02}}, + {2, 280, 0, {0x04, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x18, 0xA9, 0x80, 0x02}}, + {2, 280, 0, {0x04, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x18, 0xA9, 0x80, 0x02}}, + {2, 280, 0, {0x04, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x18, 0xA9, 0x80, 0x02}}, + }, + /* 15 fps */ + { + {3, 410, 0, {0x03, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x9A, 0x71, 0x80, 0x02}}, + {3, 410, 0, {0x03, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x9A, 0x71, 0x80, 0x02}}, + {3, 410, 0, {0x03, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x9A, 0x71, 0x80, 0x02}}, + {3, 410, 0, {0x03, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x9A, 0x71, 0x80, 0x02}}, + }, + /* 20 fps */ + { + {4, 559, 0, {0x02, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x2F, 0x56, 0x80, 0x02}}, + {4, 559, 0, {0x02, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x2F, 0x56, 0x80, 0x02}}, + {4, 559, 0, {0x02, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x2F, 0x56, 0x80, 0x02}}, + {4, 559, 0, {0x02, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x2F, 0x56, 0x80, 0x02}}, + }, + /* 25 fps */ + { + {5, 659, 0, {0x01, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x93, 0x46, 0x80, 0x02}}, + {5, 659, 0, {0x01, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x93, 0x46, 0x80, 0x02}}, + {5, 659, 0, {0x01, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x93, 0x46, 0x80, 0x02}}, + {5, 659, 0, {0x01, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x93, 0x46, 0x80, 0x02}}, + }, + /* 30 fps */ + { + {7, 838, 0, {0x00, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x46, 0x3B, 0x80, 0x02}}, + {7, 838, 0, {0x00, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x46, 0x3B, 0x80, 0x02}}, + {7, 838, 0, {0x00, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x46, 0x3B, 0x80, 0x02}}, + {7, 838, 0, {0x00, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x46, 0x3B, 0x80, 0x02}}, + }, + }, + /* QSIF */ + { + /* 5 fps */ + { + {1, 146, 0, {0x2D, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x92, 0xFC, 0xC0, 0x02}}, + {1, 146, 0, {0x2D, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x92, 0xFC, 0xC0, 0x02}}, + {1, 146, 0, {0x2D, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x92, 0xFC, 0xC0, 0x02}}, + {1, 146, 0, {0x2D, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x92, 0xFC, 0xC0, 0x02}}, + }, + /* 10 fps */ + { + {2, 291, 0, {0x2C, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x23, 0xA1, 0xC0, 0x02}}, + {1, 191, 630, {0x2C, 0xF4, 0x05, 0x13, 0xA9, 0x12, 0xE1, 0x17, 0x08, 0xBF, 0xF4, 0xC0, 0x02}}, + {1, 191, 630, {0x2C, 0xF4, 0x05, 0x13, 0xA9, 0x12, 0xE1, 0x17, 0x08, 0xBF, 0xF4, 0xC0, 0x02}}, + {1, 191, 630, {0x2C, 0xF4, 0x05, 0x13, 0xA9, 0x12, 0xE1, 0x17, 0x08, 0xBF, 0xF4, 0xC0, 0x02}}, + }, + /* 15 fps */ + { + {3, 437, 0, {0x2B, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0xB5, 0x6D, 0xC0, 0x02}}, + {2, 291, 640, {0x2B, 0xF4, 0x05, 0x13, 0xF7, 0x13, 0x2F, 0x13, 0x08, 0x23, 0xA1, 0xC0, 0x02}}, + {2, 291, 640, {0x2B, 0xF4, 0x05, 0x13, 0xF7, 0x13, 0x2F, 0x13, 0x08, 0x23, 0xA1, 0xC0, 0x02}}, + {1, 191, 420, {0x2B, 0xF4, 0x0D, 0x0D, 0x1B, 0x0C, 0x53, 0x1E, 0x08, 0xBF, 0xF4, 0xC0, 0x02}}, + }, + /* 20 fps */ + { + {4, 588, 0, {0x2A, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x4C, 0x52, 0xC0, 0x02}}, + {3, 447, 730, {0x2A, 0xF4, 0x05, 0x16, 0xC9, 0x16, 0x01, 0x0E, 0x18, 0xBF, 0x69, 0xC0, 0x02}}, + {2, 292, 476, {0x2A, 0xF4, 0x0D, 0x0E, 0xD8, 0x0E, 0x10, 0x19, 0x18, 0x24, 0xA1, 0xC0, 0x02}}, + {1, 192, 312, {0x2A, 0xF4, 0x1D, 0x09, 0xB3, 0x08, 0xEB, 0x1E, 0x18, 0xC0, 0xF4, 0xC0, 0x02}}, + }, + /* 25 fps */ + { + {5, 703, 0, {0x29, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0xBF, 0x42, 0xC0, 0x02}}, + {3, 447, 610, {0x29, 0xF4, 0x05, 0x13, 0x0B, 0x12, 0x43, 0x14, 0x18, 0xBF, 0x69, 0xC0, 0x02}}, + {2, 292, 398, {0x29, 0xF4, 0x0D, 0x0C, 0x6C, 0x0B, 0xA4, 0x1E, 0x18, 0x24, 0xA1, 0xC0, 0x02}}, + {1, 192, 262, {0x29, 0xF4, 0x25, 0x08, 0x23, 0x07, 0x5B, 0x1E, 0x18, 0xC0, 0xF4, 0xC0, 0x02}}, + }, + /* 30 fps */ + { + {8, 873, 0, {0x28, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x69, 0x37, 0xC0, 0x02}}, + {5, 704, 774, {0x28, 0xF4, 0x05, 0x18, 0x21, 0x17, 0x59, 0x0F, 0x18, 0xC0, 0x42, 0xC0, 0x02}}, + {3, 448, 492, {0x28, 0xF4, 0x05, 0x0F, 0x5D, 0x0E, 0x95, 0x15, 0x18, 0xC0, 0x69, 0xC0, 0x02}}, + {2, 291, 320, {0x28, 0xF4, 0x1D, 0x09, 0xFB, 0x09, 0x33, 0x1E, 0x18, 0x23, 0xA1, 0xC0, 0x02}}, + }, + }, + /* QCIF */ + { + /* 5 fps */ + { + {1, 193, 0, {0x0D, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x12, 0x00, 0xC1, 0xF4, 0xC0, 0x02}}, + {1, 193, 0, {0x0D, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x12, 0x00, 0xC1, 0xF4, 0xC0, 0x02}}, + {1, 193, 0, {0x0D, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x12, 0x00, 0xC1, 0xF4, 0xC0, 0x02}}, + {1, 193, 0, {0x0D, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x12, 0x00, 0xC1, 0xF4, 0xC0, 0x02}}, + }, + /* 10 fps */ + { + {3, 385, 0, {0x0C, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x12, 0x00, 0x81, 0x79, 0xC0, 0x02}}, + {2, 291, 800, {0x0C, 0xF4, 0x05, 0x18, 0xF4, 0x18, 0x18, 0x11, 0x08, 0x23, 0xA1, 0xC0, 0x02}}, + {2, 291, 800, {0x0C, 0xF4, 0x05, 0x18, 0xF4, 0x18, 0x18, 0x11, 0x08, 0x23, 0xA1, 0xC0, 0x02}}, + {1, 194, 532, {0x0C, 0xF4, 0x05, 0x10, 0x9A, 0x0F, 0xBE, 0x1B, 0x08, 0xC2, 0xF0, 0xC0, 0x02}}, + }, + /* 15 fps */ + { + {4, 577, 0, {0x0B, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x12, 0x00, 0x41, 0x52, 0xC0, 0x02}}, + {3, 447, 818, {0x0B, 0xF4, 0x05, 0x19, 0x89, 0x18, 0xAD, 0x0F, 0x10, 0xBF, 0x69, 0xC0, 0x02}}, + {2, 292, 534, {0x0B, 0xF4, 0x05, 0x10, 0xA3, 0x0F, 0xC7, 0x19, 0x10, 0x24, 0xA1, 0xC0, 0x02}}, + {1, 195, 356, {0x0B, 0xF4, 0x15, 0x0B, 0x11, 0x0A, 0x35, 0x1E, 0x10, 0xC3, 0xF0, 0xC0, 0x02}}, + }, + /* 20 fps */ + { + {6, 776, 0, {0x0A, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x12, 0x00, 0x08, 0x3F, 0xC0, 0x02}}, + {4, 591, 804, {0x0A, 0xF4, 0x05, 0x19, 0x1E, 0x18, 0x42, 0x0F, 0x18, 0x4F, 0x4E, 0xC0, 0x02}}, + {3, 447, 608, {0x0A, 0xF4, 0x05, 0x12, 0xFD, 0x12, 0x21, 0x15, 0x18, 0xBF, 0x69, 0xC0, 0x02}}, + {2, 291, 396, {0x0A, 0xF4, 0x15, 0x0C, 0x5E, 0x0B, 0x82, 0x1E, 0x18, 0x23, 0xA1, 0xC0, 0x02}}, + }, + /* 25 fps */ + { + {9, 928, 0, {0x09, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x12, 0x00, 0xA0, 0x33, 0xC0, 0x02}}, + {5, 703, 800, {0x09, 0xF4, 0x05, 0x18, 0xF4, 0x18, 0x18, 0x10, 0x18, 0xBF, 0x42, 0xC0, 0x02}}, + {3, 447, 508, {0x09, 0xF4, 0x0D, 0x0F, 0xD2, 0x0E, 0xF6, 0x1B, 0x18, 0xBF, 0x69, 0xC0, 0x02}}, + {2, 292, 332, {0x09, 0xF4, 0x1D, 0x0A, 0x5A, 0x09, 0x7E, 0x1E, 0x18, 0x24, 0xA1, 0xC0, 0x02}}, + }, + /* 30 fps */ + { + {0, }, + {9, 956, 876, {0x08, 0xF4, 0x05, 0x1B, 0x58, 0x1A, 0x7C, 0x0E, 0x20, 0xBC, 0x33, 0x10, 0x02}}, + {4, 592, 542, {0x08, 0xF4, 0x05, 0x10, 0xE4, 0x10, 0x08, 0x17, 0x20, 0x50, 0x4E, 0x10, 0x02}}, + {2, 291, 266, {0x08, 0xF4, 0x25, 0x08, 0x48, 0x07, 0x6C, 0x1E, 0x20, 0x23, 0xA1, 0x10, 0x02}}, + }, + }, + /* SIF */ + { + /* 5 fps */ + { + {4, 582, 0, {0x35, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x46, 0x52, 0x60, 0x02}}, + {3, 387, 1276, {0x35, 0xF4, 0x05, 0x27, 0xD8, 0x26, 0x48, 0x03, 0x10, 0x83, 0x79, 0x60, 0x02}}, + {2, 291, 960, {0x35, 0xF4, 0x0D, 0x1D, 0xF2, 0x1C, 0x62, 0x04, 0x10, 0x23, 0xA1, 0x60, 0x02}}, + {1, 191, 630, {0x35, 0xF4, 0x1D, 0x13, 0xA9, 0x12, 0x19, 0x05, 0x08, 0xBF, 0xF4, 0x60, 0x02}}, + }, + /* 10 fps */ + { + {0, }, + {6, 775, 1278, {0x34, 0xF4, 0x05, 0x27, 0xE8, 0x26, 0x58, 0x05, 0x30, 0x07, 0x3F, 0x10, 0x02}}, + {3, 447, 736, {0x34, 0xF4, 0x15, 0x16, 0xFB, 0x15, 0x6B, 0x05, 0x18, 0xBF, 0x69, 0x10, 0x02}}, + {2, 291, 480, {0x34, 0xF4, 0x2D, 0x0E, 0xF9, 0x0D, 0x69, 0x09, 0x18, 0x23, 0xA1, 0x10, 0x02}}, + }, + /* 15 fps */ + { + {0, }, + {9, 955, 1050, {0x33, 0xF4, 0x05, 0x20, 0xCF, 0x1F, 0x3F, 0x06, 0x48, 0xBB, 0x33, 0x10, 0x02}}, + {4, 591, 650, {0x33, 0xF4, 0x15, 0x14, 0x44, 0x12, 0xB4, 0x08, 0x30, 0x4F, 0x4E, 0x10, 0x02}}, + {3, 448, 492, {0x33, 0xF4, 0x25, 0x0F, 0x52, 0x0D, 0xC2, 0x09, 0x28, 0xC0, 0x69, 0x10, 0x02}}, + }, + /* 20 fps */ + { + {0, }, + {9, 958, 782, {0x32, 0xF4, 0x0D, 0x18, 0x6A, 0x16, 0xDA, 0x0B, 0x58, 0xBE, 0x33, 0xD0, 0x02}}, + {5, 703, 574, {0x32, 0xF4, 0x1D, 0x11, 0xE7, 0x10, 0x57, 0x0B, 0x40, 0xBF, 0x42, 0xD0, 0x02}}, + {3, 446, 364, {0x32, 0xF4, 0x3D, 0x0B, 0x5C, 0x09, 0xCC, 0x0E, 0x30, 0xBE, 0x69, 0xD0, 0x02}}, + }, + /* 25 fps */ + { + {0, }, + {9, 958, 654, {0x31, 0xF4, 0x15, 0x14, 0x66, 0x12, 0xD6, 0x0B, 0x50, 0xBE, 0x33, 0x90, 0x02}}, + {6, 776, 530, {0x31, 0xF4, 0x25, 0x10, 0x8C, 0x0E, 0xFC, 0x0C, 0x48, 0x08, 0x3F, 0x90, 0x02}}, + {4, 592, 404, {0x31, 0xF4, 0x35, 0x0C, 0x96, 0x0B, 0x06, 0x0B, 0x38, 0x50, 0x4E, 0x90, 0x02}}, + }, + /* 30 fps */ + { + {0, }, + {9, 957, 526, {0x30, 0xF4, 0x25, 0x10, 0x68, 0x0E, 0xD8, 0x0D, 0x58, 0xBD, 0x33, 0x60, 0x02}}, + {6, 775, 426, {0x30, 0xF4, 0x35, 0x0D, 0x48, 0x0B, 0xB8, 0x0F, 0x50, 0x07, 0x3F, 0x60, 0x02}}, + {4, 590, 324, {0x30, 0x7A, 0x4B, 0x0A, 0x1C, 0x08, 0xB4, 0x0E, 0x40, 0x4E, 0x52, 0x60, 0x02}}, + }, + }, + /* CIF */ + { + /* 5 fps */ + { + {6, 771, 0, {0x15, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x03, 0x3F, 0x80, 0x02}}, + {4, 465, 1278, {0x15, 0xF4, 0x05, 0x27, 0xEE, 0x26, 0x36, 0x03, 0x18, 0xD1, 0x65, 0x80, 0x02}}, + {2, 291, 800, {0x15, 0xF4, 0x15, 0x18, 0xF4, 0x17, 0x3C, 0x05, 0x18, 0x23, 0xA1, 0x80, 0x02}}, + {1, 193, 528, {0x15, 0xF4, 0x2D, 0x10, 0x7E, 0x0E, 0xC6, 0x0A, 0x18, 0xC1, 0xF4, 0x80, 0x02}}, + }, + /* 10 fps */ + { + {0, }, + {9, 932, 1278, {0x14, 0xF4, 0x05, 0x27, 0xEE, 0x26, 0x36, 0x04, 0x30, 0xA4, 0x33, 0x10, 0x02}}, + {4, 591, 812, {0x14, 0xF4, 0x15, 0x19, 0x56, 0x17, 0x9E, 0x06, 0x28, 0x4F, 0x4E, 0x10, 0x02}}, + {2, 291, 400, {0x14, 0xF4, 0x3D, 0x0C, 0x7A, 0x0A, 0xC2, 0x0E, 0x28, 0x23, 0xA1, 0x10, 0x02}}, + }, + /* 15 fps */ + { + {0, }, + {9, 956, 876, {0x13, 0xF4, 0x0D, 0x1B, 0x58, 0x19, 0xA0, 0x05, 0x38, 0xBC, 0x33, 0x60, 0x02}}, + {5, 703, 644, {0x13, 0xF4, 0x1D, 0x14, 0x1C, 0x12, 0x64, 0x08, 0x38, 0xBF, 0x42, 0x60, 0x02}}, + {3, 448, 410, {0x13, 0xF4, 0x3D, 0x0C, 0xC4, 0x0B, 0x0C, 0x0E, 0x38, 0xC0, 0x69, 0x60, 0x02}}, + }, + /* 20 fps */ + { + {0, }, + {9, 956, 650, {0x12, 0xF4, 0x1D, 0x14, 0x4A, 0x12, 0x92, 0x09, 0x48, 0xBC, 0x33, 0x10, 0x03}}, + {6, 776, 528, {0x12, 0xF4, 0x2D, 0x10, 0x7E, 0x0E, 0xC6, 0x0A, 0x40, 0x08, 0x3F, 0x10, 0x03}}, + {4, 591, 402, {0x12, 0xF4, 0x3D, 0x0C, 0x8F, 0x0A, 0xD7, 0x0E, 0x40, 0x4F, 0x4E, 0x10, 0x03}}, + }, + /* 25 fps */ + { + {0, }, + {9, 956, 544, {0x11, 0xF4, 0x25, 0x10, 0xF4, 0x0F, 0x3C, 0x0A, 0x48, 0xBC, 0x33, 0xC0, 0x02}}, + {7, 840, 478, {0x11, 0xF4, 0x2D, 0x0E, 0xEB, 0x0D, 0x33, 0x0B, 0x48, 0x48, 0x3B, 0xC0, 0x02}}, + {5, 703, 400, {0x11, 0xF4, 0x3D, 0x0C, 0x7A, 0x0A, 0xC2, 0x0E, 0x48, 0xBF, 0x42, 0xC0, 0x02}}, + }, + /* 30 fps */ + { + {0, }, + {9, 956, 438, {0x10, 0xF4, 0x35, 0x0D, 0xAC, 0x0B, 0xF4, 0x0D, 0x50, 0xBC, 0x33, 0x10, 0x02}}, + {7, 838, 384, {0x10, 0xF4, 0x45, 0x0B, 0xFD, 0x0A, 0x45, 0x0F, 0x50, 0x46, 0x3B, 0x10, 0x02}}, + {6, 773, 354, {0x10, 0x7A, 0x4B, 0x0B, 0x0C, 0x09, 0x80, 0x10, 0x50, 0x05, 0x3F, 0x10, 0x02}}, + }, + }, + /* VGA */ + { + /* 5 fps */ + { + {0, }, + {6, 773, 1272, {0x1D, 0xF4, 0x15, 0x27, 0xB6, 0x24, 0x96, 0x02, 0x30, 0x05, 0x3F, 0x10, 0x02}}, + {4, 592, 976, {0x1D, 0xF4, 0x25, 0x1E, 0x78, 0x1B, 0x58, 0x03, 0x30, 0x50, 0x4E, 0x10, 0x02}}, + {3, 448, 738, {0x1D, 0xF4, 0x3D, 0x17, 0x0C, 0x13, 0xEC, 0x04, 0x30, 0xC0, 0x69, 0x10, 0x02}}, + }, + /* 10 fps */ + { + {0, }, + {9, 956, 788, {0x1C, 0xF4, 0x35, 0x18, 0x9C, 0x15, 0x7C, 0x03, 0x48, 0xBC, 0x33, 0x10, 0x02}}, + {6, 776, 640, {0x1C, 0x7A, 0x53, 0x13, 0xFC, 0x11, 0x2C, 0x04, 0x48, 0x08, 0x3F, 0x10, 0x02}}, + {4, 592, 488, {0x1C, 0x7A, 0x6B, 0x0F, 0x3C, 0x0C, 0x6C, 0x06, 0x48, 0x50, 0x4E, 0x10, 0x02}}, + }, + /* 15 fps */ + { + {0, }, + {9, 957, 526, {0x1B, 0x7A, 0x63, 0x10, 0x68, 0x0D, 0x98, 0x06, 0x58, 0xBD, 0x33, 0x80, 0x02}}, + {9, 957, 526, {0x1B, 0x7A, 0x63, 0x10, 0x68, 0x0D, 0x98, 0x06, 0x58, 0xBD, 0x33, 0x80, 0x02}}, + {8, 895, 492, {0x1B, 0x7A, 0x6B, 0x0F, 0x5D, 0x0C, 0x8D, 0x06, 0x58, 0x7F, 0x37, 0x80, 0x02}}, + }, + /* 20 fps */ + { + {0, }, + {0, }, + {0, }, + {0, }, + }, + /* 25 fps */ + { + {0, }, + {0, }, + {0, }, + {0, }, + }, + /* 30 fps */ + { + {0, }, + {0, }, + {0, }, + {0, }, + }, + }, +}; + +/* + * 16 versions: + * 2 tables (one for Y, and one for U&V) + * 16 levels of details per tables + * 8 blocs + */ + +const unsigned int TimonRomTable [16][2][16][8] = +{ + { /* version 0 */ + { /* version 0, passes 0 */ + {0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000001}, + {0x00000000,0x00000000,0x00000001,0x00000001, + 0x00000001,0x00000001,0x00000001,0x00000001}, + {0x00000000,0x00000000,0x00000001,0x00000001, + 0x00000001,0x00000009,0x00000009,0x00000009}, + {0x00000000,0x00000000,0x00000009,0x00000001, + 0x00000009,0x00000009,0x00000009,0x00000009}, + {0x00000000,0x00000000,0x00000009,0x00000009, + 0x00000009,0x00000009,0x00000049,0x00000009}, + {0x00000000,0x00000000,0x00000009,0x00000009, + 0x00000009,0x00000049,0x00000049,0x00000049}, + {0x00000000,0x00000000,0x00000009,0x00000009, + 0x00000049,0x00000049,0x00000049,0x00000049}, + {0x00000000,0x00000000,0x00000009,0x00000049, + 0x00000049,0x00000049,0x00000049,0x00000049}, + {0x00000000,0x00000000,0x00000049,0x00000049, + 0x00000049,0x00000049,0x0000024a,0x0000024a}, + {0x00000000,0x00000000,0x00000049,0x00000049, + 0x00000049,0x00000249,0x0000024a,0x0000024a}, + {0x00000000,0x00000000,0x00000049,0x00000049, + 0x00000249,0x00000249,0x0000024a,0x0000024a}, + {0x00000000,0x00000000,0x00000049,0x00000049, + 0x00000249,0x00000249,0x00001252,0x0000024a}, + {0x00000000,0x00000000,0x00000049,0x00000049, + 0x00000249,0x0000124a,0x00001252,0x0000024a}, + {0x00000000,0x00000000,0x00000049,0x00000249, + 0x00000249,0x0000124a,0x00001252,0x0000024a}, + {0x00000000,0x00000000,0x00000249,0x00001249, + 0x0000124a,0x00009252,0x00009292,0x00001252}, + {0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000} + }, + { /* version 0, passes 1 */ + {0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000}, + {0x00000000,0x00000000,0x00000001,0x00000001, + 0x00000001,0x00000001,0x00000000,0x00000000}, + {0x00000000,0x00000000,0x00000009,0x00000001, + 0x00000001,0x00000009,0x00000000,0x00000000}, + {0x00000000,0x00000000,0x00000009,0x00000009, + 0x00000009,0x00000009,0x00000000,0x00000000}, + {0x00000000,0x00000000,0x00000009,0x00000009, + 0x00000009,0x00000009,0x00000001,0x00000000}, + {0x00000000,0x00000000,0x00000049,0x00000009, + 0x00000009,0x00000049,0x00000001,0x00000001}, + {0x00000000,0x00000000,0x00000049,0x00000009, + 0x00000009,0x00000049,0x00000001,0x00000001}, + {0x00000000,0x00000000,0x00000049,0x00000049, + 0x00000049,0x00000049,0x00000009,0x00000001}, + {0x00000000,0x00000000,0x00000049,0x00000049, + 0x00000049,0x00000049,0x00000009,0x00000001}, + {0x00000000,0x00000000,0x00000049,0x00000049, + 0x00000049,0x00000049,0x00000009,0x00000001}, + {0x00000000,0x00000000,0x00000049,0x00000049, + 0x00000049,0x00000049,0x00000009,0x00000009}, + {0x00000000,0x00000000,0x00000049,0x00000049, + 0x00000049,0x00000249,0x00000049,0x00000009}, + {0x00000000,0x00000000,0x00000049,0x00000049, + 0x00000049,0x00000249,0x00000049,0x00000009}, + {0x00000000,0x00000000,0x00000249,0x00000049, + 0x00000249,0x00000249,0x00000049,0x00000009}, + {0x00000000,0x00000000,0x00001249,0x00000249, + 0x0000124a,0x0000124a,0x0000024a,0x00000049}, + {0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000} + } + }, + { /* version 1 */ + { /* version 1, passes 0 */ + {0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000001}, + {0x00000000,0x00000000,0x00000001,0x00000001, + 0x00000001,0x00000009,0x00000009,0x00000009}, + {0x00000000,0x00000000,0x00000009,0x00000009, + 0x00000009,0x00000009,0x00000009,0x00000009}, + {0x00000000,0x00000000,0x00000009,0x00000009, + 0x00000009,0x00000049,0x00000049,0x00000049}, + {0x00000000,0x00000000,0x00000009,0x00000049, + 0x00000049,0x00000049,0x00000049,0x00000049}, + {0x00000000,0x00000000,0x00000049,0x00000049, + 0x00000049,0x00000249,0x0000024a,0x0000024a}, + {0x00000000,0x00000000,0x00000049,0x00000049, + 0x00000249,0x00000249,0x0000024a,0x0000024a}, + {0x00000000,0x00000000,0x00000049,0x00000249, + 0x00000249,0x00000249,0x0000024a,0x00001252}, + {0x00000000,0x00000000,0x00000049,0x00000249, + 0x00000249,0x0000124a,0x00001252,0x00001252}, + {0x00000000,0x00000000,0x00000049,0x00000249, + 0x0000124a,0x0000124a,0x00001252,0x00001252}, + {0x00000000,0x00000000,0x00000249,0x00000249, + 0x0000124a,0x0000124a,0x00009292,0x00009292}, + {0x00000000,0x00000000,0x00000249,0x00001249, + 0x0000124a,0x00009252,0x00009292,0x00009292}, + {0x00000000,0x00000000,0x00000249,0x00001249, + 0x00009252,0x00009252,0x00009292,0x00009292}, + {0x00000000,0x00000000,0x00000249,0x0000924a, + 0x00009292,0x00009493,0x00009493,0x00009493}, + {0x00000000,0x00000000,0x00001249,0x00009252, + 0x00009492,0x0000a49b,0x0000a49b,0x0000a49b}, + {0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000} + }, + { /* version 1, passes 1 */ + {0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000}, + {0x00000000,0x00000000,0x00000009,0x00000009, + 0x00000009,0x00000001,0x00000001,0x00000000}, + {0x00000000,0x00000000,0x00000009,0x00000009, + 0x00000009,0x00000009,0x00000001,0x00000000}, + {0x00000000,0x00000000,0x00000049,0x00000049, + 0x00000049,0x00000009,0x00000001,0x00000000}, + {0x00000000,0x00000000,0x00000049,0x00000049, + 0x00000049,0x00000049,0x00000001,0x00000001}, + {0x00000000,0x00000000,0x00000049,0x00000049, + 0x00000049,0x00000049,0x00000009,0x00000001}, + {0x00000000,0x00000000,0x00000249,0x00000049, + 0x00000049,0x00000249,0x00000009,0x00000001}, + {0x00000000,0x00000000,0x00000249,0x00000049, + 0x00000249,0x00000249,0x00000009,0x00000009}, + {0x00000000,0x00000000,0x00000249,0x00000249, + 0x00000249,0x00000249,0x00000049,0x00000009}, + {0x00000000,0x00000000,0x00000249,0x00000249, + 0x00000249,0x0000124a,0x00000049,0x00000009}, + {0x00000000,0x00000000,0x00000249,0x00000249, + 0x00000249,0x0000124a,0x00000049,0x00000009}, + {0x00000000,0x00000000,0x00000249,0x00000249, + 0x00000249,0x0000124a,0x0000024a,0x00000049}, + {0x00000000,0x00000000,0x00000249,0x00000249, + 0x0000124a,0x0000124a,0x0000024a,0x00000049}, + {0x00000000,0x00000000,0x00000249,0x00000249, + 0x0000124a,0x0000124a,0x0000024a,0x00000049}, + {0x00000000,0x00000000,0x00001249,0x00001249, + 0x00009252,0x00009252,0x00001252,0x0000024a}, + {0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000} + } + }, + { /* version 2 */ + { /* version 2, passes 0 */ + {0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000001}, + {0x00000000,0x00000000,0x00000009,0x00000009, + 0x00000009,0x00000009,0x00000009,0x00000009}, + {0x00000000,0x00000000,0x00000049,0x00000049, + 0x00000049,0x00000049,0x00000049,0x00000049}, + {0x00000000,0x00000000,0x00000049,0x00000049, + 0x00000049,0x00000249,0x0000024a,0x0000024a}, + {0x00000000,0x00000000,0x00000049,0x00000249, + 0x00000249,0x00000249,0x0000024a,0x00001252}, + {0x00000000,0x00000000,0x00000249,0x00000249, + 0x00000249,0x0000124a,0x00001252,0x00001252}, + {0x00000000,0x00000000,0x00000249,0x00000249, + 0x0000124a,0x0000124a,0x00009292,0x00009292}, + {0x00000000,0x00000000,0x00000249,0x00001249, + 0x0000124a,0x00009252,0x00009292,0x00009292}, + {0x00000000,0x00000000,0x00000249,0x00001249, + 0x00009252,0x00009292,0x00009292,0x00009292}, + {0x00000000,0x00000000,0x00000249,0x00001249, + 0x00009252,0x00009292,0x00009493,0x00009493}, + {0x00000000,0x00000000,0x00000249,0x0000924a, + 0x00009252,0x00009493,0x00009493,0x00009493}, + {0x00000000,0x00000000,0x00000249,0x0000924a, + 0x00009292,0x00009493,0x00009493,0x00009493}, + {0x00000000,0x00000000,0x00000249,0x00009252, + 0x00009492,0x00009493,0x0000a49b,0x0000a49b}, + {0x00000000,0x00000000,0x00001249,0x00009292, + 0x00009492,0x000124db,0x000124db,0x000124db}, + {0x00000000,0x00000000,0x0000924a,0x00009493, + 0x0000a493,0x000126dc,0x000126dc,0x000126dc}, + {0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000} + }, + { /* version 2, passes 1 */ + {0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000}, + {0x00000000,0x00000000,0x00000049,0x00000009, + 0x00000049,0x00000009,0x00000001,0x00000000}, + {0x00000000,0x00000000,0x00000049,0x00000049, + 0x00000049,0x00000049,0x00000049,0x00000000}, + {0x00000000,0x00000000,0x00000249,0x00000049, + 0x00000249,0x00000049,0x0000024a,0x00000001}, + {0x00000000,0x00000000,0x00000249,0x00000249, + 0x00000249,0x00000249,0x0000024a,0x00000001}, + {0x00000000,0x00000000,0x00000249,0x00000249, + 0x00000249,0x00000249,0x0000024a,0x00000001}, + {0x00000000,0x00000000,0x00000249,0x00000249, + 0x00000249,0x00000249,0x0000024a,0x00000009}, + {0x00000000,0x00000000,0x00000249,0x00000249, + 0x0000124a,0x0000124a,0x0000024a,0x00000009}, + {0x00000000,0x00000000,0x00000249,0x00000249, + 0x0000124a,0x0000124a,0x0000024a,0x00000009}, + {0x00000000,0x00000000,0x00001249,0x00001249, + 0x0000124a,0x00009252,0x00001252,0x00000049}, + {0x00000000,0x00000000,0x00001249,0x00001249, + 0x0000124a,0x00009292,0x00001252,0x00000049}, + {0x00000000,0x00000000,0x00001249,0x00001249, + 0x0000124a,0x00009292,0x00001252,0x00000049}, + {0x00000000,0x00000000,0x00001249,0x00001249, + 0x00009252,0x00009292,0x00001252,0x0000024a}, + {0x00000000,0x00000000,0x00001249,0x00001249, + 0x00009292,0x00009292,0x00001252,0x0000024a}, + {0x00000000,0x00000000,0x0000924a,0x0000924a, + 0x00009492,0x00009493,0x00009292,0x00001252}, + {0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000} + } + }, + { /* version 3 */ + { /* version 3, passes 0 */ + {0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000001}, + {0x00000000,0x00000000,0x00000049,0x00000049, + 0x00000049,0x00000049,0x00000049,0x00000049}, + {0x00000000,0x00000000,0x00000049,0x00000249, + 0x00000249,0x00000249,0x00001252,0x0000024a}, + {0x00000000,0x00000000,0x00000249,0x00000249, + 0x00000249,0x0000124a,0x00001252,0x00001252}, + {0x00000000,0x00000000,0x00000249,0x00000249, + 0x0000124a,0x00009252,0x00009292,0x00009292}, + {0x00000000,0x00000000,0x00000249,0x00001249, + 0x0000124a,0x00009292,0x00009292,0x00009493}, + {0x00000000,0x00000000,0x00000249,0x00001249, + 0x00009252,0x00009292,0x00009493,0x00009493}, + {0x00000000,0x00000000,0x00000249,0x00001249, + 0x00009292,0x00009493,0x00009493,0x00009493}, + {0x00000000,0x00000000,0x00000249,0x00009252, + 0x00009292,0x00009493,0x0000a49b,0x0000a49b}, + {0x00000000,0x00000000,0x00001249,0x00009252, + 0x00009292,0x0000a49b,0x0000a49b,0x0000a49b}, + {0x00000000,0x00000000,0x00001249,0x00009252, + 0x00009492,0x0000a49b,0x0000a49b,0x0000a49b}, + {0x00000000,0x00000000,0x00001249,0x00009292, + 0x00009492,0x0000a49b,0x000124db,0x000124db}, + {0x00000000,0x00000000,0x00001249,0x00009292, + 0x0000a493,0x0000a49b,0x000124db,0x000124db}, + {0x00000000,0x00000000,0x00001249,0x00009493, + 0x0001249b,0x000126dc,0x000136e4,0x000126dc}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x000124db,0x000136e4,0x0001b725,0x000136e4}, + {0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000} + }, + { /* version 3, passes 1 */ + {0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000}, + {0x00000000,0x00000000,0x00000049,0x00000049, + 0x00000049,0x00000049,0x00000001,0x00000000}, + {0x00000000,0x00000000,0x00000249,0x00000249, + 0x00000249,0x00000249,0x00000049,0x00000001}, + {0x00000000,0x00000000,0x00000249,0x00000249, + 0x00000249,0x0000124a,0x00001252,0x00000001}, + {0x00000000,0x00000000,0x00000249,0x00000249, + 0x0000124a,0x0000124a,0x00001252,0x00000009}, + {0x00000000,0x00000000,0x00000249,0x00001249, + 0x0000124a,0x00009252,0x00009292,0x00000009}, + {0x00000000,0x00000000,0x00001249,0x00001249, + 0x0000124a,0x00009252,0x00009292,0x00000049}, + {0x00000000,0x00000000,0x00001249,0x00001249, + 0x00009252,0x00009252,0x00009292,0x00000049}, + {0x00000000,0x00000000,0x00001249,0x00001249, + 0x00009252,0x00009493,0x00009292,0x0000024a}, + {0x00000000,0x00000000,0x00001249,0x00001249, + 0x00009252,0x00009493,0x00009292,0x0000024a}, + {0x00000000,0x00000000,0x00001249,0x00001249, + 0x00009252,0x00009493,0x00009493,0x00001252}, + {0x00000000,0x00000000,0x00001249,0x0000924a, + 0x00009292,0x00009493,0x00009493,0x00001252}, + {0x00000000,0x00000000,0x00001249,0x0000924a, + 0x00009492,0x00009493,0x00009493,0x00009292}, + {0x00000000,0x00000000,0x00001249,0x00009252, + 0x00009492,0x0000a49b,0x00009493,0x00009292}, + {0x00000000,0x00000000,0x0000924a,0x00009292, + 0x0000a493,0x000124db,0x0000a49b,0x00009493}, + {0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000} + } + }, + { /* version 4 */ + { /* version 4, passes 0 */ + {0x00000000,0x00000000,0x00000049,0x00000049, + 0x00000049,0x00000049,0x0000024a,0x0000024a}, + {0x00000000,0x00000000,0x00000249,0x00000249, + 0x00000249,0x0000124a,0x00001252,0x00009292}, + {0x00000000,0x00000000,0x00000249,0x00000249, + 0x0000124a,0x00009252,0x00009292,0x00009292}, + {0x00000000,0x00000000,0x00000249,0x00001249, + 0x0000124a,0x00009292,0x00009493,0x00009493}, + {0x00000000,0x00000000,0x00000249,0x00001249, + 0x00009252,0x00009493,0x00009493,0x0000a49b}, + {0x00000000,0x00000000,0x00000249,0x0000924a, + 0x00009292,0x00009493,0x0000a49b,0x0000a49b}, + {0x00000000,0x00000000,0x00001249,0x0000924a, + 0x00009292,0x00009493,0x0000a49b,0x000124db}, + {0x00000000,0x00000000,0x00001249,0x00009252, + 0x00009492,0x0000a49b,0x0000a49b,0x000124db}, + {0x00000000,0x00000000,0x00001249,0x00009292, + 0x00009492,0x000124db,0x000124db,0x000126dc}, + {0x00000000,0x00000000,0x00001249,0x00009292, + 0x0000a493,0x000124db,0x000126dc,0x000126dc}, + {0x00000000,0x00000000,0x00001249,0x00009493, + 0x0000a493,0x000124db,0x000126dc,0x000136e4}, + {0x00000000,0x00000000,0x00001249,0x00009493, + 0x0000a493,0x000126dc,0x000136e4,0x000136e4}, + {0x00000000,0x00000000,0x0000924a,0x00009493, + 0x0001249b,0x000126dc,0x000136e4,0x000136e4}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x000124db,0x000136e4,0x000136e4,0x0001b724}, + {0x00000000,0x00000000,0x00009252,0x000124db, + 0x000126dc,0x0001b724,0x0001b725,0x0001b925}, + {0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000} + }, + { /* version 4, passes 1 */ + {0x00000000,0x00000000,0x00000049,0x00000049, + 0x00000049,0x00000049,0x00000049,0x00000049}, + {0x00000000,0x00000000,0x00000249,0x00000249, + 0x00000249,0x00000249,0x0000024a,0x00000049}, + {0x00000000,0x00000000,0x00001249,0x00000249, + 0x0000124a,0x0000124a,0x00001252,0x00000049}, + {0x00000000,0x00000000,0x00001249,0x00001249, + 0x0000124a,0x0000124a,0x00009292,0x0000024a}, + {0x00000000,0x00000000,0x00001249,0x00001249, + 0x00009252,0x00009292,0x00009292,0x0000024a}, + {0x00000000,0x00000000,0x00001249,0x00001249, + 0x00009252,0x00009292,0x0000a49b,0x0000024a}, + {0x00000000,0x00000000,0x00001249,0x00001249, + 0x00009292,0x00009493,0x0000a49b,0x00001252}, + {0x00000000,0x00000000,0x00001249,0x00001249, + 0x00009292,0x00009493,0x0000a49b,0x00001252}, + {0x00000000,0x00000000,0x00001249,0x0000924a, + 0x00009492,0x0000a49b,0x0000a49b,0x00001252}, + {0x00000000,0x00000000,0x00001249,0x00009252, + 0x00009492,0x0000a49b,0x0000a49b,0x00009292}, + {0x00000000,0x00000000,0x00001249,0x00009292, + 0x00009492,0x0000a49b,0x0000a49b,0x00009292}, + {0x00000000,0x00000000,0x00001249,0x00009493, + 0x0000a493,0x0000a49b,0x0000a49b,0x00009292}, + {0x00000000,0x00000000,0x00001249,0x00009493, + 0x0000a493,0x0000a49b,0x0000a49b,0x00009493}, + {0x00000000,0x00000000,0x0000924a,0x00009493, + 0x0000a493,0x000124db,0x0000a49b,0x00009493}, + {0x00000000,0x00000000,0x00009252,0x0000a49b, + 0x0001249b,0x000126dc,0x000124db,0x0000a49b}, + {0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000} + } + }, + { /* version 5 */ + { /* version 5, passes 0 */ + {0x00000000,0x00000000,0x00000249,0x00000249, + 0x00000249,0x0000124a,0x00001252,0x00009292}, + {0x00000000,0x00000000,0x00000249,0x00001249, + 0x0000124a,0x00009292,0x00009292,0x00009493}, + {0x00000000,0x00000000,0x00000249,0x0000924a, + 0x00009292,0x00009493,0x0000a49b,0x0000a49b}, + {0x00000000,0x00000000,0x00001249,0x0000924a, + 0x00009292,0x00009493,0x0000a49b,0x0000a49b}, + {0x00000000,0x00000000,0x00001249,0x0000924a, + 0x00009492,0x0000a49b,0x0000a49b,0x000124db}, + {0x00000000,0x00000000,0x00001249,0x00009292, + 0x00009492,0x0000a49b,0x000124db,0x000124db}, + {0x00000000,0x00000000,0x00001249,0x00009292, + 0x0000a493,0x000124db,0x000124db,0x000126dc}, + {0x00000000,0x00000000,0x00001249,0x00009493, + 0x0000a493,0x000124db,0x000126dc,0x000126dc}, + {0x00000000,0x00000000,0x00001249,0x00009493, + 0x0000a493,0x000126dc,0x000136e4,0x000136e4}, + {0x00000000,0x00000000,0x00001249,0x00009493, + 0x0001249b,0x000126dc,0x000136e4,0x000136e4}, + {0x00000000,0x00000000,0x00001249,0x00009493, + 0x0001249b,0x000126dc,0x000136e4,0x000136e4}, + {0x00000000,0x00000000,0x0000924a,0x00009493, + 0x0001249b,0x000126dc,0x0001b725,0x0001b724}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x000124db,0x000126dc,0x0001b725,0x0001b724}, + {0x00000000,0x00000000,0x00009292,0x0000a49b, + 0x000126dc,0x000136e4,0x0001b92d,0x0001b925}, + {0x00000000,0x00000000,0x00009492,0x000124db, + 0x000136e4,0x0001b724,0x0001c96e,0x0001c92d}, + {0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000} + }, + { /* version 5, passes 1 */ + {0x00000000,0x00000000,0x00000249,0x00000249, + 0x0000124a,0x00000249,0x0000024a,0x0000024a}, + {0x00000000,0x00000000,0x00001249,0x00001249, + 0x0000124a,0x0000124a,0x00001252,0x0000024a}, + {0x00000000,0x00000000,0x00001249,0x00001249, + 0x00009292,0x00009493,0x00009493,0x0000024a}, + {0x00000000,0x00000000,0x00001249,0x00001249, + 0x00009292,0x00009493,0x00009493,0x00001252}, + {0x00000000,0x00000000,0x00001249,0x00001249, + 0x00009292,0x00009493,0x0000a49b,0x00001252}, + {0x00000000,0x00000000,0x00001249,0x0000924a, + 0x00009492,0x00009493,0x000124db,0x00001252}, + {0x00000000,0x00000000,0x00001249,0x00009292, + 0x00009492,0x00009493,0x000124db,0x00009292}, + {0x00000000,0x00000000,0x00001249,0x00009292, + 0x00009492,0x0000a49b,0x000124db,0x00009292}, + {0x00000000,0x00000000,0x00001249,0x00009493, + 0x0000a493,0x0000a49b,0x000124db,0x00009292}, + {0x00000000,0x00000000,0x00001249,0x00009493, + 0x0000a493,0x000124db,0x000124db,0x00009493}, + {0x00000000,0x00000000,0x0000924a,0x00009493, + 0x0000a493,0x000124db,0x000124db,0x00009493}, + {0x00000000,0x00000000,0x0000924a,0x00009493, + 0x0000a493,0x000124db,0x000124db,0x00009493}, + {0x00000000,0x00000000,0x0000924a,0x00009493, + 0x0000a493,0x000124db,0x000124db,0x0000a49b}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x000124db,0x000126dc,0x000124db,0x0000a49b}, + {0x00000000,0x00000000,0x00009252,0x000124db, + 0x000126dc,0x000136e4,0x000126dc,0x000124db}, + {0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000} + } + }, + { /* version 6 */ + { /* version 6, passes 0 */ + {0x00000000,0x00000000,0x00000249,0x00000249, + 0x0000124a,0x0000124a,0x00009292,0x00009292}, + {0x00000000,0x00000000,0x00001249,0x00001249, + 0x00009292,0x00009493,0x0000a49b,0x0000a49b}, + {0x00000000,0x00000000,0x00001249,0x0000924a, + 0x00009492,0x0000a49b,0x0000a49b,0x000124db}, + {0x00000000,0x00000000,0x00001249,0x00009292, + 0x00009492,0x000124db,0x000126dc,0x000126dc}, + {0x00000000,0x00000000,0x00001249,0x00009493, + 0x0000a493,0x000124db,0x000126dc,0x000126dc}, + {0x00000000,0x00000000,0x00001249,0x00009493, + 0x0000a493,0x000126dc,0x000136e4,0x000136e4}, + {0x00000000,0x00000000,0x00001249,0x00009493, + 0x0000a493,0x000126dc,0x000136e4,0x0001b724}, + {0x00000000,0x00000000,0x00001249,0x00009493, + 0x0001249b,0x000126dc,0x000136e4,0x0001b724}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x0001249b,0x000126dc,0x000136e4,0x0001b724}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x0001249b,0x000136e4,0x0001b725,0x0001b724}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x000124db,0x000136e4,0x0001b725,0x0001b925}, + {0x00000000,0x00000000,0x00009292,0x0000a49b, + 0x000126dc,0x000136e4,0x0001b92d,0x0001b925}, + {0x00000000,0x00000000,0x00009292,0x0000a49b, + 0x000126dc,0x0001b724,0x0001b92d,0x0001c92d}, + {0x00000000,0x00000000,0x00009492,0x000124db, + 0x000126dc,0x0001b724,0x0001c96e,0x0001c92d}, + {0x00000000,0x00000000,0x0000a492,0x000126db, + 0x000136e4,0x0001b925,0x00025bb6,0x00024b77}, + {0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000} + }, + { /* version 6, passes 1 */ + {0x00000000,0x00000000,0x00001249,0x00000249, + 0x0000124a,0x0000124a,0x00001252,0x00001252}, + {0x00000000,0x00000000,0x00001249,0x00001249, + 0x00009252,0x00009292,0x00009292,0x00001252}, + {0x00000000,0x00000000,0x00001249,0x0000924a, + 0x00009492,0x00009493,0x0000a49b,0x00001252}, + {0x00000000,0x00000000,0x00001249,0x00009252, + 0x00009492,0x0000a49b,0x0000a49b,0x00009292}, + {0x00000000,0x00000000,0x00001249,0x00009292, + 0x00009492,0x0000a49b,0x0000a49b,0x00009292}, + {0x00000000,0x00000000,0x00001249,0x00009493, + 0x0000a493,0x0000a49b,0x000126dc,0x00009292}, + {0x00000000,0x00000000,0x0000924a,0x00009493, + 0x0000a493,0x0000a49b,0x000126dc,0x00009493}, + {0x00000000,0x00000000,0x0000924a,0x00009493, + 0x0000a493,0x0000a49b,0x000126dc,0x00009493}, + {0x00000000,0x00000000,0x0000924a,0x00009493, + 0x0000a493,0x000124db,0x000126dc,0x00009493}, + {0x00000000,0x00000000,0x0000924a,0x00009493, + 0x0000a493,0x000124db,0x000126dc,0x0000a49b}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x0000a493,0x000124db,0x000126dc,0x0000a49b}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x0001249b,0x000126dc,0x000126dc,0x0000a49b}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x000124db,0x000136e4,0x000126dc,0x000124db}, + {0x00000000,0x00000000,0x00009492,0x0000a49b, + 0x000136e4,0x000136e4,0x000126dc,0x000124db}, + {0x00000000,0x00000000,0x0000a492,0x000124db, + 0x0001b724,0x0001b724,0x000136e4,0x000126dc}, + {0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000} + } + }, + { /* version 7 */ + { /* version 7, passes 0 */ + {0x00000000,0x00000000,0x00001249,0x00001249, + 0x00009292,0x00009493,0x0000a49b,0x000124db}, + {0x00000000,0x00000000,0x00001249,0x00009292, + 0x0000a493,0x0000a49b,0x000124db,0x000126dc}, + {0x00000000,0x00000000,0x00001249,0x00009493, + 0x0000a493,0x000124db,0x000126dc,0x000136e4}, + {0x00000000,0x00000000,0x00001249,0x00009493, + 0x0000a493,0x000124db,0x000136e4,0x000136e4}, + {0x00000000,0x00000000,0x00001249,0x00009493, + 0x0001249b,0x000126dc,0x000136e4,0x000136e4}, + {0x00000000,0x00000000,0x00001249,0x0000a49b, + 0x0001249b,0x000126dc,0x000136e4,0x0001b724}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x0001249b,0x000126dc,0x000136e4,0x0001b724}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x000124db,0x000136e4,0x0001b725,0x0001b724}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x000126dc,0x000136e4,0x0001b725,0x0001b925}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x000126dc,0x0001b724,0x0001b92d,0x0001b925}, + {0x00000000,0x00000000,0x00009292,0x0000a49b, + 0x000126dc,0x0001b724,0x0001c96e,0x0001c92d}, + {0x00000000,0x00000000,0x00009292,0x000124db, + 0x000126dc,0x0001b724,0x0001c96e,0x0001c92d}, + {0x00000000,0x00000000,0x00009492,0x000124db, + 0x000136e4,0x0001b724,0x0001c96e,0x0002496e}, + {0x00000000,0x00000000,0x00009492,0x000126db, + 0x000136e4,0x0001b925,0x0001c96e,0x0002496e}, + {0x00000000,0x00000000,0x0000a492,0x000136db, + 0x0001b724,0x0002496d,0x00025bb6,0x00025bbf}, + {0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000} + }, + { /* version 7, passes 1 */ + {0x00000000,0x00000000,0x00001249,0x00001249, + 0x00009252,0x00009292,0x00009292,0x00009292}, + {0x00000000,0x00000000,0x00001249,0x0000924a, + 0x00009492,0x00009493,0x00009493,0x00009292}, + {0x00000000,0x00000000,0x00001249,0x00009493, + 0x0000a493,0x0000a49b,0x0000a49b,0x00009292}, + {0x00000000,0x00000000,0x0000924a,0x00009493, + 0x0000a493,0x0000a49b,0x000124db,0x00009493}, + {0x00000000,0x00000000,0x0000924a,0x00009493, + 0x0000a493,0x000124db,0x000124db,0x00009493}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x0000a493,0x000124db,0x000136e4,0x00009493}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x0000a493,0x000124db,0x000136e4,0x0000a49b}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x0001249b,0x000124db,0x000136e4,0x0000a49b}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x0001249b,0x000126dc,0x000136e4,0x0000a49b}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x0001249b,0x000126dc,0x000136e4,0x000124db}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x000126dc,0x000136e4,0x000136e4,0x000124db}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x000126dc,0x000136e4,0x000136e4,0x000124db}, + {0x00000000,0x00000000,0x0000924a,0x000124db, + 0x000136e4,0x000136e4,0x000136e4,0x000126dc}, + {0x00000000,0x00000000,0x0000a492,0x000124db, + 0x000136e4,0x0001b724,0x000136e4,0x000126dc}, + {0x00000000,0x00000000,0x00012492,0x000126db, + 0x0001b724,0x0001b925,0x0001b725,0x000136e4}, + {0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000} + } + }, + { /* version 8 */ + { /* version 8, passes 0 */ + {0x00000000,0x00000000,0x00001249,0x00001249, + 0x00009292,0x00009493,0x0000a49b,0x000124db}, + {0x00000000,0x00000000,0x00001249,0x00009292, + 0x0000a493,0x000124db,0x000126dc,0x000126dc}, + {0x00000000,0x00000000,0x00001249,0x00009493, + 0x0000a493,0x000124db,0x000126dc,0x000136e4}, + {0x00000000,0x00000000,0x00001249,0x0000a49b, + 0x0001249b,0x000126dc,0x000136e4,0x0001b724}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x0001249b,0x000126dc,0x000136e4,0x0001b724}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x000124db,0x000136e4,0x0001b725,0x0001b724}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x000126dc,0x000136e4,0x0001b725,0x0001b925}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x000126dc,0x0001b724,0x0001b92d,0x0001c92d}, + {0x00000000,0x00000000,0x00009252,0x000124db, + 0x000126dc,0x0001b724,0x0001b92d,0x0001c92d}, + {0x00000000,0x00000000,0x00009292,0x000124db, + 0x000126dc,0x0001b925,0x0001c96e,0x0001c92d}, + {0x00000000,0x00000000,0x00009492,0x000124db, + 0x000136e4,0x0001b925,0x0001c96e,0x0001c92d}, + {0x00000000,0x00000000,0x00009492,0x000124db, + 0x000136e4,0x0001b925,0x00024b76,0x00024b77}, + {0x00000000,0x00000000,0x00009492,0x000126db, + 0x000136e4,0x0001b925,0x00024b76,0x00025bbf}, + {0x00000000,0x00000000,0x0000a492,0x000126db, + 0x000136e4,0x0001c92d,0x00024b76,0x00025bbf}, + {0x00000000,0x00000000,0x00012492,0x000136db, + 0x0001b724,0x00024b6d,0x0002ddb6,0x0002efff}, + {0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000} + }, + { /* version 8, passes 1 */ + {0x00000000,0x00000000,0x00001249,0x00001249, + 0x00009252,0x00009493,0x00009493,0x00009493}, + {0x00000000,0x00000000,0x00001249,0x00009292, + 0x0000a493,0x0000a49b,0x0000a49b,0x00009493}, + {0x00000000,0x00000000,0x0000924a,0x00009493, + 0x0000a493,0x0000a49b,0x000124db,0x00009493}, + {0x00000000,0x00000000,0x0000924a,0x00009493, + 0x0000a493,0x000124db,0x000126dc,0x0000a49b}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x0000a493,0x000124db,0x000126dc,0x0000a49b}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x0000a493,0x000124db,0x000136e4,0x000124db}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x0001249b,0x000126dc,0x000136e4,0x000124db}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x000126dc,0x000126dc,0x000136e4,0x000126dc}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x000126dc,0x000136e4,0x000136e4,0x000126dc}, + {0x00000000,0x00000000,0x0000924a,0x000124db, + 0x000126dc,0x000136e4,0x000136e4,0x000126dc}, + {0x00000000,0x00000000,0x0000924a,0x000124db, + 0x000126dc,0x000136e4,0x000136e4,0x000136e4}, + {0x00000000,0x00000000,0x00009292,0x000124db, + 0x000136e4,0x0001b724,0x0001b725,0x000136e4}, + {0x00000000,0x00000000,0x00009492,0x000126db, + 0x000136e4,0x0001b925,0x0001b725,0x0001b724}, + {0x00000000,0x00000000,0x00009492,0x000126db, + 0x000136e4,0x0001b925,0x0001b725,0x0001b724}, + {0x00000000,0x00000000,0x0000a492,0x000136db, + 0x0001b724,0x0002496d,0x0001b92d,0x0001b925}, + {0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000} + } + }, + { /* version 9 */ + { /* version 9, passes 0 */ + {0x00000000,0x00000000,0x00000049,0x00000049, + 0x00000049,0x00000049,0x00000049,0x00000049}, + {0x00000000,0x00000000,0x00000249,0x00000049, + 0x00000249,0x00000249,0x0000024a,0x00000049}, + {0x00000000,0x00000000,0x00000249,0x00000249, + 0x0000124a,0x00009252,0x00001252,0x0000024a}, + {0x00000000,0x00000000,0x00001249,0x00001249, + 0x00009252,0x00009292,0x00009493,0x00001252}, + {0x00000000,0x00000000,0x00001249,0x0000924a, + 0x00009292,0x00009493,0x00009493,0x00001252}, + {0x00000000,0x00000000,0x00001249,0x00009292, + 0x00009492,0x0000a49b,0x0000a49b,0x00009292}, + {0x00000000,0x00000000,0x00001249,0x00009493, + 0x0000a493,0x000124db,0x000124db,0x00009493}, + {0x00000000,0x00000000,0x0000924a,0x00009493, + 0x0000a493,0x000124db,0x000126dc,0x0000a49b}, + {0x00000000,0x00000000,0x0000924a,0x00009493, + 0x0000a493,0x000124db,0x000126dc,0x0000a49b}, + {0x00000000,0x00000000,0x0000924a,0x00009493, + 0x0001249b,0x000126dc,0x000126dc,0x000124db}, + {0x00000000,0x00000000,0x00009252,0x00009493, + 0x000124db,0x000136e4,0x000136e4,0x000126dc}, + {0x00000000,0x00000000,0x00009252,0x0000a49b, + 0x000124db,0x000136e4,0x000136e4,0x000126dc}, + {0x00000000,0x00000000,0x00009292,0x0000a49b, + 0x000126dc,0x000136e4,0x000136e4,0x000136e4}, + {0x00000000,0x00000000,0x00009492,0x0000a49b, + 0x000126dc,0x0001b724,0x0001b725,0x0001b724}, + {0x00000000,0x00000000,0x0000a492,0x000124db, + 0x000136e4,0x0001b925,0x0001b92d,0x0001b925}, + {0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000} + }, + { /* version 9, passes 1 */ + {0x00000000,0x00000000,0x00000249,0x00000049, + 0x00000009,0x00000009,0x00000009,0x00000009}, + {0x00000000,0x00000000,0x00000249,0x00000249, + 0x00000049,0x00000049,0x00000009,0x00000009}, + {0x00000000,0x00000000,0x00001249,0x00001249, + 0x0000124a,0x00000249,0x00000049,0x00000049}, + {0x00000000,0x00000000,0x00001249,0x00001249, + 0x0000124a,0x0000124a,0x00000049,0x00000049}, + {0x00000000,0x00000000,0x00001249,0x00001249, + 0x00009252,0x0000124a,0x0000024a,0x0000024a}, + {0x00000000,0x00000000,0x00001249,0x0000924a, + 0x00009252,0x0000124a,0x0000024a,0x0000024a}, + {0x00000000,0x00000000,0x00001249,0x00009292, + 0x00009492,0x00009252,0x00001252,0x00001252}, + {0x00000000,0x00000000,0x00001249,0x00009493, + 0x0000a493,0x00009292,0x00009292,0x00001252}, + {0x00000000,0x00000000,0x0000924a,0x00009493, + 0x0000a493,0x00009292,0x00009292,0x00009292}, + {0x00000000,0x00000000,0x0000924a,0x00009493, + 0x0000a493,0x00009493,0x00009493,0x00009292}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x0000a493,0x0000a49b,0x00009493,0x00009493}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x0000a493,0x0000a49b,0x0000a49b,0x00009493}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x0001249b,0x000124db,0x0000a49b,0x0000a49b}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x000136e4,0x000126dc,0x000124db,0x0000a49b}, + {0x00000000,0x00000000,0x00009252,0x000124db, + 0x0001b724,0x000136e4,0x000126dc,0x000124db}, + {0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000} + } + }, + { /* version 10 */ + { /* version 10, passes 0 */ + {0x00000000,0x00000000,0x00000249,0x00000249, + 0x00000249,0x00000249,0x0000024a,0x0000024a}, + {0x00000000,0x00000000,0x00000249,0x00001249, + 0x00009252,0x00009292,0x00009292,0x0000024a}, + {0x00000000,0x00000000,0x00001249,0x00001249, + 0x00009252,0x00009292,0x00009292,0x00001252}, + {0x00000000,0x00000000,0x00001249,0x0000924a, + 0x00009492,0x00009493,0x0000a49b,0x00009292}, + {0x00000000,0x00000000,0x00001249,0x00009292, + 0x00009492,0x000124db,0x000124db,0x00009292}, + {0x00000000,0x00000000,0x00001249,0x00009493, + 0x0000a493,0x000124db,0x000124db,0x00009493}, + {0x00000000,0x00000000,0x00001249,0x00009493, + 0x0000a493,0x000124db,0x000126dc,0x0000a49b}, + {0x00000000,0x00000000,0x0000924a,0x00009493, + 0x0000a493,0x000124db,0x000126dc,0x000124db}, + {0x00000000,0x00000000,0x0000924a,0x00009493, + 0x0001249b,0x000126dc,0x000126dc,0x000124db}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x000124db,0x000126dc,0x000136e4,0x000126dc}, + {0x00000000,0x00000000,0x00009252,0x0000a49b, + 0x000124db,0x000136e4,0x000136e4,0x000136e4}, + {0x00000000,0x00000000,0x00009292,0x0000a49b, + 0x000126dc,0x000136e4,0x000136e4,0x000136e4}, + {0x00000000,0x00000000,0x00009492,0x0000a49b, + 0x000126dc,0x0001b724,0x0001b92d,0x0001b724}, + {0x00000000,0x00000000,0x00009492,0x000124db, + 0x000126dc,0x0001b925,0x0001b92d,0x0001b925}, + {0x00000000,0x00000000,0x0000a492,0x000126db, + 0x000136e4,0x0002496d,0x0001c96e,0x0001c92d}, + {0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000} + }, + { /* version 10, passes 1 */ + {0x00000000,0x00000000,0x00000249,0x00000249, + 0x00000049,0x00000049,0x00000049,0x00000049}, + {0x00000000,0x00000000,0x00001249,0x00001249, + 0x0000124a,0x00000249,0x00000049,0x00000049}, + {0x00000000,0x00000000,0x00001249,0x00001249, + 0x0000124a,0x00009252,0x0000024a,0x00000049}, + {0x00000000,0x00000000,0x00001249,0x00001249, + 0x00009252,0x00009493,0x0000024a,0x0000024a}, + {0x00000000,0x00000000,0x00001249,0x00009252, + 0x00009492,0x00009493,0x00001252,0x0000024a}, + {0x00000000,0x00000000,0x00001249,0x00009292, + 0x00009492,0x00009493,0x00001252,0x00001252}, + {0x00000000,0x00000000,0x0000924a,0x00009493, + 0x00009492,0x00009493,0x00009292,0x00001252}, + {0x00000000,0x00000000,0x0000924a,0x00009493, + 0x0000a493,0x00009493,0x00009292,0x00009292}, + {0x00000000,0x00000000,0x0000924a,0x00009493, + 0x0000a493,0x0000a49b,0x00009493,0x00009292}, + {0x00000000,0x00000000,0x0000924a,0x00009493, + 0x0000a493,0x0000a49b,0x00009493,0x00009292}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x0000a493,0x000124db,0x0000a49b,0x00009493}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x0000a493,0x000124db,0x0000a49b,0x00009493}, + {0x00000000,0x00000000,0x0000924a,0x000124db, + 0x000136e4,0x000126dc,0x000124db,0x0000a49b}, + {0x00000000,0x00000000,0x0000924a,0x000124db, + 0x000136e4,0x000126dc,0x000124db,0x0000a49b}, + {0x00000000,0x00000000,0x00009252,0x000126db, + 0x0001b724,0x000136e4,0x000126dc,0x000124db}, + {0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000} + } + }, + { /* version 11 */ + { /* version 11, passes 0 */ + {0x00000000,0x00000000,0x00000249,0x00000249, + 0x00000249,0x00000249,0x00001252,0x00001252}, + {0x00000000,0x00000000,0x00001249,0x00001249, + 0x00009252,0x00009292,0x00009292,0x00001252}, + {0x00000000,0x00000000,0x00001249,0x0000924a, + 0x00009492,0x0000a49b,0x0000a49b,0x00009292}, + {0x00000000,0x00000000,0x00001249,0x00009493, + 0x0000a493,0x0000a49b,0x000124db,0x00009493}, + {0x00000000,0x00000000,0x00001249,0x00009493, + 0x0000a493,0x000124db,0x000126dc,0x00009493}, + {0x00000000,0x00000000,0x0000924a,0x00009493, + 0x0000a493,0x000126dc,0x000126dc,0x0000a49b}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x0001249b,0x000126dc,0x000136e4,0x000124db}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x000126dc,0x000136e4,0x000136e4,0x000126dc}, + {0x00000000,0x00000000,0x00009292,0x0000a49b, + 0x000126dc,0x000136e4,0x000136e4,0x000126dc}, + {0x00000000,0x00000000,0x00009292,0x0000a49b, + 0x000126dc,0x0001b724,0x0001b725,0x000136e4}, + {0x00000000,0x00000000,0x00009292,0x0000a49b, + 0x000136e4,0x0001b724,0x0001b92d,0x0001b724}, + {0x00000000,0x00000000,0x00009492,0x0000a49b, + 0x000136e4,0x0001b724,0x0001b92d,0x0001b724}, + {0x00000000,0x00000000,0x00009492,0x000124db, + 0x000136e4,0x0001b925,0x0001c96e,0x0001b925}, + {0x00000000,0x00000000,0x00009492,0x000124db, + 0x0001b724,0x0001b925,0x0001c96e,0x0001c92d}, + {0x00000000,0x00000000,0x0000a492,0x000126db, + 0x0001c924,0x0002496d,0x00025bb6,0x00024b77}, + {0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000} + }, + { /* version 11, passes 1 */ + {0x00000000,0x00000000,0x00001249,0x00000249, + 0x00000249,0x00000249,0x0000024a,0x0000024a}, + {0x00000000,0x00000000,0x00001249,0x00001249, + 0x0000124a,0x0000124a,0x0000024a,0x0000024a}, + {0x00000000,0x00000000,0x00001249,0x0000924a, + 0x00009252,0x00009252,0x0000024a,0x0000024a}, + {0x00000000,0x00000000,0x00001249,0x00009292, + 0x00009492,0x0000a49b,0x00001252,0x00001252}, + {0x00000000,0x00000000,0x0000924a,0x00009493, + 0x0000a493,0x0000a49b,0x00001252,0x00001252}, + {0x00000000,0x00000000,0x0000924a,0x00009493, + 0x0000a493,0x0000a49b,0x00009292,0x00001252}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x0000a493,0x0000a49b,0x00009292,0x00009292}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x0000a493,0x0000a49b,0x00009493,0x00009292}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x0001249b,0x000124db,0x00009493,0x00009292}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x0001249b,0x000124db,0x00009493,0x00009493}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x000124db,0x000124db,0x0000a49b,0x00009493}, + {0x00000000,0x00000000,0x0000924a,0x000124db, + 0x000126dc,0x000126dc,0x0000a49b,0x00009493}, + {0x00000000,0x00000000,0x0000924a,0x000124db, + 0x000136e4,0x000126dc,0x000124db,0x0000a49b}, + {0x00000000,0x00000000,0x00009292,0x000124db, + 0x000136e4,0x000126dc,0x000124db,0x0000a49b}, + {0x00000000,0x00000000,0x00009492,0x000126db, + 0x0001b724,0x000136e4,0x000126dc,0x000124db}, + {0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000} + } + }, + { /* version 12 */ + { /* version 12, passes 0 */ + {0x00000000,0x00000000,0x00001249,0x00001249, + 0x00009252,0x00009292,0x00009493,0x00009493}, + {0x00000000,0x00000000,0x00001249,0x00009292, + 0x0000a493,0x0000a49b,0x0000a49b,0x00009493}, + {0x00000000,0x00000000,0x00001249,0x00009493, + 0x0000a493,0x000124db,0x000124db,0x0000a49b}, + {0x00000000,0x00000000,0x0000924a,0x00009493, + 0x0000a493,0x000126dc,0x000126dc,0x0000a49b}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x0001249b,0x000126dc,0x000136e4,0x000124db}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x000126dc,0x000136e4,0x000136e4,0x000126dc}, + {0x00000000,0x00000000,0x00009292,0x0000a49b, + 0x000126dc,0x0001b724,0x0001b725,0x000126dc}, + {0x00000000,0x00000000,0x00009292,0x0000a49b, + 0x000136e4,0x0001b724,0x0001b92d,0x000136e4}, + {0x00000000,0x00000000,0x00009492,0x0000a49b, + 0x000136e4,0x0001b724,0x0001b92d,0x0001b724}, + {0x00000000,0x00000000,0x00009492,0x000124db, + 0x000136e4,0x0001b724,0x0001b92d,0x0001b724}, + {0x00000000,0x00000000,0x00009492,0x000124db, + 0x000136e4,0x0001b925,0x0001b92d,0x0001b925}, + {0x00000000,0x00000000,0x00009492,0x000124db, + 0x0001b724,0x0001b925,0x0001c96e,0x0001c92d}, + {0x00000000,0x00000000,0x0000a492,0x000124db, + 0x0001b724,0x0001c92d,0x0001c96e,0x0001c92d}, + {0x00000000,0x00000000,0x0000a492,0x000124db, + 0x0001b724,0x0001c92d,0x00024b76,0x0002496e}, + {0x00000000,0x00000000,0x00012492,0x000126db, + 0x0001c924,0x00024b6d,0x0002ddb6,0x00025bbf}, + {0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000} + }, + { /* version 12, passes 1 */ + {0x00000000,0x00000000,0x00001249,0x00001249, + 0x0000124a,0x0000124a,0x00001252,0x00001252}, + {0x00000000,0x00000000,0x00001249,0x00009292, + 0x00009492,0x00009252,0x00001252,0x00001252}, + {0x00000000,0x00000000,0x0000924a,0x00009493, + 0x0000a493,0x00009292,0x00001252,0x00001252}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x0000a493,0x0000a49b,0x00009292,0x00009292}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x0000a493,0x0000a49b,0x00009292,0x00009292}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x0001249b,0x0000a49b,0x00009493,0x00009292}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x000124db,0x000124db,0x00009493,0x00009493}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x000124db,0x000124db,0x0000a49b,0x00009493}, + {0x00000000,0x00000000,0x0000924a,0x000124db, + 0x000126dc,0x000124db,0x0000a49b,0x00009493}, + {0x00000000,0x00000000,0x0000924a,0x000124db, + 0x000126dc,0x000126dc,0x0000a49b,0x0000a49b}, + {0x00000000,0x00000000,0x0000924a,0x000124db, + 0x000136e4,0x000126dc,0x000124db,0x0000a49b}, + {0x00000000,0x00000000,0x00009492,0x000126db, + 0x000136e4,0x000126dc,0x000124db,0x0000a49b}, + {0x00000000,0x00000000,0x00009492,0x000126db, + 0x0001b724,0x000136e4,0x000126dc,0x000124db}, + {0x00000000,0x00000000,0x00009492,0x000126db, + 0x0001b724,0x000136e4,0x000126dc,0x000124db}, + {0x00000000,0x00000000,0x0000a492,0x000136db, + 0x0001c924,0x0001b724,0x000136e4,0x000126dc}, + {0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000} + } + }, + { /* version 13 */ + { /* version 13, passes 0 */ + {0x00000000,0x00000000,0x00001249,0x00001249, + 0x00009252,0x00009292,0x00009493,0x00009493}, + {0x00000000,0x00000000,0x00001249,0x00009493, + 0x0000a493,0x000124db,0x000126dc,0x00009493}, + {0x00000000,0x00000000,0x00001249,0x0000a49b, + 0x0001249b,0x000126dc,0x000126dc,0x0000a49b}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x0001249b,0x000126dc,0x000136e4,0x0000a49b}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x000126dc,0x000136e4,0x0001b725,0x000124db}, + {0x00000000,0x00000000,0x00009292,0x0000a49b, + 0x000136e4,0x0001b724,0x0001b725,0x000126dc}, + {0x00000000,0x00000000,0x00009292,0x000124db, + 0x000136e4,0x0001b724,0x0001b725,0x000126dc}, + {0x00000000,0x00000000,0x00009492,0x000124db, + 0x000136e4,0x0001b724,0x0001c96e,0x000136e4}, + {0x00000000,0x00000000,0x00009492,0x000124db, + 0x000136e4,0x0001c92d,0x0001c96e,0x0001b724}, + {0x00000000,0x00000000,0x0000a492,0x000124db, + 0x000136e4,0x0001c92d,0x0001c96e,0x0001b724}, + {0x00000000,0x00000000,0x0000a492,0x000124db, + 0x0001b724,0x0001c92d,0x0001c96e,0x0001b925}, + {0x00000000,0x00000000,0x0000a492,0x000126db, + 0x0001b724,0x0001c92d,0x00024b76,0x0001c92d}, + {0x00000000,0x00000000,0x0000a492,0x000126db, + 0x0001b924,0x0001c92d,0x00024b76,0x0001c92d}, + {0x00000000,0x00000000,0x0000a492,0x000126db, + 0x0001b924,0x0001c92d,0x00024b76,0x0002496e}, + {0x00000000,0x00000000,0x00012492,0x000136db, + 0x00024924,0x00024b6d,0x0002ddb6,0x00025bbf}, + {0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000} + }, + { /* version 13, passes 1 */ + {0x00000000,0x00000000,0x00001249,0x00001249, + 0x0000124a,0x0000124a,0x00001252,0x00001252}, + {0x00000000,0x00000000,0x0000924a,0x00009493, + 0x00009492,0x00009292,0x00001252,0x00001252}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x0000a493,0x0000a49b,0x00001252,0x00001252}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x0000a493,0x0000a49b,0x00009292,0x00009292}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x0000a493,0x0000a49b,0x00009292,0x00009292}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x000126dc,0x0000a49b,0x00009493,0x00009292}, + {0x00000000,0x00000000,0x0000924a,0x000124db, + 0x000126dc,0x000124db,0x00009493,0x00009493}, + {0x00000000,0x00000000,0x0000924a,0x000124db, + 0x000136e4,0x000124db,0x0000a49b,0x00009493}, + {0x00000000,0x00000000,0x0000924a,0x000136db, + 0x0001b724,0x000124db,0x0000a49b,0x00009493}, + {0x00000000,0x00000000,0x0000924a,0x000136db, + 0x0001b724,0x000126dc,0x0000a49b,0x0000a49b}, + {0x00000000,0x00000000,0x00009292,0x000136db, + 0x0001b724,0x000126dc,0x000124db,0x0000a49b}, + {0x00000000,0x00000000,0x00009492,0x000136db, + 0x0001b724,0x000126dc,0x000124db,0x0000a49b}, + {0x00000000,0x00000000,0x0000a492,0x000136db, + 0x0001b724,0x000136e4,0x000126dc,0x000124db}, + {0x00000000,0x00000000,0x0000a492,0x000136db, + 0x0001b724,0x000136e4,0x000126dc,0x000124db}, + {0x00000000,0x00000000,0x00012492,0x0001b6db, + 0x0001c924,0x0001b724,0x000136e4,0x000126dc}, + {0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000} + } + }, + { /* version 14 */ + { /* version 14, passes 0 */ + {0x00000000,0x00000000,0x00001249,0x0000924a, + 0x00009292,0x00009493,0x00009493,0x00009493}, + {0x00000000,0x00000000,0x00001249,0x0000a49b, + 0x0000a493,0x000124db,0x000126dc,0x00009493}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x0001249b,0x000126dc,0x000136e4,0x0000a49b}, + {0x00000000,0x00000000,0x0000924a,0x000124db, + 0x000126dc,0x000136e4,0x0001b725,0x000124db}, + {0x00000000,0x00000000,0x00009292,0x000124db, + 0x000126dc,0x0001b724,0x0001b92d,0x000126dc}, + {0x00000000,0x00000000,0x00009492,0x000124db, + 0x000136e4,0x0001b724,0x0001b92d,0x000126dc}, + {0x00000000,0x00000000,0x00009492,0x000124db, + 0x000136e4,0x0001c92d,0x0001c96e,0x000136e4}, + {0x00000000,0x00000000,0x00009492,0x000124db, + 0x0001b724,0x0001c92d,0x0001c96e,0x0001b724}, + {0x00000000,0x00000000,0x0000a492,0x000124db, + 0x0001b724,0x0001c92d,0x00024b76,0x0001b925}, + {0x00000000,0x00000000,0x0000a492,0x000126db, + 0x0001b724,0x0001c92d,0x00024b76,0x0001c92d}, + {0x00000000,0x00000000,0x0000a492,0x000126db, + 0x0001b724,0x0001c92d,0x00024b76,0x0001c92d}, + {0x00000000,0x00000000,0x0000a492,0x000136db, + 0x0001b724,0x0001c92d,0x00024b76,0x0002496e}, + {0x00000000,0x00000000,0x0000a492,0x000136db, + 0x0001b924,0x0002496d,0x00024b76,0x00024b77}, + {0x00000000,0x00000000,0x0000a492,0x000136db, + 0x0001b924,0x00024b6d,0x0002ddb6,0x00025bbf}, + {0x00000000,0x00000000,0x00012492,0x0001b6db, + 0x00024924,0x0002db6d,0x00036db6,0x0002efff}, + {0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000} + }, + { /* version 14, passes 1 */ + {0x00000000,0x00000000,0x00001249,0x00001249, + 0x0000124a,0x0000124a,0x00001252,0x00001252}, + {0x00000000,0x00000000,0x0000924a,0x00009493, + 0x0000a493,0x00009292,0x00001252,0x00001252}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x0000a493,0x0000a49b,0x00001252,0x00001252}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x0001249b,0x000136e4,0x00009292,0x00009292}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x0001249b,0x000136e4,0x00009292,0x00009292}, + {0x00000000,0x00000000,0x0000924a,0x000124db, + 0x000136e4,0x000136e4,0x00009493,0x00009292}, + {0x00000000,0x00000000,0x00009492,0x000136db, + 0x0001b724,0x000136e4,0x00009493,0x00009493}, + {0x00000000,0x00000000,0x00009492,0x000136db, + 0x0001b724,0x000136e4,0x0000a49b,0x00009493}, + {0x00000000,0x00000000,0x00009492,0x000136db, + 0x0001b724,0x000136e4,0x0000a49b,0x00009493}, + {0x00000000,0x00000000,0x00009492,0x000136db, + 0x0001b724,0x000136e4,0x0000a49b,0x0000a49b}, + {0x00000000,0x00000000,0x0000a492,0x000136db, + 0x0001b724,0x000136e4,0x000124db,0x0000a49b}, + {0x00000000,0x00000000,0x0000a492,0x000136db, + 0x0001b724,0x000136e4,0x000124db,0x0000a49b}, + {0x00000000,0x00000000,0x0000a492,0x000136db, + 0x0001b724,0x000136e4,0x000126dc,0x000124db}, + {0x00000000,0x00000000,0x0000a492,0x000136db, + 0x0001b724,0x000136e4,0x000126dc,0x000124db}, + {0x00000000,0x00000000,0x00012492,0x0001b6db, + 0x0001c924,0x0001b724,0x000136e4,0x000126dc}, + {0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000} + } + }, + { /* version 15 */ + { /* version 15, passes 0 */ + {0x00000000,0x00000000,0x00001249,0x00009493, + 0x0000a493,0x0000a49b,0x000124db,0x000124db}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x0001249b,0x000126dc,0x000136e4,0x000124db}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x000126dc,0x0001b724,0x0001b725,0x000126dc}, + {0x00000000,0x00000000,0x0000924a,0x000124db, + 0x000136e4,0x0001b724,0x0001b92d,0x000126dc}, + {0x00000000,0x00000000,0x00009492,0x000124db, + 0x000136e4,0x0001b925,0x0001c96e,0x000136e4}, + {0x00000000,0x00000000,0x00009492,0x000124db, + 0x0001b724,0x0001c92d,0x0001c96e,0x0001b724}, + {0x00000000,0x00000000,0x0000a492,0x000124db, + 0x0001b724,0x0001c92d,0x0001c96e,0x0001b724}, + {0x00000000,0x00000000,0x0000a492,0x000126db, + 0x0001b724,0x0001c92d,0x0001c96e,0x0001b925}, + {0x00000000,0x00000000,0x0000a492,0x000126db, + 0x0001b924,0x0001c92d,0x00024b76,0x0001c92d}, + {0x00000000,0x00000000,0x0000a492,0x000136db, + 0x0001b924,0x0001c92d,0x00024b76,0x0001c92d}, + {0x00000000,0x00000000,0x0000a492,0x000136db, + 0x0001b924,0x0002496d,0x00024b76,0x0002496e}, + {0x00000000,0x00000000,0x0000a492,0x000136db, + 0x0001c924,0x0002496d,0x00025bb6,0x00024b77}, + {0x00000000,0x00000000,0x0000a492,0x000136db, + 0x0001c924,0x00024b6d,0x00025bb6,0x00024b77}, + {0x00000000,0x00000000,0x00012492,0x000136db, + 0x0001c924,0x00024b6d,0x0002ddb6,0x00025bbf}, + {0x00000000,0x00000000,0x00012492,0x0001b6db, + 0x00024924,0x0002db6d,0x00036db6,0x0002efff}, + {0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000} + }, + { /* version 15, passes 1 */ + {0x00000000,0x00000000,0x0000924a,0x0000924a, + 0x00009292,0x00009292,0x00009292,0x00009292}, + {0x00000000,0x00000000,0x0000924a,0x0000a49b, + 0x0000a493,0x000124db,0x00009292,0x00009292}, + {0x00000000,0x00000000,0x0000924a,0x000124db, + 0x000124db,0x0001b724,0x00009493,0x00009493}, + {0x00000000,0x00000000,0x0000924a,0x000124db, + 0x000126dc,0x0001b724,0x00009493,0x00009493}, + {0x00000000,0x00000000,0x0000924a,0x000124db, + 0x000136e4,0x0001b724,0x0000a49b,0x0000a49b}, + {0x00000000,0x00000000,0x00009292,0x000136db, + 0x0001b724,0x0001b724,0x0000a49b,0x0000a49b}, + {0x00000000,0x00000000,0x00009492,0x000136db, + 0x0001c924,0x0001b724,0x000124db,0x000124db}, + {0x00000000,0x00000000,0x00009492,0x000136db, + 0x0001c924,0x0001b724,0x000124db,0x000124db}, + {0x00000000,0x00000000,0x0000a492,0x000136db, + 0x0001c924,0x0001b724,0x000126dc,0x000126dc}, + {0x00000000,0x00000000,0x0000a492,0x000136db, + 0x0001c924,0x0001b925,0x000126dc,0x000126dc}, + {0x00000000,0x00000000,0x0000a492,0x000136db, + 0x0001c924,0x0001b925,0x000136e4,0x000136e4}, + {0x00000000,0x00000000,0x0000a492,0x000136db, + 0x0001c924,0x0001b925,0x000136e4,0x000136e4}, + {0x00000000,0x00000000,0x0000a492,0x000136db, + 0x0001c924,0x0001b925,0x0001b725,0x0001b724}, + {0x00000000,0x00000000,0x00012492,0x000136db, + 0x0001c924,0x0001b925,0x0001b725,0x0001b724}, + {0x00000000,0x00000000,0x00012492,0x0001b6db, + 0x00024924,0x0002496d,0x0001b92d,0x0001b925}, + {0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000} + } + } +}; diff --git a/drivers/usb/media/pwc/pwc-timon.h b/drivers/usb/media/pwc/pwc-timon.h new file mode 100644 index 000000000..a86b3782a --- /dev/null +++ b/drivers/usb/media/pwc/pwc-timon.h @@ -0,0 +1,61 @@ +/* Linux driver for Philips webcam + (C) 2004 Luc Saillard (luc@saillard.org) + + NOTE: this version of pwc is an unofficial (modified) release of pwc & pcwx + driver and thus may have bugs that are not present in the original version. + Please send bug reports and support requests to . + The decompression routines have been implemented by reverse-engineering the + Nemosoft binary pwcx module. Caveat emptor. + + This program is free software; you can redistribute it and/or modify + it under the terms of the 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 +*/ + + + +/* This tables contains entries for the 675/680/690 (Timon) camera, with + 4 different qualities (no compression, low, medium, high). + It lists the bandwidth requirements for said mode by its alternate interface + number. An alternate of 0 means that the mode is unavailable. + + There are 6 * 4 * 4 entries: + 6 different resolutions subqcif, qsif, qcif, sif, cif, vga + 6 framerates: 5, 10, 15, 20, 25, 30 + 4 compression modi: none, low, medium, high + + When an uncompressed mode is not available, the next available compressed mode + will be chosen (unless the decompressor is absent). Sometimes there are only + 1 or 2 compressed modes available; in that case entries are duplicated. +*/ + +#ifndef PWC_TIMON_H +#define PWC_TIMON_H + +#include "pwc-ioctl.h" + +struct Timon_table_entry +{ + char alternate; /* USB alternate interface */ + unsigned short packetsize; /* Normal packet size */ + unsigned short bandlength; /* Bandlength when decompressing */ + unsigned char mode[13]; /* precomputed mode settings for cam */ +}; + +const extern struct Timon_table_entry Timon_table[PSZ_MAX][6][4]; +const extern unsigned int TimonRomTable [16][2][16][8]; + + +#endif + + diff --git a/drivers/usb/media/pwc/pwc-uncompress.c b/drivers/usb/media/pwc/pwc-uncompress.c new file mode 100644 index 000000000..c062e43b3 --- /dev/null +++ b/drivers/usb/media/pwc/pwc-uncompress.c @@ -0,0 +1,147 @@ +/* Linux driver for Philips webcam + Decompression frontend. + (C) 1999-2003 Nemosoft Unv. + (C) 2004 Luc Saillard (luc@saillard.org) + + NOTE: this version of pwc is an unofficial (modified) release of pwc & pcwx + driver and thus may have bugs that are not present in the original version. + Please send bug reports and support requests to . + The decompression routines have been implemented by reverse-engineering the + Nemosoft binary pwcx module. Caveat emptor. + + This program is free software; you can redistribute it and/or modify + it under the terms of the 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 +#include + +#include "pwc.h" +#include "pwc-uncompress.h" +#include "pwc-dec1.h" +#include "pwc-dec23.h" + +int pwc_decompress(struct pwc_device *pdev) +{ + struct pwc_frame_buf *fbuf; + int n, line, col, stride; + void *yuv, *image; + u16 *src; + u16 *dsty, *dstu, *dstv; + + if (pdev == NULL) + return -EFAULT; +#if defined(__KERNEL__) && defined(PWC_MAGIC) + if (pdev->magic != PWC_MAGIC) { + Err("pwc_decompress(): magic failed.\n"); + return -EFAULT; + } +#endif + + fbuf = pdev->read_frame; + if (fbuf == NULL) + return -EFAULT; + image = pdev->image_ptr[pdev->fill_image]; + if (!image) + return -EFAULT; + + yuv = fbuf->data + pdev->frame_header_size; /* Skip header */ + + /* Raw format; that's easy... */ + if (pdev->vpalette == VIDEO_PALETTE_RAW) + { + memcpy(image, yuv, pdev->frame_size); + return 0; + } + + if (pdev->vbandlength == 0) { + /* Uncompressed mode. We copy the data into the output buffer, + using the viewport size (which may be larger than the image + size). Unfortunately we have to do a bit of byte stuffing + to get the desired output format/size. + */ + /* + * We do some byte shuffling here to go from the + * native format to YUV420P. + */ + src = (u16 *)yuv; + n = pdev->view.x * pdev->view.y; + + /* offset in Y plane */ + stride = pdev->view.x * pdev->offset.y + pdev->offset.x; + dsty = (u16 *)(image + stride); + + /* offsets in U/V planes */ + stride = pdev->view.x * pdev->offset.y / 4 + pdev->offset.x / 2; + dstu = (u16 *)(image + n + stride); + dstv = (u16 *)(image + n + n / 4 + stride); + + /* increment after each line */ + stride = (pdev->view.x - pdev->image.x) / 2; /* u16 is 2 bytes */ + + for (line = 0; line < pdev->image.y; line++) { + for (col = 0; col < pdev->image.x; col += 4) { + *dsty++ = *src++; + *dsty++ = *src++; + if (line & 1) + *dstv++ = *src++; + else + *dstu++ = *src++; + } + dsty += stride; + if (line & 1) + dstv += (stride >> 1); + else + dstu += (stride >> 1); + } + } + else { + /* Compressed; the decompressor routines will write the data + in planar format immediately. + */ + int flags; + + flags = PWCX_FLAG_PLANAR; + if (pdev->vsize == PSZ_VGA && pdev->vframes == 5 && pdev->vsnapshot) + { + printk(KERN_ERR "pwc: Mode Bayer is not supported for now\n"); + flags |= PWCX_FLAG_BAYER; + return -ENXIO; /* No such device or address: missing decompressor */ + } + + switch (pdev->type) + { + case 675: + case 680: + case 690: + case 720: + case 730: + case 740: + case 750: + pwc_dec23_decompress(&pdev->image, &pdev->view, &pdev->offset, + yuv, image, + flags, + pdev->decompress_data, pdev->vbandlength); + break; + case 645: + case 646: + /* TODO & FIXME */ + return -ENXIO; /* No such device or address: missing decompressor */ + break; + } + } + return 0; +} + + diff --git a/drivers/usb/media/pwc/pwc-uncompress.h b/drivers/usb/media/pwc/pwc-uncompress.h new file mode 100644 index 000000000..d3b9250e4 --- /dev/null +++ b/drivers/usb/media/pwc/pwc-uncompress.h @@ -0,0 +1,41 @@ +/* (C) 1999-2003 Nemosoft Unv. + (C) 2004 Luc Saillard (luc@saillard.org) + + NOTE: this version of pwc is an unofficial (modified) release of pwc & pcwx + driver and thus may have bugs that are not present in the original version. + Please send bug reports and support requests to . + The decompression routines have been implemented by reverse-engineering the + Nemosoft binary pwcx module. Caveat emptor. + + This program is free software; you can redistribute it and/or modify + it under the terms of the 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 +*/ + +/* This file is the bridge between the kernel module and the plugin; it + describes the structures and datatypes used in both modules. Any + significant change should be reflected by increasing the + pwc_decompressor_version major number. + */ +#ifndef PWC_UNCOMPRESS_H +#define PWC_UNCOMPRESS_H + +#include + +#include "pwc-ioctl.h" + +/* from pwc-dec.h */ +#define PWCX_FLAG_PLANAR 0x0001 +/* */ + +#endif diff --git a/drivers/usb/media/pwc/pwc.h b/drivers/usb/media/pwc/pwc.h new file mode 100644 index 000000000..53b516d29 --- /dev/null +++ b/drivers/usb/media/pwc/pwc.h @@ -0,0 +1,278 @@ +/* (C) 1999-2003 Nemosoft Unv. + (C) 2004 Luc Saillard (luc@saillard.org) + + NOTE: this version of pwc is an unofficial (modified) release of pwc & pcwx + driver and thus may have bugs that are not present in the original version. + Please send bug reports and support requests to . + The decompression routines have been implemented by reverse-engineering the + Nemosoft binary pwcx module. Caveat emptor. + + This program is free software; you can redistribute it and/or modify + it under the terms of the 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 PWC_H +#define PWC_H + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "pwc-uncompress.h" +#include "pwc-ioctl.h" + +/* Defines and structures for the Philips webcam */ +/* Used for checking memory corruption/pointer validation */ +#define PWC_MAGIC 0x89DC10ABUL +#undef PWC_MAGIC + +/* Turn some debugging options on/off */ +#define PWC_DEBUG 0 + +/* Trace certain actions in the driver */ +#define TRACE_MODULE 0x0001 +#define TRACE_PROBE 0x0002 +#define TRACE_OPEN 0x0004 +#define TRACE_READ 0x0008 +#define TRACE_MEMORY 0x0010 +#define TRACE_FLOW 0x0020 +#define TRACE_SIZE 0x0040 +#define TRACE_PWCX 0x0080 +#define TRACE_SEQUENCE 0x1000 + +#define Trace(R, A...) if (pwc_trace & R) printk(KERN_DEBUG PWC_NAME " " A) +#define Debug(A...) printk(KERN_DEBUG PWC_NAME " " A) +#define Info(A...) printk(KERN_INFO PWC_NAME " " A) +#define Err(A...) printk(KERN_ERR PWC_NAME " " A) + + +/* Defines for ToUCam cameras */ +#define TOUCAM_HEADER_SIZE 8 +#define TOUCAM_TRAILER_SIZE 4 + +#define FEATURE_MOTOR_PANTILT 0x0001 + +/* Version block */ +#define PWC_MAJOR 9 +#define PWC_MINOR 0 +#define PWC_VERSION "9.0.2-unofficial" +#define PWC_NAME "pwc" + +/* Turn certain features on/off */ +#define PWC_INT_PIPE 0 + +/* Ignore errors in the first N frames, to allow for startup delays */ +#define FRAME_LOWMARK 5 + +/* Size and number of buffers for the ISO pipe. */ +#define MAX_ISO_BUFS 2 +#define ISO_FRAMES_PER_DESC 10 +#define ISO_MAX_FRAME_SIZE 960 +#define ISO_BUFFER_SIZE (ISO_FRAMES_PER_DESC * ISO_MAX_FRAME_SIZE) + +/* Frame buffers: contains compressed or uncompressed video data. */ +#define MAX_FRAMES 5 +/* Maximum size after decompression is 640x480 YUV data, 1.5 * 640 * 480 */ +#define PWC_FRAME_SIZE (460800 + TOUCAM_HEADER_SIZE + TOUCAM_TRAILER_SIZE) + +/* Absolute maximum number of buffers available for mmap() */ +#define MAX_IMAGES 10 + +/* The following structures were based on cpia.h. Why reinvent the wheel? :-) */ +struct pwc_iso_buf +{ + void *data; + int length; + int read; + struct urb *urb; +}; + +/* intermediate buffers with raw data from the USB cam */ +struct pwc_frame_buf +{ + void *data; + volatile int filled; /* number of bytes filled */ + struct pwc_frame_buf *next; /* list */ +#if PWC_DEBUG + int sequence; /* Sequence number */ +#endif +}; + +struct pwc_device +{ + struct video_device *vdev; +#ifdef PWC_MAGIC + int magic; +#endif + /* Pointer to our usb_device */ + struct usb_device *udev; + + int type; /* type of cam (645, 646, 675, 680, 690, 720, 730, 740, 750) */ + int release; /* release number */ + int features; /* feature bits */ + char serial[30]; /* serial number (string) */ + int error_status; /* set when something goes wrong with the cam (unplugged, USB errors) */ + int usb_init; /* set when the cam has been initialized over USB */ + + /*** Video data ***/ + int vopen; /* flag */ + int vendpoint; /* video isoc endpoint */ + int vcinterface; /* video control interface */ + int valternate; /* alternate interface needed */ + int vframes, vsize; /* frames-per-second & size (see PSZ_*) */ + int vpalette; /* palette: 420P, RAW or RGBBAYER */ + int vframe_count; /* received frames */ + int vframes_dumped; /* counter for dumped frames */ + int vframes_error; /* frames received in error */ + int vmax_packet_size; /* USB maxpacket size */ + int vlast_packet_size; /* for frame synchronisation */ + int visoc_errors; /* number of contiguous ISOC errors */ + int vcompression; /* desired compression factor */ + int vbandlength; /* compressed band length; 0 is uncompressed */ + char vsnapshot; /* snapshot mode */ + char vsync; /* used by isoc handler */ + char vmirror; /* for ToUCaM series */ + + int cmd_len; + unsigned char cmd_buf[13]; + + /* The image acquisition requires 3 to 4 steps: + 1. data is gathered in short packets from the USB controller + 2. data is synchronized and packed into a frame buffer + 3a. in case data is compressed, decompress it directly into image buffer + 3b. in case data is uncompressed, copy into image buffer with viewport + 4. data is transferred to the user process + + Note that MAX_ISO_BUFS != MAX_FRAMES != MAX_IMAGES.... + We have in effect a back-to-back-double-buffer system. + */ + /* 1: isoc */ + struct pwc_iso_buf sbuf[MAX_ISO_BUFS]; + char iso_init; + + /* 2: frame */ + struct pwc_frame_buf *fbuf; /* all frames */ + struct pwc_frame_buf *empty_frames, *empty_frames_tail; /* all empty frames */ + struct pwc_frame_buf *full_frames, *full_frames_tail; /* all filled frames */ + struct pwc_frame_buf *fill_frame; /* frame currently being filled */ + struct pwc_frame_buf *read_frame; /* frame currently read by user process */ + int frame_header_size, frame_trailer_size; + int frame_size; + int frame_total_size; /* including header & trailer */ + int drop_frames; +#if PWC_DEBUG + int sequence; /* Debugging aid */ +#endif + + /* 3: decompression */ + struct pwc_decompressor *decompressor; /* function block with decompression routines */ + void *decompress_data; /* private data for decompression engine */ + + /* 4: image */ + /* We have an 'image' and a 'view', where 'image' is the fixed-size image + as delivered by the camera, and 'view' is the size requested by the + program. The camera image is centered in this viewport, laced with + a gray or black border. view_min <= image <= view <= view_max; + */ + int image_mask; /* bitmask of supported sizes */ + struct pwc_coord view_min, view_max; /* minimum and maximum viewable sizes */ + struct pwc_coord abs_max; /* maximum supported size with compression */ + struct pwc_coord image, view; /* image and viewport size */ + struct pwc_coord offset; /* offset within the viewport */ + + void *image_data; /* total buffer, which is subdivided into ... */ + void *image_ptr[MAX_IMAGES]; /* ...several images... */ + int fill_image; /* ...which are rotated. */ + int len_per_image; /* length per image */ + int image_read_pos; /* In case we read data in pieces, keep track of were we are in the imagebuffer */ + int image_used[MAX_IMAGES]; /* For MCAPTURE and SYNC */ + + struct semaphore modlock; /* to prevent races in video_open(), etc */ + spinlock_t ptrlock; /* for manipulating the buffer pointers */ + + /*** motorized pan/tilt feature */ + struct pwc_mpt_range angle_range; + int pan_angle; /* in degrees * 100 */ + int tilt_angle; /* absolute angle; 0,0 is home position */ + + /*** Misc. data ***/ + wait_queue_head_t frameq; /* When waiting for a frame to finish... */ +#if PWC_INT_PIPE + void *usb_int_handler; /* for the interrupt endpoint */ +#endif +}; + + +#ifdef __cplusplus +extern "C" { +#endif + +/* Global variables */ +extern int pwc_trace; +extern int pwc_preferred_compression; + +/** functions in pwc-if.c */ +int pwc_try_video_mode(struct pwc_device *pdev, int width, int height, int new_fps, int new_compression, int new_snapshot); + +/** Functions in pwc-misc.c */ +/* sizes in pixels */ +extern struct pwc_coord pwc_image_sizes[PSZ_MAX]; + +int pwc_decode_size(struct pwc_device *pdev, int width, int height); +void pwc_construct(struct pwc_device *pdev); + +/** Functions in pwc-ctrl.c */ +/* Request a certain video mode. Returns < 0 if not possible */ +extern int pwc_set_video_mode(struct pwc_device *pdev, int width, int height, int frames, int compression, int snapshot); +/* Calculate the number of bytes per image (not frame) */ +extern void pwc_set_image_buffer_size(struct pwc_device *pdev); + +/* Various controls; should be obvious. Value 0..65535, or < 0 on error */ +extern int pwc_get_brightness(struct pwc_device *pdev); +extern int pwc_set_brightness(struct pwc_device *pdev, int value); +extern int pwc_get_contrast(struct pwc_device *pdev); +extern int pwc_set_contrast(struct pwc_device *pdev, int value); +extern int pwc_get_gamma(struct pwc_device *pdev); +extern int pwc_set_gamma(struct pwc_device *pdev, int value); +extern int pwc_get_saturation(struct pwc_device *pdev); +extern int pwc_set_saturation(struct pwc_device *pdev, int value); +extern int pwc_set_leds(struct pwc_device *pdev, int on_value, int off_value); +extern int pwc_get_leds(struct pwc_device *pdev, int *on_value, int *off_value); +extern int pwc_get_cmos_sensor(struct pwc_device *pdev, int *sensor); + +/* Power down or up the camera; not supported by all models */ +extern int pwc_camera_power(struct pwc_device *pdev, int power); + +/* Private ioctl()s; see pwc-ioctl.h */ +extern int pwc_ioctl(struct pwc_device *pdev, unsigned int cmd, void *arg); + + +/** pwc-uncompress.c */ +/* Expand frame to image, possibly including decompression. Uses read_frame and fill_image */ +extern int pwc_decompress(struct pwc_device *pdev); + +#ifdef __cplusplus +} +#endif + + +#endif diff --git a/drivers/usb/media/sn9c102.h b/drivers/usb/media/sn9c102.h index fc9cb47e6..62ff21443 100644 --- a/drivers/usb/media/sn9c102.h +++ b/drivers/usb/media/sn9c102.h @@ -31,6 +31,7 @@ #include #include #include +#include #include "sn9c102_sensor.h" @@ -39,6 +40,7 @@ #define SN9C102_DEBUG #define SN9C102_DEBUG_LEVEL 2 #define SN9C102_MAX_DEVICES 64 +#define SN9C102_PRESERVE_IMGSCALE 0 #define SN9C102_MAX_FRAMES 32 #define SN9C102_URBS 2 #define SN9C102_ISO_PACKETS 7 @@ -51,8 +53,8 @@ #define SN9C102_MODULE_AUTHOR "(C) 2004 Luca Risolia" #define SN9C102_AUTHOR_EMAIL "" #define SN9C102_MODULE_LICENSE "GPL" -#define SN9C102_MODULE_VERSION "1:1.01-beta" -#define SN9C102_MODULE_VERSION_CODE KERNEL_VERSION(1, 0, 1) +#define SN9C102_MODULE_VERSION "1:1.08" +#define SN9C102_MODULE_VERSION_CODE KERNEL_VERSION(1, 0, 8) SN9C102_ID_TABLE; SN9C102_SENSOR_TABLE; diff --git a/drivers/usb/media/sn9c102_core.c b/drivers/usb/media/sn9c102_core.c index bcd9fbd3b..9f775f740 100644 --- a/drivers/usb/media/sn9c102_core.c +++ b/drivers/usb/media/sn9c102_core.c @@ -31,6 +31,7 @@ #include #include #include +#include #include #include #include @@ -83,7 +84,7 @@ MODULE_PARM_DESC(debug, /*****************************************************************************/ -typedef char sn9c102_sof_header_t[7]; +typedef char sn9c102_sof_header_t[12]; typedef char sn9c102_eof_header_t[4]; static sn9c102_sof_header_t sn9c102_sof_header[] = { @@ -91,8 +92,6 @@ static sn9c102_sof_header_t sn9c102_sof_header[] = { {0xff, 0xff, 0x00, 0xc4, 0xc4, 0x96, 0x01}, }; -/* Number of random bytes that complete the SOF above headers */ -#define SN9C102_SOFLEN 5 static sn9c102_eof_header_t sn9c102_eof_header[] = { {0x00, 0x00, 0x00, 0x00}, @@ -237,9 +236,6 @@ int sn9c102_write_reg(struct sn9c102_device* cam, u8 value, u16 index) u8* buff = cam->control_buffer; int res; - if (index == 0x18) - value = (value & 0xcf) | (cam->reg[0x18] & 0x30); - *buff = value; res = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), 0x08, 0x41, @@ -443,14 +439,15 @@ int sn9c102_i2c_write(struct sn9c102_device* cam, u8 address, u8 value) static void* sn9c102_find_sof_header(void* mem, size_t len) { - size_t soflen=sizeof(sn9c102_sof_header_t), SOFLEN=SN9C102_SOFLEN, i; + size_t soflen = sizeof(sn9c102_sof_header_t), i; u8 j, n = sizeof(sn9c102_sof_header) / soflen; - for (i = 0; (len >= soflen+SOFLEN) && (i <= len-soflen-SOFLEN); i++) + for (i = 0; (len >= soflen) && (i <= len - soflen); i++) for (j = 0; j < n; j++) - if (!memcmp(mem + i, sn9c102_sof_header[j], soflen)) + /* It's enough to compare 7 bytes */ + if (!memcmp(mem + i, sn9c102_sof_header[j], 7)) /* Skips the header */ - return mem + i + soflen + SOFLEN; + return mem + i + soflen; return NULL; } @@ -517,10 +514,12 @@ static void sn9c102_urb_complete(struct urb *urb, struct pt_regs* regs) PDBGG("Isochrnous frame: length %u, #%u i", len, i) - /* NOTE: It is probably correct to assume that SOF and EOF + /* + NOTE: It is probably correct to assume that SOF and EOF headers do not occur between two consecutive packets, but who knows..Whatever is the truth, this assumption - doesn't introduce bugs. */ + doesn't introduce bugs. + */ redo: sof = sn9c102_find_sof_header(pos, len); @@ -764,9 +763,11 @@ static u8 sn9c102_strtou8(const char* buff, size_t len, ssize_t* count) return (u8)val; } -/* NOTE 1: being inside one of the following methods implies that the v4l +/* + NOTE 1: being inside one of the following methods implies that the v4l device exists for sure (see kobjects and reference counters) - NOTE 2: buffers are PAGE_SIZE long */ + NOTE 2: buffers are PAGE_SIZE long +*/ static ssize_t sn9c102_show_reg(struct class_device* cd, char* buf) { @@ -1018,24 +1019,6 @@ sn9c102_store_i2c_val(struct class_device* cd, const char* buf, size_t len) } -static ssize_t -sn9c102_store_redblue(struct class_device* cd, const char* buf, size_t len) -{ - ssize_t res = 0; - u8 value; - ssize_t count; - - value = sn9c102_strtou8(buf, len, &count); - if (!count) - return -EINVAL; - - if ((res = sn9c102_store_reg(cd, "0x10", 4)) >= 0) - res = sn9c102_store_val(cd, buf, len); - - return res; -} - - static ssize_t sn9c102_store_green(struct class_device* cd, const char* buf, size_t len) { @@ -1062,7 +1045,6 @@ static CLASS_DEVICE_ATTR(i2c_reg, S_IRUGO | S_IWUSR, sn9c102_show_i2c_reg, sn9c102_store_i2c_reg); static CLASS_DEVICE_ATTR(i2c_val, S_IRUGO | S_IWUSR, sn9c102_show_i2c_val, sn9c102_store_i2c_val); -static CLASS_DEVICE_ATTR(redblue, S_IWUGO, NULL, sn9c102_store_redblue); static CLASS_DEVICE_ATTR(green, S_IWUGO, NULL, sn9c102_store_green); @@ -1072,7 +1054,6 @@ static void sn9c102_create_sysfs(struct sn9c102_device* cam) video_device_create_file(v4ldev, &class_device_attr_reg); video_device_create_file(v4ldev, &class_device_attr_val); - video_device_create_file(v4ldev, &class_device_attr_redblue); video_device_create_file(v4ldev, &class_device_attr_green); if (cam->sensor->slave_write_id && cam->sensor->slave_read_id) { video_device_create_file(v4ldev, &class_device_attr_i2c_reg); @@ -1118,10 +1099,6 @@ static int sn9c102_set_crop(struct sn9c102_device* cam, struct v4l2_rect* rect) ae_endy = v_size / 2; int err = 0; - /* These are a sort of stroboscopic signal for some sensors */ - err += sn9c102_write_reg(cam, h_size, 0x1a); - err += sn9c102_write_reg(cam, v_size, 0x1b); - err += sn9c102_write_reg(cam, h_start, 0x12); err += sn9c102_write_reg(cam, v_start, 0x13); err += sn9c102_write_reg(cam, h_size, 0x15); @@ -1134,8 +1111,7 @@ static int sn9c102_set_crop(struct sn9c102_device* cam, struct v4l2_rect* rect) return -EIO; PDBGG("h_start, v_start, h_size, v_size, ho_size, vo_size " - "%u %u %u %u %u %u", h_start, v_start, h_size, v_size, ho_size, - vo_size) + "%u %u %u %u", h_start, v_start, h_size, v_size) return 0; } @@ -1229,7 +1205,10 @@ static int sn9c102_open(struct inode* inode, struct file* filp) struct sn9c102_device* cam; int err = 0; - /* This the only safe way to prevent race conditions with disconnect */ + /* + This is the only safe way to prevent race conditions with + disconnect + */ if (!down_read_trylock(&sn9c102_disconnect)) return -ERESTARTSYS; @@ -1727,6 +1706,12 @@ static int sn9c102_v4l2_ioctl(struct inode* inode, struct file* filp, return -EINVAL; } + /* Preserve R,G or B origin */ + rect->left = (s->_rect.left & 1L) ? + rect->left | 1L : rect->left & ~1L; + rect->top = (s->_rect.top & 1L) ? + rect->top | 1L : rect->top & ~1L; + if (rect->width < 16) rect->width = 16; if (rect->height < 16) @@ -1747,13 +1732,15 @@ static int sn9c102_v4l2_ioctl(struct inode* inode, struct file* filp, rect->width &= ~15L; rect->height &= ~15L; - { /* calculate the scaling factor */ + if (SN9C102_PRESERVE_IMGSCALE) { + /* Calculate the actual scaling factor */ u32 a, b; a = rect->width * rect->height; b = pix_format->width * pix_format->height; - scale = b ? (u8)((a / b) <= 1 ? 1 : ((a / b) == 3 ? 2 : - ((a / b) > 4 ? 4 : (a / b)))) : 1; - } + scale = b ? (u8)((a / b) < 4 ? 1 : + ((a / b) < 16 ? 2 : 4)) : 1; + } else + scale = 1; if (cam->stream == STREAM_ON) { cam->stream = STREAM_INTERRUPT; @@ -1879,12 +1866,12 @@ static int sn9c102_v4l2_ioctl(struct inode* inode, struct file* filp, memcpy(&rect, &(s->_rect), sizeof(rect)); - { /* calculate the scaling factor */ + { /* calculate the actual scaling factor */ u32 a, b; a = rect.width * rect.height; b = pix->width * pix->height; - scale = b ? (u8)((a / b) <= 1 ? 1 : ((a / b) == 3 ? 2 : - ((a / b) > 4 ? 4 : (a / b)))) : 1; + scale = b ? (u8)((a / b) < 4 ? 1 : + ((a / b) < 16 ? 2 : 4)) : 1; } rect.width = scale * pix->width; @@ -1895,13 +1882,21 @@ static int sn9c102_v4l2_ioctl(struct inode* inode, struct file* filp, if (rect.height < 16) rect.height = 16; if (rect.width > bounds->left + bounds->width - rect.left) - rect.width = bounds->left+bounds->width - rect.left; + rect.width = bounds->left + bounds->width - rect.left; if (rect.height > bounds->top + bounds->height - rect.top) rect.height = bounds->top + bounds->height - rect.top; rect.width &= ~15L; rect.height &= ~15L; + { /* adjust the scaling factor */ + u32 a, b; + a = rect.width * rect.height; + b = pix->width * pix->height; + scale = b ? (u8)((a / b) < 4 ? 1 : + ((a / b) < 16 ? 2 : 4)) : 1; + } + pix->width = rect.width / scale; pix->height = rect.height / scale; @@ -2119,7 +2114,7 @@ static int sn9c102_v4l2_ioctl(struct inode* inode, struct file* filp, spin_lock_irqsave(&cam->queue_lock, lock_flags); f = list_entry(cam->outqueue.next, struct sn9c102_frame_t, frame); - list_del(&cam->outqueue); + list_del(cam->outqueue.next); spin_unlock_irqrestore(&cam->queue_lock, lock_flags); f->state = F_UNUSED; diff --git a/drivers/usb/media/sn9c102_pas106b.c b/drivers/usb/media/sn9c102_pas106b.c index 34bdfcf8e..a302a4845 100644 --- a/drivers/usb/media/sn9c102_pas106b.c +++ b/drivers/usb/media/sn9c102_pas106b.c @@ -40,13 +40,12 @@ static int pas106b_init(struct sn9c102_device* cam) err += sn9c102_i2c_write(cam, 0x02, 0x0c); err += sn9c102_i2c_write(cam, 0x03, 0x12); err += sn9c102_i2c_write(cam, 0x04, 0x05); - err += sn9c102_i2c_write(cam, 0x05, 0x22); - err += sn9c102_i2c_write(cam, 0x06, 0xac); - err += sn9c102_i2c_write(cam, 0x07, 0x00); + err += sn9c102_i2c_write(cam, 0x05, 0x5a); + err += sn9c102_i2c_write(cam, 0x06, 0x88); + err += sn9c102_i2c_write(cam, 0x07, 0x80); err += sn9c102_i2c_write(cam, 0x08, 0x01); - err += sn9c102_i2c_write(cam, 0x0a, 0x00); + err += sn9c102_i2c_write(cam, 0x0a, 0x01); err += sn9c102_i2c_write(cam, 0x0b, 0x00); - err += sn9c102_i2c_write(cam, 0x0d, 0x00); err += sn9c102_i2c_write(cam, 0x10, 0x06); err += sn9c102_i2c_write(cam, 0x11, 0x06); err += sn9c102_i2c_write(cam, 0x12, 0x00); @@ -64,11 +63,30 @@ static int pas106b_get_ctrl(struct sn9c102_device* cam, { switch (ctrl->id) { case V4L2_CID_RED_BALANCE: - return (ctrl->value = sn9c102_i2c_read(cam, 0x0c))<0 ? -EIO:0; + if ((ctrl->value = sn9c102_i2c_read(cam, 0x0c)) < 0) + return -EIO; + ctrl->value &= 0x1f; + return 0; case V4L2_CID_BLUE_BALANCE: - return (ctrl->value = sn9c102_i2c_read(cam, 0x09))<0 ? -EIO:0; + if ((ctrl->value = sn9c102_i2c_read(cam, 0x09)) < 0) + return -EIO; + ctrl->value &= 0x1f; + return 0; + case V4L2_CID_GAIN: + if ((ctrl->value = sn9c102_i2c_read(cam, 0x0e)) < 0) + return -EIO; + ctrl->value &= 0x1f; + return 0; case V4L2_CID_BRIGHTNESS: - return (ctrl->value = sn9c102_i2c_read(cam, 0x0e))<0 ? -EIO:0; + if ((ctrl->value = sn9c102_i2c_read(cam, 0x0d)) < 0) + return -EIO; + ctrl->value &= 0x1f; + return 0; + case V4L2_CID_CONTRAST: + if ((ctrl->value = sn9c102_i2c_read(cam, 0x0f)) < 0) + return -EIO; + ctrl->value &= 0x07; + return 0; default: return -EINVAL; } @@ -87,9 +105,15 @@ static int pas106b_set_ctrl(struct sn9c102_device* cam, case V4L2_CID_BLUE_BALANCE: err += sn9c102_i2c_write(cam, 0x09, ctrl->value & 0x1f); break; - case V4L2_CID_BRIGHTNESS: + case V4L2_CID_GAIN: err += sn9c102_i2c_write(cam, 0x0e, ctrl->value & 0x1f); break; + case V4L2_CID_BRIGHTNESS: + err += sn9c102_i2c_write(cam, 0x0d, 0x1f-(ctrl->value & 0x1f)); + break; + case V4L2_CID_CONTRAST: + err += sn9c102_i2c_write(cam, 0x0f, ctrl->value & 0x03); + break; default: return -EINVAL; } @@ -130,7 +154,7 @@ static struct sn9c102_sensor pas106b = { .minimum = 0x00, .maximum = 0x1f, .step = 0x01, - .default_value = 0x03, + .default_value = 0x04, .flags = 0, }, { @@ -140,7 +164,17 @@ static struct sn9c102_sensor pas106b = { .minimum = 0x00, .maximum = 0x1f, .step = 0x01, - .default_value = 0x02, + .default_value = 0x06, + .flags = 0, + }, + { + .id = V4L2_CID_GAIN, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "global gain", + .minimum = 0x00, + .maximum = 0x1f, + .step = 0x01, + .default_value = 0x0d, .flags = 0, }, { @@ -150,7 +184,17 @@ static struct sn9c102_sensor pas106b = { .minimum = 0x00, .maximum = 0x1f, .step = 0x01, - .default_value = 0x06, + .default_value = 0x1f, + .flags = 0, + }, + { + .id = V4L2_CID_CONTRAST, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "contrast", + .minimum = 0x00, + .maximum = 0x07, + .step = 0x01, + .default_value = 0x00, /* 0x00~0x03 have same effect */ .flags = 0, }, }, @@ -185,11 +229,13 @@ int sn9c102_probe_pas106b(struct sn9c102_device* cam) int r0 = 0, r1 = 0, err = 0; unsigned int pid = 0; - /* Minimal initialization to enable the I2C communication - NOTE: do NOT change the values! */ + /* + Minimal initialization to enable the I2C communication + NOTE: do NOT change the values! + */ err += sn9c102_write_reg(cam, 0x01, 0x01); /* sensor power down */ err += sn9c102_write_reg(cam, 0x00, 0x01); /* sensor power on */ - err += sn9c102_write_reg(cam, 0x28, 0x17); /* sensor clock at 48 MHz */ + err += sn9c102_write_reg(cam, 0x28, 0x17); /* sensor clock at 24 MHz */ if (err) return -EIO; diff --git a/drivers/usb/media/sn9c102_pas202bcb.c b/drivers/usb/media/sn9c102_pas202bcb.c new file mode 100644 index 000000000..26944eaf8 --- /dev/null +++ b/drivers/usb/media/sn9c102_pas202bcb.c @@ -0,0 +1,238 @@ +/*************************************************************************** + * Driver for PAS202BCB image sensor connected to the SN9C10[12] PC Camera * + * Controllers * + * * + * Copyright (C) 2004 by Carlos Eduardo Medaglia Dyonisio * + * * + * http://cadu.homelinux.com:8080/ * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the 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 +#include "sn9c102_sensor.h" + + +static struct sn9c102_sensor pas202bcb; + + +static int pas202bcb_init(struct sn9c102_device* cam) +{ + int err = 0; + + err += sn9c102_write_reg(cam, 0x00, 0x10); + err += sn9c102_write_reg(cam, 0x00, 0x11); + err += sn9c102_write_reg(cam, 0x00, 0x14); + err += sn9c102_write_reg(cam, 0x20, 0x17); + err += sn9c102_write_reg(cam, 0x20, 0x19); + err += sn9c102_write_reg(cam, 0x09, 0x18); + + err += sn9c102_i2c_write(cam, 0x02, 0x0c); + err += sn9c102_i2c_write(cam, 0x03, 0x40); + err += sn9c102_i2c_write(cam, 0x04, 0x07); + err += sn9c102_i2c_write(cam, 0x05, 0x25); + err += sn9c102_i2c_write(cam, 0x0d, 0x2c); + err += sn9c102_i2c_write(cam, 0x0e, 0x01); + err += sn9c102_i2c_write(cam, 0x0f, 0xa9); + err += sn9c102_i2c_write(cam, 0x08, 0x01); + err += sn9c102_i2c_write(cam, 0x0b, 0x01); + err += sn9c102_i2c_write(cam, 0x13, 0x63); + err += sn9c102_i2c_write(cam, 0x15, 0x70); + err += sn9c102_i2c_write(cam, 0x11, 0x01); + + msleep(400); + + return err; +} + + +static int pas202bcb_get_ctrl(struct sn9c102_device* cam, + struct v4l2_control* ctrl) +{ + switch (ctrl->id) { + case V4L2_CID_RED_BALANCE: + if ((ctrl->value = sn9c102_i2c_read(cam, 0x09)) < 0) + return -EIO; + ctrl->value &= 0x0f; + return 0; + case V4L2_CID_BLUE_BALANCE: + if ((ctrl->value = sn9c102_i2c_read(cam, 0x07)) < 0) + return -EIO; + ctrl->value &= 0x0f; + return 0; + case V4L2_CID_GAIN: + if ((ctrl->value = sn9c102_i2c_read(cam, 0x10)) < 0) + return -EIO; + ctrl->value &= 0x1f; + return 0; + case V4L2_CID_BRIGHTNESS: + if ((ctrl->value = sn9c102_i2c_read(cam, 0x06)) < 0) + return -EIO; + ctrl->value &= 0x0f; + return 0; + default: + return -EINVAL; + } +} + + +static int pas202bcb_set_ctrl(struct sn9c102_device* cam, + const struct v4l2_control* ctrl) +{ + int err = 0; + + switch (ctrl->id) { + case V4L2_CID_RED_BALANCE: + err += sn9c102_i2c_write(cam, 0x09, ctrl->value & 0x0f); + break; + case V4L2_CID_BLUE_BALANCE: + err += sn9c102_i2c_write(cam, 0x07, ctrl->value & 0x0f); + break; + case V4L2_CID_GAIN: + err += sn9c102_i2c_write(cam, 0x10, ctrl->value & 0x1f); + break; + case V4L2_CID_BRIGHTNESS: + err += sn9c102_i2c_write(cam, 0x06, 0x0f-(ctrl->value & 0x0f)); + break; + default: + return -EINVAL; + } + err += sn9c102_i2c_write(cam, 0x11, 0x01); + + return err; +} + + +static int pas202bcb_set_crop(struct sn9c102_device* cam, + const struct v4l2_rect* rect) +{ + struct sn9c102_sensor* s = &pas202bcb; + int err = 0; + u8 h_start = (u8)(rect->left - s->cropcap.bounds.left) + 4, + v_start = (u8)(rect->top - s->cropcap.bounds.top) + 3; + + err += sn9c102_write_reg(cam, h_start, 0x12); + err += sn9c102_write_reg(cam, v_start, 0x13); + + return err; +} + + +static struct sn9c102_sensor pas202bcb = { + .name = "PAS202BCB", + .maintainer = "Carlos Eduardo Medaglia Dyonisio " + "", + .frequency = SN9C102_I2C_400KHZ | SN9C102_I2C_100KHZ, + .interface = SN9C102_I2C_2WIRES, + .slave_read_id = 0x40, + .slave_write_id = 0x40, + .init = &pas202bcb_init, + .qctrl = { + { + .id = V4L2_CID_RED_BALANCE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "red balance", + .minimum = 0x00, + .maximum = 0x0f, + .step = 0x01, + .default_value = 0x01, + .flags = 0, + }, + { + .id = V4L2_CID_BLUE_BALANCE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "blue balance", + .minimum = 0x00, + .maximum = 0x0f, + .step = 0x01, + .default_value = 0x05, + .flags = 0, + }, + { + .id = V4L2_CID_GAIN, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "global gain", + .minimum = 0x00, + .maximum = 0x1f, + .step = 0x01, + .default_value = 0x0c, + .flags = 0, + }, + { + .id = V4L2_CID_BRIGHTNESS, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "brightness", + .minimum = 0x00, + .maximum = 0x0f, + .step = 0x01, + .default_value = 0x0f, + .flags = 0, + }, + }, + .get_ctrl = &pas202bcb_get_ctrl, + .set_ctrl = &pas202bcb_set_ctrl, + .cropcap = { + .bounds = { + .left = 0, + .top = 0, + .width = 640, + .height = 480, + }, + .defrect = { + .left = 0, + .top = 0, + .width = 640, + .height = 480, + }, + }, + .set_crop = &pas202bcb_set_crop, + .pix_format = { + .width = 640, + .height = 480, + .pixelformat = V4L2_PIX_FMT_SBGGR8, + .priv = 8, + } +}; + + +int sn9c102_probe_pas202bcb(struct sn9c102_device* cam) +{ + int r0 = 0, r1 = 0, err = 0; + unsigned int pid = 0; + + /* + * Minimal initialization to enable the I2C communication + * NOTE: do NOT change the values! + */ + err += sn9c102_write_reg(cam, 0x01, 0x01); /* sensor power down */ + err += sn9c102_write_reg(cam, 0x00, 0x01); /* sensor power on */ + err += sn9c102_write_reg(cam, 0x28, 0x17); /* sensor clock at 24 MHz */ + if (err) + return -EIO; + + r0 = sn9c102_i2c_try_read(cam, &pas202bcb, 0x00); + r1 = sn9c102_i2c_try_read(cam, &pas202bcb, 0x01); + + if (r0 < 0 || r1 < 0) + return -EIO; + + pid = (r0 << 4) | ((r1 & 0xf0) >> 4); + if (pid != 0x017) + return -ENODEV; + + sn9c102_attach_sensor(cam, &pas202bcb); + + return 0; +} diff --git a/drivers/usb/media/sn9c102_sensor.h b/drivers/usb/media/sn9c102_sensor.h index 54a3499b5..3e7e4a257 100644 --- a/drivers/usb/media/sn9c102_sensor.h +++ b/drivers/usb/media/sn9c102_sensor.h @@ -33,7 +33,8 @@ struct sn9c102_sensor; /*****************************************************************************/ -/* OVERVIEW. +/* + OVERVIEW. This is a small interface that allows you to add support for any CCD/CMOS image sensors connected to the SN9C10X bridges. The entire API is documented below. In the most general case, to support a sensor there are three steps @@ -48,26 +49,33 @@ struct sn9c102_sensor; "sn9c102_pas106b.c" is an example of all this stuff. Remember that you do NOT need to touch the source code of the core module for the things to work properly, unless you find bugs or flaws in it. Finally, do not forget to - read the V4L2 API for completeness. */ + read the V4L2 API for completeness. +*/ /*****************************************************************************/ - -/* Probing functions: on success, you must attach the sensor to the camera + +/* + Probing functions: on success, you must attach the sensor to the camera by calling sn9c102_attach_sensor() provided below. To enable the I2C communication, you might need to perform a really basic initialization of the SN9C10X chip by using the write function declared ahead. - Functions must return 0 on success, the appropriate error otherwise. */ + Functions must return 0 on success, the appropriate error otherwise. +*/ extern int sn9c102_probe_pas106b(struct sn9c102_device* cam); +extern int sn9c102_probe_pas202bcb(struct sn9c102_device* cam); extern int sn9c102_probe_tas5110c1b(struct sn9c102_device* cam); extern int sn9c102_probe_tas5130d1b(struct sn9c102_device* cam); -/* Add the above entries to this table. Be sure to add the entry in the right +/* + Add the above entries to this table. Be sure to add the entry in the right place, since, on failure, the next probing routine is called according to - the order of the list below, from top to bottom */ + the order of the list below, from top to bottom. +*/ #define SN9C102_SENSOR_TABLE \ static int (*sn9c102_sensor_table[])(struct sn9c102_device*) = { \ - &sn9c102_probe_pas106b, /* strong detection based on SENSOR vid/pid */\ + &sn9c102_probe_pas106b, /* strong detection based on SENSOR ids */ \ + &sn9c102_probe_pas202bcb, /* strong detection based on SENSOR ids */ \ &sn9c102_probe_tas5110c1b, /* detection based on USB pid/vid */ \ &sn9c102_probe_tas5130d1b, /* detection based on USB pid/vid */ \ NULL, \ @@ -81,28 +89,29 @@ sn9c102_attach_sensor(struct sn9c102_device* cam, /* Each SN9C10X camera has proper PID/VID identifiers. Add them here in case.*/ #define SN9C102_ID_TABLE \ static const struct usb_device_id sn9c102_id_table[] = { \ - { USB_DEVICE(0xc45, 0x6001), }, \ + { USB_DEVICE(0xc45, 0x6001), }, /* TAS5110C1B */ \ { USB_DEVICE(0xc45, 0x6005), }, /* TAS5110C1B */ \ { USB_DEVICE(0xc45, 0x6009), }, /* PAS106B */ \ { USB_DEVICE(0xc45, 0x600d), }, /* PAS106B */ \ { USB_DEVICE(0xc45, 0x6024), }, \ - { USB_DEVICE(0xc45, 0x6025), }, /* TAS5130D1B Maybe also TAS5110C1B */\ - { USB_DEVICE(0xc45, 0x6028), }, /* Maybe PAS202B */ \ - { USB_DEVICE(0xc45, 0x6029), }, \ - { USB_DEVICE(0xc45, 0x602a), }, /* Maybe HV7131[D|E1] */ \ - { USB_DEVICE(0xc45, 0x602c), }, /* Maybe OV7620 */ \ - { USB_DEVICE(0xc45, 0x6030), }, /* Maybe MI03 */ \ - { USB_DEVICE(0xc45, 0x8001), }, \ + { USB_DEVICE(0xc45, 0x6025), }, /* TAS5130D1B and TAS5110C1B */ \ + { USB_DEVICE(0xc45, 0x6028), }, /* PAS202BCB */ \ + { USB_DEVICE(0xc45, 0x6029), }, /* PAS106B */ \ + { USB_DEVICE(0xc45, 0x602a), }, /* HV7131[D|E1] */ \ + { USB_DEVICE(0xc45, 0x602c), }, /* OV7620 */ \ + { USB_DEVICE(0xc45, 0x6030), }, /* MI03 */ \ { } \ }; /*****************************************************************************/ -/* Read/write routines: they always return -1 on error, 0 or the read value +/* + Read/write routines: they always return -1 on error, 0 or the read value otherwise. NOTE that a real read operation is not supported by the SN9C10X chip for some of its registers. To work around this problem, a pseudo-read call is provided instead: it returns the last successfully written value - on the register (0 if it has never been written), the usual -1 on error. */ + on the register (0 if it has never been written), the usual -1 on error. +*/ /* The "try" I2C I/O versions are used when probing the sensor */ extern int sn9c102_i2c_try_write(struct sn9c102_device*,struct sn9c102_sensor*, @@ -110,10 +119,12 @@ extern int sn9c102_i2c_try_write(struct sn9c102_device*,struct sn9c102_sensor*, extern int sn9c102_i2c_try_read(struct sn9c102_device*,struct sn9c102_sensor*, u8 address); -/* This must be used if and only if the sensor doesn't implement the standard - I2C protocol, like the TASC sensors. There a number of good reasons why you - must use the single-byte versions of this function: do not abuse. It writes - n bytes, from data0 to datan, (registers 0x09 - 0x09+n of SN9C10X chip) */ +/* + This must be used if and only if the sensor doesn't implement the standard + I2C protocol. There a number of good reasons why you must use the + single-byte versions of this function: do not abuse. It writes n bytes, + from data0 to datan, (registers 0x09 - 0x09+n of SN9C10X chip). +*/ extern int sn9c102_i2c_try_raw_write(struct sn9c102_device* cam, struct sn9c102_sensor* sensor, u8 n, u8 data0, u8 data1, u8 data2, u8 data3, @@ -127,12 +138,14 @@ extern int sn9c102_i2c_read(struct sn9c102_device*, u8 address); extern int sn9c102_write_reg(struct sn9c102_device*, u8 value, u16 index); extern int sn9c102_pread_reg(struct sn9c102_device*, u16 index); -/* NOTE: there are no debugging functions here. To uniform the output you must +/* + NOTE: there are no debugging functions here. To uniform the output you must use the dev_info()/dev_warn()/dev_err() macros defined in device.h, already included here, the argument being the struct device 'dev' of the sensor structure. Do NOT use these macros before the sensor is attached or the kernel will crash! However you should not need to notify the user about - common errors or other messages, since this is done by the master module. */ + common errors or other messages, since this is done by the master module. +*/ /*****************************************************************************/ @@ -150,24 +163,31 @@ struct sn9c102_sensor { char name[32], /* sensor name */ maintainer[64]; /* name of the mantainer */ - /* These sensor capabilities must be provided if the SN9C10X controller + /* + These sensor capabilities must be provided if the SN9C10X controller needs to communicate through the sensor serial interface by using - at least one of the i2c functions available */ + at least one of the i2c functions available. + */ enum sn9c102_i2c_frequency frequency; enum sn9c102_i2c_interface interface; - /* These identifiers must be provided if the image sensor implements + /* + These identifiers must be provided if the image sensor implements the standard I2C protocol. TASC sensors don't, although they have a serial interface: so this is a case where the "raw" I2C version - could be helpful. */ + could be helpful. + */ u8 slave_read_id, slave_write_id; /* reg. 0x09 */ - /* NOTE: Where not noted,most of the functions below are not mandatory. + /* + NOTE: Where not noted,most of the functions below are not mandatory. Set to null if you do not implement them. If implemented, - they must return 0 on success, the proper error otherwise. */ + they must return 0 on success, the proper error otherwise. + */ int (*init)(struct sn9c102_device* cam); - /* This function is called after the sensor has been attached. + /* + This function is called after the sensor has been attached. It should be used to initialize the sensor only, but may also configure part of the SN9C10X chip if necessary. You don't need to setup picture settings like brightness, contrast, etc.. here, if @@ -177,23 +197,29 @@ struct sn9c102_sensor { specified in the v4l2_queryctrl list of supported controls; Same suggestions apply for other settings, _if_ the corresponding methods are present; if not, the initialization must configure the - sensor according to the default configuration structures below. */ + sensor according to the default configuration structures below. + */ struct v4l2_queryctrl qctrl[V4L2_CID_LASTP1-V4L2_CID_BASE]; - /* Optional list of default controls, defined as indicated in the - V4L2 API. Menu type controls are not handled by this interface. */ + /* + Optional list of default controls, defined as indicated in the + V4L2 API. Menu type controls are not handled by this interface. + */ int (*get_ctrl)(struct sn9c102_device* cam, struct v4l2_control* ctrl); int (*set_ctrl)(struct sn9c102_device* cam, const struct v4l2_control* ctrl); - /* You must implement at least the set_ctrl method if you have defined + /* + You must implement at least the set_ctrl method if you have defined the list above. The returned value must follow the V4L2 specifications for the VIDIOC_G|C_CTRL ioctls. V4L2_CID_H|VCENTER are not supported by this driver, so do not implement them. Also, - passed values are NOT checked to see if they are out of bounds. */ + passed values are NOT checked to see if they are out of bounds. + */ struct v4l2_cropcap cropcap; - /* Think the image sensor as a grid of R,G,B monochromatic pixels + /* + Think the image sensor as a grid of R,G,B monochromatic pixels disposed according to a particular Bayer pattern, which describes the complete array of pixels, from (0,0) to (xmax, ymax). We will use this coordinate system from now on. It is assumed the sensor @@ -221,11 +247,13 @@ struct sn9c102_sensor { NOTE: once you have defined the bounds of the active window (struct cropcap.bounds) you must not change them.anymore. Only 'bounds' and 'defrect' fields are mandatory, other fields - will be ignored. */ + will be ignored. + */ int (*set_crop)(struct sn9c102_device* cam, const struct v4l2_rect* rect); - /* To be called on VIDIOC_C_SETCROP. The core module always calls a + /* + To be called on VIDIOC_C_SETCROP. The core module always calls a default routine which configures the appropriate SN9C10X regs (also scaling), but you may need to override/adjust specific stuff. 'rect' contains width and height values that are multiple of 16: in @@ -237,10 +265,12 @@ struct sn9c102_sensor { frame after each HSYNC or VSYNC, so that the image starts with real RGB data (see regs 0x12,0x13) (having set H_SIZE and, V_SIZE you don't have to care about blank pixels or blank - lines at the end of each line or frame). */ + lines at the end of each line or frame). + */ struct v4l2_pix_format pix_format; - /* What you have to define here are: initial 'width' and 'height' of + /* + What you have to define here are: initial 'width' and 'height' of the target rectangle, the bayer 'pixelformat' and 'priv' which we'll be used to indicate the number of bits per pixel, 8 or 9. Nothing more. @@ -249,20 +279,27 @@ struct sn9c102_sensor { suggest 1/1. NOTE 2: as said above, you have to program the SN9C10X chip to get rid of any blank pixels, so that the output of the sensor - matches the RGB bayer sequence (i.e. BGBGBG...GRGRGR). */ + matches the RGB bayer sequence (i.e. BGBGBG...GRGRGR). + */ const struct device* dev; - /* This is the argument for dev_err(), dev_info() and dev_warn(). It + /* + This is the argument for dev_err(), dev_info() and dev_warn(). It is used for debugging purposes. You must not access the struct - before the sensor is attached. */ + before the sensor is attached. + */ const struct usb_device* usbdev; - /* Points to the usb_device struct after the sensor is attached. - Do not touch unless you know what you are doing. */ + /* + Points to the usb_device struct after the sensor is attached. + Do not touch unless you know what you are doing. + */ - /* Do NOT write to the data below, it's READ ONLY. It is used by the + /* + Do NOT write to the data below, it's READ ONLY. It is used by the core module to store successfully updated values of the above - settings, for rollbacks..etc..in case of errors during atomic I/O */ + settings, for rollbacks..etc..in case of errors during atomic I/O + */ struct v4l2_queryctrl _qctrl[V4L2_CID_LASTP1-V4L2_CID_BASE]; struct v4l2_rect _rect; }; diff --git a/drivers/usb/media/sn9c102_tas5110c1b.c b/drivers/usb/media/sn9c102_tas5110c1b.c index d67470013..68e1b2e0c 100644 --- a/drivers/usb/media/sn9c102_tas5110c1b.c +++ b/drivers/usb/media/sn9c102_tas5110c1b.c @@ -33,15 +33,33 @@ static int tas5110c1b_init(struct sn9c102_device* cam) err += sn9c102_write_reg(cam, 0x44, 0x01); err += sn9c102_write_reg(cam, 0x00, 0x10); err += sn9c102_write_reg(cam, 0x00, 0x11); - err += sn9c102_write_reg(cam, 0x00, 0x14); + err += sn9c102_write_reg(cam, 0x0a, 0x14); err += sn9c102_write_reg(cam, 0x60, 0x17); err += sn9c102_write_reg(cam, 0x06, 0x18); - err += sn9c102_write_reg(cam, 0xcb, 0x19); + err += sn9c102_write_reg(cam, 0xfb, 0x19); + + err += sn9c102_i2c_try_raw_write(cam, &tas5110c1b, 4, 0x11, 0x00, 0xc0, + 0x80, 0, 0); return err; } +static int tas5110c1b_set_ctrl(struct sn9c102_device* cam, + const struct v4l2_control* ctrl) +{ + switch (ctrl->id) { + case V4L2_CID_GAIN: + return sn9c102_i2c_try_raw_write(cam, &tas5110c1b, 4, 0x11, + 0x02, 0x20, + 0xff - (ctrl->value & 0xff), + 0, 0); + default: + return -EINVAL; + } +} + + static int tas5110c1b_set_crop(struct sn9c102_device* cam, const struct v4l2_rect* rect) { @@ -53,6 +71,11 @@ static int tas5110c1b_set_crop(struct sn9c102_device* cam, err += sn9c102_write_reg(cam, h_start, 0x12); err += sn9c102_write_reg(cam, v_start, 0x13); + /* Don't change ! */ + err += sn9c102_write_reg(cam, 0x14, 0x1a); + err += sn9c102_write_reg(cam, 0x0a, 0x1b); + err += sn9c102_write_reg(cam, 0xfb, 0x19); + return err; } @@ -60,7 +83,22 @@ static int tas5110c1b_set_crop(struct sn9c102_device* cam, static struct sn9c102_sensor tas5110c1b = { .name = "TAS5110C1B", .maintainer = "Luca Risolia ", + .frequency = SN9C102_I2C_100KHZ, + .interface = SN9C102_I2C_3WIRES, .init = &tas5110c1b_init, + .qctrl = { + { + .id = V4L2_CID_GAIN, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "global gain", + .minimum = 0x00, + .maximum = 0xff, + .step = 0x01, + .default_value = 0x48, + .flags = 0, + }, + }, + .set_ctrl = &tas5110c1b_set_ctrl, .cropcap = { .bounds = { .left = 0, @@ -90,8 +128,9 @@ int sn9c102_probe_tas5110c1b(struct sn9c102_device* cam) /* This sensor has no identifiers, so let's attach it anyway */ sn9c102_attach_sensor(cam, &tas5110c1b); - /* At the moment, only devices whose PID is 0x6005 have this sensor */ - if (tas5110c1b.usbdev->descriptor.idProduct != 0x6005) + /* At the moment, sensor detection is based on USB pid/vid */ + if (tas5110c1b.usbdev->descriptor.idProduct != 0x6001 && + tas5110c1b.usbdev->descriptor.idProduct != 0x6005) return -ENODEV; return 0; diff --git a/drivers/usb/media/sn9c102_tas5130d1b.c b/drivers/usb/media/sn9c102_tas5130d1b.c index 5f1b0f9a6..0bab19435 100644 --- a/drivers/usb/media/sn9c102_tas5130d1b.c +++ b/drivers/usb/media/sn9c102_tas5130d1b.c @@ -37,38 +37,45 @@ static int tas5130d1b_init(struct sn9c102_device* cam) err += sn9c102_write_reg(cam, 0x00, 0x14); err += sn9c102_write_reg(cam, 0x60, 0x17); err += sn9c102_write_reg(cam, 0x07, 0x18); - err += sn9c102_write_reg(cam, 0x33, 0x19); err += sn9c102_i2c_try_raw_write(cam, &tas5130d1b, 4, 0x11, 0x00, 0x40, 0x47, 0, 0); - err += sn9c102_i2c_try_raw_write(cam, &tas5130d1b, 4, 0x11, 0x02, 0x20, - 0xa9, 0, 0); - err += sn9c102_i2c_try_raw_write(cam, &tas5130d1b, 4, 0x11, 0x00, 0xc0, - 0x49, 0, 0); - err += sn9c102_i2c_try_raw_write(cam, &tas5130d1b, 4, 0x11, 0x02, 0x20, - 0x6c, 0, 0); - err += sn9c102_i2c_try_raw_write(cam, &tas5130d1b, 4, 0x11, 0x00, 0xc0, - 0x08, 0, 0); - err += sn9c102_i2c_try_raw_write(cam, &tas5130d1b, 4, 0x11, 0x00, 0x20, - 0x00, 0, 0); - - err += sn9c102_write_reg(cam, 0x63, 0x19); return err; } +static int tas5130d1b_set_ctrl(struct sn9c102_device* cam, + const struct v4l2_control* ctrl) +{ + switch (ctrl->id) { + case V4L2_CID_GAIN: + return sn9c102_i2c_try_raw_write(cam, &tas5130d1b, 4, 0x11, + 0x02, 0x20, + 0xff - (ctrl->value & 0xff), + 0, 0); + default: + return -EINVAL; + } +} + + static int tas5130d1b_set_crop(struct sn9c102_device* cam, const struct v4l2_rect* rect) { struct sn9c102_sensor* s = &tas5130d1b; - int err = 0; u8 h_start = (u8)(rect->left - s->cropcap.bounds.left) + 104, v_start = (u8)(rect->top - s->cropcap.bounds.top) + 12; + int err = 0; err += sn9c102_write_reg(cam, h_start, 0x12); err += sn9c102_write_reg(cam, v_start, 0x13); + /* Do NOT change! */ + err += sn9c102_write_reg(cam, 0x1d, 0x1a); + err += sn9c102_write_reg(cam, 0x10, 0x1b); + err += sn9c102_write_reg(cam, 0xf3, 0x19); + return err; } @@ -79,6 +86,19 @@ static struct sn9c102_sensor tas5130d1b = { .frequency = SN9C102_I2C_100KHZ, .interface = SN9C102_I2C_3WIRES, .init = &tas5130d1b_init, + .qctrl = { + { + .id = V4L2_CID_GAIN, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "global gain", + .minimum = 0x00, + .maximum = 0xff, + .step = 0x01, + .default_value = 0x00, + .flags = 0, + }, + }, + .set_ctrl = &tas5130d1b_set_ctrl, .cropcap = { .bounds = { .left = 0, @@ -108,13 +128,9 @@ int sn9c102_probe_tas5130d1b(struct sn9c102_device* cam) /* This sensor has no identifiers, so let's attach it anyway */ sn9c102_attach_sensor(cam, &tas5130d1b); - /* At the moment, only devices whose PID is 0x6025 have this sensor */ + /* At the moment, sensor detection is based on USB pid/vid */ if (tas5130d1b.usbdev->descriptor.idProduct != 0x6025) return -ENODEV; - dev_info(tas5130d1b.dev, "TAS5130D1B detected, but the support for it " - "is disabled at the moment - needs further " - "testing -\n"); - - return -ENODEV; + return 0; } diff --git a/drivers/usb/misc/phidgetservo.c b/drivers/usb/misc/phidgetservo.c index b89b53401..8cca7e747 100644 --- a/drivers/usb/misc/phidgetservo.c +++ b/drivers/usb/misc/phidgetservo.c @@ -12,8 +12,6 @@ * controllers available at: http://www.phidgets.com/ * * Note that the driver takes input as: degrees.minutes - * -23 < degrees < 203 - * 0 < minutes < 59 * * CAUTION: Generally you should use 0 < degrees < 180 as anything else * is probably beyond the range of your servo and may damage it. @@ -21,6 +19,10 @@ * Jun 16, 2004: Sean Young * - cleanups * - was using memory after kfree() + * Aug 8, 2004: Sean Young + * - set the highest angle as high as the hardware allows, there are + * some odd servos out there + * */ #include @@ -87,6 +89,9 @@ change_position_v30(struct phidget_servo *servo, int servo_no, int degrees, int retval; unsigned char *buffer; + if (degrees < -23 || degrees > 362) + return -EINVAL; + buffer = kmalloc(6, GFP_KERNEL); if (!buffer) { dev_err(&servo->udev->dev, "%s - out of memory\n", @@ -157,6 +162,9 @@ change_position_v20(struct phidget_servo *servo, int servo_no, int degrees, int retval; unsigned char *buffer; + if (degrees < -23 || degrees > 278) + return -EINVAL; + buffer = kmalloc(2, GFP_KERNEL); if (!buffer) { dev_err(&servo->udev->dev, "%s - out of memory\n", @@ -212,10 +220,8 @@ static ssize_t set_servo##value (struct device *dev, \ return -EINVAL; \ } \ \ - if (degrees < -23 || degrees > (180 + 23) || \ - minutes < 0 || minutes > 59) { \ + if (minutes < 0 || minutes > 59) \ return -EINVAL; \ - } \ \ if (servo->type & SERVO_VERSION_30) \ retval = change_position_v30 (servo, value, degrees, \ diff --git a/drivers/usb/net/catc.c b/drivers/usb/net/catc.c index de9bf863e..455fe6e3b 100644 --- a/drivers/usb/net/catc.c +++ b/drivers/usb/net/catc.c @@ -242,7 +242,7 @@ static void catc_rx_done(struct urb *urb, struct pt_regs *regs) do { if(!catc->is_f5u011) { - pkt_len = le16_to_cpup((u16*)pkt_start); + pkt_len = le16_to_cpup((__le16*)pkt_start); if (pkt_len > urb->actual_length) { catc->stats.rx_length_errors++; catc->stats.rx_errors++; @@ -303,7 +303,7 @@ static void catc_irq_done(struct urb *urb, struct pt_regs *regs) else if (data[1] & 0x20) linksts = LinkBad; } else { - hasdata = (unsigned int)(be16_to_cpup((u16*)data) & 0x0fff); + hasdata = (unsigned int)(be16_to_cpup((__be16*)data) & 0x0fff); if (data[0] == 0x90) linksts = LinkGood; else if (data[0] == 0xA0) diff --git a/drivers/usb/serial/io_edgeport.c b/drivers/usb/serial/io_edgeport.c index e727628bd..87f99a338 100644 --- a/drivers/usb/serial/io_edgeport.c +++ b/drivers/usb/serial/io_edgeport.c @@ -479,7 +479,7 @@ static void get_boot_desc (struct edgeport_serial *edge_serial); static void load_application_firmware (struct edgeport_serial *edge_serial); -static void unicode_to_ascii (char *string, short *unicode, int unicode_size); +static void unicode_to_ascii (char *string, __le16 *unicode, int unicode_size); @@ -504,7 +504,7 @@ static void update_edgeport_E2PROM (struct edgeport_serial *edge_serial) __u32 BootNewVer; __u8 BootMajorVersion; __u8 BootMinorVersion; - __u16 BootBuildNumber; + __le16 BootBuildNumber; __u8 *BootImage; __u32 BootSize; struct edge_firmware_image_record *record; @@ -653,7 +653,7 @@ static void get_product_info(struct edgeport_serial *edge_serial) memset (product_info, 0, sizeof(struct edgeport_product_info)); - product_info->ProductId = (__u16)(edge_serial->serial->dev->descriptor.idProduct & ~ION_DEVICE_ID_GENERATION_2); + product_info->ProductId = (__u16)(edge_serial->serial->dev->descriptor.idProduct & ~ION_DEVICE_ID_80251_NETCHIP); product_info->NumPorts = edge_serial->manuf_descriptor.NumPorts; product_info->ProdInfoVer = 0; @@ -669,7 +669,7 @@ static void get_product_info(struct edgeport_serial *edge_serial) memcpy(product_info->ManufactureDescDate, edge_serial->manuf_descriptor.DescDate, sizeof(edge_serial->manuf_descriptor.DescDate)); // check if this is 2nd generation hardware - if (edge_serial->serial->dev->descriptor.idProduct & ION_DEVICE_ID_GENERATION_2) { + if (edge_serial->serial->dev->descriptor.idProduct & ION_DEVICE_ID_80251_NETCHIP) { product_info->FirmwareMajorVersion = OperationalCodeImageVersion_GEN2.MajorVersion; product_info->FirmwareMinorVersion = OperationalCodeImageVersion_GEN2.MinorVersion; product_info->FirmwareBuildNumber = cpu_to_le16(OperationalCodeImageVersion_GEN2.BuildNumber); @@ -900,12 +900,7 @@ static void edge_bulk_out_data_callback (struct urb *urb, struct pt_regs *regs) if (tty && edge_port->open) { /* let the tty driver wakeup if it has a special write_wakeup function */ - if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && tty->ldisc.write_wakeup) { - (tty->ldisc.write_wakeup)(tty); - } - - /* tell the tty driver that something has changed */ - wake_up_interruptible(&tty->write_wait); + tty_wakeup(tty); } // Release the Write URB @@ -1389,7 +1384,7 @@ static void send_more_port_data(struct edgeport_serial *edge_serial, struct edge // to bother queueing a write. If it's too small, say a few bytes, // it's better to wait for more credits so we can do a larger // write. - if (edge_port->txCredits < EDGE_FW_GET_TX_CREDITS_SEND_THRESHOLD(edge_port->maxTxCredits)) { + if (edge_port->txCredits < EDGE_FW_GET_TX_CREDITS_SEND_THRESHOLD(edge_port->maxTxCredits,EDGE_FW_BULK_MAX_PACKET_SIZE)) { dbg("%s(%d) Not enough credit - fifo %d TxCredit %d", __FUNCTION__, edge_port->port->number, fifo->count, edge_port->txCredits ); return; } @@ -2747,12 +2742,15 @@ static void change_port_settings (struct edgeport_port *edge_port, struct termio * ASCII range, but it's only for debugging... * NOTE: expects the unicode in LE format ****************************************************************************/ -static void unicode_to_ascii (char *string, short *unicode, int unicode_size) +static void unicode_to_ascii (char *string, __le16 *unicode, int unicode_size) { int i; - for (i = 0; i < unicode_size; ++i) { + + if (unicode_size <= 0) + return; + + for (i = 0; i < unicode_size; ++i) string[i] = (char)(le16_to_cpu(unicode[i])); - } string[unicode_size] = 0x00; } @@ -3007,9 +3005,6 @@ static void edge_shutdown (struct usb_serial *serial) static int __init edgeport_init(void) { int retval; - retval = usb_serial_register(&edgeport_1port_device); - if (retval) - goto failed_1port_device_register; retval = usb_serial_register(&edgeport_2port_device); if (retval) goto failed_2port_device_register; @@ -3031,8 +3026,6 @@ failed_8port_device_register: failed_4port_device_register: usb_serial_deregister(&edgeport_2port_device); failed_2port_device_register: - usb_serial_deregister(&edgeport_1port_device); -failed_1port_device_register: return retval; } @@ -3045,7 +3038,6 @@ failed_1port_device_register: static void __exit edgeport_exit (void) { usb_deregister (&io_driver); - usb_serial_deregister (&edgeport_1port_device); usb_serial_deregister (&edgeport_2port_device); usb_serial_deregister (&edgeport_4port_device); usb_serial_deregister (&edgeport_8port_device); diff --git a/drivers/usb/serial/io_edgeport.h b/drivers/usb/serial/io_edgeport.h index 386139d45..5112d7aac 100644 --- a/drivers/usb/serial/io_edgeport.h +++ b/drivers/usb/serial/io_edgeport.h @@ -107,11 +107,11 @@ struct edgeport_product_info { __u8 BootMajorVersion; /* Boot Firmware version: xx. */ __u8 BootMinorVersion; /* yy. */ - __u16 BootBuildNumber; /* zzzz (LE format) */ + __le16 BootBuildNumber; /* zzzz (LE format) */ __u8 FirmwareMajorVersion; /* Operational Firmware version:xx. */ __u8 FirmwareMinorVersion; /* yy. */ - __u16 FirmwareBuildNumber; /* zzzz (LE format) */ + __le16 FirmwareBuildNumber; /* zzzz (LE format) */ __u8 ManufactureDescDate[3]; /* MM/DD/YY when descriptor template was compiled */ __u8 Unused1[1]; /* Available */ diff --git a/drivers/usb/serial/io_fw_down.h b/drivers/usb/serial/io_fw_down.h index 245740eb1..d01ec7b22 100644 --- a/drivers/usb/serial/io_fw_down.h +++ b/drivers/usb/serial/io_fw_down.h @@ -17,7 +17,7 @@ unsigned short Addr; unsigned short Len; unsigned char Data[0]; - } __attribute ((packed)); + } __attribute__ ((packed)); struct edge_firmware_version_info { unsigned char MajorVersion; @@ -36,11 +36,11 @@ static unsigned char IMAGE_ARRAY_NAME[] = { // Segment #1, Start Address 00ff0000, Length 6 0xff,0x00,0x00,0x00,0x06,0x00, - 0x02, 0x00, 0x80, 0x02, 0x49, 0x39, + 0x02, 0x00, 0x80, 0x02, 0x44, 0xb0, // Segment #2, Start Address 00ff000b, Length 3 0xff,0x00,0x0b,0x00,0x03,0x00, - 0x02, 0x44, 0x1a, + 0x02, 0x44, 0x72, // Segment #3, Start Address 00ff0013, Length 3 0xff,0x00,0x13,0x00,0x03,0x00, @@ -72,11 +72,11 @@ static unsigned char IMAGE_ARRAY_NAME[] = { // Segment #10, Start Address 00ff004b, Length 3 0xff,0x00,0x4b,0x00,0x03,0x00, - 0x02, 0x70, 0xd4, + 0x02, 0x6e, 0xc4, // Segment #11, Start Address 00ff0053, Length 3 0xff,0x00,0x53,0x00,0x03,0x00, - 0x02, 0x77, 0x9d, + 0x02, 0x75, 0x8d, // Segment #12, Start Address 00ff007b, Length 3 0xff,0x00,0x7b,0x00,0x03,0x00, @@ -86,657 +86,745 @@ static unsigned char IMAGE_ARRAY_NAME[] = { 0xff,0x00,0x80,0x00,0x07,0x00, 0x7e, 0x14, 0x00, 0x00, 0x02, 0x40, 0x51, -// Segment #14, Start Address 00ff4000, Length 15920 -0xff,0x00,0x00,0x40,0x30,0x3e, +// Segment #14, Start Address 00ff3000, Length 2178 +0xff,0x00,0x00,0x30,0x82,0x08, + 0x12, 0x37, 0x28, 0x12, 0x30, 0x3e, 0x12, 0x30, 0x54, 0x12, 0x30, 0xe5, 0x12, 0x31, 0x68, 0x12, + 0x35, 0x20, 0x12, 0x38, 0x58, 0x12, 0x31, 0x15, 0x12, 0x31, 0x40, 0x12, 0x30, 0xa0, 0x80, 0xe0, + 0xe5, 0x23, 0x60, 0x19, 0x7e, 0x14, 0x00, 0x00, 0x09, 0xb1, 0x01, 0xcf, 0xb4, 0x00, 0x02, 0x80, + 0x05, 0x14, 0x19, 0xb1, 0x01, 0xcf, 0xa5, 0x0b, 0xbe, 0x31, 0x2f, 0x78, 0xeb, 0x22, 0xc2, 0xaf, + 0x7e, 0xb3, 0x3f, 0xf1, 0xb4, 0x01, 0x0a, 0xc0, 0xf1, 0x75, 0xf1, 0x02, 0x12, 0x70, 0xef, 0xd0, + 0xf1, 0xd2, 0xaf, 0x22, 0xc2, 0xaf, 0xe5, 0x22, 0x60, 0x43, 0x7e, 0x07, 0x01, 0xe1, 0xbe, 0x04, + 0x03, 0x80, 0x38, 0x39, 0x7e, 0x04, 0x80, 0x00, 0x7e, 0x20, 0x00, 0x13, 0x50, 0x21, 0x09, 0xa0, + 0x00, 0x04, 0x4e, 0xa0, 0x05, 0x19, 0xa0, 0x00, 0x04, 0x0a, 0x32, 0x09, 0x53, 0x67, 0x8e, 0x5e, + 0x51, 0x27, 0x68, 0x0b, 0x09, 0xa0, 0x00, 0x10, 0x4e, 0xa0, 0x01, 0x19, 0xa0, 0x00, 0x10, 0x2e, + 0x04, 0x01, 0x00, 0xa5, 0x0a, 0xbe, 0x21, 0x2f, 0x78, 0xd1, 0x75, 0x22, 0x00, 0xd2, 0xaf, 0x22, + 0xc2, 0xaf, 0x7e, 0x20, 0x00, 0x7e, 0x30, 0x01, 0x7c, 0xb2, 0x23, 0x0a, 0x2b, 0x49, 0x32, 0x01, + 0x8f, 0xbe, 0x34, 0x00, 0x00, 0x68, 0x12, 0x7e, 0xb1, 0x21, 0xa5, 0x4b, 0x7a, 0xb1, 0x21, 0xca, + 0x19, 0x49, 0x22, 0x30, 0xd5, 0x99, 0x24, 0xda, 0x19, 0x3e, 0x30, 0xa5, 0x0a, 0xbe, 0x21, 0x2f, + 0x78, 0xd6, 0xd2, 0xaf, 0x22, 0x46, 0x0f, 0x49, 0x67, 0x4c, 0xbf, 0x50, 0x17, 0x53, 0x6f, 0x56, + 0xc7, 0x5a, 0x1f, 0x5d, 0x77, 0xc2, 0xaf, 0xe5, 0x32, 0x60, 0x14, 0x7e, 0x20, 0x00, 0x13, 0x50, + 0x07, 0xca, 0xb8, 0x12, 0x31, 0x02, 0xda, 0xb8, 0xa5, 0x0a, 0xbe, 0x21, 0x2f, 0x78, 0xef, 0xd2, + 0xaf, 0x22, 0xca, 0x28, 0x12, 0x67, 0xab, 0xda, 0x28, 0x40, 0x09, 0x0a, 0x22, 0x09, 0xb2, 0x67, + 0x8e, 0xf4, 0x52, 0x32, 0x22, 0xc2, 0xaf, 0xe5, 0x34, 0x60, 0x14, 0x7e, 0x20, 0x00, 0x13, 0x50, + 0x07, 0xca, 0xb8, 0x12, 0x31, 0x32, 0xda, 0xb8, 0xa5, 0x0a, 0xbe, 0x21, 0x2f, 0x78, 0xef, 0xd2, + 0xaf, 0x22, 0xca, 0x28, 0x0a, 0x22, 0x09, 0x42, 0x00, 0x3e, 0x12, 0x69, 0xc2, 0xda, 0x28, 0x22, + 0xc2, 0xaf, 0xe5, 0x35, 0x60, 0x14, 0x7e, 0x20, 0x00, 0x13, 0x50, 0x07, 0xca, 0xb8, 0x12, 0x31, + 0x5d, 0xda, 0xb8, 0xa5, 0x0a, 0xbe, 0x21, 0x2f, 0x78, 0xef, 0xd2, 0xaf, 0x22, 0xca, 0x28, 0x7e, + 0x40, 0x00, 0x12, 0x6c, 0x5b, 0xda, 0x28, 0x22, 0xc2, 0xaf, 0xe5, 0x23, 0x60, 0x14, 0x7e, 0x20, + 0x00, 0x13, 0x50, 0x07, 0xca, 0xb8, 0x12, 0x31, 0x85, 0xda, 0xb8, 0xa5, 0x0a, 0xbe, 0x21, 0x2f, + 0x78, 0xef, 0xd2, 0xaf, 0x22, 0x7c, 0xb2, 0x23, 0x0a, 0x2b, 0x49, 0x22, 0x31, 0x90, 0x89, 0x24, + 0x31, 0xa0, 0x32, 0x10, 0x32, 0x80, 0x32, 0xf0, 0x33, 0x60, 0x33, 0xd0, 0x34, 0x40, 0x34, 0xb0, + 0x7e, 0x27, 0x01, 0x8f, 0xbe, 0x24, 0x00, 0x00, 0x78, 0x24, 0x7e, 0x24, 0x80, 0x00, 0x09, 0xb2, + 0x00, 0x14, 0xca, 0xb8, 0x5e, 0xb0, 0x1e, 0xda, 0xb8, 0x68, 0x0b, 0xca, 0x19, 0x7d, 0x12, 0x12, + 0x49, 0x30, 0x7d, 0x21, 0xda, 0x19, 0x30, 0xe6, 0x18, 0x7e, 0x60, 0x00, 0x80, 0x1e, 0xbe, 0x27, + 0x01, 0x9f, 0x68, 0x0d, 0x7a, 0x27, 0x01, 0x9f, 0x7e, 0x60, 0x9c, 0x7a, 0x63, 0x01, 0xcf, 0x80, + 0x2e, 0x7e, 0x63, 0x01, 0xcf, 0xa5, 0xbe, 0x00, 0x26, 0x7e, 0x60, 0x01, 0x7e, 0xb0, 0x00, 0x7e, + 0xa0, 0xc8, 0x12, 0x62, 0xb6, 0x40, 0x18, 0x75, 0x31, 0xb3, 0x12, 0x7c, 0x15, 0xc2, 0x18, 0x6c, + 0x00, 0x7a, 0x03, 0x01, 0xcf, 0xc0, 0xf1, 0x75, 0xf1, 0x01, 0x12, 0x6f, 0xd9, 0xd0, 0xf1, 0x22, + 0x7e, 0x27, 0x01, 0x91, 0xbe, 0x24, 0x00, 0x00, 0x78, 0x24, 0x7e, 0x24, 0x81, 0x00, 0x09, 0xb2, + 0x00, 0x14, 0xca, 0xb8, 0x5e, 0xb0, 0x1e, 0xda, 0xb8, 0x68, 0x0b, 0xca, 0x19, 0x7d, 0x12, 0x12, + 0x4c, 0x88, 0x7d, 0x21, 0xda, 0x19, 0x30, 0xe6, 0x18, 0x7e, 0x60, 0x00, 0x80, 0x1e, 0xbe, 0x27, + 0x01, 0xa1, 0x68, 0x0d, 0x7a, 0x27, 0x01, 0xa1, 0x7e, 0x60, 0x9c, 0x7a, 0x63, 0x01, 0xd0, 0x80, + 0x2e, 0x7e, 0x63, 0x01, 0xd0, 0xa5, 0xbe, 0x00, 0x26, 0x7e, 0x60, 0x01, 0x7e, 0xb0, 0x00, 0x7e, + 0xa0, 0xc8, 0x12, 0x62, 0xb6, 0x40, 0x18, 0x75, 0x31, 0xb3, 0x12, 0x7c, 0x15, 0xc2, 0x19, 0x6c, + 0x00, 0x7a, 0x03, 0x01, 0xd0, 0xc0, 0xf1, 0x75, 0xf1, 0x01, 0x12, 0x6f, 0xd9, 0xd0, 0xf1, 0x22, + 0x7e, 0x27, 0x01, 0x93, 0xbe, 0x24, 0x00, 0x00, 0x78, 0x24, 0x7e, 0x24, 0x82, 0x00, 0x09, 0xb2, + 0x00, 0x14, 0xca, 0xb8, 0x5e, 0xb0, 0x1e, 0xda, 0xb8, 0x68, 0x0b, 0xca, 0x19, 0x7d, 0x12, 0x12, + 0x4f, 0xe0, 0x7d, 0x21, 0xda, 0x19, 0x30, 0xe6, 0x18, 0x7e, 0x60, 0x00, 0x80, 0x1e, 0xbe, 0x27, + 0x01, 0xa3, 0x68, 0x0d, 0x7a, 0x27, 0x01, 0xa3, 0x7e, 0x60, 0x9c, 0x7a, 0x63, 0x01, 0xd1, 0x80, + 0x2e, 0x7e, 0x63, 0x01, 0xd1, 0xa5, 0xbe, 0x00, 0x26, 0x7e, 0x60, 0x01, 0x7e, 0xb0, 0x00, 0x7e, + 0xa0, 0xc8, 0x12, 0x62, 0xb6, 0x40, 0x18, 0x75, 0x31, 0xb3, 0x12, 0x7c, 0x15, 0xc2, 0x1a, 0x6c, + 0x00, 0x7a, 0x03, 0x01, 0xd1, 0xc0, 0xf1, 0x75, 0xf1, 0x01, 0x12, 0x6f, 0xd9, 0xd0, 0xf1, 0x22, + 0x7e, 0x27, 0x01, 0x95, 0xbe, 0x24, 0x00, 0x00, 0x78, 0x24, 0x7e, 0x24, 0x83, 0x00, 0x09, 0xb2, + 0x00, 0x14, 0xca, 0xb8, 0x5e, 0xb0, 0x1e, 0xda, 0xb8, 0x68, 0x0b, 0xca, 0x19, 0x7d, 0x12, 0x12, + 0x53, 0x38, 0x7d, 0x21, 0xda, 0x19, 0x30, 0xe6, 0x18, 0x7e, 0x60, 0x00, 0x80, 0x1e, 0xbe, 0x27, + 0x01, 0xa5, 0x68, 0x0d, 0x7a, 0x27, 0x01, 0xa5, 0x7e, 0x60, 0x9c, 0x7a, 0x63, 0x01, 0xd2, 0x80, + 0x2e, 0x7e, 0x63, 0x01, 0xd2, 0xa5, 0xbe, 0x00, 0x26, 0x7e, 0x60, 0x01, 0x7e, 0xb0, 0x00, 0x7e, + 0xa0, 0xc8, 0x12, 0x62, 0xb6, 0x40, 0x18, 0x75, 0x31, 0xb3, 0x12, 0x7c, 0x15, 0xc2, 0x1b, 0x6c, + 0x00, 0x7a, 0x03, 0x01, 0xd2, 0xc0, 0xf1, 0x75, 0xf1, 0x01, 0x12, 0x6f, 0xd9, 0xd0, 0xf1, 0x22, + 0x7e, 0x27, 0x01, 0x97, 0xbe, 0x24, 0x00, 0x00, 0x78, 0x24, 0x7e, 0x24, 0x84, 0x00, 0x09, 0xb2, + 0x00, 0x14, 0xca, 0xb8, 0x5e, 0xb0, 0x1e, 0xda, 0xb8, 0x68, 0x0b, 0xca, 0x19, 0x7d, 0x12, 0x12, + 0x56, 0x90, 0x7d, 0x21, 0xda, 0x19, 0x30, 0xe6, 0x18, 0x7e, 0x60, 0x00, 0x80, 0x1e, 0xbe, 0x27, + 0x01, 0xa7, 0x68, 0x0d, 0x7a, 0x27, 0x01, 0xa7, 0x7e, 0x60, 0x9c, 0x7a, 0x63, 0x01, 0xd3, 0x80, + 0x2e, 0x7e, 0x63, 0x01, 0xd3, 0xa5, 0xbe, 0x00, 0x26, 0x7e, 0x60, 0x01, 0x7e, 0xb0, 0x00, 0x7e, + 0xa0, 0xc8, 0x12, 0x62, 0xb6, 0x40, 0x18, 0x75, 0x31, 0xb3, 0x12, 0x7c, 0x15, 0xc2, 0x1c, 0x6c, + 0x00, 0x7a, 0x03, 0x01, 0xd3, 0xc0, 0xf1, 0x75, 0xf1, 0x01, 0x12, 0x6f, 0xd9, 0xd0, 0xf1, 0x22, + 0x7e, 0x27, 0x01, 0x99, 0xbe, 0x24, 0x00, 0x00, 0x78, 0x24, 0x7e, 0x24, 0x85, 0x00, 0x09, 0xb2, + 0x00, 0x14, 0xca, 0xb8, 0x5e, 0xb0, 0x1e, 0xda, 0xb8, 0x68, 0x0b, 0xca, 0x19, 0x7d, 0x12, 0x12, + 0x59, 0xe8, 0x7d, 0x21, 0xda, 0x19, 0x30, 0xe6, 0x18, 0x7e, 0x60, 0x00, 0x80, 0x1e, 0xbe, 0x27, + 0x01, 0xa9, 0x68, 0x0d, 0x7a, 0x27, 0x01, 0xa9, 0x7e, 0x60, 0x9c, 0x7a, 0x63, 0x01, 0xd4, 0x80, + 0x2e, 0x7e, 0x63, 0x01, 0xd4, 0xa5, 0xbe, 0x00, 0x26, 0x7e, 0x60, 0x01, 0x7e, 0xb0, 0x00, 0x7e, + 0xa0, 0xc8, 0x12, 0x62, 0xb6, 0x40, 0x18, 0x75, 0x31, 0xb3, 0x12, 0x7c, 0x15, 0xc2, 0x1d, 0x6c, + 0x00, 0x7a, 0x03, 0x01, 0xd4, 0xc0, 0xf1, 0x75, 0xf1, 0x01, 0x12, 0x6f, 0xd9, 0xd0, 0xf1, 0x22, + 0x7e, 0x27, 0x01, 0x9b, 0xbe, 0x24, 0x00, 0x00, 0x78, 0x24, 0x7e, 0x24, 0x86, 0x00, 0x09, 0xb2, + 0x00, 0x14, 0xca, 0xb8, 0x5e, 0xb0, 0x1e, 0xda, 0xb8, 0x68, 0x0b, 0xca, 0x19, 0x7d, 0x12, 0x12, + 0x5d, 0x40, 0x7d, 0x21, 0xda, 0x19, 0x30, 0xe6, 0x18, 0x7e, 0x60, 0x00, 0x80, 0x1e, 0xbe, 0x27, + 0x01, 0xab, 0x68, 0x0d, 0x7a, 0x27, 0x01, 0xab, 0x7e, 0x60, 0x9c, 0x7a, 0x63, 0x01, 0xd5, 0x80, + 0x2e, 0x7e, 0x63, 0x01, 0xd5, 0xa5, 0xbe, 0x00, 0x26, 0x7e, 0x60, 0x01, 0x7e, 0xb0, 0x00, 0x7e, + 0xa0, 0xc8, 0x12, 0x62, 0xb6, 0x40, 0x18, 0x75, 0x31, 0xb3, 0x12, 0x7c, 0x15, 0xc2, 0x1e, 0x6c, + 0x00, 0x7a, 0x03, 0x01, 0xd5, 0xc0, 0xf1, 0x75, 0xf1, 0x01, 0x12, 0x6f, 0xd9, 0xd0, 0xf1, 0x22, + 0x7e, 0x27, 0x01, 0x9d, 0xbe, 0x24, 0x00, 0x00, 0x78, 0x24, 0x7e, 0x24, 0x87, 0x00, 0x09, 0xb2, + 0x00, 0x14, 0xca, 0xb8, 0x5e, 0xb0, 0x1e, 0xda, 0xb8, 0x68, 0x0b, 0xca, 0x19, 0x7d, 0x12, 0x12, + 0x60, 0x98, 0x7d, 0x21, 0xda, 0x19, 0x30, 0xe6, 0x18, 0x7e, 0x60, 0x00, 0x80, 0x1e, 0xbe, 0x27, + 0x01, 0xad, 0x68, 0x0d, 0x7a, 0x27, 0x01, 0xad, 0x7e, 0x60, 0x9c, 0x7a, 0x63, 0x01, 0xd6, 0x80, + 0x2e, 0x7e, 0x63, 0x01, 0xd6, 0xa5, 0xbe, 0x00, 0x26, 0x7e, 0x60, 0x01, 0x7e, 0xb0, 0x00, 0x7e, + 0xa0, 0xc8, 0x12, 0x62, 0xb6, 0x40, 0x18, 0x75, 0x31, 0xb3, 0x12, 0x7c, 0x15, 0xc2, 0x1f, 0x6c, + 0x00, 0x7a, 0x03, 0x01, 0xd6, 0xc0, 0xf1, 0x75, 0xf1, 0x01, 0x12, 0x6f, 0xd9, 0xd0, 0xf1, 0x22, + 0xc2, 0xaf, 0xe5, 0x24, 0x60, 0x14, 0x7e, 0x20, 0x00, 0x13, 0x50, 0x07, 0xca, 0xb8, 0x12, 0x35, + 0x3d, 0xda, 0xb8, 0xa5, 0x0a, 0xbe, 0x21, 0x2f, 0x78, 0xef, 0xd2, 0xaf, 0x22, 0x7c, 0xb2, 0x23, + 0x0a, 0x2b, 0x49, 0x22, 0x35, 0x48, 0x89, 0x24, 0x35, 0x58, 0x35, 0x92, 0x35, 0xcc, 0x36, 0x06, + 0x36, 0x40, 0x36, 0x7a, 0x36, 0xb4, 0x36, 0xee, 0x7e, 0x24, 0x80, 0x00, 0x09, 0xb2, 0x00, 0x14, + 0xca, 0xb8, 0x5e, 0xb0, 0x1e, 0xda, 0xb8, 0x68, 0x0b, 0xca, 0x19, 0x7d, 0x12, 0x12, 0x49, 0x30, + 0x7d, 0x21, 0xda, 0x19, 0x5e, 0xb0, 0x01, 0x7e, 0xa0, 0x90, 0x12, 0x62, 0x93, 0x40, 0x12, 0x75, + 0x31, 0xb8, 0x12, 0x7c, 0x15, 0xc2, 0x20, 0xc0, 0xf1, 0x75, 0xf1, 0x01, 0x12, 0x6f, 0xd9, 0xd0, + 0xf1, 0x22, 0x7e, 0x24, 0x81, 0x00, 0x09, 0xb2, 0x00, 0x14, 0xca, 0xb8, 0x5e, 0xb0, 0x1e, 0xda, + 0xb8, 0x68, 0x0b, 0xca, 0x19, 0x7d, 0x12, 0x12, 0x4c, 0x88, 0x7d, 0x21, 0xda, 0x19, 0x5e, 0xb0, + 0x01, 0x7e, 0xa0, 0x90, 0x12, 0x62, 0x93, 0x40, 0x12, 0x75, 0x31, 0xb8, 0x12, 0x7c, 0x15, 0xc2, + 0x21, 0xc0, 0xf1, 0x75, 0xf1, 0x01, 0x12, 0x6f, 0xd9, 0xd0, 0xf1, 0x22, 0x7e, 0x24, 0x82, 0x00, + 0x09, 0xb2, 0x00, 0x14, 0xca, 0xb8, 0x5e, 0xb0, 0x1e, 0xda, 0xb8, 0x68, 0x0b, 0xca, 0x19, 0x7d, + 0x12, 0x12, 0x4f, 0xe0, 0x7d, 0x21, 0xda, 0x19, 0x5e, 0xb0, 0x01, 0x7e, 0xa0, 0x90, 0x12, 0x62, + 0x93, 0x40, 0x12, 0x75, 0x31, 0xb8, 0x12, 0x7c, 0x15, 0xc2, 0x22, 0xc0, 0xf1, 0x75, 0xf1, 0x01, + 0x12, 0x6f, 0xd9, 0xd0, 0xf1, 0x22, 0x7e, 0x24, 0x83, 0x00, 0x09, 0xb2, 0x00, 0x14, 0xca, 0xb8, + 0x5e, 0xb0, 0x1e, 0xda, 0xb8, 0x68, 0x0b, 0xca, 0x19, 0x7d, 0x12, 0x12, 0x53, 0x38, 0x7d, 0x21, + 0xda, 0x19, 0x5e, 0xb0, 0x01, 0x7e, 0xa0, 0x90, 0x12, 0x62, 0x93, 0x40, 0x12, 0x75, 0x31, 0xb8, + 0x12, 0x7c, 0x15, 0xc2, 0x23, 0xc0, 0xf1, 0x75, 0xf1, 0x01, 0x12, 0x6f, 0xd9, 0xd0, 0xf1, 0x22, + 0x7e, 0x24, 0x84, 0x00, 0x09, 0xb2, 0x00, 0x14, 0xca, 0xb8, 0x5e, 0xb0, 0x1e, 0xda, 0xb8, 0x68, + 0x0b, 0xca, 0x19, 0x7d, 0x12, 0x12, 0x56, 0x90, 0x7d, 0x21, 0xda, 0x19, 0x5e, 0xb0, 0x01, 0x7e, + 0xa0, 0x90, 0x12, 0x62, 0x93, 0x40, 0x12, 0x75, 0x31, 0xb8, 0x12, 0x7c, 0x15, 0xc2, 0x24, 0xc0, + 0xf1, 0x75, 0xf1, 0x01, 0x12, 0x6f, 0xd9, 0xd0, 0xf1, 0x22, 0x7e, 0x24, 0x85, 0x00, 0x09, 0xb2, + 0x00, 0x14, 0xca, 0xb8, 0x5e, 0xb0, 0x1e, 0xda, 0xb8, 0x68, 0x0b, 0xca, 0x19, 0x7d, 0x12, 0x12, + 0x59, 0xe8, 0x7d, 0x21, 0xda, 0x19, 0x5e, 0xb0, 0x01, 0x7e, 0xa0, 0x90, 0x12, 0x62, 0x93, 0x40, + 0x12, 0x75, 0x31, 0xb8, 0x12, 0x7c, 0x15, 0xc2, 0x25, 0xc0, 0xf1, 0x75, 0xf1, 0x01, 0x12, 0x6f, + 0xd9, 0xd0, 0xf1, 0x22, 0x7e, 0x24, 0x86, 0x00, 0x09, 0xb2, 0x00, 0x14, 0xca, 0xb8, 0x5e, 0xb0, + 0x1e, 0xda, 0xb8, 0x68, 0x0b, 0xca, 0x19, 0x7d, 0x12, 0x12, 0x5d, 0x40, 0x7d, 0x21, 0xda, 0x19, + 0x5e, 0xb0, 0x01, 0x7e, 0xa0, 0x90, 0x12, 0x62, 0x93, 0x40, 0x12, 0x75, 0x31, 0xb8, 0x12, 0x7c, + 0x15, 0xc2, 0x26, 0xc0, 0xf1, 0x75, 0xf1, 0x01, 0x12, 0x6f, 0xd9, 0xd0, 0xf1, 0x22, 0x7e, 0x24, + 0x87, 0x00, 0x09, 0xb2, 0x00, 0x14, 0xca, 0xb8, 0x5e, 0xb0, 0x1e, 0xda, 0xb8, 0x68, 0x0b, 0xca, + 0x19, 0x7d, 0x12, 0x12, 0x60, 0x98, 0x7d, 0x21, 0xda, 0x19, 0x5e, 0xb0, 0x01, 0x7e, 0xa0, 0x90, + 0x12, 0x62, 0x93, 0x40, 0x12, 0x75, 0x31, 0xb8, 0x12, 0x7c, 0x15, 0xc2, 0x27, 0xc0, 0xf1, 0x75, + 0xf1, 0x01, 0x12, 0x6f, 0xd9, 0xd0, 0xf1, 0x22, 0xc2, 0xaf, 0xe5, 0x33, 0x60, 0x14, 0x7e, 0x20, + 0x00, 0x13, 0x50, 0x07, 0xca, 0xb8, 0x12, 0x37, 0x45, 0xda, 0xb8, 0xa5, 0x0a, 0xbe, 0x21, 0x2f, + 0x78, 0xef, 0xd2, 0xaf, 0x22, 0x7c, 0xb2, 0x23, 0x0a, 0x2b, 0x49, 0x22, 0x37, 0x50, 0x89, 0x24, + 0x37, 0x60, 0x37, 0x7f, 0x37, 0x9e, 0x37, 0xbd, 0x37, 0xdc, 0x37, 0xfb, 0x38, 0x1a, 0x38, 0x39, + 0x7e, 0x24, 0x80, 0x00, 0xca, 0x19, 0x7d, 0x12, 0x12, 0x48, 0x1b, 0xda, 0x19, 0x10, 0x04, 0x02, + 0x80, 0x0c, 0xd2, 0x01, 0xc0, 0xf1, 0x75, 0xf1, 0x01, 0x12, 0x6f, 0xd9, 0xd0, 0xf1, 0x22, 0x7e, + 0x24, 0x81, 0x00, 0xca, 0x19, 0x7d, 0x12, 0x12, 0x4b, 0x73, 0xda, 0x19, 0x10, 0x04, 0x02, 0x80, + 0x0c, 0xd2, 0x01, 0xc0, 0xf1, 0x75, 0xf1, 0x01, 0x12, 0x6f, 0xd9, 0xd0, 0xf1, 0x22, 0x7e, 0x24, + 0x82, 0x00, 0xca, 0x19, 0x7d, 0x12, 0x12, 0x4e, 0xcb, 0xda, 0x19, 0x10, 0x04, 0x02, 0x80, 0x0c, + 0xd2, 0x01, 0xc0, 0xf1, 0x75, 0xf1, 0x01, 0x12, 0x6f, 0xd9, 0xd0, 0xf1, 0x22, 0x7e, 0x24, 0x83, + 0x00, 0xca, 0x19, 0x7d, 0x12, 0x12, 0x52, 0x23, 0xda, 0x19, 0x10, 0x04, 0x02, 0x80, 0x0c, 0xd2, + 0x01, 0xc0, 0xf1, 0x75, 0xf1, 0x01, 0x12, 0x6f, 0xd9, 0xd0, 0xf1, 0x22, 0x7e, 0x24, 0x84, 0x00, + 0xca, 0x19, 0x7d, 0x12, 0x12, 0x55, 0x7b, 0xda, 0x19, 0x10, 0x04, 0x02, 0x80, 0x0c, 0xd2, 0x01, + 0xc0, 0xf1, 0x75, 0xf1, 0x01, 0x12, 0x6f, 0xd9, 0xd0, 0xf1, 0x22, 0x7e, 0x24, 0x85, 0x00, 0xca, + 0x19, 0x7d, 0x12, 0x12, 0x58, 0xd3, 0xda, 0x19, 0x10, 0x04, 0x02, 0x80, 0x0c, 0xd2, 0x01, 0xc0, + 0xf1, 0x75, 0xf1, 0x01, 0x12, 0x6f, 0xd9, 0xd0, 0xf1, 0x22, 0x7e, 0x24, 0x86, 0x00, 0xca, 0x19, + 0x7d, 0x12, 0x12, 0x5c, 0x2b, 0xda, 0x19, 0x10, 0x04, 0x02, 0x80, 0x0c, 0xd2, 0x01, 0xc0, 0xf1, + 0x75, 0xf1, 0x01, 0x12, 0x6f, 0xd9, 0xd0, 0xf1, 0x22, 0x7e, 0x24, 0x87, 0x00, 0xca, 0x19, 0x7d, + 0x12, 0x12, 0x5f, 0x83, 0xda, 0x19, 0x10, 0x04, 0x02, 0x80, 0x0c, 0xd2, 0x01, 0xc0, 0xf1, 0x75, + 0xf1, 0x01, 0x12, 0x6f, 0xd9, 0xd0, 0xf1, 0x22, 0xc2, 0xaf, 0xc0, 0xf1, 0x75, 0xf1, 0x01, 0xa9, + 0x32, 0xf2, 0x1a, 0x7e, 0x07, 0x01, 0xe1, 0xbe, 0x04, 0x00, 0x00, 0x78, 0x10, 0xe5, 0xf5, 0x33, + 0x82, 0xe7, 0x40, 0x09, 0x85, 0x31, 0x31, 0x12, 0x7c, 0x15, 0x75, 0xf6, 0x00, 0xd0, 0xf1, 0xd2, + 0xaf, 0x22, + +// Segment #15, Start Address 00ff4000, Length 15381 +0xff,0x00,0x00,0x40,0x15,0x3c, 0x7e, 0x04, 0x00, 0x01, 0x7e, 0x14, 0x7f, 0xf8, 0x7e, 0x24, 0x00, 0xfe, 0x7d, 0x31, 0x0b, 0x1a, - 0x50, 0x1b, 0x0a, 0x50, 0x7e, 0x14, 0x40, 0x1b, 0x02, 0x40, 0x6a, 0x7e, 0xf8, 0x00, 0x59, 0xd2, - 0x04, 0xc2, 0x94, 0xd2, 0x95, 0x7e, 0xf4, 0x40, 0x2c, 0x02, 0x40, 0x7c, 0x12, 0x7f, 0x3f, 0xf5, - 0x2e, 0x7a, 0xa1, 0x2d, 0x7a, 0x11, 0x58, 0x12, 0x77, 0xda, 0x12, 0x40, 0xdc, 0x7e, 0xb3, 0x3f, - 0xf1, 0x60, 0x03, 0x12, 0x43, 0x68, 0x75, 0xf1, 0x00, 0x12, 0x78, 0x7f, 0xd2, 0xaf, 0x02, 0x44, - 0x06, 0x7e, 0x04, 0x00, 0xff, 0x7e, 0x18, 0x40, 0x5f, 0x7a, 0x1c, 0x00, 0x01, 0x89, 0x18, 0xa9, + 0x50, 0x1b, 0x0a, 0x50, 0x7e, 0x14, 0x40, 0x1b, 0x02, 0x40, 0x6a, 0x7e, 0xf8, 0x00, 0x6f, 0xd2, + 0x04, 0xc2, 0x94, 0xd2, 0x95, 0x7e, 0xf4, 0x40, 0x2c, 0x02, 0x40, 0x7c, 0x12, 0x7d, 0x30, 0xf5, + 0x2f, 0x7a, 0xa1, 0x30, 0x7a, 0x11, 0x6e, 0x12, 0x75, 0xca, 0x12, 0x40, 0xdc, 0x7e, 0xb3, 0x3f, + 0xf1, 0x60, 0x03, 0x12, 0x43, 0xd4, 0x75, 0xf1, 0x00, 0x12, 0x76, 0x6f, 0xd2, 0xaf, 0x02, 0x30, + 0x00, 0x7e, 0x04, 0x00, 0xff, 0x7e, 0x18, 0x40, 0x5f, 0x7a, 0x1c, 0x00, 0x01, 0x89, 0x18, 0xa9, 0x25, 0x87, 0x03, 0xa9, 0xd5, 0x87, 0xd2, 0x93, 0x89, 0x08, 0x7e, 0x04, 0x00, 0xff, 0x7e, 0x18, 0x40, 0x78, 0x7a, 0x1c, 0x00, 0x01, 0x89, 0x18, 0xc2, 0x93, 0x89, 0x08, 0x7e, 0x08, 0x00, 0x20, - 0x7e, 0x44, 0x04, 0x00, 0x7e, 0x40, 0x00, 0x7e, 0xe4, 0x40, 0x8e, 0x02, 0x7e, 0x4b, 0x7e, 0x08, - 0x01, 0x59, 0x7e, 0x44, 0x28, 0x7c, 0x7e, 0x40, 0x00, 0x7e, 0xe4, 0x40, 0xa0, 0x02, 0x7e, 0x4b, - 0x7e, 0x08, 0x00, 0x59, 0x7e, 0x44, 0x01, 0x00, 0x7e, 0x40, 0x53, 0x7e, 0xe4, 0x40, 0xb2, 0x02, - 0x7e, 0x4b, 0x75, 0x57, 0x20, 0x75, 0x56, 0x30, 0x7e, 0x04, 0x00, 0x08, 0x75, 0x54, 0x58, 0x75, - 0x55, 0x08, 0x75, 0x51, 0x08, 0x75, 0x53, 0x01, 0x75, 0x89, 0x01, 0x75, 0x8a, 0x01, 0x75, 0x8c, - 0x00, 0xd2, 0x8c, 0x7e, 0x04, 0x00, 0x02, 0x7a, 0x05, 0x42, 0x89, 0xf4, 0x75, 0xb7, 0x7f, 0x75, + 0x7e, 0x44, 0x04, 0x00, 0x7e, 0x40, 0x00, 0x7e, 0xe4, 0x40, 0x8e, 0x02, 0x7c, 0x30, 0x7e, 0x08, + 0x01, 0x6f, 0x7e, 0x44, 0x28, 0x7c, 0x7e, 0x40, 0x00, 0x7e, 0xe4, 0x40, 0xa0, 0x02, 0x7c, 0x30, + 0x7e, 0x08, 0x00, 0x6f, 0x7e, 0x44, 0x01, 0x00, 0x7e, 0x40, 0x53, 0x7e, 0xe4, 0x40, 0xb2, 0x02, + 0x7c, 0x30, 0x75, 0x6d, 0x20, 0x75, 0x6c, 0x30, 0x7e, 0x04, 0x00, 0x08, 0x75, 0x6a, 0x58, 0x75, + 0x6b, 0x08, 0x75, 0x67, 0x08, 0x75, 0x69, 0x01, 0x75, 0x89, 0x01, 0x75, 0x8a, 0x01, 0x75, 0x8c, + 0x00, 0xd2, 0x8c, 0x7e, 0x04, 0x00, 0x02, 0x7a, 0x05, 0x58, 0x89, 0xf4, 0x75, 0xb7, 0x7f, 0x75, 0xb8, 0x7f, 0x75, 0xb3, 0x07, 0x75, 0xb2, 0x07, 0xd2, 0xa9, 0x22, 0xd2, 0x92, 0xe4, 0xd5, 0xe0, - 0xfd, 0xc2, 0x92, 0x7e, 0x24, 0x80, 0x00, 0x7e, 0x11, 0x2e, 0x7e, 0xa0, 0x08, 0x19, 0xa2, 0x00, - 0x10, 0x2e, 0x24, 0x01, 0x00, 0xa5, 0xd9, 0xf2, 0x7e, 0x20, 0x00, 0x12, 0x41, 0x72, 0x0b, 0x20, - 0xbe, 0x21, 0x2e, 0x78, 0xf6, 0x22, 0x7e, 0x04, 0x80, 0x00, 0x4c, 0x02, 0x74, 0xbf, 0x19, 0xb0, + 0xfd, 0xc2, 0x92, 0x7e, 0x24, 0x80, 0x00, 0x7e, 0x11, 0x2f, 0x7e, 0xa0, 0x08, 0x19, 0xa2, 0x00, + 0x10, 0x2e, 0x24, 0x01, 0x00, 0xa5, 0xd9, 0xf2, 0x7e, 0x20, 0x00, 0x12, 0x41, 0x7e, 0x0b, 0x20, + 0xbe, 0x21, 0x2f, 0x78, 0xf6, 0x22, 0x7e, 0x04, 0x80, 0x00, 0x4c, 0x02, 0x74, 0xbf, 0x19, 0xb0, 0x00, 0x0c, 0x74, 0x10, 0x19, 0xb0, 0x00, 0x08, 0x74, 0x80, 0x19, 0xb0, 0x00, 0x0c, 0x7e, 0x54, 0x00, 0x02, 0x19, 0xa0, 0x00, 0x04, 0x19, 0xb0, 0x00, 0x00, 0x74, 0x03, 0x19, 0xb0, 0x00, 0x0c, - 0x74, 0x07, 0x20, 0x68, 0x02, 0x74, 0x0f, 0x19, 0xb0, 0x00, 0x04, 0x30, 0x6b, 0x17, 0x74, 0xbf, - 0x19, 0xb0, 0x00, 0x0c, 0x74, 0x28, 0x20, 0x68, 0x02, 0x74, 0x20, 0x19, 0xb0, 0x00, 0x04, 0x74, - 0x03, 0x19, 0xb0, 0x00, 0x0c, 0x74, 0xa7, 0x19, 0xb0, 0x00, 0x08, 0x74, 0x0c, 0x19, 0xb0, 0x00, - 0x10, 0x22, 0x7e, 0x04, 0x80, 0x00, 0x4c, 0x02, 0xe4, 0x19, 0xb0, 0x00, 0x04, 0x09, 0xb0, 0x00, - 0x10, 0x54, 0x08, 0x19, 0xb0, 0x00, 0x10, 0x74, 0xa7, 0x19, 0xb0, 0x00, 0x08, 0x22, 0x7c, 0xb2, - 0x23, 0x0a, 0x2b, 0x49, 0x22, 0x41, 0x99, 0x89, 0x24, 0x41, 0xa9, 0x41, 0xc6, 0x41, 0xe3, 0x42, - 0x00, 0x42, 0x1d, 0x42, 0x3a, 0x42, 0x57, 0x42, 0x74, 0xc2, 0x10, 0xc2, 0x18, 0xc2, 0x08, 0x7e, - 0x04, 0x09, 0xcd, 0x7a, 0x07, 0x01, 0x59, 0x7a, 0x07, 0x01, 0x69, 0x6d, 0x00, 0x7a, 0x07, 0x01, - 0x79, 0x7a, 0x07, 0x01, 0x89, 0x22, 0xc2, 0x11, 0xc2, 0x19, 0xc2, 0x09, 0x7e, 0x04, 0x0d, 0xcd, - 0x7a, 0x07, 0x01, 0x5b, 0x7a, 0x07, 0x01, 0x6b, 0x6d, 0x00, 0x7a, 0x07, 0x01, 0x7b, 0x7a, 0x07, - 0x01, 0x8b, 0x22, 0xc2, 0x12, 0xc2, 0x1a, 0xc2, 0x0a, 0x7e, 0x04, 0x11, 0xcd, 0x7a, 0x07, 0x01, - 0x5d, 0x7a, 0x07, 0x01, 0x6d, 0x6d, 0x00, 0x7a, 0x07, 0x01, 0x7d, 0x7a, 0x07, 0x01, 0x8d, 0x22, - 0xc2, 0x13, 0xc2, 0x1b, 0xc2, 0x0b, 0x7e, 0x04, 0x15, 0xcd, 0x7a, 0x07, 0x01, 0x5f, 0x7a, 0x07, - 0x01, 0x6f, 0x6d, 0x00, 0x7a, 0x07, 0x01, 0x7f, 0x7a, 0x07, 0x01, 0x8f, 0x22, 0xc2, 0x14, 0xc2, - 0x1c, 0xc2, 0x0c, 0x7e, 0x04, 0x19, 0xcd, 0x7a, 0x07, 0x01, 0x61, 0x7a, 0x07, 0x01, 0x71, 0x6d, - 0x00, 0x7a, 0x07, 0x01, 0x81, 0x7a, 0x07, 0x01, 0x91, 0x22, 0xc2, 0x15, 0xc2, 0x1d, 0xc2, 0x0d, - 0x7e, 0x04, 0x1d, 0xcd, 0x7a, 0x07, 0x01, 0x63, 0x7a, 0x07, 0x01, 0x73, 0x6d, 0x00, 0x7a, 0x07, - 0x01, 0x83, 0x7a, 0x07, 0x01, 0x93, 0x22, 0xc2, 0x16, 0xc2, 0x1e, 0xc2, 0x0e, 0x7e, 0x04, 0x21, - 0xcd, 0x7a, 0x07, 0x01, 0x65, 0x7a, 0x07, 0x01, 0x75, 0x6d, 0x00, 0x7a, 0x07, 0x01, 0x85, 0x7a, - 0x07, 0x01, 0x95, 0x22, 0xc2, 0x17, 0xc2, 0x1f, 0xc2, 0x0f, 0x7e, 0x04, 0x25, 0xcd, 0x7a, 0x07, - 0x01, 0x67, 0x7a, 0x07, 0x01, 0x77, 0x6d, 0x00, 0x7a, 0x07, 0x01, 0x87, 0x7a, 0x07, 0x01, 0x97, - 0x22, 0x7c, 0xb2, 0x23, 0x0a, 0x2b, 0x49, 0x22, 0x42, 0x9c, 0x89, 0x24, 0x42, 0xac, 0x42, 0xc3, - 0x42, 0xda, 0x42, 0xf1, 0x43, 0x08, 0x43, 0x1f, 0x43, 0x36, 0x43, 0x4d, 0x30, 0x40, 0x07, 0x20, - 0x58, 0x04, 0xc2, 0x28, 0x80, 0x0c, 0x30, 0x48, 0x07, 0x20, 0x50, 0x04, 0xc2, 0x28, 0x80, 0x02, - 0xd2, 0x28, 0x22, 0x30, 0x41, 0x07, 0x20, 0x59, 0x04, 0xc2, 0x29, 0x80, 0x0c, 0x30, 0x49, 0x07, - 0x20, 0x51, 0x04, 0xc2, 0x29, 0x80, 0x02, 0xd2, 0x29, 0x22, 0x30, 0x42, 0x07, 0x20, 0x5a, 0x04, - 0xc2, 0x2a, 0x80, 0x0c, 0x30, 0x4a, 0x07, 0x20, 0x52, 0x04, 0xc2, 0x2a, 0x80, 0x02, 0xd2, 0x2a, - 0x22, 0x30, 0x43, 0x07, 0x20, 0x5b, 0x04, 0xc2, 0x2b, 0x80, 0x0c, 0x30, 0x4b, 0x07, 0x20, 0x53, - 0x04, 0xc2, 0x2b, 0x80, 0x02, 0xd2, 0x2b, 0x22, 0x30, 0x44, 0x07, 0x20, 0x5c, 0x04, 0xc2, 0x2c, - 0x80, 0x0c, 0x30, 0x4c, 0x07, 0x20, 0x54, 0x04, 0xc2, 0x2c, 0x80, 0x02, 0xd2, 0x2c, 0x22, 0x30, - 0x45, 0x07, 0x20, 0x5d, 0x04, 0xc2, 0x2d, 0x80, 0x0c, 0x30, 0x4d, 0x07, 0x20, 0x55, 0x04, 0xc2, - 0x2d, 0x80, 0x02, 0xd2, 0x2d, 0x22, 0x30, 0x46, 0x07, 0x20, 0x5e, 0x04, 0xc2, 0x2e, 0x80, 0x0c, - 0x30, 0x4e, 0x07, 0x20, 0x56, 0x04, 0xc2, 0x2e, 0x80, 0x02, 0xd2, 0x2e, 0x22, 0x30, 0x47, 0x07, - 0x20, 0x5f, 0x04, 0xc2, 0x2f, 0x80, 0x0c, 0x30, 0x4f, 0x07, 0x20, 0x57, 0x04, 0xc2, 0x2f, 0x80, - 0x02, 0xd2, 0x2f, 0x22, 0x43, 0xcc, 0x43, 0x79, 0xbe, 0xb0, 0x02, 0x40, 0x01, 0x22, 0x23, 0x0a, - 0x5b, 0x49, 0x55, 0x43, 0x64, 0x99, 0x54, 0xd3, 0x22, 0xa9, 0xc5, 0x87, 0x12, 0x43, 0xd7, 0x7e, - 0x04, 0x05, 0xcd, 0x7a, 0x07, 0x01, 0xc1, 0x7a, 0x07, 0x01, 0xc3, 0x7e, 0x04, 0x01, 0xcd, 0x7a, - 0x07, 0x01, 0xc7, 0x7a, 0x07, 0x01, 0xc9, 0x7e, 0x04, 0x76, 0xbd, 0x7a, 0x05, 0x4b, 0x75, 0xf1, - 0x01, 0x75, 0xe1, 0x1f, 0x75, 0xe4, 0x04, 0x75, 0xf4, 0x04, 0x75, 0xf1, 0x02, 0x75, 0xe1, 0x03, - 0x75, 0xe4, 0x04, 0x75, 0xf4, 0x04, 0x43, 0xa2, 0x1c, 0x12, 0x40, 0xeb, 0x7e, 0x20, 0x00, 0x12, - 0x41, 0x8e, 0x0b, 0x20, 0xbe, 0x21, 0x2e, 0x78, 0xf6, 0xd2, 0xa8, 0x22, 0xa9, 0xd5, 0x87, 0x12, - 0x43, 0xd7, 0xd2, 0x92, 0xc2, 0xa8, 0x22, 0x75, 0xa3, 0x00, 0x53, 0xa2, 0x03, 0x75, 0xc1, 0x00, - 0x53, 0xc0, 0x03, 0x7e, 0x00, 0x05, 0x7a, 0x01, 0xf1, 0x43, 0xf4, 0x80, 0x43, 0xe4, 0x80, 0xe5, - 0xf2, 0x54, 0x7f, 0x44, 0x08, 0xf5, 0xf2, 0xe5, 0xe2, 0x54, 0x7f, 0x44, 0x08, 0xf5, 0xe2, 0x75, - 0xe1, 0x10, 0xa5, 0xd8, 0xe1, 0x22, 0x12, 0x44, 0x76, 0x12, 0x44, 0x8c, 0x12, 0x45, 0x27, 0x12, - 0x45, 0x57, 0x12, 0x49, 0x0f, 0x12, 0x44, 0xd8, 0x80, 0xec, 0xca, 0x09, 0x12, 0x44, 0x58, 0x10, - 0x01, 0x12, 0xd5, 0x51, 0x1e, 0x63, 0x53, 0x01, 0x7e, 0x00, 0x54, 0x2e, 0x01, 0x53, 0xa5, 0xe6, - 0xf5, 0x51, 0x80, 0x12, 0x20, 0x02, 0x1e, 0x75, 0x53, 0x00, 0x85, 0x54, 0x51, 0xd2, 0x02, 0x74, - 0x00, 0x80, 0x0d, 0x30, 0x02, 0x0f, 0xc2, 0x02, 0x7e, 0x00, 0x56, 0x2e, 0x01, 0x53, 0xa5, 0xe6, - 0x53, 0x90, 0xcf, 0x42, 0x90, 0xda, 0x09, 0x32, 0xe5, 0x23, 0x60, 0x19, 0x7e, 0x14, 0x00, 0x00, - 0x09, 0xb1, 0x01, 0xb9, 0xb4, 0x00, 0x02, 0x80, 0x05, 0x14, 0x19, 0xb1, 0x01, 0xb9, 0xa5, 0x0a, - 0xbe, 0x21, 0x2e, 0x78, 0xeb, 0x22, 0xc2, 0xaf, 0x7e, 0xb3, 0x3f, 0xf1, 0xb4, 0x01, 0x0a, 0xc0, - 0xf1, 0x75, 0xf1, 0x02, 0x12, 0x72, 0xff, 0xd0, 0xf1, 0xd2, 0xaf, 0x22, 0xc2, 0xaf, 0xe5, 0x22, - 0x60, 0x43, 0x7e, 0x07, 0x01, 0xcb, 0xbe, 0x04, 0x03, 0x80, 0x38, 0x39, 0x7e, 0x04, 0x80, 0x00, - 0x7e, 0x20, 0x00, 0x13, 0x50, 0x21, 0x09, 0xa0, 0x00, 0x04, 0x4e, 0xa0, 0x05, 0x19, 0xa0, 0x00, - 0x04, 0x0a, 0x32, 0x09, 0x53, 0x6a, 0x93, 0x5e, 0x51, 0x27, 0x68, 0x0b, 0x09, 0xa0, 0x00, 0x10, - 0x4e, 0xa0, 0x01, 0x19, 0xa0, 0x00, 0x10, 0x2e, 0x04, 0x01, 0x00, 0xa5, 0x0a, 0xbe, 0x21, 0x2e, - 0x78, 0xd1, 0x75, 0x22, 0x00, 0xd2, 0xaf, 0x22, 0xc2, 0xaf, 0xe5, 0x26, 0x60, 0x36, 0x7e, 0x20, - 0x00, 0x7e, 0x30, 0x01, 0xe5, 0x26, 0xa5, 0x5b, 0x68, 0x21, 0x7c, 0xb2, 0x23, 0x0a, 0x2b, 0x49, - 0x32, 0x01, 0x79, 0xbe, 0x34, 0x00, 0x00, 0x68, 0x12, 0x7e, 0xb1, 0x21, 0xa5, 0x4b, 0x7a, 0xb1, - 0x21, 0xca, 0x19, 0x49, 0x22, 0x45, 0x17, 0x99, 0x24, 0xda, 0x19, 0x3e, 0x30, 0xa5, 0x0a, 0xbe, - 0x21, 0x2e, 0x78, 0xd0, 0xd2, 0xaf, 0x22, 0x4a, 0x98, 0x4d, 0xcd, 0x51, 0x02, 0x54, 0x37, 0x57, - 0x6c, 0x5a, 0xa1, 0x5d, 0xd6, 0x61, 0x0b, 0xc2, 0xaf, 0xe5, 0x24, 0x60, 0x14, 0x7e, 0x20, 0x00, - 0x13, 0x50, 0x07, 0xca, 0xb8, 0x12, 0x45, 0x44, 0xda, 0xb8, 0xa5, 0x0a, 0xbe, 0x21, 0x2e, 0x78, - 0xef, 0xd2, 0xaf, 0x22, 0xca, 0x28, 0x12, 0x6a, 0xac, 0xda, 0x28, 0x40, 0x09, 0x0a, 0x22, 0x09, - 0xb2, 0x6a, 0x93, 0xf4, 0x52, 0x24, 0x22, 0xc2, 0xaf, 0xe5, 0x23, 0x60, 0x14, 0x7e, 0x20, 0x00, - 0x13, 0x50, 0x07, 0xca, 0xb8, 0x12, 0x45, 0x74, 0xda, 0xb8, 0xa5, 0x0a, 0xbe, 0x21, 0x2e, 0x78, - 0xef, 0xd2, 0xaf, 0x22, 0x7c, 0xb2, 0x23, 0x0a, 0x2b, 0x49, 0x22, 0x45, 0x7f, 0x89, 0x24, 0x45, - 0x8f, 0x45, 0xff, 0x46, 0x6f, 0x46, 0xdf, 0x47, 0x4f, 0x47, 0xbf, 0x48, 0x2f, 0x48, 0x9f, 0x7e, - 0x27, 0x01, 0x79, 0xbe, 0x24, 0x00, 0x00, 0x78, 0x24, 0x7e, 0x24, 0x80, 0x00, 0x09, 0xb2, 0x00, - 0x14, 0xca, 0xb8, 0x5e, 0xb0, 0x1e, 0xda, 0xb8, 0x68, 0x0b, 0xca, 0x19, 0x7d, 0x12, 0x12, 0x4d, - 0x96, 0x7d, 0x21, 0xda, 0x19, 0x30, 0xe6, 0x18, 0x7e, 0x60, 0x00, 0x80, 0x1e, 0xbe, 0x27, 0x01, - 0x89, 0x68, 0x0d, 0x7a, 0x27, 0x01, 0x89, 0x7e, 0x60, 0x9c, 0x7a, 0x63, 0x01, 0xb9, 0x80, 0x2e, - 0x7e, 0x63, 0x01, 0xb9, 0xa5, 0xbe, 0x00, 0x26, 0x7e, 0x60, 0x01, 0x7e, 0xb0, 0x00, 0x7e, 0xa0, - 0xc8, 0x12, 0x65, 0xbf, 0x40, 0x18, 0x75, 0x2f, 0xb3, 0x12, 0x7e, 0x30, 0xc2, 0x18, 0x6c, 0x00, - 0x7a, 0x03, 0x01, 0xb9, 0xc0, 0xf1, 0x75, 0xf1, 0x01, 0x12, 0x71, 0xe9, 0xd0, 0xf1, 0x22, 0x7e, - 0x27, 0x01, 0x7b, 0xbe, 0x24, 0x00, 0x00, 0x78, 0x24, 0x7e, 0x24, 0x81, 0x00, 0x09, 0xb2, 0x00, - 0x14, 0xca, 0xb8, 0x5e, 0xb0, 0x1e, 0xda, 0xb8, 0x68, 0x0b, 0xca, 0x19, 0x7d, 0x12, 0x12, 0x50, - 0xcb, 0x7d, 0x21, 0xda, 0x19, 0x30, 0xe6, 0x18, 0x7e, 0x60, 0x00, 0x80, 0x1e, 0xbe, 0x27, 0x01, - 0x8b, 0x68, 0x0d, 0x7a, 0x27, 0x01, 0x8b, 0x7e, 0x60, 0x9c, 0x7a, 0x63, 0x01, 0xba, 0x80, 0x2e, - 0x7e, 0x63, 0x01, 0xba, 0xa5, 0xbe, 0x00, 0x26, 0x7e, 0x60, 0x01, 0x7e, 0xb0, 0x00, 0x7e, 0xa0, - 0xc8, 0x12, 0x65, 0xbf, 0x40, 0x18, 0x75, 0x2f, 0xb3, 0x12, 0x7e, 0x30, 0xc2, 0x19, 0x6c, 0x00, - 0x7a, 0x03, 0x01, 0xba, 0xc0, 0xf1, 0x75, 0xf1, 0x01, 0x12, 0x71, 0xe9, 0xd0, 0xf1, 0x22, 0x7e, - 0x27, 0x01, 0x7d, 0xbe, 0x24, 0x00, 0x00, 0x78, 0x24, 0x7e, 0x24, 0x82, 0x00, 0x09, 0xb2, 0x00, - 0x14, 0xca, 0xb8, 0x5e, 0xb0, 0x1e, 0xda, 0xb8, 0x68, 0x0b, 0xca, 0x19, 0x7d, 0x12, 0x12, 0x54, - 0x00, 0x7d, 0x21, 0xda, 0x19, 0x30, 0xe6, 0x18, 0x7e, 0x60, 0x00, 0x80, 0x1e, 0xbe, 0x27, 0x01, - 0x8d, 0x68, 0x0d, 0x7a, 0x27, 0x01, 0x8d, 0x7e, 0x60, 0x9c, 0x7a, 0x63, 0x01, 0xbb, 0x80, 0x2e, - 0x7e, 0x63, 0x01, 0xbb, 0xa5, 0xbe, 0x00, 0x26, 0x7e, 0x60, 0x01, 0x7e, 0xb0, 0x00, 0x7e, 0xa0, - 0xc8, 0x12, 0x65, 0xbf, 0x40, 0x18, 0x75, 0x2f, 0xb3, 0x12, 0x7e, 0x30, 0xc2, 0x1a, 0x6c, 0x00, - 0x7a, 0x03, 0x01, 0xbb, 0xc0, 0xf1, 0x75, 0xf1, 0x01, 0x12, 0x71, 0xe9, 0xd0, 0xf1, 0x22, 0x7e, - 0x27, 0x01, 0x7f, 0xbe, 0x24, 0x00, 0x00, 0x78, 0x24, 0x7e, 0x24, 0x83, 0x00, 0x09, 0xb2, 0x00, - 0x14, 0xca, 0xb8, 0x5e, 0xb0, 0x1e, 0xda, 0xb8, 0x68, 0x0b, 0xca, 0x19, 0x7d, 0x12, 0x12, 0x57, - 0x35, 0x7d, 0x21, 0xda, 0x19, 0x30, 0xe6, 0x18, 0x7e, 0x60, 0x00, 0x80, 0x1e, 0xbe, 0x27, 0x01, - 0x8f, 0x68, 0x0d, 0x7a, 0x27, 0x01, 0x8f, 0x7e, 0x60, 0x9c, 0x7a, 0x63, 0x01, 0xbc, 0x80, 0x2e, - 0x7e, 0x63, 0x01, 0xbc, 0xa5, 0xbe, 0x00, 0x26, 0x7e, 0x60, 0x01, 0x7e, 0xb0, 0x00, 0x7e, 0xa0, - 0xc8, 0x12, 0x65, 0xbf, 0x40, 0x18, 0x75, 0x2f, 0xb3, 0x12, 0x7e, 0x30, 0xc2, 0x1b, 0x6c, 0x00, - 0x7a, 0x03, 0x01, 0xbc, 0xc0, 0xf1, 0x75, 0xf1, 0x01, 0x12, 0x71, 0xe9, 0xd0, 0xf1, 0x22, 0x7e, - 0x27, 0x01, 0x81, 0xbe, 0x24, 0x00, 0x00, 0x78, 0x24, 0x7e, 0x24, 0x84, 0x00, 0x09, 0xb2, 0x00, - 0x14, 0xca, 0xb8, 0x5e, 0xb0, 0x1e, 0xda, 0xb8, 0x68, 0x0b, 0xca, 0x19, 0x7d, 0x12, 0x12, 0x5a, - 0x6a, 0x7d, 0x21, 0xda, 0x19, 0x30, 0xe6, 0x18, 0x7e, 0x60, 0x00, 0x80, 0x1e, 0xbe, 0x27, 0x01, - 0x91, 0x68, 0x0d, 0x7a, 0x27, 0x01, 0x91, 0x7e, 0x60, 0x9c, 0x7a, 0x63, 0x01, 0xbd, 0x80, 0x2e, - 0x7e, 0x63, 0x01, 0xbd, 0xa5, 0xbe, 0x00, 0x26, 0x7e, 0x60, 0x01, 0x7e, 0xb0, 0x00, 0x7e, 0xa0, - 0xc8, 0x12, 0x65, 0xbf, 0x40, 0x18, 0x75, 0x2f, 0xb3, 0x12, 0x7e, 0x30, 0xc2, 0x1c, 0x6c, 0x00, - 0x7a, 0x03, 0x01, 0xbd, 0xc0, 0xf1, 0x75, 0xf1, 0x01, 0x12, 0x71, 0xe9, 0xd0, 0xf1, 0x22, 0x7e, - 0x27, 0x01, 0x83, 0xbe, 0x24, 0x00, 0x00, 0x78, 0x24, 0x7e, 0x24, 0x85, 0x00, 0x09, 0xb2, 0x00, - 0x14, 0xca, 0xb8, 0x5e, 0xb0, 0x1e, 0xda, 0xb8, 0x68, 0x0b, 0xca, 0x19, 0x7d, 0x12, 0x12, 0x5d, - 0x9f, 0x7d, 0x21, 0xda, 0x19, 0x30, 0xe6, 0x18, 0x7e, 0x60, 0x00, 0x80, 0x1e, 0xbe, 0x27, 0x01, - 0x93, 0x68, 0x0d, 0x7a, 0x27, 0x01, 0x93, 0x7e, 0x60, 0x9c, 0x7a, 0x63, 0x01, 0xbe, 0x80, 0x2e, - 0x7e, 0x63, 0x01, 0xbe, 0xa5, 0xbe, 0x00, 0x26, 0x7e, 0x60, 0x01, 0x7e, 0xb0, 0x00, 0x7e, 0xa0, - 0xc8, 0x12, 0x65, 0xbf, 0x40, 0x18, 0x75, 0x2f, 0xb3, 0x12, 0x7e, 0x30, 0xc2, 0x1d, 0x6c, 0x00, - 0x7a, 0x03, 0x01, 0xbe, 0xc0, 0xf1, 0x75, 0xf1, 0x01, 0x12, 0x71, 0xe9, 0xd0, 0xf1, 0x22, 0x7e, - 0x27, 0x01, 0x85, 0xbe, 0x24, 0x00, 0x00, 0x78, 0x24, 0x7e, 0x24, 0x86, 0x00, 0x09, 0xb2, 0x00, - 0x14, 0xca, 0xb8, 0x5e, 0xb0, 0x1e, 0xda, 0xb8, 0x68, 0x0b, 0xca, 0x19, 0x7d, 0x12, 0x12, 0x60, - 0xd4, 0x7d, 0x21, 0xda, 0x19, 0x30, 0xe6, 0x18, 0x7e, 0x60, 0x00, 0x80, 0x1e, 0xbe, 0x27, 0x01, - 0x95, 0x68, 0x0d, 0x7a, 0x27, 0x01, 0x95, 0x7e, 0x60, 0x9c, 0x7a, 0x63, 0x01, 0xbf, 0x80, 0x2e, - 0x7e, 0x63, 0x01, 0xbf, 0xa5, 0xbe, 0x00, 0x26, 0x7e, 0x60, 0x01, 0x7e, 0xb0, 0x00, 0x7e, 0xa0, - 0xc8, 0x12, 0x65, 0xbf, 0x40, 0x18, 0x75, 0x2f, 0xb3, 0x12, 0x7e, 0x30, 0xc2, 0x1e, 0x6c, 0x00, - 0x7a, 0x03, 0x01, 0xbf, 0xc0, 0xf1, 0x75, 0xf1, 0x01, 0x12, 0x71, 0xe9, 0xd0, 0xf1, 0x22, 0x7e, - 0x27, 0x01, 0x87, 0xbe, 0x24, 0x00, 0x00, 0x78, 0x24, 0x7e, 0x24, 0x87, 0x00, 0x09, 0xb2, 0x00, - 0x14, 0xca, 0xb8, 0x5e, 0xb0, 0x1e, 0xda, 0xb8, 0x68, 0x0b, 0xca, 0x19, 0x7d, 0x12, 0x12, 0x64, - 0x09, 0x7d, 0x21, 0xda, 0x19, 0x30, 0xe6, 0x18, 0x7e, 0x60, 0x00, 0x80, 0x1e, 0xbe, 0x27, 0x01, - 0x97, 0x68, 0x0d, 0x7a, 0x27, 0x01, 0x97, 0x7e, 0x60, 0x9c, 0x7a, 0x63, 0x01, 0xc0, 0x80, 0x2e, - 0x7e, 0x63, 0x01, 0xc0, 0xa5, 0xbe, 0x00, 0x26, 0x7e, 0x60, 0x01, 0x7e, 0xb0, 0x00, 0x7e, 0xa0, - 0xc8, 0x12, 0x65, 0xbf, 0x40, 0x18, 0x75, 0x2f, 0xb3, 0x12, 0x7e, 0x30, 0xc2, 0x1f, 0x6c, 0x00, - 0x7a, 0x03, 0x01, 0xc0, 0xc0, 0xf1, 0x75, 0xf1, 0x01, 0x12, 0x71, 0xe9, 0xd0, 0xf1, 0x22, 0xc2, - 0xaf, 0xc0, 0xf1, 0x75, 0xf1, 0x01, 0xa9, 0x32, 0xf2, 0x1a, 0x7e, 0x07, 0x01, 0xcb, 0xbe, 0x04, - 0x00, 0x00, 0x78, 0x10, 0xe5, 0xf5, 0x33, 0x82, 0xe7, 0x40, 0x09, 0x85, 0x31, 0x2f, 0x12, 0x7e, - 0x30, 0x75, 0xf6, 0x00, 0xd0, 0xf1, 0xd2, 0xaf, 0x22, 0xc0, 0xd0, 0xc0, 0xd1, 0xc0, 0xe0, 0xc0, - 0xf0, 0xca, 0x0b, 0xca, 0x1b, 0xca, 0x2b, 0xd2, 0x01, 0x75, 0x2f, 0x89, 0x12, 0x7e, 0x30, 0x7e, - 0x14, 0x80, 0x00, 0x09, 0xb1, 0x00, 0x08, 0x20, 0xe0, 0x03, 0x02, 0x49, 0xd8, 0x20, 0x70, 0x5a, - 0xa5, 0x0a, 0x09, 0xb1, 0x00, 0x08, 0x20, 0xe0, 0x03, 0x02, 0x49, 0xf0, 0x20, 0x71, 0x4b, 0xa5, - 0x0a, 0x09, 0xb1, 0x00, 0x08, 0x20, 0xe0, 0x03, 0x02, 0x4a, 0x08, 0xa5, 0x0a, 0x09, 0xb1, 0x00, - 0x08, 0x20, 0xe0, 0x03, 0x02, 0x4a, 0x20, 0x20, 0x72, 0x30, 0xa5, 0x0a, 0x09, 0xb1, 0x00, 0x08, - 0x20, 0xe0, 0x03, 0x02, 0x4a, 0x38, 0xa5, 0x0a, 0x09, 0xb1, 0x00, 0x08, 0x20, 0xe0, 0x03, 0x02, - 0x4a, 0x50, 0xa5, 0x0a, 0x09, 0xb1, 0x00, 0x08, 0x20, 0xe0, 0x03, 0x02, 0x4a, 0x68, 0xa5, 0x0a, - 0x09, 0xb1, 0x00, 0x08, 0x20, 0xe0, 0x03, 0x02, 0x4a, 0x80, 0x30, 0x04, 0x0c, 0xc2, 0x04, 0xc0, - 0xf1, 0x75, 0xf1, 0x01, 0x12, 0x71, 0xe9, 0xd0, 0xf1, 0xda, 0x2b, 0xda, 0x1b, 0xda, 0x0b, 0xd0, - 0xf0, 0xd0, 0xe0, 0xd0, 0xd1, 0xd0, 0xd0, 0x32, 0x75, 0x2f, 0x80, 0x12, 0x7e, 0x30, 0x54, 0x3e, - 0x0a, 0x5b, 0x7e, 0x44, 0x00, 0xff, 0x69, 0x52, 0x66, 0x34, 0xca, 0x06, 0x4f, 0x49, 0x89, 0x54, - 0x75, 0x2f, 0x81, 0x12, 0x7e, 0x30, 0x54, 0x3e, 0x0a, 0x5b, 0x7e, 0x44, 0x00, 0xff, 0x69, 0x52, - 0x66, 0x74, 0xca, 0x06, 0x4f, 0x49, 0x89, 0x54, 0x75, 0x2f, 0x82, 0x12, 0x7e, 0x30, 0x54, 0x3e, - 0x0a, 0x5b, 0x7e, 0x44, 0x00, 0xff, 0x69, 0x52, 0x66, 0xb4, 0xca, 0x06, 0x4f, 0x49, 0x89, 0x54, - 0x75, 0x2f, 0x83, 0x12, 0x7e, 0x30, 0x54, 0x3e, 0x0a, 0x5b, 0x7e, 0x44, 0x00, 0xff, 0x69, 0x52, - 0x66, 0xf4, 0xca, 0x06, 0x4f, 0x49, 0x89, 0x54, 0x75, 0x2f, 0x84, 0x12, 0x7e, 0x30, 0x54, 0x3e, - 0x0a, 0x5b, 0x7e, 0x44, 0x00, 0xff, 0x69, 0x52, 0x67, 0x34, 0xca, 0x06, 0x4f, 0x49, 0x89, 0x54, - 0x75, 0x2f, 0x85, 0x12, 0x7e, 0x30, 0x54, 0x3e, 0x0a, 0x5b, 0x7e, 0x44, 0x00, 0xff, 0x69, 0x52, - 0x67, 0x74, 0xca, 0x06, 0x4f, 0x49, 0x89, 0x54, 0x75, 0x2f, 0x86, 0x12, 0x7e, 0x30, 0x54, 0x3e, - 0x0a, 0x5b, 0x7e, 0x44, 0x00, 0xff, 0x69, 0x52, 0x67, 0xb4, 0xca, 0x06, 0x4f, 0x49, 0x89, 0x54, - 0x75, 0x2f, 0x87, 0x12, 0x7e, 0x30, 0x54, 0x3e, 0x0a, 0x5b, 0x7e, 0x44, 0x00, 0xff, 0x69, 0x52, - 0x67, 0xf4, 0xca, 0x06, 0x4f, 0x49, 0x89, 0x54, 0x10, 0x08, 0x01, 0x22, 0x20, 0x28, 0x03, 0xd2, - 0x08, 0x22, 0x75, 0x2f, 0xa0, 0x12, 0x7e, 0x30, 0x7e, 0x14, 0x80, 0x00, 0x80, 0x06, 0x20, 0x28, - 0x03, 0xd2, 0x08, 0x22, 0x09, 0xb1, 0x00, 0x14, 0xca, 0xb8, 0x5e, 0xb0, 0x1e, 0xda, 0xb8, 0x68, - 0x03, 0x12, 0x4d, 0x96, 0x30, 0x30, 0x06, 0x20, 0xe6, 0x4f, 0xd2, 0x08, 0x22, 0x30, 0xe6, 0x02, - 0xd2, 0x60, 0x7e, 0x37, 0x01, 0x79, 0x7e, 0x27, 0x01, 0x99, 0x9d, 0x32, 0x40, 0x31, 0x7d, 0x02, - 0x2e, 0x05, 0x32, 0x7a, 0x05, 0x32, 0x7a, 0x37, 0x01, 0x79, 0x7e, 0x37, 0x01, 0x59, 0x7d, 0x43, - 0x2d, 0x42, 0xbe, 0x44, 0x0d, 0xcc, 0x38, 0x68, 0x7a, 0x47, 0x01, 0x59, 0x75, 0x2f, 0x94, 0x12, - 0x7e, 0x30, 0x7a, 0x51, 0x2f, 0x12, 0x7e, 0x30, 0x12, 0x68, 0x34, 0x10, 0x60, 0xc4, 0x22, 0xc2, - 0x60, 0x2d, 0x23, 0x68, 0x78, 0x6d, 0x33, 0x80, 0x1a, 0x7e, 0x27, 0x01, 0x79, 0xbe, 0x24, 0x00, - 0x00, 0x68, 0x6a, 0xbe, 0x27, 0x01, 0x99, 0x28, 0x04, 0x7e, 0x27, 0x01, 0x99, 0x7e, 0x37, 0x01, - 0x79, 0x9d, 0x32, 0x7d, 0x02, 0x2e, 0x05, 0x32, 0x7a, 0x05, 0x32, 0x7a, 0x37, 0x01, 0x79, 0x7e, - 0x37, 0x01, 0x59, 0x7d, 0x43, 0x2d, 0x42, 0xbe, 0x44, 0x0d, 0xcc, 0x38, 0x13, 0x7a, 0x47, 0x01, - 0x59, 0x75, 0x2f, 0x94, 0x12, 0x7e, 0x30, 0x7a, 0x51, 0x2f, 0x12, 0x7e, 0x30, 0x02, 0x68, 0x34, - 0x75, 0x2f, 0x94, 0x12, 0x7e, 0x30, 0x7a, 0x51, 0x2f, 0x12, 0x7e, 0x30, 0x9e, 0x44, 0x0d, 0xcd, - 0x9d, 0x24, 0x12, 0x68, 0x34, 0x7e, 0x34, 0x09, 0xcd, 0x7d, 0x24, 0x2d, 0x43, 0x7a, 0x47, 0x01, - 0x59, 0x12, 0x68, 0x34, 0xbe, 0x25, 0x20, 0x78, 0x03, 0x02, 0x4b, 0x0b, 0x22, 0xd2, 0x08, 0x7e, - 0x04, 0x09, 0xcd, 0x7a, 0x07, 0x01, 0x59, 0x7a, 0x07, 0x01, 0x69, 0x75, 0x2f, 0x94, 0x12, 0x7e, - 0x30, 0x75, 0x2f, 0x00, 0x12, 0x7e, 0x30, 0x22, 0x75, 0x2f, 0x92, 0x12, 0x7e, 0x30, 0xd2, 0x04, - 0x09, 0xb1, 0x00, 0x14, 0xca, 0xb8, 0x54, 0x82, 0xda, 0xb8, 0x78, 0x70, 0x7e, 0x37, 0x01, 0xcb, - 0x7e, 0x27, 0x01, 0xa9, 0x2e, 0x24, 0x00, 0x02, 0x2d, 0x32, 0xbe, 0x34, 0x04, 0x00, 0x38, 0x3c, - 0x7d, 0x02, 0x2e, 0x05, 0x30, 0x7a, 0x05, 0x30, 0x7a, 0x37, 0x01, 0xcb, 0x7e, 0x37, 0x01, 0xc9, - 0x7d, 0x43, 0x2d, 0x42, 0xbe, 0x44, 0x05, 0xcc, 0x38, 0x44, 0x7a, 0x47, 0x01, 0xc9, 0x7e, 0x24, - 0x00, 0x00, 0x2e, 0x27, 0x01, 0xa9, 0x1b, 0x38, 0x20, 0x0b, 0x35, 0x7a, 0x51, 0x2f, 0x12, 0x7e, - 0x30, 0xbe, 0x50, 0x38, 0x78, 0x03, 0x02, 0x69, 0x1f, 0x02, 0x69, 0x04, 0x75, 0x2f, 0x99, 0x12, - 0x7e, 0x30, 0x09, 0xb1, 0x00, 0x04, 0x54, 0xfa, 0x19, 0xb1, 0x00, 0x04, 0x30, 0x38, 0x0a, 0x09, - 0xb1, 0x00, 0x10, 0x54, 0xfe, 0x19, 0xb1, 0x00, 0x10, 0xd2, 0x10, 0x22, 0x80, 0x7f, 0x7a, 0x51, - 0x2f, 0x12, 0x7e, 0x30, 0x9e, 0x44, 0x05, 0xcd, 0x9d, 0x24, 0x7e, 0x64, 0x00, 0x00, 0x2e, 0x67, - 0x01, 0xa9, 0x9e, 0x24, 0x00, 0x02, 0x40, 0x17, 0x1b, 0x38, 0x60, 0x0b, 0x35, 0x12, 0x69, 0x04, - 0x7e, 0x34, 0x01, 0xcd, 0x7d, 0x24, 0x2d, 0x43, 0x7a, 0x47, 0x01, 0xc9, 0x02, 0x69, 0x04, 0x7a, - 0x39, 0xc0, 0x7e, 0x34, 0x01, 0xcd, 0x7a, 0x39, 0xd0, 0x0b, 0x34, 0x1b, 0x44, 0x80, 0xe5, 0x9d, - 0x32, 0x7c, 0xb6, 0x54, 0x0f, 0x23, 0x23, 0x23, 0x44, 0x00, 0x7a, 0x69, 0xb0, 0x7a, 0x79, 0x70, - 0x0b, 0x35, 0x75, 0x2f, 0x93, 0x12, 0x7e, 0x30, 0x7a, 0x71, 0x2f, 0x12, 0x7e, 0x30, 0xbd, 0x04, - 0x68, 0x2b, 0x7a, 0x07, 0x01, 0xc9, 0x7e, 0x47, 0x01, 0xcb, 0x2d, 0x43, 0x7a, 0x47, 0x01, 0xcb, - 0x2e, 0x35, 0x30, 0x7a, 0x35, 0x30, 0x22, 0xd2, 0x04, 0x09, 0xb1, 0x00, 0x14, 0x20, 0xe0, 0x13, - 0x22, 0x7e, 0x04, 0x01, 0xcd, 0x80, 0x28, 0x7e, 0x04, 0x01, 0xcd, 0x80, 0x2a, 0x7e, 0x04, 0x01, - 0xcd, 0x80, 0xcf, 0x7e, 0x07, 0x01, 0xcb, 0x7e, 0x24, 0x03, 0xfe, 0x9d, 0x20, 0x28, 0x40, 0x7e, - 0x07, 0x01, 0xc9, 0x7e, 0x44, 0x05, 0xcd, 0x7d, 0x60, 0x0b, 0x04, 0xbd, 0x04, 0x68, 0xd2, 0x7d, - 0x70, 0x0b, 0x04, 0xbd, 0x04, 0x68, 0xd0, 0x7d, 0x54, 0x9d, 0x50, 0xbd, 0x25, 0x40, 0x02, 0x7d, - 0x25, 0x7d, 0x32, 0x09, 0xb1, 0x00, 0x14, 0xca, 0xb8, 0x54, 0x1f, 0xb4, 0x01, 0x31, 0xda, 0xb8, - 0x7e, 0x19, 0xb0, 0x7a, 0x09, 0xb0, 0x0b, 0x04, 0x1b, 0x24, 0x78, 0xe7, 0x02, 0x4c, 0x6f, 0x75, - 0x2f, 0x99, 0x12, 0x7e, 0x30, 0x09, 0xb1, 0x00, 0x04, 0x54, 0xfa, 0x19, 0xb1, 0x00, 0x04, 0x30, - 0x38, 0x0a, 0x09, 0xb1, 0x00, 0x10, 0x54, 0xfe, 0x19, 0xb1, 0x00, 0x10, 0xd2, 0x10, 0x22, 0xda, - 0xb8, 0x30, 0xe0, 0xd8, 0xbd, 0x32, 0x68, 0x07, 0xca, 0xb8, 0x12, 0x4c, 0x6f, 0xda, 0xb8, 0x02, - 0x4d, 0x96, 0x09, 0xb1, 0x00, 0x18, 0x7e, 0xa0, 0x88, 0x75, 0x2f, 0x90, 0x12, 0x7e, 0x30, 0xf5, - 0x2f, 0x12, 0x7e, 0x30, 0xa5, 0xfd, 0x5e, 0x50, 0x0a, 0x68, 0x1d, 0xa5, 0xfd, 0x5e, 0x50, 0x20, - 0x68, 0x04, 0xd2, 0x58, 0x80, 0x02, 0xc2, 0x58, 0xa5, 0xfd, 0x5e, 0x50, 0x80, 0x68, 0x04, 0xd2, - 0x50, 0x80, 0x02, 0xc2, 0x50, 0x12, 0x42, 0xac, 0x02, 0x65, 0x9c, 0x75, 0x2f, 0x91, 0x12, 0x7e, - 0x30, 0x09, 0xb1, 0x00, 0x14, 0x7a, 0xb1, 0x2f, 0x12, 0x7e, 0x30, 0x20, 0xe0, 0x08, 0xd2, 0x04, - 0x7e, 0xa0, 0x80, 0x02, 0x65, 0x9c, 0xd2, 0x04, 0x30, 0xe1, 0x06, 0x7e, 0xa0, 0x80, 0x12, 0x65, - 0x9c, 0xca, 0xb8, 0x5e, 0xb0, 0x1c, 0xda, 0xb8, 0x68, 0x12, 0x7e, 0xa0, 0xc0, 0x09, 0x61, 0x00, - 0x00, 0x12, 0x65, 0xbf, 0x09, 0xb1, 0x00, 0x14, 0x20, 0xe0, 0xdb, 0x22, 0x02, 0x4c, 0xc3, 0x75, - 0x2f, 0x95, 0x12, 0x7e, 0x30, 0x22, 0x75, 0x2f, 0x96, 0x12, 0x7e, 0x30, 0x22, 0x10, 0x09, 0x01, - 0x22, 0x20, 0x29, 0x03, 0xd2, 0x09, 0x22, 0x75, 0x2f, 0xa1, 0x12, 0x7e, 0x30, 0x7e, 0x14, 0x81, - 0x00, 0x80, 0x06, 0x20, 0x29, 0x03, 0xd2, 0x09, 0x22, 0x09, 0xb1, 0x00, 0x14, 0xca, 0xb8, 0x5e, - 0xb0, 0x1e, 0xda, 0xb8, 0x68, 0x03, 0x12, 0x50, 0xcb, 0x30, 0x31, 0x06, 0x20, 0xe6, 0x4f, 0xd2, - 0x09, 0x22, 0x30, 0xe6, 0x02, 0xd2, 0x61, 0x7e, 0x37, 0x01, 0x7b, 0x7e, 0x27, 0x01, 0x9b, 0x9d, - 0x32, 0x40, 0x31, 0x7d, 0x02, 0x2e, 0x05, 0x34, 0x7a, 0x05, 0x34, 0x7a, 0x37, 0x01, 0x7b, 0x7e, - 0x37, 0x01, 0x5b, 0x7d, 0x43, 0x2d, 0x42, 0xbe, 0x44, 0x11, 0xcc, 0x38, 0x68, 0x7a, 0x47, 0x01, - 0x5b, 0x75, 0x2f, 0x94, 0x12, 0x7e, 0x30, 0x7a, 0x51, 0x2f, 0x12, 0x7e, 0x30, 0x12, 0x68, 0x34, - 0x10, 0x61, 0xc4, 0x22, 0xc2, 0x61, 0x2d, 0x23, 0x68, 0x78, 0x6d, 0x33, 0x80, 0x1a, 0x7e, 0x27, - 0x01, 0x7b, 0xbe, 0x24, 0x00, 0x00, 0x68, 0x6a, 0xbe, 0x27, 0x01, 0x9b, 0x28, 0x04, 0x7e, 0x27, - 0x01, 0x9b, 0x7e, 0x37, 0x01, 0x7b, 0x9d, 0x32, 0x7d, 0x02, 0x2e, 0x05, 0x34, 0x7a, 0x05, 0x34, - 0x7a, 0x37, 0x01, 0x7b, 0x7e, 0x37, 0x01, 0x5b, 0x7d, 0x43, 0x2d, 0x42, 0xbe, 0x44, 0x11, 0xcc, - 0x38, 0x13, 0x7a, 0x47, 0x01, 0x5b, 0x75, 0x2f, 0x94, 0x12, 0x7e, 0x30, 0x7a, 0x51, 0x2f, 0x12, - 0x7e, 0x30, 0x02, 0x68, 0x34, 0x75, 0x2f, 0x94, 0x12, 0x7e, 0x30, 0x7a, 0x51, 0x2f, 0x12, 0x7e, - 0x30, 0x9e, 0x44, 0x11, 0xcd, 0x9d, 0x24, 0x12, 0x68, 0x34, 0x7e, 0x34, 0x0d, 0xcd, 0x7d, 0x24, - 0x2d, 0x43, 0x7a, 0x47, 0x01, 0x5b, 0x12, 0x68, 0x34, 0xbe, 0x25, 0x20, 0x78, 0x03, 0x02, 0x4e, - 0x40, 0x22, 0xd2, 0x09, 0x7e, 0x04, 0x0d, 0xcd, 0x7a, 0x07, 0x01, 0x5b, 0x7a, 0x07, 0x01, 0x6b, - 0x75, 0x2f, 0x94, 0x12, 0x7e, 0x30, 0x75, 0x2f, 0x00, 0x12, 0x7e, 0x30, 0x22, 0x75, 0x2f, 0x92, - 0x12, 0x7e, 0x30, 0xd2, 0x04, 0x09, 0xb1, 0x00, 0x14, 0xca, 0xb8, 0x54, 0x82, 0xda, 0xb8, 0x78, - 0x70, 0x7e, 0x37, 0x01, 0xcb, 0x7e, 0x27, 0x01, 0xab, 0x2e, 0x24, 0x00, 0x02, 0x2d, 0x32, 0xbe, - 0x34, 0x04, 0x00, 0x38, 0x3c, 0x7d, 0x02, 0x2e, 0x05, 0x30, 0x7a, 0x05, 0x30, 0x7a, 0x37, 0x01, - 0xcb, 0x7e, 0x37, 0x01, 0xc9, 0x7d, 0x43, 0x2d, 0x42, 0xbe, 0x44, 0x05, 0xcc, 0x38, 0x44, 0x7a, - 0x47, 0x01, 0xc9, 0x7e, 0x24, 0x01, 0x00, 0x2e, 0x27, 0x01, 0xab, 0x1b, 0x38, 0x20, 0x0b, 0x35, - 0x7a, 0x51, 0x2f, 0x12, 0x7e, 0x30, 0xbe, 0x50, 0x38, 0x78, 0x03, 0x02, 0x69, 0x1f, 0x02, 0x69, - 0x04, 0x75, 0x2f, 0x99, 0x12, 0x7e, 0x30, 0x09, 0xb1, 0x00, 0x04, 0x54, 0xfa, 0x19, 0xb1, 0x00, - 0x04, 0x30, 0x39, 0x0a, 0x09, 0xb1, 0x00, 0x10, 0x54, 0xfe, 0x19, 0xb1, 0x00, 0x10, 0xd2, 0x11, - 0x22, 0x80, 0x7f, 0x7a, 0x51, 0x2f, 0x12, 0x7e, 0x30, 0x9e, 0x44, 0x05, 0xcd, 0x9d, 0x24, 0x7e, - 0x64, 0x01, 0x00, 0x2e, 0x67, 0x01, 0xab, 0x9e, 0x24, 0x00, 0x02, 0x40, 0x17, 0x1b, 0x38, 0x60, - 0x0b, 0x35, 0x12, 0x69, 0x04, 0x7e, 0x34, 0x01, 0xcd, 0x7d, 0x24, 0x2d, 0x43, 0x7a, 0x47, 0x01, - 0xc9, 0x02, 0x69, 0x04, 0x7a, 0x39, 0xc0, 0x7e, 0x34, 0x01, 0xcd, 0x7a, 0x39, 0xd0, 0x0b, 0x34, - 0x1b, 0x44, 0x80, 0xe5, 0x9d, 0x32, 0x7c, 0xb6, 0x54, 0x0f, 0x23, 0x23, 0x23, 0x44, 0x01, 0x7a, - 0x69, 0xb0, 0x7a, 0x79, 0x70, 0x0b, 0x35, 0x75, 0x2f, 0x93, 0x12, 0x7e, 0x30, 0x7a, 0x71, 0x2f, - 0x12, 0x7e, 0x30, 0xbd, 0x04, 0x68, 0x2b, 0x7a, 0x07, 0x01, 0xc9, 0x7e, 0x47, 0x01, 0xcb, 0x2d, - 0x43, 0x7a, 0x47, 0x01, 0xcb, 0x2e, 0x35, 0x30, 0x7a, 0x35, 0x30, 0x22, 0xd2, 0x04, 0x09, 0xb1, - 0x00, 0x14, 0x20, 0xe0, 0x13, 0x22, 0x7e, 0x04, 0x01, 0xcd, 0x80, 0x28, 0x7e, 0x04, 0x01, 0xcd, - 0x80, 0x2a, 0x7e, 0x04, 0x01, 0xcd, 0x80, 0xcf, 0x7e, 0x07, 0x01, 0xcb, 0x7e, 0x24, 0x03, 0xfe, - 0x9d, 0x20, 0x28, 0x40, 0x7e, 0x07, 0x01, 0xc9, 0x7e, 0x44, 0x05, 0xcd, 0x7d, 0x60, 0x0b, 0x04, - 0xbd, 0x04, 0x68, 0xd2, 0x7d, 0x70, 0x0b, 0x04, 0xbd, 0x04, 0x68, 0xd0, 0x7d, 0x54, 0x9d, 0x50, - 0xbd, 0x25, 0x40, 0x02, 0x7d, 0x25, 0x7d, 0x32, 0x09, 0xb1, 0x00, 0x14, 0xca, 0xb8, 0x54, 0x1f, - 0xb4, 0x01, 0x31, 0xda, 0xb8, 0x7e, 0x19, 0xb0, 0x7a, 0x09, 0xb0, 0x0b, 0x04, 0x1b, 0x24, 0x78, - 0xe7, 0x02, 0x4f, 0xa4, 0x75, 0x2f, 0x99, 0x12, 0x7e, 0x30, 0x09, 0xb1, 0x00, 0x04, 0x54, 0xfa, - 0x19, 0xb1, 0x00, 0x04, 0x30, 0x39, 0x0a, 0x09, 0xb1, 0x00, 0x10, 0x54, 0xfe, 0x19, 0xb1, 0x00, - 0x10, 0xd2, 0x11, 0x22, 0xda, 0xb8, 0x30, 0xe0, 0xd8, 0xbd, 0x32, 0x68, 0x07, 0xca, 0xb8, 0x12, - 0x4f, 0xa4, 0xda, 0xb8, 0x02, 0x50, 0xcb, 0x09, 0xb1, 0x00, 0x18, 0x7e, 0xa0, 0x88, 0x75, 0x2f, - 0x90, 0x12, 0x7e, 0x30, 0xf5, 0x2f, 0x12, 0x7e, 0x30, 0xa5, 0xfd, 0x5e, 0x50, 0x0a, 0x68, 0x1d, - 0xa5, 0xfd, 0x5e, 0x50, 0x20, 0x68, 0x04, 0xd2, 0x59, 0x80, 0x02, 0xc2, 0x59, 0xa5, 0xfd, 0x5e, - 0x50, 0x80, 0x68, 0x04, 0xd2, 0x51, 0x80, 0x02, 0xc2, 0x51, 0x12, 0x42, 0xc3, 0x02, 0x65, 0x9c, - 0x75, 0x2f, 0x91, 0x12, 0x7e, 0x30, 0x09, 0xb1, 0x00, 0x14, 0x7a, 0xb1, 0x2f, 0x12, 0x7e, 0x30, - 0x20, 0xe0, 0x08, 0xd2, 0x04, 0x7e, 0xa0, 0x80, 0x02, 0x65, 0x9c, 0xd2, 0x04, 0x30, 0xe1, 0x06, - 0x7e, 0xa0, 0x80, 0x12, 0x65, 0x9c, 0xca, 0xb8, 0x5e, 0xb0, 0x1c, 0xda, 0xb8, 0x68, 0x12, 0x7e, - 0xa0, 0xc0, 0x09, 0x61, 0x00, 0x00, 0x12, 0x65, 0xbf, 0x09, 0xb1, 0x00, 0x14, 0x20, 0xe0, 0xdb, - 0x22, 0x02, 0x4f, 0xf8, 0x75, 0x2f, 0x95, 0x12, 0x7e, 0x30, 0x22, 0x75, 0x2f, 0x96, 0x12, 0x7e, - 0x30, 0x22, 0x10, 0x0a, 0x01, 0x22, 0x20, 0x2a, 0x03, 0xd2, 0x0a, 0x22, 0x75, 0x2f, 0xa2, 0x12, - 0x7e, 0x30, 0x7e, 0x14, 0x82, 0x00, 0x80, 0x06, 0x20, 0x2a, 0x03, 0xd2, 0x0a, 0x22, 0x09, 0xb1, - 0x00, 0x14, 0xca, 0xb8, 0x5e, 0xb0, 0x1e, 0xda, 0xb8, 0x68, 0x03, 0x12, 0x54, 0x00, 0x30, 0x32, - 0x06, 0x20, 0xe6, 0x4f, 0xd2, 0x0a, 0x22, 0x30, 0xe6, 0x02, 0xd2, 0x62, 0x7e, 0x37, 0x01, 0x7d, - 0x7e, 0x27, 0x01, 0x9d, 0x9d, 0x32, 0x40, 0x31, 0x7d, 0x02, 0x2e, 0x05, 0x36, 0x7a, 0x05, 0x36, - 0x7a, 0x37, 0x01, 0x7d, 0x7e, 0x37, 0x01, 0x5d, 0x7d, 0x43, 0x2d, 0x42, 0xbe, 0x44, 0x15, 0xcc, - 0x38, 0x68, 0x7a, 0x47, 0x01, 0x5d, 0x75, 0x2f, 0x94, 0x12, 0x7e, 0x30, 0x7a, 0x51, 0x2f, 0x12, - 0x7e, 0x30, 0x12, 0x68, 0x34, 0x10, 0x62, 0xc4, 0x22, 0xc2, 0x62, 0x2d, 0x23, 0x68, 0x78, 0x6d, - 0x33, 0x80, 0x1a, 0x7e, 0x27, 0x01, 0x7d, 0xbe, 0x24, 0x00, 0x00, 0x68, 0x6a, 0xbe, 0x27, 0x01, - 0x9d, 0x28, 0x04, 0x7e, 0x27, 0x01, 0x9d, 0x7e, 0x37, 0x01, 0x7d, 0x9d, 0x32, 0x7d, 0x02, 0x2e, - 0x05, 0x36, 0x7a, 0x05, 0x36, 0x7a, 0x37, 0x01, 0x7d, 0x7e, 0x37, 0x01, 0x5d, 0x7d, 0x43, 0x2d, - 0x42, 0xbe, 0x44, 0x15, 0xcc, 0x38, 0x13, 0x7a, 0x47, 0x01, 0x5d, 0x75, 0x2f, 0x94, 0x12, 0x7e, - 0x30, 0x7a, 0x51, 0x2f, 0x12, 0x7e, 0x30, 0x02, 0x68, 0x34, 0x75, 0x2f, 0x94, 0x12, 0x7e, 0x30, - 0x7a, 0x51, 0x2f, 0x12, 0x7e, 0x30, 0x9e, 0x44, 0x15, 0xcd, 0x9d, 0x24, 0x12, 0x68, 0x34, 0x7e, - 0x34, 0x11, 0xcd, 0x7d, 0x24, 0x2d, 0x43, 0x7a, 0x47, 0x01, 0x5d, 0x12, 0x68, 0x34, 0xbe, 0x25, - 0x20, 0x78, 0x03, 0x02, 0x51, 0x75, 0x22, 0xd2, 0x0a, 0x7e, 0x04, 0x11, 0xcd, 0x7a, 0x07, 0x01, - 0x5d, 0x7a, 0x07, 0x01, 0x6d, 0x75, 0x2f, 0x94, 0x12, 0x7e, 0x30, 0x75, 0x2f, 0x00, 0x12, 0x7e, - 0x30, 0x22, 0x75, 0x2f, 0x92, 0x12, 0x7e, 0x30, 0xd2, 0x04, 0x09, 0xb1, 0x00, 0x14, 0xca, 0xb8, - 0x54, 0x82, 0xda, 0xb8, 0x78, 0x70, 0x7e, 0x37, 0x01, 0xcb, 0x7e, 0x27, 0x01, 0xad, 0x2e, 0x24, - 0x00, 0x02, 0x2d, 0x32, 0xbe, 0x34, 0x04, 0x00, 0x38, 0x3c, 0x7d, 0x02, 0x2e, 0x05, 0x30, 0x7a, - 0x05, 0x30, 0x7a, 0x37, 0x01, 0xcb, 0x7e, 0x37, 0x01, 0xc9, 0x7d, 0x43, 0x2d, 0x42, 0xbe, 0x44, - 0x05, 0xcc, 0x38, 0x44, 0x7a, 0x47, 0x01, 0xc9, 0x7e, 0x24, 0x02, 0x00, 0x2e, 0x27, 0x01, 0xad, - 0x1b, 0x38, 0x20, 0x0b, 0x35, 0x7a, 0x51, 0x2f, 0x12, 0x7e, 0x30, 0xbe, 0x50, 0x38, 0x78, 0x03, - 0x02, 0x69, 0x1f, 0x02, 0x69, 0x04, 0x75, 0x2f, 0x99, 0x12, 0x7e, 0x30, 0x09, 0xb1, 0x00, 0x04, - 0x54, 0xfa, 0x19, 0xb1, 0x00, 0x04, 0x30, 0x3a, 0x0a, 0x09, 0xb1, 0x00, 0x10, 0x54, 0xfe, 0x19, - 0xb1, 0x00, 0x10, 0xd2, 0x12, 0x22, 0x80, 0x7f, 0x7a, 0x51, 0x2f, 0x12, 0x7e, 0x30, 0x9e, 0x44, - 0x05, 0xcd, 0x9d, 0x24, 0x7e, 0x64, 0x02, 0x00, 0x2e, 0x67, 0x01, 0xad, 0x9e, 0x24, 0x00, 0x02, - 0x40, 0x17, 0x1b, 0x38, 0x60, 0x0b, 0x35, 0x12, 0x69, 0x04, 0x7e, 0x34, 0x01, 0xcd, 0x7d, 0x24, - 0x2d, 0x43, 0x7a, 0x47, 0x01, 0xc9, 0x02, 0x69, 0x04, 0x7a, 0x39, 0xc0, 0x7e, 0x34, 0x01, 0xcd, - 0x7a, 0x39, 0xd0, 0x0b, 0x34, 0x1b, 0x44, 0x80, 0xe5, 0x9d, 0x32, 0x7c, 0xb6, 0x54, 0x0f, 0x23, - 0x23, 0x23, 0x44, 0x02, 0x7a, 0x69, 0xb0, 0x7a, 0x79, 0x70, 0x0b, 0x35, 0x75, 0x2f, 0x93, 0x12, - 0x7e, 0x30, 0x7a, 0x71, 0x2f, 0x12, 0x7e, 0x30, 0xbd, 0x04, 0x68, 0x2b, 0x7a, 0x07, 0x01, 0xc9, - 0x7e, 0x47, 0x01, 0xcb, 0x2d, 0x43, 0x7a, 0x47, 0x01, 0xcb, 0x2e, 0x35, 0x30, 0x7a, 0x35, 0x30, - 0x22, 0xd2, 0x04, 0x09, 0xb1, 0x00, 0x14, 0x20, 0xe0, 0x13, 0x22, 0x7e, 0x04, 0x01, 0xcd, 0x80, - 0x28, 0x7e, 0x04, 0x01, 0xcd, 0x80, 0x2a, 0x7e, 0x04, 0x01, 0xcd, 0x80, 0xcf, 0x7e, 0x07, 0x01, - 0xcb, 0x7e, 0x24, 0x03, 0xfe, 0x9d, 0x20, 0x28, 0x40, 0x7e, 0x07, 0x01, 0xc9, 0x7e, 0x44, 0x05, - 0xcd, 0x7d, 0x60, 0x0b, 0x04, 0xbd, 0x04, 0x68, 0xd2, 0x7d, 0x70, 0x0b, 0x04, 0xbd, 0x04, 0x68, - 0xd0, 0x7d, 0x54, 0x9d, 0x50, 0xbd, 0x25, 0x40, 0x02, 0x7d, 0x25, 0x7d, 0x32, 0x09, 0xb1, 0x00, - 0x14, 0xca, 0xb8, 0x54, 0x1f, 0xb4, 0x01, 0x31, 0xda, 0xb8, 0x7e, 0x19, 0xb0, 0x7a, 0x09, 0xb0, - 0x0b, 0x04, 0x1b, 0x24, 0x78, 0xe7, 0x02, 0x52, 0xd9, 0x75, 0x2f, 0x99, 0x12, 0x7e, 0x30, 0x09, - 0xb1, 0x00, 0x04, 0x54, 0xfa, 0x19, 0xb1, 0x00, 0x04, 0x30, 0x3a, 0x0a, 0x09, 0xb1, 0x00, 0x10, - 0x54, 0xfe, 0x19, 0xb1, 0x00, 0x10, 0xd2, 0x12, 0x22, 0xda, 0xb8, 0x30, 0xe0, 0xd8, 0xbd, 0x32, - 0x68, 0x07, 0xca, 0xb8, 0x12, 0x52, 0xd9, 0xda, 0xb8, 0x02, 0x54, 0x00, 0x09, 0xb1, 0x00, 0x18, - 0x7e, 0xa0, 0x88, 0x75, 0x2f, 0x90, 0x12, 0x7e, 0x30, 0xf5, 0x2f, 0x12, 0x7e, 0x30, 0xa5, 0xfd, - 0x5e, 0x50, 0x0a, 0x68, 0x1d, 0xa5, 0xfd, 0x5e, 0x50, 0x20, 0x68, 0x04, 0xd2, 0x5a, 0x80, 0x02, - 0xc2, 0x5a, 0xa5, 0xfd, 0x5e, 0x50, 0x80, 0x68, 0x04, 0xd2, 0x52, 0x80, 0x02, 0xc2, 0x52, 0x12, - 0x42, 0xda, 0x02, 0x65, 0x9c, 0x75, 0x2f, 0x91, 0x12, 0x7e, 0x30, 0x09, 0xb1, 0x00, 0x14, 0x7a, - 0xb1, 0x2f, 0x12, 0x7e, 0x30, 0x20, 0xe0, 0x08, 0xd2, 0x04, 0x7e, 0xa0, 0x80, 0x02, 0x65, 0x9c, - 0xd2, 0x04, 0x30, 0xe1, 0x06, 0x7e, 0xa0, 0x80, 0x12, 0x65, 0x9c, 0xca, 0xb8, 0x5e, 0xb0, 0x1c, - 0xda, 0xb8, 0x68, 0x12, 0x7e, 0xa0, 0xc0, 0x09, 0x61, 0x00, 0x00, 0x12, 0x65, 0xbf, 0x09, 0xb1, - 0x00, 0x14, 0x20, 0xe0, 0xdb, 0x22, 0x02, 0x53, 0x2d, 0x75, 0x2f, 0x95, 0x12, 0x7e, 0x30, 0x22, - 0x75, 0x2f, 0x96, 0x12, 0x7e, 0x30, 0x22, 0x10, 0x0b, 0x01, 0x22, 0x20, 0x2b, 0x03, 0xd2, 0x0b, - 0x22, 0x75, 0x2f, 0xa3, 0x12, 0x7e, 0x30, 0x7e, 0x14, 0x83, 0x00, 0x80, 0x06, 0x20, 0x2b, 0x03, + 0x74, 0x07, 0xa9, 0x20, 0x30, 0x0b, 0xa9, 0x35, 0x30, 0x05, 0xbe, 0x20, 0x01, 0x28, 0x02, 0x74, + 0x0f, 0x19, 0xb0, 0x00, 0x04, 0xa9, 0x33, 0x30, 0x18, 0x74, 0xbf, 0x19, 0xb0, 0x00, 0x0c, 0x74, + 0x28, 0xa9, 0x20, 0x30, 0x02, 0x74, 0x20, 0x19, 0xb0, 0x00, 0x04, 0x74, 0x03, 0x19, 0xb0, 0x00, + 0x0c, 0x74, 0xa7, 0x19, 0xb0, 0x00, 0x08, 0x74, 0x0c, 0x19, 0xb0, 0x00, 0x10, 0x22, 0x7e, 0x04, + 0x80, 0x00, 0x4c, 0x02, 0xe4, 0x19, 0xb0, 0x00, 0x04, 0x09, 0xb0, 0x00, 0x10, 0x54, 0x08, 0x19, + 0xb0, 0x00, 0x10, 0x74, 0xa7, 0x19, 0xb0, 0x00, 0x08, 0x22, 0x7c, 0xb2, 0x23, 0x0a, 0x2b, 0x49, + 0x22, 0x41, 0xa5, 0x89, 0x24, 0x41, 0xb5, 0x41, 0xd4, 0x41, 0xf3, 0x42, 0x12, 0x42, 0x31, 0x42, + 0x50, 0x42, 0x6f, 0x42, 0x8e, 0xc2, 0x10, 0xc2, 0x18, 0xc2, 0x20, 0xc2, 0x08, 0x7e, 0x04, 0x09, + 0xe3, 0x7a, 0x07, 0x01, 0x6f, 0x7a, 0x07, 0x01, 0x7f, 0x6d, 0x00, 0x7a, 0x07, 0x01, 0x8f, 0x7a, + 0x07, 0x01, 0x9f, 0x22, 0xc2, 0x11, 0xc2, 0x19, 0xc2, 0x21, 0xc2, 0x09, 0x7e, 0x04, 0x0d, 0xe3, + 0x7a, 0x07, 0x01, 0x71, 0x7a, 0x07, 0x01, 0x81, 0x6d, 0x00, 0x7a, 0x07, 0x01, 0x91, 0x7a, 0x07, + 0x01, 0xa1, 0x22, 0xc2, 0x12, 0xc2, 0x1a, 0xc2, 0x22, 0xc2, 0x0a, 0x7e, 0x04, 0x11, 0xe3, 0x7a, + 0x07, 0x01, 0x73, 0x7a, 0x07, 0x01, 0x83, 0x6d, 0x00, 0x7a, 0x07, 0x01, 0x93, 0x7a, 0x07, 0x01, + 0xa3, 0x22, 0xc2, 0x13, 0xc2, 0x1b, 0xc2, 0x23, 0xc2, 0x0b, 0x7e, 0x04, 0x15, 0xe3, 0x7a, 0x07, + 0x01, 0x75, 0x7a, 0x07, 0x01, 0x85, 0x6d, 0x00, 0x7a, 0x07, 0x01, 0x95, 0x7a, 0x07, 0x01, 0xa5, + 0x22, 0xc2, 0x14, 0xc2, 0x1c, 0xc2, 0x24, 0xc2, 0x0c, 0x7e, 0x04, 0x19, 0xe3, 0x7a, 0x07, 0x01, + 0x77, 0x7a, 0x07, 0x01, 0x87, 0x6d, 0x00, 0x7a, 0x07, 0x01, 0x97, 0x7a, 0x07, 0x01, 0xa7, 0x22, + 0xc2, 0x15, 0xc2, 0x1d, 0xc2, 0x25, 0xc2, 0x0d, 0x7e, 0x04, 0x1d, 0xe3, 0x7a, 0x07, 0x01, 0x79, + 0x7a, 0x07, 0x01, 0x89, 0x6d, 0x00, 0x7a, 0x07, 0x01, 0x99, 0x7a, 0x07, 0x01, 0xa9, 0x22, 0xc2, + 0x16, 0xc2, 0x1e, 0xc2, 0x26, 0xc2, 0x0e, 0x7e, 0x04, 0x21, 0xe3, 0x7a, 0x07, 0x01, 0x7b, 0x7a, + 0x07, 0x01, 0x8b, 0x6d, 0x00, 0x7a, 0x07, 0x01, 0x9b, 0x7a, 0x07, 0x01, 0xab, 0x22, 0xc2, 0x17, + 0xc2, 0x1f, 0xc2, 0x27, 0xc2, 0x0f, 0x7e, 0x04, 0x25, 0xe3, 0x7a, 0x07, 0x01, 0x7d, 0x7a, 0x07, + 0x01, 0x8d, 0x6d, 0x00, 0x7a, 0x07, 0x01, 0x9d, 0x7a, 0x07, 0x01, 0xad, 0x22, 0x7c, 0xb2, 0x23, + 0x0a, 0x2b, 0x49, 0x22, 0x42, 0xb8, 0x89, 0x24, 0x42, 0xc8, 0x42, 0xe9, 0x43, 0x0a, 0x43, 0x2b, + 0x43, 0x4c, 0x43, 0x6d, 0x43, 0x8e, 0x43, 0xaf, 0x30, 0x50, 0x07, 0x20, 0x68, 0x04, 0xc2, 0x28, + 0x80, 0x16, 0x30, 0x40, 0x07, 0x20, 0x60, 0x04, 0xc2, 0x28, 0x80, 0x0c, 0x30, 0x48, 0x07, 0x20, + 0x58, 0x04, 0xc2, 0x28, 0x80, 0x02, 0xd2, 0x28, 0x22, 0x30, 0x51, 0x07, 0x20, 0x69, 0x04, 0xc2, + 0x29, 0x80, 0x16, 0x30, 0x41, 0x07, 0x20, 0x61, 0x04, 0xc2, 0x29, 0x80, 0x0c, 0x30, 0x49, 0x07, + 0x20, 0x59, 0x04, 0xc2, 0x29, 0x80, 0x02, 0xd2, 0x29, 0x22, 0x30, 0x52, 0x07, 0x20, 0x6a, 0x04, + 0xc2, 0x2a, 0x80, 0x16, 0x30, 0x42, 0x07, 0x20, 0x62, 0x04, 0xc2, 0x2a, 0x80, 0x0c, 0x30, 0x4a, + 0x07, 0x20, 0x5a, 0x04, 0xc2, 0x2a, 0x80, 0x02, 0xd2, 0x2a, 0x22, 0x30, 0x53, 0x07, 0x20, 0x6b, + 0x04, 0xc2, 0x2b, 0x80, 0x16, 0x30, 0x43, 0x07, 0x20, 0x63, 0x04, 0xc2, 0x2b, 0x80, 0x0c, 0x30, + 0x4b, 0x07, 0x20, 0x5b, 0x04, 0xc2, 0x2b, 0x80, 0x02, 0xd2, 0x2b, 0x22, 0x30, 0x54, 0x07, 0x20, + 0x6c, 0x04, 0xc2, 0x2c, 0x80, 0x16, 0x30, 0x44, 0x07, 0x20, 0x64, 0x04, 0xc2, 0x2c, 0x80, 0x0c, + 0x30, 0x4c, 0x07, 0x20, 0x5c, 0x04, 0xc2, 0x2c, 0x80, 0x02, 0xd2, 0x2c, 0x22, 0x30, 0x55, 0x07, + 0x20, 0x6d, 0x04, 0xc2, 0x2d, 0x80, 0x16, 0x30, 0x45, 0x07, 0x20, 0x65, 0x04, 0xc2, 0x2d, 0x80, + 0x0c, 0x30, 0x4d, 0x07, 0x20, 0x5d, 0x04, 0xc2, 0x2d, 0x80, 0x02, 0xd2, 0x2d, 0x22, 0x30, 0x56, + 0x07, 0x20, 0x6e, 0x04, 0xc2, 0x2e, 0x80, 0x16, 0x30, 0x46, 0x07, 0x20, 0x66, 0x04, 0xc2, 0x2e, + 0x80, 0x0c, 0x30, 0x4e, 0x07, 0x20, 0x5e, 0x04, 0xc2, 0x2e, 0x80, 0x02, 0xd2, 0x2e, 0x22, 0x30, + 0x57, 0x07, 0x20, 0x6f, 0x04, 0xc2, 0x2f, 0x80, 0x16, 0x30, 0x47, 0x07, 0x20, 0x67, 0x04, 0xc2, + 0x2f, 0x80, 0x0c, 0x30, 0x4f, 0x07, 0x20, 0x5f, 0x04, 0xc2, 0x2f, 0x80, 0x02, 0xd2, 0x2f, 0x22, + 0x44, 0x38, 0x43, 0xe5, 0xbe, 0xb0, 0x02, 0x40, 0x01, 0x22, 0x23, 0x0a, 0x5b, 0x49, 0x55, 0x43, + 0xd0, 0x99, 0x54, 0xd3, 0x22, 0xa9, 0xc5, 0x87, 0x12, 0x44, 0x43, 0x7e, 0x04, 0x05, 0xe3, 0x7a, + 0x07, 0x01, 0xd7, 0x7a, 0x07, 0x01, 0xd9, 0x7e, 0x04, 0x01, 0xe3, 0x7a, 0x07, 0x01, 0xdd, 0x7a, + 0x07, 0x01, 0xdf, 0x7e, 0x04, 0x74, 0xad, 0x7a, 0x05, 0x61, 0x75, 0xf1, 0x01, 0x75, 0xe1, 0x1f, + 0x75, 0xe4, 0x04, 0x75, 0xf4, 0x04, 0x75, 0xf1, 0x02, 0x75, 0xe1, 0x03, 0x75, 0xe4, 0x04, 0x75, + 0xf4, 0x04, 0x43, 0xa2, 0x1c, 0x12, 0x40, 0xeb, 0x7e, 0x20, 0x00, 0x12, 0x41, 0x9a, 0x0b, 0x20, + 0xbe, 0x21, 0x2f, 0x78, 0xf6, 0xd2, 0xa8, 0x22, 0xa9, 0xd5, 0x87, 0x12, 0x44, 0x43, 0xd2, 0x92, + 0xc2, 0xa8, 0x22, 0x75, 0xa3, 0x00, 0x53, 0xa2, 0x03, 0x75, 0xc1, 0x00, 0x53, 0xc0, 0x03, 0x7e, + 0x00, 0x05, 0x7a, 0x01, 0xf1, 0x43, 0xf4, 0x80, 0x43, 0xe4, 0x80, 0xe5, 0xf2, 0x54, 0x7f, 0x44, + 0x08, 0xf5, 0xf2, 0xe5, 0xe2, 0x54, 0x7f, 0x44, 0x08, 0xf5, 0xe2, 0x75, 0xe1, 0x10, 0xa5, 0xd8, + 0xe1, 0x22, 0xca, 0x09, 0x12, 0x30, 0x20, 0x10, 0x01, 0x12, 0xd5, 0x67, 0x1e, 0x63, 0x69, 0x01, + 0x7e, 0x00, 0x6a, 0x2e, 0x01, 0x69, 0xa5, 0xe6, 0xf5, 0x67, 0x80, 0x12, 0x20, 0x02, 0x1e, 0x75, + 0x69, 0x00, 0x85, 0x6a, 0x67, 0xd2, 0x02, 0x74, 0x00, 0x80, 0x0d, 0x30, 0x02, 0x0f, 0xc2, 0x02, + 0x7e, 0x00, 0x6c, 0x2e, 0x01, 0x69, 0xa5, 0xe6, 0x53, 0x90, 0xcf, 0x42, 0x90, 0xda, 0x09, 0x32, + 0xc0, 0xd0, 0xc0, 0xd1, 0xc0, 0xe0, 0xc0, 0xf0, 0xca, 0x0b, 0xca, 0x1b, 0xca, 0x2b, 0xd2, 0x01, + 0x75, 0x31, 0x89, 0x12, 0x7c, 0x15, 0x7e, 0x14, 0x80, 0x00, 0x09, 0xb1, 0x00, 0x08, 0x20, 0xe0, + 0x03, 0x02, 0x45, 0x4f, 0x20, 0x78, 0x5a, 0xa5, 0x0a, 0x09, 0xb1, 0x00, 0x08, 0x20, 0xe0, 0x03, + 0x02, 0x45, 0x67, 0x20, 0x79, 0x4b, 0xa5, 0x0a, 0x09, 0xb1, 0x00, 0x08, 0x20, 0xe0, 0x03, 0x02, + 0x45, 0x7f, 0xa5, 0x0a, 0x09, 0xb1, 0x00, 0x08, 0x20, 0xe0, 0x03, 0x02, 0x45, 0x97, 0x20, 0x7a, + 0x30, 0xa5, 0x0a, 0x09, 0xb1, 0x00, 0x08, 0x20, 0xe0, 0x03, 0x02, 0x45, 0xaf, 0xa5, 0x0a, 0x09, + 0xb1, 0x00, 0x08, 0x20, 0xe0, 0x03, 0x02, 0x45, 0xc7, 0xa5, 0x0a, 0x09, 0xb1, 0x00, 0x08, 0x20, + 0xe0, 0x03, 0x02, 0x45, 0xdf, 0xa5, 0x0a, 0x09, 0xb1, 0x00, 0x08, 0x20, 0xe0, 0x03, 0x02, 0x45, + 0xf7, 0x30, 0x04, 0x0c, 0xc2, 0x04, 0xc0, 0xf1, 0x75, 0xf1, 0x01, 0x12, 0x6f, 0xd9, 0xd0, 0xf1, + 0xda, 0x2b, 0xda, 0x1b, 0xda, 0x0b, 0xd0, 0xf0, 0xd0, 0xe0, 0xd0, 0xd1, 0xd0, 0xd0, 0x32, 0x75, + 0x31, 0x80, 0x12, 0x7c, 0x15, 0x54, 0x3e, 0x0a, 0x5b, 0x7e, 0x44, 0x00, 0xff, 0x69, 0x52, 0x63, + 0x2b, 0xca, 0x06, 0xc6, 0x44, 0x89, 0x54, 0x75, 0x31, 0x81, 0x12, 0x7c, 0x15, 0x54, 0x3e, 0x0a, + 0x5b, 0x7e, 0x44, 0x00, 0xff, 0x69, 0x52, 0x63, 0x6b, 0xca, 0x06, 0xc6, 0x44, 0x89, 0x54, 0x75, + 0x31, 0x82, 0x12, 0x7c, 0x15, 0x54, 0x3e, 0x0a, 0x5b, 0x7e, 0x44, 0x00, 0xff, 0x69, 0x52, 0x63, + 0xab, 0xca, 0x06, 0xc6, 0x44, 0x89, 0x54, 0x75, 0x31, 0x83, 0x12, 0x7c, 0x15, 0x54, 0x3e, 0x0a, + 0x5b, 0x7e, 0x44, 0x00, 0xff, 0x69, 0x52, 0x63, 0xeb, 0xca, 0x06, 0xc6, 0x44, 0x89, 0x54, 0x75, + 0x31, 0x84, 0x12, 0x7c, 0x15, 0x54, 0x3e, 0x0a, 0x5b, 0x7e, 0x44, 0x00, 0xff, 0x69, 0x52, 0x64, + 0x2b, 0xca, 0x06, 0xc6, 0x44, 0x89, 0x54, 0x75, 0x31, 0x85, 0x12, 0x7c, 0x15, 0x54, 0x3e, 0x0a, + 0x5b, 0x7e, 0x44, 0x00, 0xff, 0x69, 0x52, 0x64, 0x6b, 0xca, 0x06, 0xc6, 0x44, 0x89, 0x54, 0x75, + 0x31, 0x86, 0x12, 0x7c, 0x15, 0x54, 0x3e, 0x0a, 0x5b, 0x7e, 0x44, 0x00, 0xff, 0x69, 0x52, 0x64, + 0xab, 0xca, 0x06, 0xc6, 0x44, 0x89, 0x54, 0x75, 0x31, 0x87, 0x12, 0x7c, 0x15, 0x54, 0x3e, 0x0a, + 0x5b, 0x7e, 0x44, 0x00, 0xff, 0x69, 0x52, 0x64, 0xeb, 0xca, 0x06, 0xc6, 0x44, 0x89, 0x54, 0x10, + 0x08, 0x01, 0x22, 0x20, 0x28, 0x03, 0xd2, 0x08, 0x22, 0x75, 0x31, 0xa0, 0x12, 0x7c, 0x15, 0x7e, + 0x14, 0x80, 0x00, 0x80, 0x06, 0x20, 0x28, 0x03, 0xd2, 0x08, 0x22, 0x09, 0xb1, 0x00, 0x14, 0xca, + 0xb8, 0x5e, 0xb0, 0x1e, 0xda, 0xb8, 0x68, 0x03, 0x12, 0x49, 0x30, 0x20, 0xe6, 0x03, 0xd2, 0x08, + 0x22, 0x30, 0x30, 0x49, 0xd2, 0x70, 0x7e, 0x37, 0x01, 0x8f, 0x7e, 0x27, 0x01, 0xaf, 0x9d, 0x32, + 0x40, 0x31, 0x7d, 0x02, 0x2e, 0x05, 0x48, 0x7a, 0x05, 0x48, 0x7a, 0x37, 0x01, 0x8f, 0x7e, 0x37, + 0x01, 0x6f, 0x7d, 0x43, 0x2d, 0x42, 0xbe, 0x44, 0x0d, 0xe2, 0x38, 0x68, 0x7a, 0x47, 0x01, 0x6f, + 0x75, 0x31, 0x94, 0x12, 0x7c, 0x15, 0x7a, 0x51, 0x31, 0x12, 0x7c, 0x15, 0x12, 0x65, 0x2b, 0x10, + 0x70, 0xc4, 0x22, 0xc2, 0x70, 0x2d, 0x23, 0x68, 0x78, 0x6d, 0x33, 0x80, 0x1a, 0x7e, 0x27, 0x01, + 0x8f, 0xbe, 0x24, 0x00, 0x00, 0x68, 0x6a, 0xbe, 0x27, 0x01, 0xaf, 0x28, 0x04, 0x7e, 0x27, 0x01, + 0xaf, 0x7e, 0x37, 0x01, 0x8f, 0x9d, 0x32, 0x7d, 0x02, 0x2e, 0x05, 0x48, 0x7a, 0x05, 0x48, 0x7a, + 0x37, 0x01, 0x8f, 0x7e, 0x37, 0x01, 0x6f, 0x7d, 0x43, 0x2d, 0x42, 0xbe, 0x44, 0x0d, 0xe2, 0x38, + 0x13, 0x7a, 0x47, 0x01, 0x6f, 0x75, 0x31, 0x94, 0x12, 0x7c, 0x15, 0x7a, 0x51, 0x31, 0x12, 0x7c, + 0x15, 0x02, 0x65, 0x2b, 0x75, 0x31, 0x94, 0x12, 0x7c, 0x15, 0x7a, 0x51, 0x31, 0x12, 0x7c, 0x15, + 0x9e, 0x44, 0x0d, 0xe3, 0x9d, 0x24, 0x12, 0x65, 0x2b, 0x7e, 0x34, 0x09, 0xe3, 0x7d, 0x24, 0x2d, + 0x43, 0x7a, 0x47, 0x01, 0x6f, 0x12, 0x65, 0x2b, 0xbe, 0x25, 0x20, 0x78, 0x03, 0x02, 0x46, 0x7f, + 0x22, 0xd2, 0x08, 0x7e, 0x04, 0x09, 0xe3, 0x7a, 0x07, 0x01, 0x6f, 0x7a, 0x07, 0x01, 0x7f, 0x75, + 0x31, 0x94, 0x12, 0x7c, 0x15, 0x75, 0x31, 0x00, 0x12, 0x7c, 0x15, 0x22, 0x75, 0x31, 0x92, 0x12, + 0x7c, 0x15, 0xd2, 0x04, 0x09, 0xb1, 0x00, 0x14, 0xca, 0xb8, 0x54, 0x82, 0xda, 0xb8, 0x78, 0x70, + 0x7e, 0x37, 0x01, 0xe1, 0x7e, 0x27, 0x01, 0xbf, 0x2e, 0x24, 0x00, 0x02, 0x2d, 0x32, 0xbe, 0x34, + 0x04, 0x00, 0x38, 0x3c, 0x7d, 0x02, 0x2e, 0x05, 0x46, 0x7a, 0x05, 0x46, 0x7a, 0x37, 0x01, 0xe1, + 0x7e, 0x37, 0x01, 0xdf, 0x7d, 0x43, 0x2d, 0x42, 0xbe, 0x44, 0x05, 0xe2, 0x38, 0x44, 0x7a, 0x47, + 0x01, 0xdf, 0x7e, 0x24, 0x00, 0x00, 0x2e, 0x27, 0x01, 0xbf, 0x1b, 0x38, 0x20, 0x0b, 0x35, 0x7a, + 0x51, 0x31, 0x12, 0x7c, 0x15, 0xbe, 0x50, 0x38, 0x78, 0x03, 0x02, 0x66, 0x16, 0x02, 0x65, 0xfb, + 0x75, 0x31, 0x99, 0x12, 0x7c, 0x15, 0x09, 0xb1, 0x00, 0x04, 0x54, 0xfa, 0x19, 0xb1, 0x00, 0x04, + 0x30, 0x38, 0x0a, 0x09, 0xb1, 0x00, 0x10, 0x54, 0xfe, 0x19, 0xb1, 0x00, 0x10, 0xd2, 0x10, 0x22, + 0x80, 0x7d, 0x7a, 0x51, 0x31, 0x12, 0x7c, 0x15, 0x9e, 0x44, 0x05, 0xe3, 0x9d, 0x24, 0x7e, 0x64, + 0x00, 0x00, 0x2e, 0x67, 0x01, 0xbf, 0x9e, 0x24, 0x00, 0x02, 0x40, 0x17, 0x1b, 0x38, 0x60, 0x0b, + 0x35, 0x12, 0x65, 0xfb, 0x7e, 0x34, 0x01, 0xe3, 0x7d, 0x24, 0x2d, 0x43, 0x7a, 0x47, 0x01, 0xdf, + 0x02, 0x65, 0xfb, 0x7a, 0x39, 0xc0, 0x7e, 0x34, 0x01, 0xe3, 0x7a, 0x39, 0xd0, 0x0b, 0x34, 0x1b, + 0x44, 0x80, 0xe5, 0x9d, 0x32, 0x7c, 0xb6, 0x54, 0x0f, 0x23, 0x23, 0x23, 0x44, 0x00, 0x7a, 0x69, + 0xb0, 0x7a, 0x79, 0x70, 0x0b, 0x35, 0x75, 0x31, 0x93, 0x12, 0x7c, 0x15, 0x7a, 0x71, 0x31, 0x12, + 0x7c, 0x15, 0xbd, 0x04, 0x68, 0x29, 0x7a, 0x07, 0x01, 0xdf, 0x7e, 0x47, 0x01, 0xe1, 0x2d, 0x43, + 0x7a, 0x47, 0x01, 0xe1, 0x2e, 0x35, 0x46, 0x7a, 0x35, 0x46, 0x22, 0x09, 0xb1, 0x00, 0x14, 0x20, + 0xe0, 0x13, 0x22, 0x7e, 0x04, 0x01, 0xe3, 0x80, 0x2a, 0x7e, 0x04, 0x01, 0xe3, 0x80, 0x2c, 0x7e, + 0x04, 0x01, 0xe3, 0x80, 0xd1, 0xd2, 0x04, 0x7e, 0x07, 0x01, 0xe1, 0x7e, 0x24, 0x03, 0xfe, 0x9d, + 0x20, 0x28, 0x40, 0x7e, 0x07, 0x01, 0xdf, 0x7e, 0x44, 0x05, 0xe3, 0x7d, 0x60, 0x0b, 0x04, 0xbd, + 0x04, 0x68, 0xd0, 0x7d, 0x70, 0x0b, 0x04, 0xbd, 0x04, 0x68, 0xce, 0x7d, 0x54, 0x9d, 0x50, 0xbd, + 0x25, 0x40, 0x02, 0x7d, 0x25, 0x7d, 0x32, 0x09, 0xb1, 0x00, 0x14, 0xca, 0xb8, 0x54, 0x1f, 0xb4, + 0x01, 0x31, 0xda, 0xb8, 0x7e, 0x19, 0xb0, 0x7a, 0x09, 0xb0, 0x0b, 0x04, 0x1b, 0x24, 0x78, 0xe7, + 0x02, 0x47, 0xe3, 0x75, 0x31, 0x99, 0x12, 0x7c, 0x15, 0x09, 0xb1, 0x00, 0x04, 0x54, 0xfa, 0x19, + 0xb1, 0x00, 0x04, 0x30, 0x38, 0x0a, 0x09, 0xb1, 0x00, 0x10, 0x54, 0xfe, 0x19, 0xb1, 0x00, 0x10, + 0xd2, 0x10, 0x22, 0xda, 0xb8, 0x30, 0xe0, 0xd8, 0xbd, 0x32, 0x68, 0x07, 0xca, 0xb8, 0x12, 0x47, + 0xe3, 0xda, 0xb8, 0x02, 0x49, 0x30, 0x09, 0xb1, 0x00, 0x18, 0x7e, 0xa0, 0x88, 0x75, 0x31, 0x90, + 0x12, 0x7c, 0x15, 0xf5, 0x31, 0x12, 0x7c, 0x15, 0xa5, 0xfc, 0x5e, 0xb0, 0xf0, 0xa5, 0xfd, 0x09, + 0xb1, 0x00, 0x18, 0x4c, 0x4b, 0x5e, 0xb0, 0xf0, 0xbc, 0xb5, 0x78, 0xf1, 0x5e, 0x40, 0x0f, 0x4c, + 0x54, 0x7c, 0xb5, 0x5e, 0x50, 0x0b, 0x68, 0x2a, 0xa5, 0xfd, 0x5e, 0x50, 0x10, 0x68, 0x04, 0xd2, + 0x68, 0x80, 0x02, 0xc2, 0x68, 0xa5, 0xfd, 0x5e, 0x50, 0x20, 0x68, 0x04, 0xd2, 0x60, 0x80, 0x02, + 0xc2, 0x60, 0xa5, 0xfd, 0x5e, 0x50, 0x80, 0x68, 0x04, 0xd2, 0x58, 0x80, 0x02, 0xc2, 0x58, 0x12, + 0x42, 0xc8, 0x02, 0x62, 0x93, 0x75, 0x31, 0x91, 0x12, 0x7c, 0x15, 0x09, 0xb1, 0x00, 0x14, 0x7a, + 0xb1, 0x31, 0x12, 0x7c, 0x15, 0x20, 0xe0, 0x08, 0xd2, 0x04, 0x7e, 0xa0, 0x80, 0x02, 0x62, 0x93, + 0xd2, 0x04, 0x30, 0xe1, 0x06, 0x7e, 0xa0, 0x80, 0x12, 0x62, 0x93, 0xca, 0xb8, 0x5e, 0xb0, 0x1c, + 0xda, 0xb8, 0x68, 0x12, 0x7e, 0xa0, 0xc0, 0x09, 0x61, 0x00, 0x00, 0x12, 0x62, 0xb6, 0x09, 0xb1, + 0x00, 0x14, 0x20, 0xe0, 0xdb, 0x22, 0x02, 0x48, 0x35, 0x75, 0x31, 0x95, 0x12, 0x7c, 0x15, 0x22, + 0x75, 0x31, 0x96, 0x12, 0x7c, 0x15, 0x22, 0x10, 0x09, 0x01, 0x22, 0x20, 0x29, 0x03, 0xd2, 0x09, + 0x22, 0x75, 0x31, 0xa1, 0x12, 0x7c, 0x15, 0x7e, 0x14, 0x81, 0x00, 0x80, 0x06, 0x20, 0x29, 0x03, + 0xd2, 0x09, 0x22, 0x09, 0xb1, 0x00, 0x14, 0xca, 0xb8, 0x5e, 0xb0, 0x1e, 0xda, 0xb8, 0x68, 0x03, + 0x12, 0x4c, 0x88, 0x20, 0xe6, 0x03, 0xd2, 0x09, 0x22, 0x30, 0x31, 0x49, 0xd2, 0x71, 0x7e, 0x37, + 0x01, 0x91, 0x7e, 0x27, 0x01, 0xb1, 0x9d, 0x32, 0x40, 0x31, 0x7d, 0x02, 0x2e, 0x05, 0x4a, 0x7a, + 0x05, 0x4a, 0x7a, 0x37, 0x01, 0x91, 0x7e, 0x37, 0x01, 0x71, 0x7d, 0x43, 0x2d, 0x42, 0xbe, 0x44, + 0x11, 0xe2, 0x38, 0x68, 0x7a, 0x47, 0x01, 0x71, 0x75, 0x31, 0x94, 0x12, 0x7c, 0x15, 0x7a, 0x51, + 0x31, 0x12, 0x7c, 0x15, 0x12, 0x65, 0x2b, 0x10, 0x71, 0xc4, 0x22, 0xc2, 0x71, 0x2d, 0x23, 0x68, + 0x78, 0x6d, 0x33, 0x80, 0x1a, 0x7e, 0x27, 0x01, 0x91, 0xbe, 0x24, 0x00, 0x00, 0x68, 0x6a, 0xbe, + 0x27, 0x01, 0xb1, 0x28, 0x04, 0x7e, 0x27, 0x01, 0xb1, 0x7e, 0x37, 0x01, 0x91, 0x9d, 0x32, 0x7d, + 0x02, 0x2e, 0x05, 0x4a, 0x7a, 0x05, 0x4a, 0x7a, 0x37, 0x01, 0x91, 0x7e, 0x37, 0x01, 0x71, 0x7d, + 0x43, 0x2d, 0x42, 0xbe, 0x44, 0x11, 0xe2, 0x38, 0x13, 0x7a, 0x47, 0x01, 0x71, 0x75, 0x31, 0x94, + 0x12, 0x7c, 0x15, 0x7a, 0x51, 0x31, 0x12, 0x7c, 0x15, 0x02, 0x65, 0x2b, 0x75, 0x31, 0x94, 0x12, + 0x7c, 0x15, 0x7a, 0x51, 0x31, 0x12, 0x7c, 0x15, 0x9e, 0x44, 0x11, 0xe3, 0x9d, 0x24, 0x12, 0x65, + 0x2b, 0x7e, 0x34, 0x0d, 0xe3, 0x7d, 0x24, 0x2d, 0x43, 0x7a, 0x47, 0x01, 0x71, 0x12, 0x65, 0x2b, + 0xbe, 0x25, 0x20, 0x78, 0x03, 0x02, 0x49, 0xd7, 0x22, 0xd2, 0x09, 0x7e, 0x04, 0x0d, 0xe3, 0x7a, + 0x07, 0x01, 0x71, 0x7a, 0x07, 0x01, 0x81, 0x75, 0x31, 0x94, 0x12, 0x7c, 0x15, 0x75, 0x31, 0x00, + 0x12, 0x7c, 0x15, 0x22, 0x75, 0x31, 0x92, 0x12, 0x7c, 0x15, 0xd2, 0x04, 0x09, 0xb1, 0x00, 0x14, + 0xca, 0xb8, 0x54, 0x82, 0xda, 0xb8, 0x78, 0x70, 0x7e, 0x37, 0x01, 0xe1, 0x7e, 0x27, 0x01, 0xc1, + 0x2e, 0x24, 0x00, 0x02, 0x2d, 0x32, 0xbe, 0x34, 0x04, 0x00, 0x38, 0x3c, 0x7d, 0x02, 0x2e, 0x05, + 0x46, 0x7a, 0x05, 0x46, 0x7a, 0x37, 0x01, 0xe1, 0x7e, 0x37, 0x01, 0xdf, 0x7d, 0x43, 0x2d, 0x42, + 0xbe, 0x44, 0x05, 0xe2, 0x38, 0x44, 0x7a, 0x47, 0x01, 0xdf, 0x7e, 0x24, 0x01, 0x00, 0x2e, 0x27, + 0x01, 0xc1, 0x1b, 0x38, 0x20, 0x0b, 0x35, 0x7a, 0x51, 0x31, 0x12, 0x7c, 0x15, 0xbe, 0x50, 0x38, + 0x78, 0x03, 0x02, 0x66, 0x16, 0x02, 0x65, 0xfb, 0x75, 0x31, 0x99, 0x12, 0x7c, 0x15, 0x09, 0xb1, + 0x00, 0x04, 0x54, 0xfa, 0x19, 0xb1, 0x00, 0x04, 0x30, 0x39, 0x0a, 0x09, 0xb1, 0x00, 0x10, 0x54, + 0xfe, 0x19, 0xb1, 0x00, 0x10, 0xd2, 0x11, 0x22, 0x80, 0x7d, 0x7a, 0x51, 0x31, 0x12, 0x7c, 0x15, + 0x9e, 0x44, 0x05, 0xe3, 0x9d, 0x24, 0x7e, 0x64, 0x01, 0x00, 0x2e, 0x67, 0x01, 0xc1, 0x9e, 0x24, + 0x00, 0x02, 0x40, 0x17, 0x1b, 0x38, 0x60, 0x0b, 0x35, 0x12, 0x65, 0xfb, 0x7e, 0x34, 0x01, 0xe3, + 0x7d, 0x24, 0x2d, 0x43, 0x7a, 0x47, 0x01, 0xdf, 0x02, 0x65, 0xfb, 0x7a, 0x39, 0xc0, 0x7e, 0x34, + 0x01, 0xe3, 0x7a, 0x39, 0xd0, 0x0b, 0x34, 0x1b, 0x44, 0x80, 0xe5, 0x9d, 0x32, 0x7c, 0xb6, 0x54, + 0x0f, 0x23, 0x23, 0x23, 0x44, 0x01, 0x7a, 0x69, 0xb0, 0x7a, 0x79, 0x70, 0x0b, 0x35, 0x75, 0x31, + 0x93, 0x12, 0x7c, 0x15, 0x7a, 0x71, 0x31, 0x12, 0x7c, 0x15, 0xbd, 0x04, 0x68, 0x29, 0x7a, 0x07, + 0x01, 0xdf, 0x7e, 0x47, 0x01, 0xe1, 0x2d, 0x43, 0x7a, 0x47, 0x01, 0xe1, 0x2e, 0x35, 0x46, 0x7a, + 0x35, 0x46, 0x22, 0x09, 0xb1, 0x00, 0x14, 0x20, 0xe0, 0x13, 0x22, 0x7e, 0x04, 0x01, 0xe3, 0x80, + 0x2a, 0x7e, 0x04, 0x01, 0xe3, 0x80, 0x2c, 0x7e, 0x04, 0x01, 0xe3, 0x80, 0xd1, 0xd2, 0x04, 0x7e, + 0x07, 0x01, 0xe1, 0x7e, 0x24, 0x03, 0xfe, 0x9d, 0x20, 0x28, 0x40, 0x7e, 0x07, 0x01, 0xdf, 0x7e, + 0x44, 0x05, 0xe3, 0x7d, 0x60, 0x0b, 0x04, 0xbd, 0x04, 0x68, 0xd0, 0x7d, 0x70, 0x0b, 0x04, 0xbd, + 0x04, 0x68, 0xce, 0x7d, 0x54, 0x9d, 0x50, 0xbd, 0x25, 0x40, 0x02, 0x7d, 0x25, 0x7d, 0x32, 0x09, + 0xb1, 0x00, 0x14, 0xca, 0xb8, 0x54, 0x1f, 0xb4, 0x01, 0x31, 0xda, 0xb8, 0x7e, 0x19, 0xb0, 0x7a, + 0x09, 0xb0, 0x0b, 0x04, 0x1b, 0x24, 0x78, 0xe7, 0x02, 0x4b, 0x3b, 0x75, 0x31, 0x99, 0x12, 0x7c, + 0x15, 0x09, 0xb1, 0x00, 0x04, 0x54, 0xfa, 0x19, 0xb1, 0x00, 0x04, 0x30, 0x39, 0x0a, 0x09, 0xb1, + 0x00, 0x10, 0x54, 0xfe, 0x19, 0xb1, 0x00, 0x10, 0xd2, 0x11, 0x22, 0xda, 0xb8, 0x30, 0xe0, 0xd8, + 0xbd, 0x32, 0x68, 0x07, 0xca, 0xb8, 0x12, 0x4b, 0x3b, 0xda, 0xb8, 0x02, 0x4c, 0x88, 0x09, 0xb1, + 0x00, 0x18, 0x7e, 0xa0, 0x88, 0x75, 0x31, 0x90, 0x12, 0x7c, 0x15, 0xf5, 0x31, 0x12, 0x7c, 0x15, + 0xa5, 0xfc, 0x5e, 0xb0, 0xf0, 0xa5, 0xfd, 0x09, 0xb1, 0x00, 0x18, 0x4c, 0x4b, 0x5e, 0xb0, 0xf0, + 0xbc, 0xb5, 0x78, 0xf1, 0x5e, 0x40, 0x0f, 0x4c, 0x54, 0x7c, 0xb5, 0x5e, 0x50, 0x0b, 0x68, 0x2a, + 0xa5, 0xfd, 0x5e, 0x50, 0x10, 0x68, 0x04, 0xd2, 0x69, 0x80, 0x02, 0xc2, 0x69, 0xa5, 0xfd, 0x5e, + 0x50, 0x20, 0x68, 0x04, 0xd2, 0x61, 0x80, 0x02, 0xc2, 0x61, 0xa5, 0xfd, 0x5e, 0x50, 0x80, 0x68, + 0x04, 0xd2, 0x59, 0x80, 0x02, 0xc2, 0x59, 0x12, 0x42, 0xe9, 0x02, 0x62, 0x93, 0x75, 0x31, 0x91, + 0x12, 0x7c, 0x15, 0x09, 0xb1, 0x00, 0x14, 0x7a, 0xb1, 0x31, 0x12, 0x7c, 0x15, 0x20, 0xe0, 0x08, + 0xd2, 0x04, 0x7e, 0xa0, 0x80, 0x02, 0x62, 0x93, 0xd2, 0x04, 0x30, 0xe1, 0x06, 0x7e, 0xa0, 0x80, + 0x12, 0x62, 0x93, 0xca, 0xb8, 0x5e, 0xb0, 0x1c, 0xda, 0xb8, 0x68, 0x12, 0x7e, 0xa0, 0xc0, 0x09, + 0x61, 0x00, 0x00, 0x12, 0x62, 0xb6, 0x09, 0xb1, 0x00, 0x14, 0x20, 0xe0, 0xdb, 0x22, 0x02, 0x4b, + 0x8d, 0x75, 0x31, 0x95, 0x12, 0x7c, 0x15, 0x22, 0x75, 0x31, 0x96, 0x12, 0x7c, 0x15, 0x22, 0x10, + 0x0a, 0x01, 0x22, 0x20, 0x2a, 0x03, 0xd2, 0x0a, 0x22, 0x75, 0x31, 0xa2, 0x12, 0x7c, 0x15, 0x7e, + 0x14, 0x82, 0x00, 0x80, 0x06, 0x20, 0x2a, 0x03, 0xd2, 0x0a, 0x22, 0x09, 0xb1, 0x00, 0x14, 0xca, + 0xb8, 0x5e, 0xb0, 0x1e, 0xda, 0xb8, 0x68, 0x03, 0x12, 0x4f, 0xe0, 0x20, 0xe6, 0x03, 0xd2, 0x0a, + 0x22, 0x30, 0x32, 0x49, 0xd2, 0x72, 0x7e, 0x37, 0x01, 0x93, 0x7e, 0x27, 0x01, 0xb3, 0x9d, 0x32, + 0x40, 0x31, 0x7d, 0x02, 0x2e, 0x05, 0x4c, 0x7a, 0x05, 0x4c, 0x7a, 0x37, 0x01, 0x93, 0x7e, 0x37, + 0x01, 0x73, 0x7d, 0x43, 0x2d, 0x42, 0xbe, 0x44, 0x15, 0xe2, 0x38, 0x68, 0x7a, 0x47, 0x01, 0x73, + 0x75, 0x31, 0x94, 0x12, 0x7c, 0x15, 0x7a, 0x51, 0x31, 0x12, 0x7c, 0x15, 0x12, 0x65, 0x2b, 0x10, + 0x72, 0xc4, 0x22, 0xc2, 0x72, 0x2d, 0x23, 0x68, 0x78, 0x6d, 0x33, 0x80, 0x1a, 0x7e, 0x27, 0x01, + 0x93, 0xbe, 0x24, 0x00, 0x00, 0x68, 0x6a, 0xbe, 0x27, 0x01, 0xb3, 0x28, 0x04, 0x7e, 0x27, 0x01, + 0xb3, 0x7e, 0x37, 0x01, 0x93, 0x9d, 0x32, 0x7d, 0x02, 0x2e, 0x05, 0x4c, 0x7a, 0x05, 0x4c, 0x7a, + 0x37, 0x01, 0x93, 0x7e, 0x37, 0x01, 0x73, 0x7d, 0x43, 0x2d, 0x42, 0xbe, 0x44, 0x15, 0xe2, 0x38, + 0x13, 0x7a, 0x47, 0x01, 0x73, 0x75, 0x31, 0x94, 0x12, 0x7c, 0x15, 0x7a, 0x51, 0x31, 0x12, 0x7c, + 0x15, 0x02, 0x65, 0x2b, 0x75, 0x31, 0x94, 0x12, 0x7c, 0x15, 0x7a, 0x51, 0x31, 0x12, 0x7c, 0x15, + 0x9e, 0x44, 0x15, 0xe3, 0x9d, 0x24, 0x12, 0x65, 0x2b, 0x7e, 0x34, 0x11, 0xe3, 0x7d, 0x24, 0x2d, + 0x43, 0x7a, 0x47, 0x01, 0x73, 0x12, 0x65, 0x2b, 0xbe, 0x25, 0x20, 0x78, 0x03, 0x02, 0x4d, 0x2f, + 0x22, 0xd2, 0x0a, 0x7e, 0x04, 0x11, 0xe3, 0x7a, 0x07, 0x01, 0x73, 0x7a, 0x07, 0x01, 0x83, 0x75, + 0x31, 0x94, 0x12, 0x7c, 0x15, 0x75, 0x31, 0x00, 0x12, 0x7c, 0x15, 0x22, 0x75, 0x31, 0x92, 0x12, + 0x7c, 0x15, 0xd2, 0x04, 0x09, 0xb1, 0x00, 0x14, 0xca, 0xb8, 0x54, 0x82, 0xda, 0xb8, 0x78, 0x70, + 0x7e, 0x37, 0x01, 0xe1, 0x7e, 0x27, 0x01, 0xc3, 0x2e, 0x24, 0x00, 0x02, 0x2d, 0x32, 0xbe, 0x34, + 0x04, 0x00, 0x38, 0x3c, 0x7d, 0x02, 0x2e, 0x05, 0x46, 0x7a, 0x05, 0x46, 0x7a, 0x37, 0x01, 0xe1, + 0x7e, 0x37, 0x01, 0xdf, 0x7d, 0x43, 0x2d, 0x42, 0xbe, 0x44, 0x05, 0xe2, 0x38, 0x44, 0x7a, 0x47, + 0x01, 0xdf, 0x7e, 0x24, 0x02, 0x00, 0x2e, 0x27, 0x01, 0xc3, 0x1b, 0x38, 0x20, 0x0b, 0x35, 0x7a, + 0x51, 0x31, 0x12, 0x7c, 0x15, 0xbe, 0x50, 0x38, 0x78, 0x03, 0x02, 0x66, 0x16, 0x02, 0x65, 0xfb, + 0x75, 0x31, 0x99, 0x12, 0x7c, 0x15, 0x09, 0xb1, 0x00, 0x04, 0x54, 0xfa, 0x19, 0xb1, 0x00, 0x04, + 0x30, 0x3a, 0x0a, 0x09, 0xb1, 0x00, 0x10, 0x54, 0xfe, 0x19, 0xb1, 0x00, 0x10, 0xd2, 0x12, 0x22, + 0x80, 0x7d, 0x7a, 0x51, 0x31, 0x12, 0x7c, 0x15, 0x9e, 0x44, 0x05, 0xe3, 0x9d, 0x24, 0x7e, 0x64, + 0x02, 0x00, 0x2e, 0x67, 0x01, 0xc3, 0x9e, 0x24, 0x00, 0x02, 0x40, 0x17, 0x1b, 0x38, 0x60, 0x0b, + 0x35, 0x12, 0x65, 0xfb, 0x7e, 0x34, 0x01, 0xe3, 0x7d, 0x24, 0x2d, 0x43, 0x7a, 0x47, 0x01, 0xdf, + 0x02, 0x65, 0xfb, 0x7a, 0x39, 0xc0, 0x7e, 0x34, 0x01, 0xe3, 0x7a, 0x39, 0xd0, 0x0b, 0x34, 0x1b, + 0x44, 0x80, 0xe5, 0x9d, 0x32, 0x7c, 0xb6, 0x54, 0x0f, 0x23, 0x23, 0x23, 0x44, 0x02, 0x7a, 0x69, + 0xb0, 0x7a, 0x79, 0x70, 0x0b, 0x35, 0x75, 0x31, 0x93, 0x12, 0x7c, 0x15, 0x7a, 0x71, 0x31, 0x12, + 0x7c, 0x15, 0xbd, 0x04, 0x68, 0x29, 0x7a, 0x07, 0x01, 0xdf, 0x7e, 0x47, 0x01, 0xe1, 0x2d, 0x43, + 0x7a, 0x47, 0x01, 0xe1, 0x2e, 0x35, 0x46, 0x7a, 0x35, 0x46, 0x22, 0x09, 0xb1, 0x00, 0x14, 0x20, + 0xe0, 0x13, 0x22, 0x7e, 0x04, 0x01, 0xe3, 0x80, 0x2a, 0x7e, 0x04, 0x01, 0xe3, 0x80, 0x2c, 0x7e, + 0x04, 0x01, 0xe3, 0x80, 0xd1, 0xd2, 0x04, 0x7e, 0x07, 0x01, 0xe1, 0x7e, 0x24, 0x03, 0xfe, 0x9d, + 0x20, 0x28, 0x40, 0x7e, 0x07, 0x01, 0xdf, 0x7e, 0x44, 0x05, 0xe3, 0x7d, 0x60, 0x0b, 0x04, 0xbd, + 0x04, 0x68, 0xd0, 0x7d, 0x70, 0x0b, 0x04, 0xbd, 0x04, 0x68, 0xce, 0x7d, 0x54, 0x9d, 0x50, 0xbd, + 0x25, 0x40, 0x02, 0x7d, 0x25, 0x7d, 0x32, 0x09, 0xb1, 0x00, 0x14, 0xca, 0xb8, 0x54, 0x1f, 0xb4, + 0x01, 0x31, 0xda, 0xb8, 0x7e, 0x19, 0xb0, 0x7a, 0x09, 0xb0, 0x0b, 0x04, 0x1b, 0x24, 0x78, 0xe7, + 0x02, 0x4e, 0x93, 0x75, 0x31, 0x99, 0x12, 0x7c, 0x15, 0x09, 0xb1, 0x00, 0x04, 0x54, 0xfa, 0x19, + 0xb1, 0x00, 0x04, 0x30, 0x3a, 0x0a, 0x09, 0xb1, 0x00, 0x10, 0x54, 0xfe, 0x19, 0xb1, 0x00, 0x10, + 0xd2, 0x12, 0x22, 0xda, 0xb8, 0x30, 0xe0, 0xd8, 0xbd, 0x32, 0x68, 0x07, 0xca, 0xb8, 0x12, 0x4e, + 0x93, 0xda, 0xb8, 0x02, 0x4f, 0xe0, 0x09, 0xb1, 0x00, 0x18, 0x7e, 0xa0, 0x88, 0x75, 0x31, 0x90, + 0x12, 0x7c, 0x15, 0xf5, 0x31, 0x12, 0x7c, 0x15, 0xa5, 0xfc, 0x5e, 0xb0, 0xf0, 0xa5, 0xfd, 0x09, + 0xb1, 0x00, 0x18, 0x4c, 0x4b, 0x5e, 0xb0, 0xf0, 0xbc, 0xb5, 0x78, 0xf1, 0x5e, 0x40, 0x0f, 0x4c, + 0x54, 0x7c, 0xb5, 0x5e, 0x50, 0x0b, 0x68, 0x2a, 0xa5, 0xfd, 0x5e, 0x50, 0x10, 0x68, 0x04, 0xd2, + 0x6a, 0x80, 0x02, 0xc2, 0x6a, 0xa5, 0xfd, 0x5e, 0x50, 0x20, 0x68, 0x04, 0xd2, 0x62, 0x80, 0x02, + 0xc2, 0x62, 0xa5, 0xfd, 0x5e, 0x50, 0x80, 0x68, 0x04, 0xd2, 0x5a, 0x80, 0x02, 0xc2, 0x5a, 0x12, + 0x43, 0x0a, 0x02, 0x62, 0x93, 0x75, 0x31, 0x91, 0x12, 0x7c, 0x15, 0x09, 0xb1, 0x00, 0x14, 0x7a, + 0xb1, 0x31, 0x12, 0x7c, 0x15, 0x20, 0xe0, 0x08, 0xd2, 0x04, 0x7e, 0xa0, 0x80, 0x02, 0x62, 0x93, + 0xd2, 0x04, 0x30, 0xe1, 0x06, 0x7e, 0xa0, 0x80, 0x12, 0x62, 0x93, 0xca, 0xb8, 0x5e, 0xb0, 0x1c, + 0xda, 0xb8, 0x68, 0x12, 0x7e, 0xa0, 0xc0, 0x09, 0x61, 0x00, 0x00, 0x12, 0x62, 0xb6, 0x09, 0xb1, + 0x00, 0x14, 0x20, 0xe0, 0xdb, 0x22, 0x02, 0x4e, 0xe5, 0x75, 0x31, 0x95, 0x12, 0x7c, 0x15, 0x22, + 0x75, 0x31, 0x96, 0x12, 0x7c, 0x15, 0x22, 0x10, 0x0b, 0x01, 0x22, 0x20, 0x2b, 0x03, 0xd2, 0x0b, + 0x22, 0x75, 0x31, 0xa3, 0x12, 0x7c, 0x15, 0x7e, 0x14, 0x83, 0x00, 0x80, 0x06, 0x20, 0x2b, 0x03, 0xd2, 0x0b, 0x22, 0x09, 0xb1, 0x00, 0x14, 0xca, 0xb8, 0x5e, 0xb0, 0x1e, 0xda, 0xb8, 0x68, 0x03, - 0x12, 0x57, 0x35, 0x30, 0x33, 0x06, 0x20, 0xe6, 0x4f, 0xd2, 0x0b, 0x22, 0x30, 0xe6, 0x02, 0xd2, - 0x63, 0x7e, 0x37, 0x01, 0x7f, 0x7e, 0x27, 0x01, 0x9f, 0x9d, 0x32, 0x40, 0x31, 0x7d, 0x02, 0x2e, - 0x05, 0x38, 0x7a, 0x05, 0x38, 0x7a, 0x37, 0x01, 0x7f, 0x7e, 0x37, 0x01, 0x5f, 0x7d, 0x43, 0x2d, - 0x42, 0xbe, 0x44, 0x19, 0xcc, 0x38, 0x68, 0x7a, 0x47, 0x01, 0x5f, 0x75, 0x2f, 0x94, 0x12, 0x7e, - 0x30, 0x7a, 0x51, 0x2f, 0x12, 0x7e, 0x30, 0x12, 0x68, 0x34, 0x10, 0x63, 0xc4, 0x22, 0xc2, 0x63, - 0x2d, 0x23, 0x68, 0x78, 0x6d, 0x33, 0x80, 0x1a, 0x7e, 0x27, 0x01, 0x7f, 0xbe, 0x24, 0x00, 0x00, - 0x68, 0x6a, 0xbe, 0x27, 0x01, 0x9f, 0x28, 0x04, 0x7e, 0x27, 0x01, 0x9f, 0x7e, 0x37, 0x01, 0x7f, - 0x9d, 0x32, 0x7d, 0x02, 0x2e, 0x05, 0x38, 0x7a, 0x05, 0x38, 0x7a, 0x37, 0x01, 0x7f, 0x7e, 0x37, - 0x01, 0x5f, 0x7d, 0x43, 0x2d, 0x42, 0xbe, 0x44, 0x19, 0xcc, 0x38, 0x13, 0x7a, 0x47, 0x01, 0x5f, - 0x75, 0x2f, 0x94, 0x12, 0x7e, 0x30, 0x7a, 0x51, 0x2f, 0x12, 0x7e, 0x30, 0x02, 0x68, 0x34, 0x75, - 0x2f, 0x94, 0x12, 0x7e, 0x30, 0x7a, 0x51, 0x2f, 0x12, 0x7e, 0x30, 0x9e, 0x44, 0x19, 0xcd, 0x9d, - 0x24, 0x12, 0x68, 0x34, 0x7e, 0x34, 0x15, 0xcd, 0x7d, 0x24, 0x2d, 0x43, 0x7a, 0x47, 0x01, 0x5f, - 0x12, 0x68, 0x34, 0xbe, 0x25, 0x20, 0x78, 0x03, 0x02, 0x54, 0xaa, 0x22, 0xd2, 0x0b, 0x7e, 0x04, - 0x15, 0xcd, 0x7a, 0x07, 0x01, 0x5f, 0x7a, 0x07, 0x01, 0x6f, 0x75, 0x2f, 0x94, 0x12, 0x7e, 0x30, - 0x75, 0x2f, 0x00, 0x12, 0x7e, 0x30, 0x22, 0x75, 0x2f, 0x92, 0x12, 0x7e, 0x30, 0xd2, 0x04, 0x09, - 0xb1, 0x00, 0x14, 0xca, 0xb8, 0x54, 0x82, 0xda, 0xb8, 0x78, 0x70, 0x7e, 0x37, 0x01, 0xcb, 0x7e, - 0x27, 0x01, 0xaf, 0x2e, 0x24, 0x00, 0x02, 0x2d, 0x32, 0xbe, 0x34, 0x04, 0x00, 0x38, 0x3c, 0x7d, - 0x02, 0x2e, 0x05, 0x30, 0x7a, 0x05, 0x30, 0x7a, 0x37, 0x01, 0xcb, 0x7e, 0x37, 0x01, 0xc9, 0x7d, - 0x43, 0x2d, 0x42, 0xbe, 0x44, 0x05, 0xcc, 0x38, 0x44, 0x7a, 0x47, 0x01, 0xc9, 0x7e, 0x24, 0x03, - 0x00, 0x2e, 0x27, 0x01, 0xaf, 0x1b, 0x38, 0x20, 0x0b, 0x35, 0x7a, 0x51, 0x2f, 0x12, 0x7e, 0x30, - 0xbe, 0x50, 0x38, 0x78, 0x03, 0x02, 0x69, 0x1f, 0x02, 0x69, 0x04, 0x75, 0x2f, 0x99, 0x12, 0x7e, - 0x30, 0x09, 0xb1, 0x00, 0x04, 0x54, 0xfa, 0x19, 0xb1, 0x00, 0x04, 0x30, 0x3b, 0x0a, 0x09, 0xb1, - 0x00, 0x10, 0x54, 0xfe, 0x19, 0xb1, 0x00, 0x10, 0xd2, 0x13, 0x22, 0x80, 0x7f, 0x7a, 0x51, 0x2f, - 0x12, 0x7e, 0x30, 0x9e, 0x44, 0x05, 0xcd, 0x9d, 0x24, 0x7e, 0x64, 0x03, 0x00, 0x2e, 0x67, 0x01, - 0xaf, 0x9e, 0x24, 0x00, 0x02, 0x40, 0x17, 0x1b, 0x38, 0x60, 0x0b, 0x35, 0x12, 0x69, 0x04, 0x7e, - 0x34, 0x01, 0xcd, 0x7d, 0x24, 0x2d, 0x43, 0x7a, 0x47, 0x01, 0xc9, 0x02, 0x69, 0x04, 0x7a, 0x39, - 0xc0, 0x7e, 0x34, 0x01, 0xcd, 0x7a, 0x39, 0xd0, 0x0b, 0x34, 0x1b, 0x44, 0x80, 0xe5, 0x9d, 0x32, - 0x7c, 0xb6, 0x54, 0x0f, 0x23, 0x23, 0x23, 0x44, 0x03, 0x7a, 0x69, 0xb0, 0x7a, 0x79, 0x70, 0x0b, - 0x35, 0x75, 0x2f, 0x93, 0x12, 0x7e, 0x30, 0x7a, 0x71, 0x2f, 0x12, 0x7e, 0x30, 0xbd, 0x04, 0x68, - 0x2b, 0x7a, 0x07, 0x01, 0xc9, 0x7e, 0x47, 0x01, 0xcb, 0x2d, 0x43, 0x7a, 0x47, 0x01, 0xcb, 0x2e, - 0x35, 0x30, 0x7a, 0x35, 0x30, 0x22, 0xd2, 0x04, 0x09, 0xb1, 0x00, 0x14, 0x20, 0xe0, 0x13, 0x22, - 0x7e, 0x04, 0x01, 0xcd, 0x80, 0x28, 0x7e, 0x04, 0x01, 0xcd, 0x80, 0x2a, 0x7e, 0x04, 0x01, 0xcd, - 0x80, 0xcf, 0x7e, 0x07, 0x01, 0xcb, 0x7e, 0x24, 0x03, 0xfe, 0x9d, 0x20, 0x28, 0x40, 0x7e, 0x07, - 0x01, 0xc9, 0x7e, 0x44, 0x05, 0xcd, 0x7d, 0x60, 0x0b, 0x04, 0xbd, 0x04, 0x68, 0xd2, 0x7d, 0x70, - 0x0b, 0x04, 0xbd, 0x04, 0x68, 0xd0, 0x7d, 0x54, 0x9d, 0x50, 0xbd, 0x25, 0x40, 0x02, 0x7d, 0x25, - 0x7d, 0x32, 0x09, 0xb1, 0x00, 0x14, 0xca, 0xb8, 0x54, 0x1f, 0xb4, 0x01, 0x31, 0xda, 0xb8, 0x7e, - 0x19, 0xb0, 0x7a, 0x09, 0xb0, 0x0b, 0x04, 0x1b, 0x24, 0x78, 0xe7, 0x02, 0x56, 0x0e, 0x75, 0x2f, - 0x99, 0x12, 0x7e, 0x30, 0x09, 0xb1, 0x00, 0x04, 0x54, 0xfa, 0x19, 0xb1, 0x00, 0x04, 0x30, 0x3b, - 0x0a, 0x09, 0xb1, 0x00, 0x10, 0x54, 0xfe, 0x19, 0xb1, 0x00, 0x10, 0xd2, 0x13, 0x22, 0xda, 0xb8, - 0x30, 0xe0, 0xd8, 0xbd, 0x32, 0x68, 0x07, 0xca, 0xb8, 0x12, 0x56, 0x0e, 0xda, 0xb8, 0x02, 0x57, - 0x35, 0x09, 0xb1, 0x00, 0x18, 0x7e, 0xa0, 0x88, 0x75, 0x2f, 0x90, 0x12, 0x7e, 0x30, 0xf5, 0x2f, - 0x12, 0x7e, 0x30, 0xa5, 0xfd, 0x5e, 0x50, 0x0a, 0x68, 0x1d, 0xa5, 0xfd, 0x5e, 0x50, 0x20, 0x68, - 0x04, 0xd2, 0x5b, 0x80, 0x02, 0xc2, 0x5b, 0xa5, 0xfd, 0x5e, 0x50, 0x80, 0x68, 0x04, 0xd2, 0x53, - 0x80, 0x02, 0xc2, 0x53, 0x12, 0x42, 0xf1, 0x02, 0x65, 0x9c, 0x75, 0x2f, 0x91, 0x12, 0x7e, 0x30, - 0x09, 0xb1, 0x00, 0x14, 0x7a, 0xb1, 0x2f, 0x12, 0x7e, 0x30, 0x20, 0xe0, 0x08, 0xd2, 0x04, 0x7e, - 0xa0, 0x80, 0x02, 0x65, 0x9c, 0xd2, 0x04, 0x30, 0xe1, 0x06, 0x7e, 0xa0, 0x80, 0x12, 0x65, 0x9c, - 0xca, 0xb8, 0x5e, 0xb0, 0x1c, 0xda, 0xb8, 0x68, 0x12, 0x7e, 0xa0, 0xc0, 0x09, 0x61, 0x00, 0x00, - 0x12, 0x65, 0xbf, 0x09, 0xb1, 0x00, 0x14, 0x20, 0xe0, 0xdb, 0x22, 0x02, 0x56, 0x62, 0x75, 0x2f, - 0x95, 0x12, 0x7e, 0x30, 0x22, 0x75, 0x2f, 0x96, 0x12, 0x7e, 0x30, 0x22, 0x10, 0x0c, 0x01, 0x22, - 0x20, 0x2c, 0x03, 0xd2, 0x0c, 0x22, 0x75, 0x2f, 0xa4, 0x12, 0x7e, 0x30, 0x7e, 0x14, 0x84, 0x00, - 0x80, 0x06, 0x20, 0x2c, 0x03, 0xd2, 0x0c, 0x22, 0x09, 0xb1, 0x00, 0x14, 0xca, 0xb8, 0x5e, 0xb0, - 0x1e, 0xda, 0xb8, 0x68, 0x03, 0x12, 0x5a, 0x6a, 0x30, 0x34, 0x06, 0x20, 0xe6, 0x4f, 0xd2, 0x0c, - 0x22, 0x30, 0xe6, 0x02, 0xd2, 0x64, 0x7e, 0x37, 0x01, 0x81, 0x7e, 0x27, 0x01, 0xa1, 0x9d, 0x32, - 0x40, 0x31, 0x7d, 0x02, 0x2e, 0x05, 0x3a, 0x7a, 0x05, 0x3a, 0x7a, 0x37, 0x01, 0x81, 0x7e, 0x37, - 0x01, 0x61, 0x7d, 0x43, 0x2d, 0x42, 0xbe, 0x44, 0x1d, 0xcc, 0x38, 0x68, 0x7a, 0x47, 0x01, 0x61, - 0x75, 0x2f, 0x94, 0x12, 0x7e, 0x30, 0x7a, 0x51, 0x2f, 0x12, 0x7e, 0x30, 0x12, 0x68, 0x34, 0x10, - 0x64, 0xc4, 0x22, 0xc2, 0x64, 0x2d, 0x23, 0x68, 0x78, 0x6d, 0x33, 0x80, 0x1a, 0x7e, 0x27, 0x01, - 0x81, 0xbe, 0x24, 0x00, 0x00, 0x68, 0x6a, 0xbe, 0x27, 0x01, 0xa1, 0x28, 0x04, 0x7e, 0x27, 0x01, - 0xa1, 0x7e, 0x37, 0x01, 0x81, 0x9d, 0x32, 0x7d, 0x02, 0x2e, 0x05, 0x3a, 0x7a, 0x05, 0x3a, 0x7a, - 0x37, 0x01, 0x81, 0x7e, 0x37, 0x01, 0x61, 0x7d, 0x43, 0x2d, 0x42, 0xbe, 0x44, 0x1d, 0xcc, 0x38, - 0x13, 0x7a, 0x47, 0x01, 0x61, 0x75, 0x2f, 0x94, 0x12, 0x7e, 0x30, 0x7a, 0x51, 0x2f, 0x12, 0x7e, - 0x30, 0x02, 0x68, 0x34, 0x75, 0x2f, 0x94, 0x12, 0x7e, 0x30, 0x7a, 0x51, 0x2f, 0x12, 0x7e, 0x30, - 0x9e, 0x44, 0x1d, 0xcd, 0x9d, 0x24, 0x12, 0x68, 0x34, 0x7e, 0x34, 0x19, 0xcd, 0x7d, 0x24, 0x2d, - 0x43, 0x7a, 0x47, 0x01, 0x61, 0x12, 0x68, 0x34, 0xbe, 0x25, 0x20, 0x78, 0x03, 0x02, 0x57, 0xdf, - 0x22, 0xd2, 0x0c, 0x7e, 0x04, 0x19, 0xcd, 0x7a, 0x07, 0x01, 0x61, 0x7a, 0x07, 0x01, 0x71, 0x75, - 0x2f, 0x94, 0x12, 0x7e, 0x30, 0x75, 0x2f, 0x00, 0x12, 0x7e, 0x30, 0x22, 0x75, 0x2f, 0x92, 0x12, - 0x7e, 0x30, 0xd2, 0x04, 0x09, 0xb1, 0x00, 0x14, 0xca, 0xb8, 0x54, 0x82, 0xda, 0xb8, 0x78, 0x70, - 0x7e, 0x37, 0x01, 0xcb, 0x7e, 0x27, 0x01, 0xb1, 0x2e, 0x24, 0x00, 0x02, 0x2d, 0x32, 0xbe, 0x34, - 0x04, 0x00, 0x38, 0x3c, 0x7d, 0x02, 0x2e, 0x05, 0x30, 0x7a, 0x05, 0x30, 0x7a, 0x37, 0x01, 0xcb, - 0x7e, 0x37, 0x01, 0xc9, 0x7d, 0x43, 0x2d, 0x42, 0xbe, 0x44, 0x05, 0xcc, 0x38, 0x44, 0x7a, 0x47, - 0x01, 0xc9, 0x7e, 0x24, 0x04, 0x00, 0x2e, 0x27, 0x01, 0xb1, 0x1b, 0x38, 0x20, 0x0b, 0x35, 0x7a, - 0x51, 0x2f, 0x12, 0x7e, 0x30, 0xbe, 0x50, 0x38, 0x78, 0x03, 0x02, 0x69, 0x1f, 0x02, 0x69, 0x04, - 0x75, 0x2f, 0x99, 0x12, 0x7e, 0x30, 0x09, 0xb1, 0x00, 0x04, 0x54, 0xfa, 0x19, 0xb1, 0x00, 0x04, + 0x12, 0x53, 0x38, 0x20, 0xe6, 0x03, 0xd2, 0x0b, 0x22, 0x30, 0x33, 0x49, 0xd2, 0x73, 0x7e, 0x37, + 0x01, 0x95, 0x7e, 0x27, 0x01, 0xb5, 0x9d, 0x32, 0x40, 0x31, 0x7d, 0x02, 0x2e, 0x05, 0x4e, 0x7a, + 0x05, 0x4e, 0x7a, 0x37, 0x01, 0x95, 0x7e, 0x37, 0x01, 0x75, 0x7d, 0x43, 0x2d, 0x42, 0xbe, 0x44, + 0x19, 0xe2, 0x38, 0x68, 0x7a, 0x47, 0x01, 0x75, 0x75, 0x31, 0x94, 0x12, 0x7c, 0x15, 0x7a, 0x51, + 0x31, 0x12, 0x7c, 0x15, 0x12, 0x65, 0x2b, 0x10, 0x73, 0xc4, 0x22, 0xc2, 0x73, 0x2d, 0x23, 0x68, + 0x78, 0x6d, 0x33, 0x80, 0x1a, 0x7e, 0x27, 0x01, 0x95, 0xbe, 0x24, 0x00, 0x00, 0x68, 0x6a, 0xbe, + 0x27, 0x01, 0xb5, 0x28, 0x04, 0x7e, 0x27, 0x01, 0xb5, 0x7e, 0x37, 0x01, 0x95, 0x9d, 0x32, 0x7d, + 0x02, 0x2e, 0x05, 0x4e, 0x7a, 0x05, 0x4e, 0x7a, 0x37, 0x01, 0x95, 0x7e, 0x37, 0x01, 0x75, 0x7d, + 0x43, 0x2d, 0x42, 0xbe, 0x44, 0x19, 0xe2, 0x38, 0x13, 0x7a, 0x47, 0x01, 0x75, 0x75, 0x31, 0x94, + 0x12, 0x7c, 0x15, 0x7a, 0x51, 0x31, 0x12, 0x7c, 0x15, 0x02, 0x65, 0x2b, 0x75, 0x31, 0x94, 0x12, + 0x7c, 0x15, 0x7a, 0x51, 0x31, 0x12, 0x7c, 0x15, 0x9e, 0x44, 0x19, 0xe3, 0x9d, 0x24, 0x12, 0x65, + 0x2b, 0x7e, 0x34, 0x15, 0xe3, 0x7d, 0x24, 0x2d, 0x43, 0x7a, 0x47, 0x01, 0x75, 0x12, 0x65, 0x2b, + 0xbe, 0x25, 0x20, 0x78, 0x03, 0x02, 0x50, 0x87, 0x22, 0xd2, 0x0b, 0x7e, 0x04, 0x15, 0xe3, 0x7a, + 0x07, 0x01, 0x75, 0x7a, 0x07, 0x01, 0x85, 0x75, 0x31, 0x94, 0x12, 0x7c, 0x15, 0x75, 0x31, 0x00, + 0x12, 0x7c, 0x15, 0x22, 0x75, 0x31, 0x92, 0x12, 0x7c, 0x15, 0xd2, 0x04, 0x09, 0xb1, 0x00, 0x14, + 0xca, 0xb8, 0x54, 0x82, 0xda, 0xb8, 0x78, 0x70, 0x7e, 0x37, 0x01, 0xe1, 0x7e, 0x27, 0x01, 0xc5, + 0x2e, 0x24, 0x00, 0x02, 0x2d, 0x32, 0xbe, 0x34, 0x04, 0x00, 0x38, 0x3c, 0x7d, 0x02, 0x2e, 0x05, + 0x46, 0x7a, 0x05, 0x46, 0x7a, 0x37, 0x01, 0xe1, 0x7e, 0x37, 0x01, 0xdf, 0x7d, 0x43, 0x2d, 0x42, + 0xbe, 0x44, 0x05, 0xe2, 0x38, 0x44, 0x7a, 0x47, 0x01, 0xdf, 0x7e, 0x24, 0x03, 0x00, 0x2e, 0x27, + 0x01, 0xc5, 0x1b, 0x38, 0x20, 0x0b, 0x35, 0x7a, 0x51, 0x31, 0x12, 0x7c, 0x15, 0xbe, 0x50, 0x38, + 0x78, 0x03, 0x02, 0x66, 0x16, 0x02, 0x65, 0xfb, 0x75, 0x31, 0x99, 0x12, 0x7c, 0x15, 0x09, 0xb1, + 0x00, 0x04, 0x54, 0xfa, 0x19, 0xb1, 0x00, 0x04, 0x30, 0x3b, 0x0a, 0x09, 0xb1, 0x00, 0x10, 0x54, + 0xfe, 0x19, 0xb1, 0x00, 0x10, 0xd2, 0x13, 0x22, 0x80, 0x7d, 0x7a, 0x51, 0x31, 0x12, 0x7c, 0x15, + 0x9e, 0x44, 0x05, 0xe3, 0x9d, 0x24, 0x7e, 0x64, 0x03, 0x00, 0x2e, 0x67, 0x01, 0xc5, 0x9e, 0x24, + 0x00, 0x02, 0x40, 0x17, 0x1b, 0x38, 0x60, 0x0b, 0x35, 0x12, 0x65, 0xfb, 0x7e, 0x34, 0x01, 0xe3, + 0x7d, 0x24, 0x2d, 0x43, 0x7a, 0x47, 0x01, 0xdf, 0x02, 0x65, 0xfb, 0x7a, 0x39, 0xc0, 0x7e, 0x34, + 0x01, 0xe3, 0x7a, 0x39, 0xd0, 0x0b, 0x34, 0x1b, 0x44, 0x80, 0xe5, 0x9d, 0x32, 0x7c, 0xb6, 0x54, + 0x0f, 0x23, 0x23, 0x23, 0x44, 0x03, 0x7a, 0x69, 0xb0, 0x7a, 0x79, 0x70, 0x0b, 0x35, 0x75, 0x31, + 0x93, 0x12, 0x7c, 0x15, 0x7a, 0x71, 0x31, 0x12, 0x7c, 0x15, 0xbd, 0x04, 0x68, 0x29, 0x7a, 0x07, + 0x01, 0xdf, 0x7e, 0x47, 0x01, 0xe1, 0x2d, 0x43, 0x7a, 0x47, 0x01, 0xe1, 0x2e, 0x35, 0x46, 0x7a, + 0x35, 0x46, 0x22, 0x09, 0xb1, 0x00, 0x14, 0x20, 0xe0, 0x13, 0x22, 0x7e, 0x04, 0x01, 0xe3, 0x80, + 0x2a, 0x7e, 0x04, 0x01, 0xe3, 0x80, 0x2c, 0x7e, 0x04, 0x01, 0xe3, 0x80, 0xd1, 0xd2, 0x04, 0x7e, + 0x07, 0x01, 0xe1, 0x7e, 0x24, 0x03, 0xfe, 0x9d, 0x20, 0x28, 0x40, 0x7e, 0x07, 0x01, 0xdf, 0x7e, + 0x44, 0x05, 0xe3, 0x7d, 0x60, 0x0b, 0x04, 0xbd, 0x04, 0x68, 0xd0, 0x7d, 0x70, 0x0b, 0x04, 0xbd, + 0x04, 0x68, 0xce, 0x7d, 0x54, 0x9d, 0x50, 0xbd, 0x25, 0x40, 0x02, 0x7d, 0x25, 0x7d, 0x32, 0x09, + 0xb1, 0x00, 0x14, 0xca, 0xb8, 0x54, 0x1f, 0xb4, 0x01, 0x31, 0xda, 0xb8, 0x7e, 0x19, 0xb0, 0x7a, + 0x09, 0xb0, 0x0b, 0x04, 0x1b, 0x24, 0x78, 0xe7, 0x02, 0x51, 0xeb, 0x75, 0x31, 0x99, 0x12, 0x7c, + 0x15, 0x09, 0xb1, 0x00, 0x04, 0x54, 0xfa, 0x19, 0xb1, 0x00, 0x04, 0x30, 0x3b, 0x0a, 0x09, 0xb1, + 0x00, 0x10, 0x54, 0xfe, 0x19, 0xb1, 0x00, 0x10, 0xd2, 0x13, 0x22, 0xda, 0xb8, 0x30, 0xe0, 0xd8, + 0xbd, 0x32, 0x68, 0x07, 0xca, 0xb8, 0x12, 0x51, 0xeb, 0xda, 0xb8, 0x02, 0x53, 0x38, 0x09, 0xb1, + 0x00, 0x18, 0x7e, 0xa0, 0x88, 0x75, 0x31, 0x90, 0x12, 0x7c, 0x15, 0xf5, 0x31, 0x12, 0x7c, 0x15, + 0xa5, 0xfc, 0x5e, 0xb0, 0xf0, 0xa5, 0xfd, 0x09, 0xb1, 0x00, 0x18, 0x4c, 0x4b, 0x5e, 0xb0, 0xf0, + 0xbc, 0xb5, 0x78, 0xf1, 0x5e, 0x40, 0x0f, 0x4c, 0x54, 0x7c, 0xb5, 0x5e, 0x50, 0x0b, 0x68, 0x2a, + 0xa5, 0xfd, 0x5e, 0x50, 0x10, 0x68, 0x04, 0xd2, 0x6b, 0x80, 0x02, 0xc2, 0x6b, 0xa5, 0xfd, 0x5e, + 0x50, 0x20, 0x68, 0x04, 0xd2, 0x63, 0x80, 0x02, 0xc2, 0x63, 0xa5, 0xfd, 0x5e, 0x50, 0x80, 0x68, + 0x04, 0xd2, 0x5b, 0x80, 0x02, 0xc2, 0x5b, 0x12, 0x43, 0x2b, 0x02, 0x62, 0x93, 0x75, 0x31, 0x91, + 0x12, 0x7c, 0x15, 0x09, 0xb1, 0x00, 0x14, 0x7a, 0xb1, 0x31, 0x12, 0x7c, 0x15, 0x20, 0xe0, 0x08, + 0xd2, 0x04, 0x7e, 0xa0, 0x80, 0x02, 0x62, 0x93, 0xd2, 0x04, 0x30, 0xe1, 0x06, 0x7e, 0xa0, 0x80, + 0x12, 0x62, 0x93, 0xca, 0xb8, 0x5e, 0xb0, 0x1c, 0xda, 0xb8, 0x68, 0x12, 0x7e, 0xa0, 0xc0, 0x09, + 0x61, 0x00, 0x00, 0x12, 0x62, 0xb6, 0x09, 0xb1, 0x00, 0x14, 0x20, 0xe0, 0xdb, 0x22, 0x02, 0x52, + 0x3d, 0x75, 0x31, 0x95, 0x12, 0x7c, 0x15, 0x22, 0x75, 0x31, 0x96, 0x12, 0x7c, 0x15, 0x22, 0x10, + 0x0c, 0x01, 0x22, 0x20, 0x2c, 0x03, 0xd2, 0x0c, 0x22, 0x75, 0x31, 0xa4, 0x12, 0x7c, 0x15, 0x7e, + 0x14, 0x84, 0x00, 0x80, 0x06, 0x20, 0x2c, 0x03, 0xd2, 0x0c, 0x22, 0x09, 0xb1, 0x00, 0x14, 0xca, + 0xb8, 0x5e, 0xb0, 0x1e, 0xda, 0xb8, 0x68, 0x03, 0x12, 0x56, 0x90, 0x20, 0xe6, 0x03, 0xd2, 0x0c, + 0x22, 0x30, 0x34, 0x49, 0xd2, 0x74, 0x7e, 0x37, 0x01, 0x97, 0x7e, 0x27, 0x01, 0xb7, 0x9d, 0x32, + 0x40, 0x31, 0x7d, 0x02, 0x2e, 0x05, 0x50, 0x7a, 0x05, 0x50, 0x7a, 0x37, 0x01, 0x97, 0x7e, 0x37, + 0x01, 0x77, 0x7d, 0x43, 0x2d, 0x42, 0xbe, 0x44, 0x1d, 0xe2, 0x38, 0x68, 0x7a, 0x47, 0x01, 0x77, + 0x75, 0x31, 0x94, 0x12, 0x7c, 0x15, 0x7a, 0x51, 0x31, 0x12, 0x7c, 0x15, 0x12, 0x65, 0x2b, 0x10, + 0x74, 0xc4, 0x22, 0xc2, 0x74, 0x2d, 0x23, 0x68, 0x78, 0x6d, 0x33, 0x80, 0x1a, 0x7e, 0x27, 0x01, + 0x97, 0xbe, 0x24, 0x00, 0x00, 0x68, 0x6a, 0xbe, 0x27, 0x01, 0xb7, 0x28, 0x04, 0x7e, 0x27, 0x01, + 0xb7, 0x7e, 0x37, 0x01, 0x97, 0x9d, 0x32, 0x7d, 0x02, 0x2e, 0x05, 0x50, 0x7a, 0x05, 0x50, 0x7a, + 0x37, 0x01, 0x97, 0x7e, 0x37, 0x01, 0x77, 0x7d, 0x43, 0x2d, 0x42, 0xbe, 0x44, 0x1d, 0xe2, 0x38, + 0x13, 0x7a, 0x47, 0x01, 0x77, 0x75, 0x31, 0x94, 0x12, 0x7c, 0x15, 0x7a, 0x51, 0x31, 0x12, 0x7c, + 0x15, 0x02, 0x65, 0x2b, 0x75, 0x31, 0x94, 0x12, 0x7c, 0x15, 0x7a, 0x51, 0x31, 0x12, 0x7c, 0x15, + 0x9e, 0x44, 0x1d, 0xe3, 0x9d, 0x24, 0x12, 0x65, 0x2b, 0x7e, 0x34, 0x19, 0xe3, 0x7d, 0x24, 0x2d, + 0x43, 0x7a, 0x47, 0x01, 0x77, 0x12, 0x65, 0x2b, 0xbe, 0x25, 0x20, 0x78, 0x03, 0x02, 0x53, 0xdf, + 0x22, 0xd2, 0x0c, 0x7e, 0x04, 0x19, 0xe3, 0x7a, 0x07, 0x01, 0x77, 0x7a, 0x07, 0x01, 0x87, 0x75, + 0x31, 0x94, 0x12, 0x7c, 0x15, 0x75, 0x31, 0x00, 0x12, 0x7c, 0x15, 0x22, 0x75, 0x31, 0x92, 0x12, + 0x7c, 0x15, 0xd2, 0x04, 0x09, 0xb1, 0x00, 0x14, 0xca, 0xb8, 0x54, 0x82, 0xda, 0xb8, 0x78, 0x70, + 0x7e, 0x37, 0x01, 0xe1, 0x7e, 0x27, 0x01, 0xc7, 0x2e, 0x24, 0x00, 0x02, 0x2d, 0x32, 0xbe, 0x34, + 0x04, 0x00, 0x38, 0x3c, 0x7d, 0x02, 0x2e, 0x05, 0x46, 0x7a, 0x05, 0x46, 0x7a, 0x37, 0x01, 0xe1, + 0x7e, 0x37, 0x01, 0xdf, 0x7d, 0x43, 0x2d, 0x42, 0xbe, 0x44, 0x05, 0xe2, 0x38, 0x44, 0x7a, 0x47, + 0x01, 0xdf, 0x7e, 0x24, 0x04, 0x00, 0x2e, 0x27, 0x01, 0xc7, 0x1b, 0x38, 0x20, 0x0b, 0x35, 0x7a, + 0x51, 0x31, 0x12, 0x7c, 0x15, 0xbe, 0x50, 0x38, 0x78, 0x03, 0x02, 0x66, 0x16, 0x02, 0x65, 0xfb, + 0x75, 0x31, 0x99, 0x12, 0x7c, 0x15, 0x09, 0xb1, 0x00, 0x04, 0x54, 0xfa, 0x19, 0xb1, 0x00, 0x04, 0x30, 0x3c, 0x0a, 0x09, 0xb1, 0x00, 0x10, 0x54, 0xfe, 0x19, 0xb1, 0x00, 0x10, 0xd2, 0x14, 0x22, - 0x80, 0x7f, 0x7a, 0x51, 0x2f, 0x12, 0x7e, 0x30, 0x9e, 0x44, 0x05, 0xcd, 0x9d, 0x24, 0x7e, 0x64, - 0x04, 0x00, 0x2e, 0x67, 0x01, 0xb1, 0x9e, 0x24, 0x00, 0x02, 0x40, 0x17, 0x1b, 0x38, 0x60, 0x0b, - 0x35, 0x12, 0x69, 0x04, 0x7e, 0x34, 0x01, 0xcd, 0x7d, 0x24, 0x2d, 0x43, 0x7a, 0x47, 0x01, 0xc9, - 0x02, 0x69, 0x04, 0x7a, 0x39, 0xc0, 0x7e, 0x34, 0x01, 0xcd, 0x7a, 0x39, 0xd0, 0x0b, 0x34, 0x1b, + 0x80, 0x7d, 0x7a, 0x51, 0x31, 0x12, 0x7c, 0x15, 0x9e, 0x44, 0x05, 0xe3, 0x9d, 0x24, 0x7e, 0x64, + 0x04, 0x00, 0x2e, 0x67, 0x01, 0xc7, 0x9e, 0x24, 0x00, 0x02, 0x40, 0x17, 0x1b, 0x38, 0x60, 0x0b, + 0x35, 0x12, 0x65, 0xfb, 0x7e, 0x34, 0x01, 0xe3, 0x7d, 0x24, 0x2d, 0x43, 0x7a, 0x47, 0x01, 0xdf, + 0x02, 0x65, 0xfb, 0x7a, 0x39, 0xc0, 0x7e, 0x34, 0x01, 0xe3, 0x7a, 0x39, 0xd0, 0x0b, 0x34, 0x1b, 0x44, 0x80, 0xe5, 0x9d, 0x32, 0x7c, 0xb6, 0x54, 0x0f, 0x23, 0x23, 0x23, 0x44, 0x04, 0x7a, 0x69, - 0xb0, 0x7a, 0x79, 0x70, 0x0b, 0x35, 0x75, 0x2f, 0x93, 0x12, 0x7e, 0x30, 0x7a, 0x71, 0x2f, 0x12, - 0x7e, 0x30, 0xbd, 0x04, 0x68, 0x2b, 0x7a, 0x07, 0x01, 0xc9, 0x7e, 0x47, 0x01, 0xcb, 0x2d, 0x43, - 0x7a, 0x47, 0x01, 0xcb, 0x2e, 0x35, 0x30, 0x7a, 0x35, 0x30, 0x22, 0xd2, 0x04, 0x09, 0xb1, 0x00, - 0x14, 0x20, 0xe0, 0x13, 0x22, 0x7e, 0x04, 0x01, 0xcd, 0x80, 0x28, 0x7e, 0x04, 0x01, 0xcd, 0x80, - 0x2a, 0x7e, 0x04, 0x01, 0xcd, 0x80, 0xcf, 0x7e, 0x07, 0x01, 0xcb, 0x7e, 0x24, 0x03, 0xfe, 0x9d, - 0x20, 0x28, 0x40, 0x7e, 0x07, 0x01, 0xc9, 0x7e, 0x44, 0x05, 0xcd, 0x7d, 0x60, 0x0b, 0x04, 0xbd, - 0x04, 0x68, 0xd2, 0x7d, 0x70, 0x0b, 0x04, 0xbd, 0x04, 0x68, 0xd0, 0x7d, 0x54, 0x9d, 0x50, 0xbd, + 0xb0, 0x7a, 0x79, 0x70, 0x0b, 0x35, 0x75, 0x31, 0x93, 0x12, 0x7c, 0x15, 0x7a, 0x71, 0x31, 0x12, + 0x7c, 0x15, 0xbd, 0x04, 0x68, 0x29, 0x7a, 0x07, 0x01, 0xdf, 0x7e, 0x47, 0x01, 0xe1, 0x2d, 0x43, + 0x7a, 0x47, 0x01, 0xe1, 0x2e, 0x35, 0x46, 0x7a, 0x35, 0x46, 0x22, 0x09, 0xb1, 0x00, 0x14, 0x20, + 0xe0, 0x13, 0x22, 0x7e, 0x04, 0x01, 0xe3, 0x80, 0x2a, 0x7e, 0x04, 0x01, 0xe3, 0x80, 0x2c, 0x7e, + 0x04, 0x01, 0xe3, 0x80, 0xd1, 0xd2, 0x04, 0x7e, 0x07, 0x01, 0xe1, 0x7e, 0x24, 0x03, 0xfe, 0x9d, + 0x20, 0x28, 0x40, 0x7e, 0x07, 0x01, 0xdf, 0x7e, 0x44, 0x05, 0xe3, 0x7d, 0x60, 0x0b, 0x04, 0xbd, + 0x04, 0x68, 0xd0, 0x7d, 0x70, 0x0b, 0x04, 0xbd, 0x04, 0x68, 0xce, 0x7d, 0x54, 0x9d, 0x50, 0xbd, 0x25, 0x40, 0x02, 0x7d, 0x25, 0x7d, 0x32, 0x09, 0xb1, 0x00, 0x14, 0xca, 0xb8, 0x54, 0x1f, 0xb4, 0x01, 0x31, 0xda, 0xb8, 0x7e, 0x19, 0xb0, 0x7a, 0x09, 0xb0, 0x0b, 0x04, 0x1b, 0x24, 0x78, 0xe7, - 0x02, 0x59, 0x43, 0x75, 0x2f, 0x99, 0x12, 0x7e, 0x30, 0x09, 0xb1, 0x00, 0x04, 0x54, 0xfa, 0x19, + 0x02, 0x55, 0x43, 0x75, 0x31, 0x99, 0x12, 0x7c, 0x15, 0x09, 0xb1, 0x00, 0x04, 0x54, 0xfa, 0x19, 0xb1, 0x00, 0x04, 0x30, 0x3c, 0x0a, 0x09, 0xb1, 0x00, 0x10, 0x54, 0xfe, 0x19, 0xb1, 0x00, 0x10, - 0xd2, 0x14, 0x22, 0xda, 0xb8, 0x30, 0xe0, 0xd8, 0xbd, 0x32, 0x68, 0x07, 0xca, 0xb8, 0x12, 0x59, - 0x43, 0xda, 0xb8, 0x02, 0x5a, 0x6a, 0x09, 0xb1, 0x00, 0x18, 0x7e, 0xa0, 0x88, 0x75, 0x2f, 0x90, - 0x12, 0x7e, 0x30, 0xf5, 0x2f, 0x12, 0x7e, 0x30, 0xa5, 0xfd, 0x5e, 0x50, 0x0a, 0x68, 0x1d, 0xa5, - 0xfd, 0x5e, 0x50, 0x20, 0x68, 0x04, 0xd2, 0x5c, 0x80, 0x02, 0xc2, 0x5c, 0xa5, 0xfd, 0x5e, 0x50, - 0x80, 0x68, 0x04, 0xd2, 0x54, 0x80, 0x02, 0xc2, 0x54, 0x12, 0x43, 0x08, 0x02, 0x65, 0x9c, 0x75, - 0x2f, 0x91, 0x12, 0x7e, 0x30, 0x09, 0xb1, 0x00, 0x14, 0x7a, 0xb1, 0x2f, 0x12, 0x7e, 0x30, 0x20, - 0xe0, 0x08, 0xd2, 0x04, 0x7e, 0xa0, 0x80, 0x02, 0x65, 0x9c, 0xd2, 0x04, 0x30, 0xe1, 0x06, 0x7e, - 0xa0, 0x80, 0x12, 0x65, 0x9c, 0xca, 0xb8, 0x5e, 0xb0, 0x1c, 0xda, 0xb8, 0x68, 0x12, 0x7e, 0xa0, - 0xc0, 0x09, 0x61, 0x00, 0x00, 0x12, 0x65, 0xbf, 0x09, 0xb1, 0x00, 0x14, 0x20, 0xe0, 0xdb, 0x22, - 0x02, 0x59, 0x97, 0x75, 0x2f, 0x95, 0x12, 0x7e, 0x30, 0x22, 0x75, 0x2f, 0x96, 0x12, 0x7e, 0x30, - 0x22, 0x10, 0x0d, 0x01, 0x22, 0x20, 0x2d, 0x03, 0xd2, 0x0d, 0x22, 0x75, 0x2f, 0xa5, 0x12, 0x7e, - 0x30, 0x7e, 0x14, 0x85, 0x00, 0x80, 0x06, 0x20, 0x2d, 0x03, 0xd2, 0x0d, 0x22, 0x09, 0xb1, 0x00, - 0x14, 0xca, 0xb8, 0x5e, 0xb0, 0x1e, 0xda, 0xb8, 0x68, 0x03, 0x12, 0x5d, 0x9f, 0x30, 0x35, 0x06, - 0x20, 0xe6, 0x4f, 0xd2, 0x0d, 0x22, 0x30, 0xe6, 0x02, 0xd2, 0x65, 0x7e, 0x37, 0x01, 0x83, 0x7e, - 0x27, 0x01, 0xa3, 0x9d, 0x32, 0x40, 0x31, 0x7d, 0x02, 0x2e, 0x05, 0x3c, 0x7a, 0x05, 0x3c, 0x7a, - 0x37, 0x01, 0x83, 0x7e, 0x37, 0x01, 0x63, 0x7d, 0x43, 0x2d, 0x42, 0xbe, 0x44, 0x21, 0xcc, 0x38, - 0x68, 0x7a, 0x47, 0x01, 0x63, 0x75, 0x2f, 0x94, 0x12, 0x7e, 0x30, 0x7a, 0x51, 0x2f, 0x12, 0x7e, - 0x30, 0x12, 0x68, 0x34, 0x10, 0x65, 0xc4, 0x22, 0xc2, 0x65, 0x2d, 0x23, 0x68, 0x78, 0x6d, 0x33, - 0x80, 0x1a, 0x7e, 0x27, 0x01, 0x83, 0xbe, 0x24, 0x00, 0x00, 0x68, 0x6a, 0xbe, 0x27, 0x01, 0xa3, - 0x28, 0x04, 0x7e, 0x27, 0x01, 0xa3, 0x7e, 0x37, 0x01, 0x83, 0x9d, 0x32, 0x7d, 0x02, 0x2e, 0x05, - 0x3c, 0x7a, 0x05, 0x3c, 0x7a, 0x37, 0x01, 0x83, 0x7e, 0x37, 0x01, 0x63, 0x7d, 0x43, 0x2d, 0x42, - 0xbe, 0x44, 0x21, 0xcc, 0x38, 0x13, 0x7a, 0x47, 0x01, 0x63, 0x75, 0x2f, 0x94, 0x12, 0x7e, 0x30, - 0x7a, 0x51, 0x2f, 0x12, 0x7e, 0x30, 0x02, 0x68, 0x34, 0x75, 0x2f, 0x94, 0x12, 0x7e, 0x30, 0x7a, - 0x51, 0x2f, 0x12, 0x7e, 0x30, 0x9e, 0x44, 0x21, 0xcd, 0x9d, 0x24, 0x12, 0x68, 0x34, 0x7e, 0x34, - 0x1d, 0xcd, 0x7d, 0x24, 0x2d, 0x43, 0x7a, 0x47, 0x01, 0x63, 0x12, 0x68, 0x34, 0xbe, 0x25, 0x20, - 0x78, 0x03, 0x02, 0x5b, 0x14, 0x22, 0xd2, 0x0d, 0x7e, 0x04, 0x1d, 0xcd, 0x7a, 0x07, 0x01, 0x63, - 0x7a, 0x07, 0x01, 0x73, 0x75, 0x2f, 0x94, 0x12, 0x7e, 0x30, 0x75, 0x2f, 0x00, 0x12, 0x7e, 0x30, - 0x22, 0x75, 0x2f, 0x92, 0x12, 0x7e, 0x30, 0xd2, 0x04, 0x09, 0xb1, 0x00, 0x14, 0xca, 0xb8, 0x54, - 0x82, 0xda, 0xb8, 0x78, 0x70, 0x7e, 0x37, 0x01, 0xcb, 0x7e, 0x27, 0x01, 0xb3, 0x2e, 0x24, 0x00, - 0x02, 0x2d, 0x32, 0xbe, 0x34, 0x04, 0x00, 0x38, 0x3c, 0x7d, 0x02, 0x2e, 0x05, 0x30, 0x7a, 0x05, - 0x30, 0x7a, 0x37, 0x01, 0xcb, 0x7e, 0x37, 0x01, 0xc9, 0x7d, 0x43, 0x2d, 0x42, 0xbe, 0x44, 0x05, - 0xcc, 0x38, 0x44, 0x7a, 0x47, 0x01, 0xc9, 0x7e, 0x24, 0x05, 0x00, 0x2e, 0x27, 0x01, 0xb3, 0x1b, - 0x38, 0x20, 0x0b, 0x35, 0x7a, 0x51, 0x2f, 0x12, 0x7e, 0x30, 0xbe, 0x50, 0x38, 0x78, 0x03, 0x02, - 0x69, 0x1f, 0x02, 0x69, 0x04, 0x75, 0x2f, 0x99, 0x12, 0x7e, 0x30, 0x09, 0xb1, 0x00, 0x04, 0x54, - 0xfa, 0x19, 0xb1, 0x00, 0x04, 0x30, 0x3d, 0x0a, 0x09, 0xb1, 0x00, 0x10, 0x54, 0xfe, 0x19, 0xb1, - 0x00, 0x10, 0xd2, 0x15, 0x22, 0x80, 0x7f, 0x7a, 0x51, 0x2f, 0x12, 0x7e, 0x30, 0x9e, 0x44, 0x05, - 0xcd, 0x9d, 0x24, 0x7e, 0x64, 0x05, 0x00, 0x2e, 0x67, 0x01, 0xb3, 0x9e, 0x24, 0x00, 0x02, 0x40, - 0x17, 0x1b, 0x38, 0x60, 0x0b, 0x35, 0x12, 0x69, 0x04, 0x7e, 0x34, 0x01, 0xcd, 0x7d, 0x24, 0x2d, - 0x43, 0x7a, 0x47, 0x01, 0xc9, 0x02, 0x69, 0x04, 0x7a, 0x39, 0xc0, 0x7e, 0x34, 0x01, 0xcd, 0x7a, - 0x39, 0xd0, 0x0b, 0x34, 0x1b, 0x44, 0x80, 0xe5, 0x9d, 0x32, 0x7c, 0xb6, 0x54, 0x0f, 0x23, 0x23, - 0x23, 0x44, 0x05, 0x7a, 0x69, 0xb0, 0x7a, 0x79, 0x70, 0x0b, 0x35, 0x75, 0x2f, 0x93, 0x12, 0x7e, - 0x30, 0x7a, 0x71, 0x2f, 0x12, 0x7e, 0x30, 0xbd, 0x04, 0x68, 0x2b, 0x7a, 0x07, 0x01, 0xc9, 0x7e, - 0x47, 0x01, 0xcb, 0x2d, 0x43, 0x7a, 0x47, 0x01, 0xcb, 0x2e, 0x35, 0x30, 0x7a, 0x35, 0x30, 0x22, - 0xd2, 0x04, 0x09, 0xb1, 0x00, 0x14, 0x20, 0xe0, 0x13, 0x22, 0x7e, 0x04, 0x01, 0xcd, 0x80, 0x28, - 0x7e, 0x04, 0x01, 0xcd, 0x80, 0x2a, 0x7e, 0x04, 0x01, 0xcd, 0x80, 0xcf, 0x7e, 0x07, 0x01, 0xcb, - 0x7e, 0x24, 0x03, 0xfe, 0x9d, 0x20, 0x28, 0x40, 0x7e, 0x07, 0x01, 0xc9, 0x7e, 0x44, 0x05, 0xcd, - 0x7d, 0x60, 0x0b, 0x04, 0xbd, 0x04, 0x68, 0xd2, 0x7d, 0x70, 0x0b, 0x04, 0xbd, 0x04, 0x68, 0xd0, - 0x7d, 0x54, 0x9d, 0x50, 0xbd, 0x25, 0x40, 0x02, 0x7d, 0x25, 0x7d, 0x32, 0x09, 0xb1, 0x00, 0x14, - 0xca, 0xb8, 0x54, 0x1f, 0xb4, 0x01, 0x31, 0xda, 0xb8, 0x7e, 0x19, 0xb0, 0x7a, 0x09, 0xb0, 0x0b, - 0x04, 0x1b, 0x24, 0x78, 0xe7, 0x02, 0x5c, 0x78, 0x75, 0x2f, 0x99, 0x12, 0x7e, 0x30, 0x09, 0xb1, + 0xd2, 0x14, 0x22, 0xda, 0xb8, 0x30, 0xe0, 0xd8, 0xbd, 0x32, 0x68, 0x07, 0xca, 0xb8, 0x12, 0x55, + 0x43, 0xda, 0xb8, 0x02, 0x56, 0x90, 0x09, 0xb1, 0x00, 0x18, 0x7e, 0xa0, 0x88, 0x75, 0x31, 0x90, + 0x12, 0x7c, 0x15, 0xf5, 0x31, 0x12, 0x7c, 0x15, 0xa5, 0xfc, 0x5e, 0xb0, 0xf0, 0xa5, 0xfd, 0x09, + 0xb1, 0x00, 0x18, 0x4c, 0x4b, 0x5e, 0xb0, 0xf0, 0xbc, 0xb5, 0x78, 0xf1, 0x5e, 0x40, 0x0f, 0x4c, + 0x54, 0x7c, 0xb5, 0x5e, 0x50, 0x0b, 0x68, 0x2a, 0xa5, 0xfd, 0x5e, 0x50, 0x10, 0x68, 0x04, 0xd2, + 0x6c, 0x80, 0x02, 0xc2, 0x6c, 0xa5, 0xfd, 0x5e, 0x50, 0x20, 0x68, 0x04, 0xd2, 0x64, 0x80, 0x02, + 0xc2, 0x64, 0xa5, 0xfd, 0x5e, 0x50, 0x80, 0x68, 0x04, 0xd2, 0x5c, 0x80, 0x02, 0xc2, 0x5c, 0x12, + 0x43, 0x4c, 0x02, 0x62, 0x93, 0x75, 0x31, 0x91, 0x12, 0x7c, 0x15, 0x09, 0xb1, 0x00, 0x14, 0x7a, + 0xb1, 0x31, 0x12, 0x7c, 0x15, 0x20, 0xe0, 0x08, 0xd2, 0x04, 0x7e, 0xa0, 0x80, 0x02, 0x62, 0x93, + 0xd2, 0x04, 0x30, 0xe1, 0x06, 0x7e, 0xa0, 0x80, 0x12, 0x62, 0x93, 0xca, 0xb8, 0x5e, 0xb0, 0x1c, + 0xda, 0xb8, 0x68, 0x12, 0x7e, 0xa0, 0xc0, 0x09, 0x61, 0x00, 0x00, 0x12, 0x62, 0xb6, 0x09, 0xb1, + 0x00, 0x14, 0x20, 0xe0, 0xdb, 0x22, 0x02, 0x55, 0x95, 0x75, 0x31, 0x95, 0x12, 0x7c, 0x15, 0x22, + 0x75, 0x31, 0x96, 0x12, 0x7c, 0x15, 0x22, 0x10, 0x0d, 0x01, 0x22, 0x20, 0x2d, 0x03, 0xd2, 0x0d, + 0x22, 0x75, 0x31, 0xa5, 0x12, 0x7c, 0x15, 0x7e, 0x14, 0x85, 0x00, 0x80, 0x06, 0x20, 0x2d, 0x03, + 0xd2, 0x0d, 0x22, 0x09, 0xb1, 0x00, 0x14, 0xca, 0xb8, 0x5e, 0xb0, 0x1e, 0xda, 0xb8, 0x68, 0x03, + 0x12, 0x59, 0xe8, 0x20, 0xe6, 0x03, 0xd2, 0x0d, 0x22, 0x30, 0x35, 0x49, 0xd2, 0x75, 0x7e, 0x37, + 0x01, 0x99, 0x7e, 0x27, 0x01, 0xb9, 0x9d, 0x32, 0x40, 0x31, 0x7d, 0x02, 0x2e, 0x05, 0x52, 0x7a, + 0x05, 0x52, 0x7a, 0x37, 0x01, 0x99, 0x7e, 0x37, 0x01, 0x79, 0x7d, 0x43, 0x2d, 0x42, 0xbe, 0x44, + 0x21, 0xe2, 0x38, 0x68, 0x7a, 0x47, 0x01, 0x79, 0x75, 0x31, 0x94, 0x12, 0x7c, 0x15, 0x7a, 0x51, + 0x31, 0x12, 0x7c, 0x15, 0x12, 0x65, 0x2b, 0x10, 0x75, 0xc4, 0x22, 0xc2, 0x75, 0x2d, 0x23, 0x68, + 0x78, 0x6d, 0x33, 0x80, 0x1a, 0x7e, 0x27, 0x01, 0x99, 0xbe, 0x24, 0x00, 0x00, 0x68, 0x6a, 0xbe, + 0x27, 0x01, 0xb9, 0x28, 0x04, 0x7e, 0x27, 0x01, 0xb9, 0x7e, 0x37, 0x01, 0x99, 0x9d, 0x32, 0x7d, + 0x02, 0x2e, 0x05, 0x52, 0x7a, 0x05, 0x52, 0x7a, 0x37, 0x01, 0x99, 0x7e, 0x37, 0x01, 0x79, 0x7d, + 0x43, 0x2d, 0x42, 0xbe, 0x44, 0x21, 0xe2, 0x38, 0x13, 0x7a, 0x47, 0x01, 0x79, 0x75, 0x31, 0x94, + 0x12, 0x7c, 0x15, 0x7a, 0x51, 0x31, 0x12, 0x7c, 0x15, 0x02, 0x65, 0x2b, 0x75, 0x31, 0x94, 0x12, + 0x7c, 0x15, 0x7a, 0x51, 0x31, 0x12, 0x7c, 0x15, 0x9e, 0x44, 0x21, 0xe3, 0x9d, 0x24, 0x12, 0x65, + 0x2b, 0x7e, 0x34, 0x1d, 0xe3, 0x7d, 0x24, 0x2d, 0x43, 0x7a, 0x47, 0x01, 0x79, 0x12, 0x65, 0x2b, + 0xbe, 0x25, 0x20, 0x78, 0x03, 0x02, 0x57, 0x37, 0x22, 0xd2, 0x0d, 0x7e, 0x04, 0x1d, 0xe3, 0x7a, + 0x07, 0x01, 0x79, 0x7a, 0x07, 0x01, 0x89, 0x75, 0x31, 0x94, 0x12, 0x7c, 0x15, 0x75, 0x31, 0x00, + 0x12, 0x7c, 0x15, 0x22, 0x75, 0x31, 0x92, 0x12, 0x7c, 0x15, 0xd2, 0x04, 0x09, 0xb1, 0x00, 0x14, + 0xca, 0xb8, 0x54, 0x82, 0xda, 0xb8, 0x78, 0x70, 0x7e, 0x37, 0x01, 0xe1, 0x7e, 0x27, 0x01, 0xc9, + 0x2e, 0x24, 0x00, 0x02, 0x2d, 0x32, 0xbe, 0x34, 0x04, 0x00, 0x38, 0x3c, 0x7d, 0x02, 0x2e, 0x05, + 0x46, 0x7a, 0x05, 0x46, 0x7a, 0x37, 0x01, 0xe1, 0x7e, 0x37, 0x01, 0xdf, 0x7d, 0x43, 0x2d, 0x42, + 0xbe, 0x44, 0x05, 0xe2, 0x38, 0x44, 0x7a, 0x47, 0x01, 0xdf, 0x7e, 0x24, 0x05, 0x00, 0x2e, 0x27, + 0x01, 0xc9, 0x1b, 0x38, 0x20, 0x0b, 0x35, 0x7a, 0x51, 0x31, 0x12, 0x7c, 0x15, 0xbe, 0x50, 0x38, + 0x78, 0x03, 0x02, 0x66, 0x16, 0x02, 0x65, 0xfb, 0x75, 0x31, 0x99, 0x12, 0x7c, 0x15, 0x09, 0xb1, 0x00, 0x04, 0x54, 0xfa, 0x19, 0xb1, 0x00, 0x04, 0x30, 0x3d, 0x0a, 0x09, 0xb1, 0x00, 0x10, 0x54, - 0xfe, 0x19, 0xb1, 0x00, 0x10, 0xd2, 0x15, 0x22, 0xda, 0xb8, 0x30, 0xe0, 0xd8, 0xbd, 0x32, 0x68, - 0x07, 0xca, 0xb8, 0x12, 0x5c, 0x78, 0xda, 0xb8, 0x02, 0x5d, 0x9f, 0x09, 0xb1, 0x00, 0x18, 0x7e, - 0xa0, 0x88, 0x75, 0x2f, 0x90, 0x12, 0x7e, 0x30, 0xf5, 0x2f, 0x12, 0x7e, 0x30, 0xa5, 0xfd, 0x5e, - 0x50, 0x0a, 0x68, 0x1d, 0xa5, 0xfd, 0x5e, 0x50, 0x20, 0x68, 0x04, 0xd2, 0x5d, 0x80, 0x02, 0xc2, - 0x5d, 0xa5, 0xfd, 0x5e, 0x50, 0x80, 0x68, 0x04, 0xd2, 0x55, 0x80, 0x02, 0xc2, 0x55, 0x12, 0x43, - 0x1f, 0x02, 0x65, 0x9c, 0x75, 0x2f, 0x91, 0x12, 0x7e, 0x30, 0x09, 0xb1, 0x00, 0x14, 0x7a, 0xb1, - 0x2f, 0x12, 0x7e, 0x30, 0x20, 0xe0, 0x08, 0xd2, 0x04, 0x7e, 0xa0, 0x80, 0x02, 0x65, 0x9c, 0xd2, - 0x04, 0x30, 0xe1, 0x06, 0x7e, 0xa0, 0x80, 0x12, 0x65, 0x9c, 0xca, 0xb8, 0x5e, 0xb0, 0x1c, 0xda, - 0xb8, 0x68, 0x12, 0x7e, 0xa0, 0xc0, 0x09, 0x61, 0x00, 0x00, 0x12, 0x65, 0xbf, 0x09, 0xb1, 0x00, - 0x14, 0x20, 0xe0, 0xdb, 0x22, 0x02, 0x5c, 0xcc, 0x75, 0x2f, 0x95, 0x12, 0x7e, 0x30, 0x22, 0x75, - 0x2f, 0x96, 0x12, 0x7e, 0x30, 0x22, 0x10, 0x0e, 0x01, 0x22, 0x20, 0x2e, 0x03, 0xd2, 0x0e, 0x22, - 0x75, 0x2f, 0xa6, 0x12, 0x7e, 0x30, 0x7e, 0x14, 0x86, 0x00, 0x80, 0x06, 0x20, 0x2e, 0x03, 0xd2, - 0x0e, 0x22, 0x09, 0xb1, 0x00, 0x14, 0xca, 0xb8, 0x5e, 0xb0, 0x1e, 0xda, 0xb8, 0x68, 0x03, 0x12, - 0x60, 0xd4, 0x30, 0x36, 0x06, 0x20, 0xe6, 0x4f, 0xd2, 0x0e, 0x22, 0x30, 0xe6, 0x02, 0xd2, 0x66, - 0x7e, 0x37, 0x01, 0x85, 0x7e, 0x27, 0x01, 0xa5, 0x9d, 0x32, 0x40, 0x31, 0x7d, 0x02, 0x2e, 0x05, - 0x3e, 0x7a, 0x05, 0x3e, 0x7a, 0x37, 0x01, 0x85, 0x7e, 0x37, 0x01, 0x65, 0x7d, 0x43, 0x2d, 0x42, - 0xbe, 0x44, 0x25, 0xcc, 0x38, 0x68, 0x7a, 0x47, 0x01, 0x65, 0x75, 0x2f, 0x94, 0x12, 0x7e, 0x30, - 0x7a, 0x51, 0x2f, 0x12, 0x7e, 0x30, 0x12, 0x68, 0x34, 0x10, 0x66, 0xc4, 0x22, 0xc2, 0x66, 0x2d, - 0x23, 0x68, 0x78, 0x6d, 0x33, 0x80, 0x1a, 0x7e, 0x27, 0x01, 0x85, 0xbe, 0x24, 0x00, 0x00, 0x68, - 0x6a, 0xbe, 0x27, 0x01, 0xa5, 0x28, 0x04, 0x7e, 0x27, 0x01, 0xa5, 0x7e, 0x37, 0x01, 0x85, 0x9d, - 0x32, 0x7d, 0x02, 0x2e, 0x05, 0x3e, 0x7a, 0x05, 0x3e, 0x7a, 0x37, 0x01, 0x85, 0x7e, 0x37, 0x01, - 0x65, 0x7d, 0x43, 0x2d, 0x42, 0xbe, 0x44, 0x25, 0xcc, 0x38, 0x13, 0x7a, 0x47, 0x01, 0x65, 0x75, - 0x2f, 0x94, 0x12, 0x7e, 0x30, 0x7a, 0x51, 0x2f, 0x12, 0x7e, 0x30, 0x02, 0x68, 0x34, 0x75, 0x2f, - 0x94, 0x12, 0x7e, 0x30, 0x7a, 0x51, 0x2f, 0x12, 0x7e, 0x30, 0x9e, 0x44, 0x25, 0xcd, 0x9d, 0x24, - 0x12, 0x68, 0x34, 0x7e, 0x34, 0x21, 0xcd, 0x7d, 0x24, 0x2d, 0x43, 0x7a, 0x47, 0x01, 0x65, 0x12, - 0x68, 0x34, 0xbe, 0x25, 0x20, 0x78, 0x03, 0x02, 0x5e, 0x49, 0x22, 0xd2, 0x0e, 0x7e, 0x04, 0x21, - 0xcd, 0x7a, 0x07, 0x01, 0x65, 0x7a, 0x07, 0x01, 0x75, 0x75, 0x2f, 0x94, 0x12, 0x7e, 0x30, 0x75, - 0x2f, 0x00, 0x12, 0x7e, 0x30, 0x22, 0x75, 0x2f, 0x92, 0x12, 0x7e, 0x30, 0xd2, 0x04, 0x09, 0xb1, - 0x00, 0x14, 0xca, 0xb8, 0x54, 0x82, 0xda, 0xb8, 0x78, 0x70, 0x7e, 0x37, 0x01, 0xcb, 0x7e, 0x27, - 0x01, 0xb5, 0x2e, 0x24, 0x00, 0x02, 0x2d, 0x32, 0xbe, 0x34, 0x04, 0x00, 0x38, 0x3c, 0x7d, 0x02, - 0x2e, 0x05, 0x30, 0x7a, 0x05, 0x30, 0x7a, 0x37, 0x01, 0xcb, 0x7e, 0x37, 0x01, 0xc9, 0x7d, 0x43, - 0x2d, 0x42, 0xbe, 0x44, 0x05, 0xcc, 0x38, 0x44, 0x7a, 0x47, 0x01, 0xc9, 0x7e, 0x24, 0x06, 0x00, - 0x2e, 0x27, 0x01, 0xb5, 0x1b, 0x38, 0x20, 0x0b, 0x35, 0x7a, 0x51, 0x2f, 0x12, 0x7e, 0x30, 0xbe, - 0x50, 0x38, 0x78, 0x03, 0x02, 0x69, 0x1f, 0x02, 0x69, 0x04, 0x75, 0x2f, 0x99, 0x12, 0x7e, 0x30, - 0x09, 0xb1, 0x00, 0x04, 0x54, 0xfa, 0x19, 0xb1, 0x00, 0x04, 0x30, 0x3e, 0x0a, 0x09, 0xb1, 0x00, - 0x10, 0x54, 0xfe, 0x19, 0xb1, 0x00, 0x10, 0xd2, 0x16, 0x22, 0x80, 0x7f, 0x7a, 0x51, 0x2f, 0x12, - 0x7e, 0x30, 0x9e, 0x44, 0x05, 0xcd, 0x9d, 0x24, 0x7e, 0x64, 0x06, 0x00, 0x2e, 0x67, 0x01, 0xb5, - 0x9e, 0x24, 0x00, 0x02, 0x40, 0x17, 0x1b, 0x38, 0x60, 0x0b, 0x35, 0x12, 0x69, 0x04, 0x7e, 0x34, - 0x01, 0xcd, 0x7d, 0x24, 0x2d, 0x43, 0x7a, 0x47, 0x01, 0xc9, 0x02, 0x69, 0x04, 0x7a, 0x39, 0xc0, - 0x7e, 0x34, 0x01, 0xcd, 0x7a, 0x39, 0xd0, 0x0b, 0x34, 0x1b, 0x44, 0x80, 0xe5, 0x9d, 0x32, 0x7c, - 0xb6, 0x54, 0x0f, 0x23, 0x23, 0x23, 0x44, 0x06, 0x7a, 0x69, 0xb0, 0x7a, 0x79, 0x70, 0x0b, 0x35, - 0x75, 0x2f, 0x93, 0x12, 0x7e, 0x30, 0x7a, 0x71, 0x2f, 0x12, 0x7e, 0x30, 0xbd, 0x04, 0x68, 0x2b, - 0x7a, 0x07, 0x01, 0xc9, 0x7e, 0x47, 0x01, 0xcb, 0x2d, 0x43, 0x7a, 0x47, 0x01, 0xcb, 0x2e, 0x35, - 0x30, 0x7a, 0x35, 0x30, 0x22, 0xd2, 0x04, 0x09, 0xb1, 0x00, 0x14, 0x20, 0xe0, 0x13, 0x22, 0x7e, - 0x04, 0x01, 0xcd, 0x80, 0x28, 0x7e, 0x04, 0x01, 0xcd, 0x80, 0x2a, 0x7e, 0x04, 0x01, 0xcd, 0x80, - 0xcf, 0x7e, 0x07, 0x01, 0xcb, 0x7e, 0x24, 0x03, 0xfe, 0x9d, 0x20, 0x28, 0x40, 0x7e, 0x07, 0x01, - 0xc9, 0x7e, 0x44, 0x05, 0xcd, 0x7d, 0x60, 0x0b, 0x04, 0xbd, 0x04, 0x68, 0xd2, 0x7d, 0x70, 0x0b, - 0x04, 0xbd, 0x04, 0x68, 0xd0, 0x7d, 0x54, 0x9d, 0x50, 0xbd, 0x25, 0x40, 0x02, 0x7d, 0x25, 0x7d, - 0x32, 0x09, 0xb1, 0x00, 0x14, 0xca, 0xb8, 0x54, 0x1f, 0xb4, 0x01, 0x31, 0xda, 0xb8, 0x7e, 0x19, - 0xb0, 0x7a, 0x09, 0xb0, 0x0b, 0x04, 0x1b, 0x24, 0x78, 0xe7, 0x02, 0x5f, 0xad, 0x75, 0x2f, 0x99, - 0x12, 0x7e, 0x30, 0x09, 0xb1, 0x00, 0x04, 0x54, 0xfa, 0x19, 0xb1, 0x00, 0x04, 0x30, 0x3e, 0x0a, - 0x09, 0xb1, 0x00, 0x10, 0x54, 0xfe, 0x19, 0xb1, 0x00, 0x10, 0xd2, 0x16, 0x22, 0xda, 0xb8, 0x30, - 0xe0, 0xd8, 0xbd, 0x32, 0x68, 0x07, 0xca, 0xb8, 0x12, 0x5f, 0xad, 0xda, 0xb8, 0x02, 0x60, 0xd4, - 0x09, 0xb1, 0x00, 0x18, 0x7e, 0xa0, 0x88, 0x75, 0x2f, 0x90, 0x12, 0x7e, 0x30, 0xf5, 0x2f, 0x12, - 0x7e, 0x30, 0xa5, 0xfd, 0x5e, 0x50, 0x0a, 0x68, 0x1d, 0xa5, 0xfd, 0x5e, 0x50, 0x20, 0x68, 0x04, - 0xd2, 0x5e, 0x80, 0x02, 0xc2, 0x5e, 0xa5, 0xfd, 0x5e, 0x50, 0x80, 0x68, 0x04, 0xd2, 0x56, 0x80, - 0x02, 0xc2, 0x56, 0x12, 0x43, 0x36, 0x02, 0x65, 0x9c, 0x75, 0x2f, 0x91, 0x12, 0x7e, 0x30, 0x09, - 0xb1, 0x00, 0x14, 0x7a, 0xb1, 0x2f, 0x12, 0x7e, 0x30, 0x20, 0xe0, 0x08, 0xd2, 0x04, 0x7e, 0xa0, - 0x80, 0x02, 0x65, 0x9c, 0xd2, 0x04, 0x30, 0xe1, 0x06, 0x7e, 0xa0, 0x80, 0x12, 0x65, 0x9c, 0xca, - 0xb8, 0x5e, 0xb0, 0x1c, 0xda, 0xb8, 0x68, 0x12, 0x7e, 0xa0, 0xc0, 0x09, 0x61, 0x00, 0x00, 0x12, - 0x65, 0xbf, 0x09, 0xb1, 0x00, 0x14, 0x20, 0xe0, 0xdb, 0x22, 0x02, 0x60, 0x01, 0x75, 0x2f, 0x95, - 0x12, 0x7e, 0x30, 0x22, 0x75, 0x2f, 0x96, 0x12, 0x7e, 0x30, 0x22, 0x10, 0x0f, 0x01, 0x22, 0x20, - 0x2f, 0x03, 0xd2, 0x0f, 0x22, 0x75, 0x2f, 0xa7, 0x12, 0x7e, 0x30, 0x7e, 0x14, 0x87, 0x00, 0x80, - 0x06, 0x20, 0x2f, 0x03, 0xd2, 0x0f, 0x22, 0x09, 0xb1, 0x00, 0x14, 0xca, 0xb8, 0x5e, 0xb0, 0x1e, - 0xda, 0xb8, 0x68, 0x03, 0x12, 0x64, 0x09, 0x30, 0x37, 0x06, 0x20, 0xe6, 0x4f, 0xd2, 0x0f, 0x22, - 0x30, 0xe6, 0x02, 0xd2, 0x67, 0x7e, 0x37, 0x01, 0x87, 0x7e, 0x27, 0x01, 0xa7, 0x9d, 0x32, 0x40, - 0x31, 0x7d, 0x02, 0x2e, 0x05, 0x40, 0x7a, 0x05, 0x40, 0x7a, 0x37, 0x01, 0x87, 0x7e, 0x37, 0x01, - 0x67, 0x7d, 0x43, 0x2d, 0x42, 0xbe, 0x44, 0x29, 0xcc, 0x38, 0x68, 0x7a, 0x47, 0x01, 0x67, 0x75, - 0x2f, 0x94, 0x12, 0x7e, 0x30, 0x7a, 0x51, 0x2f, 0x12, 0x7e, 0x30, 0x12, 0x68, 0x34, 0x10, 0x67, - 0xc4, 0x22, 0xc2, 0x67, 0x2d, 0x23, 0x68, 0x78, 0x6d, 0x33, 0x80, 0x1a, 0x7e, 0x27, 0x01, 0x87, - 0xbe, 0x24, 0x00, 0x00, 0x68, 0x6a, 0xbe, 0x27, 0x01, 0xa7, 0x28, 0x04, 0x7e, 0x27, 0x01, 0xa7, - 0x7e, 0x37, 0x01, 0x87, 0x9d, 0x32, 0x7d, 0x02, 0x2e, 0x05, 0x40, 0x7a, 0x05, 0x40, 0x7a, 0x37, - 0x01, 0x87, 0x7e, 0x37, 0x01, 0x67, 0x7d, 0x43, 0x2d, 0x42, 0xbe, 0x44, 0x29, 0xcc, 0x38, 0x13, - 0x7a, 0x47, 0x01, 0x67, 0x75, 0x2f, 0x94, 0x12, 0x7e, 0x30, 0x7a, 0x51, 0x2f, 0x12, 0x7e, 0x30, - 0x02, 0x68, 0x34, 0x75, 0x2f, 0x94, 0x12, 0x7e, 0x30, 0x7a, 0x51, 0x2f, 0x12, 0x7e, 0x30, 0x9e, - 0x44, 0x29, 0xcd, 0x9d, 0x24, 0x12, 0x68, 0x34, 0x7e, 0x34, 0x25, 0xcd, 0x7d, 0x24, 0x2d, 0x43, - 0x7a, 0x47, 0x01, 0x67, 0x12, 0x68, 0x34, 0xbe, 0x25, 0x20, 0x78, 0x03, 0x02, 0x61, 0x7e, 0x22, - 0xd2, 0x0f, 0x7e, 0x04, 0x25, 0xcd, 0x7a, 0x07, 0x01, 0x67, 0x7a, 0x07, 0x01, 0x77, 0x75, 0x2f, - 0x94, 0x12, 0x7e, 0x30, 0x75, 0x2f, 0x00, 0x12, 0x7e, 0x30, 0x22, 0x75, 0x2f, 0x92, 0x12, 0x7e, - 0x30, 0xd2, 0x04, 0x09, 0xb1, 0x00, 0x14, 0xca, 0xb8, 0x54, 0x82, 0xda, 0xb8, 0x78, 0x70, 0x7e, - 0x37, 0x01, 0xcb, 0x7e, 0x27, 0x01, 0xb7, 0x2e, 0x24, 0x00, 0x02, 0x2d, 0x32, 0xbe, 0x34, 0x04, - 0x00, 0x38, 0x3c, 0x7d, 0x02, 0x2e, 0x05, 0x30, 0x7a, 0x05, 0x30, 0x7a, 0x37, 0x01, 0xcb, 0x7e, - 0x37, 0x01, 0xc9, 0x7d, 0x43, 0x2d, 0x42, 0xbe, 0x44, 0x05, 0xcc, 0x38, 0x44, 0x7a, 0x47, 0x01, - 0xc9, 0x7e, 0x24, 0x07, 0x00, 0x2e, 0x27, 0x01, 0xb7, 0x1b, 0x38, 0x20, 0x0b, 0x35, 0x7a, 0x51, - 0x2f, 0x12, 0x7e, 0x30, 0xbe, 0x50, 0x38, 0x78, 0x03, 0x02, 0x69, 0x1f, 0x02, 0x69, 0x04, 0x75, - 0x2f, 0x99, 0x12, 0x7e, 0x30, 0x09, 0xb1, 0x00, 0x04, 0x54, 0xfa, 0x19, 0xb1, 0x00, 0x04, 0x30, - 0x3f, 0x0a, 0x09, 0xb1, 0x00, 0x10, 0x54, 0xfe, 0x19, 0xb1, 0x00, 0x10, 0xd2, 0x17, 0x22, 0x80, - 0x7f, 0x7a, 0x51, 0x2f, 0x12, 0x7e, 0x30, 0x9e, 0x44, 0x05, 0xcd, 0x9d, 0x24, 0x7e, 0x64, 0x07, - 0x00, 0x2e, 0x67, 0x01, 0xb7, 0x9e, 0x24, 0x00, 0x02, 0x40, 0x17, 0x1b, 0x38, 0x60, 0x0b, 0x35, - 0x12, 0x69, 0x04, 0x7e, 0x34, 0x01, 0xcd, 0x7d, 0x24, 0x2d, 0x43, 0x7a, 0x47, 0x01, 0xc9, 0x02, - 0x69, 0x04, 0x7a, 0x39, 0xc0, 0x7e, 0x34, 0x01, 0xcd, 0x7a, 0x39, 0xd0, 0x0b, 0x34, 0x1b, 0x44, - 0x80, 0xe5, 0x9d, 0x32, 0x7c, 0xb6, 0x54, 0x0f, 0x23, 0x23, 0x23, 0x44, 0x07, 0x7a, 0x69, 0xb0, - 0x7a, 0x79, 0x70, 0x0b, 0x35, 0x75, 0x2f, 0x93, 0x12, 0x7e, 0x30, 0x7a, 0x71, 0x2f, 0x12, 0x7e, - 0x30, 0xbd, 0x04, 0x68, 0x2b, 0x7a, 0x07, 0x01, 0xc9, 0x7e, 0x47, 0x01, 0xcb, 0x2d, 0x43, 0x7a, - 0x47, 0x01, 0xcb, 0x2e, 0x35, 0x30, 0x7a, 0x35, 0x30, 0x22, 0xd2, 0x04, 0x09, 0xb1, 0x00, 0x14, - 0x20, 0xe0, 0x13, 0x22, 0x7e, 0x04, 0x01, 0xcd, 0x80, 0x28, 0x7e, 0x04, 0x01, 0xcd, 0x80, 0x2a, - 0x7e, 0x04, 0x01, 0xcd, 0x80, 0xcf, 0x7e, 0x07, 0x01, 0xcb, 0x7e, 0x24, 0x03, 0xfe, 0x9d, 0x20, - 0x28, 0x40, 0x7e, 0x07, 0x01, 0xc9, 0x7e, 0x44, 0x05, 0xcd, 0x7d, 0x60, 0x0b, 0x04, 0xbd, 0x04, - 0x68, 0xd2, 0x7d, 0x70, 0x0b, 0x04, 0xbd, 0x04, 0x68, 0xd0, 0x7d, 0x54, 0x9d, 0x50, 0xbd, 0x25, - 0x40, 0x02, 0x7d, 0x25, 0x7d, 0x32, 0x09, 0xb1, 0x00, 0x14, 0xca, 0xb8, 0x54, 0x1f, 0xb4, 0x01, - 0x31, 0xda, 0xb8, 0x7e, 0x19, 0xb0, 0x7a, 0x09, 0xb0, 0x0b, 0x04, 0x1b, 0x24, 0x78, 0xe7, 0x02, - 0x62, 0xe2, 0x75, 0x2f, 0x99, 0x12, 0x7e, 0x30, 0x09, 0xb1, 0x00, 0x04, 0x54, 0xfa, 0x19, 0xb1, - 0x00, 0x04, 0x30, 0x3f, 0x0a, 0x09, 0xb1, 0x00, 0x10, 0x54, 0xfe, 0x19, 0xb1, 0x00, 0x10, 0xd2, - 0x17, 0x22, 0xda, 0xb8, 0x30, 0xe0, 0xd8, 0xbd, 0x32, 0x68, 0x07, 0xca, 0xb8, 0x12, 0x62, 0xe2, - 0xda, 0xb8, 0x02, 0x64, 0x09, 0x09, 0xb1, 0x00, 0x18, 0x7e, 0xa0, 0x88, 0x75, 0x2f, 0x90, 0x12, - 0x7e, 0x30, 0xf5, 0x2f, 0x12, 0x7e, 0x30, 0xa5, 0xfd, 0x5e, 0x50, 0x0a, 0x68, 0x1d, 0xa5, 0xfd, - 0x5e, 0x50, 0x20, 0x68, 0x04, 0xd2, 0x5f, 0x80, 0x02, 0xc2, 0x5f, 0xa5, 0xfd, 0x5e, 0x50, 0x80, - 0x68, 0x04, 0xd2, 0x57, 0x80, 0x02, 0xc2, 0x57, 0x12, 0x43, 0x4d, 0x02, 0x65, 0x9c, 0x75, 0x2f, - 0x91, 0x12, 0x7e, 0x30, 0x09, 0xb1, 0x00, 0x14, 0x7a, 0xb1, 0x2f, 0x12, 0x7e, 0x30, 0x20, 0xe0, - 0x08, 0xd2, 0x04, 0x7e, 0xa0, 0x80, 0x02, 0x65, 0x9c, 0xd2, 0x04, 0x30, 0xe1, 0x06, 0x7e, 0xa0, - 0x80, 0x12, 0x65, 0x9c, 0xca, 0xb8, 0x5e, 0xb0, 0x1c, 0xda, 0xb8, 0x68, 0x12, 0x7e, 0xa0, 0xc0, - 0x09, 0x61, 0x00, 0x00, 0x12, 0x65, 0xbf, 0x09, 0xb1, 0x00, 0x14, 0x20, 0xe0, 0xdb, 0x22, 0x02, - 0x63, 0x36, 0x75, 0x2f, 0x95, 0x12, 0x7e, 0x30, 0x22, 0x75, 0x2f, 0x96, 0x12, 0x7e, 0x30, 0x22, - 0x7c, 0x02, 0x7e, 0x14, 0x80, 0x00, 0x4c, 0x20, 0x09, 0xb1, 0x00, 0x18, 0xa5, 0xfd, 0x5e, 0x50, - 0x20, 0x68, 0x04, 0xd2, 0x58, 0x80, 0x02, 0xc2, 0x58, 0xa5, 0xfd, 0x5e, 0x50, 0x80, 0x68, 0x04, - 0xd2, 0x50, 0x80, 0x02, 0xc2, 0x50, 0x02, 0x65, 0x88, 0x7c, 0x02, 0x7e, 0x14, 0x80, 0x00, 0x4c, - 0x20, 0x09, 0xb1, 0x00, 0x18, 0xa5, 0xfd, 0x5e, 0x50, 0x20, 0x68, 0x04, 0xd2, 0x59, 0x80, 0x02, - 0xc2, 0x59, 0xa5, 0xfd, 0x5e, 0x50, 0x80, 0x68, 0x04, 0xd2, 0x51, 0x80, 0x02, 0xc2, 0x51, 0x02, - 0x65, 0x88, 0x7c, 0x02, 0x7e, 0x14, 0x80, 0x00, 0x4c, 0x20, 0x09, 0xb1, 0x00, 0x18, 0xa5, 0xfd, - 0x5e, 0x50, 0x20, 0x68, 0x04, 0xd2, 0x5a, 0x80, 0x02, 0xc2, 0x5a, 0xa5, 0xfd, 0x5e, 0x50, 0x80, - 0x68, 0x04, 0xd2, 0x52, 0x80, 0x02, 0xc2, 0x52, 0x02, 0x65, 0x88, 0x7c, 0x02, 0x7e, 0x14, 0x80, - 0x00, 0x4c, 0x20, 0x09, 0xb1, 0x00, 0x18, 0xa5, 0xfd, 0x5e, 0x50, 0x20, 0x68, 0x04, 0xd2, 0x5b, - 0x80, 0x02, 0xc2, 0x5b, 0xa5, 0xfd, 0x5e, 0x50, 0x80, 0x68, 0x04, 0xd2, 0x53, 0x80, 0x02, 0xc2, - 0x53, 0x02, 0x65, 0x88, 0x7c, 0x02, 0x7e, 0x14, 0x80, 0x00, 0x4c, 0x20, 0x09, 0xb1, 0x00, 0x18, - 0xa5, 0xfd, 0x5e, 0x50, 0x20, 0x68, 0x04, 0xd2, 0x5c, 0x80, 0x02, 0xc2, 0x5c, 0xa5, 0xfd, 0x5e, - 0x50, 0x80, 0x68, 0x04, 0xd2, 0x54, 0x80, 0x02, 0xc2, 0x54, 0x02, 0x65, 0x88, 0x7c, 0x02, 0x7e, - 0x14, 0x80, 0x00, 0x4c, 0x20, 0x09, 0xb1, 0x00, 0x18, 0xa5, 0xfd, 0x5e, 0x50, 0x20, 0x68, 0x04, - 0xd2, 0x5d, 0x80, 0x02, 0xc2, 0x5d, 0xa5, 0xfd, 0x5e, 0x50, 0x80, 0x68, 0x04, 0xd2, 0x55, 0x80, - 0x02, 0xc2, 0x55, 0x02, 0x65, 0x88, 0x7c, 0x02, 0x7e, 0x14, 0x80, 0x00, 0x4c, 0x20, 0x09, 0xb1, - 0x00, 0x18, 0xa5, 0xfd, 0x5e, 0x50, 0x20, 0x68, 0x04, 0xd2, 0x5e, 0x80, 0x02, 0xc2, 0x5e, 0xa5, - 0xfd, 0x5e, 0x50, 0x80, 0x68, 0x04, 0xd2, 0x56, 0x80, 0x02, 0xc2, 0x56, 0x02, 0x65, 0x88, 0x7c, - 0x02, 0x7e, 0x14, 0x80, 0x00, 0x4c, 0x20, 0x09, 0xb1, 0x00, 0x18, 0xa5, 0xfd, 0x5e, 0x50, 0x20, - 0x68, 0x04, 0xd2, 0x5f, 0x80, 0x02, 0xc2, 0x5f, 0xa5, 0xfd, 0x5e, 0x50, 0x80, 0x68, 0x04, 0xd2, - 0x57, 0x80, 0x02, 0xc2, 0x57, 0x02, 0x65, 0x88, 0x54, 0xf0, 0xc4, 0xa5, 0xff, 0xc4, 0xa5, 0x4f, - 0x75, 0x2f, 0x90, 0x12, 0x7e, 0x30, 0xf5, 0x2f, 0x12, 0x7e, 0x30, 0x22, 0xca, 0x19, 0x5e, 0x20, - 0x07, 0x4c, 0xa2, 0x7e, 0x74, 0x29, 0xcd, 0xca, 0x79, 0x7a, 0x79, 0xa0, 0x0b, 0x74, 0x7a, 0x79, - 0xb0, 0x0b, 0x74, 0xda, 0x79, 0x7e, 0x30, 0x02, 0x7e, 0x64, 0x00, 0x02, 0x02, 0x65, 0xe7, 0xca, - 0x19, 0x5e, 0x20, 0x07, 0x4c, 0xa2, 0x7e, 0x74, 0x29, 0xcd, 0xca, 0x79, 0x7a, 0x79, 0xa0, 0x0b, - 0x74, 0x7a, 0x79, 0xb0, 0x0b, 0x74, 0x7a, 0x79, 0x60, 0x0b, 0x74, 0xda, 0x79, 0x7e, 0x30, 0x03, - 0x7e, 0x64, 0x00, 0x03, 0x02, 0x65, 0xe7, 0xd2, 0x04, 0x7e, 0x27, 0x01, 0xcb, 0x2d, 0x26, 0xbe, - 0x24, 0x04, 0x00, 0x38, 0x2e, 0x7e, 0x07, 0x01, 0xc9, 0x7e, 0x44, 0x05, 0xcd, 0x7e, 0x79, 0xa0, - 0x7a, 0x09, 0xa0, 0x0b, 0x04, 0x0b, 0x74, 0xbd, 0x04, 0x68, 0x23, 0xa5, 0xdb, 0xef, 0x7a, 0x27, - 0x01, 0xcb, 0x7e, 0x25, 0x30, 0x2d, 0x26, 0x7a, 0x25, 0x30, 0x7a, 0x07, 0x01, 0xc9, 0xda, 0x19, - 0xc2, 0xd7, 0x22, 0x75, 0x2f, 0x9a, 0x12, 0x7e, 0x30, 0xda, 0x19, 0xd2, 0xd7, 0x22, 0x7e, 0x04, - 0x01, 0xcd, 0x80, 0xd7, 0x4d, 0x42, 0x4a, 0xae, 0x4b, 0xa8, 0x4d, 0x7b, 0x49, 0x4f, 0x49, 0x4f, - 0x4c, 0xa7, 0x49, 0x4f, 0x4d, 0xbf, 0x49, 0x4f, 0x49, 0x4f, 0x49, 0x4f, 0x49, 0x4f, 0x49, 0x4f, - 0x49, 0x4f, 0x49, 0x4f, 0x4d, 0xc6, 0x49, 0x4f, 0x49, 0x4f, 0x49, 0x4f, 0x49, 0x4f, 0x49, 0x4f, - 0x49, 0x4f, 0x49, 0x4f, 0x49, 0x4f, 0x49, 0x4f, 0x49, 0x4f, 0x49, 0x4f, 0x49, 0x4f, 0x49, 0x4f, - 0x49, 0x4f, 0x49, 0x4f, 0x50, 0x77, 0x4d, 0xe3, 0x4e, 0xdd, 0x50, 0xb0, 0x49, 0x4f, 0x49, 0x4f, - 0x4f, 0xdc, 0x49, 0x4f, 0x50, 0xf4, 0x49, 0x4f, 0x49, 0x4f, 0x49, 0x4f, 0x49, 0x4f, 0x49, 0x4f, - 0x49, 0x4f, 0x49, 0x4f, 0x50, 0xfb, 0x49, 0x4f, 0x49, 0x4f, 0x49, 0x4f, 0x49, 0x4f, 0x49, 0x4f, - 0x49, 0x4f, 0x49, 0x4f, 0x49, 0x4f, 0x49, 0x4f, 0x49, 0x4f, 0x49, 0x4f, 0x49, 0x4f, 0x49, 0x4f, - 0x49, 0x4f, 0x49, 0x4f, 0x53, 0xac, 0x51, 0x18, 0x52, 0x12, 0x53, 0xe5, 0x49, 0x4f, 0x49, 0x4f, - 0x53, 0x11, 0x49, 0x4f, 0x54, 0x29, 0x49, 0x4f, 0x49, 0x4f, 0x49, 0x4f, 0x49, 0x4f, 0x49, 0x4f, - 0x49, 0x4f, 0x49, 0x4f, 0x54, 0x30, 0x49, 0x4f, 0x49, 0x4f, 0x49, 0x4f, 0x49, 0x4f, 0x49, 0x4f, - 0x49, 0x4f, 0x49, 0x4f, 0x49, 0x4f, 0x49, 0x4f, 0x49, 0x4f, 0x49, 0x4f, 0x49, 0x4f, 0x49, 0x4f, - 0x49, 0x4f, 0x49, 0x4f, 0x56, 0xe1, 0x54, 0x4d, 0x55, 0x47, 0x57, 0x1a, 0x49, 0x4f, 0x49, 0x4f, - 0x56, 0x46, 0x49, 0x4f, 0x57, 0x5e, 0x49, 0x4f, 0x49, 0x4f, 0x49, 0x4f, 0x49, 0x4f, 0x49, 0x4f, - 0x49, 0x4f, 0x49, 0x4f, 0x57, 0x65, 0x49, 0x4f, 0x49, 0x4f, 0x49, 0x4f, 0x49, 0x4f, 0x49, 0x4f, - 0x49, 0x4f, 0x49, 0x4f, 0x49, 0x4f, 0x49, 0x4f, 0x49, 0x4f, 0x49, 0x4f, 0x49, 0x4f, 0x49, 0x4f, - 0x49, 0x4f, 0x49, 0x4f, 0x5a, 0x16, 0x57, 0x82, 0x58, 0x7c, 0x5a, 0x4f, 0x49, 0x4f, 0x49, 0x4f, - 0x59, 0x7b, 0x49, 0x4f, 0x5a, 0x93, 0x49, 0x4f, 0x49, 0x4f, 0x49, 0x4f, 0x49, 0x4f, 0x49, 0x4f, - 0x49, 0x4f, 0x49, 0x4f, 0x5a, 0x9a, 0x49, 0x4f, 0x49, 0x4f, 0x49, 0x4f, 0x49, 0x4f, 0x49, 0x4f, - 0x49, 0x4f, 0x49, 0x4f, 0x49, 0x4f, 0x49, 0x4f, 0x49, 0x4f, 0x49, 0x4f, 0x49, 0x4f, 0x49, 0x4f, - 0x49, 0x4f, 0x49, 0x4f, 0x5d, 0x4b, 0x5a, 0xb7, 0x5b, 0xb1, 0x5d, 0x84, 0x49, 0x4f, 0x49, 0x4f, - 0x5c, 0xb0, 0x49, 0x4f, 0x5d, 0xc8, 0x49, 0x4f, 0x49, 0x4f, 0x49, 0x4f, 0x49, 0x4f, 0x49, 0x4f, - 0x49, 0x4f, 0x49, 0x4f, 0x5d, 0xcf, 0x49, 0x4f, 0x49, 0x4f, 0x49, 0x4f, 0x49, 0x4f, 0x49, 0x4f, - 0x49, 0x4f, 0x49, 0x4f, 0x49, 0x4f, 0x49, 0x4f, 0x49, 0x4f, 0x49, 0x4f, 0x49, 0x4f, 0x49, 0x4f, - 0x49, 0x4f, 0x49, 0x4f, 0x60, 0x80, 0x5d, 0xec, 0x5e, 0xe6, 0x60, 0xb9, 0x49, 0x4f, 0x49, 0x4f, - 0x5f, 0xe5, 0x49, 0x4f, 0x60, 0xfd, 0x49, 0x4f, 0x49, 0x4f, 0x49, 0x4f, 0x49, 0x4f, 0x49, 0x4f, - 0x49, 0x4f, 0x49, 0x4f, 0x61, 0x04, 0x49, 0x4f, 0x49, 0x4f, 0x49, 0x4f, 0x49, 0x4f, 0x49, 0x4f, - 0x49, 0x4f, 0x49, 0x4f, 0x49, 0x4f, 0x49, 0x4f, 0x49, 0x4f, 0x49, 0x4f, 0x49, 0x4f, 0x49, 0x4f, - 0x49, 0x4f, 0x49, 0x4f, 0x63, 0xb5, 0x61, 0x21, 0x62, 0x1b, 0x63, 0xee, 0x49, 0x4f, 0x49, 0x4f, - 0x63, 0x1a, 0x49, 0x4f, 0x64, 0x32, 0x49, 0x4f, 0x49, 0x4f, 0x49, 0x4f, 0x49, 0x4f, 0x49, 0x4f, - 0x49, 0x4f, 0x49, 0x4f, 0x64, 0x39, 0x49, 0x4f, 0x49, 0x4f, 0x49, 0x4f, 0x49, 0x4f, 0x49, 0x4f, - 0x49, 0x4f, 0x49, 0x4f, 0x49, 0x4f, 0x49, 0x4f, 0x49, 0x4f, 0x49, 0x4f, 0x49, 0x4f, 0x49, 0x4f, - 0x49, 0x4f, 0x49, 0x4f, 0xca, 0x29, 0x1e, 0x50, 0x40, 0x0d, 0x7e, 0x54, 0x0b, 0x10, 0x9c, 0xb5, - 0xa4, 0x2e, 0x54, 0x68, 0x51, 0x89, 0x54, 0x7e, 0x39, 0x00, 0x7a, 0x19, 0x00, 0x0b, 0x34, 0x80, - 0xe9, 0x0b, 0x38, 0x00, 0x7a, 0x19, 0x00, 0x7a, 0x19, 0x10, 0x0b, 0x35, 0x0b, 0x38, 0x00, 0x7a, - 0x19, 0x00, 0x7a, 0x19, 0x10, 0x0b, 0x35, 0x0b, 0x38, 0x00, 0x7a, 0x19, 0x00, 0x7a, 0x19, 0x10, - 0x0b, 0x35, 0x0b, 0x38, 0x00, 0x7a, 0x19, 0x00, 0x7a, 0x19, 0x10, 0x0b, 0x35, 0x0b, 0x38, 0x00, - 0x7a, 0x19, 0x00, 0x7a, 0x19, 0x10, 0x0b, 0x35, 0x0b, 0x38, 0x00, 0x7a, 0x19, 0x00, 0x7a, 0x19, + 0xfe, 0x19, 0xb1, 0x00, 0x10, 0xd2, 0x15, 0x22, 0x80, 0x7d, 0x7a, 0x51, 0x31, 0x12, 0x7c, 0x15, + 0x9e, 0x44, 0x05, 0xe3, 0x9d, 0x24, 0x7e, 0x64, 0x05, 0x00, 0x2e, 0x67, 0x01, 0xc9, 0x9e, 0x24, + 0x00, 0x02, 0x40, 0x17, 0x1b, 0x38, 0x60, 0x0b, 0x35, 0x12, 0x65, 0xfb, 0x7e, 0x34, 0x01, 0xe3, + 0x7d, 0x24, 0x2d, 0x43, 0x7a, 0x47, 0x01, 0xdf, 0x02, 0x65, 0xfb, 0x7a, 0x39, 0xc0, 0x7e, 0x34, + 0x01, 0xe3, 0x7a, 0x39, 0xd0, 0x0b, 0x34, 0x1b, 0x44, 0x80, 0xe5, 0x9d, 0x32, 0x7c, 0xb6, 0x54, + 0x0f, 0x23, 0x23, 0x23, 0x44, 0x05, 0x7a, 0x69, 0xb0, 0x7a, 0x79, 0x70, 0x0b, 0x35, 0x75, 0x31, + 0x93, 0x12, 0x7c, 0x15, 0x7a, 0x71, 0x31, 0x12, 0x7c, 0x15, 0xbd, 0x04, 0x68, 0x29, 0x7a, 0x07, + 0x01, 0xdf, 0x7e, 0x47, 0x01, 0xe1, 0x2d, 0x43, 0x7a, 0x47, 0x01, 0xe1, 0x2e, 0x35, 0x46, 0x7a, + 0x35, 0x46, 0x22, 0x09, 0xb1, 0x00, 0x14, 0x20, 0xe0, 0x13, 0x22, 0x7e, 0x04, 0x01, 0xe3, 0x80, + 0x2a, 0x7e, 0x04, 0x01, 0xe3, 0x80, 0x2c, 0x7e, 0x04, 0x01, 0xe3, 0x80, 0xd1, 0xd2, 0x04, 0x7e, + 0x07, 0x01, 0xe1, 0x7e, 0x24, 0x03, 0xfe, 0x9d, 0x20, 0x28, 0x40, 0x7e, 0x07, 0x01, 0xdf, 0x7e, + 0x44, 0x05, 0xe3, 0x7d, 0x60, 0x0b, 0x04, 0xbd, 0x04, 0x68, 0xd0, 0x7d, 0x70, 0x0b, 0x04, 0xbd, + 0x04, 0x68, 0xce, 0x7d, 0x54, 0x9d, 0x50, 0xbd, 0x25, 0x40, 0x02, 0x7d, 0x25, 0x7d, 0x32, 0x09, + 0xb1, 0x00, 0x14, 0xca, 0xb8, 0x54, 0x1f, 0xb4, 0x01, 0x31, 0xda, 0xb8, 0x7e, 0x19, 0xb0, 0x7a, + 0x09, 0xb0, 0x0b, 0x04, 0x1b, 0x24, 0x78, 0xe7, 0x02, 0x58, 0x9b, 0x75, 0x31, 0x99, 0x12, 0x7c, + 0x15, 0x09, 0xb1, 0x00, 0x04, 0x54, 0xfa, 0x19, 0xb1, 0x00, 0x04, 0x30, 0x3d, 0x0a, 0x09, 0xb1, + 0x00, 0x10, 0x54, 0xfe, 0x19, 0xb1, 0x00, 0x10, 0xd2, 0x15, 0x22, 0xda, 0xb8, 0x30, 0xe0, 0xd8, + 0xbd, 0x32, 0x68, 0x07, 0xca, 0xb8, 0x12, 0x58, 0x9b, 0xda, 0xb8, 0x02, 0x59, 0xe8, 0x09, 0xb1, + 0x00, 0x18, 0x7e, 0xa0, 0x88, 0x75, 0x31, 0x90, 0x12, 0x7c, 0x15, 0xf5, 0x31, 0x12, 0x7c, 0x15, + 0xa5, 0xfc, 0x5e, 0xb0, 0xf0, 0xa5, 0xfd, 0x09, 0xb1, 0x00, 0x18, 0x4c, 0x4b, 0x5e, 0xb0, 0xf0, + 0xbc, 0xb5, 0x78, 0xf1, 0x5e, 0x40, 0x0f, 0x4c, 0x54, 0x7c, 0xb5, 0x5e, 0x50, 0x0b, 0x68, 0x2a, + 0xa5, 0xfd, 0x5e, 0x50, 0x10, 0x68, 0x04, 0xd2, 0x6d, 0x80, 0x02, 0xc2, 0x6d, 0xa5, 0xfd, 0x5e, + 0x50, 0x20, 0x68, 0x04, 0xd2, 0x65, 0x80, 0x02, 0xc2, 0x65, 0xa5, 0xfd, 0x5e, 0x50, 0x80, 0x68, + 0x04, 0xd2, 0x5d, 0x80, 0x02, 0xc2, 0x5d, 0x12, 0x43, 0x6d, 0x02, 0x62, 0x93, 0x75, 0x31, 0x91, + 0x12, 0x7c, 0x15, 0x09, 0xb1, 0x00, 0x14, 0x7a, 0xb1, 0x31, 0x12, 0x7c, 0x15, 0x20, 0xe0, 0x08, + 0xd2, 0x04, 0x7e, 0xa0, 0x80, 0x02, 0x62, 0x93, 0xd2, 0x04, 0x30, 0xe1, 0x06, 0x7e, 0xa0, 0x80, + 0x12, 0x62, 0x93, 0xca, 0xb8, 0x5e, 0xb0, 0x1c, 0xda, 0xb8, 0x68, 0x12, 0x7e, 0xa0, 0xc0, 0x09, + 0x61, 0x00, 0x00, 0x12, 0x62, 0xb6, 0x09, 0xb1, 0x00, 0x14, 0x20, 0xe0, 0xdb, 0x22, 0x02, 0x58, + 0xed, 0x75, 0x31, 0x95, 0x12, 0x7c, 0x15, 0x22, 0x75, 0x31, 0x96, 0x12, 0x7c, 0x15, 0x22, 0x10, + 0x0e, 0x01, 0x22, 0x20, 0x2e, 0x03, 0xd2, 0x0e, 0x22, 0x75, 0x31, 0xa6, 0x12, 0x7c, 0x15, 0x7e, + 0x14, 0x86, 0x00, 0x80, 0x06, 0x20, 0x2e, 0x03, 0xd2, 0x0e, 0x22, 0x09, 0xb1, 0x00, 0x14, 0xca, + 0xb8, 0x5e, 0xb0, 0x1e, 0xda, 0xb8, 0x68, 0x03, 0x12, 0x5d, 0x40, 0x20, 0xe6, 0x03, 0xd2, 0x0e, + 0x22, 0x30, 0x36, 0x49, 0xd2, 0x76, 0x7e, 0x37, 0x01, 0x9b, 0x7e, 0x27, 0x01, 0xbb, 0x9d, 0x32, + 0x40, 0x31, 0x7d, 0x02, 0x2e, 0x05, 0x54, 0x7a, 0x05, 0x54, 0x7a, 0x37, 0x01, 0x9b, 0x7e, 0x37, + 0x01, 0x7b, 0x7d, 0x43, 0x2d, 0x42, 0xbe, 0x44, 0x25, 0xe2, 0x38, 0x68, 0x7a, 0x47, 0x01, 0x7b, + 0x75, 0x31, 0x94, 0x12, 0x7c, 0x15, 0x7a, 0x51, 0x31, 0x12, 0x7c, 0x15, 0x12, 0x65, 0x2b, 0x10, + 0x76, 0xc4, 0x22, 0xc2, 0x76, 0x2d, 0x23, 0x68, 0x78, 0x6d, 0x33, 0x80, 0x1a, 0x7e, 0x27, 0x01, + 0x9b, 0xbe, 0x24, 0x00, 0x00, 0x68, 0x6a, 0xbe, 0x27, 0x01, 0xbb, 0x28, 0x04, 0x7e, 0x27, 0x01, + 0xbb, 0x7e, 0x37, 0x01, 0x9b, 0x9d, 0x32, 0x7d, 0x02, 0x2e, 0x05, 0x54, 0x7a, 0x05, 0x54, 0x7a, + 0x37, 0x01, 0x9b, 0x7e, 0x37, 0x01, 0x7b, 0x7d, 0x43, 0x2d, 0x42, 0xbe, 0x44, 0x25, 0xe2, 0x38, + 0x13, 0x7a, 0x47, 0x01, 0x7b, 0x75, 0x31, 0x94, 0x12, 0x7c, 0x15, 0x7a, 0x51, 0x31, 0x12, 0x7c, + 0x15, 0x02, 0x65, 0x2b, 0x75, 0x31, 0x94, 0x12, 0x7c, 0x15, 0x7a, 0x51, 0x31, 0x12, 0x7c, 0x15, + 0x9e, 0x44, 0x25, 0xe3, 0x9d, 0x24, 0x12, 0x65, 0x2b, 0x7e, 0x34, 0x21, 0xe3, 0x7d, 0x24, 0x2d, + 0x43, 0x7a, 0x47, 0x01, 0x7b, 0x12, 0x65, 0x2b, 0xbe, 0x25, 0x20, 0x78, 0x03, 0x02, 0x5a, 0x8f, + 0x22, 0xd2, 0x0e, 0x7e, 0x04, 0x21, 0xe3, 0x7a, 0x07, 0x01, 0x7b, 0x7a, 0x07, 0x01, 0x8b, 0x75, + 0x31, 0x94, 0x12, 0x7c, 0x15, 0x75, 0x31, 0x00, 0x12, 0x7c, 0x15, 0x22, 0x75, 0x31, 0x92, 0x12, + 0x7c, 0x15, 0xd2, 0x04, 0x09, 0xb1, 0x00, 0x14, 0xca, 0xb8, 0x54, 0x82, 0xda, 0xb8, 0x78, 0x70, + 0x7e, 0x37, 0x01, 0xe1, 0x7e, 0x27, 0x01, 0xcb, 0x2e, 0x24, 0x00, 0x02, 0x2d, 0x32, 0xbe, 0x34, + 0x04, 0x00, 0x38, 0x3c, 0x7d, 0x02, 0x2e, 0x05, 0x46, 0x7a, 0x05, 0x46, 0x7a, 0x37, 0x01, 0xe1, + 0x7e, 0x37, 0x01, 0xdf, 0x7d, 0x43, 0x2d, 0x42, 0xbe, 0x44, 0x05, 0xe2, 0x38, 0x44, 0x7a, 0x47, + 0x01, 0xdf, 0x7e, 0x24, 0x06, 0x00, 0x2e, 0x27, 0x01, 0xcb, 0x1b, 0x38, 0x20, 0x0b, 0x35, 0x7a, + 0x51, 0x31, 0x12, 0x7c, 0x15, 0xbe, 0x50, 0x38, 0x78, 0x03, 0x02, 0x66, 0x16, 0x02, 0x65, 0xfb, + 0x75, 0x31, 0x99, 0x12, 0x7c, 0x15, 0x09, 0xb1, 0x00, 0x04, 0x54, 0xfa, 0x19, 0xb1, 0x00, 0x04, + 0x30, 0x3e, 0x0a, 0x09, 0xb1, 0x00, 0x10, 0x54, 0xfe, 0x19, 0xb1, 0x00, 0x10, 0xd2, 0x16, 0x22, + 0x80, 0x7d, 0x7a, 0x51, 0x31, 0x12, 0x7c, 0x15, 0x9e, 0x44, 0x05, 0xe3, 0x9d, 0x24, 0x7e, 0x64, + 0x06, 0x00, 0x2e, 0x67, 0x01, 0xcb, 0x9e, 0x24, 0x00, 0x02, 0x40, 0x17, 0x1b, 0x38, 0x60, 0x0b, + 0x35, 0x12, 0x65, 0xfb, 0x7e, 0x34, 0x01, 0xe3, 0x7d, 0x24, 0x2d, 0x43, 0x7a, 0x47, 0x01, 0xdf, + 0x02, 0x65, 0xfb, 0x7a, 0x39, 0xc0, 0x7e, 0x34, 0x01, 0xe3, 0x7a, 0x39, 0xd0, 0x0b, 0x34, 0x1b, + 0x44, 0x80, 0xe5, 0x9d, 0x32, 0x7c, 0xb6, 0x54, 0x0f, 0x23, 0x23, 0x23, 0x44, 0x06, 0x7a, 0x69, + 0xb0, 0x7a, 0x79, 0x70, 0x0b, 0x35, 0x75, 0x31, 0x93, 0x12, 0x7c, 0x15, 0x7a, 0x71, 0x31, 0x12, + 0x7c, 0x15, 0xbd, 0x04, 0x68, 0x29, 0x7a, 0x07, 0x01, 0xdf, 0x7e, 0x47, 0x01, 0xe1, 0x2d, 0x43, + 0x7a, 0x47, 0x01, 0xe1, 0x2e, 0x35, 0x46, 0x7a, 0x35, 0x46, 0x22, 0x09, 0xb1, 0x00, 0x14, 0x20, + 0xe0, 0x13, 0x22, 0x7e, 0x04, 0x01, 0xe3, 0x80, 0x2a, 0x7e, 0x04, 0x01, 0xe3, 0x80, 0x2c, 0x7e, + 0x04, 0x01, 0xe3, 0x80, 0xd1, 0xd2, 0x04, 0x7e, 0x07, 0x01, 0xe1, 0x7e, 0x24, 0x03, 0xfe, 0x9d, + 0x20, 0x28, 0x40, 0x7e, 0x07, 0x01, 0xdf, 0x7e, 0x44, 0x05, 0xe3, 0x7d, 0x60, 0x0b, 0x04, 0xbd, + 0x04, 0x68, 0xd0, 0x7d, 0x70, 0x0b, 0x04, 0xbd, 0x04, 0x68, 0xce, 0x7d, 0x54, 0x9d, 0x50, 0xbd, + 0x25, 0x40, 0x02, 0x7d, 0x25, 0x7d, 0x32, 0x09, 0xb1, 0x00, 0x14, 0xca, 0xb8, 0x54, 0x1f, 0xb4, + 0x01, 0x31, 0xda, 0xb8, 0x7e, 0x19, 0xb0, 0x7a, 0x09, 0xb0, 0x0b, 0x04, 0x1b, 0x24, 0x78, 0xe7, + 0x02, 0x5b, 0xf3, 0x75, 0x31, 0x99, 0x12, 0x7c, 0x15, 0x09, 0xb1, 0x00, 0x04, 0x54, 0xfa, 0x19, + 0xb1, 0x00, 0x04, 0x30, 0x3e, 0x0a, 0x09, 0xb1, 0x00, 0x10, 0x54, 0xfe, 0x19, 0xb1, 0x00, 0x10, + 0xd2, 0x16, 0x22, 0xda, 0xb8, 0x30, 0xe0, 0xd8, 0xbd, 0x32, 0x68, 0x07, 0xca, 0xb8, 0x12, 0x5b, + 0xf3, 0xda, 0xb8, 0x02, 0x5d, 0x40, 0x09, 0xb1, 0x00, 0x18, 0x7e, 0xa0, 0x88, 0x75, 0x31, 0x90, + 0x12, 0x7c, 0x15, 0xf5, 0x31, 0x12, 0x7c, 0x15, 0xa5, 0xfc, 0x5e, 0xb0, 0xf0, 0xa5, 0xfd, 0x09, + 0xb1, 0x00, 0x18, 0x4c, 0x4b, 0x5e, 0xb0, 0xf0, 0xbc, 0xb5, 0x78, 0xf1, 0x5e, 0x40, 0x0f, 0x4c, + 0x54, 0x7c, 0xb5, 0x5e, 0x50, 0x0b, 0x68, 0x2a, 0xa5, 0xfd, 0x5e, 0x50, 0x10, 0x68, 0x04, 0xd2, + 0x6e, 0x80, 0x02, 0xc2, 0x6e, 0xa5, 0xfd, 0x5e, 0x50, 0x20, 0x68, 0x04, 0xd2, 0x66, 0x80, 0x02, + 0xc2, 0x66, 0xa5, 0xfd, 0x5e, 0x50, 0x80, 0x68, 0x04, 0xd2, 0x5e, 0x80, 0x02, 0xc2, 0x5e, 0x12, + 0x43, 0x8e, 0x02, 0x62, 0x93, 0x75, 0x31, 0x91, 0x12, 0x7c, 0x15, 0x09, 0xb1, 0x00, 0x14, 0x7a, + 0xb1, 0x31, 0x12, 0x7c, 0x15, 0x20, 0xe0, 0x08, 0xd2, 0x04, 0x7e, 0xa0, 0x80, 0x02, 0x62, 0x93, + 0xd2, 0x04, 0x30, 0xe1, 0x06, 0x7e, 0xa0, 0x80, 0x12, 0x62, 0x93, 0xca, 0xb8, 0x5e, 0xb0, 0x1c, + 0xda, 0xb8, 0x68, 0x12, 0x7e, 0xa0, 0xc0, 0x09, 0x61, 0x00, 0x00, 0x12, 0x62, 0xb6, 0x09, 0xb1, + 0x00, 0x14, 0x20, 0xe0, 0xdb, 0x22, 0x02, 0x5c, 0x45, 0x75, 0x31, 0x95, 0x12, 0x7c, 0x15, 0x22, + 0x75, 0x31, 0x96, 0x12, 0x7c, 0x15, 0x22, 0x10, 0x0f, 0x01, 0x22, 0x20, 0x2f, 0x03, 0xd2, 0x0f, + 0x22, 0x75, 0x31, 0xa7, 0x12, 0x7c, 0x15, 0x7e, 0x14, 0x87, 0x00, 0x80, 0x06, 0x20, 0x2f, 0x03, + 0xd2, 0x0f, 0x22, 0x09, 0xb1, 0x00, 0x14, 0xca, 0xb8, 0x5e, 0xb0, 0x1e, 0xda, 0xb8, 0x68, 0x03, + 0x12, 0x60, 0x98, 0x20, 0xe6, 0x03, 0xd2, 0x0f, 0x22, 0x30, 0x37, 0x49, 0xd2, 0x77, 0x7e, 0x37, + 0x01, 0x9d, 0x7e, 0x27, 0x01, 0xbd, 0x9d, 0x32, 0x40, 0x31, 0x7d, 0x02, 0x2e, 0x05, 0x56, 0x7a, + 0x05, 0x56, 0x7a, 0x37, 0x01, 0x9d, 0x7e, 0x37, 0x01, 0x7d, 0x7d, 0x43, 0x2d, 0x42, 0xbe, 0x44, + 0x29, 0xe2, 0x38, 0x68, 0x7a, 0x47, 0x01, 0x7d, 0x75, 0x31, 0x94, 0x12, 0x7c, 0x15, 0x7a, 0x51, + 0x31, 0x12, 0x7c, 0x15, 0x12, 0x65, 0x2b, 0x10, 0x77, 0xc4, 0x22, 0xc2, 0x77, 0x2d, 0x23, 0x68, + 0x78, 0x6d, 0x33, 0x80, 0x1a, 0x7e, 0x27, 0x01, 0x9d, 0xbe, 0x24, 0x00, 0x00, 0x68, 0x6a, 0xbe, + 0x27, 0x01, 0xbd, 0x28, 0x04, 0x7e, 0x27, 0x01, 0xbd, 0x7e, 0x37, 0x01, 0x9d, 0x9d, 0x32, 0x7d, + 0x02, 0x2e, 0x05, 0x56, 0x7a, 0x05, 0x56, 0x7a, 0x37, 0x01, 0x9d, 0x7e, 0x37, 0x01, 0x7d, 0x7d, + 0x43, 0x2d, 0x42, 0xbe, 0x44, 0x29, 0xe2, 0x38, 0x13, 0x7a, 0x47, 0x01, 0x7d, 0x75, 0x31, 0x94, + 0x12, 0x7c, 0x15, 0x7a, 0x51, 0x31, 0x12, 0x7c, 0x15, 0x02, 0x65, 0x2b, 0x75, 0x31, 0x94, 0x12, + 0x7c, 0x15, 0x7a, 0x51, 0x31, 0x12, 0x7c, 0x15, 0x9e, 0x44, 0x29, 0xe3, 0x9d, 0x24, 0x12, 0x65, + 0x2b, 0x7e, 0x34, 0x25, 0xe3, 0x7d, 0x24, 0x2d, 0x43, 0x7a, 0x47, 0x01, 0x7d, 0x12, 0x65, 0x2b, + 0xbe, 0x25, 0x20, 0x78, 0x03, 0x02, 0x5d, 0xe7, 0x22, 0xd2, 0x0f, 0x7e, 0x04, 0x25, 0xe3, 0x7a, + 0x07, 0x01, 0x7d, 0x7a, 0x07, 0x01, 0x8d, 0x75, 0x31, 0x94, 0x12, 0x7c, 0x15, 0x75, 0x31, 0x00, + 0x12, 0x7c, 0x15, 0x22, 0x75, 0x31, 0x92, 0x12, 0x7c, 0x15, 0xd2, 0x04, 0x09, 0xb1, 0x00, 0x14, + 0xca, 0xb8, 0x54, 0x82, 0xda, 0xb8, 0x78, 0x70, 0x7e, 0x37, 0x01, 0xe1, 0x7e, 0x27, 0x01, 0xcd, + 0x2e, 0x24, 0x00, 0x02, 0x2d, 0x32, 0xbe, 0x34, 0x04, 0x00, 0x38, 0x3c, 0x7d, 0x02, 0x2e, 0x05, + 0x46, 0x7a, 0x05, 0x46, 0x7a, 0x37, 0x01, 0xe1, 0x7e, 0x37, 0x01, 0xdf, 0x7d, 0x43, 0x2d, 0x42, + 0xbe, 0x44, 0x05, 0xe2, 0x38, 0x44, 0x7a, 0x47, 0x01, 0xdf, 0x7e, 0x24, 0x07, 0x00, 0x2e, 0x27, + 0x01, 0xcd, 0x1b, 0x38, 0x20, 0x0b, 0x35, 0x7a, 0x51, 0x31, 0x12, 0x7c, 0x15, 0xbe, 0x50, 0x38, + 0x78, 0x03, 0x02, 0x66, 0x16, 0x02, 0x65, 0xfb, 0x75, 0x31, 0x99, 0x12, 0x7c, 0x15, 0x09, 0xb1, + 0x00, 0x04, 0x54, 0xfa, 0x19, 0xb1, 0x00, 0x04, 0x30, 0x3f, 0x0a, 0x09, 0xb1, 0x00, 0x10, 0x54, + 0xfe, 0x19, 0xb1, 0x00, 0x10, 0xd2, 0x17, 0x22, 0x80, 0x7d, 0x7a, 0x51, 0x31, 0x12, 0x7c, 0x15, + 0x9e, 0x44, 0x05, 0xe3, 0x9d, 0x24, 0x7e, 0x64, 0x07, 0x00, 0x2e, 0x67, 0x01, 0xcd, 0x9e, 0x24, + 0x00, 0x02, 0x40, 0x17, 0x1b, 0x38, 0x60, 0x0b, 0x35, 0x12, 0x65, 0xfb, 0x7e, 0x34, 0x01, 0xe3, + 0x7d, 0x24, 0x2d, 0x43, 0x7a, 0x47, 0x01, 0xdf, 0x02, 0x65, 0xfb, 0x7a, 0x39, 0xc0, 0x7e, 0x34, + 0x01, 0xe3, 0x7a, 0x39, 0xd0, 0x0b, 0x34, 0x1b, 0x44, 0x80, 0xe5, 0x9d, 0x32, 0x7c, 0xb6, 0x54, + 0x0f, 0x23, 0x23, 0x23, 0x44, 0x07, 0x7a, 0x69, 0xb0, 0x7a, 0x79, 0x70, 0x0b, 0x35, 0x75, 0x31, + 0x93, 0x12, 0x7c, 0x15, 0x7a, 0x71, 0x31, 0x12, 0x7c, 0x15, 0xbd, 0x04, 0x68, 0x29, 0x7a, 0x07, + 0x01, 0xdf, 0x7e, 0x47, 0x01, 0xe1, 0x2d, 0x43, 0x7a, 0x47, 0x01, 0xe1, 0x2e, 0x35, 0x46, 0x7a, + 0x35, 0x46, 0x22, 0x09, 0xb1, 0x00, 0x14, 0x20, 0xe0, 0x13, 0x22, 0x7e, 0x04, 0x01, 0xe3, 0x80, + 0x2a, 0x7e, 0x04, 0x01, 0xe3, 0x80, 0x2c, 0x7e, 0x04, 0x01, 0xe3, 0x80, 0xd1, 0xd2, 0x04, 0x7e, + 0x07, 0x01, 0xe1, 0x7e, 0x24, 0x03, 0xfe, 0x9d, 0x20, 0x28, 0x40, 0x7e, 0x07, 0x01, 0xdf, 0x7e, + 0x44, 0x05, 0xe3, 0x7d, 0x60, 0x0b, 0x04, 0xbd, 0x04, 0x68, 0xd0, 0x7d, 0x70, 0x0b, 0x04, 0xbd, + 0x04, 0x68, 0xce, 0x7d, 0x54, 0x9d, 0x50, 0xbd, 0x25, 0x40, 0x02, 0x7d, 0x25, 0x7d, 0x32, 0x09, + 0xb1, 0x00, 0x14, 0xca, 0xb8, 0x54, 0x1f, 0xb4, 0x01, 0x31, 0xda, 0xb8, 0x7e, 0x19, 0xb0, 0x7a, + 0x09, 0xb0, 0x0b, 0x04, 0x1b, 0x24, 0x78, 0xe7, 0x02, 0x5f, 0x4b, 0x75, 0x31, 0x99, 0x12, 0x7c, + 0x15, 0x09, 0xb1, 0x00, 0x04, 0x54, 0xfa, 0x19, 0xb1, 0x00, 0x04, 0x30, 0x3f, 0x0a, 0x09, 0xb1, + 0x00, 0x10, 0x54, 0xfe, 0x19, 0xb1, 0x00, 0x10, 0xd2, 0x17, 0x22, 0xda, 0xb8, 0x30, 0xe0, 0xd8, + 0xbd, 0x32, 0x68, 0x07, 0xca, 0xb8, 0x12, 0x5f, 0x4b, 0xda, 0xb8, 0x02, 0x60, 0x98, 0x09, 0xb1, + 0x00, 0x18, 0x7e, 0xa0, 0x88, 0x75, 0x31, 0x90, 0x12, 0x7c, 0x15, 0xf5, 0x31, 0x12, 0x7c, 0x15, + 0xa5, 0xfc, 0x5e, 0xb0, 0xf0, 0xa5, 0xfd, 0x09, 0xb1, 0x00, 0x18, 0x4c, 0x4b, 0x5e, 0xb0, 0xf0, + 0xbc, 0xb5, 0x78, 0xf1, 0x5e, 0x40, 0x0f, 0x4c, 0x54, 0x7c, 0xb5, 0x5e, 0x50, 0x0b, 0x68, 0x2a, + 0xa5, 0xfd, 0x5e, 0x50, 0x10, 0x68, 0x04, 0xd2, 0x6f, 0x80, 0x02, 0xc2, 0x6f, 0xa5, 0xfd, 0x5e, + 0x50, 0x20, 0x68, 0x04, 0xd2, 0x67, 0x80, 0x02, 0xc2, 0x67, 0xa5, 0xfd, 0x5e, 0x50, 0x80, 0x68, + 0x04, 0xd2, 0x5f, 0x80, 0x02, 0xc2, 0x5f, 0x12, 0x43, 0xaf, 0x02, 0x62, 0x93, 0x75, 0x31, 0x91, + 0x12, 0x7c, 0x15, 0x09, 0xb1, 0x00, 0x14, 0x7a, 0xb1, 0x31, 0x12, 0x7c, 0x15, 0x20, 0xe0, 0x08, + 0xd2, 0x04, 0x7e, 0xa0, 0x80, 0x02, 0x62, 0x93, 0xd2, 0x04, 0x30, 0xe1, 0x06, 0x7e, 0xa0, 0x80, + 0x12, 0x62, 0x93, 0xca, 0xb8, 0x5e, 0xb0, 0x1c, 0xda, 0xb8, 0x68, 0x12, 0x7e, 0xa0, 0xc0, 0x09, + 0x61, 0x00, 0x00, 0x12, 0x62, 0xb6, 0x09, 0xb1, 0x00, 0x14, 0x20, 0xe0, 0xdb, 0x22, 0x02, 0x5f, + 0x9d, 0x75, 0x31, 0x95, 0x12, 0x7c, 0x15, 0x22, 0x75, 0x31, 0x96, 0x12, 0x7c, 0x15, 0x22, 0x7c, + 0x02, 0x7e, 0x14, 0x80, 0x00, 0x4c, 0x20, 0x09, 0xb1, 0x00, 0x18, 0xa5, 0xfd, 0x5e, 0x50, 0x10, + 0x68, 0x04, 0xd2, 0x68, 0x80, 0x02, 0xc2, 0x68, 0xa5, 0xfd, 0x5e, 0x50, 0x20, 0x68, 0x04, 0xd2, + 0x60, 0x80, 0x02, 0xc2, 0x60, 0xa5, 0xfd, 0x5e, 0x50, 0x80, 0x68, 0x04, 0xd2, 0x58, 0x80, 0x02, + 0xc2, 0x58, 0x02, 0x62, 0x7f, 0x7c, 0x02, 0x7e, 0x14, 0x80, 0x00, 0x4c, 0x20, 0x09, 0xb1, 0x00, + 0x18, 0xa5, 0xfd, 0x5e, 0x50, 0x10, 0x68, 0x04, 0xd2, 0x69, 0x80, 0x02, 0xc2, 0x69, 0xa5, 0xfd, + 0x5e, 0x50, 0x20, 0x68, 0x04, 0xd2, 0x61, 0x80, 0x02, 0xc2, 0x61, 0xa5, 0xfd, 0x5e, 0x50, 0x80, + 0x68, 0x04, 0xd2, 0x59, 0x80, 0x02, 0xc2, 0x59, 0x02, 0x62, 0x7f, 0x7c, 0x02, 0x7e, 0x14, 0x80, + 0x00, 0x4c, 0x20, 0x09, 0xb1, 0x00, 0x18, 0xa5, 0xfd, 0x5e, 0x50, 0x10, 0x68, 0x04, 0xd2, 0x6a, + 0x80, 0x02, 0xc2, 0x6a, 0xa5, 0xfd, 0x5e, 0x50, 0x20, 0x68, 0x04, 0xd2, 0x62, 0x80, 0x02, 0xc2, + 0x62, 0xa5, 0xfd, 0x5e, 0x50, 0x80, 0x68, 0x04, 0xd2, 0x5a, 0x80, 0x02, 0xc2, 0x5a, 0x02, 0x62, + 0x7f, 0x7c, 0x02, 0x7e, 0x14, 0x80, 0x00, 0x4c, 0x20, 0x09, 0xb1, 0x00, 0x18, 0xa5, 0xfd, 0x5e, + 0x50, 0x10, 0x68, 0x04, 0xd2, 0x6b, 0x80, 0x02, 0xc2, 0x6b, 0xa5, 0xfd, 0x5e, 0x50, 0x20, 0x68, + 0x04, 0xd2, 0x63, 0x80, 0x02, 0xc2, 0x63, 0xa5, 0xfd, 0x5e, 0x50, 0x80, 0x68, 0x04, 0xd2, 0x5b, + 0x80, 0x02, 0xc2, 0x5b, 0x02, 0x62, 0x7f, 0x7c, 0x02, 0x7e, 0x14, 0x80, 0x00, 0x4c, 0x20, 0x09, + 0xb1, 0x00, 0x18, 0xa5, 0xfd, 0x5e, 0x50, 0x10, 0x68, 0x04, 0xd2, 0x6c, 0x80, 0x02, 0xc2, 0x6c, + 0xa5, 0xfd, 0x5e, 0x50, 0x20, 0x68, 0x04, 0xd2, 0x64, 0x80, 0x02, 0xc2, 0x64, 0xa5, 0xfd, 0x5e, + 0x50, 0x80, 0x68, 0x04, 0xd2, 0x5c, 0x80, 0x02, 0xc2, 0x5c, 0x02, 0x62, 0x7f, 0x7c, 0x02, 0x7e, + 0x14, 0x80, 0x00, 0x4c, 0x20, 0x09, 0xb1, 0x00, 0x18, 0xa5, 0xfd, 0x5e, 0x50, 0x10, 0x68, 0x04, + 0xd2, 0x6d, 0x80, 0x02, 0xc2, 0x6d, 0xa5, 0xfd, 0x5e, 0x50, 0x20, 0x68, 0x04, 0xd2, 0x65, 0x80, + 0x02, 0xc2, 0x65, 0xa5, 0xfd, 0x5e, 0x50, 0x80, 0x68, 0x04, 0xd2, 0x5d, 0x80, 0x02, 0xc2, 0x5d, + 0x02, 0x62, 0x7f, 0x7c, 0x02, 0x7e, 0x14, 0x80, 0x00, 0x4c, 0x20, 0x09, 0xb1, 0x00, 0x18, 0xa5, + 0xfd, 0x5e, 0x50, 0x10, 0x68, 0x04, 0xd2, 0x6e, 0x80, 0x02, 0xc2, 0x6e, 0xa5, 0xfd, 0x5e, 0x50, + 0x20, 0x68, 0x04, 0xd2, 0x66, 0x80, 0x02, 0xc2, 0x66, 0xa5, 0xfd, 0x5e, 0x50, 0x80, 0x68, 0x04, + 0xd2, 0x5e, 0x80, 0x02, 0xc2, 0x5e, 0x02, 0x62, 0x7f, 0x7c, 0x02, 0x7e, 0x14, 0x80, 0x00, 0x4c, + 0x20, 0x09, 0xb1, 0x00, 0x18, 0xa5, 0xfd, 0x5e, 0x50, 0x10, 0x68, 0x04, 0xd2, 0x6f, 0x80, 0x02, + 0xc2, 0x6f, 0xa5, 0xfd, 0x5e, 0x50, 0x20, 0x68, 0x04, 0xd2, 0x67, 0x80, 0x02, 0xc2, 0x67, 0xa5, + 0xfd, 0x5e, 0x50, 0x80, 0x68, 0x04, 0xd2, 0x5f, 0x80, 0x02, 0xc2, 0x5f, 0x02, 0x62, 0x7f, 0x54, + 0xf0, 0xc4, 0xa5, 0xff, 0xc4, 0xa5, 0x4f, 0x75, 0x31, 0x90, 0x12, 0x7c, 0x15, 0xf5, 0x31, 0x12, + 0x7c, 0x15, 0x22, 0xca, 0x19, 0x5e, 0x20, 0x07, 0x4c, 0xa2, 0x7e, 0x74, 0x29, 0xe3, 0xca, 0x79, + 0x7a, 0x79, 0xa0, 0x0b, 0x74, 0x7a, 0x79, 0xb0, 0x0b, 0x74, 0xda, 0x79, 0x7e, 0x30, 0x02, 0x7e, + 0x64, 0x00, 0x02, 0x02, 0x62, 0xde, 0xca, 0x19, 0x5e, 0x20, 0x07, 0x4c, 0xa2, 0x7e, 0x74, 0x29, + 0xe3, 0xca, 0x79, 0x7a, 0x79, 0xa0, 0x0b, 0x74, 0x7a, 0x79, 0xb0, 0x0b, 0x74, 0x7a, 0x79, 0x60, + 0x0b, 0x74, 0xda, 0x79, 0x7e, 0x30, 0x03, 0x7e, 0x64, 0x00, 0x03, 0x02, 0x62, 0xde, 0xd2, 0x04, + 0x7e, 0x27, 0x01, 0xe1, 0x2d, 0x26, 0xbe, 0x24, 0x04, 0x00, 0x38, 0x2e, 0x7e, 0x07, 0x01, 0xdf, + 0x7e, 0x44, 0x05, 0xe3, 0x7e, 0x79, 0xa0, 0x7a, 0x09, 0xa0, 0x0b, 0x04, 0x0b, 0x74, 0xbd, 0x04, + 0x68, 0x23, 0xa5, 0xdb, 0xef, 0x7a, 0x27, 0x01, 0xe1, 0x7e, 0x25, 0x46, 0x2d, 0x26, 0x7a, 0x25, + 0x46, 0x7a, 0x07, 0x01, 0xdf, 0xda, 0x19, 0xc2, 0xd7, 0x22, 0x75, 0x31, 0x9a, 0x12, 0x7c, 0x15, + 0xda, 0x19, 0xd2, 0xd7, 0x22, 0x7e, 0x04, 0x01, 0xe3, 0x80, 0xd7, 0x48, 0xb6, 0x46, 0x25, 0x47, + 0x1c, 0x49, 0x15, 0x44, 0xc6, 0x44, 0xc6, 0x48, 0x1b, 0x44, 0xc6, 0x49, 0x59, 0x44, 0xc6, 0x44, + 0xc6, 0x44, 0xc6, 0x44, 0xc6, 0x44, 0xc6, 0x44, 0xc6, 0x44, 0xc6, 0x49, 0x60, 0x44, 0xc6, 0x44, + 0xc6, 0x44, 0xc6, 0x44, 0xc6, 0x44, 0xc6, 0x44, 0xc6, 0x44, 0xc6, 0x44, 0xc6, 0x44, 0xc6, 0x44, + 0xc6, 0x44, 0xc6, 0x44, 0xc6, 0x44, 0xc6, 0x44, 0xc6, 0x44, 0xc6, 0x4c, 0x0e, 0x49, 0x7d, 0x4a, + 0x74, 0x4c, 0x6d, 0x44, 0xc6, 0x44, 0xc6, 0x4b, 0x73, 0x44, 0xc6, 0x4c, 0xb1, 0x44, 0xc6, 0x44, + 0xc6, 0x44, 0xc6, 0x44, 0xc6, 0x44, 0xc6, 0x44, 0xc6, 0x44, 0xc6, 0x4c, 0xb8, 0x44, 0xc6, 0x44, + 0xc6, 0x44, 0xc6, 0x44, 0xc6, 0x44, 0xc6, 0x44, 0xc6, 0x44, 0xc6, 0x44, 0xc6, 0x44, 0xc6, 0x44, + 0xc6, 0x44, 0xc6, 0x44, 0xc6, 0x44, 0xc6, 0x44, 0xc6, 0x44, 0xc6, 0x4f, 0x66, 0x4c, 0xd5, 0x4d, + 0xcc, 0x4f, 0xc5, 0x44, 0xc6, 0x44, 0xc6, 0x4e, 0xcb, 0x44, 0xc6, 0x50, 0x09, 0x44, 0xc6, 0x44, + 0xc6, 0x44, 0xc6, 0x44, 0xc6, 0x44, 0xc6, 0x44, 0xc6, 0x44, 0xc6, 0x50, 0x10, 0x44, 0xc6, 0x44, + 0xc6, 0x44, 0xc6, 0x44, 0xc6, 0x44, 0xc6, 0x44, 0xc6, 0x44, 0xc6, 0x44, 0xc6, 0x44, 0xc6, 0x44, + 0xc6, 0x44, 0xc6, 0x44, 0xc6, 0x44, 0xc6, 0x44, 0xc6, 0x44, 0xc6, 0x52, 0xbe, 0x50, 0x2d, 0x51, + 0x24, 0x53, 0x1d, 0x44, 0xc6, 0x44, 0xc6, 0x52, 0x23, 0x44, 0xc6, 0x53, 0x61, 0x44, 0xc6, 0x44, + 0xc6, 0x44, 0xc6, 0x44, 0xc6, 0x44, 0xc6, 0x44, 0xc6, 0x44, 0xc6, 0x53, 0x68, 0x44, 0xc6, 0x44, + 0xc6, 0x44, 0xc6, 0x44, 0xc6, 0x44, 0xc6, 0x44, 0xc6, 0x44, 0xc6, 0x44, 0xc6, 0x44, 0xc6, 0x44, + 0xc6, 0x44, 0xc6, 0x44, 0xc6, 0x44, 0xc6, 0x44, 0xc6, 0x44, 0xc6, 0x56, 0x16, 0x53, 0x85, 0x54, + 0x7c, 0x56, 0x75, 0x44, 0xc6, 0x44, 0xc6, 0x55, 0x7b, 0x44, 0xc6, 0x56, 0xb9, 0x44, 0xc6, 0x44, + 0xc6, 0x44, 0xc6, 0x44, 0xc6, 0x44, 0xc6, 0x44, 0xc6, 0x44, 0xc6, 0x56, 0xc0, 0x44, 0xc6, 0x44, + 0xc6, 0x44, 0xc6, 0x44, 0xc6, 0x44, 0xc6, 0x44, 0xc6, 0x44, 0xc6, 0x44, 0xc6, 0x44, 0xc6, 0x44, + 0xc6, 0x44, 0xc6, 0x44, 0xc6, 0x44, 0xc6, 0x44, 0xc6, 0x44, 0xc6, 0x59, 0x6e, 0x56, 0xdd, 0x57, + 0xd4, 0x59, 0xcd, 0x44, 0xc6, 0x44, 0xc6, 0x58, 0xd3, 0x44, 0xc6, 0x5a, 0x11, 0x44, 0xc6, 0x44, + 0xc6, 0x44, 0xc6, 0x44, 0xc6, 0x44, 0xc6, 0x44, 0xc6, 0x44, 0xc6, 0x5a, 0x18, 0x44, 0xc6, 0x44, + 0xc6, 0x44, 0xc6, 0x44, 0xc6, 0x44, 0xc6, 0x44, 0xc6, 0x44, 0xc6, 0x44, 0xc6, 0x44, 0xc6, 0x44, + 0xc6, 0x44, 0xc6, 0x44, 0xc6, 0x44, 0xc6, 0x44, 0xc6, 0x44, 0xc6, 0x5c, 0xc6, 0x5a, 0x35, 0x5b, + 0x2c, 0x5d, 0x25, 0x44, 0xc6, 0x44, 0xc6, 0x5c, 0x2b, 0x44, 0xc6, 0x5d, 0x69, 0x44, 0xc6, 0x44, + 0xc6, 0x44, 0xc6, 0x44, 0xc6, 0x44, 0xc6, 0x44, 0xc6, 0x44, 0xc6, 0x5d, 0x70, 0x44, 0xc6, 0x44, + 0xc6, 0x44, 0xc6, 0x44, 0xc6, 0x44, 0xc6, 0x44, 0xc6, 0x44, 0xc6, 0x44, 0xc6, 0x44, 0xc6, 0x44, + 0xc6, 0x44, 0xc6, 0x44, 0xc6, 0x44, 0xc6, 0x44, 0xc6, 0x44, 0xc6, 0x60, 0x1e, 0x5d, 0x8d, 0x5e, + 0x84, 0x60, 0x7d, 0x44, 0xc6, 0x44, 0xc6, 0x5f, 0x83, 0x44, 0xc6, 0x60, 0xc1, 0x44, 0xc6, 0x44, + 0xc6, 0x44, 0xc6, 0x44, 0xc6, 0x44, 0xc6, 0x44, 0xc6, 0x44, 0xc6, 0x60, 0xc8, 0x44, 0xc6, 0x44, + 0xc6, 0x44, 0xc6, 0x44, 0xc6, 0x44, 0xc6, 0x44, 0xc6, 0x44, 0xc6, 0x44, 0xc6, 0x44, 0xc6, 0x44, + 0xc6, 0x44, 0xc6, 0x44, 0xc6, 0x44, 0xc6, 0x44, 0xc6, 0x44, 0xc6, 0xca, 0x29, 0x1e, 0x50, 0x40, + 0x0d, 0x7e, 0x54, 0x0b, 0x10, 0x9c, 0xb5, 0xa4, 0x2e, 0x54, 0x65, 0x48, 0x89, 0x54, 0x7e, 0x39, + 0x00, 0x7a, 0x19, 0x00, 0x0b, 0x34, 0x80, 0xe9, 0x0b, 0x38, 0x00, 0x7a, 0x19, 0x00, 0x7a, 0x19, 0x10, 0x0b, 0x35, 0x0b, 0x38, 0x00, 0x7a, 0x19, 0x00, 0x7a, 0x19, 0x10, 0x0b, 0x35, 0x0b, 0x38, 0x00, 0x7a, 0x19, 0x00, 0x7a, 0x19, 0x10, 0x0b, 0x35, 0x0b, 0x38, 0x00, 0x7a, 0x19, 0x00, 0x7a, 0x19, 0x10, 0x0b, 0x35, 0x0b, 0x38, 0x00, 0x7a, 0x19, 0x00, 0x7a, 0x19, 0x10, 0x0b, 0x35, 0x0b, @@ -744,12 +832,12 @@ static unsigned char IMAGE_ARRAY_NAME[] = { 0x7a, 0x19, 0x10, 0x0b, 0x35, 0x0b, 0x38, 0x00, 0x7a, 0x19, 0x00, 0x7a, 0x19, 0x10, 0x0b, 0x35, 0x0b, 0x38, 0x00, 0x7a, 0x19, 0x00, 0x7a, 0x19, 0x10, 0x0b, 0x35, 0x0b, 0x38, 0x00, 0x7a, 0x19, 0x00, 0x7a, 0x19, 0x10, 0x0b, 0x35, 0x0b, 0x38, 0x00, 0x7a, 0x19, 0x00, 0x7a, 0x19, 0x10, 0x0b, - 0x35, 0xda, 0x29, 0x22, 0x1e, 0x50, 0x40, 0x0d, 0x7e, 0x54, 0x0b, 0x1c, 0x9c, 0xb5, 0xa4, 0x2e, - 0x54, 0x69, 0x1f, 0x89, 0x54, 0x7e, 0x19, 0x00, 0x7a, 0x39, 0x00, 0x0b, 0x34, 0x80, 0xe9, 0x7e, - 0x19, 0x00, 0x7e, 0x19, 0x10, 0x1b, 0x38, 0x00, 0x0b, 0x35, 0x7e, 0x19, 0x00, 0x7e, 0x19, 0x10, - 0x1b, 0x38, 0x00, 0x0b, 0x35, 0x7e, 0x19, 0x00, 0x7e, 0x19, 0x10, 0x1b, 0x38, 0x00, 0x0b, 0x35, - 0x7e, 0x19, 0x00, 0x7e, 0x19, 0x10, 0x1b, 0x38, 0x00, 0x0b, 0x35, 0x7e, 0x19, 0x00, 0x7e, 0x19, - 0x10, 0x1b, 0x38, 0x00, 0x0b, 0x35, 0x7e, 0x19, 0x00, 0x7e, 0x19, 0x10, 0x1b, 0x38, 0x00, 0x0b, + 0x35, 0x0b, 0x38, 0x00, 0x7a, 0x19, 0x00, 0x7a, 0x19, 0x10, 0x0b, 0x35, 0x0b, 0x38, 0x00, 0x7a, + 0x19, 0x00, 0x7a, 0x19, 0x10, 0x0b, 0x35, 0x0b, 0x38, 0x00, 0x7a, 0x19, 0x00, 0x7a, 0x19, 0x10, + 0x0b, 0x35, 0x0b, 0x38, 0x00, 0x7a, 0x19, 0x00, 0x7a, 0x19, 0x10, 0x0b, 0x35, 0x0b, 0x38, 0x00, + 0x7a, 0x19, 0x00, 0x7a, 0x19, 0x10, 0x0b, 0x35, 0xda, 0x29, 0x22, 0x1e, 0x50, 0x40, 0x0d, 0x7e, + 0x54, 0x0b, 0x1c, 0x9c, 0xb5, 0xa4, 0x2e, 0x54, 0x66, 0x16, 0x89, 0x54, 0x7e, 0x19, 0x00, 0x7a, + 0x39, 0x00, 0x0b, 0x34, 0x80, 0xe9, 0x7e, 0x19, 0x00, 0x7e, 0x19, 0x10, 0x1b, 0x38, 0x00, 0x0b, 0x35, 0x7e, 0x19, 0x00, 0x7e, 0x19, 0x10, 0x1b, 0x38, 0x00, 0x0b, 0x35, 0x7e, 0x19, 0x00, 0x7e, 0x19, 0x10, 0x1b, 0x38, 0x00, 0x0b, 0x35, 0x7e, 0x19, 0x00, 0x7e, 0x19, 0x10, 0x1b, 0x38, 0x00, 0x0b, 0x35, 0x7e, 0x19, 0x00, 0x7e, 0x19, 0x10, 0x1b, 0x38, 0x00, 0x0b, 0x35, 0x7e, 0x19, 0x00, @@ -765,157 +853,176 @@ static unsigned char IMAGE_ARRAY_NAME[] = { 0x19, 0x10, 0x1b, 0x38, 0x00, 0x0b, 0x35, 0x7e, 0x19, 0x00, 0x7e, 0x19, 0x10, 0x1b, 0x38, 0x00, 0x0b, 0x35, 0x7e, 0x19, 0x00, 0x7e, 0x19, 0x10, 0x1b, 0x38, 0x00, 0x0b, 0x35, 0x7e, 0x19, 0x00, 0x7e, 0x19, 0x10, 0x1b, 0x38, 0x00, 0x0b, 0x35, 0x7e, 0x19, 0x00, 0x7e, 0x19, 0x10, 0x1b, 0x38, - 0x00, 0x0b, 0x35, 0x22, 0x6a, 0x9b, 0x6c, 0x4f, 0x6c, 0x67, 0x6c, 0x82, 0x6d, 0x1d, 0x6d, 0xb5, - 0x6d, 0xd0, 0x6e, 0x62, 0x6d, 0xeb, 0x6e, 0x2c, 0x7c, 0xb3, 0xbe, 0xb0, 0x09, 0x28, 0x14, 0x75, - 0x2f, 0x09, 0x12, 0x7e, 0x30, 0x75, 0x57, 0x10, 0xc0, 0xf1, 0x75, 0xf1, 0x01, 0x43, 0xe1, 0xc0, - 0xd0, 0xf1, 0x22, 0xc0, 0xa8, 0xc2, 0xaf, 0x23, 0x6c, 0xaa, 0x2e, 0x54, 0x6a, 0x54, 0x0b, 0x58, - 0x50, 0x89, 0x54, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x75, 0x2f, 0xb0, 0x12, 0x7e, - 0x30, 0x0a, 0x22, 0x09, 0xb2, 0x6a, 0x93, 0x42, 0x24, 0xd0, 0xa8, 0x22, 0x7c, 0xb2, 0x23, 0x0a, - 0x3b, 0x49, 0x33, 0x6a, 0xb7, 0x89, 0x34, 0x6a, 0xc7, 0x6a, 0xf5, 0x6b, 0x23, 0x6b, 0x51, 0x6b, - 0x7f, 0x6b, 0xad, 0x6b, 0xdb, 0x6c, 0x09, 0x12, 0x41, 0xa9, 0xd2, 0x28, 0xd2, 0x08, 0xc2, 0x40, - 0xc2, 0x48, 0xc2, 0x38, 0xc2, 0x30, 0x6d, 0x00, 0x7a, 0x03, 0x01, 0xb9, 0x7e, 0x04, 0x00, 0x20, - 0x7a, 0x07, 0x01, 0x99, 0x7e, 0x04, 0x00, 0x38, 0x7a, 0x07, 0x01, 0xa9, 0x12, 0x41, 0x16, 0x12, - 0x64, 0x40, 0x02, 0x6c, 0x37, 0x12, 0x41, 0xc6, 0xd2, 0x29, 0xd2, 0x09, 0xc2, 0x41, 0xc2, 0x49, - 0xc2, 0x39, 0xc2, 0x31, 0x6d, 0x00, 0x7a, 0x03, 0x01, 0xba, 0x7e, 0x04, 0x00, 0x20, 0x7a, 0x07, - 0x01, 0x9b, 0x7e, 0x04, 0x00, 0x38, 0x7a, 0x07, 0x01, 0xab, 0x12, 0x41, 0x16, 0x12, 0x64, 0x69, - 0x02, 0x6c, 0x37, 0x12, 0x41, 0xe3, 0xd2, 0x2a, 0xd2, 0x0a, 0xc2, 0x42, 0xc2, 0x4a, 0xc2, 0x3a, - 0xc2, 0x32, 0x6d, 0x00, 0x7a, 0x03, 0x01, 0xbb, 0x7e, 0x04, 0x00, 0x20, 0x7a, 0x07, 0x01, 0x9d, - 0x7e, 0x04, 0x00, 0x38, 0x7a, 0x07, 0x01, 0xad, 0x12, 0x41, 0x16, 0x12, 0x64, 0x92, 0x02, 0x6c, - 0x37, 0x12, 0x42, 0x00, 0xd2, 0x2b, 0xd2, 0x0b, 0xc2, 0x43, 0xc2, 0x4b, 0xc2, 0x3b, 0xc2, 0x33, - 0x6d, 0x00, 0x7a, 0x03, 0x01, 0xbc, 0x7e, 0x04, 0x00, 0x20, 0x7a, 0x07, 0x01, 0x9f, 0x7e, 0x04, - 0x00, 0x38, 0x7a, 0x07, 0x01, 0xaf, 0x12, 0x41, 0x16, 0x12, 0x64, 0xbb, 0x02, 0x6c, 0x37, 0x12, - 0x42, 0x1d, 0xd2, 0x2c, 0xd2, 0x0c, 0xc2, 0x44, 0xc2, 0x4c, 0xc2, 0x3c, 0xc2, 0x34, 0x6d, 0x00, - 0x7a, 0x03, 0x01, 0xbd, 0x7e, 0x04, 0x00, 0x20, 0x7a, 0x07, 0x01, 0xa1, 0x7e, 0x04, 0x00, 0x38, - 0x7a, 0x07, 0x01, 0xb1, 0x12, 0x41, 0x16, 0x12, 0x64, 0xe4, 0x02, 0x6c, 0x37, 0x12, 0x42, 0x3a, - 0xd2, 0x2d, 0xd2, 0x0d, 0xc2, 0x45, 0xc2, 0x4d, 0xc2, 0x3d, 0xc2, 0x35, 0x6d, 0x00, 0x7a, 0x03, - 0x01, 0xbe, 0x7e, 0x04, 0x00, 0x20, 0x7a, 0x07, 0x01, 0xa3, 0x7e, 0x04, 0x00, 0x38, 0x7a, 0x07, - 0x01, 0xb3, 0x12, 0x41, 0x16, 0x12, 0x65, 0x0d, 0x02, 0x6c, 0x37, 0x12, 0x42, 0x57, 0xd2, 0x2e, - 0xd2, 0x0e, 0xc2, 0x46, 0xc2, 0x4e, 0xc2, 0x3e, 0xc2, 0x36, 0x6d, 0x00, 0x7a, 0x03, 0x01, 0xbf, - 0x7e, 0x04, 0x00, 0x20, 0x7a, 0x07, 0x01, 0xa5, 0x7e, 0x04, 0x00, 0x38, 0x7a, 0x07, 0x01, 0xb5, - 0x12, 0x41, 0x16, 0x12, 0x65, 0x36, 0x02, 0x6c, 0x37, 0x12, 0x42, 0x74, 0xd2, 0x2f, 0xd2, 0x0f, - 0xc2, 0x47, 0xc2, 0x4f, 0xc2, 0x3f, 0xc2, 0x37, 0x6d, 0x00, 0x7a, 0x03, 0x01, 0xc0, 0x7e, 0x04, - 0x00, 0x20, 0x7a, 0x07, 0x01, 0xa7, 0x7e, 0x04, 0x00, 0x38, 0x7a, 0x07, 0x01, 0xb7, 0x12, 0x41, - 0x16, 0x12, 0x65, 0x5f, 0x02, 0x6c, 0x37, 0x7e, 0xa0, 0xd0, 0x7e, 0x60, 0x0f, 0x12, 0x65, 0xbf, - 0x40, 0x0c, 0xc0, 0xf1, 0x75, 0xf1, 0x01, 0x12, 0x71, 0xe9, 0xd0, 0xf1, 0xc2, 0xd7, 0x22, 0x75, - 0x2f, 0xb1, 0x12, 0x7e, 0x30, 0x0a, 0x52, 0x23, 0x6d, 0x00, 0x59, 0x05, 0x00, 0x32, 0x12, 0x41, - 0x72, 0x12, 0x41, 0x8e, 0xd0, 0xa8, 0x22, 0x75, 0x2f, 0xb2, 0x12, 0x7e, 0x30, 0x0a, 0x22, 0x09, - 0xb2, 0x6a, 0x93, 0x42, 0x23, 0x7e, 0xb0, 0x9c, 0x19, 0xb2, 0x01, 0xb9, 0x12, 0x45, 0x74, 0xd0, - 0xa8, 0x22, 0x7e, 0x04, 0x80, 0x00, 0x4c, 0x02, 0x09, 0x30, 0x00, 0x0c, 0x74, 0xbf, 0x19, 0xb0, - 0x00, 0x0c, 0x09, 0xb0, 0x00, 0x08, 0x19, 0x30, 0x00, 0x0c, 0x7c, 0x74, 0x5e, 0x70, 0x01, 0x68, - 0x12, 0x44, 0x40, 0xca, 0xb8, 0x09, 0xb0, 0x00, 0x10, 0x44, 0x02, 0x19, 0xb0, 0x00, 0x10, 0xda, - 0xb8, 0x80, 0x02, 0x54, 0xbf, 0x7c, 0x74, 0x5e, 0x70, 0x08, 0x68, 0x04, 0x44, 0x08, 0x80, 0x02, - 0x54, 0xf7, 0x09, 0x30, 0x00, 0x0c, 0xca, 0xb8, 0x74, 0xbf, 0x19, 0xb0, 0x00, 0x0c, 0xda, 0xb8, - 0x19, 0xb0, 0x00, 0x08, 0x19, 0x30, 0x00, 0x0c, 0x0a, 0x62, 0x09, 0xb6, 0x6a, 0x93, 0x3e, 0x20, - 0x0a, 0x62, 0x7c, 0x74, 0x5e, 0x70, 0x02, 0x68, 0x20, 0x42, 0x27, 0xca, 0xb8, 0x74, 0x61, 0x19, - 0xb0, 0x00, 0x08, 0x7e, 0x44, 0x00, 0x10, 0x59, 0x46, 0x01, 0xa9, 0x09, 0xb0, 0x00, 0x10, 0x44, - 0x01, 0x19, 0xb0, 0x00, 0x10, 0xda, 0xb8, 0x80, 0x11, 0xf4, 0x52, 0x27, 0x74, 0xa1, 0x19, 0xb0, - 0x00, 0x08, 0x7e, 0x44, 0x00, 0x38, 0x59, 0x46, 0x01, 0xa9, 0xd0, 0xa8, 0x22, 0x7c, 0x74, 0x7e, - 0x04, 0x80, 0x00, 0x4c, 0x02, 0x09, 0x30, 0x00, 0x0c, 0x74, 0xbf, 0x19, 0xb0, 0x00, 0x0c, 0x09, - 0xb0, 0x00, 0x08, 0x7c, 0x74, 0x5e, 0x70, 0x01, 0x68, 0x04, 0x44, 0x80, 0x80, 0x02, 0x54, 0x7f, - 0x7c, 0x74, 0x5e, 0x70, 0x08, 0x68, 0x04, 0x44, 0x02, 0x80, 0x02, 0x54, 0xfd, 0x19, 0xb0, 0x00, - 0x08, 0x19, 0x30, 0x00, 0x0c, 0x0a, 0x62, 0x09, 0xb6, 0x6a, 0x93, 0xa5, 0xfd, 0xf4, 0xa5, 0xfe, - 0xca, 0x28, 0x3e, 0x20, 0x0a, 0x62, 0xda, 0x28, 0x7c, 0x74, 0x5e, 0x70, 0x02, 0x68, 0x10, 0xa5, - 0xed, 0x42, 0x28, 0x42, 0x26, 0x7e, 0x44, 0x00, 0x08, 0x59, 0x46, 0x01, 0x99, 0x80, 0x04, 0xa5, - 0xee, 0x52, 0x28, 0x7c, 0x74, 0x5e, 0x70, 0x04, 0x68, 0x10, 0xa5, 0xed, 0x42, 0x29, 0x42, 0x26, - 0x7e, 0x44, 0x00, 0x08, 0x59, 0x46, 0x01, 0x99, 0x80, 0x15, 0xa5, 0xee, 0x52, 0x29, 0x7c, 0x74, - 0x5e, 0x70, 0x02, 0x78, 0x0a, 0x52, 0x26, 0x7e, 0x44, 0x00, 0x20, 0x59, 0x46, 0x01, 0x99, 0x12, - 0x42, 0x91, 0xd0, 0xa8, 0x22, 0x7e, 0x04, 0x80, 0x00, 0x4c, 0x02, 0x09, 0x30, 0x00, 0x0c, 0x74, - 0xbf, 0x19, 0xb0, 0x00, 0x0c, 0x19, 0x40, 0x00, 0x10, 0x19, 0x30, 0x00, 0x0c, 0xd0, 0xa8, 0x22, - 0x7e, 0x04, 0x80, 0x00, 0x4c, 0x02, 0x09, 0x30, 0x00, 0x0c, 0x74, 0xbf, 0x19, 0xb0, 0x00, 0x0c, - 0x19, 0x40, 0x00, 0x18, 0x19, 0x30, 0x00, 0x0c, 0xd0, 0xa8, 0x22, 0x75, 0x2f, 0xb5, 0x12, 0x7e, - 0x30, 0x7e, 0x04, 0x80, 0x00, 0x4c, 0x02, 0x09, 0xb0, 0x00, 0x0c, 0x44, 0x40, 0x19, 0xb0, 0x00, - 0x0c, 0xe5, 0x58, 0xb4, 0x07, 0x23, 0x09, 0xb0, 0x00, 0x10, 0x4e, 0xb0, 0x02, 0x19, 0xb0, 0x00, - 0x10, 0x09, 0x30, 0x00, 0x0c, 0x74, 0xbf, 0x19, 0xb0, 0x00, 0x0c, 0x09, 0xb0, 0x00, 0x04, 0x54, - 0xf7, 0x19, 0xb0, 0x00, 0x04, 0x19, 0x30, 0x00, 0x0c, 0xd0, 0xa8, 0x22, 0x75, 0x2f, 0xb6, 0x12, - 0x7e, 0x30, 0x7e, 0x04, 0x80, 0x00, 0x4c, 0x02, 0xe5, 0x58, 0xb4, 0x07, 0x18, 0x09, 0x30, 0x00, - 0x0c, 0x74, 0xbf, 0x19, 0xb0, 0x00, 0x0c, 0x09, 0xb0, 0x00, 0x04, 0x44, 0x08, 0x19, 0xb0, 0x00, - 0x04, 0x19, 0x30, 0x00, 0x0c, 0x09, 0xb0, 0x00, 0x0c, 0x54, 0xbf, 0x19, 0xb0, 0x00, 0x0c, 0xd0, - 0xa8, 0x22, 0x75, 0x2f, 0xb4, 0x12, 0x7e, 0x30, 0x7a, 0x21, 0x2f, 0x12, 0x7e, 0x30, 0x7a, 0x41, - 0x2f, 0x12, 0x7e, 0x30, 0x7e, 0xb0, 0x01, 0x7e, 0xa0, 0xc8, 0x7c, 0x64, 0x12, 0x65, 0xbf, 0xc0, - 0xf1, 0x75, 0xf1, 0x01, 0x12, 0x71, 0xe9, 0xd0, 0xf1, 0xd0, 0xa8, 0x22, 0x6e, 0x9c, 0x6e, 0xe3, - 0x6f, 0x2a, 0x6f, 0x71, 0x6f, 0xb8, 0x6f, 0xff, 0x70, 0x46, 0x70, 0x8d, 0x75, 0x2f, 0x55, 0x12, - 0x7e, 0x30, 0x75, 0x2f, 0x00, 0x12, 0x7e, 0x30, 0x7a, 0x61, 0x2f, 0x12, 0x7e, 0x30, 0x7a, 0x71, - 0x2f, 0x12, 0x7e, 0x30, 0x7e, 0x17, 0x01, 0x69, 0x7e, 0x27, 0x01, 0x79, 0x2d, 0x23, 0x7e, 0x09, - 0xb0, 0x0b, 0x04, 0x7a, 0x19, 0xb0, 0x0b, 0x14, 0xbe, 0x14, 0x0d, 0xcc, 0x38, 0x0f, 0x1b, 0x34, - 0x78, 0xec, 0x7a, 0x17, 0x01, 0x69, 0x7a, 0x27, 0x01, 0x79, 0x02, 0x4a, 0x98, 0x7e, 0x14, 0x09, - 0xcd, 0x80, 0xeb, 0x75, 0x2f, 0x55, 0x12, 0x7e, 0x30, 0x75, 0x2f, 0x01, 0x12, 0x7e, 0x30, 0x7a, - 0x61, 0x2f, 0x12, 0x7e, 0x30, 0x7a, 0x71, 0x2f, 0x12, 0x7e, 0x30, 0x7e, 0x17, 0x01, 0x6b, 0x7e, - 0x27, 0x01, 0x7b, 0x2d, 0x23, 0x7e, 0x09, 0xb0, 0x0b, 0x04, 0x7a, 0x19, 0xb0, 0x0b, 0x14, 0xbe, - 0x14, 0x11, 0xcc, 0x38, 0x0f, 0x1b, 0x34, 0x78, 0xec, 0x7a, 0x17, 0x01, 0x6b, 0x7a, 0x27, 0x01, - 0x7b, 0x02, 0x4d, 0xcd, 0x7e, 0x14, 0x0d, 0xcd, 0x80, 0xeb, 0x75, 0x2f, 0x55, 0x12, 0x7e, 0x30, - 0x75, 0x2f, 0x02, 0x12, 0x7e, 0x30, 0x7a, 0x61, 0x2f, 0x12, 0x7e, 0x30, 0x7a, 0x71, 0x2f, 0x12, - 0x7e, 0x30, 0x7e, 0x17, 0x01, 0x6d, 0x7e, 0x27, 0x01, 0x7d, 0x2d, 0x23, 0x7e, 0x09, 0xb0, 0x0b, - 0x04, 0x7a, 0x19, 0xb0, 0x0b, 0x14, 0xbe, 0x14, 0x15, 0xcc, 0x38, 0x0f, 0x1b, 0x34, 0x78, 0xec, - 0x7a, 0x17, 0x01, 0x6d, 0x7a, 0x27, 0x01, 0x7d, 0x02, 0x51, 0x02, 0x7e, 0x14, 0x11, 0xcd, 0x80, - 0xeb, 0x75, 0x2f, 0x55, 0x12, 0x7e, 0x30, 0x75, 0x2f, 0x03, 0x12, 0x7e, 0x30, 0x7a, 0x61, 0x2f, - 0x12, 0x7e, 0x30, 0x7a, 0x71, 0x2f, 0x12, 0x7e, 0x30, 0x7e, 0x17, 0x01, 0x6f, 0x7e, 0x27, 0x01, - 0x7f, 0x2d, 0x23, 0x7e, 0x09, 0xb0, 0x0b, 0x04, 0x7a, 0x19, 0xb0, 0x0b, 0x14, 0xbe, 0x14, 0x19, - 0xcc, 0x38, 0x0f, 0x1b, 0x34, 0x78, 0xec, 0x7a, 0x17, 0x01, 0x6f, 0x7a, 0x27, 0x01, 0x7f, 0x02, - 0x54, 0x37, 0x7e, 0x14, 0x15, 0xcd, 0x80, 0xeb, 0x75, 0x2f, 0x55, 0x12, 0x7e, 0x30, 0x75, 0x2f, - 0x04, 0x12, 0x7e, 0x30, 0x7a, 0x61, 0x2f, 0x12, 0x7e, 0x30, 0x7a, 0x71, 0x2f, 0x12, 0x7e, 0x30, - 0x7e, 0x17, 0x01, 0x71, 0x7e, 0x27, 0x01, 0x81, 0x2d, 0x23, 0x7e, 0x09, 0xb0, 0x0b, 0x04, 0x7a, - 0x19, 0xb0, 0x0b, 0x14, 0xbe, 0x14, 0x1d, 0xcc, 0x38, 0x0f, 0x1b, 0x34, 0x78, 0xec, 0x7a, 0x17, - 0x01, 0x71, 0x7a, 0x27, 0x01, 0x81, 0x02, 0x57, 0x6c, 0x7e, 0x14, 0x19, 0xcd, 0x80, 0xeb, 0x75, - 0x2f, 0x55, 0x12, 0x7e, 0x30, 0x75, 0x2f, 0x05, 0x12, 0x7e, 0x30, 0x7a, 0x61, 0x2f, 0x12, 0x7e, - 0x30, 0x7a, 0x71, 0x2f, 0x12, 0x7e, 0x30, 0x7e, 0x17, 0x01, 0x73, 0x7e, 0x27, 0x01, 0x83, 0x2d, - 0x23, 0x7e, 0x09, 0xb0, 0x0b, 0x04, 0x7a, 0x19, 0xb0, 0x0b, 0x14, 0xbe, 0x14, 0x21, 0xcc, 0x38, - 0x0f, 0x1b, 0x34, 0x78, 0xec, 0x7a, 0x17, 0x01, 0x73, 0x7a, 0x27, 0x01, 0x83, 0x02, 0x5a, 0xa1, - 0x7e, 0x14, 0x1d, 0xcd, 0x80, 0xeb, 0x75, 0x2f, 0x55, 0x12, 0x7e, 0x30, 0x75, 0x2f, 0x06, 0x12, - 0x7e, 0x30, 0x7a, 0x61, 0x2f, 0x12, 0x7e, 0x30, 0x7a, 0x71, 0x2f, 0x12, 0x7e, 0x30, 0x7e, 0x17, - 0x01, 0x75, 0x7e, 0x27, 0x01, 0x85, 0x2d, 0x23, 0x7e, 0x09, 0xb0, 0x0b, 0x04, 0x7a, 0x19, 0xb0, - 0x0b, 0x14, 0xbe, 0x14, 0x25, 0xcc, 0x38, 0x0f, 0x1b, 0x34, 0x78, 0xec, 0x7a, 0x17, 0x01, 0x75, - 0x7a, 0x27, 0x01, 0x85, 0x02, 0x5d, 0xd6, 0x7e, 0x14, 0x21, 0xcd, 0x80, 0xeb, 0x75, 0x2f, 0x55, - 0x12, 0x7e, 0x30, 0x75, 0x2f, 0x07, 0x12, 0x7e, 0x30, 0x7a, 0x61, 0x2f, 0x12, 0x7e, 0x30, 0x7a, - 0x71, 0x2f, 0x12, 0x7e, 0x30, 0x7e, 0x17, 0x01, 0x77, 0x7e, 0x27, 0x01, 0x87, 0x2d, 0x23, 0x7e, - 0x09, 0xb0, 0x0b, 0x04, 0x7a, 0x19, 0xb0, 0x0b, 0x14, 0xbe, 0x14, 0x29, 0xcc, 0x38, 0x0f, 0x1b, - 0x34, 0x78, 0xec, 0x7a, 0x17, 0x01, 0x77, 0x7a, 0x27, 0x01, 0x87, 0x02, 0x61, 0x0b, 0x7e, 0x14, - 0x25, 0xcd, 0x80, 0xeb, 0xca, 0xb8, 0xc0, 0xf1, 0x75, 0x2f, 0x02, 0x12, 0x7e, 0x30, 0xe5, 0xc0, - 0x54, 0x03, 0x68, 0x05, 0x12, 0x77, 0xdd, 0x80, 0xf5, 0x30, 0xc2, 0x08, 0x75, 0xf1, 0x01, 0x12, - 0x71, 0xe9, 0x80, 0x14, 0x30, 0xc3, 0x08, 0x75, 0xf1, 0x01, 0x12, 0x71, 0x0d, 0x80, 0x09, 0x30, - 0xc4, 0x06, 0x75, 0xf1, 0x02, 0x12, 0x72, 0xf9, 0xd0, 0xf1, 0xda, 0xb8, 0x32, 0x75, 0x2f, 0x10, - 0x12, 0x7e, 0x30, 0xca, 0x0b, 0xca, 0x39, 0xca, 0x59, 0xc2, 0xc3, 0xa9, 0x21, 0xe2, 0x5c, 0xe5, - 0xe5, 0x54, 0xc0, 0x68, 0x4f, 0xe5, 0xe6, 0x6c, 0xaa, 0x7e, 0x37, 0x01, 0xc5, 0x2d, 0x35, 0xbe, - 0x34, 0x04, 0x00, 0x38, 0x4a, 0x7a, 0x37, 0x01, 0xc5, 0x7e, 0x37, 0x01, 0xc3, 0x7d, 0x43, 0x2d, - 0x45, 0xbe, 0x44, 0x09, 0xcc, 0x38, 0x40, 0x7a, 0x47, 0x01, 0xc3, 0x75, 0x2f, 0x11, 0x12, 0x7e, - 0x30, 0x7a, 0xb1, 0x2f, 0x12, 0x7e, 0x30, 0x12, 0x73, 0xc8, 0xa9, 0x21, 0xe5, 0x1f, 0xa9, 0xd4, + 0x00, 0x0b, 0x35, 0x7e, 0x19, 0x00, 0x7e, 0x19, 0x10, 0x1b, 0x38, 0x00, 0x0b, 0x35, 0x7e, 0x19, + 0x00, 0x7e, 0x19, 0x10, 0x1b, 0x38, 0x00, 0x0b, 0x35, 0x7e, 0x19, 0x00, 0x7e, 0x19, 0x10, 0x1b, + 0x38, 0x00, 0x0b, 0x35, 0x7e, 0x19, 0x00, 0x7e, 0x19, 0x10, 0x1b, 0x38, 0x00, 0x0b, 0x35, 0x7e, + 0x19, 0x00, 0x7e, 0x19, 0x10, 0x1b, 0x38, 0x00, 0x0b, 0x35, 0x22, 0x67, 0x96, 0x69, 0x63, 0x69, + 0x7b, 0x6a, 0x49, 0x6a, 0xe4, 0x6b, 0x8e, 0x6b, 0xa9, 0x6c, 0x3b, 0x6b, 0xc4, 0x6c, 0x05, 0x69, + 0x96, 0x69, 0xaa, 0x7c, 0xb3, 0xbe, 0xb0, 0x0b, 0x28, 0x14, 0x75, 0x31, 0x09, 0x12, 0x7c, 0x15, + 0x75, 0x6d, 0x10, 0xc0, 0xf1, 0x75, 0xf1, 0x01, 0x43, 0xe1, 0xc0, 0xd0, 0xf1, 0x22, 0xc0, 0xa8, + 0xc2, 0xaf, 0x23, 0x6c, 0xaa, 0x2e, 0x54, 0x67, 0x4b, 0x0b, 0x58, 0x50, 0x89, 0x54, 0x01, 0x02, + 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x75, 0x31, 0xb0, 0x12, 0x7c, 0x15, 0x0a, 0x32, 0x09, 0xb3, + 0x67, 0x8e, 0x42, 0x32, 0x19, 0x43, 0x00, 0x36, 0xd0, 0xa8, 0x22, 0x7c, 0xb2, 0x23, 0x0a, 0x3b, + 0x49, 0x33, 0x67, 0xcb, 0x0a, 0x22, 0x09, 0x32, 0x00, 0x36, 0x09, 0xb2, 0x67, 0x8e, 0xa5, 0xbb, + 0x00, 0x05, 0xf4, 0x52, 0x33, 0x80, 0x02, 0x42, 0x33, 0x89, 0x34, 0x67, 0xdb, 0x68, 0x09, 0x68, + 0x37, 0x68, 0x65, 0x68, 0x93, 0x68, 0xc1, 0x68, 0xef, 0x69, 0x1d, 0x12, 0x41, 0xb5, 0xd2, 0x28, + 0xd2, 0x08, 0xc2, 0x40, 0xc2, 0x48, 0xc2, 0x38, 0xc2, 0x30, 0x6d, 0x00, 0x7a, 0x03, 0x01, 0xcf, + 0x7e, 0x04, 0x00, 0x20, 0x7a, 0x07, 0x01, 0xaf, 0x7e, 0x04, 0x00, 0x38, 0x7a, 0x07, 0x01, 0xbf, + 0x12, 0x41, 0x16, 0x12, 0x60, 0xcf, 0x02, 0x69, 0x4b, 0x12, 0x41, 0xd4, 0xd2, 0x29, 0xd2, 0x09, + 0xc2, 0x41, 0xc2, 0x49, 0xc2, 0x39, 0xc2, 0x31, 0x6d, 0x00, 0x7a, 0x03, 0x01, 0xd0, 0x7e, 0x04, + 0x00, 0x20, 0x7a, 0x07, 0x01, 0xb1, 0x7e, 0x04, 0x00, 0x38, 0x7a, 0x07, 0x01, 0xc1, 0x12, 0x41, + 0x16, 0x12, 0x61, 0x05, 0x02, 0x69, 0x4b, 0x12, 0x41, 0xf3, 0xd2, 0x2a, 0xd2, 0x0a, 0xc2, 0x42, + 0xc2, 0x4a, 0xc2, 0x3a, 0xc2, 0x32, 0x6d, 0x00, 0x7a, 0x03, 0x01, 0xd1, 0x7e, 0x04, 0x00, 0x20, + 0x7a, 0x07, 0x01, 0xb3, 0x7e, 0x04, 0x00, 0x38, 0x7a, 0x07, 0x01, 0xc3, 0x12, 0x41, 0x16, 0x12, + 0x61, 0x3b, 0x02, 0x69, 0x4b, 0x12, 0x42, 0x12, 0xd2, 0x2b, 0xd2, 0x0b, 0xc2, 0x43, 0xc2, 0x4b, + 0xc2, 0x3b, 0xc2, 0x33, 0x6d, 0x00, 0x7a, 0x03, 0x01, 0xd2, 0x7e, 0x04, 0x00, 0x20, 0x7a, 0x07, + 0x01, 0xb5, 0x7e, 0x04, 0x00, 0x38, 0x7a, 0x07, 0x01, 0xc5, 0x12, 0x41, 0x16, 0x12, 0x61, 0x71, + 0x02, 0x69, 0x4b, 0x12, 0x42, 0x31, 0xd2, 0x2c, 0xd2, 0x0c, 0xc2, 0x44, 0xc2, 0x4c, 0xc2, 0x3c, + 0xc2, 0x34, 0x6d, 0x00, 0x7a, 0x03, 0x01, 0xd3, 0x7e, 0x04, 0x00, 0x20, 0x7a, 0x07, 0x01, 0xb7, + 0x7e, 0x04, 0x00, 0x38, 0x7a, 0x07, 0x01, 0xc7, 0x12, 0x41, 0x16, 0x12, 0x61, 0xa7, 0x02, 0x69, + 0x4b, 0x12, 0x42, 0x50, 0xd2, 0x2d, 0xd2, 0x0d, 0xc2, 0x45, 0xc2, 0x4d, 0xc2, 0x3d, 0xc2, 0x35, + 0x6d, 0x00, 0x7a, 0x03, 0x01, 0xd4, 0x7e, 0x04, 0x00, 0x20, 0x7a, 0x07, 0x01, 0xb9, 0x7e, 0x04, + 0x00, 0x38, 0x7a, 0x07, 0x01, 0xc9, 0x12, 0x41, 0x16, 0x12, 0x61, 0xdd, 0x02, 0x69, 0x4b, 0x12, + 0x42, 0x6f, 0xd2, 0x2e, 0xd2, 0x0e, 0xc2, 0x46, 0xc2, 0x4e, 0xc2, 0x3e, 0xc2, 0x36, 0x6d, 0x00, + 0x7a, 0x03, 0x01, 0xd5, 0x7e, 0x04, 0x00, 0x20, 0x7a, 0x07, 0x01, 0xbb, 0x7e, 0x04, 0x00, 0x38, + 0x7a, 0x07, 0x01, 0xcb, 0x12, 0x41, 0x16, 0x12, 0x62, 0x13, 0x02, 0x69, 0x4b, 0x12, 0x42, 0x8e, + 0xd2, 0x2f, 0xd2, 0x0f, 0xc2, 0x47, 0xc2, 0x4f, 0xc2, 0x3f, 0xc2, 0x37, 0x6d, 0x00, 0x7a, 0x03, + 0x01, 0xd6, 0x7e, 0x04, 0x00, 0x20, 0x7a, 0x07, 0x01, 0xbd, 0x7e, 0x04, 0x00, 0x38, 0x7a, 0x07, + 0x01, 0xcd, 0x12, 0x41, 0x16, 0x12, 0x62, 0x49, 0x02, 0x69, 0x4b, 0x7e, 0xa0, 0xd0, 0x7e, 0x60, + 0x0f, 0x12, 0x62, 0xb6, 0x40, 0x0c, 0xc0, 0xf1, 0x75, 0xf1, 0x01, 0x12, 0x6f, 0xd9, 0xd0, 0xf1, + 0xc2, 0xd7, 0x22, 0x75, 0x31, 0xb1, 0x12, 0x7c, 0x15, 0x0a, 0x52, 0x23, 0x6d, 0x00, 0x59, 0x05, + 0x00, 0x48, 0x12, 0x41, 0x7e, 0x12, 0x41, 0x9a, 0xd0, 0xa8, 0x22, 0x75, 0x31, 0xb2, 0x12, 0x7c, + 0x15, 0x0a, 0x22, 0x09, 0xb2, 0x67, 0x8e, 0x42, 0x23, 0x7e, 0xb0, 0x9c, 0x19, 0xb2, 0x01, 0xcf, + 0x12, 0x31, 0x85, 0xd0, 0xa8, 0x22, 0x75, 0x31, 0xb7, 0x12, 0x7c, 0x15, 0x0a, 0x22, 0x09, 0xb2, + 0x67, 0x8e, 0x42, 0x24, 0x12, 0x35, 0x3d, 0xd0, 0xa8, 0x22, 0x75, 0x31, 0xb9, 0x12, 0x7c, 0x15, + 0x0a, 0x32, 0x09, 0xb3, 0x67, 0x8e, 0x42, 0x34, 0x19, 0x43, 0x00, 0x3e, 0x12, 0x69, 0xc2, 0xd0, + 0xa8, 0x22, 0x7c, 0xb2, 0x23, 0x0a, 0x0b, 0x7c, 0xb4, 0x20, 0xe0, 0x04, 0x6d, 0x33, 0x80, 0x04, + 0x49, 0x30, 0x01, 0x8f, 0x7e, 0xa0, 0xd8, 0xa5, 0xef, 0xca, 0x0b, 0xca, 0x29, 0x12, 0x62, 0xb6, + 0xda, 0x29, 0xda, 0x0b, 0x40, 0x62, 0x75, 0x31, 0xba, 0x12, 0x7c, 0x15, 0x7c, 0xb4, 0x30, 0xe0, + 0x1e, 0x6d, 0x33, 0x59, 0x30, 0x01, 0x8f, 0x7e, 0x34, 0x09, 0xe3, 0x0a, 0x82, 0x7e, 0x94, 0x04, + 0x00, 0xad, 0x89, 0x2d, 0x39, 0x59, 0x30, 0x01, 0x6f, 0x59, 0x30, 0x01, 0x7f, 0x7c, 0xb4, 0x30, + 0xe1, 0x10, 0x7e, 0x04, 0x80, 0x00, 0x4c, 0x02, 0x09, 0xb0, 0x00, 0x08, 0x44, 0x04, 0x19, 0xb0, + 0x00, 0x08, 0x0a, 0x02, 0x09, 0xb0, 0x67, 0x8e, 0x42, 0x21, 0xf4, 0x52, 0x34, 0x7c, 0xb2, 0x23, + 0x0a, 0x0b, 0xca, 0x19, 0x49, 0x00, 0x30, 0xd5, 0x99, 0x04, 0xda, 0x19, 0xc0, 0xf1, 0x75, 0xf1, + 0x01, 0x12, 0x6f, 0xd9, 0xd0, 0xf1, 0xc2, 0xd7, 0x22, 0x7e, 0x04, 0x80, 0x00, 0x4c, 0x02, 0x09, + 0x30, 0x00, 0x0c, 0x74, 0xbf, 0x19, 0xb0, 0x00, 0x0c, 0x09, 0xb0, 0x00, 0x08, 0x19, 0x30, 0x00, + 0x0c, 0x7c, 0x74, 0x5e, 0x70, 0x01, 0x68, 0x12, 0x44, 0x40, 0xca, 0xb8, 0x09, 0xb0, 0x00, 0x10, + 0x44, 0x02, 0x19, 0xb0, 0x00, 0x10, 0xda, 0xb8, 0x80, 0x02, 0x54, 0xbf, 0x7c, 0x74, 0x5e, 0x70, + 0x08, 0x68, 0x04, 0x44, 0x08, 0x80, 0x02, 0x54, 0xf7, 0x09, 0x30, 0x00, 0x0c, 0xca, 0xb8, 0x74, + 0xbf, 0x19, 0xb0, 0x00, 0x0c, 0xda, 0xb8, 0x19, 0xb0, 0x00, 0x08, 0x19, 0x30, 0x00, 0x0c, 0x0a, + 0x62, 0x09, 0xb6, 0x67, 0x8e, 0x3e, 0x20, 0x0a, 0x62, 0x7c, 0x74, 0x5e, 0x70, 0x02, 0x68, 0x20, + 0x42, 0x27, 0xca, 0xb8, 0x74, 0x61, 0x19, 0xb0, 0x00, 0x08, 0x7e, 0x44, 0x00, 0x10, 0x59, 0x46, + 0x01, 0xbf, 0x09, 0xb0, 0x00, 0x10, 0x44, 0x01, 0x19, 0xb0, 0x00, 0x10, 0xda, 0xb8, 0x80, 0x11, + 0xf4, 0x52, 0x27, 0x74, 0xa1, 0x19, 0xb0, 0x00, 0x08, 0x7e, 0x44, 0x00, 0x38, 0x59, 0x46, 0x01, + 0xbf, 0xd0, 0xa8, 0x22, 0x7c, 0x74, 0x7e, 0x04, 0x80, 0x00, 0x4c, 0x02, 0x0a, 0x62, 0x09, 0xb6, + 0x67, 0x8e, 0xa5, 0xfd, 0xf4, 0xa5, 0xfe, 0xca, 0x28, 0x3e, 0x20, 0x0a, 0x62, 0xa5, 0xee, 0x52, + 0x26, 0x7e, 0x44, 0x00, 0x20, 0x59, 0x46, 0x01, 0xaf, 0xda, 0x28, 0x09, 0x30, 0x00, 0x0c, 0x74, + 0xbf, 0x19, 0xb0, 0x00, 0x0c, 0x09, 0xb0, 0x00, 0x08, 0x7c, 0x74, 0x5e, 0x70, 0x01, 0x68, 0x0c, + 0x44, 0x80, 0xca, 0xb8, 0xa5, 0xed, 0x42, 0x2a, 0xda, 0xb8, 0x80, 0x0a, 0x54, 0x7f, 0xca, 0xb8, + 0xa5, 0xee, 0x52, 0x2a, 0xda, 0xb8, 0x7c, 0x74, 0x5e, 0x70, 0x08, 0x68, 0x04, 0x44, 0x02, 0x80, + 0x02, 0x54, 0xfd, 0x19, 0xb0, 0x00, 0x08, 0x19, 0x30, 0x00, 0x0c, 0x7c, 0x74, 0x5e, 0x70, 0x02, + 0x68, 0x10, 0xa5, 0xed, 0x42, 0x28, 0x42, 0x26, 0x7e, 0x44, 0x00, 0x08, 0x59, 0x46, 0x01, 0xaf, + 0x80, 0x04, 0xa5, 0xee, 0x52, 0x28, 0x7c, 0x74, 0x5e, 0x70, 0x04, 0x68, 0x10, 0xa5, 0xed, 0x42, + 0x29, 0x42, 0x26, 0x7e, 0x44, 0x00, 0x08, 0x59, 0x46, 0x01, 0xaf, 0x80, 0x0b, 0xa5, 0xee, 0x52, + 0x29, 0x7c, 0x74, 0x5e, 0x70, 0x02, 0x78, 0x00, 0x12, 0x42, 0xad, 0xd0, 0xa8, 0x22, 0x7e, 0x04, + 0x80, 0x00, 0x4c, 0x02, 0x09, 0x30, 0x00, 0x0c, 0x74, 0xbf, 0x19, 0xb0, 0x00, 0x0c, 0x19, 0x40, + 0x00, 0x10, 0x19, 0x30, 0x00, 0x0c, 0xd0, 0xa8, 0x22, 0x7e, 0x04, 0x80, 0x00, 0x4c, 0x02, 0x09, + 0x30, 0x00, 0x0c, 0x74, 0xbf, 0x19, 0xb0, 0x00, 0x0c, 0x19, 0x40, 0x00, 0x18, 0x19, 0x30, 0x00, + 0x0c, 0xd0, 0xa8, 0x22, 0x75, 0x31, 0xb5, 0x12, 0x7c, 0x15, 0x7e, 0x04, 0x80, 0x00, 0x4c, 0x02, + 0x09, 0xb0, 0x00, 0x0c, 0x44, 0x40, 0x19, 0xb0, 0x00, 0x0c, 0xe5, 0x6e, 0xb4, 0x07, 0x23, 0x09, + 0xb0, 0x00, 0x10, 0x4e, 0xb0, 0x02, 0x19, 0xb0, 0x00, 0x10, 0x09, 0x30, 0x00, 0x0c, 0x74, 0xbf, + 0x19, 0xb0, 0x00, 0x0c, 0x09, 0xb0, 0x00, 0x04, 0x54, 0xf7, 0x19, 0xb0, 0x00, 0x04, 0x19, 0x30, + 0x00, 0x0c, 0xd0, 0xa8, 0x22, 0x75, 0x31, 0xb6, 0x12, 0x7c, 0x15, 0x7e, 0x04, 0x80, 0x00, 0x4c, + 0x02, 0xe5, 0x6e, 0xb4, 0x07, 0x18, 0x09, 0x30, 0x00, 0x0c, 0x74, 0xbf, 0x19, 0xb0, 0x00, 0x0c, + 0x09, 0xb0, 0x00, 0x04, 0x44, 0x08, 0x19, 0xb0, 0x00, 0x04, 0x19, 0x30, 0x00, 0x0c, 0x09, 0xb0, + 0x00, 0x0c, 0x54, 0xbf, 0x19, 0xb0, 0x00, 0x0c, 0xd0, 0xa8, 0x22, 0x75, 0x31, 0xb4, 0x12, 0x7c, + 0x15, 0x7a, 0x21, 0x31, 0x12, 0x7c, 0x15, 0x7a, 0x41, 0x31, 0x12, 0x7c, 0x15, 0x0a, 0x32, 0x09, + 0xb3, 0x67, 0x8e, 0x42, 0x35, 0x12, 0x6c, 0x5b, 0xd0, 0xa8, 0x22, 0x7e, 0xb0, 0x01, 0x7e, 0xa0, + 0xc8, 0x7c, 0x64, 0x12, 0x62, 0xb6, 0x40, 0x13, 0x0a, 0x32, 0x09, 0xb3, 0x67, 0x8e, 0xf4, 0x52, + 0x35, 0xc0, 0xf1, 0x75, 0xf1, 0x01, 0x12, 0x6f, 0xd9, 0xd0, 0xf1, 0x22, 0x6c, 0x8c, 0x6c, 0xd3, + 0x6d, 0x1a, 0x6d, 0x61, 0x6d, 0xa8, 0x6d, 0xef, 0x6e, 0x36, 0x6e, 0x7d, 0x75, 0x31, 0x55, 0x12, + 0x7c, 0x15, 0x75, 0x31, 0x00, 0x12, 0x7c, 0x15, 0x7a, 0x61, 0x31, 0x12, 0x7c, 0x15, 0x7a, 0x71, + 0x31, 0x12, 0x7c, 0x15, 0x7e, 0x17, 0x01, 0x7f, 0x7e, 0x27, 0x01, 0x8f, 0x2d, 0x23, 0x7e, 0x09, + 0xb0, 0x0b, 0x04, 0x7a, 0x19, 0xb0, 0x0b, 0x14, 0xbe, 0x14, 0x0d, 0xe2, 0x38, 0x0f, 0x1b, 0x34, + 0x78, 0xec, 0x7a, 0x17, 0x01, 0x7f, 0x7a, 0x27, 0x01, 0x8f, 0x02, 0x46, 0x0f, 0x7e, 0x14, 0x09, + 0xe3, 0x80, 0xeb, 0x75, 0x31, 0x55, 0x12, 0x7c, 0x15, 0x75, 0x31, 0x01, 0x12, 0x7c, 0x15, 0x7a, + 0x61, 0x31, 0x12, 0x7c, 0x15, 0x7a, 0x71, 0x31, 0x12, 0x7c, 0x15, 0x7e, 0x17, 0x01, 0x81, 0x7e, + 0x27, 0x01, 0x91, 0x2d, 0x23, 0x7e, 0x09, 0xb0, 0x0b, 0x04, 0x7a, 0x19, 0xb0, 0x0b, 0x14, 0xbe, + 0x14, 0x11, 0xe2, 0x38, 0x0f, 0x1b, 0x34, 0x78, 0xec, 0x7a, 0x17, 0x01, 0x81, 0x7a, 0x27, 0x01, + 0x91, 0x02, 0x49, 0x67, 0x7e, 0x14, 0x0d, 0xe3, 0x80, 0xeb, 0x75, 0x31, 0x55, 0x12, 0x7c, 0x15, + 0x75, 0x31, 0x02, 0x12, 0x7c, 0x15, 0x7a, 0x61, 0x31, 0x12, 0x7c, 0x15, 0x7a, 0x71, 0x31, 0x12, + 0x7c, 0x15, 0x7e, 0x17, 0x01, 0x83, 0x7e, 0x27, 0x01, 0x93, 0x2d, 0x23, 0x7e, 0x09, 0xb0, 0x0b, + 0x04, 0x7a, 0x19, 0xb0, 0x0b, 0x14, 0xbe, 0x14, 0x15, 0xe2, 0x38, 0x0f, 0x1b, 0x34, 0x78, 0xec, + 0x7a, 0x17, 0x01, 0x83, 0x7a, 0x27, 0x01, 0x93, 0x02, 0x4c, 0xbf, 0x7e, 0x14, 0x11, 0xe3, 0x80, + 0xeb, 0x75, 0x31, 0x55, 0x12, 0x7c, 0x15, 0x75, 0x31, 0x03, 0x12, 0x7c, 0x15, 0x7a, 0x61, 0x31, + 0x12, 0x7c, 0x15, 0x7a, 0x71, 0x31, 0x12, 0x7c, 0x15, 0x7e, 0x17, 0x01, 0x85, 0x7e, 0x27, 0x01, + 0x95, 0x2d, 0x23, 0x7e, 0x09, 0xb0, 0x0b, 0x04, 0x7a, 0x19, 0xb0, 0x0b, 0x14, 0xbe, 0x14, 0x19, + 0xe2, 0x38, 0x0f, 0x1b, 0x34, 0x78, 0xec, 0x7a, 0x17, 0x01, 0x85, 0x7a, 0x27, 0x01, 0x95, 0x02, + 0x50, 0x17, 0x7e, 0x14, 0x15, 0xe3, 0x80, 0xeb, 0x75, 0x31, 0x55, 0x12, 0x7c, 0x15, 0x75, 0x31, + 0x04, 0x12, 0x7c, 0x15, 0x7a, 0x61, 0x31, 0x12, 0x7c, 0x15, 0x7a, 0x71, 0x31, 0x12, 0x7c, 0x15, + 0x7e, 0x17, 0x01, 0x87, 0x7e, 0x27, 0x01, 0x97, 0x2d, 0x23, 0x7e, 0x09, 0xb0, 0x0b, 0x04, 0x7a, + 0x19, 0xb0, 0x0b, 0x14, 0xbe, 0x14, 0x1d, 0xe2, 0x38, 0x0f, 0x1b, 0x34, 0x78, 0xec, 0x7a, 0x17, + 0x01, 0x87, 0x7a, 0x27, 0x01, 0x97, 0x02, 0x53, 0x6f, 0x7e, 0x14, 0x19, 0xe3, 0x80, 0xeb, 0x75, + 0x31, 0x55, 0x12, 0x7c, 0x15, 0x75, 0x31, 0x05, 0x12, 0x7c, 0x15, 0x7a, 0x61, 0x31, 0x12, 0x7c, + 0x15, 0x7a, 0x71, 0x31, 0x12, 0x7c, 0x15, 0x7e, 0x17, 0x01, 0x89, 0x7e, 0x27, 0x01, 0x99, 0x2d, + 0x23, 0x7e, 0x09, 0xb0, 0x0b, 0x04, 0x7a, 0x19, 0xb0, 0x0b, 0x14, 0xbe, 0x14, 0x21, 0xe2, 0x38, + 0x0f, 0x1b, 0x34, 0x78, 0xec, 0x7a, 0x17, 0x01, 0x89, 0x7a, 0x27, 0x01, 0x99, 0x02, 0x56, 0xc7, + 0x7e, 0x14, 0x1d, 0xe3, 0x80, 0xeb, 0x75, 0x31, 0x55, 0x12, 0x7c, 0x15, 0x75, 0x31, 0x06, 0x12, + 0x7c, 0x15, 0x7a, 0x61, 0x31, 0x12, 0x7c, 0x15, 0x7a, 0x71, 0x31, 0x12, 0x7c, 0x15, 0x7e, 0x17, + 0x01, 0x8b, 0x7e, 0x27, 0x01, 0x9b, 0x2d, 0x23, 0x7e, 0x09, 0xb0, 0x0b, 0x04, 0x7a, 0x19, 0xb0, + 0x0b, 0x14, 0xbe, 0x14, 0x25, 0xe2, 0x38, 0x0f, 0x1b, 0x34, 0x78, 0xec, 0x7a, 0x17, 0x01, 0x8b, + 0x7a, 0x27, 0x01, 0x9b, 0x02, 0x5a, 0x1f, 0x7e, 0x14, 0x21, 0xe3, 0x80, 0xeb, 0x75, 0x31, 0x55, + 0x12, 0x7c, 0x15, 0x75, 0x31, 0x07, 0x12, 0x7c, 0x15, 0x7a, 0x61, 0x31, 0x12, 0x7c, 0x15, 0x7a, + 0x71, 0x31, 0x12, 0x7c, 0x15, 0x7e, 0x17, 0x01, 0x8d, 0x7e, 0x27, 0x01, 0x9d, 0x2d, 0x23, 0x7e, + 0x09, 0xb0, 0x0b, 0x04, 0x7a, 0x19, 0xb0, 0x0b, 0x14, 0xbe, 0x14, 0x29, 0xe2, 0x38, 0x0f, 0x1b, + 0x34, 0x78, 0xec, 0x7a, 0x17, 0x01, 0x8d, 0x7a, 0x27, 0x01, 0x9d, 0x02, 0x5d, 0x77, 0x7e, 0x14, + 0x25, 0xe3, 0x80, 0xeb, 0xca, 0xb8, 0xc0, 0xf1, 0x75, 0x31, 0x02, 0x12, 0x7c, 0x15, 0xe5, 0xc0, + 0x54, 0x03, 0x68, 0x05, 0x12, 0x75, 0xcd, 0x80, 0xf5, 0x30, 0xc2, 0x08, 0x75, 0xf1, 0x01, 0x12, + 0x6f, 0xd9, 0x80, 0x14, 0x30, 0xc3, 0x08, 0x75, 0xf1, 0x01, 0x12, 0x6e, 0xfd, 0x80, 0x09, 0x30, + 0xc4, 0x06, 0x75, 0xf1, 0x02, 0x12, 0x70, 0xe9, 0xd0, 0xf1, 0xda, 0xb8, 0x32, 0x75, 0x31, 0x10, + 0x12, 0x7c, 0x15, 0xca, 0x0b, 0xca, 0x39, 0xca, 0x59, 0xc2, 0xc3, 0xa9, 0x21, 0xe2, 0x5c, 0xe5, + 0xe5, 0x54, 0xc0, 0x68, 0x4f, 0xe5, 0xe6, 0x6c, 0xaa, 0x7e, 0x37, 0x01, 0xdb, 0x2d, 0x35, 0xbe, + 0x34, 0x04, 0x00, 0x38, 0x4a, 0x7a, 0x37, 0x01, 0xdb, 0x7e, 0x37, 0x01, 0xd9, 0x7d, 0x43, 0x2d, + 0x45, 0xbe, 0x44, 0x09, 0xe2, 0x38, 0x40, 0x7a, 0x47, 0x01, 0xd9, 0x75, 0x31, 0x11, 0x12, 0x7c, + 0x15, 0x7a, 0xb1, 0x31, 0x12, 0x7c, 0x15, 0x12, 0x71, 0xb8, 0xa9, 0x21, 0xe5, 0x1f, 0xa9, 0xd4, 0xe4, 0xa9, 0x24, 0xe4, 0xfc, 0xc2, 0xc3, 0xa9, 0x21, 0xe2, 0x3b, 0xe5, 0xe5, 0x54, 0xc0, 0x78, - 0xb4, 0x12, 0x76, 0x6a, 0xda, 0x59, 0xda, 0x39, 0xda, 0x0b, 0x22, 0x80, 0x29, 0x80, 0x58, 0x75, - 0x2f, 0x16, 0x12, 0x7e, 0x30, 0x80, 0xed, 0x75, 0x2f, 0x12, 0x12, 0x7e, 0x30, 0x7a, 0xb1, 0x2f, - 0x12, 0x7e, 0x30, 0x9e, 0x44, 0x09, 0xcd, 0x9d, 0x54, 0x12, 0x73, 0xc8, 0x7e, 0x34, 0x05, 0xcd, - 0x7d, 0x54, 0x2d, 0x43, 0x80, 0xa1, 0xe5, 0xe5, 0x54, 0x03, 0x78, 0x12, 0x75, 0x2f, 0x13, 0x12, - 0x7e, 0x30, 0x7e, 0x0f, 0x29, 0xe9, 0x0b, 0x0c, 0x7a, 0x0f, 0x29, 0xe9, 0x80, 0xa7, 0x75, 0x2f, - 0x14, 0x12, 0x7e, 0x30, 0x7e, 0x0f, 0x29, 0xed, 0x0b, 0x0c, 0x7a, 0x0f, 0x29, 0xed, 0xa9, 0xd7, - 0xe4, 0xa9, 0x27, 0xe4, 0xfc, 0x80, 0x9d, 0x75, 0x2f, 0x15, 0x12, 0x7e, 0x30, 0x7e, 0x0f, 0x29, - 0xf1, 0x0b, 0x0c, 0x7a, 0x0f, 0x29, 0xf1, 0x80, 0xe5, 0x75, 0x2f, 0x18, 0x12, 0x7e, 0x30, 0xca, + 0xb4, 0x12, 0x74, 0x5a, 0xda, 0x59, 0xda, 0x39, 0xda, 0x0b, 0x22, 0x80, 0x29, 0x80, 0x58, 0x75, + 0x31, 0x16, 0x12, 0x7c, 0x15, 0x80, 0xed, 0x75, 0x31, 0x12, 0x12, 0x7c, 0x15, 0x7a, 0xb1, 0x31, + 0x12, 0x7c, 0x15, 0x9e, 0x44, 0x09, 0xe3, 0x9d, 0x54, 0x12, 0x71, 0xb8, 0x7e, 0x34, 0x05, 0xe3, + 0x7d, 0x54, 0x2d, 0x43, 0x80, 0xa1, 0xe5, 0xe5, 0x54, 0x03, 0x78, 0x12, 0x75, 0x31, 0x13, 0x12, + 0x7c, 0x15, 0x7e, 0x0f, 0x29, 0xff, 0x0b, 0x0c, 0x7a, 0x0f, 0x29, 0xff, 0x80, 0xa7, 0x75, 0x31, + 0x14, 0x12, 0x7c, 0x15, 0x7e, 0x0f, 0x2a, 0x03, 0x0b, 0x0c, 0x7a, 0x0f, 0x2a, 0x03, 0xa9, 0xd7, + 0xe4, 0xa9, 0x27, 0xe4, 0xfc, 0x80, 0x9d, 0x75, 0x31, 0x15, 0x12, 0x7c, 0x15, 0x7e, 0x0f, 0x2a, + 0x07, 0x0b, 0x0c, 0x7a, 0x0f, 0x2a, 0x07, 0x80, 0xe5, 0x75, 0x31, 0x18, 0x12, 0x7c, 0x15, 0xca, 0x09, 0xca, 0x39, 0xca, 0x2b, 0xc2, 0xc2, 0xa9, 0x21, 0xf2, 0x52, 0xe5, 0xf5, 0x33, 0x82, 0xe7, - 0x40, 0x44, 0x7e, 0x37, 0x01, 0xcb, 0x7e, 0x54, 0x00, 0x40, 0x9d, 0x35, 0x40, 0x43, 0x7a, 0x37, - 0x01, 0xcb, 0x7e, 0x37, 0x01, 0xc7, 0x7d, 0x43, 0x2d, 0x45, 0xbe, 0x44, 0x05, 0xcc, 0x38, 0x52, - 0x7a, 0x47, 0x01, 0xc7, 0x7d, 0x45, 0x12, 0x75, 0x26, 0xa9, 0x20, 0xf5, 0x22, 0x75, 0x2f, 0x19, - 0x12, 0x7e, 0x30, 0x7a, 0x91, 0x2f, 0x12, 0x7e, 0x30, 0x7a, 0x81, 0xf7, 0x7a, 0x91, 0xf6, 0xe5, + 0x40, 0x44, 0x7e, 0x37, 0x01, 0xe1, 0x7e, 0x54, 0x00, 0x40, 0x9d, 0x35, 0x40, 0x43, 0x7a, 0x37, + 0x01, 0xe1, 0x7e, 0x37, 0x01, 0xdd, 0x7d, 0x43, 0x2d, 0x45, 0xbe, 0x44, 0x05, 0xe2, 0x38, 0x52, + 0x7a, 0x47, 0x01, 0xdd, 0x7d, 0x45, 0x12, 0x73, 0x16, 0xa9, 0x20, 0xf5, 0x22, 0x75, 0x31, 0x19, + 0x12, 0x7c, 0x15, 0x7a, 0x91, 0x31, 0x12, 0x7c, 0x15, 0x7a, 0x81, 0xf7, 0x7a, 0x91, 0xf6, 0xe5, 0xf5, 0x33, 0x82, 0xe7, 0x50, 0xbc, 0xda, 0x2b, 0xda, 0x39, 0xda, 0x09, 0x22, 0x80, 0x41, 0x80, - 0x64, 0x2d, 0x53, 0x6d, 0x33, 0x70, 0xb7, 0x7e, 0x04, 0x01, 0xcd, 0x7a, 0x07, 0x01, 0xc9, 0x7a, - 0x07, 0x01, 0xc7, 0xa9, 0x32, 0xf2, 0xdf, 0x85, 0x30, 0x2f, 0x12, 0x7e, 0x30, 0x75, 0xf6, 0x00, - 0x80, 0xd4, 0xca, 0x59, 0x9e, 0x44, 0x05, 0xcd, 0x9d, 0x54, 0x12, 0x75, 0x26, 0x7e, 0x34, 0x01, - 0xcd, 0x7d, 0x54, 0x2d, 0x43, 0x7a, 0x47, 0x01, 0xc7, 0x12, 0x75, 0x26, 0xda, 0x49, 0x80, 0x99, - 0xe5, 0xf5, 0x54, 0x03, 0x78, 0x1f, 0x7e, 0x0f, 0x29, 0xd9, 0x0b, 0x0c, 0x7a, 0x0f, 0x29, 0xd9, - 0x80, 0x9d, 0x7e, 0x0f, 0x29, 0xe1, 0x0b, 0x0c, 0x7a, 0x0f, 0x29, 0xe1, 0xa9, 0xd7, 0xf4, 0xa9, - 0x27, 0xf4, 0xfc, 0x80, 0x8a, 0x7e, 0x0f, 0x29, 0xdd, 0x0b, 0x0c, 0x7a, 0x0f, 0x29, 0xdd, 0x80, - 0xeb, 0xe5, 0xf5, 0x54, 0x03, 0x78, 0x1f, 0x7e, 0x2f, 0x29, 0xf9, 0x0b, 0x2c, 0x7a, 0x2f, 0x29, - 0xf9, 0x80, 0x34, 0x7e, 0x2f, 0x2a, 0x01, 0x0b, 0x2c, 0x7a, 0x2f, 0x2a, 0x01, 0xa9, 0xd7, 0xf4, - 0xa9, 0x27, 0xf4, 0xfc, 0x80, 0x21, 0x7e, 0x2f, 0x29, 0xfd, 0x0b, 0x2c, 0x7a, 0x2f, 0x29, 0xfd, - 0x80, 0xeb, 0xda, 0x2b, 0xda, 0x1b, 0xda, 0x0b, 0x22, 0x75, 0x2f, 0x28, 0x12, 0x7e, 0x30, 0xca, + 0x64, 0x2d, 0x53, 0x6d, 0x33, 0x70, 0xb7, 0x7e, 0x04, 0x01, 0xe3, 0x7a, 0x07, 0x01, 0xdf, 0x7a, + 0x07, 0x01, 0xdd, 0xa9, 0x32, 0xf2, 0xdf, 0x85, 0x30, 0x31, 0x12, 0x7c, 0x15, 0x75, 0xf6, 0x00, + 0x80, 0xd4, 0xca, 0x59, 0x9e, 0x44, 0x05, 0xe3, 0x9d, 0x54, 0x12, 0x73, 0x16, 0x7e, 0x34, 0x01, + 0xe3, 0x7d, 0x54, 0x2d, 0x43, 0x7a, 0x47, 0x01, 0xdd, 0x12, 0x73, 0x16, 0xda, 0x49, 0x80, 0x99, + 0xe5, 0xf5, 0x54, 0x03, 0x78, 0x1f, 0x7e, 0x0f, 0x29, 0xef, 0x0b, 0x0c, 0x7a, 0x0f, 0x29, 0xef, + 0x80, 0x9d, 0x7e, 0x0f, 0x29, 0xf7, 0x0b, 0x0c, 0x7a, 0x0f, 0x29, 0xf7, 0xa9, 0xd7, 0xf4, 0xa9, + 0x27, 0xf4, 0xfc, 0x80, 0x8a, 0x7e, 0x0f, 0x29, 0xf3, 0x0b, 0x0c, 0x7a, 0x0f, 0x29, 0xf3, 0x80, + 0xeb, 0xe5, 0xf5, 0x54, 0x03, 0x78, 0x1f, 0x7e, 0x2f, 0x2a, 0x0f, 0x0b, 0x2c, 0x7a, 0x2f, 0x2a, + 0x0f, 0x80, 0x34, 0x7e, 0x2f, 0x2a, 0x17, 0x0b, 0x2c, 0x7a, 0x2f, 0x2a, 0x17, 0xa9, 0xd7, 0xf4, + 0xa9, 0x27, 0xf4, 0xfc, 0x80, 0x21, 0x7e, 0x2f, 0x2a, 0x13, 0x0b, 0x2c, 0x7a, 0x2f, 0x2a, 0x13, + 0x80, 0xeb, 0xda, 0x2b, 0xda, 0x1b, 0xda, 0x0b, 0x22, 0x75, 0x31, 0x28, 0x12, 0x7c, 0x15, 0xca, 0x0b, 0xca, 0x1b, 0xca, 0x2b, 0xc2, 0xc4, 0xa9, 0x21, 0xf2, 0xb6, 0xe5, 0xf5, 0x33, 0x72, 0xe7, - 0x40, 0xe0, 0x7e, 0x0d, 0x30, 0x7e, 0x1d, 0x34, 0x7e, 0x2d, 0x38, 0x7e, 0x3d, 0x3c, 0x7e, 0x85, - 0x40, 0x7d, 0x90, 0x4d, 0x91, 0x4d, 0x92, 0x4d, 0x93, 0x4d, 0x94, 0x4d, 0x95, 0x4d, 0x96, 0x4d, + 0x40, 0xe0, 0x7e, 0x0d, 0x46, 0x7e, 0x1d, 0x4a, 0x7e, 0x2d, 0x4e, 0x7e, 0x3d, 0x52, 0x7e, 0x85, + 0x56, 0x7d, 0x90, 0x4d, 0x91, 0x4d, 0x92, 0x4d, 0x93, 0x4d, 0x94, 0x4d, 0x95, 0x4d, 0x96, 0x4d, 0x97, 0x4d, 0x98, 0x68, 0x72, 0x7a, 0x11, 0xf3, 0x7a, 0x01, 0xf3, 0x7a, 0x31, 0xf3, 0x7a, 0x21, 0xf3, 0x7a, 0x51, 0xf3, 0x7a, 0x41, 0xf3, 0x7a, 0x71, 0xf3, 0x7a, 0x61, 0xf3, 0x7a, 0x91, 0xf3, - 0x7a, 0x81, 0xf3, 0x30, 0x73, 0x1a, 0x7a, 0xb1, 0xf3, 0x7a, 0xa1, 0xf3, 0x7a, 0xd1, 0xf3, 0x7a, + 0x7a, 0x81, 0xf3, 0x30, 0x7b, 0x1a, 0x7a, 0xb1, 0xf3, 0x7a, 0xa1, 0xf3, 0x7a, 0xd1, 0xf3, 0x7a, 0xc1, 0xf3, 0x7a, 0xf1, 0xf3, 0x7a, 0xe1, 0xf3, 0x7d, 0x78, 0x7a, 0xf1, 0xf3, 0x7a, 0xe1, 0xf3, - 0xa9, 0x30, 0xf5, 0x03, 0x02, 0x72, 0xe6, 0x75, 0x2f, 0x29, 0x12, 0x7e, 0x30, 0x20, 0x73, 0x0b, - 0x75, 0x2f, 0x0a, 0x12, 0x7e, 0x30, 0x75, 0xf6, 0x0a, 0x80, 0x09, 0x75, 0x2f, 0x12, 0x12, 0x7e, - 0x30, 0x75, 0xf6, 0x12, 0x6d, 0x00, 0x7d, 0x10, 0x7a, 0x0d, 0x30, 0x7a, 0x0d, 0x34, 0x7a, 0x0d, - 0x38, 0x7a, 0x0d, 0x3c, 0x7a, 0x05, 0x40, 0xda, 0x2b, 0xda, 0x1b, 0xda, 0x0b, 0x22, 0x1e, 0xb0, - 0x40, 0x0c, 0x7e, 0xa0, 0x0a, 0xa4, 0x7e, 0x04, 0x75, 0x0b, 0x9d, 0x05, 0x89, 0x04, 0x7e, 0xa1, + 0xa9, 0x30, 0xf5, 0x03, 0x02, 0x70, 0xd6, 0x75, 0x31, 0x29, 0x12, 0x7c, 0x15, 0x20, 0x7b, 0x0b, + 0x75, 0x31, 0x0a, 0x12, 0x7c, 0x15, 0x75, 0xf6, 0x0a, 0x80, 0x09, 0x75, 0x31, 0x12, 0x12, 0x7c, + 0x15, 0x75, 0xf6, 0x12, 0x6d, 0x00, 0x7d, 0x10, 0x7a, 0x0d, 0x46, 0x7a, 0x0d, 0x4a, 0x7a, 0x0d, + 0x4e, 0x7a, 0x0d, 0x52, 0x7a, 0x05, 0x56, 0xda, 0x2b, 0xda, 0x1b, 0xda, 0x0b, 0x22, 0x1e, 0xb0, + 0x40, 0x0c, 0x7e, 0xa0, 0x0a, 0xa4, 0x7e, 0x04, 0x72, 0xfb, 0x9d, 0x05, 0x89, 0x04, 0x7e, 0xa1, 0xe3, 0x7a, 0x39, 0xa0, 0x0b, 0x34, 0x80, 0xea, 0xb4, 0x40, 0xe3, 0x7e, 0xa1, 0xe3, 0xe5, 0xe3, 0x1b, 0x38, 0x50, 0x0b, 0x35, 0x7e, 0xa1, 0xe3, 0xe5, 0xe3, 0x1b, 0x38, 0x50, 0x0b, 0x35, 0x7e, 0xa1, 0xe3, 0xe5, 0xe3, 0x1b, 0x38, 0x50, 0x0b, 0x35, 0x7e, 0xa1, 0xe3, 0xe5, 0xe3, 0x1b, 0x38, @@ -937,7 +1044,7 @@ static unsigned char IMAGE_ARRAY_NAME[] = { 0x50, 0x0b, 0x35, 0x7e, 0xa1, 0xe3, 0xe5, 0xe3, 0x1b, 0x38, 0x50, 0x0b, 0x35, 0x7e, 0xa1, 0xe3, 0xe5, 0xe3, 0x1b, 0x38, 0x50, 0x0b, 0x35, 0x7e, 0xa1, 0xe3, 0xe5, 0xe3, 0x1b, 0x38, 0x50, 0x0b, 0x35, 0x7e, 0xa1, 0xe3, 0xe5, 0xe3, 0x1b, 0x38, 0x50, 0x0b, 0x35, 0x22, 0x1e, 0xb0, 0x40, 0x0c, - 0x7e, 0xa0, 0x0a, 0xa4, 0x7e, 0x04, 0x76, 0x69, 0x9d, 0x05, 0x89, 0x04, 0x7e, 0x39, 0xa0, 0x7a, + 0x7e, 0xa0, 0x0a, 0xa4, 0x7e, 0x04, 0x74, 0x59, 0x9d, 0x05, 0x89, 0x04, 0x7e, 0x39, 0xa0, 0x7a, 0xa1, 0xf3, 0x0b, 0x34, 0x80, 0xea, 0xb4, 0x40, 0xe3, 0x0b, 0x38, 0x50, 0x7a, 0xa1, 0xf3, 0xf5, 0xf3, 0x0b, 0x35, 0x0b, 0x38, 0x50, 0x7a, 0xa1, 0xf3, 0xf5, 0xf3, 0x0b, 0x35, 0x0b, 0x38, 0x50, 0x7a, 0xa1, 0xf3, 0xf5, 0xf3, 0x0b, 0x35, 0x0b, 0x38, 0x50, 0x7a, 0xa1, 0xf3, 0xf5, 0xf3, 0x0b, @@ -958,144 +1065,144 @@ static unsigned char IMAGE_ARRAY_NAME[] = { 0x7a, 0xa1, 0xf3, 0xf5, 0xf3, 0x0b, 0x35, 0x0b, 0x38, 0x50, 0x7a, 0xa1, 0xf3, 0xf5, 0xf3, 0x0b, 0x35, 0x0b, 0x38, 0x50, 0x7a, 0xa1, 0xf3, 0xf5, 0xf3, 0x0b, 0x35, 0x0b, 0x38, 0x50, 0x7a, 0xa1, 0xf3, 0xf5, 0xf3, 0x0b, 0x35, 0x0b, 0x38, 0x50, 0x7a, 0xa1, 0xf3, 0xf5, 0xf3, 0x0b, 0x35, 0x0b, - 0x38, 0x50, 0x7a, 0xa1, 0xf3, 0xf5, 0xf3, 0x0b, 0x35, 0x22, 0xc2, 0xaf, 0x7e, 0x37, 0x01, 0xc5, - 0x4d, 0x33, 0x68, 0x3b, 0x7e, 0x07, 0x01, 0xc1, 0x7e, 0x54, 0x09, 0xcd, 0x9d, 0x50, 0xbd, 0x35, - 0x40, 0x02, 0x7d, 0x35, 0xca, 0x39, 0x7e, 0x65, 0x4b, 0x99, 0x64, 0xda, 0x39, 0x7e, 0x07, 0x01, - 0xc5, 0x9d, 0x03, 0x7a, 0x07, 0x01, 0xc5, 0x2e, 0x37, 0x01, 0xc1, 0x7a, 0x37, 0x01, 0xc1, 0xbe, - 0x34, 0x09, 0xcc, 0x28, 0xc7, 0x7e, 0x34, 0x05, 0xcd, 0x7a, 0x37, 0x01, 0xc1, 0x80, 0xbd, 0xd2, - 0xaf, 0x22, 0x75, 0x2f, 0x53, 0x12, 0x7e, 0x30, 0x7e, 0x15, 0x4d, 0x80, 0x11, 0x75, 0x2f, 0x51, - 0x12, 0x7e, 0x30, 0x0b, 0x08, 0x10, 0x0b, 0x05, 0x9e, 0x34, 0x00, 0x02, 0x28, 0x4d, 0x7c, 0xb2, - 0x20, 0xe7, 0x27, 0x54, 0x07, 0x23, 0x0a, 0x2b, 0x49, 0x22, 0x6e, 0x8c, 0x7c, 0xb2, 0x54, 0x78, - 0x03, 0x03, 0x03, 0x7c, 0x2b, 0x9d, 0x13, 0x40, 0x1a, 0x68, 0x12, 0x7a, 0x15, 0x4d, 0x7a, 0x25, - 0x4f, 0x7e, 0x64, 0x77, 0x47, 0x7a, 0x65, 0x4b, 0x89, 0x24, 0x02, 0x77, 0x55, 0x7e, 0x64, 0x76, - 0xbd, 0x80, 0xf2, 0x2d, 0x13, 0x9d, 0x31, 0xca, 0x39, 0x7d, 0x31, 0x2d, 0x10, 0xca, 0x19, 0xca, - 0x29, 0x99, 0x24, 0xda, 0x29, 0xda, 0x09, 0xda, 0x39, 0x80, 0xa2, 0x7a, 0x15, 0x4d, 0x7e, 0x64, - 0x77, 0x33, 0x4d, 0x33, 0x78, 0x09, 0x7c, 0xb2, 0x20, 0xe7, 0x2a, 0x7e, 0x64, 0x76, 0xb2, 0x7a, - 0x65, 0x4b, 0x22, 0x75, 0x2f, 0x52, 0x12, 0x7e, 0x30, 0x7e, 0x21, 0x4d, 0x7e, 0x09, 0x30, 0x0b, - 0x04, 0x1b, 0x34, 0x78, 0x89, 0x80, 0xd4, 0x75, 0x2f, 0x54, 0x12, 0x7e, 0x30, 0x7e, 0x15, 0x4d, - 0x7e, 0x25, 0x4f, 0x80, 0x90, 0x5e, 0x20, 0x07, 0x54, 0x78, 0x7e, 0x44, 0x77, 0xd1, 0x30, 0xe6, - 0x16, 0x4d, 0x33, 0x68, 0x26, 0x1b, 0x34, 0x7e, 0x09, 0x40, 0x0b, 0x04, 0x7e, 0x44, 0x6a, 0x68, - 0x20, 0xe3, 0x04, 0x7e, 0x44, 0x77, 0xd9, 0xca, 0x09, 0xca, 0x39, 0x99, 0x44, 0xda, 0x39, 0xda, - 0x09, 0x7e, 0x64, 0x76, 0xbd, 0x4d, 0x33, 0x68, 0xa6, 0x89, 0x64, 0x7a, 0x15, 0x4d, 0xf5, 0x4f, - 0x7e, 0x64, 0x77, 0x96, 0x80, 0x99, 0x7e, 0x15, 0x4d, 0xe5, 0x4f, 0x80, 0xc4, 0xc0, 0xd0, 0xc0, - 0xd1, 0xc0, 0xe0, 0xca, 0x19, 0xa9, 0x20, 0xdf, 0x12, 0xa9, 0x21, 0xdf, 0x1b, 0x75, 0x2f, 0x01, - 0x12, 0x7e, 0x30, 0x53, 0xdf, 0xf7, 0x12, 0x40, 0xdc, 0x80, 0x0d, 0x75, 0x2f, 0xfe, 0x12, 0x7e, - 0x30, 0x7e, 0x14, 0x00, 0x53, 0x02, 0x40, 0x51, 0xda, 0x19, 0xd0, 0xe0, 0xd0, 0xd1, 0xd0, 0xd0, - 0x32, 0x03, 0xa5, 0xcb, 0x19, 0xb1, 0x80, 0x00, 0x22, 0x22, 0x02, 0x78, 0x52, 0xca, 0x0b, 0xca, + 0x38, 0x50, 0x7a, 0xa1, 0xf3, 0xf5, 0xf3, 0x0b, 0x35, 0x22, 0xc2, 0xaf, 0x7e, 0x37, 0x01, 0xdb, + 0x4d, 0x33, 0x68, 0x3b, 0x7e, 0x07, 0x01, 0xd7, 0x7e, 0x54, 0x09, 0xe3, 0x9d, 0x50, 0xbd, 0x35, + 0x40, 0x02, 0x7d, 0x35, 0xca, 0x39, 0x7e, 0x65, 0x61, 0x99, 0x64, 0xda, 0x39, 0x7e, 0x07, 0x01, + 0xdb, 0x9d, 0x03, 0x7a, 0x07, 0x01, 0xdb, 0x2e, 0x37, 0x01, 0xd7, 0x7a, 0x37, 0x01, 0xd7, 0xbe, + 0x34, 0x09, 0xe2, 0x28, 0xc7, 0x7e, 0x34, 0x05, 0xe3, 0x7a, 0x37, 0x01, 0xd7, 0x80, 0xbd, 0xd2, + 0xaf, 0x22, 0x75, 0x31, 0x53, 0x12, 0x7c, 0x15, 0x7e, 0x15, 0x63, 0x80, 0x11, 0x75, 0x31, 0x51, + 0x12, 0x7c, 0x15, 0x0b, 0x08, 0x10, 0x0b, 0x05, 0x9e, 0x34, 0x00, 0x02, 0x28, 0x4d, 0x7c, 0xb2, + 0x20, 0xe7, 0x27, 0x54, 0x07, 0x23, 0x0a, 0x2b, 0x49, 0x22, 0x6c, 0x7c, 0x7c, 0xb2, 0x54, 0x78, + 0x03, 0x03, 0x03, 0x7c, 0x2b, 0x9d, 0x13, 0x40, 0x1a, 0x68, 0x12, 0x7a, 0x15, 0x63, 0x7a, 0x25, + 0x65, 0x7e, 0x64, 0x75, 0x37, 0x7a, 0x65, 0x61, 0x89, 0x24, 0x02, 0x75, 0x45, 0x7e, 0x64, 0x74, + 0xad, 0x80, 0xf2, 0x2d, 0x13, 0x9d, 0x31, 0xca, 0x39, 0x7d, 0x31, 0x2d, 0x10, 0xca, 0x19, 0xca, + 0x29, 0x99, 0x24, 0xda, 0x29, 0xda, 0x09, 0xda, 0x39, 0x80, 0xa2, 0x7a, 0x15, 0x63, 0x7e, 0x64, + 0x75, 0x23, 0x4d, 0x33, 0x78, 0x09, 0x7c, 0xb2, 0x20, 0xe7, 0x2a, 0x7e, 0x64, 0x74, 0xa2, 0x7a, + 0x65, 0x61, 0x22, 0x75, 0x31, 0x52, 0x12, 0x7c, 0x15, 0x7e, 0x21, 0x63, 0x7e, 0x09, 0x30, 0x0b, + 0x04, 0x1b, 0x34, 0x78, 0x89, 0x80, 0xd4, 0x75, 0x31, 0x54, 0x12, 0x7c, 0x15, 0x7e, 0x15, 0x63, + 0x7e, 0x25, 0x65, 0x80, 0x90, 0x5e, 0x20, 0x07, 0x54, 0x78, 0x7e, 0x44, 0x75, 0xc1, 0x30, 0xe6, + 0x16, 0x4d, 0x33, 0x68, 0x26, 0x1b, 0x34, 0x7e, 0x09, 0x40, 0x0b, 0x04, 0x7e, 0x44, 0x67, 0x63, + 0x20, 0xe3, 0x04, 0x7e, 0x44, 0x75, 0xc9, 0xca, 0x09, 0xca, 0x39, 0x99, 0x44, 0xda, 0x39, 0xda, + 0x09, 0x7e, 0x64, 0x74, 0xad, 0x4d, 0x33, 0x68, 0xa6, 0x89, 0x64, 0x7a, 0x15, 0x63, 0xf5, 0x65, + 0x7e, 0x64, 0x75, 0x86, 0x80, 0x99, 0x7e, 0x15, 0x63, 0xe5, 0x65, 0x80, 0xc4, 0xc0, 0xd0, 0xc0, + 0xd1, 0xc0, 0xe0, 0xca, 0x19, 0xa9, 0x20, 0xdf, 0x12, 0xa9, 0x21, 0xdf, 0x1b, 0x75, 0x31, 0x01, + 0x12, 0x7c, 0x15, 0x53, 0xdf, 0xf7, 0x12, 0x40, 0xdc, 0x80, 0x0d, 0x75, 0x31, 0xfe, 0x12, 0x7c, + 0x15, 0x7e, 0x14, 0x00, 0x53, 0x02, 0x40, 0x51, 0xda, 0x19, 0xd0, 0xe0, 0xd0, 0xd1, 0xd0, 0xd0, + 0x32, 0x03, 0xa5, 0xcb, 0x19, 0xb1, 0x80, 0x00, 0x22, 0x22, 0x02, 0x76, 0x42, 0xca, 0x0b, 0xca, 0x1b, 0xca, 0x2b, 0xca, 0x3b, 0xca, 0x4b, 0xca, 0x5b, 0xca, 0x6b, 0xca, 0x7b, 0xca, 0xeb, 0xc0, - 0xf1, 0x7e, 0xb3, 0x2a, 0x1d, 0xb4, 0x00, 0x02, 0x80, 0x19, 0xb4, 0x01, 0x16, 0x30, 0xc0, 0x08, - 0x75, 0xf1, 0x00, 0x12, 0x78, 0x3c, 0x80, 0x1f, 0x30, 0xc1, 0x1c, 0x75, 0xf1, 0x00, 0x12, 0x78, - 0xcd, 0x80, 0x14, 0x30, 0xc1, 0x08, 0x75, 0xf1, 0x00, 0x12, 0x78, 0xcd, 0x80, 0x09, 0x30, 0xc0, - 0x06, 0x75, 0xf1, 0x00, 0x12, 0x78, 0x3c, 0xd0, 0xf1, 0xda, 0xeb, 0xda, 0x7b, 0xda, 0x6b, 0xda, + 0xf1, 0x7e, 0xb3, 0x2a, 0x33, 0xb4, 0x00, 0x02, 0x80, 0x19, 0xb4, 0x01, 0x16, 0x30, 0xc0, 0x08, + 0x75, 0xf1, 0x00, 0x12, 0x76, 0x2c, 0x80, 0x1f, 0x30, 0xc1, 0x1c, 0x75, 0xf1, 0x00, 0x12, 0x76, + 0xbd, 0x80, 0x14, 0x30, 0xc1, 0x08, 0x75, 0xf1, 0x00, 0x12, 0x76, 0xbd, 0x80, 0x09, 0x30, 0xc0, + 0x06, 0x75, 0xf1, 0x00, 0x12, 0x76, 0x2c, 0xd0, 0xf1, 0xda, 0xeb, 0xda, 0x7b, 0xda, 0x6b, 0xda, 0x5b, 0xda, 0x4b, 0xda, 0x3b, 0xda, 0x2b, 0xda, 0x1b, 0xda, 0x0b, 0x22, 0xc2, 0xc0, 0x7e, 0xb3, - 0x2a, 0x1d, 0xb4, 0x02, 0x07, 0x12, 0x78, 0x5e, 0x02, 0x78, 0x52, 0x22, 0xb4, 0x01, 0xfc, 0x02, - 0x78, 0x98, 0x7e, 0x00, 0x00, 0x7a, 0x03, 0x2a, 0x1d, 0x7a, 0x03, 0x2a, 0x1e, 0x22, 0x7e, 0xb3, - 0x2a, 0x15, 0x54, 0x60, 0x60, 0x05, 0xb4, 0x40, 0x15, 0x80, 0x13, 0x7e, 0xb3, 0x2a, 0x16, 0xb4, - 0x05, 0x0c, 0x75, 0x2f, 0x71, 0x12, 0x7e, 0x30, 0x7e, 0xb3, 0x2a, 0x18, 0xf5, 0x8f, 0x22, 0x75, - 0xf6, 0x00, 0x22, 0xbe, 0x57, 0x2a, 0x1b, 0x28, 0x04, 0x7e, 0x57, 0x2a, 0x1b, 0x7a, 0x0f, 0x2a, - 0x20, 0x7a, 0x57, 0x2a, 0x24, 0x02, 0x78, 0x98, 0x7e, 0xef, 0x2a, 0x20, 0x7e, 0xf7, 0x2a, 0x24, - 0x7e, 0x07, 0x2a, 0x24, 0x4d, 0x00, 0x68, 0x21, 0x7e, 0x00, 0x00, 0x7e, 0xeb, 0xb0, 0xf5, 0xf3, - 0xa3, 0xa5, 0x08, 0x1b, 0xf4, 0x68, 0x04, 0xa5, 0xb8, 0x08, 0xf0, 0x7a, 0xef, 0x2a, 0x20, 0x7a, - 0xf7, 0x2a, 0x24, 0x75, 0x2f, 0x06, 0x12, 0x7e, 0x30, 0x7a, 0x01, 0xf6, 0x22, 0xc2, 0xc1, 0x75, - 0x2f, 0x03, 0x12, 0x7e, 0x30, 0xa9, 0x36, 0xe2, 0x16, 0xe5, 0xf5, 0x54, 0xc0, 0x68, 0x07, 0xa9, - 0xd7, 0xf4, 0xa9, 0x27, 0xf4, 0xfc, 0x53, 0xe1, 0x3f, 0x43, 0xf2, 0x88, 0x02, 0x79, 0x44, 0x7e, - 0xb3, 0x2a, 0x1e, 0xb4, 0x02, 0x0f, 0xa9, 0xd4, 0xe4, 0x7e, 0xb0, 0x00, 0x7a, 0xb3, 0x2a, 0x1e, - 0x7a, 0xb3, 0x2a, 0x1d, 0x22, 0xb4, 0x01, 0x39, 0x7e, 0x21, 0xe6, 0x7c, 0x32, 0x7e, 0x13, 0x2a, - 0x1f, 0x2c, 0x21, 0x7a, 0x23, 0x2a, 0x1f, 0x7e, 0x00, 0x00, 0x2e, 0x04, 0x2a, 0x26, 0xe5, 0xe3, - 0x7a, 0x09, 0xb0, 0x0b, 0x04, 0xa5, 0xdb, 0xf6, 0xa9, 0xd4, 0xe4, 0x75, 0x2f, 0x70, 0x12, 0x7e, - 0x30, 0x7e, 0xb3, 0x2a, 0x1f, 0x7e, 0xa3, 0x2a, 0x1c, 0xbc, 0xab, 0x78, 0x03, 0x12, 0x79, 0xdb, - 0x22, 0x02, 0x7d, 0x44, 0xe5, 0xe6, 0xb4, 0x08, 0x65, 0xa9, 0xc4, 0xe2, 0x7e, 0x01, 0xe3, 0x7e, - 0x11, 0xe3, 0x7e, 0x31, 0xe3, 0x7e, 0x21, 0xe3, 0x7e, 0x51, 0xe3, 0x7e, 0x41, 0xe3, 0x7e, 0x71, - 0xe3, 0x7e, 0x61, 0xe3, 0x7a, 0x0f, 0x2a, 0x15, 0x7a, 0x1f, 0x2a, 0x19, 0x75, 0x2f, 0x04, 0x12, - 0x7e, 0x30, 0x7a, 0x01, 0x2f, 0x12, 0x7e, 0x30, 0x7a, 0x11, 0x2f, 0x12, 0x7e, 0x30, 0x7a, 0x21, - 0x2f, 0x12, 0x7e, 0x30, 0x7a, 0x31, 0x2f, 0x12, 0x7e, 0x30, 0x7a, 0x41, 0x2f, 0x12, 0x7e, 0x30, - 0x7a, 0x51, 0x2f, 0x12, 0x7e, 0x30, 0x7a, 0x61, 0x2f, 0x12, 0x7e, 0x30, 0x7a, 0x71, 0x2f, 0x12, - 0x7e, 0x30, 0xa9, 0xd4, 0xe4, 0xa9, 0xd7, 0xf4, 0xa9, 0xc6, 0xe2, 0x12, 0x79, 0xaf, 0x22, 0x6d, - 0x00, 0x7e, 0x14, 0x01, 0x02, 0x7a, 0x07, 0x2a, 0x24, 0x7a, 0x03, 0x2a, 0x1f, 0x7e, 0xb3, 0x2a, - 0x15, 0x20, 0xe7, 0x0f, 0x7a, 0x23, 0x2a, 0x1e, 0x7a, 0x33, 0x2a, 0x1d, 0xbe, 0x07, 0x2a, 0x1b, - 0x68, 0x09, 0x22, 0x7a, 0x33, 0x2a, 0x1e, 0x7a, 0x23, 0x2a, 0x1d, 0x7e, 0xb3, 0x2a, 0x15, 0x54, - 0xe3, 0x23, 0x23, 0x30, 0xe0, 0x02, 0xd2, 0xe5, 0x30, 0xe7, 0x02, 0xd2, 0xe4, 0x30, 0xe5, 0x06, - 0x30, 0xe4, 0x03, 0x02, 0x7d, 0x44, 0x54, 0x3e, 0xf5, 0xf0, 0x03, 0x54, 0x1f, 0xc3, 0x25, 0xf0, - 0x90, 0x7a, 0x07, 0x75, 0x84, 0xff, 0x73, 0x02, 0x7b, 0x5b, 0x02, 0x7a, 0x4f, 0x02, 0x7b, 0xf8, - 0x02, 0x7c, 0x13, 0x02, 0x7a, 0xf4, 0x02, 0x7a, 0xb5, 0x02, 0x7c, 0x2c, 0x02, 0x7c, 0x2c, 0x02, - 0x7c, 0x2f, 0x02, 0x7c, 0x2f, 0x02, 0x7c, 0x2f, 0x02, 0x7c, 0x2f, 0x02, 0x7c, 0x2f, 0x02, 0x7c, - 0x2f, 0x02, 0x7c, 0x2f, 0x02, 0x7c, 0x2f, 0x02, 0x7c, 0x35, 0x02, 0x7c, 0xe9, 0x02, 0x7c, 0x32, - 0x02, 0x7c, 0x32, 0x02, 0x7c, 0x32, 0x02, 0x7c, 0x32, 0x02, 0x7c, 0x32, 0x02, 0x7c, 0x32, 0x7e, - 0xb3, 0x2a, 0x16, 0xb4, 0x06, 0x2a, 0x7e, 0xb3, 0x2a, 0x17, 0x60, 0x56, 0x7c, 0x0b, 0x7e, 0x13, - 0x2a, 0x18, 0x7e, 0x17, 0x2a, 0x19, 0x75, 0x2f, 0x72, 0x12, 0x7e, 0x30, 0x7a, 0x01, 0x2f, 0x12, - 0x7e, 0x30, 0x7a, 0x11, 0x2f, 0x12, 0x7e, 0x30, 0x12, 0x7d, 0x4e, 0x40, 0x35, 0x02, 0x78, 0x83, - 0xb4, 0x08, 0x10, 0x75, 0x2f, 0x74, 0x12, 0x7e, 0x30, 0x7e, 0xb3, 0x3f, 0xf1, 0xf5, 0xf3, 0x75, - 0xf6, 0x01, 0x22, 0xb4, 0x00, 0x1c, 0x75, 0x2f, 0x75, 0x12, 0x7e, 0x30, 0x7e, 0xb3, 0x3f, 0xf2, - 0x30, 0xe0, 0x05, 0x75, 0xf3, 0x02, 0x80, 0x03, 0x75, 0xf3, 0x00, 0x75, 0xf3, 0x00, 0x75, 0xf6, - 0x02, 0x22, 0x02, 0x7d, 0x44, 0x7e, 0xb3, 0x2a, 0x16, 0xb4, 0x00, 0x35, 0x75, 0x2f, 0x76, 0x12, - 0x7e, 0x30, 0x7e, 0xb3, 0x2a, 0x1a, 0x54, 0x0f, 0xf5, 0xf1, 0x7e, 0xb3, 0x2a, 0x1a, 0x20, 0xe7, - 0x09, 0xe5, 0xe1, 0x30, 0xe7, 0x0d, 0x74, 0x01, 0x80, 0x0b, 0xe5, 0xe1, 0x30, 0xe6, 0x04, 0x74, - 0x01, 0x80, 0x02, 0x74, 0x00, 0x53, 0xf1, 0x80, 0xf5, 0xf3, 0x75, 0xf3, 0x00, 0x75, 0xf6, 0x02, - 0x22, 0x02, 0x7d, 0x44, 0xc0, 0xf1, 0x7e, 0xb3, 0x2a, 0x1a, 0x54, 0x0f, 0x42, 0xf1, 0x7e, 0xb3, - 0x2a, 0x18, 0xb4, 0x00, 0x45, 0x7e, 0xb3, 0x2a, 0x16, 0xb4, 0x01, 0x24, 0x75, 0x2f, 0x77, 0x12, - 0x7e, 0x30, 0x7e, 0xb3, 0x2a, 0x1a, 0x54, 0x0f, 0x78, 0x05, 0x53, 0xe1, 0x3f, 0x80, 0x37, 0x7e, - 0xb3, 0x2a, 0x1a, 0x20, 0xe7, 0x05, 0x53, 0xe1, 0x7f, 0x80, 0x2b, 0x53, 0xe1, 0xbf, 0x80, 0x26, - 0xb4, 0x03, 0x17, 0x75, 0x2f, 0x78, 0x12, 0x7e, 0x30, 0x7e, 0xb3, 0x2a, 0x1a, 0x20, 0xe7, 0x05, - 0x43, 0xe1, 0x80, 0x80, 0x11, 0x43, 0xe1, 0x40, 0x80, 0x0c, 0x43, 0xe1, 0xc0, 0xd0, 0xf1, 0x75, - 0x2f, 0x07, 0x12, 0x7e, 0x30, 0x22, 0xd0, 0xf1, 0x02, 0x78, 0x7f, 0x7e, 0xb3, 0x2a, 0x16, 0xb4, - 0x09, 0x23, 0x75, 0x2f, 0x79, 0x12, 0x7e, 0x30, 0x7e, 0xb3, 0x2a, 0x18, 0xbe, 0xb3, 0x3f, 0xf1, - 0x68, 0x11, 0xca, 0xb8, 0xc0, 0xf1, 0x12, 0x43, 0x68, 0xd0, 0xf1, 0xda, 0xb8, 0x50, 0x76, 0x7a, - 0xb3, 0x3f, 0xf1, 0x80, 0x6d, 0xb4, 0x05, 0x08, 0x75, 0x2f, 0x7a, 0x12, 0x7e, 0x30, 0x80, 0x62, - 0xb4, 0x03, 0x19, 0x75, 0x2f, 0x7b, 0x12, 0x7e, 0x30, 0x7e, 0xb3, 0x2a, 0x18, 0xb4, 0x01, 0x55, - 0x7e, 0xb3, 0x3f, 0xf2, 0x44, 0x01, 0x7a, 0xb3, 0x3f, 0xf2, 0x80, 0x46, 0xb4, 0x01, 0x19, 0x75, - 0x2f, 0x7c, 0x12, 0x7e, 0x30, 0x7e, 0xb3, 0x2a, 0x18, 0xb4, 0x01, 0x39, 0x7e, 0xb3, 0x3f, 0xf2, - 0x54, 0xfe, 0x7a, 0xb3, 0x3f, 0xf2, 0x80, 0x2a, 0xb4, 0x07, 0x2a, 0x7e, 0xb3, 0x2a, 0x17, 0x60, - 0x24, 0x7c, 0x0b, 0x7e, 0x13, 0x2a, 0x18, 0x7e, 0x17, 0x2a, 0x19, 0x75, 0x2f, 0x73, 0x12, 0x7e, - 0x30, 0x7a, 0x01, 0x2f, 0x12, 0x7e, 0x30, 0x7a, 0x11, 0x2f, 0x12, 0x7e, 0x30, 0x12, 0x7d, 0x7a, - 0x40, 0x03, 0x02, 0x78, 0x7f, 0x02, 0x7d, 0x44, 0x7e, 0xb3, 0x2a, 0x16, 0xb4, 0x0b, 0xf6, 0x75, - 0x2f, 0x7d, 0x12, 0x7e, 0x30, 0x7e, 0xb3, 0x2a, 0x18, 0x7e, 0xa3, 0x2a, 0x1a, 0x4c, 0xab, 0x78, - 0xe4, 0x80, 0xdf, 0x7e, 0xb3, 0x2a, 0x16, 0xb4, 0x0a, 0xdb, 0x75, 0x2f, 0x7e, 0x12, 0x7e, 0x30, - 0x7e, 0xb3, 0x2a, 0x18, 0x70, 0xcf, 0xf5, 0xf3, 0x75, 0xf6, 0x01, 0x22, 0x02, 0x7d, 0x44, 0x02, - 0x7d, 0x44, 0x02, 0x7d, 0x44, 0x7e, 0xb3, 0x2a, 0x16, 0xb4, 0x04, 0x20, 0x75, 0x2f, 0xc3, 0x12, - 0x7e, 0x30, 0x7e, 0x04, 0x00, 0x01, 0x7e, 0x17, 0x2a, 0x17, 0x7e, 0x18, 0x2a, 0x26, 0x7a, 0x1c, - 0x00, 0x00, 0x7e, 0x47, 0x2a, 0x1b, 0x12, 0x7e, 0x3c, 0x02, 0x7c, 0xe3, 0xb4, 0x06, 0x3a, 0x75, - 0x2f, 0xc1, 0x12, 0x7e, 0x30, 0x7e, 0x58, 0x00, 0x00, 0x7a, 0x5c, 0x00, 0xfe, 0x7d, 0xca, 0x7e, - 0xd7, 0x2a, 0x17, 0x7e, 0x78, 0x2a, 0x26, 0x7a, 0x7c, 0x00, 0x00, 0x7e, 0x77, 0x2a, 0x1b, 0x75, - 0x2f, 0xc1, 0x12, 0x7e, 0x30, 0xc0, 0xa8, 0xc0, 0x87, 0xc2, 0xaf, 0xa9, 0xd5, 0x87, 0x12, 0x7e, - 0x75, 0xd0, 0x87, 0xd0, 0xa8, 0x40, 0x4f, 0x80, 0x4a, 0xb4, 0x00, 0x1c, 0xc2, 0xaf, 0xa9, 0xd5, - 0x87, 0x12, 0x78, 0x7f, 0xe4, 0x8d, 0xef, 0x8d, 0xef, 0x8d, 0xef, 0xd5, 0xe0, 0xf7, 0xc0, 0xd1, - 0xca, 0x02, 0xff, 0xca, 0x06, 0x00, 0x00, 0x32, 0xb4, 0x09, 0x12, 0x7e, 0x57, 0x2a, 0x17, 0x4d, - 0x55, 0x68, 0x05, 0xa9, 0xd2, 0xb1, 0x80, 0x03, 0xa9, 0xc2, 0xb1, 0x80, 0x16, 0xb4, 0x07, 0x16, - 0xc2, 0xaf, 0x7e, 0x07, 0x2a, 0x19, 0x7e, 0x17, 0x2a, 0x17, 0xc0, 0xd1, 0xca, 0x18, 0xca, 0x38, - 0xca, 0x28, 0x32, 0x02, 0x78, 0x7f, 0x02, 0x7d, 0x44, 0x7e, 0xb3, 0x2a, 0x16, 0xb4, 0x03, 0x15, - 0x75, 0x2f, 0xc2, 0x12, 0x7e, 0x30, 0x7e, 0x04, 0x00, 0x01, 0x7e, 0x17, 0x2a, 0x17, 0x7e, 0x57, - 0x2a, 0x1b, 0x02, 0x78, 0x83, 0xb4, 0x05, 0x39, 0x75, 0x2f, 0xc0, 0x12, 0x7e, 0x30, 0xc0, 0xa8, - 0xc0, 0x87, 0xc2, 0xaf, 0xa9, 0xd5, 0x87, 0x7e, 0x08, 0x2a, 0x26, 0x7a, 0x0c, 0x00, 0x00, 0x7e, - 0x24, 0x00, 0xfe, 0x7e, 0x37, 0x2a, 0x17, 0x7e, 0x47, 0x2a, 0x1b, 0x12, 0x7e, 0x3c, 0xd0, 0x87, - 0xd0, 0xa8, 0x7e, 0x08, 0x2a, 0x26, 0x7a, 0x0c, 0x00, 0x00, 0x7e, 0x57, 0x2a, 0x1b, 0x02, 0x78, - 0x83, 0x02, 0x7d, 0x44, 0x75, 0x2f, 0x07, 0x12, 0x7e, 0x30, 0x43, 0xe1, 0xc0, 0x22, 0xc0, 0xa8, - 0xc0, 0x87, 0xc2, 0xaf, 0xa9, 0xd5, 0x87, 0x12, 0x7d, 0xb9, 0x40, 0x19, 0x7e, 0x08, 0x2a, 0x26, - 0x7a, 0x0c, 0x00, 0x00, 0xca, 0x0b, 0xca, 0x49, 0x12, 0x7e, 0x3c, 0xda, 0x59, 0xda, 0x0b, 0xd0, - 0x87, 0xd0, 0xa8, 0xc3, 0x22, 0xd0, 0x87, 0xd0, 0xa8, 0x22, 0xc0, 0xa8, 0xc0, 0x87, 0xc2, 0xaf, - 0xa9, 0xd5, 0x87, 0x12, 0x7d, 0xb9, 0x40, 0x2b, 0x7e, 0x58, 0x00, 0x00, 0x7a, 0x5c, 0x00, 0xfe, - 0x7f, 0x61, 0x7e, 0x78, 0x2a, 0x26, 0x7a, 0x7c, 0x00, 0x00, 0x7e, 0x77, 0x2a, 0x1b, 0xbd, 0x74, - 0x78, 0x11, 0x75, 0x2f, 0xc1, 0x12, 0x7e, 0x30, 0x12, 0x7e, 0x75, 0x40, 0x06, 0xd0, 0x87, 0xd0, - 0xa8, 0xc3, 0x22, 0xd0, 0x87, 0xd0, 0xa8, 0xd3, 0x22, 0x7e, 0x24, 0x00, 0xfe, 0x7e, 0x34, 0x7f, - 0xca, 0x0b, 0x1a, 0x50, 0xc5, 0xf0, 0x7d, 0x62, 0x7d, 0x75, 0x7d, 0x87, 0x7e, 0x34, 0x7f, 0xc2, - 0x7e, 0x1b, 0xb0, 0x7e, 0x34, 0x7f, 0x03, 0xb4, 0x01, 0x04, 0x7e, 0x34, 0x7f, 0xcc, 0x7e, 0x1b, - 0xb0, 0xbc, 0x0b, 0x50, 0x49, 0x3e, 0x00, 0x3e, 0x00, 0x0a, 0x50, 0x2d, 0x75, 0x0b, 0x3a, 0x30, - 0x69, 0x53, 0x00, 0x02, 0xbd, 0x38, 0x50, 0x02, 0x2d, 0x38, 0xbc, 0x1b, 0x50, 0x30, 0x3e, 0x10, - 0x3e, 0x10, 0x0a, 0x51, 0x2d, 0x35, 0x69, 0x41, 0x00, 0x02, 0x0b, 0x1a, 0x30, 0xbd, 0x38, 0x50, - 0x02, 0x2d, 0x38, 0xbe, 0x44, 0xff, 0xff, 0x78, 0x05, 0x7e, 0x1b, 0x90, 0x0a, 0x49, 0x4d, 0x44, - 0x68, 0x0c, 0xbe, 0x44, 0x00, 0xff, 0x28, 0x04, 0x7e, 0x44, 0x00, 0xff, 0xc3, 0x22, 0xd3, 0x22, + 0x2a, 0x33, 0xb4, 0x02, 0x07, 0x12, 0x76, 0x4e, 0x02, 0x76, 0x42, 0x22, 0xb4, 0x01, 0xfc, 0x02, + 0x76, 0x88, 0x7e, 0x00, 0x00, 0x7a, 0x03, 0x2a, 0x33, 0x7a, 0x03, 0x2a, 0x34, 0x22, 0x7e, 0xb3, + 0x2a, 0x2b, 0x54, 0x60, 0x60, 0x05, 0xb4, 0x40, 0x15, 0x80, 0x13, 0x7e, 0xb3, 0x2a, 0x2c, 0xb4, + 0x05, 0x0c, 0x75, 0x31, 0x71, 0x12, 0x7c, 0x15, 0x7e, 0xb3, 0x2a, 0x2e, 0xf5, 0x8f, 0x22, 0x75, + 0xf6, 0x00, 0x22, 0xbe, 0x57, 0x2a, 0x31, 0x28, 0x04, 0x7e, 0x57, 0x2a, 0x31, 0x7a, 0x0f, 0x2a, + 0x36, 0x7a, 0x57, 0x2a, 0x3a, 0x02, 0x76, 0x88, 0x7e, 0xef, 0x2a, 0x36, 0x7e, 0xf7, 0x2a, 0x3a, + 0x7e, 0x07, 0x2a, 0x3a, 0x4d, 0x00, 0x68, 0x21, 0x7e, 0x00, 0x00, 0x7e, 0xeb, 0xb0, 0xf5, 0xf3, + 0xa3, 0xa5, 0x08, 0x1b, 0xf4, 0x68, 0x04, 0xa5, 0xb8, 0x08, 0xf0, 0x7a, 0xef, 0x2a, 0x36, 0x7a, + 0xf7, 0x2a, 0x3a, 0x75, 0x31, 0x06, 0x12, 0x7c, 0x15, 0x7a, 0x01, 0xf6, 0x22, 0xc2, 0xc1, 0x75, + 0x31, 0x03, 0x12, 0x7c, 0x15, 0xa9, 0x36, 0xe2, 0x16, 0xe5, 0xf5, 0x54, 0xc0, 0x68, 0x07, 0xa9, + 0xd7, 0xf4, 0xa9, 0x27, 0xf4, 0xfc, 0x53, 0xe1, 0x3f, 0x43, 0xf2, 0x88, 0x02, 0x77, 0x29, 0x7e, + 0xb3, 0x2a, 0x34, 0xb4, 0x02, 0x04, 0xa9, 0xd4, 0xe4, 0x22, 0xb4, 0x01, 0x39, 0x7e, 0x21, 0xe6, + 0x7c, 0x32, 0x7e, 0x13, 0x2a, 0x35, 0x2c, 0x21, 0x7a, 0x23, 0x2a, 0x35, 0x7e, 0x00, 0x00, 0x2e, + 0x04, 0x2a, 0x3c, 0xe5, 0xe3, 0x7a, 0x09, 0xb0, 0x0b, 0x04, 0xa5, 0xdb, 0xf6, 0xa9, 0xd4, 0xe4, + 0x75, 0x31, 0x70, 0x12, 0x7c, 0x15, 0x7e, 0xb3, 0x2a, 0x35, 0x7e, 0xa3, 0x2a, 0x32, 0xbc, 0xab, + 0x78, 0x03, 0x12, 0x77, 0xc0, 0x22, 0x02, 0x7b, 0x29, 0xe5, 0xe6, 0xb4, 0x08, 0x65, 0xa9, 0xc4, + 0xe2, 0x7e, 0x01, 0xe3, 0x7e, 0x11, 0xe3, 0x7e, 0x31, 0xe3, 0x7e, 0x21, 0xe3, 0x7e, 0x51, 0xe3, + 0x7e, 0x41, 0xe3, 0x7e, 0x71, 0xe3, 0x7e, 0x61, 0xe3, 0x7a, 0x0f, 0x2a, 0x2b, 0x7a, 0x1f, 0x2a, + 0x2f, 0x75, 0x31, 0x04, 0x12, 0x7c, 0x15, 0x7a, 0x01, 0x31, 0x12, 0x7c, 0x15, 0x7a, 0x11, 0x31, + 0x12, 0x7c, 0x15, 0x7a, 0x21, 0x31, 0x12, 0x7c, 0x15, 0x7a, 0x31, 0x31, 0x12, 0x7c, 0x15, 0x7a, + 0x41, 0x31, 0x12, 0x7c, 0x15, 0x7a, 0x51, 0x31, 0x12, 0x7c, 0x15, 0x7a, 0x61, 0x31, 0x12, 0x7c, + 0x15, 0x7a, 0x71, 0x31, 0x12, 0x7c, 0x15, 0xa9, 0xd4, 0xe4, 0xa9, 0xd7, 0xf4, 0xa9, 0xc6, 0xe2, + 0x12, 0x77, 0x94, 0x22, 0x6d, 0x00, 0x7e, 0x14, 0x01, 0x02, 0x7a, 0x07, 0x2a, 0x3a, 0x7a, 0x03, + 0x2a, 0x35, 0x7e, 0xb3, 0x2a, 0x2b, 0x20, 0xe7, 0x0f, 0x7a, 0x23, 0x2a, 0x34, 0x7a, 0x33, 0x2a, + 0x33, 0xbe, 0x07, 0x2a, 0x31, 0x68, 0x09, 0x22, 0x7a, 0x33, 0x2a, 0x34, 0x7a, 0x23, 0x2a, 0x33, + 0x7e, 0xb3, 0x2a, 0x2b, 0x54, 0xe3, 0x23, 0x23, 0x30, 0xe0, 0x02, 0xd2, 0xe5, 0x30, 0xe7, 0x02, + 0xd2, 0xe4, 0x30, 0xe5, 0x06, 0x30, 0xe4, 0x03, 0x02, 0x7b, 0x29, 0x54, 0x3e, 0xf5, 0xf0, 0x03, + 0x54, 0x1f, 0xc3, 0x25, 0xf0, 0x90, 0x77, 0xec, 0x75, 0x84, 0xff, 0x73, 0x02, 0x79, 0x40, 0x02, + 0x78, 0x34, 0x02, 0x79, 0xdd, 0x02, 0x79, 0xf8, 0x02, 0x78, 0xd9, 0x02, 0x78, 0x9a, 0x02, 0x7a, + 0x11, 0x02, 0x7a, 0x11, 0x02, 0x7a, 0x14, 0x02, 0x7a, 0x14, 0x02, 0x7a, 0x14, 0x02, 0x7a, 0x14, + 0x02, 0x7a, 0x14, 0x02, 0x7a, 0x14, 0x02, 0x7a, 0x14, 0x02, 0x7a, 0x14, 0x02, 0x7a, 0x1a, 0x02, + 0x7a, 0xce, 0x02, 0x7a, 0x17, 0x02, 0x7a, 0x17, 0x02, 0x7a, 0x17, 0x02, 0x7a, 0x17, 0x02, 0x7a, + 0x17, 0x02, 0x7a, 0x17, 0x7e, 0xb3, 0x2a, 0x2c, 0xb4, 0x06, 0x2a, 0x7e, 0xb3, 0x2a, 0x2d, 0x60, + 0x56, 0x7c, 0x0b, 0x7e, 0x13, 0x2a, 0x2e, 0x7e, 0x17, 0x2a, 0x2f, 0x75, 0x31, 0x72, 0x12, 0x7c, + 0x15, 0x7a, 0x01, 0x31, 0x12, 0x7c, 0x15, 0x7a, 0x11, 0x31, 0x12, 0x7c, 0x15, 0x12, 0x7b, 0x33, + 0x40, 0x35, 0x02, 0x76, 0x73, 0xb4, 0x08, 0x10, 0x75, 0x31, 0x74, 0x12, 0x7c, 0x15, 0x7e, 0xb3, + 0x3f, 0xf1, 0xf5, 0xf3, 0x75, 0xf6, 0x01, 0x22, 0xb4, 0x00, 0x1c, 0x75, 0x31, 0x75, 0x12, 0x7c, + 0x15, 0x7e, 0xb3, 0x3f, 0xf2, 0x30, 0xe0, 0x05, 0x75, 0xf3, 0x02, 0x80, 0x03, 0x75, 0xf3, 0x00, + 0x75, 0xf3, 0x00, 0x75, 0xf6, 0x02, 0x22, 0x02, 0x7b, 0x29, 0x7e, 0xb3, 0x2a, 0x2c, 0xb4, 0x00, + 0x35, 0x75, 0x31, 0x76, 0x12, 0x7c, 0x15, 0x7e, 0xb3, 0x2a, 0x30, 0x54, 0x0f, 0xf5, 0xf1, 0x7e, + 0xb3, 0x2a, 0x30, 0x20, 0xe7, 0x09, 0xe5, 0xe1, 0x30, 0xe7, 0x0d, 0x74, 0x01, 0x80, 0x0b, 0xe5, + 0xe1, 0x30, 0xe6, 0x04, 0x74, 0x01, 0x80, 0x02, 0x74, 0x00, 0x53, 0xf1, 0x80, 0xf5, 0xf3, 0x75, + 0xf3, 0x00, 0x75, 0xf6, 0x02, 0x22, 0x02, 0x7b, 0x29, 0xc0, 0xf1, 0x7e, 0xb3, 0x2a, 0x30, 0x54, + 0x0f, 0x42, 0xf1, 0x7e, 0xb3, 0x2a, 0x2e, 0xb4, 0x00, 0x45, 0x7e, 0xb3, 0x2a, 0x2c, 0xb4, 0x01, + 0x24, 0x75, 0x31, 0x77, 0x12, 0x7c, 0x15, 0x7e, 0xb3, 0x2a, 0x30, 0x54, 0x0f, 0x78, 0x05, 0x53, + 0xe1, 0x3f, 0x80, 0x37, 0x7e, 0xb3, 0x2a, 0x30, 0x20, 0xe7, 0x05, 0x53, 0xe1, 0x7f, 0x80, 0x2b, + 0x53, 0xe1, 0xbf, 0x80, 0x26, 0xb4, 0x03, 0x17, 0x75, 0x31, 0x78, 0x12, 0x7c, 0x15, 0x7e, 0xb3, + 0x2a, 0x30, 0x20, 0xe7, 0x05, 0x43, 0xe1, 0x80, 0x80, 0x11, 0x43, 0xe1, 0x40, 0x80, 0x0c, 0x43, + 0xe1, 0xc0, 0xd0, 0xf1, 0x75, 0x31, 0x07, 0x12, 0x7c, 0x15, 0x22, 0xd0, 0xf1, 0x02, 0x76, 0x6f, + 0x7e, 0xb3, 0x2a, 0x2c, 0xb4, 0x09, 0x23, 0x75, 0x31, 0x79, 0x12, 0x7c, 0x15, 0x7e, 0xb3, 0x2a, + 0x2e, 0xbe, 0xb3, 0x3f, 0xf1, 0x68, 0x11, 0xca, 0xb8, 0xc0, 0xf1, 0x12, 0x43, 0xd4, 0xd0, 0xf1, + 0xda, 0xb8, 0x50, 0x76, 0x7a, 0xb3, 0x3f, 0xf1, 0x80, 0x6d, 0xb4, 0x05, 0x08, 0x75, 0x31, 0x7a, + 0x12, 0x7c, 0x15, 0x80, 0x62, 0xb4, 0x03, 0x19, 0x75, 0x31, 0x7b, 0x12, 0x7c, 0x15, 0x7e, 0xb3, + 0x2a, 0x2e, 0xb4, 0x01, 0x55, 0x7e, 0xb3, 0x3f, 0xf2, 0x44, 0x01, 0x7a, 0xb3, 0x3f, 0xf2, 0x80, + 0x46, 0xb4, 0x01, 0x19, 0x75, 0x31, 0x7c, 0x12, 0x7c, 0x15, 0x7e, 0xb3, 0x2a, 0x2e, 0xb4, 0x01, + 0x39, 0x7e, 0xb3, 0x3f, 0xf2, 0x54, 0xfe, 0x7a, 0xb3, 0x3f, 0xf2, 0x80, 0x2a, 0xb4, 0x07, 0x2a, + 0x7e, 0xb3, 0x2a, 0x2d, 0x60, 0x24, 0x7c, 0x0b, 0x7e, 0x13, 0x2a, 0x2e, 0x7e, 0x17, 0x2a, 0x2f, + 0x75, 0x31, 0x73, 0x12, 0x7c, 0x15, 0x7a, 0x01, 0x31, 0x12, 0x7c, 0x15, 0x7a, 0x11, 0x31, 0x12, + 0x7c, 0x15, 0x12, 0x7b, 0x5f, 0x40, 0x03, 0x02, 0x76, 0x6f, 0x02, 0x7b, 0x29, 0x7e, 0xb3, 0x2a, + 0x2c, 0xb4, 0x0b, 0xf6, 0x75, 0x31, 0x7d, 0x12, 0x7c, 0x15, 0x7e, 0xb3, 0x2a, 0x2e, 0x7e, 0xa3, + 0x2a, 0x30, 0x4c, 0xab, 0x78, 0xe4, 0x80, 0xdf, 0x7e, 0xb3, 0x2a, 0x2c, 0xb4, 0x0a, 0xdb, 0x75, + 0x31, 0x7e, 0x12, 0x7c, 0x15, 0x7e, 0xb3, 0x2a, 0x2e, 0x70, 0xcf, 0xf5, 0xf3, 0x75, 0xf6, 0x01, + 0x22, 0x02, 0x7b, 0x29, 0x02, 0x7b, 0x29, 0x02, 0x7b, 0x29, 0x7e, 0xb3, 0x2a, 0x2c, 0xb4, 0x04, + 0x20, 0x75, 0x31, 0xc3, 0x12, 0x7c, 0x15, 0x7e, 0x04, 0x00, 0x01, 0x7e, 0x17, 0x2a, 0x2d, 0x7e, + 0x18, 0x2a, 0x3c, 0x7a, 0x1c, 0x00, 0x00, 0x7e, 0x47, 0x2a, 0x31, 0x12, 0x7c, 0x21, 0x02, 0x7a, + 0xc8, 0xb4, 0x06, 0x3a, 0x75, 0x31, 0xc1, 0x12, 0x7c, 0x15, 0x7e, 0x58, 0x00, 0x00, 0x7a, 0x5c, + 0x00, 0xfe, 0x7d, 0xca, 0x7e, 0xd7, 0x2a, 0x2d, 0x7e, 0x78, 0x2a, 0x3c, 0x7a, 0x7c, 0x00, 0x00, + 0x7e, 0x77, 0x2a, 0x31, 0x75, 0x31, 0xc1, 0x12, 0x7c, 0x15, 0xc0, 0xa8, 0xc0, 0x87, 0xc2, 0xaf, + 0xa9, 0xd5, 0x87, 0x12, 0x7c, 0x5a, 0xd0, 0x87, 0xd0, 0xa8, 0x40, 0x4f, 0x80, 0x4a, 0xb4, 0x00, + 0x1c, 0xc2, 0xaf, 0xa9, 0xd5, 0x87, 0x12, 0x76, 0x6f, 0xe4, 0x8d, 0xef, 0x8d, 0xef, 0x8d, 0xef, + 0xd5, 0xe0, 0xf7, 0xc0, 0xd1, 0xca, 0x02, 0xff, 0xca, 0x06, 0x00, 0x00, 0x32, 0xb4, 0x09, 0x12, + 0x7e, 0x57, 0x2a, 0x2d, 0x4d, 0x55, 0x68, 0x05, 0xa9, 0xd2, 0xb1, 0x80, 0x03, 0xa9, 0xc2, 0xb1, + 0x80, 0x16, 0xb4, 0x07, 0x16, 0xc2, 0xaf, 0x7e, 0x07, 0x2a, 0x2f, 0x7e, 0x17, 0x2a, 0x2d, 0xc0, + 0xd1, 0xca, 0x18, 0xca, 0x38, 0xca, 0x28, 0x32, 0x02, 0x76, 0x6f, 0x02, 0x7b, 0x29, 0x7e, 0xb3, + 0x2a, 0x2c, 0xb4, 0x03, 0x15, 0x75, 0x31, 0xc2, 0x12, 0x7c, 0x15, 0x7e, 0x04, 0x00, 0x01, 0x7e, + 0x17, 0x2a, 0x2d, 0x7e, 0x57, 0x2a, 0x31, 0x02, 0x76, 0x73, 0xb4, 0x05, 0x39, 0x75, 0x31, 0xc0, + 0x12, 0x7c, 0x15, 0xc0, 0xa8, 0xc0, 0x87, 0xc2, 0xaf, 0xa9, 0xd5, 0x87, 0x7e, 0x08, 0x2a, 0x3c, + 0x7a, 0x0c, 0x00, 0x00, 0x7e, 0x24, 0x00, 0xfe, 0x7e, 0x37, 0x2a, 0x2d, 0x7e, 0x47, 0x2a, 0x31, + 0x12, 0x7c, 0x21, 0xd0, 0x87, 0xd0, 0xa8, 0x7e, 0x08, 0x2a, 0x3c, 0x7a, 0x0c, 0x00, 0x00, 0x7e, + 0x57, 0x2a, 0x31, 0x02, 0x76, 0x73, 0x02, 0x7b, 0x29, 0x75, 0x31, 0x07, 0x12, 0x7c, 0x15, 0x43, + 0xe1, 0xc0, 0x22, 0xc0, 0xa8, 0xc0, 0x87, 0xc2, 0xaf, 0xa9, 0xd5, 0x87, 0x12, 0x7b, 0x9e, 0x40, + 0x19, 0x7e, 0x08, 0x2a, 0x3c, 0x7a, 0x0c, 0x00, 0x00, 0xca, 0x0b, 0xca, 0x49, 0x12, 0x7c, 0x21, + 0xda, 0x59, 0xda, 0x0b, 0xd0, 0x87, 0xd0, 0xa8, 0xc3, 0x22, 0xd0, 0x87, 0xd0, 0xa8, 0x22, 0xc0, + 0xa8, 0xc0, 0x87, 0xc2, 0xaf, 0xa9, 0xd5, 0x87, 0x12, 0x7b, 0x9e, 0x40, 0x2b, 0x7e, 0x58, 0x00, + 0x00, 0x7a, 0x5c, 0x00, 0xfe, 0x7f, 0x61, 0x7e, 0x78, 0x2a, 0x3c, 0x7a, 0x7c, 0x00, 0x00, 0x7e, + 0x77, 0x2a, 0x31, 0xbd, 0x74, 0x78, 0x11, 0x75, 0x31, 0xc1, 0x12, 0x7c, 0x15, 0x12, 0x7c, 0x5a, + 0x40, 0x06, 0xd0, 0x87, 0xd0, 0xa8, 0xc3, 0x22, 0xd0, 0x87, 0xd0, 0xa8, 0xd3, 0x22, 0x7e, 0x24, + 0x00, 0xfe, 0x7e, 0x34, 0x7f, 0xca, 0x0b, 0x1a, 0x50, 0xc5, 0xf0, 0x7d, 0x62, 0x7d, 0x75, 0x7d, + 0x87, 0x7e, 0x34, 0x7f, 0xc2, 0x7e, 0x1b, 0xb0, 0x7e, 0x34, 0x7f, 0x03, 0xb4, 0x01, 0x04, 0x7e, + 0x34, 0x7f, 0xcc, 0x7e, 0x1b, 0xb0, 0xbc, 0x0b, 0x50, 0x49, 0x3e, 0x00, 0x3e, 0x00, 0x0a, 0x50, + 0x2d, 0x75, 0x0b, 0x3a, 0x30, 0x69, 0x53, 0x00, 0x02, 0xbd, 0x38, 0x50, 0x02, 0x2d, 0x38, 0xbc, + 0x1b, 0x50, 0x30, 0x3e, 0x10, 0x3e, 0x10, 0x0a, 0x51, 0x2d, 0x35, 0x69, 0x41, 0x00, 0x02, 0x0b, + 0x1a, 0x30, 0xbd, 0x38, 0x50, 0x02, 0x2d, 0x38, 0xbe, 0x44, 0xff, 0xff, 0x78, 0x05, 0x7e, 0x1b, + 0x90, 0x0a, 0x49, 0x4d, 0x44, 0x68, 0x0c, 0xbe, 0x44, 0x00, 0xff, 0x28, 0x04, 0x7e, 0x44, 0x00, + 0xff, 0xc3, 0x22, 0xd3, 0x22, -// Segment #15, Start Address 00ff7fc6, Length 4 +// Segment #16, Start Address 00ff7fc6, Length 4 0xff,0x00,0xc6,0x7f,0x04,0x00, - 0x01, 0x0c, 0x03, 0x00, + 0x01, 0x10, 0x04, 0x00, -// Segment #16, Start Address 00ff7e30, Length 315 -0xff,0x00,0x30,0x7e,0x3b,0x01, - 0xca, 0x08, 0x7e, 0x01, 0x2f, 0x7a, 0x03, 0x3f, 0xf0, 0xda, 0x08, 0x22, 0x7e, 0x1b, 0xc0, 0x7a, +// Segment #17, Start Address 00ff7c15, Length 330 +0xff,0x00,0x15,0x7c,0x4a,0x01, + 0xca, 0x08, 0x7e, 0x01, 0x31, 0x7a, 0x03, 0x3f, 0xf0, 0xda, 0x08, 0x22, 0x7e, 0x1b, 0xc0, 0x7a, 0x0b, 0xc0, 0x0b, 0x14, 0x0b, 0x34, 0x1b, 0x44, 0x78, 0xf2, 0x22, 0x7f, 0x6f, 0x7f, 0xf0, 0x1b, 0xfc, 0x7c, 0x54, 0x7d, 0x32, 0x80, 0x08, 0xca, 0x1b, 0xca, 0x1b, 0xca, 0x1b, 0xca, 0x1b, 0x9e, 0x44, 0x00, 0x10, 0x50, 0xf2, 0x2e, 0x44, 0x00, 0x10, 0x68, 0x06, 0xca, 0x48, 0x1b, 0x44, 0x78, 0xfa, 0x7f, 0xf6, 0x89, 0xe4, 0xca, 0x6b, 0x5e, 0xd4, 0x00, 0x3f, 0x68, 0x20, 0x7e, 0x84, 0x00, - 0x40, 0x9d, 0x8d, 0xda, 0x6b, 0xbd, 0x87, 0x38, 0x16, 0xca, 0x79, 0x7d, 0x78, 0x12, 0x7e, 0x9f, + 0x40, 0x9d, 0x8d, 0xda, 0x6b, 0xbd, 0x87, 0x38, 0x16, 0xca, 0x79, 0x7d, 0x78, 0x12, 0x7c, 0x84, 0xda, 0x79, 0x40, 0x08, 0x9d, 0x78, 0x68, 0x02, 0x80, 0x05, 0xc2, 0xd7, 0x22, 0xda, 0x6b, 0x43, 0x90, 0x30, 0x74, 0xaa, 0x39, 0xb5, 0x55, 0x55, 0x74, 0x55, 0x39, 0xb5, 0x2a, 0xaa, 0x74, 0xa0, 0x39, 0xb5, 0x55, 0x55, 0x7e, 0x04, 0x00, 0x40, 0x9d, 0x70, 0x50, 0x06, 0x2d, 0x70, 0x7d, 0x07, @@ -1104,16 +1211,17 @@ static unsigned char IMAGE_ARRAY_NAME[] = { 0x1b, 0x54, 0x78, 0xf5, 0x80, 0x2c, 0x6d, 0x00, 0x7c, 0x20, 0x7f, 0x16, 0x9f, 0x10, 0x7f, 0x27, 0x9f, 0x20, 0x7e, 0x2b, 0x00, 0x7e, 0x1b, 0x10, 0xbc, 0x01, 0x78, 0x16, 0x0b, 0x2c, 0x0b, 0x1c, 0xa5, 0xdb, 0xef, 0x7c, 0xb6, 0x20, 0xe0, 0x03, 0x63, 0x90, 0x30, 0x4d, 0x77, 0x78, 0x93, 0xc2, - 0xd7, 0x22, 0xd2, 0xd7, 0x22, 0x00, 0x04, 0x00, 0x04, 0x00, 0x00, 0x06, 0x04, 0x02, 0x04, 0x00, - 0x02, 0x01, 0x04, 0x01, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x02, 0x02, - 0x02, 0x02, 0x04, 0x00, 0x08, 0x10, 0x02, 0x10, 0x04, 0x02, 0x08, 0x00, 0x01, 0x01, 0x08, 0x7e, - 0x18, 0x7f, 0xbd, 0x7a, 0x1c, 0x00, 0xfe, 0x0b, 0x1a, 0x00, 0xbe, 0x10, 0x14, 0x38, 0x1a, 0x0a, - 0x51, 0x23, 0x7e, 0x18, 0x7f, 0x15, 0x7a, 0x1c, 0x00, 0xff, 0x2d, 0x35, 0x0b, 0x1a, 0x50, 0x60, - 0x08, 0xa5, 0xb8, 0x02, 0x03, 0x4e, 0xa0, 0x08, 0x22, 0x80, 0xfe, + 0xd7, 0x22, 0xd2, 0xd7, 0x22, 0x00, 0x04, 0x00, 0x04, 0x42, 0x08, 0x06, 0x04, 0x02, 0x04, 0x00, + 0x02, 0x01, 0x04, 0x01, 0x02, 0x82, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x04, 0x02, 0x08, 0x10, 0x02, 0x10, 0x04, 0x02, 0x08, 0x00, 0x01, 0x01, 0x08, 0x00, + 0x01, 0x00, 0x02, 0x00, 0x02, 0x02, 0x08, 0x02, 0x04, 0x20, 0x04, 0x7e, 0x18, 0x7f, 0xbd, 0x7a, + 0x1c, 0x00, 0xfe, 0x0b, 0x1a, 0x00, 0x5e, 0x10, 0x1f, 0xbe, 0x10, 0x1a, 0x38, 0x1a, 0x0a, 0x51, + 0x23, 0x7e, 0x18, 0x7c, 0xfa, 0x7a, 0x1c, 0x00, 0xff, 0x2d, 0x35, 0x0b, 0x1a, 0x50, 0x60, 0x08, + 0xa5, 0xb8, 0x02, 0x03, 0x4e, 0xa0, 0x08, 0x22, 0x80, 0xfe, }; static struct edge_firmware_version_info IMAGE_VERSION_NAME = { - 1, 12, 3 }; // Major, Minor, Build + 1, 16, 4 }; // Major, Minor, Build #undef IMAGE_VERSION_NAME diff --git a/drivers/usb/serial/io_fw_down3.h b/drivers/usb/serial/io_fw_down3.h index 20eecb39d..93b56d68a 100644 --- a/drivers/usb/serial/io_fw_down3.h +++ b/drivers/usb/serial/io_fw_down3.h @@ -1,11 +1,11 @@ //************************************************************** //* Edgeport Binary Image (for TI based products) -//* Generated by TIBin2C v1.00 +//* Generated by TIBin2C v2.00 (watchport) //* Copyright (C) 2001 Inside Out Networks, All rights reserved. //************************************************************** -static int IMAGE_SIZE = 12166; +static int IMAGE_SIZE = 12749; struct EDGE_FIRMWARE_VERSION_INFO { @@ -16,7 +16,7 @@ struct EDGE_FIRMWARE_VERSION_INFO static struct EDGE_FIRMWARE_VERSION_INFO IMAGE_VERSION_NAME = { - 4, 1, 0 // Major, Minor, Build + 4, 10, 0 // Major, Minor, Build }; @@ -27,20 +27,20 @@ static unsigned char IMAGE_ARRAY_NAME[] = // WORD Length; // BYTE CheckSum; // }; -0x83, 0x2f, -0x33, +0xca, 0x31, +0xa8, -0x02, 0x24, 0x84, 0x02, 0x1f, 0x36, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x1e, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x01, 0x1a, 0x85, 0x45, -0x8c, 0x85, 0x46, 0x8a, 0xc0, 0xe0, 0xc0, 0xd0, 0xc0, 0xf0, 0xc0, 0x82, 0xc0, 0x83, 0xc0, 0x00, -0xc0, 0x01, 0xc0, 0x02, 0xc0, 0x03, 0xc0, 0x04, 0xc0, 0x05, 0xc0, 0x06, 0xc0, 0x07, 0xe5, 0x44, -0x24, 0x08, 0xf8, 0xe6, 0x60, 0x2b, 0xe5, 0x44, 0x24, 0x10, 0xf8, 0xa6, 0x81, 0xe5, 0x44, 0x75, -0xf0, 0x21, 0xa4, 0x24, 0x05, 0xf5, 0x82, 0xe4, 0x34, 0xf8, 0xf5, 0x83, 0x78, 0x92, 0xe5, 0x81, -0x04, 0xc3, 0x98, 0xf9, 0x94, 0x22, 0x40, 0x03, 0x02, 0x11, 0x1a, 0xe6, 0xf0, 0x08, 0xa3, 0xd9, -0xfa, 0x74, 0x08, 0x25, 0x44, 0xf8, 0x05, 0x44, 0x08, 0xe6, 0x54, 0x80, 0x70, 0x0c, 0xe5, 0x44, -0xb4, 0x07, 0xf3, 0x78, 0x08, 0x75, 0x44, 0x00, 0x80, 0xef, 0xe5, 0x44, 0x24, 0x10, 0xf8, 0x86, -0x81, 0xe5, 0x44, 0x75, 0xf0, 0x21, 0xa4, 0x24, 0x05, 0xf5, 0x82, 0xe4, 0x34, 0xf8, 0xf5, 0x83, -0x78, 0x92, 0xe5, 0x81, 0x04, 0xc3, 0x98, 0xf9, 0xe0, 0xf6, 0x08, 0xa3, 0xd9, 0xfa, 0xd0, 0x07, +0x02, 0x26, 0xfe, 0x02, 0x21, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x1e, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x01, 0x1a, 0x85, 0x3f, +0x8c, 0x85, 0x40, 0x8a, 0xc0, 0xe0, 0xc0, 0xd0, 0xc0, 0xf0, 0xc0, 0x82, 0xc0, 0x83, 0xc0, 0x00, +0xc0, 0x01, 0xc0, 0x02, 0xc0, 0x03, 0xc0, 0x04, 0xc0, 0x05, 0xc0, 0x06, 0xc0, 0x07, 0xe5, 0x3e, +0x24, 0x08, 0xf8, 0xe6, 0x60, 0x2b, 0xe5, 0x3e, 0x24, 0x10, 0xf8, 0xa6, 0x81, 0xe5, 0x3e, 0x75, +0xf0, 0x21, 0xa4, 0x24, 0x05, 0xf5, 0x82, 0xe4, 0x34, 0xf8, 0xf5, 0x83, 0x78, 0x8c, 0xe5, 0x81, +0x04, 0xc3, 0x98, 0xf9, 0x94, 0x22, 0x40, 0x03, 0x02, 0x11, 0x94, 0xe6, 0xf0, 0x08, 0xa3, 0xd9, +0xfa, 0x74, 0x08, 0x25, 0x3e, 0xf8, 0x05, 0x3e, 0x08, 0xe6, 0x54, 0x80, 0x70, 0x0c, 0xe5, 0x3e, +0xb4, 0x07, 0xf3, 0x78, 0x08, 0x75, 0x3e, 0x00, 0x80, 0xef, 0xe5, 0x3e, 0x24, 0x10, 0xf8, 0x86, +0x81, 0xe5, 0x3e, 0x75, 0xf0, 0x21, 0xa4, 0x24, 0x05, 0xf5, 0x82, 0xe4, 0x34, 0xf8, 0xf5, 0x83, +0x78, 0x8c, 0xe5, 0x81, 0x04, 0xc3, 0x98, 0xf9, 0xe0, 0xf6, 0x08, 0xa3, 0xd9, 0xfa, 0xd0, 0x07, 0xd0, 0x06, 0xd0, 0x05, 0xd0, 0x04, 0xd0, 0x03, 0xd0, 0x02, 0xd0, 0x01, 0xd0, 0x00, 0xd0, 0x83, 0xd0, 0x82, 0xd0, 0xf0, 0xd0, 0xd0, 0xd0, 0xe0, 0x32, 0x30, 0x01, 0x4d, 0x30, 0xb4, 0x48, 0x10, 0x00, 0x45, 0x90, 0xff, 0x08, 0xe0, 0x54, 0x20, 0xf8, 0x90, 0xff, 0x48, 0xe0, 0x54, 0x20, 0xf9, @@ -50,747 +50,783 @@ static unsigned char IMAGE_ARRAY_NAME[] = 0xa3, 0xe0, 0xcb, 0xf0, 0x6b, 0x60, 0x02, 0x7e, 0x04, 0x22, 0xc0, 0xe0, 0xc0, 0xd0, 0xc0, 0xf0, 0xc0, 0x82, 0xc0, 0x83, 0xc0, 0x00, 0xc0, 0x01, 0xc0, 0x02, 0xc0, 0x03, 0xc0, 0x04, 0xc0, 0x05, 0xc0, 0x06, 0xc0, 0x07, 0x90, 0xff, 0x93, 0x74, 0x01, 0xf0, 0xe5, 0x81, 0x94, 0xfd, 0x40, 0x03, -0x02, 0x11, 0x1a, 0x85, 0x47, 0x8d, 0x85, 0x48, 0x8b, 0x74, 0xae, 0xf5, 0x82, 0x74, 0xfa, 0xf5, +0x02, 0x11, 0x94, 0x85, 0x41, 0x8d, 0x85, 0x42, 0x8b, 0x74, 0xaf, 0xf5, 0x82, 0x74, 0xfa, 0xf5, 0x83, 0xe0, 0xb4, 0x01, 0x1b, 0xc0, 0x82, 0xc0, 0x83, 0x90, 0xff, 0x4a, 0xe0, 0x30, 0xe7, 0x2c, 0x90, 0xff, 0x4e, 0xe0, 0x30, 0xe7, 0x25, 0xd0, 0x83, 0xd0, 0x82, 0x74, 0x02, 0xf0, 0x80, 0x20, -0xb4, 0x02, 0x1d, 0xc0, 0x82, 0xc0, 0x83, 0x90, 0xff, 0x7a, 0xe0, 0x30, 0xe7, 0x05, 0x12, 0x25, -0x13, 0x80, 0x09, 0xd0, 0x83, 0xd0, 0x82, 0x74, 0x03, 0xf0, 0x80, 0x04, 0xd0, 0x83, 0xd0, 0x82, +0xb4, 0x02, 0x1d, 0xc0, 0x82, 0xc0, 0x83, 0x90, 0xff, 0x7a, 0xe0, 0x30, 0xe7, 0x05, 0x12, 0x27, +0x8d, 0x80, 0x09, 0xd0, 0x83, 0xd0, 0x82, 0x74, 0x03, 0xf0, 0x80, 0x04, 0xd0, 0x83, 0xd0, 0x82, 0xa3, 0xe0, 0xb4, 0x01, 0x1b, 0xc0, 0x82, 0xc0, 0x83, 0x90, 0xff, 0x52, 0xe0, 0x30, 0xe7, 0x2c, -0x90, 0xff, 0x56, 0xe0, 0x30, 0xe7, 0x25, 0xd0, 0x83, 0xd0, 0x82, 0x74, 0x02, 0xf0, 0x80, 0x20, -0xb4, 0x02, 0x1d, 0xc0, 0x82, 0xc0, 0x83, 0x90, 0xff, 0x7a, 0xe0, 0x30, 0xe7, 0x05, 0x12, 0x25, -0x13, 0x80, 0x09, 0xd0, 0x83, 0xd0, 0x82, 0x74, 0x03, 0xf0, 0x80, 0x04, 0xd0, 0x83, 0xd0, 0x82, -0x20, 0x02, 0x03, 0x30, 0x01, 0x7b, 0x74, 0x16, 0xf5, 0x82, 0x74, 0xf9, 0xf5, 0x83, 0xe0, 0x14, -0xfc, 0xf0, 0xa3, 0xe0, 0xfd, 0xa3, 0xe0, 0xfe, 0x64, 0x04, 0x70, 0x0f, 0xec, 0x70, 0x62, 0x7e, -0x01, 0x12, 0x00, 0xc9, 0x7c, 0x0a, 0x7d, 0xfa, 0x02, 0x02, 0x22, 0x12, 0x00, 0xc9, 0xee, 0x64, -0x04, 0x60, 0x1d, 0xec, 0x70, 0x4b, 0x7c, 0x0a, 0xed, 0x14, 0xfd, 0x70, 0x15, 0xee, 0x64, 0x02, -0x60, 0x07, 0x7e, 0x02, 0x7d, 0x32, 0x02, 0x02, 0x22, 0x7e, 0x01, 0x7d, 0xfa, 0x02, 0x02, 0x22, -0x7c, 0x0a, 0x74, 0x16, 0xf5, 0x82, 0x74, 0xf9, 0xf5, 0x83, 0xec, 0xf0, 0xa3, 0xed, 0xf0, 0xa3, -0xee, 0xf0, 0x14, 0x60, 0x18, 0x20, 0xe1, 0x0f, 0x20, 0x01, 0x06, 0xd2, 0xb1, 0xc2, 0xb0, 0x80, -0x10, 0xc2, 0xb1, 0xd2, 0xb0, 0x80, 0x0a, 0xc2, 0xb1, 0xc2, 0xb0, 0x80, 0x04, 0xd2, 0xb0, 0xd2, -0xb1, 0x78, 0x19, 0x79, 0x09, 0x7a, 0x07, 0xe7, 0x70, 0x04, 0xa6, 0x00, 0x80, 0x0b, 0xe6, 0x60, -0x08, 0x16, 0xe6, 0x70, 0x04, 0xe7, 0x44, 0x80, 0xf7, 0x08, 0x09, 0xda, 0xea, 0xe5, 0x43, 0x60, -0x13, 0x14, 0xf5, 0x43, 0x70, 0x0e, 0xe5, 0x44, 0x24, 0x08, 0xf8, 0x76, 0x00, 0x12, 0x10, 0x95, -0xd2, 0x8c, 0xd2, 0x8d, 0xd0, 0x07, 0xd0, 0x06, 0xd0, 0x05, 0xd0, 0x04, 0xd0, 0x03, 0xd0, 0x02, -0xd0, 0x01, 0xd0, 0x00, 0xd0, 0x83, 0xd0, 0x82, 0xd0, 0xf0, 0xd0, 0xd0, 0xd0, 0xe0, 0x32, 0x90, -0xff, 0x04, 0xe0, 0x90, 0xfa, 0xb5, 0xf0, 0x90, 0xff, 0x06, 0xe0, 0xfc, 0xa3, 0xe0, 0xfa, 0xec, -0xff, 0xea, 0xfe, 0xef, 0xc3, 0x94, 0x08, 0xee, 0x94, 0x01, 0x50, 0x02, 0x80, 0x04, 0x7e, 0x01, -0x7f, 0x08, 0x8e, 0x34, 0x8f, 0x35, 0x90, 0xff, 0x02, 0xe0, 0xfc, 0xa3, 0xe0, 0xfa, 0xec, 0xff, -0xea, 0x90, 0xfa, 0xb9, 0xf0, 0xef, 0xa3, 0xf0, 0x12, 0x18, 0x49, 0x78, 0x24, 0x7c, 0x00, 0x7d, -0x00, 0x12, 0x19, 0x6c, 0x7e, 0x00, 0x7f, 0x05, 0x12, 0x13, 0x8f, 0xe4, 0xf5, 0x53, 0xe5, 0x53, -0xc3, 0x94, 0x02, 0x50, 0x0f, 0x12, 0x18, 0x2a, 0xe4, 0x12, 0x13, 0xfb, 0x05, 0x53, 0x04, 0x12, -0x18, 0x1b, 0x80, 0xea, 0x12, 0x18, 0x49, 0x90, 0xff, 0x00, 0xe0, 0xff, 0x54, 0x60, 0x24, 0xc0, -0x70, 0x03, 0x02, 0x08, 0xb8, 0x24, 0x40, 0x60, 0x03, 0x02, 0x0e, 0xac, 0x90, 0xfa, 0xb5, 0xe0, -0xfe, 0x54, 0x0f, 0xf5, 0x53, 0xee, 0x30, 0xe7, 0x03, 0xd3, 0x80, 0x01, 0xc3, 0x92, 0x0a, 0x90, -0xff, 0x01, 0xe0, 0x12, 0x15, 0x0f, 0x03, 0x55, 0x00, 0x04, 0x28, 0x01, 0x05, 0x2f, 0x03, 0x05, -0xf6, 0x05, 0x06, 0x38, 0x06, 0x07, 0x9a, 0x08, 0x07, 0xe2, 0x09, 0x08, 0x3e, 0x0a, 0x08, 0x7e, -0x0b, 0x00, 0x00, 0x0e, 0xac, 0xe5, 0x2c, 0x20, 0xe7, 0x03, 0x02, 0x0e, 0xac, 0x90, 0xfa, 0xb9, -0xe0, 0x70, 0x02, 0xa3, 0xe0, 0x60, 0x03, 0x02, 0x0e, 0xac, 0xe5, 0x35, 0x64, 0x02, 0x45, 0x34, -0x60, 0x03, 0x02, 0x0e, 0xac, 0xef, 0x54, 0x1f, 0x14, 0x60, 0x2b, 0x14, 0x60, 0x47, 0x24, 0x02, -0x60, 0x03, 0x02, 0x0e, 0xac, 0xee, 0x60, 0x03, 0x02, 0x0e, 0xac, 0x12, 0x18, 0x2a, 0x74, 0x01, -0x12, 0x13, 0xfb, 0x78, 0x6d, 0xe6, 0x30, 0xe0, 0x08, 0x12, 0x18, 0x2a, 0x74, 0x02, 0x12, 0x13, -0xfb, 0x7f, 0x02, 0x02, 0x2f, 0x6a, 0xe5, 0x2c, 0x20, 0xe1, 0x09, 0x90, 0xfa, 0xb5, 0xe0, 0x60, -0x03, 0x02, 0x0e, 0xac, 0x90, 0xfa, 0xb5, 0xe0, 0xd3, 0x94, 0x01, 0x40, 0x03, 0x02, 0x0e, 0xac, -0x7f, 0x02, 0x02, 0x2f, 0x6a, 0xe5, 0x2c, 0x20, 0xe1, 0x0e, 0x90, 0xfa, 0xb5, 0xe0, 0xff, 0x60, -0x07, 0x64, 0x80, 0x60, 0x03, 0x02, 0x0e, 0xac, 0x12, 0x0f, 0x38, 0x40, 0x03, 0x02, 0x0e, 0xac, -0xe5, 0x53, 0x70, 0x19, 0x30, 0x0a, 0x0b, 0x90, 0xff, 0x80, 0x12, 0x18, 0x27, 0x12, 0x13, 0xfb, -0x80, 0x24, 0x90, 0xff, 0x82, 0x12, 0x18, 0x27, 0x12, 0x13, 0xfb, 0x80, 0x19, 0x15, 0x53, 0x30, -0x0a, 0x0b, 0x12, 0x18, 0xbd, 0x12, 0x18, 0x25, 0x12, 0x13, 0xfb, 0x80, 0x09, 0x12, 0x18, 0xcb, -0x12, 0x18, 0x25, 0x12, 0x13, 0xfb, 0x12, 0x18, 0x2a, 0x12, 0x13, 0xb5, 0x60, 0x05, 0x74, 0x01, -0x12, 0x13, 0xfb, 0x7f, 0x02, 0x02, 0x2f, 0x6a, 0xe5, 0x2c, 0x30, 0xe7, 0x03, 0x02, 0x0e, 0xac, -0xe5, 0x35, 0x45, 0x34, 0x60, 0x03, 0x02, 0x0e, 0xac, 0x12, 0x18, 0xd9, 0x14, 0x60, 0x2d, 0x14, -0x60, 0x59, 0x24, 0x02, 0x60, 0x03, 0x02, 0x0e, 0xac, 0x90, 0xfa, 0xb9, 0xe0, 0x70, 0x04, 0xa3, -0xe0, 0x64, 0x01, 0x60, 0x03, 0x02, 0x0e, 0xac, 0x90, 0xfa, 0xb5, 0xe0, 0x60, 0x03, 0x02, 0x0e, -0xac, 0x78, 0x6d, 0xe6, 0x54, 0xfe, 0xf6, 0xe4, 0xff, 0x02, 0x2f, 0x6a, 0xe5, 0x2c, 0x20, 0xe1, -0x06, 0x20, 0xe0, 0x03, 0x02, 0x0e, 0xac, 0xe5, 0x2c, 0x30, 0xe0, 0x09, 0x90, 0xfa, 0xb5, 0xe0, -0x60, 0x03, 0x02, 0x0e, 0xac, 0xe5, 0x2c, 0x30, 0xe1, 0x0c, 0x90, 0xfa, 0xb5, 0xe0, 0xd3, 0x94, -0x01, 0x40, 0x03, 0x02, 0x0e, 0xac, 0xe4, 0xff, 0x02, 0x2f, 0x6a, 0x90, 0xfa, 0xb9, 0xe0, 0x70, -0x02, 0xa3, 0xe0, 0x60, 0x03, 0x02, 0x0e, 0xac, 0x12, 0x0f, 0x38, 0x40, 0x03, 0x02, 0x0e, 0xac, -0xe5, 0x2c, 0x20, 0xe1, 0x06, 0x20, 0xe0, 0x03, 0x02, 0x0e, 0xac, 0xe5, 0x2c, 0x30, 0xe0, 0x07, -0xe5, 0x53, 0x60, 0x03, 0x02, 0x0e, 0xac, 0xe5, 0x53, 0x70, 0x0f, 0x90, 0xff, 0x82, 0xe0, 0x54, -0xf7, 0xf0, 0x90, 0xff, 0x80, 0xe0, 0x54, 0xf7, 0xf0, 0x22, 0xe5, 0x53, 0x24, 0xfe, 0x60, 0x1b, -0x04, 0x70, 0x2e, 0x30, 0x0a, 0x0c, 0xa2, 0x0a, 0xe4, 0x33, 0xfd, 0x7f, 0x03, 0x12, 0x2a, 0xce, -0x80, 0x1f, 0xe4, 0xfd, 0x7f, 0x03, 0x12, 0x2a, 0xce, 0x80, 0x16, 0x30, 0x0a, 0x0c, 0xa2, 0x0a, -0xe4, 0x33, 0xfd, 0x7f, 0x04, 0x12, 0x2a, 0xce, 0x80, 0x07, 0xe4, 0xfd, 0x7f, 0x04, 0x12, 0x2a, -0xce, 0x15, 0x53, 0x30, 0x0a, 0x0b, 0x12, 0x18, 0xbd, 0xf5, 0x83, 0xe0, 0x54, 0xf7, 0xf0, 0x80, -0x09, 0x12, 0x18, 0xcb, 0xf5, 0x83, 0xe0, 0x54, 0xf7, 0xf0, 0xe4, 0xff, 0x02, 0x2f, 0x6a, 0xe5, -0x2c, 0x30, 0xe7, 0x03, 0x02, 0x0e, 0xac, 0xe5, 0x35, 0x45, 0x34, 0x60, 0x03, 0x02, 0x0e, 0xac, -0x12, 0x18, 0xd9, 0x14, 0x60, 0x2d, 0x14, 0x60, 0x55, 0x24, 0x02, 0x60, 0x03, 0x02, 0x0e, 0xac, -0x90, 0xfa, 0xb9, 0xe0, 0x70, 0x04, 0xa3, 0xe0, 0x64, 0x01, 0x60, 0x03, 0x02, 0x0e, 0xac, 0x90, -0xfa, 0xb5, 0xe0, 0x60, 0x03, 0x02, 0x0e, 0xac, 0x78, 0x6d, 0xe6, 0x44, 0x01, 0xf6, 0xe4, 0xff, -0x02, 0x2f, 0x6a, 0xe5, 0x2c, 0x20, 0xe1, 0x06, 0x20, 0xe0, 0x03, 0x02, 0x0e, 0xac, 0xe5, 0x2c, -0x30, 0xe0, 0x07, 0xe5, 0x53, 0x60, 0x03, 0x02, 0x0e, 0xac, 0xe5, 0x2c, 0x30, 0xe1, 0x0a, 0xe5, -0x53, 0xd3, 0x94, 0x01, 0x40, 0x03, 0x02, 0x0e, 0xac, 0xe4, 0xff, 0x02, 0x2f, 0x6a, 0x90, 0xfa, -0xb9, 0xe0, 0x70, 0x02, 0xa3, 0xe0, 0x60, 0x03, 0x02, 0x0e, 0xac, 0x90, 0xfa, 0xb5, 0xe0, 0xff, -0x12, 0x2f, 0x3b, 0x40, 0x03, 0x02, 0x0e, 0xac, 0xe5, 0x2c, 0x20, 0xe1, 0x06, 0x20, 0xe0, 0x03, -0x02, 0x0e, 0xac, 0xe5, 0x53, 0x70, 0x09, 0x30, 0x0a, 0x03, 0x02, 0x19, 0x7a, 0x02, 0x19, 0x3e, -0xe5, 0x2c, 0x20, 0xe1, 0x03, 0x02, 0x0e, 0xac, 0x15, 0x53, 0x30, 0x0a, 0x0b, 0x12, 0x18, 0xbd, -0xf5, 0x83, 0xe0, 0x44, 0x08, 0xf0, 0x80, 0x09, 0x12, 0x18, 0xcb, 0xf5, 0x83, 0xe0, 0x44, 0x08, -0xf0, 0xe4, 0xff, 0x02, 0x2f, 0x6a, 0xe5, 0x2c, 0x30, 0xe7, 0x03, 0x02, 0x0e, 0xac, 0xe5, 0x35, -0x45, 0x34, 0x60, 0x03, 0x02, 0x0e, 0xac, 0x90, 0xfa, 0xb5, 0xe0, 0x60, 0x03, 0x02, 0x0e, 0xac, -0x12, 0x18, 0xd9, 0x60, 0x03, 0x02, 0x0e, 0xac, 0xe5, 0x2c, 0x30, 0xe1, 0x03, 0x02, 0x0e, 0xac, -0x90, 0xfa, 0xba, 0xe0, 0x90, 0xff, 0xff, 0xf0, 0xe0, 0x60, 0x05, 0x43, 0x2c, 0x01, 0x80, 0x03, -0x53, 0x2c, 0xfe, 0xe4, 0xff, 0x02, 0x2f, 0x6a, 0xe5, 0x2c, 0x20, 0xe7, 0x03, 0x02, 0x0e, 0xac, -0xe5, 0x35, 0x45, 0x34, 0x70, 0x03, 0x02, 0x0e, 0xac, 0x12, 0x18, 0xd9, 0x60, 0x03, 0x02, 0x0e, -0xac, 0x90, 0xfa, 0xb9, 0xe0, 0xfc, 0xa3, 0xe0, 0xfd, 0xec, 0x24, 0xfe, 0x60, 0x3a, 0x14, 0x60, -0x75, 0x24, 0x02, 0x60, 0x03, 0x02, 0x0e, 0xac, 0xed, 0x60, 0x03, 0x02, 0x0e, 0xac, 0x12, 0x18, -0x49, 0x12, 0x19, 0x73, 0x7d, 0x03, 0x12, 0x0e, 0xf3, 0x60, 0x03, 0x02, 0x0e, 0xac, 0x12, 0x0e, -0xb0, 0x90, 0xfa, 0xb2, 0xe0, 0xfd, 0xa3, 0x12, 0x18, 0x93, 0x12, 0x0f, 0x0f, 0x50, 0x02, 0x80, -0x04, 0xae, 0x34, 0xaf, 0x35, 0x02, 0x0f, 0x40, 0x12, 0x18, 0x49, 0x90, 0xf9, 0x65, 0xe0, 0x30, -0xe4, 0x0d, 0x12, 0x19, 0x73, 0x7d, 0x14, 0x12, 0x0e, 0xf3, 0x60, 0x10, 0x02, 0x0e, 0xac, 0x12, -0x19, 0x73, 0x7d, 0x04, 0x12, 0x0f, 0x47, 0x60, 0x03, 0x02, 0x0e, 0xac, 0x12, 0x0e, 0xb0, 0x90, -0xfa, 0xb2, 0xe0, 0xfd, 0xa3, 0x12, 0x18, 0x93, 0x12, 0x0f, 0x0f, 0x50, 0x02, 0x80, 0x04, 0xae, -0x34, 0xaf, 0x35, 0x02, 0x0f, 0x40, 0x12, 0x19, 0x73, 0x7d, 0x05, 0x12, 0x0f, 0x47, 0x60, 0x03, -0x02, 0x0e, 0xac, 0x7b, 0x01, 0x7a, 0xfa, 0x79, 0xb2, 0x12, 0x18, 0x90, 0x7d, 0x01, 0x12, 0x23, -0xee, 0x90, 0xfa, 0xb3, 0xe4, 0x75, 0xf0, 0x03, 0x12, 0x14, 0x2f, 0x90, 0xfa, 0xba, 0xe0, 0x90, -0xfa, 0xb1, 0xf0, 0xe4, 0xf5, 0x52, 0x90, 0xfa, 0xb1, 0xe0, 0xff, 0xe5, 0x52, 0xc3, 0x9f, 0x50, -0x24, 0x12, 0x18, 0x8a, 0x12, 0x0f, 0x52, 0xff, 0xfd, 0x90, 0xfa, 0xb3, 0xe4, 0x8d, 0xf0, 0x12, -0x14, 0x2f, 0x90, 0xfa, 0xb2, 0xe0, 0xc3, 0x9f, 0xf0, 0xd3, 0x94, 0x00, 0x50, 0x03, 0x02, 0x0e, -0xac, 0x05, 0x52, 0x80, 0xd1, 0x12, 0x18, 0x8a, 0x12, 0x0f, 0x52, 0x24, 0xfe, 0xff, 0x90, 0xfa, -0xb2, 0xf0, 0xfd, 0xa3, 0xe4, 0x75, 0xf0, 0x02, 0x12, 0x14, 0x2f, 0x7a, 0xf9, 0x79, 0x6e, 0x7b, -0x01, 0x8b, 0x2d, 0x8a, 0x2e, 0x89, 0x2f, 0xe9, 0x24, 0x02, 0xf9, 0xe4, 0x3a, 0xfa, 0x12, 0x18, -0x90, 0x12, 0x23, 0xee, 0x8f, 0x52, 0x05, 0x52, 0x05, 0x52, 0x12, 0x18, 0x2a, 0xe5, 0x52, 0x12, -0x13, 0xfb, 0x12, 0x18, 0x2a, 0x90, 0x00, 0x01, 0x74, 0x03, 0x12, 0x14, 0x0d, 0xaf, 0x52, 0x7e, -0x00, 0xc3, 0xef, 0x95, 0x35, 0xee, 0x95, 0x34, 0x50, 0x02, 0x80, 0x04, 0xae, 0x34, 0xaf, 0x35, -0x8e, 0x30, 0x8f, 0x31, 0x02, 0x29, 0x2d, 0x02, 0x0e, 0xac, 0xe5, 0x2c, 0x20, 0xe7, 0x03, 0x02, -0x0e, 0xac, 0xe5, 0x35, 0x64, 0x01, 0x45, 0x34, 0x60, 0x03, 0x02, 0x0e, 0xac, 0x90, 0xfa, 0xb5, -0xe0, 0x60, 0x03, 0x02, 0x0e, 0xac, 0x90, 0xfa, 0xb9, 0xe0, 0x70, 0x02, 0xa3, 0xe0, 0x60, 0x03, -0x02, 0x0e, 0xac, 0x12, 0x18, 0xd9, 0x60, 0x03, 0x02, 0x0e, 0xac, 0xe5, 0x2c, 0x20, 0xe0, 0x06, -0x20, 0xe1, 0x03, 0x02, 0x0e, 0xac, 0x75, 0x2d, 0x00, 0x75, 0x2e, 0x00, 0x75, 0x2f, 0x29, 0x02, -0x0f, 0x2f, 0xe5, 0x2c, 0x30, 0xe7, 0x03, 0x02, 0x0e, 0xac, 0xe5, 0x35, 0x45, 0x34, 0x60, 0x03, -0x02, 0x0e, 0xac, 0x90, 0xfa, 0xb5, 0xe0, 0x60, 0x03, 0x02, 0x0e, 0xac, 0xd3, 0x90, 0xfa, 0xba, -0xe0, 0x94, 0x01, 0x90, 0xfa, 0xb9, 0xe0, 0x94, 0x00, 0x40, 0x03, 0x02, 0x0e, 0xac, 0x12, 0x18, -0xd9, 0x60, 0x03, 0x02, 0x0e, 0xac, 0xe5, 0x2c, 0x20, 0xe0, 0x06, 0x20, 0xe1, 0x03, 0x02, 0x0e, -0xac, 0x90, 0xfa, 0xba, 0xe0, 0xf5, 0x29, 0xe5, 0x29, 0x70, 0x08, 0x43, 0x2c, 0x01, 0x53, 0x2c, -0xfd, 0x80, 0x06, 0x53, 0x2c, 0xfe, 0x43, 0x2c, 0x02, 0xe4, 0xff, 0x02, 0x2f, 0x6a, 0xe5, 0x2c, -0x20, 0xe7, 0x03, 0x02, 0x0e, 0xac, 0xe5, 0x35, 0x64, 0x01, 0x45, 0x34, 0x60, 0x03, 0x02, 0x0e, -0xac, 0x90, 0xfa, 0xb5, 0xe0, 0x60, 0x03, 0x02, 0x0e, 0xac, 0x90, 0xfa, 0xb9, 0xe0, 0x70, 0x02, -0xa3, 0xe0, 0x60, 0x03, 0x02, 0x0e, 0xac, 0x12, 0x18, 0xd9, 0x64, 0x01, 0x60, 0x03, 0x02, 0x0e, -0xac, 0xe5, 0x2c, 0x20, 0xe1, 0x03, 0x02, 0x0e, 0xac, 0x7f, 0x01, 0x02, 0x2f, 0x6a, 0xe5, 0x2c, -0x30, 0xe7, 0x03, 0x02, 0x0e, 0xac, 0xe5, 0x35, 0x45, 0x34, 0x60, 0x03, 0x02, 0x0e, 0xac, 0xd3, -0x90, 0xfa, 0xba, 0xe0, 0x94, 0x00, 0x90, 0xfa, 0xb9, 0xe0, 0x94, 0x00, 0x40, 0x03, 0x02, 0x0e, -0xac, 0x12, 0x18, 0xd9, 0x64, 0x01, 0x60, 0x03, 0x02, 0x0e, 0xac, 0xe5, 0x2c, 0x20, 0xe1, 0x03, -0x02, 0x0e, 0xac, 0xe4, 0xff, 0x02, 0x2f, 0x6a, 0x90, 0xff, 0x01, 0x12, 0x19, 0x8a, 0xef, 0x12, -0x13, 0xfb, 0x90, 0xfa, 0xb5, 0x12, 0x19, 0x8a, 0x90, 0x00, 0x01, 0xef, 0x12, 0x14, 0x0d, 0x90, -0x00, 0x02, 0xe4, 0x12, 0x14, 0x0d, 0x74, 0x03, 0x12, 0x18, 0x1b, 0x90, 0xfa, 0xb9, 0xe0, 0xff, -0xa3, 0xe0, 0x85, 0x2f, 0x82, 0x85, 0x2e, 0x83, 0xcf, 0xf0, 0xa3, 0xef, 0xf0, 0x90, 0xff, 0x01, -0xe0, 0x12, 0x15, 0x0f, 0x09, 0x3d, 0x02, 0x09, 0x5f, 0x04, 0x09, 0x81, 0x05, 0x09, 0xad, 0x06, -0x09, 0xcb, 0x07, 0x09, 0xe9, 0x08, 0x0a, 0x07, 0x09, 0x0a, 0x25, 0x0b, 0x0a, 0xda, 0x80, 0x0c, -0xfa, 0x81, 0x0d, 0x1c, 0x82, 0x0b, 0x21, 0x83, 0x0b, 0x6a, 0x84, 0x0b, 0x89, 0x85, 0x0b, 0xc5, -0x86, 0x0c, 0x07, 0x87, 0x0c, 0x95, 0x88, 0x0c, 0xd0, 0x89, 0x0a, 0x43, 0x92, 0x0a, 0x43, 0x93, -0x0d, 0xcf, 0xc0, 0x0e, 0x00, 0xc1, 0x0e, 0x11, 0xc2, 0x00, 0x00, 0x0e, 0x9b, 0xe5, 0x2c, 0x20, -0xe7, 0x05, 0x7f, 0x05, 0x02, 0x2e, 0xa5, 0x12, 0x18, 0xe0, 0x60, 0x03, 0x04, 0x70, 0x09, 0xef, -0xfd, 0x7c, 0x00, 0x7f, 0x07, 0x02, 0x10, 0x9c, 0xe4, 0xfd, 0x7f, 0x07, 0x02, 0x2c, 0xc0, 0xe5, -0x2c, 0x20, 0xe7, 0x05, 0x7f, 0x05, 0x02, 0x2e, 0xa5, 0x12, 0x18, 0xe0, 0x60, 0x03, 0x04, 0x70, -0x09, 0xef, 0xfd, 0x7c, 0x00, 0x7f, 0x0c, 0x02, 0x10, 0x9c, 0xe4, 0xfd, 0x7f, 0x07, 0x02, 0x2c, -0xc0, 0xe5, 0x2c, 0x30, 0xe7, 0x03, 0x02, 0x0e, 0xaf, 0x12, 0x19, 0x99, 0x50, 0x06, 0xe5, 0x35, -0x45, 0x34, 0x70, 0x05, 0x7f, 0x02, 0x02, 0x2e, 0xa5, 0x90, 0xfa, 0xb5, 0xe0, 0x24, 0xfe, 0x24, -0xfd, 0x50, 0x02, 0x80, 0x03, 0x02, 0x2f, 0x28, 0x7f, 0x07, 0x02, 0x2e, 0xa5, 0xe5, 0x2c, 0x30, -0xe7, 0x03, 0x02, 0x0e, 0xaf, 0x12, 0x18, 0xe0, 0x60, 0x03, 0x04, 0x70, 0x09, 0xef, 0xfd, 0x7c, -0x00, 0x7f, 0x08, 0x02, 0x10, 0x9c, 0x7f, 0x07, 0x02, 0x2e, 0xa5, 0xe5, 0x2c, 0x30, 0xe7, 0x03, -0x02, 0x0e, 0xaf, 0x12, 0x18, 0xe0, 0x60, 0x03, 0x04, 0x70, 0x09, 0xef, 0xfd, 0x7c, 0x00, 0x7f, -0x09, 0x02, 0x10, 0x9c, 0x7f, 0x07, 0x02, 0x2e, 0xa5, 0xe5, 0x2c, 0x30, 0xe7, 0x03, 0x02, 0x0e, -0xaf, 0x12, 0x18, 0xe0, 0x60, 0x03, 0x04, 0x70, 0x09, 0xef, 0xfd, 0x7c, 0x00, 0x7f, 0x0a, 0x02, -0x10, 0x9c, 0x7f, 0x07, 0x02, 0x2e, 0xa5, 0xe5, 0x2c, 0x30, 0xe7, 0x03, 0x02, 0x0e, 0xaf, 0x12, -0x18, 0xe0, 0x60, 0x03, 0x04, 0x70, 0x09, 0xef, 0xfd, 0x7c, 0x00, 0x7f, 0x0b, 0x02, 0x10, 0x9c, -0x7f, 0x07, 0x02, 0x2e, 0xa5, 0xe5, 0x2c, 0x30, 0xe7, 0x03, 0x02, 0x0e, 0xaf, 0x12, 0x18, 0xe0, -0x60, 0x03, 0x04, 0x70, 0x09, 0xef, 0xfd, 0x7c, 0x00, 0x7f, 0x0e, 0x02, 0x10, 0x9c, 0x7f, 0x07, -0x02, 0x2e, 0xa5, 0xe5, 0x2c, 0x30, 0xe7, 0x56, 0x12, 0x18, 0xd9, 0x70, 0x4a, 0x90, 0xff, 0x02, -0xe0, 0xf5, 0x52, 0xe5, 0x52, 0xb4, 0x82, 0x05, 0x75, 0x52, 0x61, 0x80, 0x12, 0xe5, 0x52, 0xb4, -0x83, 0x05, 0x75, 0x52, 0x62, 0x80, 0x08, 0xe5, 0x52, 0xc4, 0x54, 0xf0, 0x04, 0xf5, 0x52, 0x12, -0x17, 0x8b, 0x12, 0x19, 0x6c, 0x12, 0x22, 0xb8, 0x12, 0x18, 0xe8, 0x12, 0x13, 0xce, 0x60, 0x05, -0x12, 0x2f, 0x76, 0x80, 0x06, 0x85, 0x2a, 0x30, 0x85, 0x2b, 0x31, 0x75, 0x2d, 0x01, 0x75, 0x2e, -0xf9, 0x75, 0x2f, 0x71, 0x02, 0x29, 0x2d, 0xe4, 0xfd, 0x7f, 0x05, 0x02, 0x2c, 0xc0, 0x12, 0x18, -0xd9, 0x60, 0x05, 0x7f, 0x05, 0x02, 0x2e, 0xa5, 0x12, 0x19, 0x99, 0x40, 0x05, 0x7f, 0x03, 0x02, -0x2e, 0xa5, 0x90, 0xff, 0x02, 0xe0, 0xf5, 0x52, 0xe5, 0x52, 0xb4, 0x82, 0x05, 0x75, 0x52, 0x61, -0x80, 0x12, 0xe5, 0x52, 0xb4, 0x83, 0x05, 0x75, 0x52, 0x62, 0x80, 0x08, 0xe5, 0x52, 0xc4, 0x54, -0xf0, 0x04, 0xf5, 0x52, 0x12, 0x17, 0x8b, 0x02, 0x2f, 0x28, 0x12, 0x19, 0xa3, 0x12, 0x27, 0x19, -0x12, 0x18, 0x9b, 0xe0, 0x54, 0x7f, 0xf0, 0x00, 0x00, 0x00, 0xe0, 0x90, 0xfa, 0xb6, 0xf0, 0x78, -0x6e, 0x12, 0x14, 0xeb, 0x90, 0x00, 0x02, 0x12, 0x13, 0xce, 0x30, 0xe7, 0xf2, 0x90, 0x00, 0x02, -0xe4, 0x12, 0x14, 0x0d, 0x90, 0xfa, 0xb6, 0xe0, 0x44, 0x80, 0xff, 0xf0, 0x78, 0x82, 0xe6, 0xfc, -0x08, 0xe6, 0x8c, 0x83, 0x12, 0x18, 0xa3, 0xef, 0xf0, 0x12, 0x2f, 0x80, 0xe4, 0xff, 0x02, 0x2e, -0xa5, 0x90, 0xfa, 0xb5, 0xe0, 0x64, 0x01, 0x70, 0x1f, 0x90, 0xfa, 0xb9, 0xe0, 0xff, 0x7e, 0x00, -0x70, 0x06, 0xa3, 0xe0, 0xf5, 0x90, 0x80, 0x2d, 0xc2, 0xaf, 0xef, 0xf4, 0x52, 0x90, 0x90, 0xfa, -0xba, 0xe0, 0x42, 0x90, 0xd2, 0xaf, 0x80, 0x1d, 0x90, 0xfa, 0xb9, 0xe0, 0xff, 0x7e, 0x00, 0x70, -0x06, 0xa3, 0xe0, 0xf5, 0xb0, 0x80, 0x0e, 0xc2, 0xaf, 0xef, 0xf4, 0x52, 0xb0, 0x90, 0xfa, 0xba, -0xe0, 0x42, 0xb0, 0xd2, 0xaf, 0xe4, 0xff, 0x02, 0x2e, 0xa5, 0x12, 0x18, 0x49, 0x90, 0xfa, 0xb5, -0xe0, 0xb4, 0x01, 0x0a, 0x12, 0x18, 0x2a, 0xe5, 0x90, 0x12, 0x13, 0xfb, 0x80, 0x08, 0x12, 0x18, -0x2a, 0xe5, 0xb0, 0x12, 0x13, 0xfb, 0x02, 0x0f, 0x2f, 0x90, 0xf9, 0x65, 0xe0, 0x20, 0xe1, 0x30, -0x12, 0x18, 0x53, 0x60, 0x18, 0x04, 0x70, 0x28, 0x90, 0xfa, 0xb6, 0xe0, 0x60, 0x09, 0x90, 0xff, -0xa4, 0xe0, 0x44, 0x10, 0xf0, 0x80, 0x19, 0x12, 0x19, 0xad, 0xf0, 0x80, 0x13, 0x90, 0xfa, 0xb6, -0xe0, 0x60, 0x09, 0x90, 0xff, 0xb4, 0xe0, 0x44, 0x10, 0xf0, 0x80, 0x04, 0x12, 0x19, 0xb4, 0xf0, -0xe4, 0xff, 0x02, 0x2e, 0xa5, 0x90, 0xf9, 0x65, 0xe0, 0x20, 0xe1, 0x36, 0x12, 0x18, 0x53, 0x60, -0x1b, 0x04, 0x70, 0x2e, 0x90, 0xfa, 0xb6, 0xe0, 0x60, 0x09, 0x90, 0xff, 0xa4, 0xe0, 0x44, 0x20, -0xf0, 0x80, 0x1f, 0x90, 0xff, 0xa4, 0xe0, 0x54, 0xdf, 0xf0, 0x80, 0x16, 0x90, 0xfa, 0xb6, 0xe0, -0x60, 0x09, 0x90, 0xff, 0xb4, 0xe0, 0x44, 0x20, 0xf0, 0x80, 0x07, 0x90, 0xff, 0xb4, 0xe0, 0x54, -0xdf, 0xf0, 0xe4, 0xff, 0x02, 0x2e, 0xa5, 0x12, 0x18, 0x53, 0x60, 0x46, 0x04, 0x60, 0x03, 0x02, -0x0c, 0x90, 0x90, 0xfa, 0xb6, 0xe0, 0x60, 0x17, 0x90, 0xff, 0xa4, 0xe0, 0x44, 0x04, 0xf0, 0x90, -0xf9, 0x65, 0xe0, 0x30, 0xe1, 0x6a, 0x90, 0xff, 0xa4, 0xe0, 0x44, 0x02, 0xf0, 0x80, 0x61, 0x90, -0xff, 0xa4, 0xe0, 0x54, 0xfb, 0xf0, 0x90, 0xf9, 0x65, 0xe0, 0x30, 0xe1, 0x53, 0x30, 0x95, 0x09, -0x90, 0xff, 0xa4, 0xe0, 0x44, 0x02, 0xf0, 0x80, 0x47, 0x90, 0xff, 0xa4, 0xe0, 0x54, 0xfd, 0xf0, -0x80, 0x3e, 0x90, 0xfa, 0xb6, 0xe0, 0x60, 0x17, 0x90, 0xff, 0xb4, 0xe0, 0x44, 0x04, 0xf0, 0x90, -0xf9, 0x65, 0xe0, 0x30, 0xe1, 0x2a, 0x90, 0xff, 0xb4, 0xe0, 0x44, 0x02, 0xf0, 0x80, 0x21, 0x90, -0xff, 0xb4, 0xe0, 0x54, 0xfb, 0xf0, 0x90, 0xf9, 0x65, 0xe0, 0x30, 0xe1, 0x13, 0x30, 0x93, 0x09, -0x90, 0xff, 0xb4, 0xe0, 0x44, 0x02, 0xf0, 0x80, 0x07, 0x90, 0xff, 0xb4, 0xe0, 0x54, 0xfd, 0xf0, -0xe4, 0xff, 0x02, 0x2e, 0xa5, 0x12, 0x18, 0x53, 0x60, 0x1b, 0x04, 0x70, 0x2e, 0x90, 0xfa, 0xb6, -0xe0, 0x60, 0x09, 0x90, 0xff, 0xa2, 0xe0, 0x44, 0x40, 0xf0, 0x80, 0x1f, 0x90, 0xff, 0xa2, 0xe0, -0x54, 0xbf, 0xf0, 0x80, 0x16, 0x90, 0xfa, 0xb6, 0xe0, 0x60, 0x09, 0x90, 0xff, 0xb2, 0xe0, 0x44, -0x40, 0xf0, 0x80, 0x07, 0x90, 0xff, 0xb2, 0xe0, 0x54, 0xbf, 0xf0, 0xe4, 0xff, 0x02, 0x2e, 0xa5, -0x12, 0x18, 0x49, 0x12, 0x18, 0x5b, 0x60, 0x0f, 0x04, 0x70, 0x16, 0x90, 0xff, 0xa4, 0xe0, 0x12, -0x18, 0x2a, 0x12, 0x13, 0xfb, 0x80, 0x0a, 0x90, 0xff, 0xb4, 0xe0, 0x12, 0x18, 0x2a, 0x12, 0x13, -0xfb, 0x75, 0x30, 0x00, 0x75, 0x31, 0x01, 0x02, 0x29, 0x2d, 0xe4, 0xff, 0x12, 0x2e, 0xa5, 0x12, -0x19, 0x46, 0x7f, 0x03, 0x12, 0x11, 0x9f, 0x90, 0xff, 0xfc, 0xe0, 0x54, 0x7f, 0xf0, 0x7f, 0xff, -0x7e, 0x00, 0x12, 0x2d, 0xee, 0xc2, 0x90, 0xc2, 0xaf, 0x00, 0x80, 0xfd, 0xe4, 0xf5, 0x54, 0xf5, -0x55, 0x90, 0xfa, 0xbb, 0x74, 0x3e, 0xf0, 0xa3, 0xe4, 0xf0, 0x90, 0xfa, 0xb3, 0xf0, 0xa3, 0x74, -0x15, 0xf0, 0xe0, 0x54, 0x3f, 0xff, 0xc3, 0x74, 0x40, 0x9f, 0x90, 0xfa, 0xb8, 0xf0, 0xd3, 0x94, -0x00, 0xe4, 0x94, 0x3e, 0x40, 0x08, 0x90, 0xfa, 0xbc, 0xe0, 0x90, 0xfa, 0xb8, 0xf0, 0x12, 0x0e, -0xd6, 0xe5, 0x23, 0x45, 0x22, 0x70, 0x73, 0x12, 0x18, 0x62, 0x90, 0xfa, 0xbb, 0x12, 0x19, 0x65, -0x60, 0x27, 0xd3, 0xef, 0x94, 0x40, 0xee, 0x94, 0x00, 0x40, 0x08, 0x90, 0xfa, 0xb8, 0x74, 0x40, -0xf0, 0x80, 0x08, 0x90, 0xfa, 0xbc, 0xe0, 0x90, 0xfa, 0xb8, 0xf0, 0x12, 0x0e, 0xd6, 0xe5, 0x23, -0x45, 0x22, 0x70, 0x46, 0x12, 0x18, 0x62, 0x80, 0xd1, 0x75, 0x52, 0x02, 0x90, 0xfa, 0xbb, 0xe4, -0xf0, 0xa3, 0x04, 0xf0, 0x90, 0xfa, 0xb3, 0xe4, 0xf0, 0xa3, 0x74, 0x0f, 0xf0, 0x7b, 0x00, 0x7a, -0x00, 0x79, 0x52, 0x90, 0xfa, 0xbc, 0xe0, 0xf5, 0x50, 0x7d, 0x0f, 0x7c, 0x00, 0x12, 0x26, 0x25, -0x75, 0x22, 0x00, 0x8f, 0x23, 0x7b, 0x00, 0x7a, 0x00, 0x79, 0x52, 0xe4, 0xf5, 0x40, 0xf5, 0x41, -0x7d, 0x01, 0x12, 0x23, 0xee, 0xe4, 0xf5, 0x22, 0xf5, 0x23, 0xaf, 0x23, 0x02, 0x2e, 0xa5, 0x90, -0xfa, 0xba, 0xe0, 0x90, 0xfa, 0xb6, 0xf0, 0x30, 0xe7, 0x10, 0xe0, 0x54, 0x0f, 0x90, 0xf9, 0x62, -0xf0, 0xd3, 0x94, 0x00, 0x40, 0x15, 0xc2, 0x95, 0x80, 0x11, 0x90, 0xfa, 0xb6, 0xe0, 0x54, 0x0f, -0x90, 0xf9, 0x61, 0xf0, 0xd3, 0x94, 0x00, 0x40, 0x02, 0xc2, 0x94, 0xe4, 0xff, 0x02, 0x2e, 0xa5, -0x12, 0x19, 0xa3, 0xbf, 0x01, 0x04, 0xd2, 0x93, 0x80, 0x02, 0xc2, 0x93, 0xe4, 0xff, 0x02, 0x2e, -0xa5, 0x90, 0xfa, 0xba, 0xe0, 0x90, 0xfa, 0xb6, 0xf0, 0x54, 0x03, 0x14, 0x60, 0x0a, 0x14, 0x60, -0x0f, 0x14, 0x60, 0x08, 0x24, 0x03, 0x70, 0x2b, 0xd2, 0x91, 0x80, 0x27, 0xc2, 0x91, 0x80, 0x23, -0x12, 0x19, 0xad, 0x12, 0x0e, 0xfe, 0x60, 0x04, 0xd2, 0x91, 0x80, 0x17, 0x90, 0xff, 0xa4, 0xe0, -0x44, 0x10, 0x12, 0x0e, 0xfe, 0xff, 0xbf, 0xa0, 0x04, 0xc2, 0x91, 0x80, 0x02, 0xd2, 0x91, 0x12, -0x19, 0xad, 0xf0, 0x90, 0xfa, 0xb6, 0xe0, 0x54, 0x0c, 0xff, 0x13, 0x13, 0x54, 0x3f, 0x14, 0x60, -0x0a, 0x14, 0x60, 0x0f, 0x14, 0x60, 0x08, 0x24, 0x03, 0x70, 0x2b, 0xd2, 0x92, 0x80, 0x27, 0xc2, -0x92, 0x80, 0x23, 0x12, 0x19, 0xb4, 0x12, 0x0f, 0x1e, 0x60, 0x04, 0xd2, 0x92, 0x80, 0x17, 0x90, -0xff, 0xb4, 0xe0, 0x44, 0x10, 0x12, 0x0f, 0x1e, 0xff, 0xbf, 0xa0, 0x04, 0xc2, 0x92, 0x80, 0x02, -0xd2, 0x92, 0x12, 0x19, 0xb4, 0xf0, 0xe4, 0xff, 0x02, 0x2e, 0xa5, 0xe5, 0x2c, 0x30, 0xe7, 0x07, -0xe4, 0xfd, 0x7f, 0x05, 0x02, 0x2c, 0xc0, 0x7f, 0x05, 0x02, 0x2e, 0xa5, 0x12, 0x2f, 0x76, 0x22, -0x7b, 0x01, 0x7a, 0xfa, 0x79, 0xb2, 0x90, 0xfa, 0xb3, 0xe0, 0xf5, 0x40, 0xa3, 0xe0, 0xf5, 0x41, -0x7d, 0x01, 0x12, 0x23, 0xee, 0x90, 0xfa, 0xb3, 0xe4, 0x75, 0xf0, 0x03, 0x12, 0x14, 0x2f, 0xab, -0x2d, 0xaa, 0x2e, 0xa9, 0x2f, 0x22, 0xaa, 0x54, 0xa9, 0x55, 0x7b, 0xff, 0x90, 0xfa, 0xb3, 0xe0, -0xfc, 0xa3, 0xe0, 0xfd, 0x90, 0xfa, 0xb8, 0xe0, 0xf5, 0x50, 0x12, 0x26, 0x25, 0x75, 0x22, 0x00, -0x8f, 0x23, 0x22, 0x12, 0x20, 0xc5, 0x7e, 0x00, 0x8e, 0x22, 0x8f, 0x23, 0xef, 0x22, 0xf0, 0x7f, -0x01, 0x12, 0x11, 0x9f, 0x90, 0xff, 0xa6, 0xe0, 0x90, 0xfa, 0xb7, 0xf0, 0x54, 0xa0, 0x22, 0x12, -0x23, 0xee, 0x8f, 0x52, 0x7e, 0x00, 0xc3, 0xef, 0x95, 0x35, 0xee, 0x95, 0x34, 0x22, 0xf0, 0x7f, -0x01, 0x12, 0x11, 0x9f, 0x90, 0xff, 0xb6, 0xe0, 0x90, 0xfa, 0xb7, 0xf0, 0x54, 0xa0, 0x22, 0x75, -0x30, 0x00, 0x75, 0x31, 0x01, 0x02, 0x29, 0x2d, 0x90, 0xfa, 0xb5, 0xe0, 0xff, 0x02, 0x2f, 0x3b, -0x8e, 0x30, 0x8f, 0x31, 0x02, 0x29, 0x2d, 0x12, 0x20, 0xc5, 0x7e, 0x00, 0x8e, 0x22, 0x8f, 0x23, -0xef, 0x22, 0x7d, 0x01, 0x12, 0x23, 0xee, 0x90, 0xfa, 0xb0, 0xe0, 0x22, 0xef, 0x90, 0xf8, 0x04, -0xf0, 0x22, 0xc0, 0xa8, 0xc2, 0xaf, 0xee, 0x60, 0x0a, 0xc0, 0x05, 0x7d, 0x7f, 0xdd, 0xfe, 0xde, -0xfa, 0xd0, 0x05, 0xef, 0xc3, 0x94, 0x15, 0x50, 0x03, 0xd0, 0xa8, 0x22, 0x13, 0x70, 0x03, 0xd0, -0xa8, 0x22, 0xff, 0xd5, 0x07, 0xfd, 0xd0, 0xa8, 0x22, 0xc0, 0x00, 0xc0, 0x01, 0xc0, 0x02, 0xc0, -0x04, 0xc0, 0x05, 0xe5, 0x44, 0x24, 0x08, 0xf8, 0x86, 0x05, 0x53, 0x05, 0x7f, 0x7c, 0xff, 0x12, -0x0f, 0xfe, 0x7f, 0x00, 0x7e, 0x00, 0xe5, 0x49, 0x60, 0x46, 0xfc, 0x90, 0xf9, 0x19, 0xe0, 0x54, -0x7f, 0x6d, 0x70, 0x0f, 0xc0, 0x83, 0xc0, 0x82, 0xa3, 0xe0, 0xfe, 0xa3, 0xe0, 0xff, 0xa3, 0x15, -0x49, 0x80, 0x07, 0xa3, 0xa3, 0xa3, 0xdc, 0xe6, 0x80, 0x26, 0xdc, 0x06, 0xd0, 0x82, 0xd0, 0x83, -0x80, 0x1e, 0xe0, 0xf8, 0xa3, 0xe0, 0xf9, 0xa3, 0xe0, 0xfa, 0xd0, 0x82, 0xd0, 0x83, 0xe8, 0xf0, -0xa3, 0xe9, 0xf0, 0xa3, 0xea, 0xf0, 0xa3, 0xc0, 0x83, 0xc0, 0x82, 0xa3, 0xa3, 0xa3, 0x80, 0xda, -0x12, 0x10, 0x95, 0xd0, 0x05, 0xd0, 0x04, 0xd0, 0x02, 0xd0, 0x01, 0xd0, 0x00, 0x22, 0x85, 0xa8, -0x4a, 0x75, 0xa8, 0x88, 0xec, 0x70, 0x02, 0x7c, 0x3f, 0x8c, 0x43, 0x22, 0xe5, 0x44, 0x24, 0x08, -0xf8, 0x76, 0x00, 0x12, 0x10, 0xec, 0x80, 0xfb, 0xc0, 0x00, 0xc0, 0x01, 0xc0, 0x02, 0xc0, 0x04, -0xc0, 0x06, 0x7c, 0xff, 0x12, 0x0f, 0xfe, 0xe5, 0x49, 0x60, 0x42, 0xfe, 0x90, 0xf9, 0x19, 0xe0, -0x54, 0x7f, 0x6f, 0x70, 0x0b, 0xc0, 0x83, 0xc0, 0x82, 0xa3, 0xa3, 0xa3, 0x15, 0x49, 0x80, 0x07, -0xa3, 0xa3, 0xa3, 0xde, 0xea, 0x80, 0x26, 0xde, 0x06, 0xd0, 0x82, 0xd0, 0x83, 0x80, 0xd8, 0xe0, -0xf8, 0xa3, 0xe0, 0xf9, 0xa3, 0xe0, 0xfa, 0xd0, 0x82, 0xd0, 0x83, 0xe8, 0xf0, 0xa3, 0xe9, 0xf0, -0xa3, 0xea, 0xf0, 0xa3, 0xc0, 0x83, 0xc0, 0x82, 0xa3, 0xa3, 0xa3, 0x80, 0xda, 0x78, 0x08, 0x08, -0x79, 0x18, 0x09, 0x7c, 0x01, 0xe6, 0x54, 0x7f, 0x6f, 0x70, 0x06, 0x76, 0x00, 0x77, 0x00, 0x80, -0x06, 0x08, 0x09, 0x0c, 0xbc, 0x08, 0xee, 0x12, 0x10, 0x95, 0xd0, 0x06, 0xd0, 0x04, 0xd0, 0x02, -0xd0, 0x01, 0xd0, 0x00, 0x22, 0x75, 0x43, 0x00, 0x85, 0x4a, 0xa8, 0x22, 0xc0, 0xf0, 0xc0, 0x82, -0xc0, 0x83, 0xc3, 0xe5, 0x49, 0x24, 0xe8, 0x50, 0x05, 0x12, 0x10, 0xec, 0x80, 0xf4, 0xef, 0x60, -0x31, 0x90, 0x2e, 0x2c, 0xe4, 0x93, 0xc3, 0x9f, 0x40, 0x2f, 0xc0, 0x04, 0x7c, 0xff, 0x12, 0x0f, -0xfe, 0xd0, 0x04, 0x43, 0x07, 0x80, 0xe5, 0x49, 0x75, 0xf0, 0x03, 0xa4, 0x24, 0x19, 0xf5, 0x82, -0xe4, 0x34, 0xf9, 0xf5, 0x83, 0xef, 0xf0, 0xec, 0xa3, 0xf0, 0xed, 0xa3, 0xf0, 0x05, 0x49, 0x12, -0x10, 0x95, 0xd0, 0x83, 0xd0, 0x82, 0xd0, 0xf0, 0x22, 0x02, 0x11, 0x1a, 0xc0, 0x04, 0x7c, 0x20, -0xd2, 0x8c, 0xd2, 0x8d, 0xd5, 0x04, 0xfd, 0xd0, 0x04, 0x22, 0x75, 0xa8, 0x00, 0x75, 0x88, 0x00, -0x75, 0xb8, 0x00, 0x75, 0xf0, 0x00, 0x75, 0xd0, 0x00, 0xe4, 0xf8, 0x90, 0xf8, 0x04, 0xf0, 0x90, -0x00, 0x00, 0xf6, 0x08, 0xb8, 0x00, 0xfb, 0x02, 0x00, 0x00, 0xc2, 0xaf, 0xe4, 0x90, 0xff, 0x48, -0xf0, 0x90, 0xff, 0x50, 0xf0, 0x90, 0xff, 0x08, 0xf0, 0x90, 0xff, 0x10, 0xf0, 0x90, 0xff, 0x80, -0xf0, 0xa3, 0xa3, 0xf0, 0xd2, 0xb1, 0xc2, 0xb0, 0x7e, 0xff, 0x7f, 0xff, 0x12, 0x0f, 0x62, 0x7e, -0xff, 0x7f, 0xff, 0x12, 0x0f, 0x62, 0x7e, 0xff, 0x7f, 0xff, 0x12, 0x0f, 0x62, 0xd2, 0xb0, 0xd2, -0xb1, 0x7e, 0xff, 0x7f, 0xff, 0x12, 0x0f, 0x62, 0x7e, 0xff, 0x7f, 0xff, 0x12, 0x0f, 0x62, 0x7e, -0xff, 0x7f, 0xff, 0x12, 0x0f, 0x62, 0x80, 0xcc, 0xc3, 0xee, 0x94, 0x02, 0x50, 0x04, 0x7e, 0x03, -0x7f, 0xe8, 0xef, 0xf4, 0xff, 0xee, 0xf4, 0xfe, 0x0f, 0xbf, 0x00, 0x01, 0x0e, 0x8f, 0x48, 0x8e, -0x47, 0x22, 0xc3, 0xef, 0x94, 0xbc, 0xee, 0x94, 0x02, 0x50, 0x04, 0x7e, 0x07, 0x7f, 0xd0, 0xef, -0xf4, 0xff, 0xee, 0xf4, 0xfe, 0x0f, 0xbf, 0x00, 0x01, 0x0e, 0x8f, 0x46, 0x8e, 0x45, 0x22, 0xef, -0x70, 0x01, 0x22, 0xc0, 0x00, 0xe5, 0x44, 0x24, 0x18, 0xf8, 0xa6, 0x07, 0xe5, 0x44, 0x24, 0x08, -0xf8, 0xc6, 0x54, 0x7f, 0xf6, 0xe6, 0x30, 0xe7, 0x03, 0xd0, 0x00, 0x22, 0x12, 0x10, 0xec, 0x80, +0x90, 0xff, 0x56, 0xe0, 0x30, 0xe7, 0x25, 0xd0, 0x83, 0xd0, 0x82, 0x74, 0x02, 0xf0, 0x80, 0x25, +0xb4, 0x02, 0x22, 0xc0, 0x82, 0xc0, 0x83, 0x90, 0xff, 0x7a, 0xe0, 0x30, 0xe7, 0x05, 0x12, 0x27, +0x8d, 0x80, 0x09, 0xd0, 0x83, 0xd0, 0x82, 0x74, 0x03, 0xf0, 0x80, 0x09, 0xd0, 0x83, 0xd0, 0x82, +0x80, 0x03, 0x02, 0x02, 0x62, 0x74, 0x15, 0xf5, 0x82, 0x74, 0xf9, 0xf5, 0x83, 0xe0, 0x20, 0x04, +0xf1, 0x20, 0x02, 0x03, 0x30, 0x01, 0xeb, 0x74, 0x18, 0xf5, 0x82, 0x74, 0xf9, 0xf5, 0x83, 0xe0, +0x14, 0xfc, 0xf0, 0xa3, 0xe0, 0xfd, 0xa3, 0xe0, 0xfe, 0x64, 0x04, 0x70, 0x0f, 0xec, 0x70, 0x62, +0x7e, 0x01, 0x12, 0x00, 0xc9, 0x7c, 0x0a, 0x7d, 0xfa, 0x02, 0x02, 0x33, 0x12, 0x00, 0xc9, 0xee, +0x64, 0x04, 0x60, 0x1d, 0xec, 0x70, 0x4b, 0x7c, 0x0a, 0xed, 0x14, 0xfd, 0x70, 0x15, 0xee, 0x64, +0x02, 0x60, 0x07, 0x7e, 0x02, 0x7d, 0x32, 0x02, 0x02, 0x33, 0x7e, 0x01, 0x7d, 0xfa, 0x02, 0x02, +0x33, 0x7c, 0x0a, 0x74, 0x18, 0xf5, 0x82, 0x74, 0xf9, 0xf5, 0x83, 0xec, 0xf0, 0xa3, 0xed, 0xf0, +0xa3, 0xee, 0xf0, 0x14, 0x60, 0x18, 0x20, 0xe1, 0x0f, 0x20, 0x01, 0x06, 0xd2, 0xb1, 0xc2, 0xb0, +0x80, 0x10, 0xc2, 0xb1, 0xd2, 0xb0, 0x80, 0x0a, 0xc2, 0xb1, 0xc2, 0xb0, 0x80, 0x04, 0xd2, 0xb0, +0xd2, 0xb1, 0x78, 0x19, 0x79, 0x09, 0x7a, 0x07, 0xe7, 0x70, 0x04, 0xa6, 0x00, 0x80, 0x0b, 0xe6, +0x60, 0x08, 0x16, 0xe6, 0x70, 0x04, 0xe7, 0x44, 0x80, 0xf7, 0x08, 0x09, 0xda, 0xea, 0xe5, 0x3d, +0x60, 0x13, 0x14, 0xf5, 0x3d, 0x70, 0x0e, 0xe5, 0x3e, 0x24, 0x08, 0xf8, 0x76, 0x00, 0x12, 0x11, +0x0f, 0xd2, 0x8c, 0xd2, 0x8d, 0xd0, 0x07, 0xd0, 0x06, 0xd0, 0x05, 0xd0, 0x04, 0xd0, 0x03, 0xd0, +0x02, 0xd0, 0x01, 0xd0, 0x00, 0xd0, 0x83, 0xd0, 0x82, 0xd0, 0xf0, 0xd0, 0xd0, 0xd0, 0xe0, 0x32, +0x90, 0xff, 0x04, 0xe0, 0x90, 0xfa, 0xb6, 0xf0, 0x90, 0xff, 0x06, 0xe0, 0xfc, 0xa3, 0xe0, 0xfa, +0xec, 0xff, 0xea, 0xfe, 0xef, 0xc3, 0x94, 0x08, 0xee, 0x94, 0x01, 0x50, 0x02, 0x80, 0x04, 0x7e, +0x01, 0x7f, 0x08, 0x8e, 0x3b, 0x8f, 0x3c, 0x90, 0xff, 0x02, 0xe0, 0xfc, 0xa3, 0xe0, 0xfa, 0xec, +0xff, 0xea, 0x90, 0xfa, 0xba, 0xf0, 0xef, 0xa3, 0xf0, 0x12, 0x1c, 0x30, 0xe4, 0xf5, 0x4d, 0xe5, +0x4d, 0xc3, 0x94, 0x02, 0x50, 0x0f, 0x12, 0x1c, 0x11, 0xe4, 0x12, 0x1a, 0x38, 0x05, 0x4d, 0x04, +0x12, 0x1c, 0x02, 0x80, 0xea, 0x12, 0x1c, 0x30, 0x90, 0xff, 0x00, 0xe0, 0xff, 0x54, 0x60, 0x24, +0xc0, 0x70, 0x03, 0x02, 0x08, 0xc5, 0x24, 0x40, 0x60, 0x03, 0x02, 0x0f, 0x26, 0x90, 0xfa, 0xb6, +0xe0, 0xfe, 0x54, 0x0f, 0xf5, 0x4d, 0xee, 0x30, 0xe7, 0x03, 0xd3, 0x80, 0x01, 0xc3, 0x92, 0x0a, +0x90, 0xff, 0x01, 0xe0, 0x12, 0x1b, 0x4c, 0x03, 0x56, 0x00, 0x04, 0x29, 0x01, 0x05, 0x3c, 0x03, +0x06, 0x03, 0x05, 0x06, 0x45, 0x06, 0x07, 0xa7, 0x08, 0x07, 0xef, 0x09, 0x08, 0x4b, 0x0a, 0x08, +0x8b, 0x0b, 0x00, 0x00, 0x0f, 0x26, 0xe5, 0x35, 0x20, 0xe7, 0x03, 0x02, 0x0f, 0x26, 0x90, 0xfa, +0xba, 0xe0, 0x70, 0x02, 0xa3, 0xe0, 0x60, 0x03, 0x02, 0x0f, 0x26, 0xe5, 0x3c, 0x64, 0x02, 0x45, +0x3b, 0x60, 0x03, 0x02, 0x0f, 0x26, 0xef, 0x54, 0x1f, 0x14, 0x60, 0x2b, 0x14, 0x60, 0x47, 0x24, +0x02, 0x60, 0x03, 0x02, 0x0f, 0x26, 0xee, 0x60, 0x03, 0x02, 0x0f, 0x26, 0x12, 0x1c, 0x11, 0x74, +0x01, 0x12, 0x1a, 0x38, 0x78, 0x67, 0xe6, 0x30, 0xe0, 0x08, 0x12, 0x1c, 0x11, 0x74, 0x02, 0x12, +0x1a, 0x38, 0x7f, 0x02, 0x02, 0x31, 0xb1, 0xe5, 0x35, 0x20, 0xe1, 0x09, 0x90, 0xfa, 0xb6, 0xe0, +0x60, 0x03, 0x02, 0x0f, 0x26, 0x90, 0xfa, 0xb6, 0xe0, 0xd3, 0x94, 0x01, 0x40, 0x03, 0x02, 0x0f, +0x26, 0x7f, 0x02, 0x02, 0x31, 0xb1, 0xe5, 0x35, 0x20, 0xe1, 0x0e, 0x90, 0xfa, 0xb6, 0xe0, 0xff, +0x60, 0x07, 0x64, 0x80, 0x60, 0x03, 0x02, 0x0f, 0x26, 0x12, 0x0f, 0xb2, 0x40, 0x03, 0x02, 0x0f, +0x26, 0xe5, 0x4d, 0x70, 0x19, 0x30, 0x0a, 0x0b, 0x90, 0xff, 0x80, 0x12, 0x1c, 0x0e, 0x12, 0x1a, +0x38, 0x80, 0x24, 0x90, 0xff, 0x82, 0x12, 0x1c, 0x0e, 0x12, 0x1a, 0x38, 0x80, 0x19, 0x15, 0x4d, +0x30, 0x0a, 0x0b, 0x12, 0x1c, 0xa5, 0x12, 0x1c, 0x0c, 0x12, 0x1a, 0x38, 0x80, 0x09, 0x12, 0x1c, +0xb3, 0x12, 0x1c, 0x0c, 0x12, 0x1a, 0x38, 0x12, 0x1c, 0x11, 0x12, 0x19, 0xf2, 0x60, 0x05, 0x74, +0x01, 0x12, 0x1a, 0x38, 0x7f, 0x02, 0x02, 0x31, 0xb1, 0xe5, 0x35, 0x30, 0xe7, 0x03, 0x02, 0x0f, +0x26, 0xe5, 0x3c, 0x45, 0x3b, 0x60, 0x03, 0x02, 0x0f, 0x26, 0x12, 0x1c, 0xc9, 0x14, 0x60, 0x2d, +0x14, 0x60, 0x59, 0x24, 0x02, 0x60, 0x03, 0x02, 0x0f, 0x26, 0x90, 0xfa, 0xba, 0xe0, 0x70, 0x04, +0xa3, 0xe0, 0x64, 0x01, 0x60, 0x03, 0x02, 0x0f, 0x26, 0x90, 0xfa, 0xb6, 0xe0, 0x60, 0x03, 0x02, +0x0f, 0x26, 0x78, 0x67, 0xe6, 0x54, 0xfe, 0xf6, 0xe4, 0xff, 0x02, 0x31, 0xb1, 0xe5, 0x35, 0x20, +0xe1, 0x06, 0x20, 0xe0, 0x03, 0x02, 0x0f, 0x26, 0xe5, 0x35, 0x30, 0xe0, 0x09, 0x90, 0xfa, 0xb6, +0xe0, 0x60, 0x03, 0x02, 0x0f, 0x26, 0xe5, 0x35, 0x30, 0xe1, 0x0c, 0x90, 0xfa, 0xb6, 0xe0, 0xd3, +0x94, 0x01, 0x40, 0x03, 0x02, 0x0f, 0x26, 0xe4, 0xff, 0x02, 0x31, 0xb1, 0x90, 0xfa, 0xba, 0xe0, +0x70, 0x02, 0xa3, 0xe0, 0x60, 0x03, 0x02, 0x0f, 0x26, 0x12, 0x0f, 0xb2, 0x40, 0x03, 0x02, 0x0f, +0x26, 0xe5, 0x35, 0x20, 0xe1, 0x06, 0x20, 0xe0, 0x03, 0x02, 0x0f, 0x26, 0xe5, 0x35, 0x30, 0xe0, +0x07, 0xe5, 0x4d, 0x60, 0x03, 0x02, 0x0f, 0x26, 0xe5, 0x4d, 0x70, 0x0f, 0x90, 0xff, 0x82, 0xe0, +0x54, 0xf7, 0xf0, 0x90, 0xff, 0x80, 0xe0, 0x54, 0xf7, 0xf0, 0x22, 0xe5, 0x4d, 0x24, 0xfe, 0x60, +0x20, 0x24, 0xfb, 0x60, 0x34, 0x24, 0x06, 0x70, 0x35, 0x30, 0x0a, 0x0c, 0xa2, 0x0a, 0xe4, 0x33, +0xfd, 0x7f, 0x03, 0x12, 0x2d, 0xa8, 0x80, 0x26, 0xe4, 0xfd, 0x7f, 0x03, 0x12, 0x2d, 0xa8, 0x80, +0x1d, 0x30, 0x0a, 0x0c, 0xa2, 0x0a, 0xe4, 0x33, 0xfd, 0x7f, 0x04, 0x12, 0x2d, 0xa8, 0x80, 0x0e, +0xe4, 0xfd, 0x7f, 0x04, 0x12, 0x2d, 0xa8, 0x80, 0x05, 0x7f, 0x87, 0x12, 0x31, 0x32, 0x15, 0x4d, +0x30, 0x0a, 0x0b, 0x12, 0x1c, 0xa5, 0xf5, 0x83, 0xe0, 0x54, 0xf7, 0xf0, 0x80, 0x09, 0x12, 0x1c, +0xb3, 0xf5, 0x83, 0xe0, 0x54, 0xf7, 0xf0, 0xe4, 0xff, 0x02, 0x31, 0xb1, 0xe5, 0x35, 0x30, 0xe7, +0x03, 0x02, 0x0f, 0x26, 0xe5, 0x3c, 0x45, 0x3b, 0x60, 0x03, 0x02, 0x0f, 0x26, 0x12, 0x1c, 0xc9, +0x14, 0x60, 0x2d, 0x14, 0x60, 0x55, 0x24, 0x02, 0x60, 0x03, 0x02, 0x0f, 0x26, 0x90, 0xfa, 0xba, +0xe0, 0x70, 0x04, 0xa3, 0xe0, 0x64, 0x01, 0x60, 0x03, 0x02, 0x0f, 0x26, 0x90, 0xfa, 0xb6, 0xe0, +0x60, 0x03, 0x02, 0x0f, 0x26, 0x78, 0x67, 0xe6, 0x44, 0x01, 0xf6, 0xe4, 0xff, 0x02, 0x31, 0xb1, +0xe5, 0x35, 0x20, 0xe1, 0x06, 0x20, 0xe0, 0x03, 0x02, 0x0f, 0x26, 0xe5, 0x35, 0x30, 0xe0, 0x07, +0xe5, 0x4d, 0x60, 0x03, 0x02, 0x0f, 0x26, 0xe5, 0x35, 0x30, 0xe1, 0x0a, 0xe5, 0x4d, 0xd3, 0x94, +0x01, 0x40, 0x03, 0x02, 0x0f, 0x26, 0xe4, 0xff, 0x02, 0x31, 0xb1, 0x90, 0xfa, 0xba, 0xe0, 0x70, +0x02, 0xa3, 0xe0, 0x60, 0x03, 0x02, 0x0f, 0x26, 0x90, 0xfa, 0xb6, 0xe0, 0xff, 0x12, 0x31, 0x82, +0x40, 0x03, 0x02, 0x0f, 0x26, 0xe5, 0x35, 0x20, 0xe1, 0x06, 0x20, 0xe0, 0x03, 0x02, 0x0f, 0x26, +0xe5, 0x4d, 0x70, 0x09, 0x30, 0x0a, 0x03, 0x02, 0x1d, 0x64, 0x02, 0x1d, 0x2f, 0xe5, 0x35, 0x20, +0xe1, 0x03, 0x02, 0x0f, 0x26, 0x15, 0x4d, 0x30, 0x0a, 0x0b, 0x12, 0x1c, 0xa5, 0xf5, 0x83, 0xe0, +0x44, 0x08, 0xf0, 0x80, 0x09, 0x12, 0x1c, 0xb3, 0xf5, 0x83, 0xe0, 0x44, 0x08, 0xf0, 0xe4, 0xff, +0x02, 0x31, 0xb1, 0xe5, 0x35, 0x30, 0xe7, 0x03, 0x02, 0x0f, 0x26, 0xe5, 0x3c, 0x45, 0x3b, 0x60, +0x03, 0x02, 0x0f, 0x26, 0x90, 0xfa, 0xb6, 0xe0, 0x60, 0x03, 0x02, 0x0f, 0x26, 0x12, 0x1c, 0xc9, +0x60, 0x03, 0x02, 0x0f, 0x26, 0xe5, 0x35, 0x30, 0xe1, 0x03, 0x02, 0x0f, 0x26, 0x90, 0xfa, 0xbb, +0xe0, 0x90, 0xff, 0xff, 0xf0, 0xe0, 0x60, 0x05, 0x43, 0x35, 0x01, 0x80, 0x03, 0x53, 0x35, 0xfe, +0xe4, 0xff, 0x02, 0x31, 0xb1, 0xe5, 0x35, 0x20, 0xe7, 0x03, 0x02, 0x0f, 0x26, 0xe5, 0x3c, 0x45, +0x3b, 0x70, 0x03, 0x02, 0x0f, 0x26, 0x12, 0x1c, 0xc9, 0x60, 0x03, 0x02, 0x0f, 0x26, 0x90, 0xfa, +0xba, 0xe0, 0xfc, 0xa3, 0xe0, 0xfd, 0xec, 0x24, 0xfe, 0x60, 0x3a, 0x14, 0x60, 0x75, 0x24, 0x02, +0x60, 0x03, 0x02, 0x0f, 0x26, 0xed, 0x60, 0x03, 0x02, 0x0f, 0x26, 0x12, 0x1c, 0x30, 0x12, 0x1d, +0x5d, 0x7d, 0x03, 0x12, 0x0f, 0x6d, 0x60, 0x03, 0x02, 0x0f, 0x26, 0x12, 0x0f, 0x2a, 0x90, 0xfa, +0xb3, 0xe0, 0xfd, 0xa3, 0x12, 0x1c, 0x7b, 0x12, 0x0f, 0x89, 0x50, 0x02, 0x80, 0x04, 0xae, 0x3b, +0xaf, 0x3c, 0x02, 0x0f, 0xba, 0x12, 0x1c, 0x30, 0x90, 0xf9, 0x15, 0xe0, 0x30, 0xe4, 0x0d, 0x12, +0x1d, 0x5d, 0x7d, 0x14, 0x12, 0x0f, 0x6d, 0x60, 0x10, 0x02, 0x0f, 0x26, 0x12, 0x1d, 0x5d, 0x7d, +0x04, 0x12, 0x0f, 0xc1, 0x60, 0x03, 0x02, 0x0f, 0x26, 0x12, 0x0f, 0x2a, 0x90, 0xfa, 0xb3, 0xe0, +0xfd, 0xa3, 0x12, 0x1c, 0x7b, 0x12, 0x0f, 0x89, 0x50, 0x02, 0x80, 0x04, 0xae, 0x3b, 0xaf, 0x3c, +0x02, 0x0f, 0xba, 0x12, 0x1d, 0x5d, 0x7d, 0x05, 0x12, 0x0f, 0xc1, 0x60, 0x03, 0x02, 0x0f, 0x26, +0x7b, 0x01, 0x7a, 0xfa, 0x79, 0xb3, 0x12, 0x1c, 0x78, 0x7d, 0x01, 0x12, 0x25, 0xd7, 0x90, 0xfa, +0xb4, 0xe4, 0x75, 0xf0, 0x03, 0x12, 0x1a, 0x6c, 0x90, 0xfa, 0xbb, 0xe0, 0x90, 0xfa, 0xb2, 0xf0, +0xe4, 0xf5, 0x4c, 0x90, 0xfa, 0xb2, 0xe0, 0xff, 0xe5, 0x4c, 0xc3, 0x9f, 0x50, 0x24, 0x12, 0x1c, +0x72, 0x12, 0x0f, 0xcc, 0xff, 0xfd, 0x90, 0xfa, 0xb4, 0xe4, 0x8d, 0xf0, 0x12, 0x1a, 0x6c, 0x90, +0xfa, 0xb3, 0xe0, 0xc3, 0x9f, 0xf0, 0xd3, 0x94, 0x00, 0x50, 0x03, 0x02, 0x0f, 0x26, 0x05, 0x4c, +0x80, 0xd1, 0x12, 0x1c, 0x72, 0x12, 0x0f, 0xcc, 0x24, 0xfe, 0xff, 0x90, 0xfa, 0xb3, 0xf0, 0xfd, +0xa3, 0xe4, 0x75, 0xf0, 0x02, 0x12, 0x1a, 0x6c, 0x7a, 0xf9, 0x79, 0x6f, 0x7b, 0x01, 0x8b, 0x36, +0x8a, 0x37, 0x89, 0x38, 0xe9, 0x24, 0x02, 0xf9, 0xe4, 0x3a, 0xfa, 0x12, 0x1c, 0x78, 0x12, 0x25, +0xd7, 0x8f, 0x4c, 0x05, 0x4c, 0x05, 0x4c, 0x12, 0x1c, 0x11, 0xe5, 0x4c, 0x12, 0x1a, 0x38, 0x12, +0x1c, 0x11, 0x90, 0x00, 0x01, 0x74, 0x03, 0x12, 0x1a, 0x4a, 0xaf, 0x4c, 0x7e, 0x00, 0xc3, 0xef, +0x95, 0x3c, 0xee, 0x95, 0x3b, 0x50, 0x02, 0x80, 0x04, 0xae, 0x3b, 0xaf, 0x3c, 0x8e, 0x39, 0x8f, +0x3a, 0x02, 0x2c, 0x07, 0x02, 0x0f, 0x26, 0xe5, 0x35, 0x20, 0xe7, 0x03, 0x02, 0x0f, 0x26, 0xe5, +0x3c, 0x64, 0x01, 0x45, 0x3b, 0x60, 0x03, 0x02, 0x0f, 0x26, 0x90, 0xfa, 0xb6, 0xe0, 0x60, 0x03, +0x02, 0x0f, 0x26, 0x90, 0xfa, 0xba, 0xe0, 0x70, 0x02, 0xa3, 0xe0, 0x60, 0x03, 0x02, 0x0f, 0x26, +0x12, 0x1c, 0xc9, 0x60, 0x03, 0x02, 0x0f, 0x26, 0xe5, 0x35, 0x20, 0xe0, 0x06, 0x20, 0xe1, 0x03, +0x02, 0x0f, 0x26, 0x75, 0x36, 0x00, 0x75, 0x37, 0x00, 0x75, 0x38, 0x32, 0x02, 0x0f, 0xa9, 0xe5, +0x35, 0x30, 0xe7, 0x03, 0x02, 0x0f, 0x26, 0xe5, 0x3c, 0x45, 0x3b, 0x60, 0x03, 0x02, 0x0f, 0x26, +0x90, 0xfa, 0xb6, 0xe0, 0x60, 0x03, 0x02, 0x0f, 0x26, 0xd3, 0x90, 0xfa, 0xbb, 0xe0, 0x94, 0x01, +0x90, 0xfa, 0xba, 0xe0, 0x94, 0x00, 0x40, 0x03, 0x02, 0x0f, 0x26, 0x12, 0x1c, 0xc9, 0x60, 0x03, +0x02, 0x0f, 0x26, 0xe5, 0x35, 0x20, 0xe0, 0x06, 0x20, 0xe1, 0x03, 0x02, 0x0f, 0x26, 0x90, 0xfa, +0xbb, 0xe0, 0xf5, 0x32, 0xe5, 0x32, 0x70, 0x08, 0x43, 0x35, 0x01, 0x53, 0x35, 0xfd, 0x80, 0x06, +0x53, 0x35, 0xfe, 0x43, 0x35, 0x02, 0xe4, 0xff, 0x02, 0x31, 0xb1, 0xe5, 0x35, 0x20, 0xe7, 0x03, +0x02, 0x0f, 0x26, 0xe5, 0x3c, 0x64, 0x01, 0x45, 0x3b, 0x60, 0x03, 0x02, 0x0f, 0x26, 0x90, 0xfa, +0xb6, 0xe0, 0x60, 0x03, 0x02, 0x0f, 0x26, 0x90, 0xfa, 0xba, 0xe0, 0x70, 0x02, 0xa3, 0xe0, 0x60, +0x03, 0x02, 0x0f, 0x26, 0x12, 0x1c, 0xc9, 0x64, 0x01, 0x60, 0x03, 0x02, 0x0f, 0x26, 0xe5, 0x35, +0x20, 0xe1, 0x03, 0x02, 0x0f, 0x26, 0x7f, 0x01, 0x02, 0x31, 0xb1, 0xe5, 0x35, 0x30, 0xe7, 0x03, +0x02, 0x0f, 0x26, 0xe5, 0x3c, 0x45, 0x3b, 0x60, 0x03, 0x02, 0x0f, 0x26, 0xd3, 0x90, 0xfa, 0xbb, +0xe0, 0x94, 0x00, 0x90, 0xfa, 0xba, 0xe0, 0x94, 0x00, 0x40, 0x03, 0x02, 0x0f, 0x26, 0x12, 0x1c, +0xc9, 0x64, 0x01, 0x60, 0x03, 0x02, 0x0f, 0x26, 0xe5, 0x35, 0x20, 0xe1, 0x03, 0x02, 0x0f, 0x26, +0xe4, 0xff, 0x02, 0x31, 0xb1, 0x90, 0xff, 0x01, 0x12, 0x1d, 0x74, 0xef, 0x12, 0x1a, 0x38, 0x90, +0xfa, 0xb6, 0x12, 0x1d, 0x74, 0x90, 0x00, 0x01, 0xef, 0x12, 0x1a, 0x4a, 0x90, 0x00, 0x02, 0xe4, +0x12, 0x1a, 0x4a, 0x74, 0x03, 0x12, 0x1c, 0x02, 0x90, 0xfa, 0xba, 0xe0, 0xff, 0xa3, 0xe0, 0x85, +0x38, 0x82, 0x85, 0x37, 0x83, 0xcf, 0xf0, 0xa3, 0xef, 0xf0, 0x90, 0xff, 0x01, 0xe0, 0x12, 0x1b, +0x4c, 0x09, 0x4a, 0x02, 0x09, 0x6c, 0x04, 0x09, 0x8e, 0x05, 0x09, 0xba, 0x06, 0x09, 0xd8, 0x07, +0x09, 0xf6, 0x08, 0x0a, 0x14, 0x09, 0x0a, 0x32, 0x0b, 0x0a, 0xe7, 0x80, 0x0d, 0x6f, 0x81, 0x0d, +0xa0, 0x82, 0x0b, 0x2e, 0x83, 0x0b, 0x77, 0x84, 0x0b, 0x96, 0x85, 0x0b, 0xdb, 0x86, 0x0c, 0x26, +0x87, 0x0c, 0xb7, 0x88, 0x0d, 0x42, 0x89, 0x0a, 0x50, 0x92, 0x0a, 0x50, 0x93, 0x0e, 0x53, 0xc0, +0x0e, 0x7f, 0xc1, 0x0e, 0x90, 0xc2, 0x00, 0x00, 0x0f, 0x15, 0xe5, 0x35, 0x20, 0xe7, 0x05, 0x7f, +0x05, 0x02, 0x30, 0xec, 0x12, 0x1c, 0xc1, 0x60, 0x03, 0x04, 0x70, 0x09, 0xef, 0xfd, 0x7c, 0x00, +0x7f, 0x07, 0x02, 0x11, 0x16, 0xe4, 0xfd, 0x7f, 0x07, 0x02, 0x2f, 0x18, 0xe5, 0x35, 0x20, 0xe7, +0x05, 0x7f, 0x05, 0x02, 0x30, 0xec, 0x12, 0x1c, 0xc1, 0x60, 0x03, 0x04, 0x70, 0x09, 0xef, 0xfd, +0x7c, 0x00, 0x7f, 0x0c, 0x02, 0x11, 0x16, 0xe4, 0xfd, 0x7f, 0x07, 0x02, 0x2f, 0x18, 0xe5, 0x35, +0x30, 0xe7, 0x03, 0x02, 0x0f, 0x29, 0x12, 0x1d, 0x92, 0x50, 0x06, 0xe5, 0x3c, 0x45, 0x3b, 0x70, +0x05, 0x7f, 0x02, 0x02, 0x30, 0xec, 0x90, 0xfa, 0xb6, 0xe0, 0x24, 0xfe, 0x24, 0xfd, 0x50, 0x02, +0x80, 0x03, 0x02, 0x31, 0x6f, 0x7f, 0x07, 0x02, 0x30, 0xec, 0xe5, 0x35, 0x30, 0xe7, 0x03, 0x02, +0x0f, 0x29, 0x12, 0x1c, 0xc1, 0x60, 0x03, 0x04, 0x70, 0x09, 0xef, 0xfd, 0x7c, 0x00, 0x7f, 0x08, +0x02, 0x11, 0x16, 0x7f, 0x07, 0x02, 0x30, 0xec, 0xe5, 0x35, 0x30, 0xe7, 0x03, 0x02, 0x0f, 0x29, +0x12, 0x1c, 0xc1, 0x60, 0x03, 0x04, 0x70, 0x09, 0xef, 0xfd, 0x7c, 0x00, 0x7f, 0x09, 0x02, 0x11, +0x16, 0x7f, 0x07, 0x02, 0x30, 0xec, 0xe5, 0x35, 0x30, 0xe7, 0x03, 0x02, 0x0f, 0x29, 0x12, 0x1c, +0xc1, 0x60, 0x03, 0x04, 0x70, 0x09, 0xef, 0xfd, 0x7c, 0x00, 0x7f, 0x0a, 0x02, 0x11, 0x16, 0x7f, +0x07, 0x02, 0x30, 0xec, 0xe5, 0x35, 0x30, 0xe7, 0x03, 0x02, 0x0f, 0x29, 0x12, 0x1c, 0xc1, 0x60, +0x03, 0x04, 0x70, 0x09, 0xef, 0xfd, 0x7c, 0x00, 0x7f, 0x0b, 0x02, 0x11, 0x16, 0x7f, 0x07, 0x02, +0x30, 0xec, 0xe5, 0x35, 0x30, 0xe7, 0x03, 0x02, 0x0f, 0x29, 0x12, 0x1c, 0xc1, 0x60, 0x03, 0x04, +0x70, 0x09, 0xef, 0xfd, 0x7c, 0x00, 0x7f, 0x0e, 0x02, 0x11, 0x16, 0x7f, 0x07, 0x02, 0x30, 0xec, +0xe5, 0x35, 0x30, 0xe7, 0x56, 0x12, 0x1c, 0xc9, 0x70, 0x4a, 0x90, 0xff, 0x02, 0xe0, 0xf5, 0x4c, +0xe5, 0x4c, 0xb4, 0x82, 0x05, 0x75, 0x4c, 0x61, 0x80, 0x12, 0xe5, 0x4c, 0xb4, 0x83, 0x05, 0x75, +0x4c, 0x62, 0x80, 0x08, 0xe5, 0x4c, 0xc4, 0x54, 0xf0, 0x04, 0xf5, 0x4c, 0x12, 0x1b, 0x72, 0x12, +0x1d, 0x8b, 0x12, 0x25, 0x39, 0x12, 0x1c, 0xd9, 0x12, 0x1a, 0x0b, 0x60, 0x05, 0x12, 0x31, 0xbd, +0x80, 0x06, 0x85, 0x33, 0x39, 0x85, 0x34, 0x3a, 0x75, 0x36, 0x01, 0x75, 0x37, 0xf9, 0x75, 0x38, +0x72, 0x02, 0x2c, 0x07, 0xe4, 0xfd, 0x7f, 0x05, 0x02, 0x2f, 0x18, 0x12, 0x1c, 0xc9, 0x60, 0x05, +0x7f, 0x05, 0x02, 0x30, 0xec, 0x12, 0x1d, 0x92, 0x40, 0x05, 0x7f, 0x03, 0x02, 0x30, 0xec, 0x90, +0xff, 0x02, 0xe0, 0xf5, 0x4c, 0xe5, 0x4c, 0xb4, 0x82, 0x05, 0x75, 0x4c, 0x61, 0x80, 0x12, 0xe5, +0x4c, 0xb4, 0x83, 0x05, 0x75, 0x4c, 0x62, 0x80, 0x08, 0xe5, 0x4c, 0xc4, 0x54, 0xf0, 0x04, 0xf5, +0x4c, 0x12, 0x1b, 0x72, 0x02, 0x31, 0x6f, 0x12, 0x1d, 0x9c, 0x12, 0x2a, 0x06, 0x12, 0x1c, 0x83, +0xe0, 0x54, 0x7f, 0xf0, 0x00, 0x00, 0x00, 0xe0, 0x90, 0xfa, 0xb7, 0xf0, 0x78, 0x68, 0x12, 0x1b, +0x28, 0x90, 0x00, 0x02, 0x12, 0x1a, 0x0b, 0x30, 0xe7, 0xf2, 0x90, 0x00, 0x02, 0xe4, 0x12, 0x1a, +0x4a, 0x90, 0xfa, 0xb7, 0xe0, 0x44, 0x80, 0xff, 0xf0, 0x78, 0x7c, 0xe6, 0xfc, 0x08, 0xe6, 0x8c, +0x83, 0x12, 0x1c, 0x8b, 0xef, 0xf0, 0x12, 0x31, 0xc7, 0xe4, 0xff, 0x02, 0x30, 0xec, 0x90, 0xfa, +0xb6, 0xe0, 0x64, 0x01, 0x70, 0x1f, 0x90, 0xfa, 0xba, 0xe0, 0xff, 0x7e, 0x00, 0x70, 0x06, 0xa3, +0xe0, 0xf5, 0x90, 0x80, 0x2d, 0xc2, 0xaf, 0xef, 0xf4, 0x52, 0x90, 0x90, 0xfa, 0xbb, 0xe0, 0x42, +0x90, 0xd2, 0xaf, 0x80, 0x1d, 0x90, 0xfa, 0xba, 0xe0, 0xff, 0x7e, 0x00, 0x70, 0x06, 0xa3, 0xe0, +0xf5, 0xb0, 0x80, 0x0e, 0xc2, 0xaf, 0xef, 0xf4, 0x52, 0xb0, 0x90, 0xfa, 0xbb, 0xe0, 0x42, 0xb0, +0xd2, 0xaf, 0xe4, 0xff, 0x02, 0x30, 0xec, 0x12, 0x1c, 0x30, 0x90, 0xfa, 0xb6, 0xe0, 0xb4, 0x01, +0x0a, 0x12, 0x1c, 0x11, 0xe5, 0x90, 0x12, 0x1a, 0x38, 0x80, 0x08, 0x12, 0x1c, 0x11, 0xe5, 0xb0, +0x12, 0x1a, 0x38, 0x02, 0x0f, 0xa9, 0x90, 0xfa, 0xb6, 0xe0, 0xff, 0x24, 0x12, 0x12, 0x1c, 0x41, +0x20, 0xe1, 0x33, 0x12, 0x1c, 0xd0, 0xef, 0x24, 0xfc, 0x60, 0x18, 0x04, 0x70, 0x28, 0x90, 0xfa, +0xb7, 0xe0, 0x60, 0x09, 0x90, 0xff, 0xa4, 0xe0, 0x44, 0x10, 0xf0, 0x80, 0x19, 0x12, 0x1d, 0xa6, +0xf0, 0x80, 0x13, 0x90, 0xfa, 0xb7, 0xe0, 0x60, 0x09, 0x90, 0xff, 0xb4, 0xe0, 0x44, 0x10, 0xf0, +0x80, 0x04, 0x12, 0x1d, 0xad, 0xf0, 0xe4, 0xff, 0x02, 0x30, 0xec, 0x90, 0xfa, 0xb6, 0xe0, 0xff, +0x24, 0x12, 0x12, 0x1c, 0x41, 0x20, 0xe1, 0x39, 0x12, 0x1c, 0xd0, 0xef, 0x24, 0xfc, 0x60, 0x1b, +0x04, 0x70, 0x2e, 0x90, 0xfa, 0xb7, 0xe0, 0x60, 0x09, 0x90, 0xff, 0xa4, 0xe0, 0x44, 0x20, 0xf0, +0x80, 0x1f, 0x90, 0xff, 0xa4, 0xe0, 0x54, 0xdf, 0xf0, 0x80, 0x16, 0x90, 0xfa, 0xb7, 0xe0, 0x60, +0x09, 0x90, 0xff, 0xb4, 0xe0, 0x44, 0x20, 0xf0, 0x80, 0x07, 0x90, 0xff, 0xb4, 0xe0, 0x54, 0xdf, +0xf0, 0xe4, 0xff, 0x02, 0x30, 0xec, 0x12, 0x1c, 0xd0, 0x12, 0x1c, 0xc1, 0x60, 0x4d, 0x04, 0x60, +0x03, 0x02, 0x0c, 0xb2, 0x90, 0xfa, 0xb7, 0xe0, 0x60, 0x0f, 0x90, 0xff, 0xa4, 0x12, 0x1c, 0x3a, +0x30, 0xe1, 0x6f, 0x12, 0x1d, 0x7c, 0x02, 0x0c, 0xb2, 0x90, 0xff, 0xa4, 0xe0, 0x54, 0xfb, 0x12, +0x1c, 0x3d, 0xfe, 0x30, 0xe1, 0x5c, 0x30, 0xe2, 0x11, 0x30, 0xb4, 0x05, 0x12, 0x1d, 0x7c, 0x80, +0x51, 0x90, 0xff, 0xa4, 0xe0, 0x54, 0xfd, 0xf0, 0x80, 0x48, 0x30, 0x95, 0x05, 0x12, 0x1d, 0x7c, +0x80, 0x40, 0x90, 0xff, 0xa4, 0xe0, 0x54, 0xfd, 0xf0, 0x80, 0x37, 0x90, 0xfa, 0xb7, 0xe0, 0x60, +0x12, 0x90, 0xff, 0xb4, 0x12, 0x1c, 0x3a, 0x30, 0xe1, 0x28, 0x90, 0xff, 0xb4, 0xe0, 0x44, 0x02, +0xf0, 0x80, 0x1f, 0x90, 0xff, 0xb4, 0xe0, 0x54, 0xfb, 0x12, 0x1c, 0x3d, 0x30, 0xe1, 0x13, 0x30, +0x93, 0x09, 0x90, 0xff, 0xb4, 0xe0, 0x44, 0x02, 0xf0, 0x80, 0x07, 0x90, 0xff, 0xb4, 0xe0, 0x54, +0xfd, 0xf0, 0xe4, 0xff, 0x02, 0x30, 0xec, 0x12, 0x1c, 0xd0, 0x90, 0xfa, 0xb6, 0xe0, 0x24, 0xfc, +0x60, 0x40, 0x04, 0x70, 0x78, 0x90, 0xfa, 0xb7, 0xe0, 0x60, 0x1d, 0x90, 0xff, 0xa2, 0xe0, 0x44, +0x40, 0xf0, 0xa3, 0xe0, 0xff, 0x30, 0xe7, 0x65, 0xd2, 0x03, 0xa3, 0xe0, 0x54, 0xdf, 0xf0, 0x90, +0xff, 0xa3, 0xef, 0x54, 0x7f, 0xf0, 0x80, 0x55, 0x30, 0x03, 0x0e, 0x90, 0xff, 0xa3, 0xe0, 0x44, +0x80, 0xf0, 0xc2, 0x03, 0xa3, 0xe0, 0x44, 0x20, 0xf0, 0x90, 0xff, 0xa2, 0xe0, 0x54, 0xbf, 0xf0, +0x80, 0x3b, 0x90, 0xfa, 0xb7, 0xe0, 0x60, 0x1d, 0x90, 0xff, 0xb2, 0xe0, 0x44, 0x40, 0xf0, 0xa3, +0xe0, 0xff, 0x30, 0xe7, 0x28, 0xd2, 0x04, 0xa3, 0xe0, 0x54, 0xdf, 0xf0, 0x90, 0xff, 0xb3, 0xef, +0x54, 0x7f, 0xf0, 0x80, 0x18, 0x30, 0x04, 0x0e, 0x90, 0xff, 0xb3, 0xe0, 0x44, 0x80, 0xf0, 0xc2, +0x04, 0xa3, 0xe0, 0x44, 0x20, 0xf0, 0x90, 0xff, 0xb2, 0xe0, 0x54, 0xbf, 0xf0, 0xe4, 0xff, 0x02, +0x30, 0xec, 0x12, 0x1c, 0x30, 0x90, 0xfa, 0xb6, 0xe0, 0x24, 0xfc, 0x60, 0x0f, 0x04, 0x70, 0x16, +0x90, 0xff, 0xa6, 0xe0, 0x12, 0x1c, 0x11, 0x12, 0x1a, 0x38, 0x80, 0x0a, 0x90, 0xff, 0xb6, 0xe0, +0x12, 0x1c, 0x11, 0x12, 0x1a, 0x38, 0x75, 0x39, 0x00, 0x75, 0x3a, 0x01, 0x02, 0x2c, 0x07, 0xe4, +0xff, 0x12, 0x30, 0xec, 0x12, 0x1d, 0x37, 0x7f, 0x03, 0x12, 0x12, 0x19, 0x90, 0xf9, 0x15, 0xe0, +0x30, 0xe4, 0x08, 0x90, 0xff, 0x93, 0x74, 0x80, 0xf0, 0x80, 0x10, 0x90, 0xff, 0xfc, 0xe0, 0x54, +0x7f, 0xf0, 0x7f, 0xff, 0x7e, 0x00, 0x12, 0x30, 0x16, 0xc2, 0x90, 0xc2, 0xaf, 0x00, 0x80, 0xfd, +0xe4, 0xf5, 0x4e, 0xf5, 0x4f, 0x90, 0xfa, 0xbc, 0x74, 0x3e, 0xf0, 0xa3, 0xe4, 0xf0, 0x90, 0xfa, +0xb4, 0xf0, 0xa3, 0x74, 0x15, 0xf0, 0xe0, 0x54, 0x3f, 0xff, 0xc3, 0x74, 0x40, 0x9f, 0x90, 0xfa, +0xb9, 0xf0, 0xd3, 0x94, 0x00, 0xe4, 0x94, 0x3e, 0x40, 0x08, 0x90, 0xfa, 0xbd, 0xe0, 0x90, 0xfa, +0xb9, 0xf0, 0x12, 0x0f, 0x50, 0xe5, 0x31, 0x45, 0x30, 0x70, 0x73, 0x12, 0x1c, 0x4a, 0x90, 0xfa, +0xbc, 0x12, 0x1d, 0x56, 0x60, 0x27, 0xd3, 0xef, 0x94, 0x40, 0xee, 0x94, 0x00, 0x40, 0x08, 0x90, +0xfa, 0xb9, 0x74, 0x40, 0xf0, 0x80, 0x08, 0x90, 0xfa, 0xbd, 0xe0, 0x90, 0xfa, 0xb9, 0xf0, 0x12, +0x0f, 0x50, 0xe5, 0x31, 0x45, 0x30, 0x70, 0x46, 0x12, 0x1c, 0x4a, 0x80, 0xd1, 0x75, 0x4c, 0x02, +0x90, 0xfa, 0xbc, 0xe4, 0xf0, 0xa3, 0x04, 0xf0, 0x90, 0xfa, 0xb4, 0xe4, 0xf0, 0xa3, 0x74, 0x0f, +0xf0, 0x7b, 0x00, 0x7a, 0x00, 0x79, 0x4c, 0x90, 0xfa, 0xbd, 0xe0, 0xf5, 0x4a, 0x7d, 0x0f, 0x7c, +0x00, 0x12, 0x28, 0x9f, 0x75, 0x30, 0x00, 0x8f, 0x31, 0x7b, 0x00, 0x7a, 0x00, 0x79, 0x4c, 0xe4, +0xf5, 0x2d, 0xf5, 0x2e, 0x7d, 0x01, 0x12, 0x25, 0xd7, 0xe4, 0xf5, 0x30, 0xf5, 0x31, 0xaf, 0x31, +0x02, 0x30, 0xec, 0x12, 0x1c, 0xd0, 0x30, 0xe7, 0x10, 0xe0, 0x54, 0x0f, 0x90, 0xf9, 0x64, 0xf0, +0xd3, 0x94, 0x00, 0x40, 0x15, 0xc2, 0x95, 0x80, 0x11, 0x90, 0xfa, 0xb7, 0xe0, 0x54, 0x0f, 0x90, +0xf9, 0x63, 0xf0, 0xd3, 0x94, 0x00, 0x40, 0x02, 0xc2, 0x94, 0xe4, 0xff, 0x02, 0x30, 0xec, 0x12, +0x1d, 0x9c, 0xbf, 0x01, 0x04, 0xd2, 0x93, 0x80, 0x02, 0xc2, 0x93, 0xe4, 0xff, 0x02, 0x30, 0xec, +0x12, 0x1c, 0xd0, 0x54, 0x03, 0x14, 0x60, 0x0a, 0x14, 0x60, 0x0f, 0x14, 0x60, 0x08, 0x24, 0x03, +0x70, 0x2b, 0xd2, 0x91, 0x80, 0x27, 0xc2, 0x91, 0x80, 0x23, 0x12, 0x1d, 0xa6, 0x12, 0x0f, 0x78, +0x60, 0x04, 0xd2, 0x91, 0x80, 0x17, 0x90, 0xff, 0xa4, 0xe0, 0x44, 0x10, 0x12, 0x0f, 0x78, 0xff, +0xbf, 0xa0, 0x04, 0xc2, 0x91, 0x80, 0x02, 0xd2, 0x91, 0x12, 0x1d, 0xa6, 0xf0, 0x90, 0xfa, 0xb7, +0xe0, 0x54, 0x0c, 0xff, 0x13, 0x13, 0x54, 0x3f, 0x14, 0x60, 0x0a, 0x14, 0x60, 0x0f, 0x14, 0x60, +0x08, 0x24, 0x03, 0x70, 0x2b, 0xd2, 0x92, 0x80, 0x27, 0xc2, 0x92, 0x80, 0x23, 0x12, 0x1d, 0xad, +0x12, 0x0f, 0x98, 0x60, 0x04, 0xd2, 0x92, 0x80, 0x17, 0x90, 0xff, 0xb4, 0xe0, 0x44, 0x10, 0x12, +0x0f, 0x98, 0xff, 0xbf, 0xa0, 0x04, 0xc2, 0x92, 0x80, 0x02, 0xd2, 0x92, 0x12, 0x1d, 0xad, 0xf0, +0xe4, 0xff, 0x02, 0x30, 0xec, 0xe5, 0x35, 0x30, 0xe7, 0x07, 0xe4, 0xfd, 0x7f, 0x05, 0x02, 0x2f, +0x18, 0x7f, 0x05, 0x02, 0x30, 0xec, 0x12, 0x31, 0xbd, 0x22, 0x7b, 0x01, 0x7a, 0xfa, 0x79, 0xb3, +0x90, 0xfa, 0xb4, 0xe0, 0xf5, 0x2d, 0xa3, 0xe0, 0xf5, 0x2e, 0x7d, 0x01, 0x12, 0x25, 0xd7, 0x90, +0xfa, 0xb4, 0xe4, 0x75, 0xf0, 0x03, 0x12, 0x1a, 0x6c, 0xab, 0x36, 0xaa, 0x37, 0xa9, 0x38, 0x22, +0xaa, 0x4e, 0xa9, 0x4f, 0x7b, 0xff, 0x90, 0xfa, 0xb4, 0xe0, 0xfc, 0xa3, 0xe0, 0xfd, 0x90, 0xfa, +0xb9, 0xe0, 0xf5, 0x4a, 0x12, 0x28, 0x9f, 0x75, 0x30, 0x00, 0x8f, 0x31, 0x22, 0x12, 0x22, 0xa0, +0x7e, 0x00, 0x8e, 0x30, 0x8f, 0x31, 0xef, 0x22, 0xf0, 0x7f, 0x01, 0x12, 0x12, 0x19, 0x90, 0xff, +0xa6, 0xe0, 0x90, 0xfa, 0xb8, 0xf0, 0x54, 0xa0, 0x22, 0x12, 0x25, 0xd7, 0x8f, 0x4c, 0x7e, 0x00, +0xc3, 0xef, 0x95, 0x3c, 0xee, 0x95, 0x3b, 0x22, 0xf0, 0x7f, 0x01, 0x12, 0x12, 0x19, 0x90, 0xff, +0xb6, 0xe0, 0x90, 0xfa, 0xb8, 0xf0, 0x54, 0xa0, 0x22, 0x75, 0x39, 0x00, 0x75, 0x3a, 0x01, 0x02, +0x2c, 0x07, 0x90, 0xfa, 0xb6, 0xe0, 0xff, 0x02, 0x31, 0x82, 0x8e, 0x39, 0x8f, 0x3a, 0x02, 0x2c, +0x07, 0x12, 0x22, 0xa0, 0x7e, 0x00, 0x8e, 0x30, 0x8f, 0x31, 0xef, 0x22, 0x7d, 0x01, 0x12, 0x25, +0xd7, 0x90, 0xfa, 0xb1, 0xe0, 0x22, 0xef, 0x90, 0xf8, 0x04, 0xf0, 0x22, 0xc0, 0xa8, 0xc2, 0xaf, +0xee, 0x60, 0x0a, 0xc0, 0x05, 0x7d, 0x7f, 0xdd, 0xfe, 0xde, 0xfa, 0xd0, 0x05, 0xef, 0xc3, 0x94, +0x15, 0x50, 0x03, 0xd0, 0xa8, 0x22, 0x13, 0x70, 0x03, 0xd0, 0xa8, 0x22, 0xff, 0xd5, 0x07, 0xfd, +0xd0, 0xa8, 0x22, 0xc0, 0x00, 0xc0, 0x01, 0xc0, 0x02, 0xc0, 0x04, 0xc0, 0x05, 0xe5, 0x3e, 0x24, +0x08, 0xf8, 0x86, 0x05, 0x53, 0x05, 0x7f, 0x7c, 0xff, 0x12, 0x10, 0x78, 0x7f, 0x00, 0x7e, 0x00, +0xe5, 0x43, 0x60, 0x46, 0xfc, 0x90, 0xf9, 0x1b, 0xe0, 0x54, 0x7f, 0x6d, 0x70, 0x0f, 0xc0, 0x83, +0xc0, 0x82, 0xa3, 0xe0, 0xfe, 0xa3, 0xe0, 0xff, 0xa3, 0x15, 0x43, 0x80, 0x07, 0xa3, 0xa3, 0xa3, +0xdc, 0xe6, 0x80, 0x26, 0xdc, 0x06, 0xd0, 0x82, 0xd0, 0x83, 0x80, 0x1e, 0xe0, 0xf8, 0xa3, 0xe0, +0xf9, 0xa3, 0xe0, 0xfa, 0xd0, 0x82, 0xd0, 0x83, 0xe8, 0xf0, 0xa3, 0xe9, 0xf0, 0xa3, 0xea, 0xf0, +0xa3, 0xc0, 0x83, 0xc0, 0x82, 0xa3, 0xa3, 0xa3, 0x80, 0xda, 0x12, 0x11, 0x0f, 0xd0, 0x05, 0xd0, +0x04, 0xd0, 0x02, 0xd0, 0x01, 0xd0, 0x00, 0x22, 0x85, 0xa8, 0x44, 0x75, 0xa8, 0x88, 0xec, 0x70, +0x02, 0x7c, 0x3f, 0x8c, 0x3d, 0x22, 0xe5, 0x3e, 0x24, 0x08, 0xf8, 0x76, 0x00, 0x12, 0x11, 0x66, +0x80, 0xfb, 0xc0, 0x00, 0xc0, 0x01, 0xc0, 0x02, 0xc0, 0x04, 0xc0, 0x06, 0x7c, 0xff, 0x12, 0x10, +0x78, 0xe5, 0x43, 0x60, 0x42, 0xfe, 0x90, 0xf9, 0x1b, 0xe0, 0x54, 0x7f, 0x6f, 0x70, 0x0b, 0xc0, +0x83, 0xc0, 0x82, 0xa3, 0xa3, 0xa3, 0x15, 0x43, 0x80, 0x07, 0xa3, 0xa3, 0xa3, 0xde, 0xea, 0x80, +0x26, 0xde, 0x06, 0xd0, 0x82, 0xd0, 0x83, 0x80, 0xd8, 0xe0, 0xf8, 0xa3, 0xe0, 0xf9, 0xa3, 0xe0, +0xfa, 0xd0, 0x82, 0xd0, 0x83, 0xe8, 0xf0, 0xa3, 0xe9, 0xf0, 0xa3, 0xea, 0xf0, 0xa3, 0xc0, 0x83, +0xc0, 0x82, 0xa3, 0xa3, 0xa3, 0x80, 0xda, 0x78, 0x08, 0x08, 0x79, 0x18, 0x09, 0x7c, 0x01, 0xe6, +0x54, 0x7f, 0x6f, 0x70, 0x06, 0x76, 0x00, 0x77, 0x00, 0x80, 0x06, 0x08, 0x09, 0x0c, 0xbc, 0x08, +0xee, 0x12, 0x11, 0x0f, 0xd0, 0x06, 0xd0, 0x04, 0xd0, 0x02, 0xd0, 0x01, 0xd0, 0x00, 0x22, 0x75, +0x3d, 0x00, 0x85, 0x44, 0xa8, 0x22, 0xc0, 0xf0, 0xc0, 0x82, 0xc0, 0x83, 0xc3, 0xe5, 0x43, 0x24, +0xe8, 0x50, 0x05, 0x12, 0x11, 0x66, 0x80, 0xf4, 0xef, 0x60, 0x31, 0x90, 0x30, 0x54, 0xe4, 0x93, +0xc3, 0x9f, 0x40, 0x2f, 0xc0, 0x04, 0x7c, 0xff, 0x12, 0x10, 0x78, 0xd0, 0x04, 0x43, 0x07, 0x80, +0xe5, 0x43, 0x75, 0xf0, 0x03, 0xa4, 0x24, 0x1b, 0xf5, 0x82, 0xe4, 0x34, 0xf9, 0xf5, 0x83, 0xef, +0xf0, 0xec, 0xa3, 0xf0, 0xed, 0xa3, 0xf0, 0x05, 0x43, 0x12, 0x11, 0x0f, 0xd0, 0x83, 0xd0, 0x82, +0xd0, 0xf0, 0x22, 0x02, 0x11, 0x94, 0xc0, 0x04, 0x7c, 0x20, 0xd2, 0x8c, 0xd2, 0x8d, 0xd5, 0x04, +0xfd, 0xd0, 0x04, 0x22, 0x75, 0xa8, 0x00, 0x75, 0x88, 0x00, 0x75, 0xb8, 0x00, 0x75, 0xf0, 0x00, +0x75, 0xd0, 0x00, 0xe4, 0xf8, 0x90, 0xf8, 0x04, 0xf0, 0x90, 0x00, 0x00, 0xf6, 0x08, 0xb8, 0x00, +0xfb, 0x02, 0x00, 0x00, 0xc2, 0xaf, 0xe4, 0x90, 0xff, 0x48, 0xf0, 0x90, 0xff, 0x50, 0xf0, 0x90, +0xff, 0x08, 0xf0, 0x90, 0xff, 0x10, 0xf0, 0x90, 0xff, 0x80, 0xf0, 0xa3, 0xa3, 0xf0, 0xd2, 0xb1, +0xc2, 0xb0, 0x7e, 0xff, 0x7f, 0xff, 0x12, 0x0f, 0xdc, 0x7e, 0xff, 0x7f, 0xff, 0x12, 0x0f, 0xdc, +0x7e, 0xff, 0x7f, 0xff, 0x12, 0x0f, 0xdc, 0xd2, 0xb0, 0xd2, 0xb1, 0x7e, 0xff, 0x7f, 0xff, 0x12, +0x0f, 0xdc, 0x7e, 0xff, 0x7f, 0xff, 0x12, 0x0f, 0xdc, 0x7e, 0xff, 0x7f, 0xff, 0x12, 0x0f, 0xdc, +0x80, 0xcc, 0xc3, 0xee, 0x94, 0x02, 0x50, 0x04, 0x7e, 0x03, 0x7f, 0xe8, 0xef, 0xf4, 0xff, 0xee, +0xf4, 0xfe, 0x0f, 0xbf, 0x00, 0x01, 0x0e, 0x8f, 0x42, 0x8e, 0x41, 0x22, 0xc3, 0xef, 0x94, 0xbc, +0xee, 0x94, 0x02, 0x50, 0x04, 0x7e, 0x07, 0x7f, 0xd0, 0xef, 0xf4, 0xff, 0xee, 0xf4, 0xfe, 0x0f, +0xbf, 0x00, 0x01, 0x0e, 0x8f, 0x40, 0x8e, 0x3f, 0x22, 0xef, 0x70, 0x01, 0x22, 0xc0, 0x00, 0xc0, +0xa8, 0xc2, 0xaf, 0xe5, 0x3e, 0x24, 0x18, 0xf8, 0xa6, 0x07, 0xe5, 0x3e, 0x24, 0x08, 0xf8, 0xc6, +0x54, 0x7f, 0xf6, 0xd0, 0xa8, 0xe6, 0x30, 0xe7, 0x03, 0xd0, 0x00, 0x22, 0x12, 0x11, 0x66, 0x80, 0xf4, 0xc0, 0x00, 0x7f, 0x01, 0xef, 0x24, 0x08, 0xf8, 0xe6, 0x60, 0x09, 0x0f, 0xbf, 0x08, 0xf5, -0x12, 0x10, 0xec, 0x80, 0xee, 0xd0, 0x00, 0x22, 0xc0, 0xf0, 0xc0, 0x82, 0xc0, 0x83, 0xc0, 0x00, -0xc0, 0x06, 0xc0, 0x04, 0xed, 0x24, 0x10, 0xf8, 0x76, 0xa0, 0xed, 0x75, 0xf0, 0x21, 0xa4, 0x24, +0x12, 0x11, 0x66, 0x80, 0xee, 0xd0, 0x00, 0x22, 0xc0, 0xf0, 0xc0, 0x82, 0xc0, 0x83, 0xc0, 0x00, +0xc0, 0x06, 0xc0, 0x04, 0xed, 0x24, 0x10, 0xf8, 0x76, 0x9a, 0xed, 0x75, 0xf0, 0x21, 0xa4, 0x24, 0x05, 0xf5, 0x82, 0xe4, 0x34, 0xf8, 0xf5, 0x83, 0xc0, 0x82, 0xc0, 0x83, 0xa3, 0xa3, 0xe4, 0x78, -0x0d, 0xf0, 0xa3, 0xd8, 0xfc, 0xef, 0x54, 0x7f, 0x75, 0xf0, 0x02, 0xa4, 0x24, 0x0e, 0xf5, 0x82, -0xe5, 0xf0, 0x34, 0x2e, 0xf5, 0x83, 0xe4, 0x93, 0xfe, 0x74, 0x01, 0x93, 0xfc, 0xd0, 0x83, 0xd0, +0x0d, 0xf0, 0xa3, 0xd8, 0xfc, 0xef, 0x54, 0x7f, 0x75, 0xf0, 0x02, 0xa4, 0x24, 0x36, 0xf5, 0x82, +0xe5, 0xf0, 0x34, 0x30, 0xf5, 0x83, 0xe4, 0x93, 0xfe, 0x74, 0x01, 0x93, 0xfc, 0xd0, 0x83, 0xd0, 0x82, 0xec, 0xf0, 0xa3, 0xee, 0xf0, 0xed, 0x24, 0x08, 0xf8, 0xef, 0x44, 0x80, 0xf6, 0xd0, 0x04, -0xd0, 0x06, 0xd0, 0x00, 0xd0, 0x83, 0xd0, 0x82, 0xd0, 0xf0, 0x22, 0x75, 0x44, 0x00, 0x75, 0x49, +0xd0, 0x06, 0xd0, 0x00, 0xd0, 0x83, 0xd0, 0x82, 0xd0, 0xf0, 0x22, 0x75, 0x3e, 0x00, 0x75, 0x43, 0x00, 0x7a, 0x08, 0x79, 0x18, 0x78, 0x08, 0x76, 0x00, 0x77, 0x00, 0x08, 0x09, 0xda, 0xf8, 0x90, -0xf8, 0x04, 0xe0, 0xfc, 0x90, 0x2e, 0x2c, 0xe4, 0x93, 0xc3, 0x9c, 0x50, 0x05, 0xe4, 0x90, 0xf8, +0xf8, 0x04, 0xe0, 0xfc, 0x90, 0x30, 0x54, 0xe4, 0x93, 0xc3, 0x9c, 0x50, 0x05, 0xe4, 0x90, 0xf8, 0x04, 0xf0, 0x78, 0x08, 0x74, 0x80, 0x44, 0x7f, 0xf6, 0x74, 0x01, 0x44, 0x10, 0xf5, 0x89, 0x75, -0xb8, 0x00, 0xd2, 0xab, 0xd2, 0xa9, 0x22, 0x75, 0x81, 0x91, 0xd2, 0x8e, 0xd2, 0x8c, 0xd2, 0xaf, -0xe5, 0x49, 0x60, 0x36, 0xff, 0x90, 0xf9, 0x19, 0xe0, 0x54, 0x80, 0x60, 0x28, 0x78, 0x08, 0x79, +0xb8, 0x00, 0xd2, 0xab, 0xd2, 0xa9, 0x22, 0x75, 0x81, 0x8b, 0xd2, 0x8e, 0xd2, 0x8c, 0xd2, 0xaf, +0xe5, 0x43, 0x60, 0x36, 0xff, 0x90, 0xf9, 0x1b, 0xe0, 0x54, 0x80, 0x60, 0x28, 0x78, 0x08, 0x79, 0x08, 0xe0, 0x54, 0x7f, 0xfa, 0x7b, 0x00, 0xe6, 0x54, 0x7f, 0xb5, 0x02, 0x02, 0x7b, 0xff, 0x08, -0xd9, 0xf5, 0xeb, 0x70, 0x10, 0xea, 0xf0, 0xc0, 0x07, 0x12, 0x11, 0xc1, 0xad, 0x07, 0xaf, 0x02, -0x12, 0x11, 0xd8, 0xd0, 0x07, 0xa3, 0xa3, 0xa3, 0xdf, 0xce, 0x12, 0x10, 0xec, 0x80, 0xc1, 0xe7, -0x09, 0xf6, 0x08, 0xdf, 0xfa, 0x80, 0x46, 0xe7, 0x09, 0xf2, 0x08, 0xdf, 0xfa, 0x80, 0x3e, 0x88, -0x82, 0x8c, 0x83, 0xe7, 0x09, 0xf0, 0xa3, 0xdf, 0xfa, 0x80, 0x32, 0xe3, 0x09, 0xf6, 0x08, 0xdf, -0xfa, 0x80, 0x78, 0xe3, 0x09, 0xf2, 0x08, 0xdf, 0xfa, 0x80, 0x70, 0x88, 0x82, 0x8c, 0x83, 0xe3, -0x09, 0xf0, 0xa3, 0xdf, 0xfa, 0x80, 0x64, 0x89, 0x82, 0x8a, 0x83, 0xe0, 0xa3, 0xf6, 0x08, 0xdf, -0xfa, 0x80, 0x58, 0x89, 0x82, 0x8a, 0x83, 0xe0, 0xa3, 0xf2, 0x08, 0xdf, 0xfa, 0x80, 0x4c, 0x80, -0xd2, 0x80, 0xfa, 0x80, 0xc6, 0x80, 0xd4, 0x80, 0x69, 0x80, 0xf2, 0x80, 0x33, 0x80, 0x10, 0x80, -0xa6, 0x80, 0xea, 0x80, 0x9a, 0x80, 0xa8, 0x80, 0xda, 0x80, 0xe2, 0x80, 0xca, 0x80, 0x33, 0x89, -0x82, 0x8a, 0x83, 0xec, 0xfa, 0xe4, 0x93, 0xa3, 0xc8, 0xc5, 0x82, 0xc8, 0xcc, 0xc5, 0x83, 0xcc, -0xf0, 0xa3, 0xc8, 0xc5, 0x82, 0xc8, 0xcc, 0xc5, 0x83, 0xcc, 0xdf, 0xe9, 0xde, 0xe7, 0x80, 0x0d, -0x89, 0x82, 0x8a, 0x83, 0xe4, 0x93, 0xa3, 0xf6, 0x08, 0xdf, 0xf9, 0xec, 0xfa, 0xa9, 0xf0, 0xed, -0xfb, 0x22, 0x89, 0x82, 0x8a, 0x83, 0xec, 0xfa, 0xe0, 0xa3, 0xc8, 0xc5, 0x82, 0xc8, 0xcc, 0xc5, -0x83, 0xcc, 0xf0, 0xa3, 0xc8, 0xc5, 0x82, 0xc8, 0xcc, 0xc5, 0x83, 0xcc, 0xdf, 0xea, 0xde, 0xe8, -0x80, 0xdb, 0x89, 0x82, 0x8a, 0x83, 0xe4, 0x93, 0xa3, 0xf2, 0x08, 0xdf, 0xf9, 0x80, 0xcc, 0x88, -0xf0, 0xef, 0x60, 0x01, 0x0e, 0x4e, 0x60, 0xc3, 0x88, 0xf0, 0xed, 0x24, 0x02, 0xb4, 0x04, 0x00, -0x50, 0xb9, 0xf5, 0x82, 0xeb, 0x24, 0x02, 0xb4, 0x04, 0x00, 0x50, 0xaf, 0x23, 0x23, 0x45, 0x82, -0x23, 0x90, 0x13, 0x0f, 0x73, 0xbb, 0x01, 0x06, 0x89, 0x82, 0x8a, 0x83, 0xe0, 0x22, 0x50, 0x02, -0xe7, 0x22, 0xbb, 0xfe, 0x02, 0xe3, 0x22, 0x89, 0x82, 0x8a, 0x83, 0xe4, 0x93, 0x22, 0xbb, 0x01, -0x0c, 0xe5, 0x82, 0x29, 0xf5, 0x82, 0xe5, 0x83, 0x3a, 0xf5, 0x83, 0xe0, 0x22, 0x50, 0x06, 0xe9, -0x25, 0x82, 0xf8, 0xe6, 0x22, 0xbb, 0xfe, 0x06, 0xe9, 0x25, 0x82, 0xf8, 0xe2, 0x22, 0xe5, 0x82, -0x29, 0xf5, 0x82, 0xe5, 0x83, 0x3a, 0xf5, 0x83, 0xe4, 0x93, 0x22, 0xbb, 0x01, 0x06, 0x89, 0x82, -0x8a, 0x83, 0xf0, 0x22, 0x50, 0x02, 0xf7, 0x22, 0xbb, 0xfe, 0x01, 0xf3, 0x22, 0xf8, 0xbb, 0x01, -0x0d, 0xe5, 0x82, 0x29, 0xf5, 0x82, 0xe5, 0x83, 0x3a, 0xf5, 0x83, 0xe8, 0xf0, 0x22, 0x50, 0x06, -0xe9, 0x25, 0x82, 0xc8, 0xf6, 0x22, 0xbb, 0xfe, 0x05, 0xe9, 0x25, 0x82, 0xc8, 0xf2, 0x22, 0xc5, -0xf0, 0xf8, 0xa3, 0xe0, 0x28, 0xf0, 0xc5, 0xf0, 0xf8, 0xe5, 0x82, 0x15, 0x82, 0x70, 0x02, 0x15, -0x83, 0xe0, 0x38, 0xf0, 0x22, 0xa3, 0xf8, 0xe0, 0xc5, 0xf0, 0x25, 0xf0, 0xf0, 0xe5, 0x82, 0x15, -0x82, 0x70, 0x02, 0x15, 0x83, 0xe0, 0xc8, 0x38, 0xf0, 0xe8, 0x22, 0xbb, 0x01, 0x10, 0xe5, 0x82, -0x29, 0xf5, 0x82, 0xe5, 0x83, 0x3a, 0xf5, 0x83, 0xe0, 0xf5, 0xf0, 0xa3, 0xe0, 0x22, 0x50, 0x09, -0xe9, 0x25, 0x82, 0xf8, 0x86, 0xf0, 0x08, 0xe6, 0x22, 0xbb, 0xfe, 0x0a, 0xe9, 0x25, 0x82, 0xf8, -0xe2, 0xf5, 0xf0, 0x08, 0xe2, 0x22, 0xe5, 0x83, 0x2a, 0xf5, 0x83, 0xe9, 0x93, 0xf5, 0xf0, 0xa3, -0xe9, 0x93, 0x22, 0xbb, 0x01, 0x0a, 0x89, 0x82, 0x8a, 0x83, 0xf0, 0xe5, 0xf0, 0xa3, 0xf0, 0x22, -0x50, 0x06, 0xf7, 0x09, 0xa7, 0xf0, 0x19, 0x22, 0xbb, 0xfe, 0x06, 0xf3, 0xe5, 0xf0, 0x09, 0xf3, -0x19, 0x22, 0xf8, 0xbb, 0x01, 0x11, 0xe5, 0x82, 0x29, 0xf5, 0x82, 0xe5, 0x83, 0x3a, 0xf5, 0x83, -0xe8, 0xf0, 0xe5, 0xf0, 0xa3, 0xf0, 0x22, 0x50, 0x09, 0xe9, 0x25, 0x82, 0xc8, 0xf6, 0x08, 0xa6, -0xf0, 0x22, 0xbb, 0xfe, 0x09, 0xe9, 0x25, 0x82, 0xc8, 0xf2, 0xe5, 0xf0, 0x08, 0xf2, 0x22, 0xa4, -0x25, 0x82, 0xf5, 0x82, 0xe5, 0xf0, 0x35, 0x83, 0xf5, 0x83, 0x22, 0xe6, 0xfb, 0x08, 0xe6, 0xfa, -0x08, 0xe6, 0xf9, 0x22, 0xeb, 0xf6, 0x08, 0xea, 0xf6, 0x08, 0xe9, 0xf6, 0x22, 0xe0, 0xfb, 0xa3, -0xe0, 0xfa, 0xa3, 0xe0, 0xf9, 0x22, 0xeb, 0xf0, 0xa3, 0xea, 0xf0, 0xa3, 0xe9, 0xf0, 0x22, 0xd0, -0x83, 0xd0, 0x82, 0xf8, 0xe4, 0x93, 0x70, 0x12, 0x74, 0x01, 0x93, 0x70, 0x0d, 0xa3, 0xa3, 0x93, -0xf8, 0x74, 0x01, 0x93, 0xf5, 0x82, 0x88, 0x83, 0xe4, 0x73, 0x74, 0x02, 0x93, 0x68, 0x60, 0xef, -0xa3, 0xa3, 0xa3, 0x80, 0xdf, 0x90, 0xff, 0xfa, 0x74, 0x08, 0xf0, 0xa3, 0x74, 0x16, 0xf0, 0x90, -0xff, 0xf9, 0x74, 0x02, 0xf0, 0x90, 0xfa, 0xcb, 0xe4, 0xf0, 0xa3, 0x74, 0x0b, 0xf0, 0x7b, 0x00, -0x7a, 0x00, 0x79, 0x37, 0x75, 0x40, 0x00, 0xf5, 0x41, 0x7d, 0x01, 0x12, 0x23, 0xee, 0xe5, 0x37, -0x24, 0x80, 0x90, 0xff, 0xf8, 0xf0, 0xe5, 0x37, 0x64, 0x07, 0x60, 0x0b, 0xe5, 0x37, 0x64, 0x06, -0x60, 0x05, 0xe5, 0x37, 0xb4, 0x14, 0x1b, 0xd2, 0x94, 0xd2, 0x95, 0xd2, 0x92, 0xd2, 0x93, 0xe5, -0x37, 0xb4, 0x07, 0x08, 0x90, 0xf9, 0x65, 0x74, 0x02, 0xf0, 0x80, 0x06, 0x90, 0xf9, 0x65, 0x74, -0x01, 0xf0, 0x90, 0xfa, 0xcb, 0xe4, 0xf0, 0xa3, 0x74, 0x0d, 0xf0, 0x12, 0x17, 0x71, 0x90, 0xff, -0xf5, 0xe5, 0x37, 0xf0, 0x7b, 0x01, 0x7a, 0xfa, 0x79, 0xcb, 0xe4, 0xfd, 0x12, 0x20, 0xc5, 0x90, -0xfa, 0xcb, 0xe4, 0x75, 0xf0, 0x03, 0x12, 0x14, 0x2f, 0x12, 0x17, 0x71, 0xe5, 0x37, 0x30, 0xe7, -0x02, 0xd2, 0x02, 0xe4, 0xf5, 0x2c, 0xf5, 0x2a, 0xf5, 0x2b, 0xf5, 0x29, 0x12, 0x19, 0x92, 0x12, -0x18, 0x49, 0x12, 0x19, 0x6c, 0x90, 0xf9, 0x66, 0x12, 0x15, 0x06, 0x90, 0xf9, 0x6b, 0x12, 0x15, -0x06, 0x90, 0xff, 0xff, 0xe4, 0xf0, 0x90, 0xff, 0x83, 0xe0, 0xe4, 0xf0, 0x90, 0xff, 0x81, 0x74, -0x80, 0xf0, 0xa3, 0x74, 0x84, 0xf0, 0x90, 0xff, 0x80, 0xf0, 0xe4, 0xf5, 0x37, 0xe5, 0x37, 0x12, -0x18, 0xbf, 0xf5, 0x83, 0xe4, 0xf0, 0xe5, 0x37, 0x12, 0x18, 0xcd, 0xf5, 0x83, 0xe4, 0xf0, 0x05, -0x37, 0xe5, 0x37, 0xb4, 0x07, 0xe7, 0x78, 0x80, 0x76, 0xfe, 0x08, 0x76, 0xf0, 0x90, 0x2f, 0x06, -0xe4, 0x93, 0xff, 0x78, 0x7e, 0xf6, 0xfd, 0xad, 0x07, 0x90, 0x2f, 0x13, 0xe4, 0x93, 0xff, 0x08, -0xf6, 0xff, 0xed, 0x54, 0x0f, 0xfd, 0x12, 0x18, 0xaf, 0x74, 0x84, 0xf0, 0xed, 0x75, 0xf0, 0x08, -0xa4, 0x24, 0x47, 0xf5, 0x82, 0xe4, 0x34, 0xff, 0xf5, 0x83, 0xef, 0xf0, 0xc3, 0x74, 0xf0, 0x9f, -0x78, 0x81, 0xf6, 0x74, 0xfe, 0x94, 0x00, 0x18, 0x12, 0x18, 0x41, 0xce, 0xc3, 0x13, 0xce, 0x13, -0xd8, 0xf9, 0xff, 0xed, 0x12, 0x19, 0x07, 0xef, 0xf0, 0xed, 0x12, 0x19, 0x2d, 0xe4, 0xf5, 0x37, -0xe5, 0x37, 0x90, 0x2f, 0x00, 0x93, 0xff, 0x78, 0x7e, 0xf6, 0xfd, 0xe5, 0x37, 0x25, 0xe0, 0x24, -0x07, 0xf5, 0x82, 0xe4, 0x34, 0x2f, 0xf5, 0x83, 0xe4, 0x93, 0x08, 0xf6, 0xed, 0x30, 0xe7, 0x53, -0x18, 0xe6, 0x54, 0x0f, 0xf9, 0x12, 0x18, 0xaf, 0x12, 0x19, 0x15, 0x24, 0x47, 0xf5, 0x82, 0xe4, -0x34, 0xff, 0x12, 0x18, 0x31, 0xce, 0xc3, 0x13, 0xce, 0x13, 0xd8, 0xf9, 0xff, 0xe9, 0x12, 0x19, -0x07, 0xef, 0xf0, 0x12, 0x18, 0x38, 0xce, 0xc3, 0x13, 0xce, 0x13, 0xd8, 0xf9, 0x12, 0x19, 0x1a, -0x24, 0x45, 0xf5, 0x82, 0xe4, 0x34, 0xff, 0xf5, 0x83, 0xef, 0xf0, 0xe9, 0x12, 0x19, 0x2d, 0xe9, -0x75, 0xf0, 0x08, 0xa4, 0x24, 0x46, 0xf5, 0x82, 0xe4, 0x34, 0xff, 0xf5, 0x83, 0x74, 0x80, 0xf0, -0x02, 0x17, 0x46, 0x78, 0x7e, 0xe6, 0x54, 0x0f, 0xf9, 0x12, 0x18, 0xf9, 0x12, 0x19, 0x15, 0x24, -0x07, 0xf5, 0x82, 0xe4, 0x34, 0xff, 0x12, 0x18, 0x31, 0xce, 0xc3, 0x13, 0xce, 0x13, 0xd8, 0xf9, -0x12, 0x19, 0x1a, 0x24, 0x01, 0xf5, 0x82, 0xe4, 0x34, 0xff, 0xf5, 0x83, 0xef, 0xf0, 0x12, 0x18, -0x38, 0xce, 0xc3, 0x13, 0xce, 0x13, 0xd8, 0xf9, 0x12, 0x19, 0x1a, 0x24, 0x05, 0xf5, 0x82, 0xe4, -0x34, 0xff, 0xf5, 0x83, 0xef, 0xf0, 0xe9, 0x75, 0xf0, 0x08, 0xa4, 0x24, 0x02, 0xf5, 0x82, 0xe4, -0x34, 0xff, 0xf5, 0x83, 0xe4, 0xf0, 0xe9, 0x75, 0xf0, 0x08, 0xa4, 0x24, 0x06, 0xf5, 0x82, 0xe4, -0x34, 0xff, 0xf5, 0x83, 0xe4, 0xf0, 0x05, 0x37, 0xe5, 0x37, 0x64, 0x04, 0x60, 0x03, 0x02, 0x16, -0x70, 0x90, 0x2f, 0x05, 0xe4, 0x93, 0xff, 0x78, 0x7e, 0xf6, 0x12, 0x18, 0xf7, 0xe4, 0xf0, 0x90, -0x2f, 0x04, 0x93, 0xff, 0xf6, 0x12, 0x18, 0xad, 0xe4, 0xf0, 0x90, 0xff, 0xfd, 0x74, 0x05, 0xf0, -0x22, 0x7b, 0x00, 0x7a, 0x00, 0x79, 0x37, 0x90, 0xfa, 0xcb, 0xe4, 0x75, 0xf0, 0x01, 0x12, 0x14, -0x45, 0x85, 0xf0, 0x41, 0xf5, 0x40, 0x7d, 0x01, 0x02, 0x23, 0xee, 0xab, 0x2d, 0xaa, 0x2e, 0xa9, -0x2f, 0xe5, 0x52, 0x12, 0x13, 0xfb, 0x74, 0x01, 0x25, 0x2f, 0xf5, 0x2f, 0xe4, 0x35, 0x2e, 0xf5, -0x2e, 0xab, 0x2d, 0xfa, 0xa9, 0x2f, 0x74, 0x11, 0x12, 0x13, 0xfb, 0x74, 0x01, 0x25, 0x2f, 0xf5, -0x2f, 0xe4, 0x35, 0x2e, 0xf5, 0x2e, 0x90, 0xff, 0x06, 0xe0, 0xab, 0x2d, 0xaa, 0x2e, 0xa9, 0x2f, -0x12, 0x13, 0xfb, 0x74, 0x01, 0x25, 0x2f, 0xf5, 0x2f, 0xe4, 0x35, 0x2e, 0xf5, 0x2e, 0xab, 0x2d, -0xfa, 0xa9, 0x2f, 0xe4, 0x12, 0x13, 0xfb, 0x04, 0x25, 0x2f, 0xf5, 0x2f, 0xe4, 0x35, 0x2e, 0xf5, -0x2e, 0xab, 0x2d, 0xfa, 0xa9, 0x2f, 0xe4, 0x12, 0x13, 0xfb, 0x04, 0x25, 0x2f, 0xf5, 0x2f, 0xe4, -0x35, 0x2e, 0xf5, 0x2e, 0x90, 0xff, 0x04, 0xe0, 0xab, 0x2d, 0xaa, 0x2e, 0xa9, 0x2f, 0x12, 0x13, -0xfb, 0x74, 0x01, 0x25, 0x2f, 0xf5, 0x2f, 0xe4, 0x35, 0x2e, 0xf5, 0x2e, 0x90, 0xff, 0x05, 0xe0, -0xab, 0x2d, 0xaa, 0x2e, 0xa9, 0x2f, 0x12, 0x13, 0xfb, 0x74, 0x01, 0x25, 0x2f, 0xf5, 0x2f, 0xe4, -0x35, 0x2e, 0xf5, 0x2e, 0x22, 0xf5, 0x83, 0xe0, 0x54, 0x08, 0xab, 0x2d, 0xaa, 0x2e, 0xa9, 0x2f, -0x22, 0xf5, 0x83, 0xef, 0xf0, 0xfd, 0x7c, 0x00, 0xc3, 0x78, 0x81, 0xe6, 0x9d, 0xf6, 0x18, 0xe6, -0x9c, 0xf6, 0xe6, 0xfe, 0x08, 0xe6, 0x78, 0x03, 0x22, 0x75, 0x2d, 0x01, 0x75, 0x2e, 0xf9, 0x75, -0x2f, 0x6e, 0x22, 0x90, 0xfa, 0xba, 0xe0, 0x90, 0xfa, 0xb6, 0xf0, 0x90, 0xfa, 0xb5, 0xe0, 0x24, -0xfc, 0x22, 0x90, 0xfa, 0xb8, 0xe0, 0xff, 0x7e, 0x00, 0xc3, 0x90, 0xfa, 0xbc, 0xe0, 0x9f, 0xf0, -0x90, 0xfa, 0xbb, 0xe0, 0x9e, 0xf0, 0x90, 0xfa, 0xb3, 0xee, 0x8f, 0xf0, 0x12, 0x14, 0x2f, 0xef, -0x25, 0x55, 0xf5, 0x55, 0xee, 0x35, 0x54, 0xf5, 0x54, 0x22, 0x7b, 0x01, 0x7a, 0xfa, 0x79, 0xb0, -0x90, 0xfa, 0xb3, 0xe0, 0xf5, 0x40, 0xa3, 0xe0, 0xf5, 0x41, 0x22, 0x78, 0x82, 0xe6, 0xfe, 0x08, -0xe6, 0x8e, 0x83, 0x24, 0x04, 0xf5, 0x82, 0xe4, 0x35, 0x83, 0xf5, 0x83, 0x22, 0x54, 0x0f, 0x75, -0xf0, 0x08, 0xa4, 0x24, 0x40, 0xf5, 0x82, 0xe4, 0x34, 0xff, 0xf5, 0x83, 0x22, 0xe5, 0x53, 0x75, -0xf0, 0x08, 0xa4, 0x24, 0x48, 0xf5, 0x82, 0xe4, 0x34, 0xff, 0x22, 0xe5, 0x53, 0x75, 0xf0, 0x08, -0xa4, 0x24, 0x08, 0xf5, 0x82, 0xe4, 0x34, 0xff, 0x22, 0x90, 0xff, 0x00, 0xe0, 0x54, 0x1f, 0x22, -0x90, 0xfa, 0xb5, 0xe0, 0xff, 0x24, 0xfc, 0x22, 0x75, 0x2a, 0x00, 0x8f, 0x2b, 0x90, 0xf9, 0x6b, -0x12, 0x14, 0xfd, 0x90, 0x00, 0x02, 0x22, 0x54, 0x0f, 0x75, 0xf0, 0x08, 0xa4, 0x24, 0x00, 0xf5, -0x82, 0xe4, 0x34, 0xff, 0xf5, 0x83, 0x22, 0x75, 0xf0, 0x08, 0xa4, 0x24, 0x41, 0xf5, 0x82, 0xe4, -0x34, 0xff, 0xf5, 0x83, 0x22, 0x74, 0x80, 0xf0, 0x08, 0xe6, 0xff, 0xe9, 0x75, 0xf0, 0x08, 0xa4, -0x22, 0x74, 0xae, 0x25, 0x36, 0xf5, 0x82, 0xe4, 0x34, 0xfa, 0xf5, 0x83, 0x22, 0x75, 0xf0, 0x08, -0xa4, 0x24, 0x42, 0xf5, 0x82, 0xe4, 0x34, 0xff, 0xf5, 0x83, 0x74, 0x80, 0xf0, 0x22, 0x90, 0xff, -0x82, 0xe0, 0x44, 0x08, 0xf0, 0x22, 0x90, 0xff, 0xfe, 0xe0, 0x44, 0x03, 0xf0, 0x90, 0xff, 0xfc, -0xe0, 0x54, 0xfd, 0xf0, 0x22, 0x78, 0x6d, 0xe6, 0x54, 0xfd, 0xf6, 0x90, 0xff, 0xfd, 0x74, 0x65, -0xf0, 0x22, 0x12, 0x14, 0xdf, 0xe0, 0xfe, 0xa3, 0xe0, 0xff, 0x4e, 0x22, 0x7b, 0x01, 0x7a, 0xf9, -0x79, 0x6e, 0x22, 0x7b, 0x01, 0x7a, 0xfa, 0x79, 0xb3, 0x22, 0x90, 0xff, 0x80, 0xe0, 0x44, 0x08, -0xf0, 0x22, 0x90, 0xff, 0x83, 0xe0, 0x54, 0x7f, 0xf0, 0x22, 0xe0, 0xff, 0x90, 0xf9, 0x66, 0x02, -0x14, 0xfd, 0x75, 0x30, 0x01, 0x75, 0x31, 0x09, 0x22, 0xd3, 0xe5, 0x35, 0x94, 0x08, 0xe5, 0x34, -0x94, 0x01, 0x22, 0x90, 0xfa, 0xba, 0xe0, 0xff, 0x90, 0xfa, 0xb6, 0xf0, 0x22, 0x90, 0xff, 0xa4, -0xe0, 0x54, 0xef, 0x22, 0x90, 0xff, 0xb4, 0xe0, 0x54, 0xef, 0x22, 0x8f, 0x38, 0x12, 0x27, 0x19, -0x78, 0x86, 0xe6, 0xfe, 0x08, 0xe6, 0xff, 0x8e, 0x83, 0x24, 0x08, 0x12, 0x20, 0x25, 0xe0, 0xfd, -0x12, 0x20, 0xa6, 0x8a, 0x83, 0x24, 0x0a, 0x12, 0x20, 0x25, 0xed, 0xf0, 0x12, 0x20, 0x7c, 0x24, -0x07, 0x12, 0x20, 0x25, 0xe0, 0xff, 0x12, 0x20, 0xbe, 0x24, 0x09, 0x12, 0x20, 0x25, 0xef, 0xf0, -0x90, 0xf9, 0x65, 0xe0, 0x30, 0xe4, 0x20, 0x08, 0x12, 0x20, 0x2f, 0xc0, 0x83, 0xc0, 0x82, 0xa3, -0xe0, 0x25, 0xe0, 0xff, 0x05, 0x82, 0xd5, 0x82, 0x02, 0x15, 0x83, 0x15, 0x82, 0xe0, 0x33, 0xd0, -0x82, 0xd0, 0x83, 0xf0, 0xa3, 0xef, 0xf0, 0x78, 0x86, 0x12, 0x20, 0x2f, 0xe0, 0xfc, 0xa3, 0xe0, -0xfd, 0xec, 0xff, 0x12, 0x20, 0xa6, 0x8a, 0x83, 0x24, 0x08, 0x12, 0x20, 0x25, 0xef, 0xf0, 0xed, -0x12, 0x20, 0xbe, 0x24, 0x07, 0x12, 0x20, 0x25, 0xed, 0xf0, 0x8b, 0x82, 0x8a, 0x83, 0xa3, 0xa3, -0xe0, 0xff, 0x53, 0x07, 0xc7, 0x08, 0xe6, 0xfc, 0x08, 0xe6, 0xfd, 0x12, 0x20, 0x69, 0xa3, 0xe0, -0x30, 0xe3, 0x12, 0x8d, 0x82, 0x8c, 0x83, 0xe5, 0x82, 0x24, 0x05, 0x12, 0x20, 0x25, 0xe0, 0x90, -0x2f, 0x4d, 0x93, 0x42, 0x07, 0x53, 0x07, 0xfb, 0x12, 0x20, 0xae, 0x24, 0x06, 0x12, 0x20, 0x25, -0xe0, 0x60, 0x03, 0x43, 0x07, 0x04, 0x53, 0x07, 0xfc, 0x78, 0x86, 0x12, 0x20, 0x96, 0x24, 0x04, -0x12, 0x20, 0x25, 0xe0, 0x42, 0x07, 0x43, 0x07, 0x80, 0x12, 0x20, 0xa6, 0xf5, 0x82, 0x8a, 0x83, -0xa3, 0xa3, 0xef, 0xf0, 0x12, 0x20, 0xbe, 0x24, 0x04, 0x12, 0x20, 0x25, 0xe0, 0xff, 0x8d, 0x82, -0x8c, 0x83, 0xa3, 0xa3, 0xe0, 0xfc, 0xa3, 0xe0, 0xfd, 0x30, 0xe1, 0x05, 0x53, 0x07, 0xdf, 0x80, -0x03, 0x43, 0x07, 0x20, 0xec, 0x30, 0xe4, 0x05, 0x53, 0x07, 0xef, 0x80, 0x03, 0x43, 0x07, 0x10, -0x90, 0xf9, 0x65, 0xe0, 0xfe, 0x54, 0x03, 0x60, 0x4c, 0x53, 0x07, 0xdf, 0xee, 0x30, 0xe1, 0x42, -0x12, 0x20, 0xae, 0x24, 0x09, 0x12, 0x20, 0x25, 0xe0, 0x14, 0x60, 0x31, 0x14, 0x60, 0x29, 0x14, -0x60, 0x26, 0x14, 0x60, 0x28, 0x24, 0x04, 0x70, 0x2c, 0xe5, 0x38, 0xb4, 0x03, 0x0d, 0x30, 0x95, -0x05, 0x43, 0x07, 0x02, 0x80, 0x1f, 0x53, 0x07, 0xfd, 0x80, 0x1a, 0x30, 0x93, 0x05, 0x43, 0x07, -0x02, 0x80, 0x12, 0x53, 0x07, 0xfd, 0x80, 0x0d, 0x43, 0x07, 0x02, 0x80, 0x08, 0x53, 0x07, 0xfd, -0x80, 0x03, 0x53, 0x07, 0xfd, 0x12, 0x20, 0x94, 0x24, 0x04, 0x12, 0x20, 0x25, 0xef, 0xf0, 0x8d, -0x82, 0x8c, 0x83, 0xa3, 0xa3, 0xa3, 0xe0, 0xff, 0x90, 0xf9, 0x65, 0xe0, 0xfe, 0x54, 0x03, 0x60, -0x4a, 0xee, 0x30, 0xe1, 0x43, 0x08, 0x12, 0x20, 0xb0, 0x24, 0x09, 0x12, 0x20, 0x25, 0xe0, 0x14, -0x60, 0x2c, 0x14, 0x60, 0x2e, 0x14, 0x60, 0x26, 0x14, 0x60, 0x28, 0x24, 0x04, 0x70, 0x2c, 0xe5, -0x38, 0xb4, 0x03, 0x0d, 0x30, 0x94, 0x05, 0x53, 0x07, 0x7f, 0x80, 0x1f, 0x43, 0x07, 0x80, 0x80, -0x1a, 0x30, 0x92, 0x05, 0x53, 0x07, 0x7f, 0x80, 0x12, 0x43, 0x07, 0x80, 0x80, 0x0d, 0x53, 0x07, -0x7f, 0x80, 0x08, 0x43, 0x07, 0x80, 0x80, 0x03, 0x53, 0x07, 0x7f, 0x78, 0x86, 0x12, 0x20, 0x65, -0xe0, 0xfc, 0xa3, 0xe0, 0xfd, 0x30, 0xe0, 0x05, 0x43, 0x07, 0x20, 0x80, 0x03, 0x53, 0x07, 0xdf, -0xec, 0x30, 0xe3, 0x05, 0x43, 0x07, 0x40, 0x80, 0x03, 0x53, 0x07, 0xbf, 0xec, 0x30, 0xe0, 0x05, -0x43, 0x07, 0x10, 0x80, 0x03, 0x53, 0x07, 0xef, 0xed, 0x30, 0xe4, 0x05, 0x43, 0x07, 0x08, 0x80, -0x03, 0x53, 0x07, 0xf7, 0xed, 0x30, 0xe5, 0x05, 0x43, 0x07, 0x04, 0x80, 0x03, 0x53, 0x07, 0xfb, -0xed, 0x30, 0xe6, 0x05, 0x43, 0x07, 0x01, 0x80, 0x03, 0x53, 0x07, 0xfe, 0xed, 0x30, 0xe7, 0x05, -0x43, 0x07, 0x02, 0x80, 0x03, 0x53, 0x07, 0xfd, 0x78, 0x84, 0x12, 0x20, 0x65, 0xa3, 0xef, 0xf0, -0x12, 0x2f, 0x80, 0x7f, 0x00, 0x22, 0x12, 0x0f, 0x89, 0x78, 0x8e, 0xef, 0xf6, 0x12, 0x27, 0x19, -0x12, 0x20, 0x70, 0x8e, 0x83, 0x24, 0x09, 0x12, 0x20, 0x25, 0xe0, 0xfd, 0x12, 0x20, 0x53, 0x90, -0x00, 0x0a, 0x12, 0x20, 0x78, 0x24, 0x0a, 0x12, 0x20, 0x25, 0xe0, 0x90, 0x00, 0x0b, 0x12, 0x14, -0x0d, 0x12, 0x20, 0x70, 0xf5, 0x82, 0x8e, 0x83, 0xa3, 0xa3, 0xa3, 0xe0, 0xf5, 0x59, 0x12, 0x20, -0x7c, 0x24, 0x04, 0x12, 0x20, 0x25, 0xe0, 0xf5, 0x5a, 0x8f, 0x82, 0x8e, 0x83, 0xa3, 0xa3, 0xe0, -0xf5, 0x5b, 0xe5, 0x59, 0xc4, 0x13, 0x13, 0x13, 0x54, 0x01, 0x78, 0x8e, 0xf6, 0xd3, 0x94, 0x00, -0x40, 0x06, 0xe5, 0x5a, 0x30, 0xe1, 0x01, 0x06, 0x78, 0x8e, 0xe6, 0x12, 0x20, 0x52, 0x90, 0x00, -0x0c, 0xef, 0x12, 0x14, 0x0d, 0x78, 0x86, 0x12, 0x20, 0x2f, 0xa3, 0xa3, 0xe0, 0xfe, 0xa3, 0xe0, -0xff, 0x53, 0x07, 0x0c, 0x53, 0x06, 0xe6, 0xe5, 0x59, 0x30, 0xe5, 0x03, 0x43, 0x07, 0x01, 0xe5, -0x5a, 0x20, 0xe5, 0x0e, 0xe5, 0x59, 0x54, 0x7f, 0x70, 0x08, 0xe5, 0x59, 0x20, 0xe7, 0x03, 0x43, -0x07, 0x02, 0xe5, 0x59, 0x30, 0xe3, 0x03, 0x43, 0x07, 0x10, 0xe5, 0x59, 0x30, 0xe2, 0x03, 0x43, -0x07, 0x20, 0xe5, 0x59, 0x54, 0x03, 0x60, 0x03, 0x43, 0x07, 0x40, 0xe5, 0x59, 0x30, 0xe1, 0x03, -0x43, 0x07, 0x80, 0xe5, 0x59, 0x30, 0xe4, 0x03, 0x43, 0x06, 0x01, 0xe5, 0x59, 0x30, 0xe6, 0x03, -0x43, 0x06, 0x08, 0xe5, 0x5a, 0x20, 0xe4, 0x0e, 0xe5, 0x59, 0x54, 0x7f, 0x70, 0x08, 0xe5, 0x59, -0x20, 0xe7, 0x03, 0x43, 0x06, 0x10, 0x53, 0x07, 0xfb, 0x53, 0x06, 0x79, 0x90, 0x00, 0x05, 0xee, -0x8f, 0xf0, 0x12, 0x14, 0xb2, 0xe5, 0x5b, 0x30, 0xe3, 0x12, 0x54, 0x30, 0xff, 0xc4, 0x54, 0x0f, -0x12, 0x20, 0x52, 0x90, 0x00, 0x08, 0xef, 0x12, 0x14, 0x0d, 0x80, 0x0a, 0x12, 0x20, 0x53, 0x90, -0x00, 0x08, 0xe4, 0x12, 0x14, 0x0d, 0xe5, 0x5b, 0x54, 0x03, 0x12, 0x20, 0x52, 0x90, 0x00, 0x07, -0xef, 0x12, 0x14, 0x0d, 0xe5, 0x5b, 0x54, 0x04, 0xff, 0xc3, 0x13, 0x90, 0x00, 0x09, 0x12, 0x14, -0x0d, 0x90, 0x00, 0x07, 0x12, 0x13, 0xce, 0x70, 0x13, 0x12, 0x20, 0x53, 0xe9, 0x24, 0x09, 0xf9, -0xe4, 0x3a, 0xfa, 0x12, 0x13, 0xb5, 0xff, 0xc3, 0x13, 0x12, 0x13, 0xfb, 0x12, 0x20, 0x94, 0x24, -0x08, 0x12, 0x20, 0x25, 0xe0, 0xfe, 0x8d, 0x82, 0x8c, 0x83, 0xe5, 0x82, 0x24, 0x07, 0x12, 0x20, -0x25, 0xe0, 0xfd, 0xee, 0xed, 0x12, 0x20, 0x52, 0x90, 0x00, 0x03, 0xee, 0x8f, 0xf0, 0x12, 0x14, -0xb2, 0x12, 0x2f, 0x80, 0x7d, 0x0a, 0xe4, 0xff, 0x12, 0x2c, 0xc0, 0x02, 0x10, 0x0c, 0x90, 0xfa, -0xe2, 0xe0, 0xb4, 0x03, 0x06, 0x7e, 0x00, 0x7f, 0x40, 0x80, 0x04, 0x7e, 0x00, 0x7f, 0x08, 0x90, -0xfa, 0xd6, 0xee, 0xf0, 0xa3, 0xef, 0xf0, 0x90, 0x00, 0x05, 0x12, 0x13, 0xce, 0xff, 0x7e, 0x00, -0x90, 0xfa, 0xd2, 0xee, 0xf0, 0xa3, 0xef, 0xf0, 0x70, 0x03, 0x7f, 0x08, 0x22, 0x90, 0x00, 0x08, -0x12, 0x14, 0x5b, 0xff, 0x90, 0xfa, 0xd4, 0xe5, 0xf0, 0xf0, 0xa3, 0xef, 0xf0, 0xae, 0x02, 0xaf, -0x01, 0x8e, 0x56, 0x8f, 0x57, 0x74, 0x0a, 0x25, 0x57, 0xf5, 0x57, 0xe4, 0x35, 0x56, 0xf5, 0x56, -0x90, 0xfa, 0xd7, 0xe0, 0xff, 0x14, 0xfe, 0x90, 0xfa, 0xd5, 0xe0, 0x5e, 0xfe, 0xc3, 0xef, 0x9e, -0xff, 0x90, 0xfa, 0xd9, 0xf0, 0xc3, 0x90, 0xfa, 0xd3, 0xe0, 0x9f, 0x90, 0xfa, 0xd2, 0xe0, 0x94, -0x00, 0x50, 0x06, 0xa3, 0xe0, 0x90, 0xfa, 0xd9, 0xf0, 0x12, 0x1e, 0x2d, 0x60, 0x03, 0xe0, 0xff, -0x22, 0x12, 0x2a, 0x80, 0x90, 0xfa, 0xd2, 0xe0, 0xfe, 0xa3, 0xe0, 0xff, 0x4e, 0x60, 0x2b, 0x90, -0xfa, 0xd6, 0xe0, 0xfc, 0xa3, 0xe0, 0xfd, 0xd3, 0xef, 0x9d, 0xee, 0x9c, 0x40, 0x07, 0xe0, 0x90, -0xfa, 0xd9, 0xf0, 0x80, 0x08, 0x90, 0xfa, 0xd3, 0xe0, 0x90, 0xfa, 0xd9, 0xf0, 0x12, 0x1e, 0x2d, -0x60, 0x03, 0xe0, 0xff, 0x22, 0x12, 0x2a, 0x80, 0x80, 0xca, 0x7b, 0x00, 0x7a, 0x00, 0x79, 0x58, -0xe4, 0xf5, 0x40, 0xf5, 0x41, 0x7d, 0x01, 0x12, 0x23, 0xee, 0x7f, 0x00, 0x22, 0xaa, 0x56, 0xa9, -0x57, 0x7b, 0x01, 0x90, 0xfa, 0xd4, 0xe0, 0xfc, 0xa3, 0xe0, 0xfd, 0x90, 0xfa, 0xd9, 0xe0, 0xf5, -0x50, 0x12, 0x26, 0x25, 0x90, 0xfa, 0xd8, 0xef, 0xf0, 0x22, 0xef, 0x24, 0xae, 0x60, 0x52, 0x24, -0xfe, 0x60, 0x2e, 0x24, 0xfe, 0x70, 0x03, 0x02, 0x1e, 0xed, 0x24, 0x06, 0x60, 0x03, 0x02, 0x1f, -0x35, 0x78, 0x77, 0xe6, 0x54, 0xfb, 0xf6, 0x90, 0xff, 0xa5, 0xe0, 0xf5, 0x36, 0x44, 0x0f, 0xf0, -0x74, 0x33, 0x90, 0xfa, 0x90, 0xf0, 0xe5, 0x36, 0xa3, 0xf0, 0x90, 0xfa, 0xae, 0x74, 0x01, 0xf0, -0x22, 0x78, 0x78, 0xe6, 0x54, 0xfb, 0xf6, 0x90, 0xff, 0xb5, 0xe0, 0xf5, 0x36, 0x44, 0x0f, 0xf0, -0x74, 0x43, 0x90, 0xfa, 0x92, 0xf0, 0xe5, 0x36, 0xa3, 0xf0, 0x90, 0xfa, 0xaf, 0x74, 0x01, 0xf0, -0x22, 0x90, 0xfa, 0x9c, 0xe0, 0xa3, 0x20, 0xe5, 0x03, 0x02, 0x1f, 0x35, 0x90, 0xff, 0xa6, 0xe0, -0x90, 0xfa, 0xc9, 0xf0, 0xa3, 0xf0, 0x90, 0xfa, 0xc9, 0xe0, 0xff, 0x54, 0x0f, 0xfe, 0x60, 0x10, -0x90, 0xff, 0xa6, 0x12, 0x20, 0x83, 0x90, 0xff, 0xa6, 0xe0, 0x90, 0xfa, 0xc9, 0xf0, 0x80, 0xe6, -0x90, 0xfa, 0xca, 0xe0, 0xff, 0x74, 0x34, 0xfe, 0x12, 0x29, 0xda, 0xef, 0x70, 0x57, 0x90, 0xfa, -0xca, 0xe0, 0xff, 0x74, 0x34, 0x90, 0xfa, 0x94, 0xf0, 0xef, 0xa3, 0xf0, 0x22, 0x90, 0xfa, 0xa6, -0xe0, 0xa3, 0x30, 0xe5, 0x40, 0x90, 0xff, 0xb6, 0xe0, 0x90, 0xfa, 0xc9, 0xf0, 0xa3, 0xf0, 0x90, -0xfa, 0xc9, 0xe0, 0xff, 0x54, 0x0f, 0xfe, 0x60, 0x10, 0x90, 0xff, 0xb6, 0x12, 0x20, 0x83, 0x90, -0xff, 0xb6, 0xe0, 0x90, 0xfa, 0xc9, 0xf0, 0x80, 0xe6, 0x90, 0xfa, 0xca, 0xe0, 0xff, 0x74, 0x44, -0xfe, 0x12, 0x29, 0xda, 0xef, 0x70, 0x0e, 0x90, 0xfa, 0xca, 0xe0, 0xff, 0x74, 0x44, 0x90, 0xfa, -0x96, 0xf0, 0xef, 0xa3, 0xf0, 0x22, 0xc0, 0xe0, 0xc0, 0xf0, 0xc0, 0x83, 0xc0, 0x82, 0xc0, 0xd0, -0x75, 0xd0, 0x00, 0xc0, 0x00, 0xc0, 0x01, 0xc0, 0x02, 0xc0, 0x03, 0xc0, 0x04, 0xc0, 0x05, 0xc0, -0x06, 0xc0, 0x07, 0x90, 0xff, 0x92, 0xe0, 0xff, 0x90, 0xfa, 0xc8, 0xf0, 0x90, 0xff, 0x92, 0xe4, -0xf0, 0xef, 0x12, 0x15, 0x0f, 0x1f, 0xed, 0x26, 0x1f, 0xed, 0x2e, 0x1f, 0x90, 0x30, 0x1f, 0x90, -0x32, 0x1f, 0x9e, 0x38, 0x1f, 0xb0, 0x3a, 0x1f, 0xe2, 0x3e, 0x1f, 0xcd, 0x44, 0x1f, 0xc2, 0x46, -0x1f, 0xd8, 0x50, 0x1f, 0xd8, 0x52, 0x1f, 0xd8, 0x54, 0x1f, 0xd8, 0x56, 0x00, 0x00, 0x1f, 0xf2, -0x90, 0xfa, 0xc8, 0xe0, 0xfd, 0x7c, 0x00, 0x7f, 0x01, 0x12, 0x10, 0x9c, 0x80, 0x62, 0x7c, 0x00, -0x7d, 0x01, 0x7f, 0x03, 0x12, 0x10, 0x9c, 0x90, 0xff, 0xfe, 0xe0, 0x44, 0x20, 0xf0, 0x80, 0x50, -0x7c, 0x00, 0x7d, 0x01, 0x7f, 0x02, 0x12, 0x10, 0x9c, 0x90, 0xff, 0xfe, 0xe0, 0x44, 0x40, 0xf0, -0x80, 0x3e, 0x7c, 0x00, 0x7d, 0x01, 0x7f, 0x05, 0x12, 0x10, 0x9c, 0x80, 0x33, 0x7c, 0x00, 0x7d, -0x01, 0x7f, 0x06, 0x12, 0x10, 0x9c, 0x80, 0x28, 0x90, 0xfa, 0xc8, 0xe0, 0xff, 0x12, 0x1e, 0x4a, -0x80, 0x1e, 0x7c, 0x00, 0x7d, 0x01, 0x7f, 0x04, 0x12, 0x10, 0x9c, 0x80, 0x13, 0x12, 0x25, 0x13, -0x80, 0x0e, 0x90, 0xfa, 0xc8, 0xe0, 0x24, 0x00, 0xff, 0xe4, 0x34, 0xff, 0xfe, 0x12, 0x29, 0xda, -0xd0, 0x07, 0xd0, 0x06, 0xd0, 0x05, 0xd0, 0x04, 0xd0, 0x03, 0xd0, 0x02, 0xd0, 0x01, 0xd0, 0x00, -0xd0, 0xd0, 0xd0, 0x82, 0xd0, 0x83, 0xd0, 0xf0, 0xd0, 0xe0, 0x32, 0x78, 0x82, 0xe6, 0xfe, 0x08, -0xe6, 0x24, 0x04, 0x8e, 0x83, 0xf5, 0x82, 0xe4, 0x35, 0x83, 0xf5, 0x83, 0x22, 0x78, 0x82, 0xe6, -0xfe, 0x08, 0xe6, 0xf5, 0x82, 0x8e, 0x83, 0x22, 0x78, 0x86, 0xe6, 0xfe, 0x08, 0xe6, 0xaa, 0x06, -0xf8, 0xac, 0x02, 0x7d, 0x01, 0x7b, 0xff, 0x7a, 0x2f, 0x79, 0x52, 0x7e, 0x00, 0x7f, 0x0a, 0x02, -0x13, 0x8f, 0xff, 0x90, 0xf9, 0x6b, 0x02, 0x14, 0xfd, 0x90, 0xf9, 0x66, 0x12, 0x14, 0xfd, 0x90, -0x00, 0x04, 0x02, 0x13, 0xce, 0xe6, 0xfc, 0x08, 0xe6, 0xf5, 0x82, 0x8c, 0x83, 0xa3, 0xa3, 0x22, -0x78, 0x84, 0xe6, 0xfe, 0x08, 0xe6, 0xff, 0x22, 0xed, 0x12, 0x14, 0x0d, 0x8f, 0x82, 0x8e, 0x83, -0xe5, 0x82, 0x22, 0xef, 0xf0, 0x90, 0xfa, 0xca, 0xe0, 0x54, 0x0f, 0x4e, 0xfe, 0xf0, 0xef, 0x54, -0xf0, 0x4e, 0xf0, 0x22, 0x78, 0x84, 0xe6, 0xfc, 0x08, 0xe6, 0xfd, 0x8c, 0x83, 0x22, 0xa6, 0x07, -0xe6, 0x24, 0x74, 0xf8, 0xe6, 0x22, 0x78, 0x84, 0xe6, 0xfa, 0x08, 0xe6, 0xfb, 0x22, 0x78, 0x86, -0xe6, 0xfc, 0x08, 0xe6, 0x8c, 0x83, 0x22, 0x26, 0xf6, 0x18, 0xee, 0x36, 0xf6, 0x22, 0x8b, 0x82, -0x8a, 0x83, 0xe5, 0x82, 0x22, 0x8b, 0x38, 0x8a, 0x39, 0x89, 0x3a, 0x8d, 0x3b, 0x90, 0xfa, 0xce, -0xe4, 0xf0, 0xa3, 0x74, 0x02, 0xf0, 0x7b, 0x01, 0x7a, 0xfa, 0x79, 0xcd, 0x90, 0xfa, 0xce, 0xe0, -0xf5, 0x40, 0xa3, 0xe0, 0xf5, 0x41, 0x7d, 0x01, 0x12, 0x23, 0xee, 0x90, 0xfa, 0xcd, 0xe0, 0x65, -0x3b, 0x60, 0x46, 0xa3, 0xe0, 0xff, 0xa3, 0xe0, 0xa3, 0xcf, 0xf0, 0xa3, 0xef, 0xf0, 0x12, 0x21, -0x54, 0x90, 0xfa, 0xcd, 0xe0, 0xff, 0x90, 0xfa, 0xd0, 0xe4, 0x8f, 0xf0, 0x12, 0x14, 0x2f, 0x12, -0x21, 0x54, 0x90, 0xfa, 0xd0, 0xe0, 0xff, 0xa3, 0xe0, 0x90, 0xfa, 0xce, 0xcf, 0xf0, 0xa3, 0xef, -0xf0, 0x90, 0xfa, 0xcd, 0xe0, 0xa3, 0x75, 0xf0, 0x00, 0x12, 0x14, 0x2f, 0x90, 0xfa, 0xce, 0xe4, -0x75, 0xf0, 0x04, 0x12, 0x14, 0x2f, 0x02, 0x20, 0xd6, 0x90, 0xfa, 0xcf, 0xe0, 0x24, 0x01, 0xff, -0x90, 0xfa, 0xce, 0xe0, 0x34, 0x00, 0xab, 0x38, 0xaa, 0x39, 0xa9, 0x3a, 0x8f, 0xf0, 0x12, 0x14, -0x93, 0x7f, 0x00, 0x22, 0x7b, 0x01, 0x7a, 0xfa, 0x79, 0xcd, 0x90, 0xfa, 0xce, 0xe4, 0x75, 0xf0, -0x01, 0x12, 0x14, 0x2f, 0x85, 0xf0, 0x41, 0xf5, 0x40, 0x7d, 0x01, 0x02, 0x23, 0xee, 0x8f, 0x68, -0x12, 0x27, 0x19, 0x12, 0x20, 0x70, 0x8e, 0x83, 0x24, 0x0b, 0x12, 0x20, 0x25, 0xe0, 0x54, 0xfb, -0xf0, 0x44, 0x02, 0xf0, 0x08, 0x12, 0x20, 0x65, 0xe0, 0xa3, 0x30, 0xe5, 0x0c, 0x12, 0x20, 0x7c, -0x24, 0x0b, 0x12, 0x20, 0x25, 0xe0, 0x44, 0x01, 0xf0, 0x78, 0x82, 0xe6, 0xfe, 0x08, 0xe6, 0xff, -0xf5, 0x82, 0x8e, 0x83, 0xe0, 0x54, 0xb8, 0xfd, 0xf0, 0xe5, 0x68, 0x24, 0xfe, 0x44, 0x20, 0xfc, -0x4d, 0xf0, 0xe5, 0x82, 0x24, 0x04, 0x12, 0x20, 0x25, 0xe0, 0x54, 0xb8, 0xf0, 0x4c, 0xf0, 0x8f, -0x82, 0x8e, 0x83, 0xa3, 0x74, 0x03, 0xf0, 0x18, 0xe6, 0xfe, 0x08, 0xe6, 0xff, 0x8e, 0x83, 0x24, -0x05, 0x12, 0x20, 0x25, 0xc0, 0x83, 0xc0, 0x82, 0xe0, 0xfd, 0x74, 0x95, 0x25, 0x68, 0xf5, 0x82, -0xe4, 0x34, 0xfa, 0xf5, 0x83, 0xe0, 0x54, 0xfc, 0x44, 0x03, 0xfc, 0xed, 0x4c, 0xd0, 0x82, 0xd0, -0x83, 0xf0, 0x8f, 0x82, 0x8e, 0x83, 0xe0, 0x44, 0x80, 0xf0, 0xe5, 0x82, 0x24, 0x04, 0x12, 0x20, -0x25, 0xe0, 0x44, 0x80, 0xf0, 0x12, 0x2f, 0x80, 0x74, 0x74, 0x25, 0x68, 0xf8, 0x74, 0x04, 0x46, -0xf6, 0x7f, 0x00, 0x22, 0x8b, 0x62, 0x8a, 0x63, 0x89, 0x64, 0x12, 0x2a, 0x62, 0x90, 0xfa, 0xbf, -0x12, 0x15, 0x06, 0xaa, 0x63, 0xa9, 0x64, 0x90, 0xfa, 0xc2, 0x12, 0x15, 0x06, 0x90, 0xfa, 0xc3, -0xe4, 0x75, 0xf0, 0x0a, 0x12, 0x14, 0x2f, 0x90, 0xfa, 0xc2, 0x12, 0x14, 0xfd, 0xe9, 0x24, 0x01, -0xf9, 0xe4, 0x3a, 0xfa, 0x90, 0xfa, 0xc5, 0x12, 0x15, 0x06, 0xab, 0x62, 0xaa, 0x63, 0xa9, 0x64, -0x12, 0x2a, 0x6e, 0xe0, 0xff, 0xc3, 0x13, 0xf0, 0xe4, 0x78, 0x88, 0xf6, 0x90, 0xfa, 0xbd, 0xe0, -0xff, 0x78, 0x88, 0xe6, 0xc3, 0x9f, 0x50, 0x4a, 0x90, 0xfa, 0xbf, 0x12, 0x2a, 0x43, 0xff, 0x78, -0x89, 0xf6, 0x90, 0xfa, 0xc2, 0x12, 0x2a, 0x43, 0xfe, 0xf4, 0x5f, 0xff, 0x78, 0x89, 0xf6, 0x12, -0x2a, 0x40, 0x5e, 0x4f, 0xff, 0x78, 0x89, 0xf6, 0x12, 0x2a, 0x49, 0x75, 0xf0, 0x02, 0x12, 0x14, -0x2f, 0x90, 0xfa, 0xc3, 0xe4, 0x75, 0xf0, 0x02, 0x12, 0x14, 0x2f, 0xab, 0x62, 0xaa, 0x63, 0xa9, -0x64, 0x90, 0x00, 0x04, 0x12, 0x13, 0xce, 0x30, 0xe4, 0x03, 0x12, 0x2a, 0x58, 0x78, 0x88, 0x06, -0x80, 0xaa, 0xe4, 0x90, 0xfa, 0xbe, 0xf0, 0x22, 0x8b, 0x5c, 0x8a, 0x5d, 0x89, 0x5e, 0x90, 0xfa, -0xbe, 0x74, 0x06, 0xf0, 0xe4, 0x90, 0xfa, 0xbd, 0xf0, 0x12, 0x13, 0xb5, 0x24, 0x6e, 0x60, 0x26, -0x14, 0x70, 0x70, 0x12, 0x2a, 0x2f, 0x60, 0x09, 0x24, 0x30, 0x70, 0x12, 0x12, 0x22, 0x14, 0x80, -0x62, 0x12, 0x2a, 0x79, 0x12, 0x1d, 0x5e, 0x90, 0xfa, 0xbe, 0xef, 0xf0, 0x80, 0x55, 0x90, 0xfa, -0xbe, 0x74, 0x81, 0xf0, 0x80, 0x4d, 0x12, 0x2a, 0x2f, 0x60, 0x09, 0x24, 0x30, 0x70, 0x3e, 0x12, -0x29, 0x85, 0x80, 0x3f, 0xe5, 0x5e, 0x24, 0x03, 0xf9, 0xe4, 0x35, 0x5d, 0xfa, 0x7b, 0x01, 0xc0, -0x03, 0xc0, 0x02, 0xc0, 0x01, 0x12, 0x2a, 0x79, 0x90, 0x00, 0x05, 0x12, 0x13, 0xce, 0xfd, 0x90, -0x00, 0x08, 0x12, 0x14, 0x5b, 0xf5, 0x41, 0x85, 0xf0, 0x40, 0xd0, 0x01, 0xd0, 0x02, 0xd0, 0x03, -0x12, 0x23, 0xee, 0x90, 0xfa, 0xbd, 0xef, 0xf0, 0xe4, 0xa3, 0xf0, 0x80, 0x06, 0x90, 0xfa, 0xbe, -0x74, 0x81, 0xf0, 0x90, 0xfa, 0xbe, 0xe0, 0x12, 0x2a, 0x79, 0x90, 0x00, 0x02, 0x12, 0x14, 0x0d, -0x90, 0xfa, 0xbd, 0xe0, 0xff, 0x22, 0x12, 0x0f, 0x89, 0x7f, 0x02, 0x12, 0x11, 0x9f, 0x78, 0x6d, -0xe6, 0x44, 0x02, 0xf6, 0xd2, 0xb0, 0xd2, 0xb1, 0xd2, 0xb3, 0x90, 0xff, 0xa4, 0xe0, 0x90, 0xfa, -0x7a, 0xf0, 0x90, 0xff, 0xb4, 0xe0, 0x90, 0xfa, 0x7b, 0xf0, 0x90, 0xff, 0xa2, 0xe0, 0x90, 0xfa, -0x78, 0xf0, 0x90, 0xff, 0xb2, 0xe0, 0x90, 0xfa, 0x79, 0xf0, 0x90, 0xff, 0xa4, 0x74, 0x30, 0xf0, -0x90, 0xff, 0xb4, 0xf0, 0x90, 0xff, 0xa2, 0x74, 0x40, 0xf0, 0x90, 0xff, 0xb2, 0xf0, 0x90, 0xfa, -0xe3, 0xe5, 0xa8, 0xf0, 0x75, 0xa8, 0x81, 0x90, 0xff, 0x92, 0xe0, 0x60, 0x04, 0xe4, 0xf0, 0x80, -0xf6, 0x90, 0xff, 0xfd, 0x74, 0x3a, 0xf0, 0x43, 0x87, 0x01, 0x00, 0x00, 0x00, 0x90, 0xfa, 0x7a, -0xe0, 0x90, 0xff, 0xa4, 0xf0, 0x90, 0xfa, 0x7b, 0xe0, 0x90, 0xff, 0xb4, 0xf0, 0x90, 0xfa, 0x78, -0xe0, 0x90, 0xff, 0xa2, 0xf0, 0x90, 0xfa, 0x79, 0xe0, 0x90, 0xff, 0xb2, 0xf0, 0x90, 0xf9, 0x15, -0xe0, 0x60, 0x02, 0xc2, 0xb3, 0x90, 0xfa, 0xe3, 0xe0, 0xf5, 0xa8, 0x02, 0x10, 0x0c, 0x8b, 0x3c, -0x8a, 0x3d, 0x89, 0x3e, 0x8d, 0x3f, 0xe5, 0x3f, 0x70, 0x03, 0xaf, 0x3f, 0x22, 0x12, 0x2a, 0xa8, -0x70, 0x16, 0x12, 0x2a, 0xc7, 0xe5, 0x40, 0x90, 0xff, 0xf1, 0xf0, 0x12, 0x2e, 0xd4, 0x50, 0xf2, -0x12, 0x24, 0x7b, 0x40, 0x0b, 0x7f, 0x00, 0x22, 0x12, 0x2a, 0xc7, 0x12, 0x24, 0x7b, 0x50, 0xf8, -0x90, 0xff, 0xf3, 0x74, 0xa1, 0xf0, 0xe5, 0x3f, 0xb4, 0x01, 0x07, 0x90, 0xff, 0xf0, 0xe0, 0x44, -0x02, 0xf0, 0x90, 0xff, 0xf1, 0xe4, 0xf0, 0xf5, 0x42, 0xe5, 0x3f, 0x14, 0xff, 0xe5, 0x42, 0xc3, -0x9f, 0x50, 0x2a, 0x12, 0x2e, 0xbd, 0x40, 0x03, 0xaf, 0x42, 0x22, 0xc3, 0xe5, 0x3f, 0x95, 0x42, -0xff, 0xbf, 0x02, 0x07, 0x90, 0xff, 0xf0, 0xe0, 0x44, 0x02, 0xf0, 0x12, 0x2a, 0xba, 0x05, 0x42, -0x74, 0x01, 0x25, 0x3e, 0xf5, 0x3e, 0xe4, 0x35, 0x3d, 0xf5, 0x3d, 0x80, 0xcc, 0x12, 0x2e, 0xbd, -0x40, 0x03, 0x7f, 0x18, 0x22, 0x12, 0x2a, 0xba, 0xaf, 0x3f, 0x22, 0x90, 0xff, 0xf1, 0xe5, 0x41, -0xf0, 0x02, 0x2e, 0xd4, 0x75, 0xa8, 0x40, 0x78, 0x7f, 0xe4, 0xf6, 0xd8, 0xfd, 0x75, 0x81, 0x91, -0x02, 0x24, 0xce, 0x02, 0x2e, 0x88, 0xe4, 0x93, 0xa3, 0xf8, 0xe4, 0x93, 0xa3, 0x40, 0x03, 0xf6, -0x80, 0x01, 0xf2, 0x08, 0xdf, 0xf4, 0x80, 0x29, 0xe4, 0x93, 0xa3, 0xf8, 0x54, 0x07, 0x24, 0x0c, -0xc8, 0xc3, 0x33, 0xc4, 0x54, 0x0f, 0x44, 0x20, 0xc8, 0x83, 0x40, 0x04, 0xf4, 0x56, 0x80, 0x01, -0x46, 0xf6, 0xdf, 0xe4, 0x80, 0x0b, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x90, 0x28, -0xcb, 0xe4, 0x7e, 0x01, 0x93, 0x60, 0xbc, 0xa3, 0xff, 0x54, 0x3f, 0x30, 0xe5, 0x09, 0x54, 0x1f, -0xfe, 0xe4, 0x93, 0xa3, 0x60, 0x01, 0x0e, 0xcf, 0x54, 0xc0, 0x25, 0xe0, 0x60, 0xa8, 0x40, 0xb8, -0xe4, 0x93, 0xa3, 0xfa, 0xe4, 0x93, 0xa3, 0xf8, 0xe4, 0x93, 0xa3, 0xc8, 0xc5, 0x82, 0xc8, 0xca, -0xc5, 0x83, 0xca, 0xf0, 0xa3, 0xc8, 0xc5, 0x82, 0xc8, 0xca, 0xc5, 0x83, 0xca, 0xdf, 0xe9, 0xde, -0xe7, 0x80, 0xbe, 0xe4, 0xf5, 0x36, 0x12, 0x19, 0x21, 0xe0, 0xb4, 0x04, 0x0d, 0xe5, 0x36, 0x24, -0x03, 0xff, 0x12, 0x2d, 0x4f, 0x12, 0x19, 0x21, 0xe4, 0xf0, 0x05, 0x36, 0xe5, 0x36, 0xc3, 0x94, -0x02, 0x40, 0xe3, 0xe4, 0xf5, 0x36, 0x75, 0xf0, 0x02, 0xe5, 0x36, 0x90, 0xfa, 0x90, 0x12, 0x19, -0x62, 0x60, 0x2c, 0x12, 0x29, 0xda, 0xef, 0x60, 0x52, 0x75, 0xf0, 0x02, 0xe5, 0x36, 0x90, 0xfa, -0x90, 0x12, 0x14, 0xdf, 0xe4, 0xf0, 0xa3, 0xf0, 0x75, 0xf0, 0x0a, 0xe5, 0x36, 0x90, 0xfa, 0x9c, -0x12, 0x14, 0xdf, 0xe0, 0xa3, 0x30, 0xe6, 0x33, 0x12, 0x19, 0x21, 0x74, 0x04, 0xf0, 0x22, 0x75, -0xf0, 0x02, 0xe5, 0x36, 0x90, 0xfa, 0x94, 0x12, 0x19, 0x62, 0x60, 0x16, 0x12, 0x29, 0xda, 0xef, -0x60, 0x19, 0x75, 0xf0, 0x02, 0xe5, 0x36, 0x90, 0xfa, 0x94, 0x12, 0x14, 0xdf, 0xe4, 0xf0, 0xa3, -0xf0, 0x22, 0x05, 0x36, 0xe5, 0x36, 0xc3, 0x94, 0x02, 0x40, 0x9b, 0x22, 0xe4, 0xff, 0x90, 0xff, -0x83, 0xe0, 0x54, 0x0f, 0xfe, 0xef, 0xc3, 0x9e, 0x50, 0x17, 0x74, 0xf0, 0x2f, 0xf5, 0x82, 0xe4, -0x34, 0xfe, 0xf5, 0x83, 0xe0, 0x12, 0x18, 0x2a, 0x12, 0x13, 0xfb, 0x0f, 0x12, 0x18, 0x19, 0x80, -0xdd, 0xef, 0xfd, 0xc3, 0xe5, 0x31, 0x9d, 0xf5, 0x31, 0xe5, 0x30, 0x94, 0x00, 0xf5, 0x30, 0xd3, -0xe5, 0x31, 0x94, 0x00, 0xe5, 0x30, 0x94, 0x00, 0x40, 0x06, 0xe4, 0x90, 0xff, 0x83, 0xf0, 0x22, -0x12, 0x19, 0x3e, 0x12, 0x19, 0x92, 0x12, 0x19, 0x8c, 0x12, 0x13, 0xb5, 0x24, 0x6e, 0x60, 0x1e, -0x14, 0x60, 0x1b, 0x24, 0x8e, 0x70, 0x2d, 0x90, 0x00, 0x01, 0x12, 0x13, 0xce, 0xff, 0x24, 0xfc, -0x60, 0x03, 0x04, 0x70, 0x1f, 0xef, 0xfd, 0x7c, 0x00, 0x7f, 0x0d, 0x02, 0x10, 0x9c, 0x12, 0x19, -0x6c, 0x12, 0x22, 0xb8, 0x12, 0x18, 0xe8, 0x12, 0x13, 0xce, 0x60, 0x03, 0x02, 0x2f, 0x76, 0xe4, -0xff, 0x12, 0x2f, 0x6a, 0x22, 0x8b, 0x4b, 0x8a, 0x4c, 0x89, 0x4d, 0x8c, 0x4e, 0x8d, 0x4f, 0xd2, -0x00, 0x12, 0x2a, 0xa8, 0x70, 0x16, 0x12, 0x2a, 0xc7, 0xe5, 0x4e, 0x90, 0xff, 0xf1, 0xf0, 0x12, -0x2e, 0xd4, 0x50, 0xf2, 0x12, 0x26, 0x9a, 0x40, 0x0b, 0x7f, 0x18, 0x22, 0x12, 0x2a, 0xc7, 0x12, -0x26, 0x9a, 0x50, 0xf8, 0xe4, 0xf5, 0x51, 0xe5, 0x50, 0x14, 0xff, 0xe5, 0x51, 0xc3, 0x9f, 0x50, -0x17, 0x12, 0x26, 0x8a, 0x40, 0x03, 0x7f, 0x18, 0x22, 0x05, 0x51, 0x74, 0x01, 0x25, 0x4d, 0xf5, -0x4d, 0xe4, 0x35, 0x4c, 0xf5, 0x4c, 0x80, 0xdf, 0x90, 0xff, 0xf0, 0xe0, 0x44, 0x01, 0xf0, 0x12, -0x26, 0x8a, 0x40, 0x03, 0x7f, 0x18, 0x22, 0x7f, 0x00, 0x22, 0xab, 0x4b, 0xaa, 0x4c, 0xa9, 0x4d, -0x12, 0x13, 0xb5, 0x90, 0xff, 0xf1, 0xf0, 0x02, 0x2e, 0xd4, 0x90, 0xff, 0xf1, 0xe5, 0x4f, 0xf0, -0x02, 0x2e, 0xd4, 0x7b, 0x01, 0x7a, 0xfa, 0x79, 0xcb, 0xe4, 0xfd, 0x12, 0x20, 0xc5, 0x90, 0xfa, -0xcb, 0xe4, 0x75, 0xf0, 0x09, 0x12, 0x14, 0x2f, 0x7b, 0x00, 0x7a, 0x00, 0x79, 0x37, 0x90, 0xfa, -0xcb, 0xe4, 0x75, 0xf0, 0x01, 0x12, 0x14, 0x45, 0x85, 0xf0, 0x41, 0xf5, 0x40, 0x7d, 0x01, 0x12, -0x23, 0xee, 0x90, 0xff, 0xf7, 0xe5, 0x37, 0x12, 0x26, 0xfe, 0x90, 0xff, 0xf6, 0xe5, 0x37, 0xf0, -0x90, 0xfa, 0xcb, 0xe4, 0xf0, 0xa3, 0x74, 0x06, 0x12, 0x26, 0xfe, 0xe5, 0x37, 0x30, 0xe0, 0x07, -0x90, 0xff, 0xfc, 0x74, 0x94, 0xf0, 0x22, 0x90, 0xff, 0xfc, 0x74, 0x90, 0xf0, 0x22, 0xf0, 0x7b, -0x00, 0x7a, 0x00, 0x79, 0x37, 0x90, 0xfa, 0xcb, 0xe4, 0x75, 0xf0, 0x01, 0x12, 0x14, 0x45, 0x85, -0xf0, 0x41, 0xf5, 0x40, 0x7d, 0x01, 0x02, 0x23, 0xee, 0x15, 0x6b, 0xa8, 0x6b, 0xa6, 0x07, 0x30, -0x08, 0x05, 0x12, 0x10, 0xec, 0x80, 0xf8, 0xd2, 0x08, 0xa8, 0x6b, 0xe6, 0xff, 0xb4, 0x03, 0x0f, -0x78, 0x82, 0x76, 0xff, 0x08, 0x76, 0xe0, 0x08, 0x76, 0xff, 0x08, 0x76, 0xa0, 0x80, 0x0d, 0x78, -0x82, 0x76, 0xff, 0x08, 0x76, 0xe2, 0x08, 0x76, 0xff, 0x08, 0x76, 0xb0, 0x78, 0x86, 0x76, 0xfa, -0x08, 0x76, 0x9a, 0xef, 0x24, 0xfd, 0x75, 0xf0, 0x0a, 0xa4, 0xae, 0xf0, 0x12, 0x20, 0xb7, 0x7b, -0x01, 0x7a, 0xff, 0x79, 0x48, 0x78, 0x6e, 0x12, 0x14, 0xf4, 0xa8, 0x6b, 0xe6, 0x24, 0xfd, 0x75, -0xf0, 0x08, 0xa4, 0xff, 0xae, 0xf0, 0x78, 0x70, 0x12, 0x20, 0xb7, 0x79, 0x08, 0x78, 0x71, 0x12, -0x14, 0xf4, 0x78, 0x73, 0xef, 0x12, 0x20, 0xb7, 0x05, 0x6b, 0x22, 0x90, 0xff, 0xf0, 0xe0, 0x54, -0xab, 0xf0, 0xe0, 0x44, 0x20, 0xf0, 0x90, 0xfa, 0xe2, 0x74, 0x02, 0xf0, 0x7b, 0x01, 0x7a, 0xfa, -0x79, 0xcb, 0xe4, 0xf5, 0x40, 0xf5, 0x41, 0x7d, 0x01, 0x12, 0x23, 0xee, 0x7e, 0x00, 0x90, 0xfa, -0xe0, 0xee, 0xf0, 0xa3, 0xef, 0xf0, 0x64, 0x01, 0x70, 0x10, 0x90, 0xfa, 0xcb, 0xe0, 0xb4, 0x52, -0x09, 0x90, 0xf9, 0x65, 0xe0, 0x54, 0xef, 0xf0, 0x80, 0x29, 0x90, 0xfa, 0xe0, 0xe0, 0x70, 0x04, -0xa3, 0xe0, 0x64, 0x01, 0x70, 0x10, 0x90, 0xfa, 0xcb, 0xe0, 0xb4, 0x10, 0x09, 0x90, 0xf9, 0x65, -0xe0, 0x44, 0x10, 0xf0, 0x80, 0x0d, 0x90, 0xfa, 0xe2, 0x74, 0x03, 0xf0, 0x90, 0xf9, 0x65, 0xe0, -0x54, 0xef, 0xf0, 0x90, 0xff, 0xf0, 0xe0, 0x44, 0x20, 0xf0, 0x22, 0x90, 0xff, 0x93, 0x74, 0x2a, -0xf0, 0x90, 0xff, 0xff, 0xe0, 0x60, 0x06, 0x90, 0xff, 0xfc, 0x74, 0x10, 0xf0, 0x90, 0xff, 0x91, -0xe0, 0x44, 0x90, 0xf0, 0x12, 0x27, 0x8b, 0x12, 0x15, 0x35, 0x12, 0x2d, 0xa5, 0x7e, 0x07, 0x7f, -0xd0, 0x12, 0x11, 0x68, 0x7e, 0x0f, 0x7f, 0xa0, 0x12, 0x11, 0x82, 0xe4, 0x78, 0x7d, 0xf6, 0x78, -0x7d, 0xe6, 0xff, 0xc3, 0x94, 0x06, 0x50, 0x0b, 0x74, 0x74, 0x2f, 0xf8, 0xe4, 0xf6, 0x78, 0x7d, -0x06, 0x80, 0xec, 0x7f, 0x03, 0x12, 0x2c, 0x5b, 0x90, 0xf9, 0x65, 0xe0, 0x20, 0xe4, 0x05, 0x7f, -0x04, 0x12, 0x2c, 0x5b, 0x90, 0xff, 0x9b, 0xe4, 0xf0, 0x90, 0xff, 0x9a, 0xf0, 0x90, 0xff, 0xe8, -0xe0, 0x54, 0x1f, 0xf0, 0xd2, 0xa8, 0x22, 0x12, 0x0f, 0x89, 0x78, 0x90, 0xef, 0xf6, 0x12, 0x27, -0x19, 0x12, 0x20, 0x59, 0x30, 0xe0, 0x25, 0x12, 0x20, 0x2d, 0xe0, 0x54, 0x7f, 0xf0, 0x78, 0x71, -0x12, 0x14, 0xeb, 0x90, 0x00, 0x02, 0x12, 0x13, 0xce, 0x30, 0xe7, 0x09, 0x90, 0x00, 0x02, 0xe4, -0x12, 0x14, 0x0d, 0x80, 0xe9, 0x12, 0x20, 0x2d, 0xe0, 0x44, 0x80, 0xf0, 0x12, 0x20, 0x59, 0x30, -0xe1, 0x1e, 0x12, 0x20, 0x1b, 0xe0, 0x54, 0x7f, 0xf0, 0x12, 0x2f, 0x15, 0x78, 0x6e, 0x12, 0x14, -0xeb, 0x90, 0x00, 0x02, 0x74, 0x80, 0x12, 0x14, 0x0d, 0x12, 0x20, 0x1b, 0xe0, 0x44, 0x80, 0xf0, -0x12, 0x2f, 0x80, 0xe4, 0xff, 0x12, 0x2e, 0xa5, 0x02, 0x10, 0x0c, 0x03, 0x6e, 0x01, 0xff, 0x48, -0x03, 0x71, 0x01, 0xff, 0x08, 0x02, 0x6c, 0x00, 0x00, 0x44, 0xfa, 0x94, 0x00, 0x00, 0x00, 0x00, -0x44, 0xfa, 0x90, 0x00, 0x00, 0x00, 0x00, 0x42, 0xfa, 0xae, 0x00, 0x00, 0x42, 0xfa, 0x7a, 0x00, -0x00, 0x42, 0xfa, 0x78, 0x00, 0x00, 0x42, 0xf9, 0x69, 0xff, 0xff, 0x42, 0xfa, 0x76, 0x00, 0x00, -0x43, 0xf9, 0x16, 0x0a, 0x32, 0x02, 0x41, 0xf9, 0x63, 0x20, 0x41, 0xf9, 0x64, 0x20, 0x41, 0xf9, -0x61, 0x00, 0x41, 0xf9, 0x62, 0x00, 0x44, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x41, 0xf9, 0x65, -0x00, 0x41, 0xf9, 0x15, 0x00, 0x01, 0x20, 0x00, 0x41, 0xf8, 0x04, 0x00, 0x00, 0x12, 0x19, 0x82, -0xe5, 0x31, 0x64, 0x09, 0x70, 0x04, 0xe5, 0x30, 0x64, 0x01, 0x60, 0x48, 0xc3, 0xe5, 0x31, 0x94, -0x08, 0xe5, 0x30, 0x94, 0x00, 0x40, 0x11, 0x7f, 0x08, 0xef, 0xe5, 0x31, 0x94, 0x08, 0xf5, 0x31, -0xe5, 0x30, 0x94, 0x00, 0xf5, 0x30, 0x80, 0x05, 0xaf, 0x31, 0x12, 0x19, 0x92, 0xe4, 0xfe, 0xee, -0xc3, 0x9f, 0x50, 0x19, 0x12, 0x18, 0x2a, 0x12, 0x13, 0xb5, 0xfd, 0x74, 0xf8, 0x2e, 0xf5, 0x82, -0xe4, 0x34, 0xfe, 0xf5, 0x83, 0xed, 0xf0, 0x0e, 0x12, 0x18, 0x19, 0x80, 0xe2, 0xef, 0x54, 0x7f, -0x90, 0xff, 0x81, 0xf0, 0x22, 0x8b, 0x5f, 0x8a, 0x60, 0x89, 0x61, 0x12, 0x2a, 0x6e, 0x70, 0x05, -0xa3, 0x74, 0x08, 0xf0, 0x22, 0xab, 0x5f, 0xaa, 0x60, 0xa9, 0x61, 0x12, 0x2a, 0x62, 0x90, 0xfa, -0xc5, 0x12, 0x15, 0x06, 0xe5, 0x61, 0x24, 0x03, 0xf9, 0xe4, 0x35, 0x60, 0xfa, 0x90, 0xfa, 0xbf, -0x12, 0x15, 0x06, 0xe4, 0x90, 0xfa, 0xbe, 0xf0, 0x78, 0x91, 0xf6, 0x90, 0xfa, 0xbd, 0xe0, 0xff, -0x78, 0x91, 0xe6, 0xc3, 0x9f, 0x50, 0x12, 0x12, 0x2a, 0x40, 0xff, 0x12, 0x2a, 0x49, 0x12, 0x2a, -0x5c, 0x78, 0x91, 0x06, 0x12, 0x2a, 0x58, 0x80, 0xe2, 0x22, 0xad, 0x07, 0xac, 0x06, 0x90, 0x2f, -0x06, 0xe4, 0x93, 0xff, 0x78, 0x7a, 0xf6, 0x54, 0x0f, 0x12, 0x19, 0x07, 0xe0, 0x08, 0x76, 0x00, -0x08, 0xf6, 0x18, 0x12, 0x18, 0x42, 0xc3, 0x33, 0xce, 0x33, 0xce, 0xd8, 0xf9, 0xff, 0x78, 0x7b, -0xee, 0xf6, 0x08, 0xef, 0xf6, 0xee, 0x44, 0xf8, 0x18, 0xf6, 0xef, 0x08, 0xf6, 0x90, 0xff, 0x7a, -0xe0, 0x20, 0xe7, 0x03, 0x7f, 0x00, 0x22, 0x78, 0x7b, 0xe6, 0xfe, 0x08, 0xe6, 0xf5, 0x82, 0x8e, -0x83, 0xec, 0xf0, 0xa3, 0xed, 0xf0, 0x90, 0xff, 0x7a, 0x74, 0x02, 0xf0, 0x7f, 0x01, 0x22, 0xab, -0x5c, 0xaa, 0x5d, 0xa9, 0x5e, 0x90, 0x00, 0x03, 0x12, 0x13, 0xce, 0x54, 0xf0, 0x24, 0xa0, 0x22, -0x90, 0xfa, 0xc5, 0x12, 0x14, 0xfd, 0x02, 0x13, 0xb5, 0x90, 0xfa, 0xbf, 0x12, 0x14, 0xfd, 0xef, -0x12, 0x13, 0xfb, 0x90, 0xfa, 0xc6, 0xe4, 0x22, 0x90, 0xfa, 0xc0, 0xe4, 0x75, 0xf0, 0x01, 0x02, -0x14, 0x2f, 0x90, 0x00, 0x08, 0x12, 0x14, 0x5b, 0xaa, 0xf0, 0xf9, 0x7b, 0x01, 0x22, 0x90, 0x00, -0x05, 0x12, 0x13, 0xce, 0x90, 0xfa, 0xbd, 0xf0, 0x22, 0xab, 0x5c, 0xaa, 0x5d, 0xa9, 0x5e, 0x22, -0x90, 0xfa, 0xd9, 0xe0, 0xff, 0x7e, 0x00, 0xc3, 0x90, 0xfa, 0xd3, 0xe0, 0x9f, 0xf0, 0x90, 0xfa, -0xd2, 0xe0, 0x9e, 0xf0, 0x90, 0xfa, 0xd4, 0xee, 0x8f, 0xf0, 0x12, 0x14, 0x2f, 0xef, 0x25, 0x57, -0xf5, 0x57, 0xee, 0x35, 0x56, 0xf5, 0x56, 0x22, 0x90, 0xff, 0xf0, 0xe0, 0x54, 0xfe, 0xf0, 0xe0, -0x54, 0xfd, 0xf0, 0x90, 0xfa, 0xe2, 0xe0, 0x64, 0x03, 0x22, 0x90, 0xff, 0xf2, 0xe0, 0xab, 0x3c, -0xaa, 0x3d, 0xa9, 0x3e, 0x02, 0x13, 0xfb, 0x90, 0xff, 0xf3, 0x74, 0xa0, 0xf0, 0x22, 0x8f, 0x6a, -0xed, 0x70, 0x0f, 0xe5, 0x6a, 0xb4, 0x03, 0x05, 0x7f, 0x01, 0x02, 0x2e, 0xeb, 0x7f, 0x02, 0x02, -0x2e, 0xeb, 0xaf, 0x6a, 0x12, 0x27, 0x19, 0x74, 0x74, 0x25, 0x6a, 0xf8, 0xe6, 0x30, 0xe2, 0x0b, -0xd2, 0x09, 0x12, 0x18, 0x9b, 0xe0, 0x54, 0x7f, 0xf0, 0x80, 0x02, 0xc2, 0x09, 0xe5, 0x6a, 0xb4, -0x03, 0x07, 0x7f, 0x81, 0x12, 0x2e, 0xeb, 0x80, 0x05, 0x7f, 0x82, 0x12, 0x2e, 0xeb, 0x30, 0x09, -0x07, 0x12, 0x18, 0x9b, 0xe0, 0x44, 0x80, 0xf0, 0x12, 0x2f, 0x80, 0x22, 0x12, 0x0f, 0x89, 0x90, -0xff, 0xfd, 0xe0, 0x44, 0x60, 0xf0, 0xd2, 0x01, 0x90, 0xff, 0xfc, 0xe0, 0x44, 0x02, 0xf0, 0x90, -0xff, 0x00, 0xe0, 0x30, 0xe7, 0x13, 0x90, 0xff, 0x83, 0xe0, 0x44, 0x80, 0xf0, 0x43, 0x2c, 0x80, -0x90, 0xff, 0xfc, 0xe0, 0x44, 0x01, 0xf0, 0x80, 0x0d, 0x12, 0x19, 0x3e, 0x53, 0x2c, 0x7f, 0x90, -0xff, 0xfc, 0xe0, 0x54, 0xfe, 0xf0, 0x90, 0xff, 0x81, 0xe0, 0x44, 0x80, 0xf0, 0x12, 0x02, 0x9f, -0x12, 0x19, 0x46, 0x02, 0x10, 0x0c, 0x12, 0x0f, 0x89, 0x78, 0x8a, 0x12, 0x20, 0x9e, 0x30, 0xe1, -0x07, 0x7f, 0x13, 0x12, 0x2e, 0xa5, 0x80, 0x34, 0x90, 0xf9, 0x65, 0xe0, 0x54, 0x03, 0x60, 0x16, -0x78, 0x8a, 0xe6, 0xb4, 0x03, 0x09, 0x90, 0xff, 0xa4, 0xe0, 0x54, 0xdf, 0xf0, 0x80, 0x07, 0x90, -0xff, 0xb4, 0xe0, 0x54, 0xdf, 0xf0, 0xc2, 0xb3, 0x90, 0xf9, 0x15, 0xe0, 0x04, 0xf0, 0x78, 0x8a, -0xe6, 0xff, 0x12, 0x20, 0x59, 0xfd, 0x12, 0x2d, 0x21, 0x12, 0x2e, 0xa5, 0x02, 0x10, 0x0c, 0x12, -0x0f, 0x89, 0x78, 0x8f, 0xef, 0xf6, 0xd2, 0x00, 0x12, 0x27, 0x19, 0x90, 0xf9, 0x66, 0x12, 0x14, -0xfd, 0xe9, 0x24, 0x03, 0xf9, 0xe4, 0x3a, 0xfa, 0xc0, 0x02, 0x78, 0x86, 0xe6, 0xfe, 0x08, 0xe6, -0xaa, 0x06, 0xf8, 0xac, 0x02, 0x7d, 0x01, 0xd0, 0x02, 0x12, 0x20, 0x4b, 0x12, 0x2f, 0x80, 0x78, -0x8f, 0xe6, 0xff, 0x12, 0x19, 0xbb, 0x12, 0x2e, 0xa5, 0x02, 0x10, 0x0c, 0x12, 0x0f, 0x89, 0x78, -0x8b, 0xef, 0xf6, 0x12, 0x2e, 0x4c, 0x12, 0x2e, 0xa5, 0x90, 0xf9, 0x65, 0xe0, 0x54, 0x03, 0x60, -0x16, 0x78, 0x8b, 0xe6, 0xb4, 0x03, 0x09, 0x90, 0xff, 0xa4, 0xe0, 0x44, 0x20, 0xf0, 0x80, 0x07, -0x90, 0xff, 0xb4, 0xe0, 0x44, 0x20, 0xf0, 0x90, 0xf9, 0x15, 0xe0, 0x14, 0xf0, 0xe0, 0x70, 0x02, -0xd2, 0xb3, 0x02, 0x10, 0x0c, 0x8f, 0x69, 0x12, 0x27, 0x19, 0x12, 0x20, 0x2d, 0xe0, 0x54, 0x3f, -0xf0, 0xe5, 0x82, 0x24, 0x04, 0x12, 0x20, 0x25, 0xe0, 0x54, 0x3f, 0xf0, 0x08, 0xe6, 0xfe, 0x08, -0xe6, 0x8e, 0x83, 0x24, 0x0b, 0x12, 0x20, 0x25, 0xe0, 0x54, 0xf8, 0xf0, 0x12, 0x2f, 0x80, 0x74, -0x74, 0x25, 0x69, 0xf8, 0x74, 0xfb, 0x56, 0xf6, 0x7f, 0x00, 0x22, 0x8f, 0x37, 0xc2, 0x08, 0x12, -0x27, 0x19, 0x12, 0x20, 0x38, 0x78, 0x84, 0x12, 0x20, 0x1d, 0xe0, 0x44, 0x01, 0xf0, 0x12, 0x20, -0x70, 0x12, 0x20, 0x21, 0xe0, 0x20, 0xe0, 0xf6, 0xef, 0x24, 0x0b, 0xf5, 0x82, 0xe4, 0x3e, 0xf5, -0x83, 0xe0, 0x54, 0xf8, 0xf0, 0x12, 0x2f, 0x80, 0xaf, 0x37, 0x12, 0x19, 0xbb, 0x22, 0x12, 0x0f, -0x89, 0x12, 0x27, 0x19, 0x12, 0x20, 0x70, 0x24, 0x06, 0x12, 0x20, 0x23, 0xe0, 0xfd, 0x12, 0x20, -0x53, 0x90, 0x00, 0x03, 0x12, 0x20, 0x78, 0x24, 0x05, 0x12, 0x20, 0x25, 0xe0, 0x90, 0x00, 0x04, -0x12, 0x14, 0x0d, 0x12, 0x2f, 0x80, 0x7d, 0x02, 0xe4, 0xff, 0x12, 0x2c, 0xc0, 0x02, 0x10, 0x0c, -0xae, 0x05, 0x12, 0x18, 0xed, 0xef, 0x12, 0x14, 0x0d, 0x0e, 0x0e, 0x0e, 0xee, 0xd3, 0x95, 0x35, -0xe4, 0x95, 0x34, 0x40, 0x02, 0xae, 0x35, 0xee, 0xd3, 0x94, 0x08, 0x74, 0x80, 0x94, 0x81, 0x40, -0x0a, 0x7e, 0x03, 0x90, 0x00, 0x02, 0x74, 0x02, 0x12, 0x14, 0x0d, 0xaf, 0x06, 0x12, 0x2f, 0x6a, -0x22, 0x12, 0x0f, 0x89, 0x78, 0x8c, 0x12, 0x20, 0x9e, 0x30, 0xe2, 0x07, 0x7f, 0x13, 0x12, 0x2e, -0xa5, 0x80, 0x1b, 0x78, 0x8c, 0xe6, 0x24, 0x74, 0xf8, 0xe6, 0x20, 0xe1, 0x07, 0x7f, 0x12, 0x12, -0x2e, 0xa5, 0x80, 0x0a, 0x78, 0x8c, 0xe6, 0xff, 0x12, 0x21, 0x6e, 0x12, 0x2e, 0xa5, 0x02, 0x10, -0x0c, 0xae, 0x07, 0xed, 0x54, 0x03, 0x64, 0x01, 0x60, 0x03, 0x7f, 0x10, 0x22, 0xed, 0x54, 0x7c, -0xc3, 0x94, 0x04, 0x50, 0x03, 0x7f, 0x0b, 0x22, 0x74, 0x74, 0x2e, 0xf8, 0x74, 0x02, 0x46, 0xf6, -0x74, 0x95, 0x2e, 0xf5, 0x82, 0xe4, 0x34, 0xfa, 0xf5, 0x83, 0xed, 0xf0, 0x7f, 0x00, 0x22, 0xbf, -0x03, 0x06, 0x7c, 0xff, 0x7d, 0xe0, 0x80, 0x04, 0x7c, 0xff, 0x7d, 0xe2, 0x8d, 0x82, 0x8c, 0x83, -0xe0, 0x44, 0x80, 0xf0, 0xe5, 0x82, 0x24, 0x04, 0x12, 0x20, 0x25, 0xe0, 0x44, 0x80, 0xf0, 0x74, -0x74, 0x2f, 0xf8, 0x74, 0x04, 0x46, 0xf6, 0x7f, 0x00, 0x22, 0x12, 0x0f, 0x89, 0xe5, 0x31, 0x64, -0x09, 0x70, 0x04, 0xe5, 0x30, 0x64, 0x01, 0x60, 0x16, 0x90, 0xff, 0x83, 0xe0, 0x54, 0x0f, 0xff, -0xc3, 0xe5, 0x31, 0x9f, 0xe5, 0x30, 0x94, 0x00, 0x40, 0x05, 0x12, 0x25, 0x9c, 0x80, 0x03, 0x12, -0x2f, 0x76, 0x02, 0x10, 0x0c, 0x90, 0xff, 0xfc, 0xe0, 0x20, 0xe7, 0x1f, 0xc2, 0xaf, 0x7d, 0xff, -0xac, 0x05, 0x1d, 0xec, 0x60, 0x15, 0x7e, 0x04, 0x7f, 0x00, 0xef, 0x1f, 0xaa, 0x06, 0x70, 0x01, -0x1e, 0x4a, 0x60, 0xec, 0x90, 0xff, 0x92, 0xe4, 0xf0, 0x80, 0xef, 0x22, 0x12, 0x0f, 0x89, 0x78, -0x6c, 0xe6, 0xfe, 0x08, 0xe6, 0xff, 0x30, 0xe0, 0x12, 0x30, 0xe1, 0x0f, 0x90, 0xff, 0xfc, 0xe0, -0x44, 0x20, 0xf0, 0x7f, 0x04, 0x12, 0x11, 0x9f, 0x12, 0x19, 0x55, 0x02, 0x10, 0x0c, 0x8e, 0x65, -0x8f, 0x66, 0xe5, 0x66, 0x15, 0x66, 0xae, 0x65, 0x70, 0x02, 0x15, 0x65, 0xd3, 0x94, 0x00, 0xee, -0x94, 0x00, 0x40, 0x09, 0x7e, 0x07, 0x7f, 0xd0, 0x12, 0x0f, 0x62, 0x80, 0xe5, 0x22, 0x11, 0x1a, -0x2b, 0x1c, 0x23, 0x56, 0x2f, 0x5c, 0x2d, 0xcc, 0x2d, 0x7a, 0x2e, 0x6b, 0x2c, 0x8e, 0x2b, 0x66, -0x2b, 0xec, 0x2c, 0xf1, 0x2e, 0x2d, 0x1b, 0xe6, 0x2b, 0xaf, 0x28, 0x67, 0x0e, 0x12, 0x0f, 0x89, -0x78, 0x8d, 0x12, 0x20, 0x9e, 0x20, 0xe2, 0x07, 0x7f, 0x11, 0x12, 0x2e, 0xa5, 0x80, 0x0a, 0x78, -0x8d, 0xe6, 0xff, 0x12, 0x2c, 0x25, 0x12, 0x2e, 0xa5, 0x02, 0x10, 0x0c, 0x8f, 0x67, 0x12, 0x2c, -0x25, 0xaf, 0x67, 0x12, 0x27, 0x19, 0x12, 0x20, 0x38, 0x12, 0x2f, 0x80, 0x74, 0x74, 0x25, 0x67, -0xf8, 0x74, 0xfd, 0x56, 0xf6, 0xaf, 0x67, 0x12, 0x19, 0xbb, 0x22, 0x12, 0x0f, 0x89, 0xe5, 0x31, -0x64, 0x09, 0x70, 0x04, 0xe5, 0x30, 0x64, 0x01, 0x60, 0x05, 0x12, 0x29, 0x2d, 0x80, 0x06, 0x12, -0x19, 0x7a, 0x12, 0x19, 0x82, 0x02, 0x10, 0x0c, 0x12, 0x27, 0xfb, 0x12, 0x12, 0x3b, 0x90, 0xf8, -0x04, 0xe0, 0xff, 0x60, 0x05, 0x7d, 0x01, 0x12, 0x11, 0xd8, 0x12, 0x26, 0xa3, 0x12, 0x12, 0x77, -0x12, 0x10, 0xfa, 0x80, 0xe3, 0x12, 0x18, 0xed, 0xef, 0x12, 0x14, 0x0d, 0xe4, 0xf5, 0x2a, 0xf5, -0x2b, 0xef, 0x60, 0x03, 0x02, 0x2f, 0x76, 0xe4, 0xff, 0x12, 0x2f, 0x6a, 0x22, 0x90, 0xff, 0xf0, -0xe0, 0xff, 0x54, 0xa0, 0x60, 0xf7, 0xef, 0x30, 0xe5, 0x08, 0x90, 0xff, 0xf0, 0x44, 0x20, 0xf0, -0xc3, 0x22, 0xd3, 0x22, 0x90, 0xff, 0xf0, 0xe0, 0xff, 0x54, 0x28, 0x60, 0xf7, 0xef, 0x30, 0xe5, -0x08, 0x90, 0xff, 0xf0, 0x44, 0x20, 0xf0, 0xc3, 0x22, 0xd3, 0x22, 0xef, 0x30, 0xe7, 0x08, 0x12, -0x18, 0xad, 0xe0, 0x54, 0xdf, 0xf0, 0x22, 0xef, 0x12, 0x18, 0xf7, 0xe0, 0x54, 0xdf, 0xf0, 0x22, -0x81, 0x01, 0x82, 0x02, 0x83, 0x03, 0x87, 0x40, 0x00, 0x40, 0x00, 0x40, 0x00, 0x40, 0x00, 0x40, -0x00, 0x40, 0x00, 0x08, 0x00, 0x78, 0x84, 0x12, 0x20, 0x2f, 0xa3, 0xa3, 0xe0, 0xff, 0x30, 0xe7, -0x06, 0x54, 0x7f, 0xf0, 0x44, 0x80, 0xf0, 0x22, 0x85, 0x34, 0x30, 0x85, 0x35, 0x31, 0x90, 0xff, -0x82, 0xe0, 0x54, 0xf7, 0xf0, 0xa3, 0xe0, 0x54, 0x7f, 0xf0, 0x22, 0xe4, 0xfe, 0xee, 0x90, 0x2f, -0x00, 0x93, 0xb5, 0x07, 0x02, 0xd3, 0x22, 0x0e, 0xbe, 0x07, 0xf2, 0xc3, 0x22, 0x00, 0x08, 0x18, -0x38, 0x28, 0x01, 0x81, 0x10, 0x0a, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x12, 0x0f, 0x89, 0x7f, -0x02, 0x12, 0x10, 0x18, 0x12, 0x19, 0x55, 0x02, 0x10, 0x0c, 0x75, 0x30, 0x00, 0x8f, 0x31, 0x12, -0x18, 0x49, 0x12, 0x29, 0x2d, 0x22, 0x12, 0x19, 0x82, 0x12, 0x19, 0x3e, 0x12, 0x19, 0x7a, 0x22, -0xc2, 0x08, 0x22, +0xd9, 0xf5, 0xeb, 0x70, 0x10, 0xea, 0xf0, 0xc0, 0x07, 0x12, 0x12, 0x41, 0xad, 0x07, 0xaf, 0x02, +0x12, 0x12, 0x58, 0xd0, 0x07, 0xa3, 0xa3, 0xa3, 0xdf, 0xce, 0x12, 0x11, 0x66, 0x80, 0xc1, 0x8f, +0x24, 0x12, 0x2a, 0x06, 0x78, 0x80, 0xe6, 0xfe, 0x08, 0xe6, 0xff, 0x8e, 0x83, 0x24, 0x08, 0x12, +0x21, 0xf3, 0xe0, 0xfd, 0x12, 0x22, 0x8a, 0x8a, 0x83, 0x24, 0x0a, 0x12, 0x21, 0xf3, 0xed, 0xf0, +0x12, 0x22, 0x56, 0x24, 0x07, 0x12, 0x21, 0xf3, 0xe0, 0xff, 0x12, 0x22, 0x99, 0x24, 0x09, 0x12, +0x21, 0xf3, 0xef, 0xf0, 0x90, 0xf9, 0x15, 0xe0, 0x30, 0xe4, 0x20, 0x08, 0x12, 0x22, 0x09, 0xc0, +0x83, 0xc0, 0x82, 0xa3, 0xe0, 0x25, 0xe0, 0xff, 0x05, 0x82, 0xd5, 0x82, 0x02, 0x15, 0x83, 0x15, +0x82, 0xe0, 0x33, 0xd0, 0x82, 0xd0, 0x83, 0xf0, 0xa3, 0xef, 0xf0, 0x78, 0x80, 0x12, 0x22, 0x09, +0xe0, 0xfc, 0xa3, 0xe0, 0xfd, 0xec, 0xff, 0x12, 0x22, 0x8a, 0x8a, 0x83, 0x24, 0x08, 0x12, 0x21, +0xf3, 0xef, 0xf0, 0xed, 0x12, 0x22, 0x99, 0x24, 0x07, 0x12, 0x21, 0xf3, 0xed, 0xf0, 0x12, 0x21, +0xfb, 0xe0, 0xff, 0x30, 0xe7, 0x19, 0x12, 0x22, 0x6e, 0x12, 0x21, 0xf3, 0xe0, 0x60, 0x09, 0x12, +0x21, 0xfb, 0xef, 0x44, 0x02, 0xf0, 0x80, 0x07, 0x12, 0x21, 0xfb, 0xef, 0x54, 0xfd, 0xf0, 0x78, +0x7e, 0x12, 0x22, 0x09, 0xa3, 0xa3, 0xe0, 0xff, 0x53, 0x07, 0xc7, 0x08, 0xe6, 0xfc, 0x08, 0xe6, +0xfd, 0x12, 0x22, 0x43, 0xa3, 0xe0, 0x30, 0xe3, 0x12, 0x8d, 0x82, 0x8c, 0x83, 0xe5, 0x82, 0x24, +0x05, 0x12, 0x21, 0xf3, 0xe0, 0x90, 0x31, 0x94, 0x93, 0x42, 0x07, 0x53, 0x07, 0xfb, 0x78, 0x80, +0xe6, 0xfc, 0x08, 0xe6, 0x8c, 0x83, 0x24, 0x06, 0x12, 0x21, 0xf3, 0xe0, 0x60, 0x03, 0x43, 0x07, +0x04, 0x53, 0x07, 0xfc, 0x78, 0x80, 0x12, 0x22, 0x7a, 0x24, 0x04, 0x12, 0x21, 0xf3, 0xe0, 0x42, +0x07, 0x43, 0x07, 0x80, 0x12, 0x22, 0x8a, 0xf5, 0x82, 0x8a, 0x83, 0xa3, 0xa3, 0xef, 0xf0, 0x12, +0x22, 0x99, 0x24, 0x04, 0x12, 0x21, 0xf3, 0xe0, 0xff, 0x8d, 0x82, 0x8c, 0x83, 0xa3, 0xa3, 0xe0, +0xfc, 0xa3, 0xe0, 0xfd, 0x30, 0xe1, 0x05, 0x53, 0x07, 0xdf, 0x80, 0x03, 0x43, 0x07, 0x20, 0xec, +0x30, 0xe4, 0x05, 0x53, 0x07, 0xef, 0x80, 0x03, 0x43, 0x07, 0x10, 0x12, 0x21, 0xfb, 0xe0, 0xfe, +0x54, 0x03, 0x60, 0x73, 0x53, 0x07, 0xdf, 0xee, 0x30, 0xe1, 0x69, 0x78, 0x80, 0x12, 0x22, 0x6f, +0x12, 0x21, 0xf3, 0xe0, 0x12, 0x1b, 0x4c, 0x14, 0xa6, 0x00, 0x14, 0xda, 0x01, 0x14, 0xdf, 0x03, +0x14, 0xda, 0x05, 0x14, 0xdf, 0x07, 0x14, 0xda, 0x09, 0x14, 0xdf, 0x0b, 0x14, 0xda, 0x0d, 0x14, +0xdf, 0x0f, 0x00, 0x00, 0x14, 0xe7, 0xe5, 0x24, 0x64, 0x03, 0x70, 0x21, 0x90, 0xf9, 0x15, 0xe0, +0x30, 0xe2, 0x0d, 0x30, 0xb4, 0x05, 0x43, 0x07, 0x02, 0x80, 0x2c, 0x53, 0x07, 0xfd, 0x80, 0x27, +0x30, 0x95, 0x05, 0x43, 0x07, 0x02, 0x80, 0x1f, 0x53, 0x07, 0xfd, 0x80, 0x1a, 0x30, 0x93, 0x05, +0x43, 0x07, 0x02, 0x80, 0x12, 0x53, 0x07, 0xfd, 0x80, 0x0d, 0x43, 0x07, 0x02, 0x80, 0x08, 0x53, +0x07, 0xfd, 0x80, 0x03, 0x53, 0x07, 0xfd, 0x12, 0x22, 0x78, 0x24, 0x04, 0x12, 0x21, 0xf3, 0xef, +0xf0, 0x8d, 0x82, 0x8c, 0x83, 0xa3, 0xa3, 0xa3, 0xe0, 0xff, 0x12, 0x21, 0xfb, 0xe0, 0xfe, 0x54, +0x03, 0x70, 0x03, 0x02, 0x15, 0xd7, 0xee, 0x20, 0xe1, 0x03, 0x02, 0x15, 0xd4, 0x12, 0x22, 0x6e, +0x12, 0x21, 0xf3, 0xe0, 0x12, 0x1b, 0x4c, 0x15, 0x36, 0x00, 0x15, 0x6c, 0x01, 0x15, 0x6c, 0x03, +0x15, 0xa0, 0x05, 0x15, 0xa0, 0x07, 0x15, 0x86, 0x09, 0x15, 0x86, 0x0b, 0x15, 0xba, 0x0d, 0x15, +0xba, 0x0f, 0x00, 0x00, 0x15, 0xd7, 0xe5, 0x24, 0x64, 0x03, 0x70, 0x23, 0x90, 0xf9, 0x15, 0xe0, +0x30, 0xe2, 0x0f, 0x30, 0xb1, 0x06, 0x53, 0x07, 0x7f, 0x02, 0x15, 0xd7, 0x43, 0x07, 0x80, 0x02, +0x15, 0xd7, 0x30, 0x94, 0x05, 0x53, 0x07, 0x7f, 0x80, 0x7d, 0x43, 0x07, 0x80, 0x80, 0x78, 0x30, +0x92, 0x05, 0x53, 0x07, 0x7f, 0x80, 0x70, 0x43, 0x07, 0x80, 0x80, 0x6b, 0xe5, 0x24, 0xb4, 0x03, +0x09, 0x90, 0xff, 0x9e, 0xe0, 0x54, 0xef, 0xf0, 0x80, 0x07, 0x90, 0xff, 0x9e, 0xe0, 0x54, 0xdf, +0xf0, 0x53, 0x07, 0x7f, 0x80, 0x51, 0xe5, 0x24, 0xb4, 0x03, 0x09, 0x90, 0xff, 0x9e, 0xe0, 0x44, +0x10, 0xf0, 0x80, 0x07, 0x90, 0xff, 0x9e, 0xe0, 0x44, 0x20, 0xf0, 0x53, 0x07, 0x7f, 0x80, 0x37, +0xe5, 0x24, 0xb4, 0x03, 0x09, 0x90, 0xff, 0x9e, 0xe0, 0x54, 0xef, 0xf0, 0x80, 0x07, 0x90, 0xff, +0x9e, 0xe0, 0x54, 0xdf, 0xf0, 0x43, 0x07, 0x80, 0x80, 0x1d, 0xe5, 0x24, 0xb4, 0x03, 0x09, 0x90, +0xff, 0x9e, 0xe0, 0x44, 0x10, 0xf0, 0x80, 0x07, 0x90, 0xff, 0x9e, 0xe0, 0x44, 0x20, 0xf0, 0x43, +0x07, 0x80, 0x80, 0x03, 0x53, 0x07, 0x7f, 0x78, 0x80, 0x12, 0x22, 0x3f, 0xe0, 0xfc, 0xa3, 0xe0, +0xfd, 0x30, 0xe0, 0x05, 0x43, 0x07, 0x20, 0x80, 0x03, 0x53, 0x07, 0xdf, 0xec, 0x30, 0xe3, 0x05, +0x43, 0x07, 0x40, 0x80, 0x03, 0x53, 0x07, 0xbf, 0xec, 0x30, 0xe0, 0x05, 0x43, 0x07, 0x10, 0x80, +0x03, 0x53, 0x07, 0xef, 0xed, 0x30, 0xe4, 0x05, 0x43, 0x07, 0x08, 0x80, 0x03, 0x53, 0x07, 0xf7, +0xed, 0x30, 0xe5, 0x05, 0x43, 0x07, 0x04, 0x80, 0x03, 0x53, 0x07, 0xfb, 0xed, 0x30, 0xe6, 0x05, +0x43, 0x07, 0x01, 0x80, 0x03, 0x53, 0x07, 0xfe, 0xed, 0x30, 0xe7, 0x05, 0x43, 0x07, 0x02, 0x80, +0x03, 0x53, 0x07, 0xfd, 0x78, 0x7e, 0x12, 0x22, 0x3f, 0xa3, 0xef, 0xf0, 0x12, 0x31, 0xc7, 0x7f, +0x00, 0x22, 0x90, 0xff, 0xfa, 0x74, 0x08, 0xf0, 0xa3, 0x74, 0x16, 0xf0, 0x90, 0xff, 0xf9, 0x74, +0x02, 0xf0, 0x7b, 0x01, 0x7a, 0xfa, 0x79, 0xcc, 0xe4, 0xfd, 0x12, 0x22, 0xa0, 0x90, 0xfa, 0xcc, +0xe4, 0x75, 0xf0, 0x03, 0x12, 0x1a, 0x6c, 0x12, 0x18, 0xe2, 0xe5, 0x23, 0x30, 0xe7, 0x02, 0xd2, +0x02, 0x7b, 0x00, 0x7a, 0x00, 0x79, 0x24, 0x90, 0xfa, 0xcc, 0xe0, 0xf5, 0x2d, 0xa3, 0xe0, 0xf5, +0x2e, 0x7d, 0x01, 0x12, 0x25, 0xd7, 0x90, 0xfa, 0xcc, 0xe4, 0xf0, 0xa3, 0x74, 0x0b, 0xf0, 0x7b, +0x00, 0x7a, 0x00, 0x79, 0x23, 0x75, 0x2d, 0x00, 0xf5, 0x2e, 0x7d, 0x01, 0x12, 0x25, 0xd7, 0xe5, +0x23, 0x24, 0x80, 0x90, 0xff, 0xf8, 0xf0, 0xe5, 0x23, 0x64, 0x07, 0x60, 0x1e, 0xe5, 0x23, 0x64, +0x06, 0x60, 0x18, 0xe5, 0x23, 0x64, 0x14, 0x60, 0x12, 0xe5, 0x23, 0x64, 0x41, 0x60, 0x0c, 0xe5, +0x23, 0x64, 0x1a, 0x70, 0x46, 0xe5, 0x24, 0x64, 0x02, 0x70, 0x40, 0xe5, 0x23, 0xb4, 0x07, 0x16, +0xd2, 0x94, 0xd2, 0x95, 0xd2, 0x92, 0xd2, 0x93, 0x90, 0xf9, 0x15, 0xe0, 0x44, 0x02, 0xf0, 0xa3, +0xe0, 0x44, 0x02, 0xf0, 0x80, 0x1e, 0xe5, 0x23, 0xb4, 0x41, 0x12, 0x90, 0xf9, 0x15, 0xe0, 0x44, +0x06, 0xf0, 0xa3, 0xe0, 0x44, 0x06, 0xf0, 0xd2, 0xb1, 0xd2, 0xb4, 0x80, 0x07, 0x90, 0xf9, 0x15, +0xe0, 0x44, 0x01, 0xf0, 0x90, 0xf9, 0x16, 0xe0, 0x44, 0x01, 0xf0, 0xe5, 0x23, 0x64, 0x42, 0x60, +0x05, 0xe5, 0x23, 0xb4, 0x43, 0x0c, 0x90, 0xf9, 0x15, 0xe0, 0x44, 0x80, 0xf0, 0xa3, 0xe0, 0x44, +0x80, 0xf0, 0x90, 0xfa, 0xcc, 0xe4, 0xf0, 0xa3, 0x74, 0x0d, 0xf0, 0x12, 0x18, 0xe2, 0x90, 0xff, +0xf5, 0xe5, 0x23, 0xf0, 0xe4, 0xf5, 0x35, 0xf5, 0x33, 0xf5, 0x34, 0xf5, 0x32, 0x12, 0x1d, 0x84, +0x12, 0x1c, 0x30, 0x12, 0x1d, 0x8b, 0x90, 0xf9, 0x67, 0x12, 0x1b, 0x43, 0x90, 0xf9, 0x6c, 0x12, +0x1b, 0x43, 0x90, 0xff, 0xff, 0xe4, 0xf0, 0x90, 0xff, 0x83, 0xe0, 0xe4, 0xf0, 0x90, 0xff, 0x81, +0x74, 0x80, 0xf0, 0xa3, 0x74, 0x84, 0xf0, 0x90, 0xff, 0x80, 0xf0, 0xe4, 0xf5, 0x23, 0xe5, 0x23, +0x12, 0x1c, 0xa7, 0xf5, 0x83, 0xe4, 0xf0, 0xe5, 0x23, 0x12, 0x1c, 0xb5, 0xf5, 0x83, 0xe4, 0xf0, +0x05, 0x23, 0xe5, 0x23, 0xb4, 0x07, 0xe7, 0x78, 0x7a, 0x76, 0xfe, 0x08, 0x76, 0xf0, 0x90, 0x31, +0x4d, 0xe4, 0x93, 0xff, 0x78, 0x78, 0xf6, 0xfd, 0xad, 0x07, 0x90, 0x31, 0x5a, 0xe4, 0x93, 0xff, +0x08, 0xf6, 0xff, 0xed, 0x54, 0x0f, 0xfd, 0x12, 0x1c, 0x97, 0x74, 0x84, 0xf0, 0xed, 0x75, 0xf0, +0x08, 0xa4, 0x24, 0x47, 0xf5, 0x82, 0xe4, 0x34, 0xff, 0xf5, 0x83, 0xef, 0xf0, 0xc3, 0x74, 0xf0, +0x9f, 0x78, 0x7b, 0xf6, 0x74, 0xfe, 0x94, 0x00, 0x18, 0x12, 0x1c, 0x28, 0xce, 0xc3, 0x13, 0xce, +0x13, 0xd8, 0xf9, 0xff, 0xed, 0x12, 0x1c, 0xf8, 0xef, 0xf0, 0xed, 0x12, 0x1d, 0x1e, 0xe4, 0xf5, +0x23, 0xe5, 0x23, 0x90, 0x31, 0x47, 0x93, 0xff, 0x78, 0x78, 0xf6, 0xfd, 0xe5, 0x23, 0x25, 0xe0, +0x24, 0x4e, 0xf5, 0x82, 0xe4, 0x34, 0x31, 0xf5, 0x83, 0xe4, 0x93, 0x08, 0xf6, 0xed, 0x30, 0xe7, +0x53, 0x18, 0xe6, 0x54, 0x0f, 0xf9, 0x12, 0x1c, 0x97, 0x12, 0x1d, 0x06, 0x24, 0x47, 0xf5, 0x82, +0xe4, 0x34, 0xff, 0x12, 0x1c, 0x18, 0xce, 0xc3, 0x13, 0xce, 0x13, 0xd8, 0xf9, 0xff, 0xe9, 0x12, +0x1c, 0xf8, 0xef, 0xf0, 0x12, 0x1c, 0x1f, 0xce, 0xc3, 0x13, 0xce, 0x13, 0xd8, 0xf9, 0x12, 0x1d, +0x0b, 0x24, 0x45, 0xf5, 0x82, 0xe4, 0x34, 0xff, 0xf5, 0x83, 0xef, 0xf0, 0xe9, 0x12, 0x1d, 0x1e, +0xe9, 0x75, 0xf0, 0x08, 0xa4, 0x24, 0x46, 0xf5, 0x82, 0xe4, 0x34, 0xff, 0xf5, 0x83, 0x74, 0x80, +0xf0, 0x02, 0x18, 0xb7, 0x78, 0x78, 0xe6, 0x54, 0x0f, 0xf9, 0x12, 0x1c, 0xea, 0x12, 0x1d, 0x06, +0x24, 0x07, 0xf5, 0x82, 0xe4, 0x34, 0xff, 0x12, 0x1c, 0x18, 0xce, 0xc3, 0x13, 0xce, 0x13, 0xd8, +0xf9, 0x12, 0x1d, 0x0b, 0x24, 0x01, 0xf5, 0x82, 0xe4, 0x34, 0xff, 0xf5, 0x83, 0xef, 0xf0, 0x12, +0x1c, 0x1f, 0xce, 0xc3, 0x13, 0xce, 0x13, 0xd8, 0xf9, 0x12, 0x1d, 0x0b, 0x24, 0x05, 0xf5, 0x82, +0xe4, 0x34, 0xff, 0xf5, 0x83, 0xef, 0xf0, 0xe9, 0x75, 0xf0, 0x08, 0xa4, 0x24, 0x02, 0xf5, 0x82, +0xe4, 0x34, 0xff, 0xf5, 0x83, 0xe4, 0xf0, 0xe9, 0x75, 0xf0, 0x08, 0xa4, 0x24, 0x06, 0xf5, 0x82, +0xe4, 0x34, 0xff, 0xf5, 0x83, 0xe4, 0xf0, 0x05, 0x23, 0xe5, 0x23, 0x64, 0x04, 0x60, 0x03, 0x02, +0x17, 0xe1, 0x90, 0x31, 0x4c, 0xe4, 0x93, 0xff, 0x78, 0x78, 0xf6, 0x12, 0x1c, 0xe8, 0xe4, 0xf0, +0x90, 0x31, 0x4b, 0x93, 0xff, 0xf6, 0x12, 0x1c, 0x95, 0xe4, 0xf0, 0x90, 0xff, 0xfd, 0x74, 0x05, +0xf0, 0x22, 0x7b, 0x00, 0x7a, 0x00, 0x79, 0x23, 0x90, 0xfa, 0xcc, 0xe4, 0x75, 0xf0, 0x01, 0x12, +0x1a, 0x82, 0x85, 0xf0, 0x2e, 0xf5, 0x2d, 0x7d, 0x01, 0x02, 0x25, 0xd7, 0xe7, 0x09, 0xf6, 0x08, +0xdf, 0xfa, 0x80, 0x46, 0xe7, 0x09, 0xf2, 0x08, 0xdf, 0xfa, 0x80, 0x3e, 0x88, 0x82, 0x8c, 0x83, +0xe7, 0x09, 0xf0, 0xa3, 0xdf, 0xfa, 0x80, 0x32, 0xe3, 0x09, 0xf6, 0x08, 0xdf, 0xfa, 0x80, 0x78, +0xe3, 0x09, 0xf2, 0x08, 0xdf, 0xfa, 0x80, 0x70, 0x88, 0x82, 0x8c, 0x83, 0xe3, 0x09, 0xf0, 0xa3, +0xdf, 0xfa, 0x80, 0x64, 0x89, 0x82, 0x8a, 0x83, 0xe0, 0xa3, 0xf6, 0x08, 0xdf, 0xfa, 0x80, 0x58, +0x89, 0x82, 0x8a, 0x83, 0xe0, 0xa3, 0xf2, 0x08, 0xdf, 0xfa, 0x80, 0x4c, 0x80, 0xd2, 0x80, 0xfa, +0x80, 0xc6, 0x80, 0xd4, 0x80, 0x69, 0x80, 0xf2, 0x80, 0x33, 0x80, 0x10, 0x80, 0xa6, 0x80, 0xea, +0x80, 0x9a, 0x80, 0xa8, 0x80, 0xda, 0x80, 0xe2, 0x80, 0xca, 0x80, 0x33, 0x89, 0x82, 0x8a, 0x83, +0xec, 0xfa, 0xe4, 0x93, 0xa3, 0xc8, 0xc5, 0x82, 0xc8, 0xcc, 0xc5, 0x83, 0xcc, 0xf0, 0xa3, 0xc8, +0xc5, 0x82, 0xc8, 0xcc, 0xc5, 0x83, 0xcc, 0xdf, 0xe9, 0xde, 0xe7, 0x80, 0x0d, 0x89, 0x82, 0x8a, +0x83, 0xe4, 0x93, 0xa3, 0xf6, 0x08, 0xdf, 0xf9, 0xec, 0xfa, 0xa9, 0xf0, 0xed, 0xfb, 0x22, 0x89, +0x82, 0x8a, 0x83, 0xec, 0xfa, 0xe0, 0xa3, 0xc8, 0xc5, 0x82, 0xc8, 0xcc, 0xc5, 0x83, 0xcc, 0xf0, +0xa3, 0xc8, 0xc5, 0x82, 0xc8, 0xcc, 0xc5, 0x83, 0xcc, 0xdf, 0xea, 0xde, 0xe8, 0x80, 0xdb, 0x89, +0x82, 0x8a, 0x83, 0xe4, 0x93, 0xa3, 0xf2, 0x08, 0xdf, 0xf9, 0x80, 0xcc, 0x88, 0xf0, 0xef, 0x60, +0x01, 0x0e, 0x4e, 0x60, 0xc3, 0x88, 0xf0, 0xed, 0x24, 0x02, 0xb4, 0x04, 0x00, 0x50, 0xb9, 0xf5, +0x82, 0xeb, 0x24, 0x02, 0xb4, 0x04, 0x00, 0x50, 0xaf, 0x23, 0x23, 0x45, 0x82, 0x23, 0x90, 0x19, +0x4c, 0x73, 0xbb, 0x01, 0x06, 0x89, 0x82, 0x8a, 0x83, 0xe0, 0x22, 0x50, 0x02, 0xe7, 0x22, 0xbb, +0xfe, 0x02, 0xe3, 0x22, 0x89, 0x82, 0x8a, 0x83, 0xe4, 0x93, 0x22, 0xbb, 0x01, 0x0c, 0xe5, 0x82, +0x29, 0xf5, 0x82, 0xe5, 0x83, 0x3a, 0xf5, 0x83, 0xe0, 0x22, 0x50, 0x06, 0xe9, 0x25, 0x82, 0xf8, +0xe6, 0x22, 0xbb, 0xfe, 0x06, 0xe9, 0x25, 0x82, 0xf8, 0xe2, 0x22, 0xe5, 0x82, 0x29, 0xf5, 0x82, +0xe5, 0x83, 0x3a, 0xf5, 0x83, 0xe4, 0x93, 0x22, 0xbb, 0x01, 0x06, 0x89, 0x82, 0x8a, 0x83, 0xf0, +0x22, 0x50, 0x02, 0xf7, 0x22, 0xbb, 0xfe, 0x01, 0xf3, 0x22, 0xf8, 0xbb, 0x01, 0x0d, 0xe5, 0x82, +0x29, 0xf5, 0x82, 0xe5, 0x83, 0x3a, 0xf5, 0x83, 0xe8, 0xf0, 0x22, 0x50, 0x06, 0xe9, 0x25, 0x82, +0xc8, 0xf6, 0x22, 0xbb, 0xfe, 0x05, 0xe9, 0x25, 0x82, 0xc8, 0xf2, 0x22, 0xc5, 0xf0, 0xf8, 0xa3, +0xe0, 0x28, 0xf0, 0xc5, 0xf0, 0xf8, 0xe5, 0x82, 0x15, 0x82, 0x70, 0x02, 0x15, 0x83, 0xe0, 0x38, +0xf0, 0x22, 0xa3, 0xf8, 0xe0, 0xc5, 0xf0, 0x25, 0xf0, 0xf0, 0xe5, 0x82, 0x15, 0x82, 0x70, 0x02, +0x15, 0x83, 0xe0, 0xc8, 0x38, 0xf0, 0xe8, 0x22, 0xbb, 0x01, 0x10, 0xe5, 0x82, 0x29, 0xf5, 0x82, +0xe5, 0x83, 0x3a, 0xf5, 0x83, 0xe0, 0xf5, 0xf0, 0xa3, 0xe0, 0x22, 0x50, 0x09, 0xe9, 0x25, 0x82, +0xf8, 0x86, 0xf0, 0x08, 0xe6, 0x22, 0xbb, 0xfe, 0x0a, 0xe9, 0x25, 0x82, 0xf8, 0xe2, 0xf5, 0xf0, +0x08, 0xe2, 0x22, 0xe5, 0x83, 0x2a, 0xf5, 0x83, 0xe9, 0x93, 0xf5, 0xf0, 0xa3, 0xe9, 0x93, 0x22, +0xbb, 0x01, 0x0a, 0x89, 0x82, 0x8a, 0x83, 0xf0, 0xe5, 0xf0, 0xa3, 0xf0, 0x22, 0x50, 0x06, 0xf7, +0x09, 0xa7, 0xf0, 0x19, 0x22, 0xbb, 0xfe, 0x06, 0xf3, 0xe5, 0xf0, 0x09, 0xf3, 0x19, 0x22, 0xf8, +0xbb, 0x01, 0x11, 0xe5, 0x82, 0x29, 0xf5, 0x82, 0xe5, 0x83, 0x3a, 0xf5, 0x83, 0xe8, 0xf0, 0xe5, +0xf0, 0xa3, 0xf0, 0x22, 0x50, 0x09, 0xe9, 0x25, 0x82, 0xc8, 0xf6, 0x08, 0xa6, 0xf0, 0x22, 0xbb, +0xfe, 0x09, 0xe9, 0x25, 0x82, 0xc8, 0xf2, 0xe5, 0xf0, 0x08, 0xf2, 0x22, 0xa4, 0x25, 0x82, 0xf5, +0x82, 0xe5, 0xf0, 0x35, 0x83, 0xf5, 0x83, 0x22, 0xe6, 0xfb, 0x08, 0xe6, 0xfa, 0x08, 0xe6, 0xf9, +0x22, 0xeb, 0xf6, 0x08, 0xea, 0xf6, 0x08, 0xe9, 0xf6, 0x22, 0xe0, 0xfb, 0xa3, 0xe0, 0xfa, 0xa3, +0xe0, 0xf9, 0x22, 0xeb, 0xf0, 0xa3, 0xea, 0xf0, 0xa3, 0xe9, 0xf0, 0x22, 0xd0, 0x83, 0xd0, 0x82, +0xf8, 0xe4, 0x93, 0x70, 0x12, 0x74, 0x01, 0x93, 0x70, 0x0d, 0xa3, 0xa3, 0x93, 0xf8, 0x74, 0x01, +0x93, 0xf5, 0x82, 0x88, 0x83, 0xe4, 0x73, 0x74, 0x02, 0x93, 0x68, 0x60, 0xef, 0xa3, 0xa3, 0xa3, +0x80, 0xdf, 0xab, 0x36, 0xaa, 0x37, 0xa9, 0x38, 0xe5, 0x4c, 0x12, 0x1a, 0x38, 0x74, 0x01, 0x25, +0x38, 0xf5, 0x38, 0xe4, 0x35, 0x37, 0xf5, 0x37, 0xab, 0x36, 0xfa, 0xa9, 0x38, 0x74, 0x11, 0x12, +0x1a, 0x38, 0x74, 0x01, 0x25, 0x38, 0xf5, 0x38, 0xe4, 0x35, 0x37, 0xf5, 0x37, 0x90, 0xff, 0x06, +0xe0, 0xab, 0x36, 0xaa, 0x37, 0xa9, 0x38, 0x12, 0x1a, 0x38, 0x74, 0x01, 0x25, 0x38, 0xf5, 0x38, +0xe4, 0x35, 0x37, 0xf5, 0x37, 0xab, 0x36, 0xfa, 0xa9, 0x38, 0xe4, 0x12, 0x1a, 0x38, 0x04, 0x25, +0x38, 0xf5, 0x38, 0xe4, 0x35, 0x37, 0xf5, 0x37, 0xab, 0x36, 0xfa, 0xa9, 0x38, 0xe4, 0x12, 0x1a, +0x38, 0x04, 0x25, 0x38, 0xf5, 0x38, 0xe4, 0x35, 0x37, 0xf5, 0x37, 0x90, 0xff, 0x04, 0xe0, 0xab, +0x36, 0xaa, 0x37, 0xa9, 0x38, 0x12, 0x1a, 0x38, 0x74, 0x01, 0x25, 0x38, 0xf5, 0x38, 0xe4, 0x35, +0x37, 0xf5, 0x37, 0x90, 0xff, 0x05, 0xe0, 0xab, 0x36, 0xaa, 0x37, 0xa9, 0x38, 0x12, 0x1a, 0x38, +0x74, 0x01, 0x25, 0x38, 0xf5, 0x38, 0xe4, 0x35, 0x37, 0xf5, 0x37, 0x22, 0xf5, 0x83, 0xe0, 0x54, +0x08, 0xab, 0x36, 0xaa, 0x37, 0xa9, 0x38, 0x22, 0xf5, 0x83, 0xef, 0xf0, 0xfd, 0x7c, 0x00, 0xc3, +0x78, 0x7b, 0xe6, 0x9d, 0xf6, 0x18, 0xe6, 0x9c, 0xf6, 0xe6, 0xfe, 0x08, 0xe6, 0x78, 0x03, 0x22, +0x75, 0x36, 0x01, 0x75, 0x37, 0xf9, 0x75, 0x38, 0x6f, 0x22, 0xe0, 0x44, 0x04, 0xf0, 0x74, 0x12, +0x2f, 0xf5, 0x82, 0xe4, 0x34, 0xf9, 0xf5, 0x83, 0xe0, 0x22, 0x90, 0xfa, 0xb9, 0xe0, 0xff, 0x7e, +0x00, 0xc3, 0x90, 0xfa, 0xbd, 0xe0, 0x9f, 0xf0, 0x90, 0xfa, 0xbc, 0xe0, 0x9e, 0xf0, 0x90, 0xfa, +0xb4, 0xee, 0x8f, 0xf0, 0x12, 0x1a, 0x6c, 0xef, 0x25, 0x4f, 0xf5, 0x4f, 0xee, 0x35, 0x4e, 0xf5, +0x4e, 0x22, 0x7b, 0x01, 0x7a, 0xfa, 0x79, 0xb1, 0x90, 0xfa, 0xb4, 0xe0, 0xf5, 0x2d, 0xa3, 0xe0, +0xf5, 0x2e, 0x22, 0x78, 0x7c, 0xe6, 0xfe, 0x08, 0xe6, 0x8e, 0x83, 0x24, 0x04, 0xf5, 0x82, 0xe4, +0x35, 0x83, 0xf5, 0x83, 0x22, 0x54, 0x0f, 0x75, 0xf0, 0x08, 0xa4, 0x24, 0x40, 0xf5, 0x82, 0xe4, +0x34, 0xff, 0xf5, 0x83, 0x22, 0xe5, 0x4d, 0x75, 0xf0, 0x08, 0xa4, 0x24, 0x48, 0xf5, 0x82, 0xe4, +0x34, 0xff, 0x22, 0xe5, 0x4d, 0x75, 0xf0, 0x08, 0xa4, 0x24, 0x08, 0xf5, 0x82, 0xe4, 0x34, 0xff, +0x22, 0x90, 0xfa, 0xb6, 0xe0, 0xff, 0x24, 0xfc, 0x22, 0x90, 0xff, 0x00, 0xe0, 0x54, 0x1f, 0x22, +0x90, 0xfa, 0xbb, 0xe0, 0x90, 0xfa, 0xb7, 0xf0, 0x22, 0x75, 0x33, 0x00, 0x8f, 0x34, 0x90, 0xf9, +0x6c, 0x12, 0x1b, 0x3a, 0x90, 0x00, 0x02, 0x22, 0x54, 0x0f, 0x75, 0xf0, 0x08, 0xa4, 0x24, 0x00, +0xf5, 0x82, 0xe4, 0x34, 0xff, 0xf5, 0x83, 0x22, 0x75, 0xf0, 0x08, 0xa4, 0x24, 0x41, 0xf5, 0x82, +0xe4, 0x34, 0xff, 0xf5, 0x83, 0x22, 0x74, 0x80, 0xf0, 0x08, 0xe6, 0xff, 0xe9, 0x75, 0xf0, 0x08, +0xa4, 0x22, 0x74, 0xaf, 0x25, 0x22, 0xf5, 0x82, 0xe4, 0x34, 0xfa, 0xf5, 0x83, 0x22, 0x75, 0xf0, +0x08, 0xa4, 0x24, 0x42, 0xf5, 0x82, 0xe4, 0x34, 0xff, 0xf5, 0x83, 0x74, 0x80, 0xf0, 0x22, 0x90, +0xff, 0x82, 0xe0, 0x44, 0x08, 0xf0, 0x22, 0x90, 0xff, 0xfe, 0xe0, 0x44, 0x03, 0xf0, 0x90, 0xff, +0xfc, 0xe0, 0x54, 0xfd, 0xf0, 0x22, 0x78, 0x67, 0xe6, 0x54, 0xfd, 0xf6, 0x90, 0xff, 0xfd, 0x74, +0x65, 0xf0, 0x22, 0x12, 0x1b, 0x1c, 0xe0, 0xfe, 0xa3, 0xe0, 0xff, 0x4e, 0x22, 0x7b, 0x01, 0x7a, +0xfa, 0x79, 0xb4, 0x22, 0x90, 0xff, 0x80, 0xe0, 0x44, 0x08, 0xf0, 0x22, 0x90, 0xff, 0x83, 0xe0, +0x54, 0x7f, 0xf0, 0x22, 0xe0, 0xff, 0x90, 0xf9, 0x67, 0x02, 0x1b, 0x3a, 0x90, 0xff, 0xa4, 0xe0, +0x44, 0x02, 0xf0, 0x22, 0x75, 0x39, 0x01, 0x75, 0x3a, 0x09, 0x22, 0x7b, 0x01, 0x7a, 0xf9, 0x79, +0x6f, 0x22, 0xd3, 0xe5, 0x3c, 0x94, 0x08, 0xe5, 0x3b, 0x94, 0x01, 0x22, 0x90, 0xfa, 0xbb, 0xe0, +0xff, 0x90, 0xfa, 0xb7, 0xf0, 0x22, 0x90, 0xff, 0xa4, 0xe0, 0x54, 0xef, 0x22, 0x90, 0xff, 0xb4, +0xe0, 0x54, 0xef, 0x22, 0x12, 0x10, 0x03, 0x78, 0x88, 0xef, 0xf6, 0x12, 0x2a, 0x06, 0x12, 0x22, +0x4a, 0x8e, 0x83, 0x24, 0x09, 0x12, 0x21, 0xf3, 0xe0, 0xfd, 0x12, 0x22, 0x2d, 0x90, 0x00, 0x0a, +0x12, 0x22, 0x52, 0x24, 0x0a, 0x12, 0x21, 0xf3, 0xe0, 0x90, 0x00, 0x0b, 0x12, 0x1a, 0x4a, 0x12, +0x22, 0x4a, 0xf5, 0x82, 0x8e, 0x83, 0xa3, 0xa3, 0xa3, 0xe0, 0xf5, 0x53, 0x12, 0x22, 0x56, 0x24, +0x04, 0x12, 0x21, 0xf3, 0xe0, 0xf5, 0x54, 0x8f, 0x82, 0x8e, 0x83, 0xa3, 0xa3, 0xe0, 0xf5, 0x55, +0xe5, 0x53, 0xc4, 0x13, 0x13, 0x13, 0x54, 0x01, 0x78, 0x88, 0xf6, 0xd3, 0x94, 0x00, 0x40, 0x06, +0xe5, 0x54, 0x30, 0xe1, 0x01, 0x06, 0x78, 0x88, 0xe6, 0x12, 0x22, 0x2c, 0x90, 0x00, 0x0c, 0xef, +0x12, 0x1a, 0x4a, 0x78, 0x80, 0x12, 0x22, 0x09, 0xa3, 0xa3, 0xe0, 0xfe, 0xa3, 0xe0, 0xff, 0x53, +0x07, 0x0c, 0x53, 0x06, 0xe6, 0xe5, 0x53, 0x30, 0xe5, 0x03, 0x43, 0x07, 0x01, 0xe5, 0x54, 0x20, +0xe5, 0x0e, 0xe5, 0x53, 0x54, 0x7f, 0x70, 0x08, 0xe5, 0x53, 0x20, 0xe7, 0x03, 0x43, 0x07, 0x02, +0xe5, 0x53, 0x30, 0xe3, 0x03, 0x43, 0x07, 0x10, 0xe5, 0x53, 0x30, 0xe2, 0x03, 0x43, 0x07, 0x20, +0xe5, 0x53, 0x54, 0x03, 0x60, 0x03, 0x43, 0x07, 0x40, 0xe5, 0x53, 0x30, 0xe1, 0x03, 0x43, 0x07, +0x80, 0xe5, 0x53, 0x30, 0xe4, 0x03, 0x43, 0x06, 0x01, 0xe5, 0x53, 0x30, 0xe6, 0x03, 0x43, 0x06, +0x08, 0xe5, 0x54, 0x20, 0xe4, 0x0e, 0xe5, 0x53, 0x54, 0x7f, 0x70, 0x08, 0xe5, 0x53, 0x20, 0xe7, +0x03, 0x43, 0x06, 0x10, 0x53, 0x07, 0xfb, 0x53, 0x06, 0x79, 0x90, 0x00, 0x05, 0xee, 0x8f, 0xf0, +0x12, 0x1a, 0xef, 0xe5, 0x55, 0x30, 0xe3, 0x12, 0x54, 0x30, 0xff, 0xc4, 0x54, 0x0f, 0x12, 0x22, +0x2c, 0x90, 0x00, 0x08, 0xef, 0x12, 0x1a, 0x4a, 0x80, 0x0a, 0x12, 0x22, 0x2d, 0x90, 0x00, 0x08, +0xe4, 0x12, 0x1a, 0x4a, 0xe5, 0x55, 0x54, 0x03, 0x12, 0x22, 0x2c, 0x90, 0x00, 0x07, 0xef, 0x12, +0x1a, 0x4a, 0xe5, 0x55, 0x54, 0x04, 0xff, 0xc3, 0x13, 0x90, 0x00, 0x09, 0x12, 0x1a, 0x4a, 0x90, +0x00, 0x07, 0x12, 0x1a, 0x0b, 0x70, 0x13, 0x12, 0x22, 0x2d, 0xe9, 0x24, 0x09, 0xf9, 0xe4, 0x3a, +0xfa, 0x12, 0x19, 0xf2, 0xff, 0xc3, 0x13, 0x12, 0x1a, 0x38, 0x12, 0x22, 0x78, 0x24, 0x08, 0x12, +0x21, 0xf3, 0xe0, 0xfe, 0x8d, 0x82, 0x8c, 0x83, 0xe5, 0x82, 0x24, 0x07, 0x12, 0x21, 0xf3, 0xe0, +0xfd, 0xee, 0xed, 0x12, 0x22, 0x2c, 0x90, 0x00, 0x03, 0xee, 0x8f, 0xf0, 0x12, 0x1a, 0xef, 0x12, +0x31, 0xc7, 0x7d, 0x0a, 0xe4, 0xff, 0x12, 0x2f, 0x18, 0x02, 0x10, 0x86, 0x90, 0xfa, 0xe3, 0xe0, +0xb4, 0x03, 0x06, 0x7e, 0x00, 0x7f, 0x40, 0x80, 0x04, 0x7e, 0x00, 0x7f, 0x08, 0x90, 0xfa, 0xd7, +0xee, 0xf0, 0xa3, 0xef, 0xf0, 0x90, 0x00, 0x05, 0x12, 0x1a, 0x0b, 0xff, 0x7e, 0x00, 0x90, 0xfa, +0xd3, 0xee, 0xf0, 0xa3, 0xef, 0xf0, 0x70, 0x03, 0x7f, 0x08, 0x22, 0x90, 0x00, 0x08, 0x12, 0x1a, +0x98, 0xff, 0x90, 0xfa, 0xd5, 0xe5, 0xf0, 0xf0, 0xa3, 0xef, 0xf0, 0xae, 0x02, 0xaf, 0x01, 0x8e, +0x50, 0x8f, 0x51, 0x74, 0x0a, 0x25, 0x51, 0xf5, 0x51, 0xe4, 0x35, 0x50, 0xf5, 0x50, 0x90, 0xfa, +0xd8, 0xe0, 0xff, 0x14, 0xfe, 0x90, 0xfa, 0xd6, 0xe0, 0x5e, 0xfe, 0xc3, 0xef, 0x9e, 0xff, 0x90, +0xfa, 0xda, 0xf0, 0xc3, 0x90, 0xfa, 0xd4, 0xe0, 0x9f, 0x90, 0xfa, 0xd3, 0xe0, 0x94, 0x00, 0x50, +0x06, 0xa3, 0xe0, 0x90, 0xfa, 0xda, 0xf0, 0x12, 0x1f, 0xfb, 0x60, 0x03, 0xe0, 0xff, 0x22, 0x12, +0x2d, 0x5a, 0x90, 0xfa, 0xd3, 0xe0, 0xfe, 0xa3, 0xe0, 0xff, 0x4e, 0x60, 0x2b, 0x90, 0xfa, 0xd7, +0xe0, 0xfc, 0xa3, 0xe0, 0xfd, 0xd3, 0xef, 0x9d, 0xee, 0x9c, 0x40, 0x07, 0xe0, 0x90, 0xfa, 0xda, +0xf0, 0x80, 0x08, 0x90, 0xfa, 0xd4, 0xe0, 0x90, 0xfa, 0xda, 0xf0, 0x12, 0x1f, 0xfb, 0x60, 0x03, +0xe0, 0xff, 0x22, 0x12, 0x2d, 0x5a, 0x80, 0xca, 0x7b, 0x00, 0x7a, 0x00, 0x79, 0x52, 0xe4, 0xf5, +0x2d, 0xf5, 0x2e, 0x7d, 0x01, 0x12, 0x25, 0xd7, 0x7f, 0x00, 0x22, 0xaa, 0x50, 0xa9, 0x51, 0x7b, +0x01, 0x90, 0xfa, 0xd5, 0xe0, 0xfc, 0xa3, 0xe0, 0xfd, 0x90, 0xfa, 0xda, 0xe0, 0xf5, 0x4a, 0x12, +0x28, 0x9f, 0x90, 0xfa, 0xd9, 0xef, 0xf0, 0x22, 0xef, 0x24, 0xae, 0x60, 0x52, 0x24, 0xfe, 0x60, +0x2e, 0x24, 0xfe, 0x70, 0x03, 0x02, 0x20, 0xbb, 0x24, 0x06, 0x60, 0x03, 0x02, 0x21, 0x03, 0x78, +0x71, 0xe6, 0x54, 0xfb, 0xf6, 0x90, 0xff, 0xa5, 0xe0, 0xf5, 0x22, 0x44, 0x0f, 0xf0, 0x74, 0x33, +0x90, 0xfa, 0x91, 0xf0, 0xe5, 0x22, 0xa3, 0xf0, 0x90, 0xfa, 0xaf, 0x74, 0x01, 0xf0, 0x22, 0x78, +0x72, 0xe6, 0x54, 0xfb, 0xf6, 0x90, 0xff, 0xb5, 0xe0, 0xf5, 0x22, 0x44, 0x0f, 0xf0, 0x74, 0x43, +0x90, 0xfa, 0x93, 0xf0, 0xe5, 0x22, 0xa3, 0xf0, 0x90, 0xfa, 0xb0, 0x74, 0x01, 0xf0, 0x22, 0x90, +0xfa, 0x9d, 0xe0, 0xa3, 0x20, 0xe5, 0x03, 0x02, 0x21, 0x03, 0x90, 0xff, 0xa6, 0xe0, 0x90, 0xfa, +0xca, 0xf0, 0xa3, 0xf0, 0x90, 0xfa, 0xca, 0xe0, 0xff, 0x54, 0x0f, 0xfe, 0x60, 0x10, 0x90, 0xff, +0xa6, 0x12, 0x22, 0x5d, 0x90, 0xff, 0xa6, 0xe0, 0x90, 0xfa, 0xca, 0xf0, 0x80, 0xe6, 0x90, 0xfa, +0xcb, 0xe0, 0xff, 0x74, 0x34, 0xfe, 0x12, 0x2c, 0xb4, 0xef, 0x70, 0x57, 0x90, 0xfa, 0xcb, 0xe0, +0xff, 0x74, 0x34, 0x90, 0xfa, 0x95, 0xf0, 0xef, 0xa3, 0xf0, 0x22, 0x90, 0xfa, 0xa7, 0xe0, 0xa3, +0x30, 0xe5, 0x40, 0x90, 0xff, 0xb6, 0xe0, 0x90, 0xfa, 0xca, 0xf0, 0xa3, 0xf0, 0x90, 0xfa, 0xca, +0xe0, 0xff, 0x54, 0x0f, 0xfe, 0x60, 0x10, 0x90, 0xff, 0xb6, 0x12, 0x22, 0x5d, 0x90, 0xff, 0xb6, +0xe0, 0x90, 0xfa, 0xca, 0xf0, 0x80, 0xe6, 0x90, 0xfa, 0xcb, 0xe0, 0xff, 0x74, 0x44, 0xfe, 0x12, +0x2c, 0xb4, 0xef, 0x70, 0x0e, 0x90, 0xfa, 0xcb, 0xe0, 0xff, 0x74, 0x44, 0x90, 0xfa, 0x97, 0xf0, +0xef, 0xa3, 0xf0, 0x22, 0xc0, 0xe0, 0xc0, 0xf0, 0xc0, 0x83, 0xc0, 0x82, 0xc0, 0xd0, 0x75, 0xd0, +0x00, 0xc0, 0x00, 0xc0, 0x01, 0xc0, 0x02, 0xc0, 0x03, 0xc0, 0x04, 0xc0, 0x05, 0xc0, 0x06, 0xc0, +0x07, 0x90, 0xff, 0x92, 0xe0, 0xff, 0x90, 0xfa, 0xc9, 0xf0, 0x90, 0xff, 0x92, 0xe4, 0xf0, 0xef, +0x12, 0x1b, 0x4c, 0x21, 0xbb, 0x26, 0x21, 0xbb, 0x2e, 0x21, 0x5e, 0x30, 0x21, 0x5e, 0x32, 0x21, +0x6c, 0x38, 0x21, 0x7e, 0x3a, 0x21, 0xb0, 0x3e, 0x21, 0x9b, 0x44, 0x21, 0x90, 0x46, 0x21, 0xa6, +0x50, 0x21, 0xa6, 0x52, 0x21, 0xa6, 0x54, 0x21, 0xa6, 0x56, 0x00, 0x00, 0x21, 0xc0, 0x90, 0xfa, +0xc9, 0xe0, 0xfd, 0x7c, 0x00, 0x7f, 0x01, 0x12, 0x11, 0x16, 0x80, 0x62, 0x7c, 0x00, 0x7d, 0x01, +0x7f, 0x03, 0x12, 0x11, 0x16, 0x90, 0xff, 0xfe, 0xe0, 0x44, 0x20, 0xf0, 0x80, 0x50, 0x7c, 0x00, +0x7d, 0x01, 0x7f, 0x02, 0x12, 0x11, 0x16, 0x90, 0xff, 0xfe, 0xe0, 0x44, 0x40, 0xf0, 0x80, 0x3e, +0x7c, 0x00, 0x7d, 0x01, 0x7f, 0x05, 0x12, 0x11, 0x16, 0x80, 0x33, 0x7c, 0x00, 0x7d, 0x01, 0x7f, +0x06, 0x12, 0x11, 0x16, 0x80, 0x28, 0x90, 0xfa, 0xc9, 0xe0, 0xff, 0x12, 0x20, 0x18, 0x80, 0x1e, +0x7c, 0x00, 0x7d, 0x01, 0x7f, 0x04, 0x12, 0x11, 0x16, 0x80, 0x13, 0x12, 0x27, 0x8d, 0x80, 0x0e, +0x90, 0xfa, 0xc9, 0xe0, 0x24, 0x00, 0xff, 0xe4, 0x34, 0xff, 0xfe, 0x12, 0x2c, 0xb4, 0xd0, 0x07, +0xd0, 0x06, 0xd0, 0x05, 0xd0, 0x04, 0xd0, 0x03, 0xd0, 0x02, 0xd0, 0x01, 0xd0, 0x00, 0xd0, 0xd0, +0xd0, 0x82, 0xd0, 0x83, 0xd0, 0xf0, 0xd0, 0xe0, 0x32, 0x78, 0x7c, 0xe6, 0xfe, 0x08, 0xe6, 0x24, +0x04, 0x8e, 0x83, 0xf5, 0x82, 0xe4, 0x35, 0x83, 0xf5, 0x83, 0x22, 0x74, 0x12, 0x25, 0x24, 0xf5, +0x82, 0xe4, 0x34, 0xf9, 0xf5, 0x83, 0x22, 0x78, 0x7c, 0xe6, 0xfe, 0x08, 0xe6, 0xf5, 0x82, 0x8e, +0x83, 0x22, 0x78, 0x80, 0xe6, 0xfe, 0x08, 0xe6, 0xaa, 0x06, 0xf8, 0xac, 0x02, 0x7d, 0x01, 0x7b, +0xff, 0x7a, 0x31, 0x79, 0x99, 0x7e, 0x00, 0x7f, 0x0a, 0x02, 0x19, 0xcc, 0xff, 0x90, 0xf9, 0x6c, +0x02, 0x1b, 0x3a, 0x90, 0xf9, 0x67, 0x12, 0x1b, 0x3a, 0x90, 0x00, 0x04, 0x02, 0x1a, 0x0b, 0xe6, +0xfc, 0x08, 0xe6, 0xf5, 0x82, 0x8c, 0x83, 0xa3, 0xa3, 0x22, 0x78, 0x7e, 0xe6, 0xfe, 0x08, 0xe6, +0xff, 0x22, 0xed, 0x12, 0x1a, 0x4a, 0x8f, 0x82, 0x8e, 0x83, 0xe5, 0x82, 0x22, 0xef, 0xf0, 0x90, +0xfa, 0xcb, 0xe0, 0x54, 0x0f, 0x4e, 0xfe, 0xf0, 0xef, 0x54, 0xf0, 0x4e, 0xf0, 0x22, 0x08, 0xe6, +0xfc, 0x08, 0xe6, 0x8c, 0x83, 0x24, 0x09, 0x22, 0x78, 0x7e, 0xe6, 0xfc, 0x08, 0xe6, 0xfd, 0x8c, +0x83, 0x22, 0xa6, 0x07, 0xe6, 0x24, 0x6e, 0xf8, 0xe6, 0x22, 0x78, 0x7e, 0xe6, 0xfa, 0x08, 0xe6, +0xfb, 0x22, 0x26, 0xf6, 0x18, 0xee, 0x36, 0xf6, 0x22, 0x8b, 0x82, 0x8a, 0x83, 0xe5, 0x82, 0x22, +0x8b, 0x25, 0x8a, 0x26, 0x89, 0x27, 0x8d, 0x28, 0x90, 0xfa, 0xcf, 0xe4, 0xf0, 0xa3, 0x74, 0x02, +0xf0, 0x7b, 0x01, 0x7a, 0xfa, 0x79, 0xce, 0x90, 0xfa, 0xcf, 0xe0, 0xf5, 0x2d, 0xa3, 0xe0, 0xf5, +0x2e, 0x7d, 0x01, 0x12, 0x25, 0xd7, 0x90, 0xfa, 0xce, 0xe0, 0x65, 0x28, 0x60, 0x46, 0xa3, 0xe0, +0xff, 0xa3, 0xe0, 0xa3, 0xcf, 0xf0, 0xa3, 0xef, 0xf0, 0x12, 0x23, 0x2f, 0x90, 0xfa, 0xce, 0xe0, +0xff, 0x90, 0xfa, 0xd1, 0xe4, 0x8f, 0xf0, 0x12, 0x1a, 0x6c, 0x12, 0x23, 0x2f, 0x90, 0xfa, 0xd1, +0xe0, 0xff, 0xa3, 0xe0, 0x90, 0xfa, 0xcf, 0xcf, 0xf0, 0xa3, 0xef, 0xf0, 0x90, 0xfa, 0xce, 0xe0, +0xa3, 0x75, 0xf0, 0x00, 0x12, 0x1a, 0x6c, 0x90, 0xfa, 0xcf, 0xe4, 0x75, 0xf0, 0x04, 0x12, 0x1a, +0x6c, 0x02, 0x22, 0xb1, 0x90, 0xfa, 0xd0, 0xe0, 0x24, 0x01, 0xff, 0x90, 0xfa, 0xcf, 0xe0, 0x34, +0x00, 0xab, 0x25, 0xaa, 0x26, 0xa9, 0x27, 0x8f, 0xf0, 0x12, 0x1a, 0xd0, 0x7f, 0x00, 0x22, 0x7b, +0x01, 0x7a, 0xfa, 0x79, 0xce, 0x90, 0xfa, 0xcf, 0xe4, 0x75, 0xf0, 0x01, 0x12, 0x1a, 0x6c, 0x85, +0xf0, 0x2e, 0xf5, 0x2d, 0x7d, 0x01, 0x02, 0x25, 0xd7, 0x8f, 0x62, 0x12, 0x2a, 0x06, 0x12, 0x22, +0x4a, 0x8e, 0x83, 0x24, 0x0b, 0x12, 0x21, 0xf3, 0xe0, 0x54, 0xfb, 0xf0, 0x44, 0x02, 0xf0, 0x08, +0x12, 0x22, 0x3f, 0xe0, 0xa3, 0x30, 0xe5, 0x0c, 0x12, 0x22, 0x56, 0x24, 0x0b, 0x12, 0x21, 0xf3, +0xe0, 0x44, 0x01, 0xf0, 0x78, 0x7c, 0xe6, 0xfe, 0x08, 0xe6, 0xff, 0xf5, 0x82, 0x8e, 0x83, 0xe0, +0x54, 0xb8, 0xfd, 0xf0, 0xe5, 0x62, 0x24, 0xfe, 0x44, 0x20, 0xfc, 0x4d, 0xf0, 0xe5, 0x82, 0x24, +0x04, 0x12, 0x21, 0xf3, 0xe0, 0x54, 0xb8, 0xf0, 0x4c, 0xf0, 0x8f, 0x82, 0x8e, 0x83, 0xa3, 0x74, +0x03, 0xf0, 0x18, 0xe6, 0xfe, 0x08, 0xe6, 0xff, 0x8e, 0x83, 0x24, 0x05, 0x12, 0x21, 0xf3, 0xc0, +0x83, 0xc0, 0x82, 0xe0, 0xfd, 0x74, 0x96, 0x25, 0x62, 0xf5, 0x82, 0xe4, 0x34, 0xfa, 0xf5, 0x83, +0xe0, 0x54, 0xfc, 0x44, 0x03, 0xfc, 0xed, 0x4c, 0xd0, 0x82, 0xd0, 0x83, 0xf0, 0x8f, 0x82, 0x8e, +0x83, 0xe0, 0x44, 0x80, 0xf0, 0xe5, 0x82, 0x24, 0x04, 0x12, 0x21, 0xf3, 0xe0, 0x44, 0x80, 0xf0, +0x12, 0x31, 0xc7, 0x74, 0x6e, 0x25, 0x62, 0xf8, 0x74, 0x04, 0x46, 0xf6, 0x7f, 0x00, 0x22, 0x12, +0x10, 0x03, 0x7f, 0x02, 0x12, 0x12, 0x19, 0x78, 0x67, 0xe6, 0x44, 0x02, 0xf6, 0xd2, 0xb0, 0xd2, +0xb1, 0x90, 0xf9, 0x15, 0xe0, 0x30, 0xe7, 0x07, 0x90, 0xff, 0x9e, 0xe4, 0xf0, 0x80, 0x36, 0xd2, +0xb3, 0x90, 0xff, 0xa4, 0xe0, 0x90, 0xfa, 0x7b, 0xf0, 0x90, 0xff, 0xb4, 0xe0, 0x90, 0xfa, 0x7c, +0xf0, 0x90, 0xff, 0xa2, 0xe0, 0x90, 0xfa, 0x79, 0xf0, 0x90, 0xff, 0xb2, 0xe0, 0x90, 0xfa, 0x7a, +0xf0, 0x90, 0xff, 0xa4, 0x74, 0x30, 0xf0, 0x90, 0xff, 0xb4, 0xf0, 0x90, 0xff, 0xa2, 0x74, 0x40, +0xf0, 0x90, 0xff, 0xb2, 0xf0, 0x90, 0xfa, 0xe4, 0xe5, 0xa8, 0xf0, 0x75, 0xa8, 0x81, 0x90, 0xff, +0x92, 0xe0, 0x60, 0x04, 0xe4, 0xf0, 0x80, 0xf6, 0x90, 0xff, 0xfd, 0x74, 0x3a, 0xf0, 0x43, 0x87, +0x01, 0x00, 0x00, 0x00, 0x90, 0xfa, 0x7b, 0xe0, 0x90, 0xff, 0xa4, 0xf0, 0x90, 0xfa, 0x7c, 0xe0, +0x90, 0xff, 0xb4, 0xf0, 0x90, 0xfa, 0x79, 0xe0, 0x90, 0xff, 0xa2, 0xf0, 0x90, 0xfa, 0x7a, 0xe0, +0x90, 0xff, 0xb2, 0xf0, 0x90, 0xf9, 0x17, 0xe0, 0x60, 0x02, 0xc2, 0xb3, 0x90, 0xfa, 0xe4, 0xe0, +0xf5, 0xa8, 0x02, 0x10, 0x86, 0x8b, 0x5c, 0x8a, 0x5d, 0x89, 0x5e, 0x12, 0x2d, 0x3c, 0x90, 0xfa, +0xc0, 0x12, 0x1b, 0x43, 0xaa, 0x5d, 0xa9, 0x5e, 0x90, 0xfa, 0xc3, 0x12, 0x1b, 0x43, 0x90, 0xfa, +0xc4, 0xe4, 0x75, 0xf0, 0x0a, 0x12, 0x1a, 0x6c, 0x90, 0xfa, 0xc3, 0x12, 0x1b, 0x3a, 0xe9, 0x24, +0x01, 0xf9, 0xe4, 0x3a, 0xfa, 0x90, 0xfa, 0xc6, 0x12, 0x1b, 0x43, 0xab, 0x5c, 0xaa, 0x5d, 0xa9, +0x5e, 0x12, 0x2d, 0x48, 0xe0, 0xff, 0xc3, 0x13, 0xf0, 0xe4, 0x78, 0x82, 0xf6, 0x90, 0xfa, 0xbe, +0xe0, 0xff, 0x78, 0x82, 0xe6, 0xc3, 0x9f, 0x50, 0x4a, 0x90, 0xfa, 0xc0, 0x12, 0x2d, 0x1d, 0xff, +0x78, 0x83, 0xf6, 0x90, 0xfa, 0xc3, 0x12, 0x2d, 0x1d, 0xfe, 0xf4, 0x5f, 0xff, 0x78, 0x83, 0xf6, +0x12, 0x2d, 0x1a, 0x5e, 0x4f, 0xff, 0x78, 0x83, 0xf6, 0x12, 0x2d, 0x23, 0x75, 0xf0, 0x02, 0x12, +0x1a, 0x6c, 0x90, 0xfa, 0xc4, 0xe4, 0x75, 0xf0, 0x02, 0x12, 0x1a, 0x6c, 0xab, 0x5c, 0xaa, 0x5d, +0xa9, 0x5e, 0x90, 0x00, 0x04, 0x12, 0x1a, 0x0b, 0x30, 0xe4, 0x03, 0x12, 0x2d, 0x32, 0x78, 0x82, +0x06, 0x80, 0xaa, 0xe4, 0x90, 0xfa, 0xbf, 0xf0, 0x22, 0x8b, 0x56, 0x8a, 0x57, 0x89, 0x58, 0x90, +0xfa, 0xbf, 0x74, 0x06, 0xf0, 0xe4, 0x90, 0xfa, 0xbe, 0xf0, 0x12, 0x19, 0xf2, 0x24, 0x6e, 0x60, +0x26, 0x14, 0x70, 0x70, 0x12, 0x2d, 0x09, 0x60, 0x09, 0x24, 0x30, 0x70, 0x12, 0x12, 0x24, 0x95, +0x80, 0x62, 0x12, 0x2d, 0x53, 0x12, 0x1f, 0x2c, 0x90, 0xfa, 0xbf, 0xef, 0xf0, 0x80, 0x55, 0x90, +0xfa, 0xbf, 0x74, 0x81, 0xf0, 0x80, 0x4d, 0x12, 0x2d, 0x09, 0x60, 0x09, 0x24, 0x30, 0x70, 0x3e, +0x12, 0x2c, 0x5f, 0x80, 0x3f, 0xe5, 0x58, 0x24, 0x03, 0xf9, 0xe4, 0x35, 0x57, 0xfa, 0x7b, 0x01, +0xc0, 0x03, 0xc0, 0x02, 0xc0, 0x01, 0x12, 0x2d, 0x53, 0x90, 0x00, 0x05, 0x12, 0x1a, 0x0b, 0xfd, +0x90, 0x00, 0x08, 0x12, 0x1a, 0x98, 0xf5, 0x2e, 0x85, 0xf0, 0x2d, 0xd0, 0x01, 0xd0, 0x02, 0xd0, +0x03, 0x12, 0x25, 0xd7, 0x90, 0xfa, 0xbe, 0xef, 0xf0, 0xe4, 0xa3, 0xf0, 0x80, 0x06, 0x90, 0xfa, +0xbf, 0x74, 0x81, 0xf0, 0x90, 0xfa, 0xbf, 0xe0, 0x12, 0x2d, 0x53, 0x90, 0x00, 0x02, 0x12, 0x1a, +0x4a, 0x90, 0xfa, 0xbe, 0xe0, 0xff, 0x22, 0x8b, 0x29, 0x8a, 0x2a, 0x89, 0x2b, 0x8d, 0x2c, 0xe5, +0x2c, 0x70, 0x03, 0xaf, 0x2c, 0x22, 0x12, 0x2d, 0x82, 0x70, 0x16, 0x12, 0x2d, 0xa1, 0xe5, 0x2d, +0x90, 0xff, 0xf1, 0xf0, 0x12, 0x31, 0x1b, 0x50, 0xf2, 0x12, 0x26, 0x64, 0x40, 0x0b, 0x7f, 0x00, +0x22, 0x12, 0x2d, 0xa1, 0x12, 0x26, 0x64, 0x50, 0xf8, 0x90, 0xff, 0xf3, 0x74, 0xa1, 0xf0, 0xe5, +0x2c, 0xb4, 0x01, 0x07, 0x90, 0xff, 0xf0, 0xe0, 0x44, 0x02, 0xf0, 0x90, 0xff, 0xf1, 0xe4, 0xf0, +0xf5, 0x2f, 0xe5, 0x2c, 0x14, 0xff, 0xe5, 0x2f, 0xc3, 0x9f, 0x50, 0x2a, 0x12, 0x31, 0x04, 0x40, +0x03, 0xaf, 0x2f, 0x22, 0xc3, 0xe5, 0x2c, 0x95, 0x2f, 0xff, 0xbf, 0x02, 0x07, 0x90, 0xff, 0xf0, +0xe0, 0x44, 0x02, 0xf0, 0x12, 0x2d, 0x94, 0x05, 0x2f, 0x74, 0x01, 0x25, 0x2b, 0xf5, 0x2b, 0xe4, +0x35, 0x2a, 0xf5, 0x2a, 0x80, 0xcc, 0x12, 0x31, 0x04, 0x40, 0x03, 0x7f, 0x18, 0x22, 0x12, 0x2d, +0x94, 0xaf, 0x2c, 0x22, 0x90, 0xff, 0xf1, 0xe5, 0x2e, 0xf0, 0x02, 0x31, 0x1b, 0x12, 0x10, 0x03, +0x78, 0x84, 0x12, 0x22, 0x82, 0x30, 0xe1, 0x08, 0x7f, 0x13, 0x12, 0x30, 0xec, 0x02, 0x26, 0xfb, +0x78, 0x84, 0xe6, 0xf9, 0x24, 0x12, 0x12, 0x21, 0xff, 0xe0, 0xff, 0x30, 0xe7, 0x40, 0x54, 0x03, +0x60, 0x1e, 0xe9, 0xb4, 0x03, 0x0d, 0x90, 0xff, 0x9e, 0xe0, 0x54, 0xfe, 0xf0, 0xe0, 0x44, 0x04, +0xf0, 0x80, 0x46, 0x90, 0xff, 0x9e, 0xe0, 0x54, 0xfd, 0xf0, 0xe0, 0x44, 0x08, 0xf0, 0x80, 0x39, +0xe9, 0xb4, 0x03, 0x0d, 0x90, 0xff, 0x9e, 0xe0, 0x54, 0xfb, 0xf0, 0xe0, 0x44, 0x01, 0xf0, 0x80, +0x28, 0x90, 0xff, 0x9e, 0xe0, 0x54, 0xf7, 0xf0, 0xe0, 0x44, 0x02, 0xf0, 0x80, 0x1b, 0xef, 0x54, +0x03, 0x60, 0x14, 0xe9, 0xb4, 0x03, 0x09, 0x90, 0xff, 0xa4, 0xe0, 0x54, 0xdf, 0xf0, 0x80, 0x07, +0x90, 0xff, 0xb4, 0xe0, 0x54, 0xdf, 0xf0, 0xc2, 0xb3, 0x90, 0xf9, 0x17, 0xe0, 0x04, 0xf0, 0xaf, +0x01, 0x12, 0x22, 0x33, 0xfd, 0x12, 0x2f, 0x49, 0x12, 0x30, 0xec, 0x02, 0x10, 0x86, 0x75, 0xa8, +0x40, 0x78, 0x7f, 0xe4, 0xf6, 0xd8, 0xfd, 0x75, 0x81, 0x8b, 0x02, 0x27, 0x48, 0x02, 0x30, 0xcf, +0xe4, 0x93, 0xa3, 0xf8, 0xe4, 0x93, 0xa3, 0x40, 0x03, 0xf6, 0x80, 0x01, 0xf2, 0x08, 0xdf, 0xf4, +0x80, 0x29, 0xe4, 0x93, 0xa3, 0xf8, 0x54, 0x07, 0x24, 0x0c, 0xc8, 0xc3, 0x33, 0xc4, 0x54, 0x0f, +0x44, 0x20, 0xc8, 0x83, 0x40, 0x04, 0xf4, 0x56, 0x80, 0x01, 0x46, 0xf6, 0xdf, 0xe4, 0x80, 0x0b, +0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x90, 0x2b, 0x4c, 0xe4, 0x7e, 0x01, 0x93, 0x60, +0xbc, 0xa3, 0xff, 0x54, 0x3f, 0x30, 0xe5, 0x09, 0x54, 0x1f, 0xfe, 0xe4, 0x93, 0xa3, 0x60, 0x01, +0x0e, 0xcf, 0x54, 0xc0, 0x25, 0xe0, 0x60, 0xa8, 0x40, 0xb8, 0xe4, 0x93, 0xa3, 0xfa, 0xe4, 0x93, +0xa3, 0xf8, 0xe4, 0x93, 0xa3, 0xc8, 0xc5, 0x82, 0xc8, 0xca, 0xc5, 0x83, 0xca, 0xf0, 0xa3, 0xc8, +0xc5, 0x82, 0xc8, 0xca, 0xc5, 0x83, 0xca, 0xdf, 0xe9, 0xde, 0xe7, 0x80, 0xbe, 0xe4, 0xf5, 0x22, +0x12, 0x1d, 0x12, 0xe0, 0xb4, 0x04, 0x0d, 0xe5, 0x22, 0x24, 0x03, 0xff, 0x12, 0x2f, 0x77, 0x12, +0x1d, 0x12, 0xe4, 0xf0, 0x05, 0x22, 0xe5, 0x22, 0xc3, 0x94, 0x02, 0x40, 0xe3, 0xe4, 0xf5, 0x22, +0x75, 0xf0, 0x02, 0xe5, 0x22, 0x90, 0xfa, 0x91, 0x12, 0x1d, 0x53, 0x60, 0x2c, 0x12, 0x2c, 0xb4, +0xef, 0x60, 0x52, 0x75, 0xf0, 0x02, 0xe5, 0x22, 0x90, 0xfa, 0x91, 0x12, 0x1b, 0x1c, 0xe4, 0xf0, +0xa3, 0xf0, 0x75, 0xf0, 0x0a, 0xe5, 0x22, 0x90, 0xfa, 0x9d, 0x12, 0x1b, 0x1c, 0xe0, 0xa3, 0x30, +0xe6, 0x33, 0x12, 0x1d, 0x12, 0x74, 0x04, 0xf0, 0x22, 0x75, 0xf0, 0x02, 0xe5, 0x22, 0x90, 0xfa, +0x95, 0x12, 0x1d, 0x53, 0x60, 0x16, 0x12, 0x2c, 0xb4, 0xef, 0x60, 0x19, 0x75, 0xf0, 0x02, 0xe5, +0x22, 0x90, 0xfa, 0x95, 0x12, 0x1b, 0x1c, 0xe4, 0xf0, 0xa3, 0xf0, 0x22, 0x05, 0x22, 0xe5, 0x22, +0xc3, 0x94, 0x02, 0x40, 0x9b, 0x22, 0xe4, 0xff, 0x90, 0xff, 0x83, 0xe0, 0x54, 0x0f, 0xfe, 0xef, +0xc3, 0x9e, 0x50, 0x17, 0x74, 0xf0, 0x2f, 0xf5, 0x82, 0xe4, 0x34, 0xfe, 0xf5, 0x83, 0xe0, 0x12, +0x1c, 0x11, 0x12, 0x1a, 0x38, 0x0f, 0x12, 0x1c, 0x00, 0x80, 0xdd, 0xef, 0xfd, 0xc3, 0xe5, 0x3a, +0x9d, 0xf5, 0x3a, 0xe5, 0x39, 0x94, 0x00, 0xf5, 0x39, 0xd3, 0xe5, 0x3a, 0x94, 0x00, 0xe5, 0x39, +0x94, 0x00, 0x40, 0x06, 0xe4, 0x90, 0xff, 0x83, 0xf0, 0x22, 0x12, 0x1d, 0x2f, 0x12, 0x1d, 0x84, +0x12, 0x1d, 0x76, 0x12, 0x19, 0xf2, 0x24, 0x6e, 0x60, 0x1e, 0x14, 0x60, 0x1b, 0x24, 0x8e, 0x70, +0x2d, 0x90, 0x00, 0x01, 0x12, 0x1a, 0x0b, 0xff, 0x24, 0xfc, 0x60, 0x03, 0x04, 0x70, 0x1f, 0xef, +0xfd, 0x7c, 0x00, 0x7f, 0x0d, 0x02, 0x11, 0x16, 0x12, 0x1d, 0x8b, 0x12, 0x25, 0x39, 0x12, 0x1c, +0xd9, 0x12, 0x1a, 0x0b, 0x60, 0x03, 0x02, 0x31, 0xbd, 0xe4, 0xff, 0x12, 0x31, 0xb1, 0x22, 0x8b, +0x45, 0x8a, 0x46, 0x89, 0x47, 0x8c, 0x48, 0x8d, 0x49, 0xd2, 0x00, 0x12, 0x2d, 0x82, 0x70, 0x16, +0x12, 0x2d, 0xa1, 0xe5, 0x48, 0x90, 0xff, 0xf1, 0xf0, 0x12, 0x31, 0x1b, 0x50, 0xf2, 0x12, 0x29, +0x14, 0x40, 0x0b, 0x7f, 0x18, 0x22, 0x12, 0x2d, 0xa1, 0x12, 0x29, 0x14, 0x50, 0xf8, 0xe4, 0xf5, +0x4b, 0xe5, 0x4a, 0x14, 0xff, 0xe5, 0x4b, 0xc3, 0x9f, 0x50, 0x17, 0x12, 0x29, 0x04, 0x40, 0x03, +0x7f, 0x18, 0x22, 0x05, 0x4b, 0x74, 0x01, 0x25, 0x47, 0xf5, 0x47, 0xe4, 0x35, 0x46, 0xf5, 0x46, +0x80, 0xdf, 0x90, 0xff, 0xf0, 0xe0, 0x44, 0x01, 0xf0, 0x12, 0x29, 0x04, 0x40, 0x03, 0x7f, 0x18, +0x22, 0x7f, 0x00, 0x22, 0xab, 0x45, 0xaa, 0x46, 0xa9, 0x47, 0x12, 0x19, 0xf2, 0x90, 0xff, 0xf1, +0xf0, 0x02, 0x31, 0x1b, 0x90, 0xff, 0xf1, 0xe5, 0x49, 0xf0, 0x02, 0x31, 0x1b, 0x7b, 0x01, 0x7a, +0xfa, 0x79, 0xcc, 0xe4, 0xfd, 0x12, 0x22, 0xa0, 0x90, 0xfa, 0xcc, 0xe4, 0x75, 0xf0, 0x09, 0x12, +0x1a, 0x6c, 0x7b, 0x00, 0x7a, 0x00, 0x79, 0x23, 0x90, 0xfa, 0xcc, 0xe4, 0x75, 0xf0, 0x01, 0x12, +0x1a, 0x82, 0x85, 0xf0, 0x2e, 0xf5, 0x2d, 0x7d, 0x01, 0x12, 0x25, 0xd7, 0x90, 0xff, 0xf7, 0xe5, +0x23, 0x12, 0x29, 0x78, 0x90, 0xff, 0xf6, 0xe5, 0x23, 0xf0, 0x90, 0xfa, 0xcc, 0xe4, 0xf0, 0xa3, +0x74, 0x06, 0x12, 0x29, 0x78, 0xe5, 0x23, 0x30, 0xe0, 0x07, 0x90, 0xff, 0xfc, 0x74, 0x94, 0xf0, +0x22, 0x90, 0xff, 0xfc, 0x74, 0x90, 0xf0, 0x22, 0xf0, 0x7b, 0x00, 0x7a, 0x00, 0x79, 0x23, 0x90, +0xfa, 0xcc, 0xe4, 0x75, 0xf0, 0x01, 0x12, 0x1a, 0x82, 0x85, 0xf0, 0x2e, 0xf5, 0x2d, 0x7d, 0x01, +0x02, 0x25, 0xd7, 0x90, 0xff, 0x93, 0x74, 0x2a, 0xf0, 0x90, 0xff, 0xff, 0xe0, 0x60, 0x06, 0x90, +0xff, 0xfc, 0x74, 0x10, 0xf0, 0x90, 0xff, 0x91, 0xe0, 0x44, 0x90, 0xf0, 0xe4, 0x90, 0xf9, 0x15, +0xf0, 0xa3, 0xf0, 0x12, 0x2a, 0x78, 0x12, 0x16, 0x42, 0x12, 0x2f, 0xcd, 0x7e, 0x07, 0x7f, 0xd0, +0x12, 0x11, 0xe2, 0x7e, 0x0f, 0x7f, 0xa0, 0x12, 0x11, 0xfc, 0xe4, 0x78, 0x77, 0xf6, 0x78, 0x77, +0xe6, 0xff, 0xc3, 0x94, 0x06, 0x50, 0x0b, 0x74, 0x6e, 0x2f, 0xf8, 0xe4, 0xf6, 0x78, 0x77, 0x06, +0x80, 0xec, 0x7f, 0x03, 0x12, 0x2e, 0xb3, 0x90, 0xf9, 0x15, 0xe0, 0x20, 0xe4, 0x05, 0x7f, 0x04, +0x12, 0x2e, 0xb3, 0x90, 0xff, 0x9b, 0xe4, 0xf0, 0x90, 0xff, 0x9a, 0xf0, 0x90, 0xff, 0xe8, 0xe0, +0x54, 0x1f, 0xf0, 0xd2, 0xa8, 0x22, 0x15, 0x65, 0xa8, 0x65, 0xa6, 0x07, 0x30, 0x08, 0x05, 0x12, +0x11, 0x66, 0x80, 0xf8, 0xd2, 0x08, 0xa8, 0x65, 0xe6, 0xff, 0xb4, 0x03, 0x0f, 0x78, 0x7c, 0x76, +0xff, 0x08, 0x76, 0xe0, 0x08, 0x76, 0xff, 0x08, 0x76, 0xa0, 0x80, 0x0d, 0x78, 0x7c, 0x76, 0xff, +0x08, 0x76, 0xe2, 0x08, 0x76, 0xff, 0x08, 0x76, 0xb0, 0x78, 0x80, 0x76, 0xfa, 0x08, 0x76, 0x9b, +0xef, 0x24, 0xfd, 0x75, 0xf0, 0x0a, 0xa4, 0xae, 0xf0, 0x12, 0x22, 0x92, 0x7b, 0x01, 0x7a, 0xff, +0x79, 0x48, 0x78, 0x68, 0x12, 0x1b, 0x31, 0xa8, 0x65, 0xe6, 0x24, 0xfd, 0x75, 0xf0, 0x08, 0xa4, +0xff, 0xae, 0xf0, 0x78, 0x6a, 0x12, 0x22, 0x92, 0x79, 0x08, 0x78, 0x6b, 0x12, 0x1b, 0x31, 0x78, +0x6d, 0xef, 0x12, 0x22, 0x92, 0x05, 0x65, 0x22, 0x90, 0xff, 0xf0, 0xe0, 0x54, 0xab, 0xf0, 0xe0, +0x44, 0x20, 0xf0, 0x90, 0xfa, 0xe3, 0x74, 0x02, 0xf0, 0x7b, 0x01, 0x7a, 0xfa, 0x79, 0xcc, 0xe4, +0xf5, 0x2d, 0xf5, 0x2e, 0x7d, 0x01, 0x12, 0x25, 0xd7, 0x7e, 0x00, 0x90, 0xfa, 0xe1, 0xee, 0xf0, +0xa3, 0xef, 0xf0, 0x64, 0x01, 0x70, 0x10, 0x90, 0xfa, 0xcc, 0xe0, 0xb4, 0x52, 0x09, 0x90, 0xf9, +0x15, 0xe0, 0x54, 0xef, 0xf0, 0x80, 0x29, 0x90, 0xfa, 0xe1, 0xe0, 0x70, 0x04, 0xa3, 0xe0, 0x64, +0x01, 0x70, 0x10, 0x90, 0xfa, 0xcc, 0xe0, 0xb4, 0x10, 0x09, 0x90, 0xf9, 0x15, 0xe0, 0x44, 0x10, +0xf0, 0x80, 0x0d, 0x90, 0xfa, 0xe3, 0x74, 0x03, 0xf0, 0x90, 0xf9, 0x15, 0xe0, 0x54, 0xef, 0xf0, +0x90, 0xff, 0xf0, 0xe0, 0x44, 0x20, 0xf0, 0x22, 0x12, 0x10, 0x03, 0x78, 0x8a, 0xef, 0xf6, 0x12, +0x2a, 0x06, 0x12, 0x22, 0x33, 0x30, 0xe0, 0x25, 0x12, 0x22, 0x07, 0xe0, 0x54, 0x7f, 0xf0, 0x78, +0x6b, 0x12, 0x1b, 0x28, 0x90, 0x00, 0x02, 0x12, 0x1a, 0x0b, 0x30, 0xe7, 0x09, 0x90, 0x00, 0x02, +0xe4, 0x12, 0x1a, 0x4a, 0x80, 0xe9, 0x12, 0x22, 0x07, 0xe0, 0x44, 0x80, 0xf0, 0x12, 0x22, 0x33, +0x30, 0xe1, 0x1e, 0x12, 0x21, 0xe9, 0xe0, 0x54, 0x7f, 0xf0, 0x12, 0x31, 0x5c, 0x78, 0x68, 0x12, +0x1b, 0x28, 0x90, 0x00, 0x02, 0x74, 0x80, 0x12, 0x1a, 0x4a, 0x12, 0x21, 0xe9, 0xe0, 0x44, 0x80, +0xf0, 0x12, 0x31, 0xc7, 0xe4, 0xff, 0x12, 0x30, 0xec, 0x02, 0x10, 0x86, 0x03, 0x68, 0x01, 0xff, +0x48, 0x03, 0x6b, 0x01, 0xff, 0x08, 0x02, 0x66, 0x00, 0x00, 0x44, 0xfa, 0x95, 0x00, 0x00, 0x00, +0x00, 0x44, 0xfa, 0x91, 0x00, 0x00, 0x00, 0x00, 0x42, 0xfa, 0xaf, 0x00, 0x00, 0x42, 0xfa, 0x7b, +0x00, 0x00, 0x42, 0xfa, 0x79, 0x00, 0x00, 0x42, 0xf9, 0x6a, 0xff, 0xff, 0x42, 0xfa, 0x77, 0x00, +0x00, 0x43, 0xf9, 0x18, 0x0a, 0x32, 0x02, 0x41, 0xf9, 0x65, 0x20, 0x41, 0xf9, 0x66, 0x20, 0x41, +0xf9, 0x63, 0x00, 0x41, 0xf9, 0x64, 0x00, 0x44, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x42, 0xf9, +0x15, 0x00, 0x00, 0x41, 0xf9, 0x17, 0x00, 0x01, 0x20, 0x00, 0x41, 0xf8, 0x04, 0x00, 0x00, 0x12, +0x10, 0x03, 0x78, 0x85, 0xef, 0xf6, 0x12, 0x30, 0x93, 0x12, 0x30, 0xec, 0x78, 0x85, 0xe6, 0xff, +0x24, 0x12, 0x12, 0x21, 0xff, 0xe0, 0xfe, 0x30, 0xe7, 0x16, 0xef, 0xb4, 0x03, 0x09, 0x90, 0xff, +0x9e, 0xe0, 0x54, 0xfa, 0xf0, 0x80, 0x22, 0x90, 0xff, 0x9e, 0xe0, 0x54, 0xf5, 0xf0, 0x80, 0x19, +0xee, 0x54, 0x03, 0x60, 0x14, 0xef, 0xb4, 0x03, 0x09, 0x90, 0xff, 0xa4, 0xe0, 0x44, 0x20, 0xf0, +0x80, 0x07, 0x90, 0xff, 0xb4, 0xe0, 0x44, 0x20, 0xf0, 0x90, 0xf9, 0x17, 0xe0, 0x14, 0xf0, 0xe0, +0x70, 0x02, 0xd2, 0xb3, 0x02, 0x10, 0x86, 0x12, 0x1d, 0x6c, 0xe5, 0x3a, 0x64, 0x09, 0x70, 0x04, +0xe5, 0x39, 0x64, 0x01, 0x60, 0x48, 0xc3, 0xe5, 0x3a, 0x94, 0x08, 0xe5, 0x39, 0x94, 0x00, 0x40, +0x11, 0x7f, 0x08, 0xef, 0xe5, 0x3a, 0x94, 0x08, 0xf5, 0x3a, 0xe5, 0x39, 0x94, 0x00, 0xf5, 0x39, +0x80, 0x05, 0xaf, 0x3a, 0x12, 0x1d, 0x84, 0xe4, 0xfe, 0xee, 0xc3, 0x9f, 0x50, 0x19, 0x12, 0x1c, +0x11, 0x12, 0x19, 0xf2, 0xfd, 0x74, 0xf8, 0x2e, 0xf5, 0x82, 0xe4, 0x34, 0xfe, 0xf5, 0x83, 0xed, +0xf0, 0x0e, 0x12, 0x1c, 0x00, 0x80, 0xe2, 0xef, 0x54, 0x7f, 0x90, 0xff, 0x81, 0xf0, 0x22, 0x8b, +0x59, 0x8a, 0x5a, 0x89, 0x5b, 0x12, 0x2d, 0x48, 0x70, 0x05, 0xa3, 0x74, 0x08, 0xf0, 0x22, 0xab, +0x59, 0xaa, 0x5a, 0xa9, 0x5b, 0x12, 0x2d, 0x3c, 0x90, 0xfa, 0xc6, 0x12, 0x1b, 0x43, 0xe5, 0x5b, +0x24, 0x03, 0xf9, 0xe4, 0x35, 0x5a, 0xfa, 0x90, 0xfa, 0xc0, 0x12, 0x1b, 0x43, 0xe4, 0x90, 0xfa, +0xbf, 0xf0, 0x78, 0x8b, 0xf6, 0x90, 0xfa, 0xbe, 0xe0, 0xff, 0x78, 0x8b, 0xe6, 0xc3, 0x9f, 0x50, +0x12, 0x12, 0x2d, 0x1a, 0xff, 0x12, 0x2d, 0x23, 0x12, 0x2d, 0x36, 0x78, 0x8b, 0x06, 0x12, 0x2d, +0x32, 0x80, 0xe2, 0x22, 0xad, 0x07, 0xac, 0x06, 0x90, 0x31, 0x4d, 0xe4, 0x93, 0xff, 0x78, 0x74, +0xf6, 0x54, 0x0f, 0x12, 0x1c, 0xf8, 0xe0, 0x08, 0x76, 0x00, 0x08, 0xf6, 0x18, 0x12, 0x1c, 0x29, +0xc3, 0x33, 0xce, 0x33, 0xce, 0xd8, 0xf9, 0xff, 0x78, 0x75, 0xee, 0xf6, 0x08, 0xef, 0xf6, 0xee, +0x44, 0xf8, 0x18, 0xf6, 0xef, 0x08, 0xf6, 0x90, 0xff, 0x7a, 0xe0, 0x20, 0xe7, 0x03, 0x7f, 0x00, +0x22, 0x78, 0x75, 0xe6, 0xfe, 0x08, 0xe6, 0xf5, 0x82, 0x8e, 0x83, 0xec, 0xf0, 0xa3, 0xed, 0xf0, +0x90, 0xff, 0x7a, 0x74, 0x02, 0xf0, 0x7f, 0x01, 0x22, 0xab, 0x56, 0xaa, 0x57, 0xa9, 0x58, 0x90, +0x00, 0x03, 0x12, 0x1a, 0x0b, 0x54, 0xf0, 0x24, 0xa0, 0x22, 0x90, 0xfa, 0xc6, 0x12, 0x1b, 0x3a, +0x02, 0x19, 0xf2, 0x90, 0xfa, 0xc0, 0x12, 0x1b, 0x3a, 0xef, 0x12, 0x1a, 0x38, 0x90, 0xfa, 0xc7, +0xe4, 0x22, 0x90, 0xfa, 0xc1, 0xe4, 0x75, 0xf0, 0x01, 0x02, 0x1a, 0x6c, 0x90, 0x00, 0x08, 0x12, +0x1a, 0x98, 0xaa, 0xf0, 0xf9, 0x7b, 0x01, 0x22, 0x90, 0x00, 0x05, 0x12, 0x1a, 0x0b, 0x90, 0xfa, +0xbe, 0xf0, 0x22, 0xab, 0x56, 0xaa, 0x57, 0xa9, 0x58, 0x22, 0x90, 0xfa, 0xda, 0xe0, 0xff, 0x7e, +0x00, 0xc3, 0x90, 0xfa, 0xd4, 0xe0, 0x9f, 0xf0, 0x90, 0xfa, 0xd3, 0xe0, 0x9e, 0xf0, 0x90, 0xfa, +0xd5, 0xee, 0x8f, 0xf0, 0x12, 0x1a, 0x6c, 0xef, 0x25, 0x51, 0xf5, 0x51, 0xee, 0x35, 0x50, 0xf5, +0x50, 0x22, 0x90, 0xff, 0xf0, 0xe0, 0x54, 0xfe, 0xf0, 0xe0, 0x54, 0xfd, 0xf0, 0x90, 0xfa, 0xe3, +0xe0, 0x64, 0x03, 0x22, 0x90, 0xff, 0xf2, 0xe0, 0xab, 0x29, 0xaa, 0x2a, 0xa9, 0x2b, 0x02, 0x1a, +0x38, 0x90, 0xff, 0xf3, 0x74, 0xa0, 0xf0, 0x22, 0x8f, 0x64, 0xed, 0x70, 0x0f, 0xe5, 0x64, 0xb4, +0x03, 0x05, 0x7f, 0x01, 0x02, 0x31, 0x32, 0x7f, 0x02, 0x02, 0x31, 0x32, 0xaf, 0x64, 0x12, 0x2a, +0x06, 0x74, 0x6e, 0x25, 0x64, 0xf8, 0xe6, 0x30, 0xe2, 0x0b, 0xd2, 0x09, 0x12, 0x1c, 0x83, 0xe0, +0x54, 0x7f, 0xf0, 0x80, 0x02, 0xc2, 0x09, 0xe5, 0x64, 0xb4, 0x03, 0x07, 0x7f, 0x81, 0x12, 0x31, +0x32, 0x80, 0x05, 0x7f, 0x82, 0x12, 0x31, 0x32, 0x30, 0x09, 0x07, 0x12, 0x1c, 0x83, 0xe0, 0x44, +0x80, 0xf0, 0x12, 0x31, 0xc7, 0x22, 0x12, 0x10, 0x03, 0x90, 0xff, 0xfd, 0xe0, 0x44, 0x60, 0xf0, +0xd2, 0x01, 0x90, 0xff, 0xfc, 0xe0, 0x44, 0x02, 0xf0, 0x90, 0xff, 0x00, 0xe0, 0x30, 0xe7, 0x13, +0x90, 0xff, 0x83, 0xe0, 0x44, 0x80, 0xf0, 0x43, 0x35, 0x80, 0x90, 0xff, 0xfc, 0xe0, 0x44, 0x01, +0xf0, 0x80, 0x0d, 0x12, 0x1d, 0x2f, 0x53, 0x35, 0x7f, 0x90, 0xff, 0xfc, 0xe0, 0x54, 0xfe, 0xf0, +0x90, 0xff, 0x81, 0xe0, 0x44, 0x80, 0xf0, 0x12, 0x02, 0xb0, 0x12, 0x1d, 0x37, 0x02, 0x10, 0x86, +0x12, 0x10, 0x03, 0x78, 0x89, 0xef, 0xf6, 0xd2, 0x00, 0x12, 0x2a, 0x06, 0x90, 0xf9, 0x67, 0x12, +0x1b, 0x3a, 0xe9, 0x24, 0x03, 0xf9, 0xe4, 0x3a, 0xfa, 0xc0, 0x02, 0x78, 0x80, 0xe6, 0xfe, 0x08, +0xe6, 0xaa, 0x06, 0xf8, 0xac, 0x02, 0x7d, 0x01, 0xd0, 0x02, 0x12, 0x22, 0x25, 0x12, 0x31, 0xc7, +0x78, 0x89, 0xe6, 0xff, 0x12, 0x13, 0x3f, 0x12, 0x30, 0xec, 0x02, 0x10, 0x86, 0x8f, 0x63, 0x12, +0x2a, 0x06, 0x12, 0x22, 0x07, 0xe0, 0x54, 0x3f, 0xf0, 0xe5, 0x82, 0x24, 0x04, 0x12, 0x21, 0xf3, +0xe0, 0x54, 0x3f, 0xf0, 0x08, 0xe6, 0xfe, 0x08, 0xe6, 0x8e, 0x83, 0x24, 0x0b, 0x12, 0x21, 0xf3, +0xe0, 0x54, 0xf8, 0xf0, 0x12, 0x31, 0xc7, 0x74, 0x6e, 0x25, 0x63, 0xf8, 0x74, 0xfb, 0x56, 0xf6, +0x7f, 0x00, 0x22, 0x8f, 0x23, 0xc2, 0x08, 0x12, 0x2a, 0x06, 0x12, 0x22, 0x12, 0x78, 0x7e, 0x12, +0x21, 0xeb, 0xe0, 0x44, 0x01, 0xf0, 0x12, 0x22, 0x4a, 0x12, 0x21, 0xef, 0xe0, 0x20, 0xe0, 0xf6, +0xef, 0x24, 0x0b, 0xf5, 0x82, 0xe4, 0x3e, 0xf5, 0x83, 0xe0, 0x54, 0xf8, 0xf0, 0x12, 0x31, 0xc7, +0xaf, 0x23, 0x12, 0x13, 0x3f, 0x22, 0x12, 0x10, 0x03, 0x12, 0x2a, 0x06, 0x12, 0x22, 0x4a, 0x24, +0x06, 0x12, 0x21, 0xf1, 0xe0, 0xfd, 0x12, 0x22, 0x2d, 0x90, 0x00, 0x03, 0x12, 0x22, 0x52, 0x24, +0x05, 0x12, 0x21, 0xf3, 0xe0, 0x90, 0x00, 0x04, 0x12, 0x1a, 0x4a, 0x12, 0x31, 0xc7, 0x7d, 0x02, +0xe4, 0xff, 0x12, 0x2f, 0x18, 0x02, 0x10, 0x86, 0xae, 0x05, 0x12, 0x1c, 0xde, 0xef, 0x12, 0x1a, +0x4a, 0x0e, 0x0e, 0x0e, 0xee, 0xd3, 0x95, 0x3c, 0xe4, 0x95, 0x3b, 0x40, 0x02, 0xae, 0x3c, 0xee, +0xd3, 0x94, 0x08, 0x74, 0x80, 0x94, 0x81, 0x40, 0x0a, 0x7e, 0x03, 0x90, 0x00, 0x02, 0x74, 0x02, +0x12, 0x1a, 0x4a, 0xaf, 0x06, 0x12, 0x31, 0xb1, 0x22, 0xae, 0x07, 0xed, 0x54, 0x03, 0x64, 0x01, +0x60, 0x03, 0x7f, 0x10, 0x22, 0xed, 0x54, 0x7c, 0xc3, 0x94, 0x04, 0x50, 0x03, 0x7f, 0x0b, 0x22, +0x74, 0x6e, 0x2e, 0xf8, 0x74, 0x02, 0x46, 0xf6, 0x74, 0x96, 0x2e, 0xf5, 0x82, 0xe4, 0x34, 0xfa, +0xf5, 0x83, 0xed, 0xf0, 0x7f, 0x00, 0x22, 0xbf, 0x03, 0x06, 0x7c, 0xff, 0x7d, 0xe0, 0x80, 0x04, +0x7c, 0xff, 0x7d, 0xe2, 0x8d, 0x82, 0x8c, 0x83, 0xe0, 0x44, 0x80, 0xf0, 0xe5, 0x82, 0x24, 0x04, +0x12, 0x21, 0xf3, 0xe0, 0x44, 0x80, 0xf0, 0x74, 0x6e, 0x2f, 0xf8, 0x74, 0x04, 0x46, 0xf6, 0x7f, +0x00, 0x22, 0x12, 0x10, 0x03, 0xe5, 0x3a, 0x64, 0x09, 0x70, 0x04, 0xe5, 0x39, 0x64, 0x01, 0x60, +0x16, 0x90, 0xff, 0x83, 0xe0, 0x54, 0x0f, 0xff, 0xc3, 0xe5, 0x3a, 0x9f, 0xe5, 0x39, 0x94, 0x00, +0x40, 0x05, 0x12, 0x28, 0x16, 0x80, 0x03, 0x12, 0x31, 0xbd, 0x02, 0x10, 0x86, 0x90, 0xff, 0xfc, +0xe0, 0x20, 0xe7, 0x1f, 0xc2, 0xaf, 0x7d, 0xff, 0xac, 0x05, 0x1d, 0xec, 0x60, 0x15, 0x7e, 0x04, +0x7f, 0x00, 0xef, 0x1f, 0xaa, 0x06, 0x70, 0x01, 0x1e, 0x4a, 0x60, 0xec, 0x90, 0xff, 0x92, 0xe4, +0xf0, 0x80, 0xef, 0x22, 0x12, 0x10, 0x03, 0x78, 0x66, 0xe6, 0xfe, 0x08, 0xe6, 0xff, 0x30, 0xe0, +0x12, 0x30, 0xe1, 0x0f, 0x90, 0xff, 0xfc, 0xe0, 0x44, 0x20, 0xf0, 0x7f, 0x04, 0x12, 0x12, 0x19, +0x12, 0x1d, 0x46, 0x02, 0x10, 0x86, 0x8e, 0x5f, 0x8f, 0x60, 0xe5, 0x60, 0x15, 0x60, 0xae, 0x5f, +0x70, 0x02, 0x15, 0x5f, 0xd3, 0x94, 0x00, 0xee, 0x94, 0x00, 0x40, 0x09, 0x7e, 0x07, 0x7f, 0xd0, +0x12, 0x0f, 0xdc, 0x80, 0xe5, 0x22, 0x11, 0x94, 0x2d, 0xf6, 0x23, 0xef, 0x31, 0xa3, 0x2f, 0xf4, +0x2f, 0xa2, 0x30, 0xb2, 0x2e, 0xe6, 0x26, 0x6d, 0x2b, 0xaf, 0x30, 0x55, 0x30, 0x74, 0x1d, 0xb4, +0x2e, 0x40, 0x2a, 0xe8, 0x0e, 0x12, 0x10, 0x03, 0x78, 0x86, 0x12, 0x22, 0x82, 0x20, 0xe1, 0x07, +0x7f, 0x12, 0x12, 0x30, 0xec, 0x80, 0x0a, 0x78, 0x86, 0xe6, 0xff, 0x12, 0x23, 0x49, 0x12, 0x30, +0xec, 0x02, 0x10, 0x86, 0x12, 0x10, 0x03, 0x78, 0x87, 0x12, 0x22, 0x82, 0x20, 0xe2, 0x07, 0x7f, +0x11, 0x12, 0x30, 0xec, 0x80, 0x0a, 0x78, 0x87, 0xe6, 0xff, 0x12, 0x2e, 0x7d, 0x12, 0x30, 0xec, +0x02, 0x10, 0x86, 0x8f, 0x61, 0x12, 0x2e, 0x7d, 0xaf, 0x61, 0x12, 0x2a, 0x06, 0x12, 0x22, 0x12, +0x12, 0x31, 0xc7, 0x74, 0x6e, 0x25, 0x61, 0xf8, 0x74, 0xfd, 0x56, 0xf6, 0xaf, 0x61, 0x12, 0x13, +0x3f, 0x22, 0x12, 0x10, 0x03, 0xe5, 0x3a, 0x64, 0x09, 0x70, 0x04, 0xe5, 0x39, 0x64, 0x01, 0x60, +0x05, 0x12, 0x2c, 0x07, 0x80, 0x06, 0x12, 0x1d, 0x64, 0x12, 0x1d, 0x6c, 0x02, 0x10, 0x86, 0x12, +0x29, 0x93, 0x12, 0x12, 0xbb, 0x90, 0xf8, 0x04, 0xe0, 0xff, 0x60, 0x05, 0x7d, 0x01, 0x12, 0x12, +0x58, 0x12, 0x29, 0x1d, 0x12, 0x12, 0xf7, 0x12, 0x11, 0x74, 0x80, 0xe3, 0x12, 0x1c, 0xde, 0xef, +0x12, 0x1a, 0x4a, 0xe4, 0xf5, 0x33, 0xf5, 0x34, 0xef, 0x60, 0x03, 0x02, 0x31, 0xbd, 0xe4, 0xff, +0x12, 0x31, 0xb1, 0x22, 0x90, 0xff, 0xf0, 0xe0, 0xff, 0x54, 0xa0, 0x60, 0xf7, 0xef, 0x30, 0xe5, +0x08, 0x90, 0xff, 0xf0, 0x44, 0x20, 0xf0, 0xc3, 0x22, 0xd3, 0x22, 0x90, 0xff, 0xf0, 0xe0, 0xff, +0x54, 0x28, 0x60, 0xf7, 0xef, 0x30, 0xe5, 0x08, 0x90, 0xff, 0xf0, 0x44, 0x20, 0xf0, 0xc3, 0x22, +0xd3, 0x22, 0xef, 0x30, 0xe7, 0x08, 0x12, 0x1c, 0x95, 0xe0, 0x54, 0xdf, 0xf0, 0x22, 0xef, 0x12, +0x1c, 0xe8, 0xe0, 0x54, 0xdf, 0xf0, 0x22, 0x81, 0x01, 0x82, 0x02, 0x83, 0x03, 0x87, 0x40, 0x00, +0x40, 0x00, 0x40, 0x00, 0x40, 0x00, 0x40, 0x00, 0x40, 0x00, 0x08, 0x00, 0x78, 0x7e, 0x12, 0x22, +0x09, 0xa3, 0xa3, 0xe0, 0xff, 0x30, 0xe7, 0x06, 0x54, 0x7f, 0xf0, 0x44, 0x80, 0xf0, 0x22, 0x85, +0x3b, 0x39, 0x85, 0x3c, 0x3a, 0x90, 0xff, 0x82, 0xe0, 0x54, 0xf7, 0xf0, 0xa3, 0xe0, 0x54, 0x7f, +0xf0, 0x22, 0xe4, 0xfe, 0xee, 0x90, 0x31, 0x47, 0x93, 0xb5, 0x07, 0x02, 0xd3, 0x22, 0x0e, 0xbe, +0x07, 0xf2, 0xc3, 0x22, 0x00, 0x08, 0x18, 0x28, 0x38, 0x01, 0x81, 0x10, 0x0a, 0x02, 0x00, 0x00, +0x00, 0x00, 0x00, 0x12, 0x10, 0x03, 0x7f, 0x02, 0x12, 0x10, 0x92, 0x12, 0x1d, 0x46, 0x02, 0x10, +0x86, 0x75, 0x39, 0x00, 0x8f, 0x3a, 0x12, 0x1c, 0x30, 0x12, 0x2c, 0x07, 0x22, 0x12, 0x1d, 0x6c, +0x12, 0x1d, 0x2f, 0x12, 0x1d, 0x64, 0x22, 0xc2, 0x08, 0x22, }; #undef IMAGE_VERSION_NAME diff --git a/drivers/usb/serial/io_tables.h b/drivers/usb/serial/io_tables.h index f4567f166..d43b9e6cd 100644 --- a/drivers/usb/serial/io_tables.h +++ b/drivers/usb/serial/io_tables.h @@ -14,22 +14,12 @@ #ifndef IO_TABLES_H #define IO_TABLES_H -static struct usb_device_id edgeport_1port_id_table [] = { - { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_EDGEPORT_PARALLEL_PORT) }, - { } -}; - static struct usb_device_id edgeport_2port_id_table [] = { { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_EDGEPORT_2) }, { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_EDGEPORT_2I) }, { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_EDGEPORT_421) }, { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_EDGEPORT_21) }, { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_EDGEPORT_2_DIN) }, - { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_BB_EDGEPORT_2) }, - { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_BB_EDGEPORT_2I) }, - { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_BB_EDGEPORT_421) }, - { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_BB_EDGEPORT_21) }, - { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_BB_EDGEPORT_2_DIN) }, { } }; @@ -41,12 +31,9 @@ static struct usb_device_id edgeport_4port_id_table [] = { { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_EDGEPORT_4I) }, { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_EDGEPORT_8_DUAL_CPU) }, { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_EDGEPORT_4_DIN) }, + { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_EDGEPORT_22I) }, + { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_EDGEPORT_412_4) }, { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_EDGEPORT_COMPATIBLE) }, - { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_BB_EDGEPORT_4) }, - { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_BB_EDGEPORT_4T) }, - { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_BB_EDGEPORT_4I) }, - { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_BB_EDGEPORT_8_DUAL_CPU) }, - { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_BB_EDGEPORT_4_DIN) }, { } }; @@ -54,9 +41,9 @@ static struct usb_device_id edgeport_8port_id_table [] = { { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_EDGEPORT_8) }, { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_EDGEPORT_16_DUAL_CPU) }, { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_EDGEPORT_8I) }, - { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_BB_EDGEPORT_8) }, - { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_BB_EDGEPORT_16_DUAL_CPU) }, - { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_BB_EDGEPORT_8I) }, + { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_EDGEPORT_8R) }, + { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_EDGEPORT_8RR) }, + { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_EDGEPORT_412_8) }, { } }; @@ -69,7 +56,6 @@ static struct usb_device_id id_table_combined [] = { { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_EDGEPORT_2) }, { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_EDGEPORT_4I) }, { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_EDGEPORT_2I) }, - { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_EDGEPORT_PARALLEL_PORT) }, { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_EDGEPORT_421) }, { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_EDGEPORT_21) }, { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_EDGEPORT_8_DUAL_CPU) }, @@ -77,51 +63,18 @@ static struct usb_device_id id_table_combined [] = { { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_EDGEPORT_2_DIN) }, { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_EDGEPORT_4_DIN) }, { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_EDGEPORT_16_DUAL_CPU) }, + { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_EDGEPORT_22I) }, + { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_EDGEPORT_412_4) }, { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_EDGEPORT_COMPATIBLE) }, { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_EDGEPORT_8I) }, - { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_BB_EDGEPORT_2) }, - { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_BB_EDGEPORT_2I) }, - { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_BB_EDGEPORT_421) }, - { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_BB_EDGEPORT_21) }, - { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_BB_EDGEPORT_2_DIN) }, - { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_BB_EDGEPORT_4) }, - { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_BB_EDGEPORT_4T) }, - { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_BB_EDGEPORT_4I) }, - { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_BB_EDGEPORT_8_DUAL_CPU) }, - { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_BB_EDGEPORT_4_DIN) }, - { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_BB_EDGEPORT_8) }, - { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_BB_EDGEPORT_16_DUAL_CPU) }, - { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_BB_EDGEPORT_8I) }, + { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_EDGEPORT_8R) }, + { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_EDGEPORT_8RR) }, + { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_EDGEPORT_412_8) }, { } /* Terminating entry */ }; MODULE_DEVICE_TABLE (usb, id_table_combined); -static struct usb_serial_device_type edgeport_1port_device = { - .owner = THIS_MODULE, - .name = "Edgeport 1 port adapter", - .short_name = "edgeport_1", - .id_table = edgeport_1port_id_table, - .num_interrupt_in = 1, - .num_bulk_in = 1, - .num_bulk_out = 1, - .num_ports = 1, - .open = edge_open, - .close = edge_close, - .throttle = edge_throttle, - .unthrottle = edge_unthrottle, - .attach = edge_startup, - .shutdown = edge_shutdown, - .ioctl = edge_ioctl, - .set_termios = edge_set_termios, - .tiocmget = edge_tiocmget, - .tiocmset = edge_tiocmset, - .write = edge_write, - .write_room = edge_write_room, - .chars_in_buffer = edge_chars_in_buffer, - .break_ctl = edge_break, -}; - static struct usb_serial_device_type edgeport_2port_device = { .owner = THIS_MODULE, .name = "Edgeport 2 port adapter", diff --git a/drivers/usb/serial/io_usbvend.h b/drivers/usb/serial/io_usbvend.h index 26a8c5a18..65073409f 100644 --- a/drivers/usb/serial/io_usbvend.h +++ b/drivers/usb/serial/io_usbvend.h @@ -18,14 +18,6 @@ #if !defined(_USBVEND_H) #define _USBVEND_H -#ifndef __KERNEL__ -#include "ionprag.h" /* Extra I/O Networks pragmas */ - -#include - -#include "iondef.h" /* Standard I/O Networks definitions */ -#endif - /************************************************************************ * * D e f i n e s / T y p e d e f s @@ -37,6 +29,7 @@ // #define USB_VENDOR_ID_ION 0x1608 // Our VID +#define USB_VENDOR_ID_TI 0x0451 // TI VID // // Definitions of USB product IDs (PID) @@ -48,36 +41,41 @@ // ION-device OEM IDs #define ION_OEM_ID_ION 0 // 00h Inside Out Networks -#define ION_OEM_ID_NLYNX 1 // 01h NLynx Systems +#define ION_OEM_ID_NLYNX 1 // 01h NLynx Systems #define ION_OEM_ID_GENERIC 2 // 02h Generic OEM #define ION_OEM_ID_MAC 3 // 03h Mac Version #define ION_OEM_ID_MEGAWOLF 4 // 04h Lupusb OEM Mac version (MegaWolf) #define ION_OEM_ID_MULTITECH 5 // 05h Multitech Rapidports +#define ION_OEM_ID_AGILENT 6 // 06h AGILENT board + - // ION-device Device IDs -// Product IDs - assigned to match middle digit of serial number +// Product IDs - assigned to match middle digit of serial number (No longer true) +#define ION_DEVICE_ID_80251_NETCHIP 0x020 // This bit is set in the PID if this edgeport hardware$ + // is based on the 80251+Netchip. -// The ION_DEVICE_ID_GENERATION_2 bit (0x20) will be ORed into the existing edgeport -// PIDs to identify 80251+Netchip hardware. This will guarantee that if a second -// generation edgeport device is plugged into a PC with an older (pre 2.0) driver, -// it will not enumerate. +#define ION_DEVICE_ID_GENERATION_1 0x00 // Value for 930 based edgeports +#define ION_DEVICE_ID_GENERATION_2 0x01 // Value for 80251+Netchip. +#define ION_DEVICE_ID_GENERATION_3 0x02 // Value for Texas Instruments TUSB5052 chip +#define ION_DEVICE_ID_GENERATION_4 0x03 // Watchport Family of products +#define ION_GENERATION_MASK 0x03 -#define ION_DEVICE_ID_GENERATION_2 0x020 // This bit is set in the PID if this edgeport hardware - // is based on the 80251+Netchip. +#define ION_DEVICE_ID_HUB_MASK 0x0080 // This bit in the PID designates a HUB device + // for example 8C would be a 421 4 port hub + // and 8D would be a 2 port embedded hub -#define EDGEPORT_DEVICE_ID_MASK 0x3df // Not including GEN_2 bit +#define EDGEPORT_DEVICE_ID_MASK 0x0ff // Not including OEM or GENERATION fields #define ION_DEVICE_ID_UNCONFIGURED_EDGE_DEVICE 0x000 // In manufacturing only #define ION_DEVICE_ID_EDGEPORT_4 0x001 // Edgeport/4 RS232 -// ION_DEVICE_ID_HUBPORT_7 0x002 // Hubport/7 (Placeholder, not used by software) +#define ION_DEVICE_ID_EDGEPORT_8R 0x002 // Edgeport with RJ45 no Ring #define ION_DEVICE_ID_RAPIDPORT_4 0x003 // Rapidport/4 #define ION_DEVICE_ID_EDGEPORT_4T 0x004 // Edgeport/4 RS232 for Telxon (aka "Fleetport") #define ION_DEVICE_ID_EDGEPORT_2 0x005 // Edgeport/2 RS232 #define ION_DEVICE_ID_EDGEPORT_4I 0x006 // Edgeport/4 RS422 #define ION_DEVICE_ID_EDGEPORT_2I 0x007 // Edgeport/2 RS422/RS485 -// ION_DEVICE_ID_HUBPORT_4 0x008 // Hubport/4 (Placeholder, not used by software) +#define ION_DEVICE_ID_EDGEPORT_8RR 0x008 // Edgeport with RJ45 with Data and RTS/CTS only // ION_DEVICE_ID_EDGEPORT_8_HANDBUILT 0x009 // Hand-built Edgeport/8 (Placeholder, used in middle digit of serial number only!) // ION_DEVICE_ID_MULTIMODEM_4X56 0x00A // MultiTech version of RP/4 (Placeholder, used in middle digit of serial number only!) #define ION_DEVICE_ID_EDGEPORT_PARALLEL_PORT 0x00B // Edgeport/(4)21 Parallel port (USS720) @@ -90,41 +88,134 @@ #define ION_DEVICE_ID_EDGEPORT_16_DUAL_CPU 0x012 // Half of an Edgeport/16 (the kind with 2 EP/8s) #define ION_DEVICE_ID_EDGEPORT_COMPATIBLE 0x013 // Edgeport Compatible, for NCR, Axiohm etc. testing #define ION_DEVICE_ID_EDGEPORT_8I 0x014 // Edgeport/8 RS422 (single-CPU) +#define ION_DEVICE_ID_EDGEPORT_1 0x015 // Edgeport/1 RS232 +#define ION_DEVICE_ID_EPOS44 0x016 // Half of an EPOS/44 (TIUMP BASED) +#define ION_DEVICE_ID_EDGEPORT_42 0x017 // Edgeport/42 +#define ION_DEVICE_ID_EDGEPORT_412_8 0x018 // Edgeport/412 8 port part +#define ION_DEVICE_ID_EDGEPORT_412_4 0x019 // Edgeport/412 4 port part +#define ION_DEVICE_ID_EDGEPORT_22I 0x01A // Edgeport/22I is an Edgeport/4 with ports 1&2 RS422 and ports 3&4 RS232 + +// Compact Form factor TI based devices 2c, 21c, 22c, 221c +#define ION_DEVICE_ID_EDGEPORT_2C 0x01B // Edgeport/2c is a TI based Edgeport/2 - Small I2c +#define ION_DEVICE_ID_EDGEPORT_221C 0x01C // Edgeport/221c is a TI based Edgeport/2 with lucent chip and + // 2 external hub ports - Large I2C +#define ION_DEVICE_ID_EDGEPORT_22C 0x01D // Edgeport/22c is a TI based Edgeport/2 with + // 2 external hub ports - Large I2C +#define ION_DEVICE_ID_EDGEPORT_21C 0x01E // Edgeport/21c is a TI based Edgeport/2 with lucent chip + // Small I2C + + +/* + * DANGER DANGER The 0x20 bit was used to indicate a 8251/netchip GEN 2 device. + * Since the MAC, Linux, and Optimal drivers still used the old code + * I suggest that you skip the 0x20 bit when creating new PIDs + */ + + +// Generation 3 devices -- 3410 based edgport/1 (256 byte I2C) +#define ION_DEVICE_ID_TI3410_EDGEPORT_1 0x040 // Edgeport/1 RS232 +#define ION_DEVICE_ID_TI3410_EDGEPORT_1I 0x041 // Edgeport/1i- RS422 model + +// Ti based software switchable RS232/RS422/RS485 devices +#define ION_DEVICE_ID_EDGEPORT_4S 0x042 // Edgeport/4s - software switchable model +#define ION_DEVICE_ID_EDGEPORT_8S 0x043 // Edgeport/8s - software switchable model + +// Usb to Ethernet dongle +#define ION_DEVICE_ID_EDGEPORT_E 0x0E0 // Edgeport/E Usb to Ethernet + +// Edgeport TI based devices +#define ION_DEVICE_ID_TI_EDGEPORT_4 0x0201 // Edgeport/4 RS232 +#define ION_DEVICE_ID_TI_EDGEPORT_2 0x0205 // Edgeport/2 RS232 +#define ION_DEVICE_ID_TI_EDGEPORT_4I 0x0206 // Edgeport/4i RS422 +#define ION_DEVICE_ID_TI_EDGEPORT_2I 0x0207 // Edgeport/2i RS422/RS485 +#define ION_DEVICE_ID_TI_EDGEPORT_421 0x020C // Edgeport/421 4 hub 2 RS232 + Parallel (lucent on a different hub port) +#define ION_DEVICE_ID_TI_EDGEPORT_21 0x020D // Edgeport/21 2 RS232 + Parallel (lucent on a different hub port) +#define ION_DEVICE_ID_TI_EDGEPORT_8 0x020F // Edgeport/8 (single-CPU) +#define ION_DEVICE_ID_TI_EDGEPORT_1 0x0215 // Edgeport/1 RS232 +#define ION_DEVICE_ID_TI_EDGEPORT_42 0x0217 // Edgeport/42 4 hub 2 RS232 +#define ION_DEVICE_ID_TI_EDGEPORT_22I 0x021A // Edgeport/22I is an Edgeport/4 with ports 1&2 RS422 and ports 3&4 RS232 +#define ION_DEVICE_ID_TI_EDGEPORT_2C 0x021B // Edgeport/2c RS232 +#define ION_DEVICE_ID_TI_EDGEPORT_221C 0x021C // Edgeport/221c is a TI based Edgeport/2 with lucent chip and + // 2 external hub ports - Large I2C +#define ION_DEVICE_ID_TI_EDGEPORT_22C 0x021D // Edgeport/22c is a TI based Edgeport/2 with + // 2 external hub ports - Large I2C +#define ION_DEVICE_ID_TI_EDGEPORT_21C 0x021E // Edgeport/21c is a TI based Edgeport/2 with lucent chip + +// Generation 3 devices -- 3410 based edgport/1 (256 byte I2C) +#define ION_DEVICE_ID_TI_TI3410_EDGEPORT_1 0x240 // Edgeport/1 RS232 +#define ION_DEVICE_ID_TI_TI3410_EDGEPORT_1I 0x241 // Edgeport/1i- RS422 model + +// Ti based software switchable RS232/RS422/RS485 devices +#define ION_DEVICE_ID_TI_EDGEPORT_4S 0x242 // Edgeport/4s - software switchable model +#define ION_DEVICE_ID_IT_EDGEPORT_8S 0x243 // Edgeport/8s - software switchable model + + +/************************************************************************ + * + * Generation 4 devices + * + ************************************************************************/ + +// Watchport based on 3410 both 1-wire and binary products (16K I2C) +#define ION_DEVICE_ID_WP_UNSERIALIZED 0x300 // Watchport based on 3410 both 1-wire and binary products +#define ION_DEVICE_ID_WP_PROXIMITY 0x301 // Watchport/P Discontinued +#define ION_DEVICE_ID_WP_MOTION 0x302 // Watchport/M +#define ION_DEVICE_ID_WP_MOISTURE 0x303 // Watchport/W +#define ION_DEVICE_ID_WP_TEMPERATURE 0x304 // Watchport/T +#define ION_DEVICE_ID_WP_HUMIDITY 0x305 // Watchport/H + +#define ION_DEVICE_ID_WP_POWER 0x306 // Watchport +#define ION_DEVICE_ID_WP_LIGHT 0x307 // Watchport +#define ION_DEVICE_ID_WP_RADIATION 0x308 // Watchport +#define ION_DEVICE_ID_WP_ACCELERATION 0x309 // Watchport/A +#define ION_DEVICE_ID_WP_DISTANCE 0x30A // Watchport/D Discontinued +#define ION_DEVICE_ID_WP_PROX_DIST 0x30B // Watchport/D uses distance sensor + // Default to /P function + +#define ION_DEVICE_ID_PLUS_PWR_HP4CD 0x30C // 5052 Plus Power HubPort/4CD+ (for Dell) +#define ION_DEVICE_ID_PLUS_PWR_HP4C 0x30D // 5052 Plus Power HubPort/4C+ +#define ION_DEVICE_ID_PLUS_PWR_PCI 0x30E // 3410 Plus Power PCI Host Controller 4 port + + +// +// Definitions for AXIOHM USB product IDs +// +#define USB_VENDOR_ID_AXIOHM 0x05D9 // Axiohm VID + +#define AXIOHM_DEVICE_ID_MASK 0xffff +#define AXIOHM_DEVICE_ID_EPIC_A758 0xA758 +#define AXIOHM_DEVICE_ID_EPIC_A794 0xA794 +#define AXIOHM_DEVICE_ID_EPIC_A225 0xA225 + + +// +// Definitions for NCR USB product IDs +// +#define USB_VENDOR_ID_NCR 0x0404 // NCR VID + +#define NCR_DEVICE_ID_MASK 0xffff +#define NCR_DEVICE_ID_EPIC_0202 0x0202 +#define NCR_DEVICE_ID_EPIC_0203 0x0203 +#define NCR_DEVICE_ID_EPIC_0310 0x0310 +#define NCR_DEVICE_ID_EPIC_0311 0x0311 +#define NCR_DEVICE_ID_EPIC_0312 0x0312 + + +// +// Definitions for SYMBOL USB product IDs +// +#define USB_VENDOR_ID_SYMBOL 0x05E0 // Symbol VID +#define SYMBOL_DEVICE_ID_MASK 0xffff +#define SYMBOL_DEVICE_ID_KEYFOB 0x0700 + + +// +// Definitions for other product IDs #define ION_DEVICE_ID_MT4X56USB 0x1403 // OEM device -// BlackBox OEM devices -#define ION_DEVICE_ID_BB_EDGEPORT_4 0x001 // Edgeport/4 RS232 -#define ION_DEVICE_ID_BB_EDGEPORT_4T 0x004 // Edgeport/4 RS232 for Telxon (aka "Fleetport") -#define ION_DEVICE_ID_BB_EDGEPORT_2 0x005 // Edgeport/2 RS232 -#define ION_DEVICE_ID_BB_EDGEPORT_4I 0x006 // Edgeport/4 RS422 -#define ION_DEVICE_ID_BB_EDGEPORT_2I 0x007 // Edgeport/2 RS422/RS485 -#define ION_DEVICE_ID_BB_EDGEPORT_421 0x00C // Edgeport/421 Hub+RS232+Parallel -#define ION_DEVICE_ID_BB_EDGEPORT_21 0x00D // Edgeport/21 RS232+Parallel -#define ION_DEVICE_ID_BB_EDGEPORT_8_DUAL_CPU 0x00E // Half of an Edgeport/8 (the kind with 2 EP/4s on 1 PCB) -#define ION_DEVICE_ID_BB_EDGEPORT_8 0x00F // Edgeport/8 (single-CPU) -#define ION_DEVICE_ID_BB_EDGEPORT_2_DIN 0x010 // Edgeport/2 RS232 with Apple DIN connector -#define ION_DEVICE_ID_BB_EDGEPORT_4_DIN 0x011 // Edgeport/4 RS232 with Apple DIN connector -#define ION_DEVICE_ID_BB_EDGEPORT_16_DUAL_CPU 0x012 // Half of an Edgeport/16 (the kind with 2 EP/8s) -#define ION_DEVICE_ID_BB_EDGEPORT_8I 0x014 // Edgeport/8 RS422 (single-CPU) - - -/* Edgeport TI based devices */ -#define ION_DEVICE_ID_TI_EDGEPORT_4 0x0201 /* Edgeport/4 RS232 */ -#define ION_DEVICE_ID_TI_EDGEPORT_2 0x0205 /* Edgeport/2 RS232 */ -#define ION_DEVICE_ID_TI_EDGEPORT_4I 0x0206 /* Edgeport/4i RS422 */ -#define ION_DEVICE_ID_TI_EDGEPORT_2I 0x0207 /* Edgeport/2i RS422/RS485 */ -#define ION_DEVICE_ID_TI_EDGEPORT_421 0x020C /* Edgeport/421 4 hub 2 RS232 + Parallel (lucent on a different hub port) */ -#define ION_DEVICE_ID_TI_EDGEPORT_21 0x020D /* Edgeport/21 2 RS232 + Parallel (lucent on a different hub port) */ -#define ION_DEVICE_ID_TI_EDGEPORT_1 0x0215 /* Edgeport/1 RS232 */ -#define ION_DEVICE_ID_TI_EDGEPORT_42 0x0217 /* Edgeport/42 4 hub 2 RS232 */ -#define ION_DEVICE_ID_TI_EDGEPORT_22 0x021A /* Edgeport/22 Edgeport/22I is an Edgeport/4 with ports 1&2 RS422 and ports 3&4 RS232 */ -#define ION_DEVICE_ID_TI_EDGEPORT_2C 0x021B /* Edgeport/2c RS232 */ - -#define ION_DEVICE_ID_TI_EDGEPORT_421_BOOT 0x0240 /* Edgeport/421 in boot mode */ -#define ION_DEVICE_ID_TI_EDGEPORT_421_DOWN 0x0241 /* Edgeport/421 in download mode first interface is 2 RS232 (Note that the second interface of this multi interface device should be a standard USB class 7 printer port) */ -#define ION_DEVICE_ID_TI_EDGEPORT_21_BOOT 0x0242 /* Edgeport/21 in boot mode */ -#define ION_DEVICE_ID_TI_EDGEPORT_21_DOWN 0x0243 /*Edgeport/42 in download mode: first interface is 2 RS232 (Note that the second interface of this multi interface device should be a standard USB class 7 printer port) */ +#define GENERATION_ID_FROM_USB_PRODUCT_ID( ProductId ) \ + ( (__u16) ((ProductId >> 8) & (ION_GENERATION_MASK)) ) #define MAKE_USB_PRODUCT_ID( OemId, DeviceId ) \ ( (__u16) (((OemId) << 10) || (DeviceId)) ) @@ -143,7 +234,7 @@ // TxCredits value below which driver won't bother sending (to prevent too many small writes). // Send only if above 25% -#define EDGE_FW_GET_TX_CREDITS_SEND_THRESHOLD(InitialCredit) (max(((InitialCredit) / 4), EDGE_FW_BULK_MAX_PACKET_SIZE)) +#define EDGE_FW_GET_TX_CREDITS_SEND_THRESHOLD(InitialCredit, MaxPacketSize) (max( ((InitialCredit) / 4), (MaxPacketSize) )) #define EDGE_FW_BULK_MAX_PACKET_SIZE 64 // Max Packet Size for Bulk In Endpoint (EP1) #define EDGE_FW_BULK_READ_BUFFER_SIZE 1024 // Size to use for Bulk reads @@ -158,8 +249,8 @@ // Definitions of I/O Networks vendor-specific requests // for default endpoint // -// bmRequestType = 00100000 Set vendor-specific, to device -// bmRequestType = 10100000 Get vendor-specific, to device +// bmRequestType = 01000000 Set vendor-specific, to device +// bmRequestType = 11000000 Get vendor-specific, to device // // These are the definitions for the bRequest field for the // above bmRequestTypes. @@ -184,11 +275,87 @@ #define USB_REQUEST_ION_ENABLE_SUSPEND 9 // Enable/Disable suspend feature // (wValue != 0: Enable; wValue = 0: Disable) +#define USB_REQUEST_ION_SEND_IOSP 10 // Send an IOSP command to the edgeport over the control pipe +#define USB_REQUEST_ION_RECV_IOSP 11 // Receive an IOSP command from the edgeport over the control pipe + + +#define USB_REQUEST_ION_DIS_INT_TIMER 0x80 // Sent to Axiohm to enable/ disable + // interrupt token timer + // wValue = 1, enable (default) + // wValue = 0, disable // // Define parameter values for our vendor-specific commands // +// +// Edgeport Compatiblity Descriptor +// +// This descriptor is only returned by Edgeport-compatible devices +// supporting the EPiC spec. True ION devices do not return this +// descriptor, but instead return STALL on receipt of the +// GET_EPIC_DESC command. The driver interprets a STALL to mean that +// this is a "real" Edgeport. +// + +struct edge_compatibility_bits +{ + // This __u32 defines which Vendor-specific commands/functionality + // the device supports on the default EP0 pipe. + + __u32 VendEnableSuspend : 1; // 0001 Set if device supports ION_ENABLE_SUSPEND + __u32 VendUnused : 31; // Available for future expansion, must be 0 + + // This __u32 defines which IOSP commands are supported over the + // bulk pipe EP1. + + // xxxx Set if device supports: + __u32 IOSPOpen : 1; // 0001 OPEN / OPEN_RSP (Currently must be 1) + __u32 IOSPClose : 1; // 0002 CLOSE + __u32 IOSPChase : 1; // 0004 CHASE / CHASE_RSP + __u32 IOSPSetRxFlow : 1; // 0008 SET_RX_FLOW + __u32 IOSPSetTxFlow : 1; // 0010 SET_TX_FLOW + __u32 IOSPSetXChar : 1; // 0020 SET_XON_CHAR/SET_XOFF_CHAR + __u32 IOSPRxCheck : 1; // 0040 RX_CHECK_REQ/RX_CHECK_RSP + __u32 IOSPSetClrBreak : 1; // 0080 SET_BREAK/CLEAR_BREAK + __u32 IOSPWriteMCR : 1; // 0100 MCR register writes (set/clr DTR/RTS) + __u32 IOSPWriteLCR : 1; // 0200 LCR register writes (wordlen/stop/parity) + __u32 IOSPSetBaudRate : 1; // 0400 setting Baud rate (writes to LCR.80h and DLL/DLM register) + __u32 IOSPDisableIntPipe : 1; // 0800 Do not use the interrupt pipe for TxCredits or RxButesAvailable + __u32 IOSPRxDataAvail : 1; // 1000 Return status of RX Fifo (Data available in Fifo) + __u32 IOSPTxPurge : 1; // 2000 Purge TXBuffer and/or Fifo in Edgeport hardware + __u32 IOSPUnused : 18; // Available for future expansion, must be 0 + + // This __u32 defines which 'general' features are supported + + __u32 TrueEdgeport : 1; // 0001 Set if device is a 'real' Edgeport + // (Used only by driver, NEVER set by an EPiC device) + __u32 GenUnused : 31; // Available for future expansion, must be 0 + +}; + +struct edge_compatibility_descriptor +{ + __u8 Length; // Descriptor Length (per USB spec) + __u8 DescType; // Descriptor Type (per USB spec, =DEVICE type) + __u8 EpicVer; // Version of EPiC spec supported + // (Currently must be 1) + __u8 NumPorts; // Number of serial ports supported + __u8 iDownloadFile; // Index of string containing download code filename + // 0=no download, FF=download compiled into driver. + __u8 Unused[ 3 ]; // Available for future expansion, must be 0 + // (Currently must be 0). + __u8 MajorVersion; // Firmware version: xx. + __u8 MinorVersion; // yy. + __le16 BuildNumber; // zzzz (LE format) + + // The following structure contains __u32s, with each bit + // specifying whether the EPiC device supports the given + // command or functionality. + + struct edge_compatibility_bits Supports; + +}; // Values for iDownloadFile #define EDGE_DOWNLOAD_FILE_NONE 0 // No download requested @@ -272,30 +439,30 @@ struct edge_manuf_descriptor { __u8 NumPorts; // F08 Number of ports __u8 DescDate[3]; // F09 MM/DD/YY when descriptor template was compiler, - // so host can track changes to USB-only descriptors. + // so host can track changes to USB-only descriptors. __u8 SerNumLength; // F0C USB string descriptor len __u8 SerNumDescType; // F0D USB descriptor type (=STRING type) - __u16 SerialNumber[MAX_SERIALNUMBER_LEN]; // F0E "01-01-000100" Unicode Serial Number + __le16 SerialNumber[MAX_SERIALNUMBER_LEN]; // F0E "01-01-000100" Unicode Serial Number __u8 AssemblyNumLength; // F26 USB string descriptor len __u8 AssemblyNumDescType; // F27 USB descriptor type (=STRING type) - __u16 AssemblyNumber[MAX_ASSEMBLYNUMBER_LEN]; // F28 "350-1000-01-A " assembly number + __le16 AssemblyNumber[MAX_ASSEMBLYNUMBER_LEN]; // F28 "350-1000-01-A " assembly number __u8 OemAssyNumLength; // F44 USB string descriptor len __u8 OemAssyNumDescType; // F45 USB descriptor type (=STRING type) - __u16 OemAssyNumber[MAX_ASSEMBLYNUMBER_LEN]; // F46 "xxxxxxxxxxxxxx" OEM assembly number + __le16 OemAssyNumber[MAX_ASSEMBLYNUMBER_LEN]; // F46 "xxxxxxxxxxxxxx" OEM assembly number __u8 ManufDateLength; // F62 USB string descriptor len __u8 ManufDateDescType; // F63 USB descriptor type (=STRING type) - __u16 ManufDate[6]; // F64 "MMDDYY" manufacturing date + __le16 ManufDate[6]; // F64 "MMDDYY" manufacturing date __u8 Reserved3[0x4D]; // F70 -- unused, set to 0 -- __u8 UartType; // FBD Uart Type __u8 IonPid; // FBE Product ID, == LSB of USB DevDesc.PID - // (Note: Edgeport/4s before 11/98 will have - // 00 here instead of 01) + // (Note: Edgeport/4s before 11/98 will have + // 00 here instead of 01) __u8 IonConfig; // FBF Config byte for ION manufacturing use // FBF end of structure, total len = 3C0h @@ -312,7 +479,7 @@ struct edge_manuf_descriptor { // both 00 and 01 values mean '654. #define MANUF_UART_EXAR_654_EARLY 0 // Exar 16C654 in Edgeport/4s before 11/98 #define MANUF_UART_EXAR_654 1 // Exar 16C654 -#define MANUF_UART_EXAR_2852 2 // Exar 16C2852 +#define MANUF_UART_EXAR_2852 2 // Exar 16C2852 // // Note: The CpuRev and BoardRev values do not conform to manufacturing @@ -334,25 +501,22 @@ struct edge_manuf_descriptor { #define MANUF_BOARD_REV_GENERATION_2 0x20 // Second generaiton edgeport - - // Values of bottom 5 bits of CpuRev & BoardRev for // Implementation 1 (ie, 251+Netchip-based) #define MANUF_CPU_REV_1 1 // C251TB Rev 1 (Need actual Intel rev here) #define MANUF_BOARD_REV_A 1 // First rev of 251+Netchip design - - #define MANUF_SERNUM_LENGTH sizeof(((struct edge_manuf_descriptor *)0)->SerialNumber) #define MANUF_ASSYNUM_LENGTH sizeof(((struct edge_manuf_descriptor *)0)->AssemblyNumber) #define MANUF_OEMASSYNUM_LENGTH sizeof(((struct edge_manuf_descriptor *)0)->OemAssyNumber) #define MANUF_MANUFDATE_LENGTH sizeof(((struct edge_manuf_descriptor *)0)->ManufDate) -#define MANUF_ION_CONFIG_MASTER 0x80 // 1=Master mode, 0=Normal -#define MANUF_ION_CONFIG_DIAG 0x40 // 1=Run h/w diags, 0=norm -#define MANUF_ION_CONFIG_DIAG_NO_LOOP 0x20 // As above but no ext loopback test - +#define MANUF_ION_CONFIG_DIAG_NO_LOOP 0x20 // As below but no ext loopback test +#define MANUF_ION_CONFIG_DIAG 0x40 // 930 based device: 1=Run h/w diags, 0=norm + // TIUMP Device : 1=IONSERIAL needs to run Final Test +#define MANUF_ION_CONFIG_MASTER 0x80 // 930 based device: 1=Master mode, 0=Normal + // TIUMP Device : 1=First device on a multi TIUMP Device // // This structure describes parameters for the boot code, and @@ -368,19 +532,19 @@ struct edge_boot_descriptor { __u8 DescVer; // C2 Desc version/format __u8 Reserved1; // C3 -- unused, set to 0 -- - __u16 BootCodeLength; // C4 Boot code goes from FF:0000 to FF:(len-1) + __le16 BootCodeLength; // C4 Boot code goes from FF:0000 to FF:(len-1) // (LE format) __u8 MajorVersion; // C6 Firmware version: xx. __u8 MinorVersion; // C7 yy. - __u16 BuildNumber; // C8 zzzz (LE format) + __le16 BuildNumber; // C8 zzzz (LE format) __u16 EnumRootDescTable; // CA Root of ROM-based descriptor table __u8 NumDescTypes; // CC Number of supported descriptor types __u8 Reserved4; // CD Fix Compiler Packing - __u16 Capabilities; // CE-CF Capabilities flags (LE format) + __le16 Capabilities; // CE-CF Capabilities flags (LE format) __u8 Reserved2[0x28]; // D0 -- unused, set to 0 -- __u8 UConfig0; // F8 930-defined CPU configuration byte 0 __u8 UConfig1; // F9 930-defined CPU configuration byte 1 @@ -398,23 +562,29 @@ struct edge_boot_descriptor { #define BOOT_CAP_RESET_CMD 0x0001 // If set, boot correctly supports ION_RESET_DEVICE - /************************************************************************ T I U M P D E F I N I T I O N S ***********************************************************************/ +// Chip definitions in I2C +#define UMP5152 0x52 +#define UMP3410 0x10 + + //************************************************************************ // TI I2C Format Definitions //************************************************************************ -#define I2C_DESC_TYPE_INFO_BASIC 1 -#define I2C_DESC_TYPE_FIRMWARE_BASIC 2 -#define I2C_DESC_TYPE_DEVICE 3 -#define I2C_DESC_TYPE_CONFIG 4 -#define I2C_DESC_TYPE_STRING 5 -#define I2C_DESC_TYPE_FIRMWARE_BLANK 0xf2 - -#define I2C_DESC_TYPE_MAX 5 -// 3410 may define types 6, 7 for other firmware downloads +#define I2C_DESC_TYPE_INFO_BASIC 0x01 +#define I2C_DESC_TYPE_FIRMWARE_BASIC 0x02 +#define I2C_DESC_TYPE_DEVICE 0x03 +#define I2C_DESC_TYPE_CONFIG 0x04 +#define I2C_DESC_TYPE_STRING 0x05 +#define I2C_DESC_TYPE_FIRMWARE_AUTO 0x07 // for 3410 download +#define I2C_DESC_TYPE_CONFIG_KLUDGE 0x14 // for 3410 +#define I2C_DESC_TYPE_WATCHPORT_VERSION 0x15 // firmware version number for watchport +#define I2C_DESC_TYPE_WATCHPORT_CALIBRATION_DATA 0x16 // Watchport Calibration Data + +#define I2C_DESC_TYPE_FIRMWARE_BLANK 0xf2 // Special section defined by ION #define I2C_DESC_TYPE_ION 0 // Not defined by TI @@ -428,7 +598,9 @@ struct ti_i2c_desc __u8 Data[0]; // Data starts here }__attribute__((packed)); -struct ti_i2c_firmware_rec +// for 5152 devices only (type 2 record) +// for 3410 the version is stored in the WATCHPORT_FIRMWARE_VERSION descriptor +struct ti_i2c_firmware_rec { __u8 Ver_Major; // Firmware Major version number __u8 Ver_Minor; // Firmware Minor version number @@ -436,6 +608,14 @@ struct ti_i2c_firmware_rec }__attribute__((packed)); +struct watchport_firmware_version +{ +// Added 2 bytes for version number + __u8 Version_Major; // Download Version (for Watchport) + __u8 Version_Minor; +}__attribute__((packed)); + + // Structure of header of download image in fw_down.h struct ti_i2c_image_header { @@ -461,6 +641,15 @@ struct ti_basic_descriptor } __attribute__((packed)); +// CPU / Board Rev Definitions +#define TI_CPU_REV_5052 2 // 5052 based edgeports +#define TI_CPU_REV_3410 3 // 3410 based edgeports + +#define TI_BOARD_REV_TI_EP 0 // Basic ti based edgeport +#define TI_BOARD_REV_COMPACT 1 // Compact board +#define TI_BOARD_REV_WATCHPORT 2 // Watchport + + #define TI_GET_CPU_REVISION(x) (__u8)((((x)>>4)&0x0f)) #define TI_GET_BOARD_REVISION(x) (__u8)(((x)&0x0f)) @@ -469,20 +658,30 @@ struct ti_basic_descriptor #define TI_MAX_I2C_SIZE ( 16 * 1024 ) -/* TI USB 5052 definitions */ +#define TI_MANUF_VERSION_0 0 + +// IonConig2 flags +#define TI_CONFIG2_RS232 0x01 +#define TI_CONFIG2_RS422 0x02 +#define TI_CONFIG2_RS485 0x04 +#define TI_CONFIG2_SWITCHABLE 0x08 + +#define TI_CONFIG2_WATCHPORT 0x10 + + struct edge_ti_manuf_descriptor { __u8 IonConfig; // Config byte for ION manufacturing use __u8 IonConfig2; // Expansion - __u8 Version; // Verqsion + __u8 Version; // Version __u8 CpuRev_BoardRev; // CPU revision level (0xF0) and Board Rev Level (0x0F) __u8 NumPorts; // Number of ports for this UMP __u8 NumVirtualPorts; // Number of Virtual ports __u8 HubConfig1; // Used to configure the Hub __u8 HubConfig2; // Used to configure the Hub __u8 TotalPorts; // Total Number of Com Ports for the entire device (All UMPs) - __u8 Reserved; + __u8 Reserved; // Reserved }__attribute__((packed)); -#endif // if !defined() +#endif // if !defined(_USBVEND_H) diff --git a/drivers/usb/storage/datafab.h b/drivers/usb/storage/datafab.h index c9343f6e4..32e3f271e 100644 --- a/drivers/usb/storage/datafab.h +++ b/drivers/usb/storage/datafab.h @@ -24,7 +24,7 @@ #ifndef _USB_DATAFAB_MDCFE_B_H #define _USB_DATAFAB_MDCFE_B_H -extern int datafab_transport(Scsi_Cmnd *srb, struct us_data *us); +extern int datafab_transport(struct scsi_cmnd *srb, struct us_data *us); struct datafab_info { unsigned long sectors; // total sector count diff --git a/drivers/usb/storage/debug.c b/drivers/usb/storage/debug.c index d4f278ca7..d76483706 100644 --- a/drivers/usb/storage/debug.c +++ b/drivers/usb/storage/debug.c @@ -44,9 +44,15 @@ * 675 Mass Ave, Cambridge, MA 02139, USA. */ +#include +#include +#include + #include "debug.h" +#include "scsi.h" + -void usb_stor_show_command(Scsi_Cmnd *srb) +void usb_stor_show_command(struct scsi_cmnd *srb) { char *what = NULL; int i; diff --git a/drivers/usb/storage/debug.h b/drivers/usb/storage/debug.h index d5d6b3c76..c4f4bcf36 100644 --- a/drivers/usb/storage/debug.h +++ b/drivers/usb/storage/debug.h @@ -46,13 +46,13 @@ #include #include -#include -#include "usb.h" + +struct scsi_cmnd; #define USB_STORAGE "usb-storage: " #ifdef CONFIG_USB_STORAGE_DEBUG -void usb_stor_show_command(Scsi_Cmnd *srb); +void usb_stor_show_command(struct scsi_cmnd *srb); void usb_stor_show_sense( unsigned char key, unsigned char asc, unsigned char ascq ); #define US_DEBUGP(x...) printk( KERN_DEBUG USB_STORAGE x ) diff --git a/drivers/usb/storage/dpcm.c b/drivers/usb/storage/dpcm.c index ae135fd3c..12c15f19a 100644 --- a/drivers/usb/storage/dpcm.c +++ b/drivers/usb/storage/dpcm.c @@ -30,6 +30,10 @@ */ #include +#include +#include +#include + #include "transport.h" #include "protocol.h" #include "usb.h" @@ -41,7 +45,7 @@ * Transport for the Microtech DPCM-USB * */ -int dpcm_transport(Scsi_Cmnd *srb, struct us_data *us) +int dpcm_transport(struct scsi_cmnd *srb, struct us_data *us) { int ret; diff --git a/drivers/usb/storage/dpcm.h b/drivers/usb/storage/dpcm.h index e6e5637d6..81b464cfc 100644 --- a/drivers/usb/storage/dpcm.h +++ b/drivers/usb/storage/dpcm.h @@ -29,6 +29,6 @@ #ifndef _MICROTECH_DPCM_USB_H #define _MICROTECH_DPCM_USB_H -extern int dpcm_transport(Scsi_Cmnd *srb, struct us_data *us); +extern int dpcm_transport(struct scsi_cmnd *srb, struct us_data *us); #endif diff --git a/drivers/usb/storage/freecom.c b/drivers/usb/storage/freecom.c index 4b1ff0ad2..1dbb6301d 100644 --- a/drivers/usb/storage/freecom.c +++ b/drivers/usb/storage/freecom.c @@ -29,12 +29,16 @@ */ #include +#include + +#include +#include + #include "transport.h" #include "protocol.h" #include "usb.h" #include "debug.h" #include "freecom.h" -#include "linux/hdreg.h" #ifdef CONFIG_USB_STORAGE_DEBUG static void pdump (void *, int); @@ -55,14 +59,14 @@ struct freecom_cb_wrap { struct freecom_xfer_wrap { u8 Type; /* Command type. */ u8 Timeout; /* Timeout in seconds. */ - u32 Count; /* Number of bytes to transfer. */ + __le32 Count; /* Number of bytes to transfer. */ u8 Pad[58]; } __attribute__ ((packed)); struct freecom_ide_out { u8 Type; /* Type + IDE register. */ u8 Pad; - u16 Value; /* Value to write. */ + __le16 Value; /* Value to write. */ u8 Pad2[60]; }; @@ -74,7 +78,7 @@ struct freecom_ide_in { struct freecom_status { u8 Status; u8 Reason; - u16 Count; + __le16 Count; u8 Pad[60]; }; @@ -105,7 +109,7 @@ struct freecom_status { #define FCM_STATUS_PACKET_LENGTH 4 static int -freecom_readdata (Scsi_Cmnd *srb, struct us_data *us, +freecom_readdata (struct scsi_cmnd *srb, struct us_data *us, unsigned int ipipe, unsigned int opipe, int count) { struct freecom_xfer_wrap *fxfr = @@ -139,7 +143,7 @@ freecom_readdata (Scsi_Cmnd *srb, struct us_data *us, } static int -freecom_writedata (Scsi_Cmnd *srb, struct us_data *us, +freecom_writedata (struct scsi_cmnd *srb, struct us_data *us, int unsigned ipipe, unsigned int opipe, int count) { struct freecom_xfer_wrap *fxfr = @@ -176,7 +180,7 @@ freecom_writedata (Scsi_Cmnd *srb, struct us_data *us, * Transport for the Freecom USB/IDE adaptor. * */ -int freecom_transport(Scsi_Cmnd *srb, struct us_data *us) +int freecom_transport(struct scsi_cmnd *srb, struct us_data *us) { struct freecom_cb_wrap *fcb; struct freecom_status *fst; @@ -302,7 +306,7 @@ int freecom_transport(Scsi_Cmnd *srb, struct us_data *us) * move in. */ switch (us->srb->sc_data_direction) { - case SCSI_DATA_READ: + case DMA_FROM_DEVICE: /* catch bogus "read 0 length" case */ if (!length) break; @@ -334,7 +338,7 @@ int freecom_transport(Scsi_Cmnd *srb, struct us_data *us) US_DEBUGP("Transfer happy\n"); break; - case SCSI_DATA_WRITE: + case DMA_TO_DEVICE: /* catch bogus "write 0 length" case */ if (!length) break; @@ -364,7 +368,7 @@ int freecom_transport(Scsi_Cmnd *srb, struct us_data *us) break; - case SCSI_DATA_NONE: + case DMA_NONE: /* Easy, do nothing. */ break; diff --git a/drivers/usb/storage/freecom.h b/drivers/usb/storage/freecom.h index 8c0ada8bb..1b012d62d 100644 --- a/drivers/usb/storage/freecom.h +++ b/drivers/usb/storage/freecom.h @@ -29,7 +29,7 @@ #ifndef _FREECOM_USB_H #define _FREECOM_USB_H -extern int freecom_transport(Scsi_Cmnd *srb, struct us_data *us); +extern int freecom_transport(struct scsi_cmnd *srb, struct us_data *us); extern int usb_stor_freecom_reset(struct us_data *us); extern int freecom_init (struct us_data *us); diff --git a/drivers/usb/storage/isd200.h b/drivers/usb/storage/isd200.h index 70ebe1c39..0a35f4fa7 100644 --- a/drivers/usb/storage/isd200.h +++ b/drivers/usb/storage/isd200.h @@ -25,7 +25,7 @@ #ifndef _USB_ISD200_H #define _USB_ISD200_H -extern void isd200_ata_command(Scsi_Cmnd *srb, struct us_data *us); +extern void isd200_ata_command(struct scsi_cmnd *srb, struct us_data *us); extern int isd200_Initialization(struct us_data *us); #endif diff --git a/drivers/usb/storage/jumpshot.h b/drivers/usb/storage/jumpshot.h index 8147197cd..19bac9d15 100644 --- a/drivers/usb/storage/jumpshot.h +++ b/drivers/usb/storage/jumpshot.h @@ -24,7 +24,7 @@ #ifndef _USB_JUMPSHOT_H #define _USB_JUMPSHOT_H -extern int jumpshot_transport(Scsi_Cmnd *srb, struct us_data *us); +extern int jumpshot_transport(struct scsi_cmnd *srb, struct us_data *us); struct jumpshot_info { unsigned long sectors; // total sector count diff --git a/drivers/usb/storage/protocol.c b/drivers/usb/storage/protocol.c index 1c56f4944..99ed4d9bc 100644 --- a/drivers/usb/storage/protocol.c +++ b/drivers/usb/storage/protocol.c @@ -45,6 +45,8 @@ */ #include +#include +#include #include "protocol.h" #include "usb.h" #include "debug.h" @@ -59,7 +61,7 @@ * Fix-up the return data from an INQUIRY command to show * ANSI SCSI rev 2 so we don't confuse the SCSI layers above us */ -static void fix_inquiry_data(Scsi_Cmnd *srb) +static void fix_inquiry_data(struct scsi_cmnd *srb) { unsigned char databuf[3]; unsigned int index, offset; @@ -91,10 +93,10 @@ static void fix_inquiry_data(Scsi_Cmnd *srb) * Fix-up the return data from a READ CAPACITY command. My Feiya reader * returns a value that is 1 too large. */ -static void fix_read_capacity(Scsi_Cmnd *srb) +static void fix_read_capacity(struct scsi_cmnd *srb) { unsigned int index, offset; - u32 c; + __be32 c; unsigned long capacity; /* verify that it's a READ CAPACITY command */ @@ -120,11 +122,11 @@ static void fix_read_capacity(Scsi_Cmnd *srb) * Protocol routines ***********************************************************************/ -void usb_stor_qic157_command(Scsi_Cmnd *srb, struct us_data *us) +void usb_stor_qic157_command(struct scsi_cmnd *srb, struct us_data *us) { /* Pad the ATAPI command with zeros * - * NOTE: This only works because a Scsi_Cmnd struct field contains + * NOTE: This only works because a scsi_cmnd struct field contains * a unsigned char cmnd[16], so we know we have storage available */ for (; srb->cmd_len<12; srb->cmd_len++) @@ -141,11 +143,11 @@ void usb_stor_qic157_command(Scsi_Cmnd *srb, struct us_data *us) } } -void usb_stor_ATAPI_command(Scsi_Cmnd *srb, struct us_data *us) +void usb_stor_ATAPI_command(struct scsi_cmnd *srb, struct us_data *us) { /* Pad the ATAPI command with zeros * - * NOTE: This only works because a Scsi_Cmnd struct field contains + * NOTE: This only works because a scsi_cmnd struct field contains * a unsigned char cmnd[16], so we know we have storage available */ @@ -166,12 +168,12 @@ void usb_stor_ATAPI_command(Scsi_Cmnd *srb, struct us_data *us) } -void usb_stor_ufi_command(Scsi_Cmnd *srb, struct us_data *us) +void usb_stor_ufi_command(struct scsi_cmnd *srb, struct us_data *us) { /* fix some commands -- this is a form of mode translation * UFI devices only accept 12 byte long commands * - * NOTE: This only works because a Scsi_Cmnd struct field contains + * NOTE: This only works because a scsi_cmnd struct field contains * a unsigned char cmnd[16], so we know we have storage available */ @@ -213,7 +215,8 @@ void usb_stor_ufi_command(Scsi_Cmnd *srb, struct us_data *us) } } -void usb_stor_transparent_scsi_command(Scsi_Cmnd *srb, struct us_data *us) +void usb_stor_transparent_scsi_command(struct scsi_cmnd *srb, + struct us_data *us) { /* send the command to the transport layer */ usb_stor_invoke_transport(srb, us); @@ -241,7 +244,7 @@ void usb_stor_transparent_scsi_command(Scsi_Cmnd *srb, struct us_data *us) * pick up from where this one left off. */ unsigned int usb_stor_access_xfer_buf(unsigned char *buffer, - unsigned int buflen, Scsi_Cmnd *srb, unsigned int *index, + unsigned int buflen, struct scsi_cmnd *srb, unsigned int *index, unsigned int *offset, enum xfer_buf_dir dir) { unsigned int cnt; @@ -327,7 +330,7 @@ unsigned int usb_stor_access_xfer_buf(unsigned char *buffer, /* Store the contents of buffer into srb's transfer buffer and set the * SCSI residue. */ void usb_stor_set_xfer_buf(unsigned char *buffer, - unsigned int buflen, Scsi_Cmnd *srb) + unsigned int buflen, struct scsi_cmnd *srb) { unsigned int index = 0, offset = 0; diff --git a/drivers/usb/storage/protocol.h b/drivers/usb/storage/protocol.h index c1fcb528a..7aa460d3b 100644 --- a/drivers/usb/storage/protocol.h +++ b/drivers/usb/storage/protocol.h @@ -41,9 +41,8 @@ #ifndef _PROTOCOL_H_ #define _PROTOCOL_H_ -#include -#include "scsi.h" -#include "usb.h" +struct scsi_cmnd; +struct us_data; /* Sub Classes */ @@ -60,18 +59,19 @@ #define US_SC_DEVICE 0xff /* Use device's value */ /* Protocol handling routines */ -extern void usb_stor_ATAPI_command(Scsi_Cmnd*, struct us_data*); -extern void usb_stor_qic157_command(Scsi_Cmnd*, struct us_data*); -extern void usb_stor_ufi_command(Scsi_Cmnd*, struct us_data*); -extern void usb_stor_transparent_scsi_command(Scsi_Cmnd*, struct us_data*); +extern void usb_stor_ATAPI_command(struct scsi_cmnd*, struct us_data*); +extern void usb_stor_qic157_command(struct scsi_cmnd*, struct us_data*); +extern void usb_stor_ufi_command(struct scsi_cmnd*, struct us_data*); +extern void usb_stor_transparent_scsi_command(struct scsi_cmnd*, + struct us_data*); -/* Scsi_Cmnd transfer buffer access utilities */ +/* struct scsi_cmnd transfer buffer access utilities */ enum xfer_buf_dir {TO_XFER_BUF, FROM_XFER_BUF}; extern unsigned int usb_stor_access_xfer_buf(unsigned char *buffer, - unsigned int buflen, Scsi_Cmnd *srb, unsigned int *index, + unsigned int buflen, struct scsi_cmnd *srb, unsigned int *index, unsigned int *offset, enum xfer_buf_dir dir); extern void usb_stor_set_xfer_buf(unsigned char *buffer, - unsigned int buflen, Scsi_Cmnd *srb); + unsigned int buflen, struct scsi_cmnd *srb); #endif diff --git a/drivers/usb/storage/sddr09.h b/drivers/usb/storage/sddr09.h index 685123939..c9d78d618 100644 --- a/drivers/usb/storage/sddr09.h +++ b/drivers/usb/storage/sddr09.h @@ -29,7 +29,7 @@ /* Sandisk SDDR-09 stuff */ -extern int sddr09_transport(Scsi_Cmnd *srb, struct us_data *us); +extern int sddr09_transport(struct scsi_cmnd *srb, struct us_data *us); struct sddr09_card_info { unsigned long capacity; /* Size of card in bytes */ diff --git a/drivers/usb/storage/sddr55.c b/drivers/usb/storage/sddr55.c index eb9615318..10270f42a 100644 --- a/drivers/usb/storage/sddr55.c +++ b/drivers/usb/storage/sddr55.c @@ -24,15 +24,19 @@ * 675 Mass Ave, Cambridge, MA 02139, USA. */ +#include +#include +#include + +#include +#include + #include "transport.h" #include "protocol.h" #include "usb.h" #include "debug.h" #include "sddr55.h" -#include -#include -#include #define short_pack(lsb,msb) ( ((u16)(lsb)) | ( ((u16)(msb))<<8 ) ) #define LSB_of(s) ((s)&0xFF) @@ -74,7 +78,7 @@ static int sddr55_bulk_transport(struct us_data *us, int direction, unsigned char *data, unsigned int len) { struct sddr55_card_info *info = (struct sddr55_card_info *)us->extra; - unsigned int pipe = (direction == SCSI_DATA_READ) ? + unsigned int pipe = (direction == DMA_FROM_DEVICE) ? us->recv_bulk_pipe : us->send_bulk_pipe; if (!len) @@ -99,7 +103,7 @@ static int sddr55_status(struct us_data *us) command[5] = 0xB0; command[7] = 0x80; result = sddr55_bulk_transport(us, - SCSI_DATA_WRITE, command, 8); + DMA_TO_DEVICE, command, 8); US_DEBUGP("Result for send_command in status %d\n", result); @@ -110,7 +114,7 @@ static int sddr55_status(struct us_data *us) } result = sddr55_bulk_transport(us, - SCSI_DATA_READ, status, 4); + DMA_FROM_DEVICE, status, 4); /* expect to get short transfer if no card fitted */ if (result == USB_STOR_XFER_SHORT || result == USB_STOR_XFER_STALLED) { @@ -139,7 +143,7 @@ static int sddr55_status(struct us_data *us) /* now read status */ result = sddr55_bulk_transport(us, - SCSI_DATA_READ, status, 2); + DMA_FROM_DEVICE, status, 2); if (result != USB_STOR_XFER_GOOD) { set_sense_info (4, 0, 0); /* hardware error */ @@ -215,7 +219,7 @@ static int sddr55_read_data(struct us_data *us, /* send command */ result = sddr55_bulk_transport(us, - SCSI_DATA_WRITE, command, 8); + DMA_TO_DEVICE, command, 8); US_DEBUGP("Result for send_command in read_data %d\n", result); @@ -227,7 +231,7 @@ static int sddr55_read_data(struct us_data *us, /* read data */ result = sddr55_bulk_transport(us, - SCSI_DATA_READ, buffer, len); + DMA_FROM_DEVICE, buffer, len); if (result != USB_STOR_XFER_GOOD) { result = USB_STOR_TRANSPORT_ERROR; @@ -236,7 +240,7 @@ static int sddr55_read_data(struct us_data *us, /* now read status */ result = sddr55_bulk_transport(us, - SCSI_DATA_READ, status, 2); + DMA_FROM_DEVICE, status, 2); if (result != USB_STOR_XFER_GOOD) { result = USB_STOR_TRANSPORT_ERROR; @@ -390,7 +394,7 @@ static int sddr55_write_data(struct us_data *us, /* send command */ result = sddr55_bulk_transport(us, - SCSI_DATA_WRITE, command, 8); + DMA_TO_DEVICE, command, 8); if (result != USB_STOR_XFER_GOOD) { US_DEBUGP("Result for send_command in write_data %d\n", @@ -404,7 +408,7 @@ static int sddr55_write_data(struct us_data *us, /* send the data */ result = sddr55_bulk_transport(us, - SCSI_DATA_WRITE, buffer, len); + DMA_TO_DEVICE, buffer, len); if (result != USB_STOR_XFER_GOOD) { US_DEBUGP("Result for send_data in write_data %d\n", @@ -417,7 +421,7 @@ static int sddr55_write_data(struct us_data *us, } /* now read status */ - result = sddr55_bulk_transport(us, SCSI_DATA_READ, status, 6); + result = sddr55_bulk_transport(us, DMA_FROM_DEVICE, status, 6); if (result != USB_STOR_XFER_GOOD) { US_DEBUGP("Result for get_status in write_data %d\n", @@ -483,7 +487,7 @@ static int sddr55_read_deviceID(struct us_data *us, memset(command, 0, 8); command[5] = 0xB0; command[7] = 0x84; - result = sddr55_bulk_transport(us, SCSI_DATA_WRITE, command, 8); + result = sddr55_bulk_transport(us, DMA_TO_DEVICE, command, 8); US_DEBUGP("Result of send_control for device ID is %d\n", result); @@ -492,7 +496,7 @@ static int sddr55_read_deviceID(struct us_data *us, return USB_STOR_TRANSPORT_ERROR; result = sddr55_bulk_transport(us, - SCSI_DATA_READ, content, 4); + DMA_FROM_DEVICE, content, 4); if (result != USB_STOR_XFER_GOOD) return USB_STOR_TRANSPORT_ERROR; @@ -502,7 +506,7 @@ static int sddr55_read_deviceID(struct us_data *us, if (content[0] != 0xff) { result = sddr55_bulk_transport(us, - SCSI_DATA_READ, content, 2); + DMA_FROM_DEVICE, content, 2); } return USB_STOR_TRANSPORT_GOOD; @@ -624,21 +628,21 @@ static int sddr55_read_map(struct us_data *us) { command[6] = numblocks * 2 / 256; command[7] = 0x8A; - result = sddr55_bulk_transport(us, SCSI_DATA_WRITE, command, 8); + result = sddr55_bulk_transport(us, DMA_TO_DEVICE, command, 8); if ( result != USB_STOR_XFER_GOOD) { kfree (buffer); return -1; } - result = sddr55_bulk_transport(us, SCSI_DATA_READ, buffer, numblocks * 2); + result = sddr55_bulk_transport(us, DMA_FROM_DEVICE, buffer, numblocks * 2); if ( result != USB_STOR_XFER_GOOD) { kfree (buffer); return -1; } - result = sddr55_bulk_transport(us, SCSI_DATA_READ, command, 2); + result = sddr55_bulk_transport(us, DMA_FROM_DEVICE, command, 2); if ( result != USB_STOR_XFER_GOOD) { kfree (buffer); @@ -734,7 +738,7 @@ static void sddr55_card_info_destructor(void *extra) { /* * Transport for the Sandisk SDDR-55 */ -int sddr55_transport(Scsi_Cmnd *srb, struct us_data *us) +int sddr55_transport(struct scsi_cmnd *srb, struct us_data *us) { int result; static unsigned char inquiry_response[8] = { @@ -834,8 +838,8 @@ int sddr55_transport(Scsi_Cmnd *srb, struct us_data *us) capacity /= PAGESIZE; capacity--; - ((u32 *) ptr)[0] = cpu_to_be32(capacity); - ((u32 *) ptr)[1] = cpu_to_be32(PAGESIZE); + ((__be32 *) ptr)[0] = cpu_to_be32(capacity); + ((__be32 *) ptr)[1] = cpu_to_be32(PAGESIZE); usb_stor_set_xfer_buf(ptr, 8, srb); sddr55_read_map(us); diff --git a/drivers/usb/storage/sddr55.h b/drivers/usb/storage/sddr55.h index 6dc0da6d2..d6bd32f6c 100644 --- a/drivers/usb/storage/sddr55.h +++ b/drivers/usb/storage/sddr55.h @@ -28,7 +28,7 @@ /* Sandisk SDDR-55 stuff */ -extern int sddr55_transport(Scsi_Cmnd *srb, struct us_data *us); +extern int sddr55_transport(struct scsi_cmnd *srb, struct us_data *us); extern int sddr55_reset(struct us_data *us); #endif diff --git a/drivers/usb/storage/transport.h b/drivers/usb/storage/transport.h index 123b041ab..153efd623 100644 --- a/drivers/usb/storage/transport.h +++ b/drivers/usb/storage/transport.h @@ -44,7 +44,8 @@ #include #include #include "usb.h" -#include "scsi.h" + +struct scsi_cmnd; /* Protocols */ @@ -82,9 +83,9 @@ /* command block wrapper */ struct bulk_cb_wrap { - __u32 Signature; /* contains 'USBC' */ + __le32 Signature; /* contains 'USBC' */ __u32 Tag; /* unique per command id */ - __u32 DataTransferLength; /* size of data */ + __le32 DataTransferLength; /* size of data */ __u8 Flags; /* direction in bit 0 */ __u8 Lun; /* LUN normally 0 */ __u8 Length; /* of of the CDB */ @@ -98,9 +99,9 @@ struct bulk_cb_wrap { /* command status wrapper */ struct bulk_cs_wrap { - __u32 Signature; /* should = 'USBS' */ + __le32 Signature; /* should = 'USBS' */ __u32 Tag; /* same as original command */ - __u32 Residue; /* amount not transferred */ + __le32 Residue; /* amount not transferred */ __u8 Status; /* see below */ __u8 Filler[18]; }; @@ -150,16 +151,16 @@ struct bulk_cs_wrap { #define US_CBI_ADSC 0 -extern int usb_stor_CBI_transport(Scsi_Cmnd*, struct us_data*); +extern int usb_stor_CBI_transport(struct scsi_cmnd *, struct us_data*); -extern int usb_stor_CB_transport(Scsi_Cmnd*, struct us_data*); +extern int usb_stor_CB_transport(struct scsi_cmnd *, struct us_data*); extern int usb_stor_CB_reset(struct us_data*); -extern int usb_stor_Bulk_transport(Scsi_Cmnd*, struct us_data*); +extern int usb_stor_Bulk_transport(struct scsi_cmnd *, struct us_data*); extern int usb_stor_Bulk_max_lun(struct us_data*); extern int usb_stor_Bulk_reset(struct us_data*); -extern void usb_stor_invoke_transport(Scsi_Cmnd*, struct us_data*); +extern void usb_stor_invoke_transport(struct scsi_cmnd *, struct us_data*); extern void usb_stor_stop_transport(struct us_data*); extern int usb_stor_control_msg(struct us_data *us, unsigned int pipe, diff --git a/drivers/usb/usb-skeleton.c b/drivers/usb/usb-skeleton.c index f893cc078..d3a3fd9b1 100644 --- a/drivers/usb/usb-skeleton.c +++ b/drivers/usb/usb-skeleton.c @@ -1,42 +1,15 @@ /* - * USB Skeleton driver - 1.1 + * USB Skeleton driver - 2.0 * - * Copyright (C) 2001-2003 Greg Kroah-Hartman (greg@kroah.com) + * Copyright (C) 2001-2004 Greg Kroah-Hartman (greg@kroah.com) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation, version 2. * - * - * This driver is to be used as a skeleton driver to be able to create a - * USB driver quickly. The design of it is based on the usb-serial and - * dc2xx drivers. - * - * Thanks to Oliver Neukum, David Brownell, and Alan Stern for their help - * in debugging this driver. - * - * - * History: - * - * 2003-05-06 - 1.1 - changes due to usb core changes with usb_register_dev() - * 2003-02-25 - 1.0 - fix races involving urb->status, unlink_urb(), and - * disconnect. Fix transfer amount in read(). Use - * macros instead of magic numbers in probe(). Change - * size variables to size_t. Show how to eliminate - * DMA bounce buffer. - * 2002_12_12 - 0.9 - compile fixes and got rid of fixed minor array. - * 2002_09_26 - 0.8 - changes due to USB core conversion to struct device - * driver. - * 2002_02_12 - 0.7 - zero out dev in probe function for devices that do - * not have both a bulk in and bulk out endpoint. - * Thanks to Holger Waechtler for the fix. - * 2001_11_05 - 0.6 - fix minor locking problem in skel_disconnect. - * Thanks to Pete Zaitcev for the fix. - * 2001_09_04 - 0.5 - fix devfs bug in skel_disconnect. Thanks to wim delvaux - * 2001_08_21 - 0.4 - more small bug fixes. - * 2001_05_29 - 0.3 - more bug fixes based on review from linux-usb-devel - * 2001_05_24 - 0.2 - bug fixes based on review from linux-usb-devel people - * 2001_05_01 - 0.1 - first version + * This driver is based on the 2.6.3 version of drivers/usb/usb-skeleton.c + * but has been rewritten to be easy to read and use, as no locks are now + * needed anymore. * */ @@ -46,31 +19,10 @@ #include #include #include -#include -#include +#include #include #include -#ifdef CONFIG_USB_DEBUG - static int debug = 1; -#else - static int debug; -#endif - -/* Use our own dbg macro */ -#undef dbg -#define dbg(format, arg...) do { if (debug) printk(KERN_DEBUG __FILE__ ": " format "\n" , ## arg); } while (0) - - -/* Version Information */ -#define DRIVER_VERSION "v1.0" -#define DRIVER_AUTHOR "Greg Kroah-Hartman, greg@kroah.com" -#define DRIVER_DESC "USB Skeleton Driver" - -/* Module parameters */ -MODULE_PARM(debug, "i"); -MODULE_PARM_DESC(debug, "Debug enabled or not"); - /* Define these values to match your devices */ #define USB_SKEL_VENDOR_ID 0xfff0 @@ -79,11 +31,8 @@ MODULE_PARM_DESC(debug, "Debug enabled or not"); /* table of devices that work with this driver */ static struct usb_device_id skel_table [] = { { USB_DEVICE(USB_SKEL_VENDOR_ID, USB_SKEL_PRODUCT_ID) }, - /* "Gadget Zero" firmware runs under Linux */ - { USB_DEVICE(0x0525, 0xa4a0) }, { } /* Terminating entry */ }; - MODULE_DEVICE_TABLE (usb, skel_table); @@ -92,413 +41,195 @@ MODULE_DEVICE_TABLE (usb, skel_table); /* Structure to hold all of our device specific stuff */ struct usb_skel { - struct usb_device * udev; /* save off the usb device pointer */ + struct usb_device * udev; /* the usb device for this device */ struct usb_interface * interface; /* the interface for this device */ - unsigned char minor; /* the starting minor number for this device */ - unsigned char num_ports; /* the number of ports this device has */ - char num_interrupt_in; /* number of interrupt in endpoints we have */ - char num_bulk_in; /* number of bulk in endpoints we have */ - char num_bulk_out; /* number of bulk out endpoints we have */ - unsigned char * bulk_in_buffer; /* the buffer to receive data */ size_t bulk_in_size; /* the size of the receive buffer */ __u8 bulk_in_endpointAddr; /* the address of the bulk in endpoint */ - - unsigned char * bulk_out_buffer; /* the buffer to send data */ - size_t bulk_out_size; /* the size of the send buffer */ - struct urb * write_urb; /* the urb used to send data */ __u8 bulk_out_endpointAddr; /* the address of the bulk out endpoint */ - atomic_t write_busy; /* true iff write urb is busy */ - struct completion write_finished; /* wait for the write to finish */ - - int open; /* if the port is open or not */ - int present; /* if the device is not disconnected */ - struct semaphore sem; /* locks this structure */ -}; - - -/* prevent races between open() and disconnect() */ -static DECLARE_MUTEX (disconnect_sem); - -/* local function prototypes */ -static ssize_t skel_read (struct file *file, char *buffer, size_t count, loff_t *ppos); -static ssize_t skel_write (struct file *file, const char *buffer, size_t count, loff_t *ppos); -static int skel_ioctl (struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg); -static int skel_open (struct inode *inode, struct file *file); -static int skel_release (struct inode *inode, struct file *file); - -static int skel_probe (struct usb_interface *interface, const struct usb_device_id *id); -static void skel_disconnect (struct usb_interface *interface); - -static void skel_write_bulk_callback (struct urb *urb, struct pt_regs *regs); - -/* - * File operations needed when we register this driver. - * This assumes that this driver NEEDS file operations, - * of course, which means that the driver is expected - * to have a node in the /dev directory. If the USB - * device were for a network interface then the driver - * would use "struct net_driver" instead, and a serial - * device would use "struct tty_driver". - */ -static struct file_operations skel_fops = { - /* - * The owner field is part of the module-locking - * mechanism. The idea is that the kernel knows - * which module to increment the use-counter of - * BEFORE it calls the device's open() function. - * This also means that the kernel can decrement - * the use-counter again before calling release() - * or should the open() function fail. - */ - .owner = THIS_MODULE, - - .read = skel_read, - .write = skel_write, - .ioctl = skel_ioctl, - .open = skel_open, - .release = skel_release, -}; - -/* - * usb class driver info in order to get a minor number from the usb core, - * and to have the device registered with devfs and the driver core - */ -static struct usb_class_driver skel_class = { - .name = "usb/skel%d", - .fops = &skel_fops, - .mode = S_IFCHR | S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH, - .minor_base = USB_SKEL_MINOR_BASE, -}; - -/* usb specific object needed to register this driver with the usb subsystem */ -static struct usb_driver skel_driver = { - .owner = THIS_MODULE, - .name = "skeleton", - .probe = skel_probe, - .disconnect = skel_disconnect, - .id_table = skel_table, + struct kref kref; }; +#define to_skel_dev(d) container_of(d, struct usb_skel, kref) +static struct usb_driver skel_driver; -/** - * usb_skel_debug_data - */ -static inline void usb_skel_debug_data (const char *function, int size, const unsigned char *data) -{ - int i; +static void skel_delete(struct kref *kref) +{ + struct usb_skel *dev = to_skel_dev(kref); - if (!debug) - return; - - printk (KERN_DEBUG __FILE__": %s - length = %d, data = ", - function, size); - for (i = 0; i < size; ++i) { - printk ("%.2x ", data[i]); - } - printk ("\n"); -} - - -/** - * skel_delete - */ -static inline void skel_delete (struct usb_skel *dev) -{ + usb_put_dev(dev->udev); kfree (dev->bulk_in_buffer); - usb_buffer_free (dev->udev, dev->bulk_out_size, - dev->bulk_out_buffer, - dev->write_urb->transfer_dma); - usb_free_urb (dev->write_urb); kfree (dev); } - -/** - * skel_open - */ -static int skel_open (struct inode *inode, struct file *file) +static int skel_open(struct inode *inode, struct file *file) { - struct usb_skel *dev = NULL; + struct usb_skel *dev; struct usb_interface *interface; int subminor; int retval = 0; - dbg("%s", __FUNCTION__); - subminor = iminor(inode); - /* prevent disconnects */ - down (&disconnect_sem); - - interface = usb_find_interface (&skel_driver, subminor); + interface = usb_find_interface(&skel_driver, subminor); if (!interface) { err ("%s - error, can't find device for minor %d", __FUNCTION__, subminor); retval = -ENODEV; - goto exit_no_device; + goto exit; } dev = usb_get_intfdata(interface); if (!dev) { retval = -ENODEV; - goto exit_no_device; + goto exit; } - /* lock this device */ - down (&dev->sem); - - /* increment our usage count for the driver */ - ++dev->open; + /* increment our usage count for the device */ + kref_get(&dev->kref); /* save our object in the file's private structure */ file->private_data = dev; - /* unlock this device */ - up (&dev->sem); - -exit_no_device: - up (&disconnect_sem); +exit: return retval; } - -/** - * skel_release - */ -static int skel_release (struct inode *inode, struct file *file) +static int skel_release(struct inode *inode, struct file *file) { struct usb_skel *dev; - int retval = 0; dev = (struct usb_skel *)file->private_data; - if (dev == NULL) { - dbg ("%s - object is NULL", __FUNCTION__); + if (dev == NULL) return -ENODEV; - } - dbg("%s - minor %d", __FUNCTION__, dev->minor); - - /* lock our device */ - down (&dev->sem); - - if (dev->open <= 0) { - dbg ("%s - device not opened", __FUNCTION__); - retval = -ENODEV; - goto exit_not_opened; - } - - /* wait for any bulk writes that might be going on to finish up */ - if (atomic_read (&dev->write_busy)) - wait_for_completion (&dev->write_finished); - - --dev->open; - - if (!dev->present && !dev->open) { - /* the device was unplugged before the file was released */ - up (&dev->sem); - skel_delete (dev); - return 0; - } - -exit_not_opened: - up (&dev->sem); - - return retval; + /* decrement the count on our device */ + kref_put(&dev->kref, skel_delete); + return 0; } - -/** - * skel_read - */ -static ssize_t skel_read (struct file *file, char *buffer, size_t count, loff_t *ppos) +static ssize_t skel_read(struct file *file, char *buffer, size_t count, loff_t *ppos) { struct usb_skel *dev; int retval = 0; dev = (struct usb_skel *)file->private_data; - - dbg("%s - minor %d, count = %d", __FUNCTION__, dev->minor, count); - - /* lock this object */ - down (&dev->sem); - - /* verify that the device wasn't unplugged */ - if (!dev->present) { - up (&dev->sem); - return -ENODEV; - } - + /* do a blocking bulk read to get data from the device */ - retval = usb_bulk_msg (dev->udev, - usb_rcvbulkpipe (dev->udev, - dev->bulk_in_endpointAddr), - dev->bulk_in_buffer, - min (dev->bulk_in_size, count), - &count, HZ*10); + retval = usb_bulk_msg(dev->udev, + usb_rcvbulkpipe(dev->udev, dev->bulk_in_endpointAddr), + dev->bulk_in_buffer, + min(dev->bulk_in_size, count), + &count, HZ*10); /* if the read was successful, copy the data to userspace */ if (!retval) { - if (copy_to_user (buffer, dev->bulk_in_buffer, count)) + if (copy_to_user(buffer, dev->bulk_in_buffer, count)) retval = -EFAULT; else retval = count; } - /* unlock the device */ - up (&dev->sem); return retval; } - -/** - * skel_write - * - * A device driver has to decide how to report I/O errors back to the - * user. The safest course is to wait for the transfer to finish before - * returning so that any errors will be reported reliably. skel_read() - * works like this. But waiting for I/O is slow, so many drivers only - * check for errors during I/O initiation and do not report problems - * that occur during the actual transfer. That's what we will do here. - * - * A driver concerned with maximum I/O throughput would use double- - * buffering: Two urbs would be devoted to write transfers, so that - * one urb could always be active while the other was waiting for the - * user to send more data. - */ -static ssize_t skel_write (struct file *file, const char *buffer, size_t count, loff_t *ppos) +static void skel_write_bulk_callback(struct urb *urb, struct pt_regs *regs) { struct usb_skel *dev; - ssize_t bytes_written = 0; - int retval = 0; - dev = (struct usb_skel *)file->private_data; + dev = (struct usb_skel *)urb->context; - dbg("%s - minor %d, count = %d", __FUNCTION__, dev->minor, count); + /* sync/async unlink faults aren't errors */ + if (urb->status && + !(urb->status == -ENOENT || + urb->status == -ECONNRESET || + urb->status == -ESHUTDOWN)) { + dbg("%s - nonzero write bulk status received: %d", + __FUNCTION__, urb->status); + } - /* lock this object */ - down (&dev->sem); + /* free up our allocated buffer */ + usb_buffer_free(urb->dev, urb->transfer_buffer_length, + urb->transfer_buffer, urb->transfer_dma); +} - /* verify that the device wasn't unplugged */ - if (!dev->present) { - retval = -ENODEV; - goto exit; - } +static ssize_t skel_write(struct file *file, const char *user_buffer, size_t count, loff_t *ppos) +{ + struct usb_skel *dev; + int retval = 0; + struct urb *urb = NULL; + char *buf = NULL; + + dev = (struct usb_skel *)file->private_data; /* verify that we actually have some data to write */ - if (count == 0) { - dbg("%s - write request of 0 bytes", __FUNCTION__); + if (count == 0) goto exit; - } - /* wait for a previous write to finish up; we don't use a timeout - * and so a nonresponsive device can delay us indefinitely. - */ - if (atomic_read (&dev->write_busy)) - wait_for_completion (&dev->write_finished); + /* create a urb, and a buffer for it, and copy the data to the urb */ + urb = usb_alloc_urb(0, GFP_KERNEL); + if (!urb) { + retval = -ENOMEM; + goto error; + } - /* we can only write as much as our buffer will hold */ - bytes_written = min (dev->bulk_out_size, count); + buf = usb_buffer_alloc(dev->udev, count, GFP_KERNEL, &urb->transfer_dma); + if (!buf) { + retval = -ENOMEM; + goto error; + } - /* copy the data from userspace into our transfer buffer; - * this is the only copy required. - */ - if (copy_from_user(dev->write_urb->transfer_buffer, buffer, - bytes_written)) { + if (copy_from_user(buf, user_buffer, count)) { retval = -EFAULT; - goto exit; + goto error; } - usb_skel_debug_data (__FUNCTION__, bytes_written, - dev->write_urb->transfer_buffer); - - /* this urb was already set up, except for this write size */ - dev->write_urb->transfer_buffer_length = bytes_written; + /* initialize the urb properly */ + usb_fill_bulk_urb(urb, dev->udev, + usb_sndbulkpipe(dev->udev, dev->bulk_out_endpointAddr), + buf, count, skel_write_bulk_callback, dev); + urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; /* send the data out the bulk port */ - /* a character device write uses GFP_KERNEL, - unless a spinlock is held */ - init_completion (&dev->write_finished); - atomic_set (&dev->write_busy, 1); - retval = usb_submit_urb(dev->write_urb, GFP_KERNEL); + retval = usb_submit_urb(urb, GFP_KERNEL); if (retval) { - atomic_set (&dev->write_busy, 0); - err("%s - failed submitting write urb, error %d", - __FUNCTION__, retval); - } else { - retval = bytes_written; + err("%s - failed submitting write urb, error %d", __FUNCTION__, retval); + goto error; } + /* release our reference to this urb, the USB core will eventually free it entirely */ + usb_free_urb(urb); + exit: - /* unlock the device */ - up (&dev->sem); + return count; +error: + usb_buffer_free(dev->udev, count, buf, urb->transfer_dma); + usb_free_urb(urb); return retval; } +static struct file_operations skel_fops = { + .owner = THIS_MODULE, + .read = skel_read, + .write = skel_write, + .open = skel_open, + .release = skel_release, +}; -/** - * skel_ioctl - */ -static int skel_ioctl (struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) -{ - struct usb_skel *dev; - - dev = (struct usb_skel *)file->private_data; - - /* lock this object */ - down (&dev->sem); - - /* verify that the device wasn't unplugged */ - if (!dev->present) { - up (&dev->sem); - return -ENODEV; - } - - dbg("%s - minor %d, cmd 0x%.4x, arg %ld", __FUNCTION__, - dev->minor, cmd, arg); - - /* fill in your device specific stuff here */ - - /* unlock the device */ - up (&dev->sem); - - /* return that we did not understand this ioctl call */ - return -ENOTTY; -} - - -/** - * skel_write_bulk_callback +/* + * usb class driver info in order to get a minor number from the usb core, + * and to have the device registered with devfs and the driver core */ -static void skel_write_bulk_callback (struct urb *urb, struct pt_regs *regs) -{ - struct usb_skel *dev = (struct usb_skel *)urb->context; - - dbg("%s - minor %d", __FUNCTION__, dev->minor); - - /* sync/async unlink faults aren't errors */ - if (urb->status && !(urb->status == -ENOENT || - urb->status == -ECONNRESET)) { - dbg("%s - nonzero write bulk status received: %d", - __FUNCTION__, urb->status); - } - - /* notify anyone waiting that the write has finished */ - atomic_set (&dev->write_busy, 0); - complete (&dev->write_finished); -} - +static struct usb_class_driver skel_class = { + .name = "usb/skel%d", + .fops = &skel_fops, + .mode = S_IFCHR | S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH, + .minor_base = USB_SKEL_MINOR_BASE, +}; -/** - * skel_probe - * - * Called by the usb core when a new device is connected that it thinks - * this driver might be interested in. - */ static int skel_probe(struct usb_interface *interface, const struct usb_device_id *id) { - struct usb_device *udev = interface_to_usbdev(interface); struct usb_skel *dev = NULL; struct usb_host_interface *iface_desc; struct usb_endpoint_descriptor *endpoint; @@ -506,28 +237,21 @@ static int skel_probe(struct usb_interface *interface, const struct usb_device_i int i; int retval = -ENOMEM; - /* See if the device offered us matches what we can accept */ - if ((udev->descriptor.idVendor != USB_SKEL_VENDOR_ID) || - (udev->descriptor.idProduct != USB_SKEL_PRODUCT_ID)) { - return -ENODEV; - } - /* allocate memory for our device state and initialize it */ - dev = kmalloc (sizeof(struct usb_skel), GFP_KERNEL); + dev = kmalloc(sizeof(*dev), GFP_KERNEL); if (dev == NULL) { - err ("Out of memory"); - return -ENOMEM; + err("Out of memory"); + goto error; } - memset (dev, 0x00, sizeof (*dev)); + memset(dev, 0x00, sizeof(*dev)); + kref_init(&dev->kref); - init_MUTEX (&dev->sem); - dev->udev = udev; + dev->udev = usb_get_dev(interface_to_usbdev(interface)); dev->interface = interface; /* set up the endpoint information */ - /* check out the endpoints */ /* use only the first bulk-in and bulk-out endpoints */ - iface_desc = &interface->altsetting[0]; + iface_desc = interface->cur_altsetting; for (i = 0; i < iface_desc->desc.bNumEndpoints; ++i) { endpoint = &iface_desc->endpoint[i].desc; @@ -539,9 +263,9 @@ static int skel_probe(struct usb_interface *interface, const struct usb_device_i buffer_size = endpoint->wMaxPacketSize; dev->bulk_in_size = buffer_size; dev->bulk_in_endpointAddr = endpoint->bEndpointAddress; - dev->bulk_in_buffer = kmalloc (buffer_size, GFP_KERNEL); + dev->bulk_in_buffer = kmalloc(buffer_size, GFP_KERNEL); if (!dev->bulk_in_buffer) { - err("Couldn't allocate bulk_in_buffer"); + err("Could not allocate bulk_in_buffer"); goto error; } } @@ -551,153 +275,85 @@ static int skel_probe(struct usb_interface *interface, const struct usb_device_i ((endpoint->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) == USB_ENDPOINT_XFER_BULK)) { /* we found a bulk out endpoint */ - /* a probe() may sleep and has no restrictions on memory allocations */ - dev->write_urb = usb_alloc_urb(0, GFP_KERNEL); - if (!dev->write_urb) { - err("No free urbs available"); - goto error; - } dev->bulk_out_endpointAddr = endpoint->bEndpointAddress; - - /* on some platforms using this kind of buffer alloc - * call eliminates a dma "bounce buffer". - * - * NOTE: you'd normally want i/o buffers that hold - * more than one packet, so that i/o delays between - * packets don't hurt throughput. - */ - buffer_size = endpoint->wMaxPacketSize; - dev->bulk_out_size = buffer_size; - dev->write_urb->transfer_flags = (URB_NO_TRANSFER_DMA_MAP | - URB_ASYNC_UNLINK); - dev->bulk_out_buffer = usb_buffer_alloc (udev, - buffer_size, GFP_KERNEL, - &dev->write_urb->transfer_dma); - if (!dev->bulk_out_buffer) { - err("Couldn't allocate bulk_out_buffer"); - goto error; - } - usb_fill_bulk_urb(dev->write_urb, udev, - usb_sndbulkpipe(udev, - endpoint->bEndpointAddress), - dev->bulk_out_buffer, buffer_size, - skel_write_bulk_callback, dev); } } if (!(dev->bulk_in_endpointAddr && dev->bulk_out_endpointAddr)) { - err("Couldn't find both bulk-in and bulk-out endpoints"); + err("Could not find both bulk-in and bulk-out endpoints"); goto error; } - /* allow device read, write and ioctl */ - dev->present = 1; + /* save our data pointer in this interface device */ + usb_set_intfdata(interface, dev); /* we can register the device now, as it is ready */ - usb_set_intfdata (interface, dev); - retval = usb_register_dev (interface, &skel_class); + retval = usb_register_dev(interface, &skel_class); if (retval) { /* something prevented us from registering this driver */ - err ("Not able to get a minor for this device."); - usb_set_intfdata (interface, NULL); + err("Not able to get a minor for this device."); + usb_set_intfdata(interface, NULL); goto error; } - dev->minor = interface->minor; - /* let the user know what node this device is now attached to */ - info ("USB Skeleton device now attached to USBSkel-%d", dev->minor); + info("USB Skeleton device now attached to USBSkel-%d", interface->minor); return 0; error: - skel_delete (dev); + if (dev) + kref_put(&dev->kref, skel_delete); return retval; } - -/** - * skel_disconnect - * - * Called by the usb core when the device is removed from the system. - * - * This routine guarantees that the driver will not submit any more urbs - * by clearing dev->udev. It is also supposed to terminate any currently - * active urbs. Unfortunately, usb_bulk_msg(), used in skel_read(), does - * not provide any way to do this. But at least we can cancel an active - * write. - */ static void skel_disconnect(struct usb_interface *interface) { struct usb_skel *dev; - int minor; + int minor = interface->minor; - /* prevent races with open() */ - down (&disconnect_sem); + /* prevent skel_open() from racing skel_disconnect() */ + lock_kernel(); - dev = usb_get_intfdata (interface); - usb_set_intfdata (interface, NULL); - - down (&dev->sem); - - minor = dev->minor; + dev = usb_get_intfdata(interface); + usb_set_intfdata(interface, NULL); /* give back our minor */ - usb_deregister_dev (interface, &skel_class); + usb_deregister_dev(interface, &skel_class); - /* terminate an ongoing write */ - if (atomic_read (&dev->write_busy)) { - usb_unlink_urb (dev->write_urb); - wait_for_completion (&dev->write_finished); - } - - /* prevent device read, write and ioctl */ - dev->present = 0; - - up (&dev->sem); + unlock_kernel(); - /* if the device is opened, skel_release will clean this up */ - if (!dev->open) - skel_delete (dev); - - up (&disconnect_sem); + /* decrement our usage count */ + kref_put(&dev->kref, skel_delete); info("USB Skeleton #%d now disconnected", minor); } +static struct usb_driver skel_driver = { + .owner = THIS_MODULE, + .name = "skeleton", + .probe = skel_probe, + .disconnect = skel_disconnect, + .id_table = skel_table, +}; - -/** - * usb_skel_init - */ static int __init usb_skel_init(void) { int result; /* register this driver with the USB subsystem */ result = usb_register(&skel_driver); - if (result) { - err("usb_register failed. Error number %d", - result); - return result; - } + if (result) + err("usb_register failed. Error number %d", result); - info(DRIVER_DESC " " DRIVER_VERSION); - return 0; + return result; } - -/** - * usb_skel_exit - */ static void __exit usb_skel_exit(void) { /* deregister this driver with the USB subsystem */ usb_deregister(&skel_driver); } - module_init (usb_skel_init); module_exit (usb_skel_exit); -MODULE_AUTHOR(DRIVER_AUTHOR); -MODULE_DESCRIPTION(DRIVER_DESC); MODULE_LICENSE("GPL"); diff --git a/drivers/video/amba-clcd.c b/drivers/video/amba-clcd.c new file mode 100644 index 000000000..d6af37b7a --- /dev/null +++ b/drivers/video/amba-clcd.c @@ -0,0 +1,515 @@ +/* + * linux/drivers/video/amba-clcd.c + * + * Copyright (C) 2001 ARM Limited, by David A Rusling + * Updated to 2.5, Deep Blue Solutions Ltd. + * + * 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. + * + * ARM PrimeCell PL110 Color LCD Controller + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include + +#define to_clcd(info) container_of(info, struct clcd_fb, fb) + +/* This is limited to 16 characters when displayed by X startup */ +static const char *clcd_name = "CLCD FB"; + +/* + * Unfortunately, the enable/disable functions may be called either from + * process or IRQ context, and we _need_ to delay. This is _not_ good. + */ +static inline void clcdfb_sleep(unsigned int ms) +{ + if (in_atomic()) { + mdelay(ms); + } else { + msleep(ms); + } +} + +static inline void clcdfb_set_start(struct clcd_fb *fb) +{ + unsigned long ustart = fb->fb.fix.smem_start; + unsigned long lstart; + + ustart += fb->fb.var.yoffset * fb->fb.fix.line_length; + lstart = ustart + fb->fb.var.yres * fb->fb.fix.line_length / 2; + + writel(ustart, fb->regs + CLCD_UBAS); + writel(lstart, fb->regs + CLCD_LBAS); +} + +static void clcdfb_disable(struct clcd_fb *fb) +{ + u32 val; + + if (fb->board->disable) + fb->board->disable(fb); + + val = readl(fb->regs + CLCD_CNTL); + if (val & CNTL_LCDPWR) { + val &= ~CNTL_LCDPWR; + writel(val, fb->regs + CLCD_CNTL); + + clcdfb_sleep(20); + } + if (val & CNTL_LCDEN) { + val &= ~CNTL_LCDEN; + writel(val, fb->regs + CLCD_CNTL); + } + + /* + * Disable CLCD clock source. + */ + clk_disable(fb->clk); +} + +static void clcdfb_enable(struct clcd_fb *fb, u32 cntl) +{ + /* + * Enable the CLCD clock source. + */ + clk_enable(fb->clk); + + /* + * Bring up by first enabling.. + */ + cntl |= CNTL_LCDEN; + writel(cntl, fb->regs + CLCD_CNTL); + + clcdfb_sleep(20); + + /* + * and now apply power. + */ + cntl |= CNTL_LCDPWR; + writel(cntl, fb->regs + CLCD_CNTL); + + /* + * finally, enable the interface. + */ + if (fb->board->enable) + fb->board->enable(fb); +} + +static int +clcdfb_set_bitfields(struct clcd_fb *fb, struct fb_var_screeninfo *var) +{ + int ret = 0; + + memset(&var->transp, 0, sizeof(var->transp)); + memset(&var->red, 0, sizeof(var->red)); + memset(&var->green, 0, sizeof(var->green)); + memset(&var->blue, 0, sizeof(var->blue)); + + switch (var->bits_per_pixel) { + case 1: + case 2: + case 4: + case 8: + var->red.length = 8; + var->red.offset = 0; + var->green.length = 8; + var->green.offset = 0; + var->blue.length = 8; + var->blue.offset = 0; + break; + case 16: + var->red.length = 5; + var->green.length = 5; + var->blue.length = 5; + if (fb->panel->cntl & CNTL_BGR) { + var->red.offset = 10; + var->green.offset = 5; + var->blue.offset = 0; + } else { + var->red.offset = 0; + var->green.offset = 5; + var->blue.offset = 10; + } + break; + case 24: + if (fb->panel->cntl & CNTL_LCDTFT) { + var->red.length = 8; + var->green.length = 8; + var->blue.length = 8; + + if (fb->panel->cntl & CNTL_BGR) { + var->red.offset = 16; + var->green.offset = 8; + var->blue.offset = 0; + } else { + var->red.offset = 0; + var->green.offset = 8; + var->blue.offset = 16; + } + break; + } + default: + ret = -EINVAL; + break; + } + + return ret; +} + +static int clcdfb_check_var(struct fb_var_screeninfo *var, struct fb_info *info) +{ + struct clcd_fb *fb = to_clcd(info); + int ret = -EINVAL; + + if (fb->board->check) + ret = fb->board->check(fb, var); + if (ret == 0) + ret = clcdfb_set_bitfields(fb, var); + + return ret; +} + +static int clcdfb_set_par(struct fb_info *info) +{ + struct clcd_fb *fb = to_clcd(info); + struct clcd_regs regs; + + fb->fb.fix.line_length = fb->fb.var.xres_virtual * + fb->fb.var.bits_per_pixel / 8; + + if (fb->fb.var.bits_per_pixel <= 8) + fb->fb.fix.visual = FB_VISUAL_PSEUDOCOLOR; + else + fb->fb.fix.visual = FB_VISUAL_TRUECOLOR; + + fb->board->decode(fb, ®s); + + clcdfb_disable(fb); + + writel(regs.tim0, fb->regs + CLCD_TIM0); + writel(regs.tim1, fb->regs + CLCD_TIM1); + writel(regs.tim2, fb->regs + CLCD_TIM2); + writel(regs.tim3, fb->regs + CLCD_TIM3); + + clcdfb_set_start(fb); + + clk_set_rate(fb->clk, (1000000000 / regs.pixclock) * 1000); + + fb->clcd_cntl = regs.cntl; + + clcdfb_enable(fb, regs.cntl); + +#ifdef DEBUG + printk(KERN_INFO "CLCD: Registers set to\n" + KERN_INFO " %08x %08x %08x %08x\n" + KERN_INFO " %08x %08x %08x %08x\n", + readl(fb->regs + CLCD_TIM0), readl(fb->regs + CLCD_TIM1), + readl(fb->regs + CLCD_TIM2), readl(fb->regs + CLCD_TIM3), + readl(fb->regs + CLCD_UBAS), readl(fb->regs + CLCD_LBAS), + readl(fb->regs + CLCD_IENB), readl(fb->regs + CLCD_CNTL)); +#endif + + return 0; +} + +static inline u32 convert_bitfield(int val, struct fb_bitfield *bf) +{ + unsigned int mask = (1 << bf->length) - 1; + + return (val >> (16 - bf->length) & mask) << bf->offset; +} + +/* + * Set a single color register. The values supplied have a 16 bit + * magnitude. Return != 0 for invalid regno. + */ +static int +clcdfb_setcolreg(unsigned int regno, unsigned int red, unsigned int green, + unsigned int blue, unsigned int transp, struct fb_info *info) +{ + struct clcd_fb *fb = to_clcd(info); + + if (regno < 16) + fb->cmap[regno] = convert_bitfield(transp, &fb->fb.var.transp) | + convert_bitfield(blue, &fb->fb.var.blue) | + convert_bitfield(green, &fb->fb.var.green) | + convert_bitfield(red, &fb->fb.var.red); + + if (fb->fb.var.bits_per_pixel == 8 && regno < 256) { + int hw_reg = CLCD_PALETTE + ((regno * 2) & ~3); + u32 val, mask, newval; + + newval = (red >> 11) & 0x001f; + newval |= (green >> 6) & 0x03e0; + newval |= (blue >> 1) & 0x7c00; + + /* + * 3.2.11: if we're configured for big endian + * byte order, the palette entries are swapped. + */ + if (fb->clcd_cntl & CNTL_BEBO) + regno ^= 1; + + if (regno & 1) { + newval <<= 16; + mask = 0x0000ffff; + } else { + mask = 0xffff0000; + } + + val = readl(fb->regs + hw_reg) & mask; + writel(val | newval, fb->regs + hw_reg); + } + + return regno > 255; +} + +/* + * Blank the screen if blank_mode != 0, else unblank. If blank == NULL + * then the caller blanks by setting the CLUT (Color Look Up Table) to all + * black. Return 0 if blanking succeeded, != 0 if un-/blanking failed due + * to e.g. a video mode which doesn't support it. Implements VESA suspend + * and powerdown modes on hardware that supports disabling hsync/vsync: + * blank_mode == 2: suspend vsync + * blank_mode == 3: suspend hsync + * blank_mode == 4: powerdown + */ +static int clcdfb_blank(int blank_mode, struct fb_info *info) +{ + struct clcd_fb *fb = to_clcd(info); + + if (blank_mode != 0) { + clcdfb_disable(fb); + } else { + clcdfb_enable(fb, fb->clcd_cntl); + } + return 0; +} + +static struct fb_ops clcdfb_ops = { + .owner = THIS_MODULE, + .fb_check_var = clcdfb_check_var, + .fb_set_par = clcdfb_set_par, + .fb_setcolreg = clcdfb_setcolreg, + .fb_blank = clcdfb_blank, + .fb_fillrect = cfb_fillrect, + .fb_copyarea = cfb_copyarea, + .fb_imageblit = cfb_imageblit, + .fb_cursor = soft_cursor, +}; + +static int clcdfb_register(struct clcd_fb *fb) +{ + int ret; + + fb->clk = clk_get(&fb->dev->dev, "CLCDCLK"); + if (IS_ERR(fb->clk)) { + ret = PTR_ERR(fb->clk); + goto out; + } + + ret = clk_use(fb->clk); + if (ret) + goto free_clk; + + fb->fb.fix.mmio_start = fb->dev->res.start; + fb->fb.fix.mmio_len = SZ_4K; + + fb->regs = ioremap(fb->fb.fix.mmio_start, fb->fb.fix.mmio_len); + if (!fb->regs) { + printk(KERN_ERR "CLCD: unable to remap registers\n"); + ret = -ENOMEM; + goto unuse_clk; + } + + fb->fb.fbops = &clcdfb_ops; + fb->fb.flags = FBINFO_FLAG_DEFAULT; + fb->fb.pseudo_palette = fb->cmap; + + strncpy(fb->fb.fix.id, clcd_name, sizeof(fb->fb.fix.id)); + fb->fb.fix.type = FB_TYPE_PACKED_PIXELS; + fb->fb.fix.type_aux = 0; + fb->fb.fix.xpanstep = 0; + fb->fb.fix.ypanstep = 0; + fb->fb.fix.ywrapstep = 0; + fb->fb.fix.accel = FB_ACCEL_NONE; + + fb->fb.var.xres = fb->panel->mode.xres; + fb->fb.var.yres = fb->panel->mode.yres; + fb->fb.var.xres_virtual = fb->panel->mode.xres; + fb->fb.var.yres_virtual = fb->panel->mode.yres; + fb->fb.var.bits_per_pixel = fb->panel->bpp; + fb->fb.var.grayscale = fb->panel->grayscale; + fb->fb.var.pixclock = fb->panel->mode.pixclock; + fb->fb.var.left_margin = fb->panel->mode.left_margin; + fb->fb.var.right_margin = fb->panel->mode.right_margin; + fb->fb.var.upper_margin = fb->panel->mode.upper_margin; + fb->fb.var.lower_margin = fb->panel->mode.lower_margin; + fb->fb.var.hsync_len = fb->panel->mode.hsync_len; + fb->fb.var.vsync_len = fb->panel->mode.vsync_len; + fb->fb.var.sync = fb->panel->mode.sync; + fb->fb.var.vmode = fb->panel->mode.vmode; + fb->fb.var.activate = FB_ACTIVATE_NOW; + fb->fb.var.nonstd = 0; + fb->fb.var.height = fb->panel->height; + fb->fb.var.width = fb->panel->width; + fb->fb.var.accel_flags = 0; + + fb->fb.monspecs.hfmin = 0; + fb->fb.monspecs.hfmax = 100000; + fb->fb.monspecs.vfmin = 0; + fb->fb.monspecs.vfmax = 400; + fb->fb.monspecs.dclkmin = 1000000; + fb->fb.monspecs.dclkmax = 100000000; + + /* + * Make sure that the bitfields are set appropriately. + */ + clcdfb_set_bitfields(fb, &fb->fb.var); + + /* + * Allocate colourmap. + */ + fb_alloc_cmap(&fb->fb.cmap, 256, 0); + + /* + * Ensure interrupts are disabled. + */ + writel(0, fb->regs + CLCD_IENB); + + fb_set_var(&fb->fb, &fb->fb.var); + + printk(KERN_INFO "CLCD: %s hardware, %s display\n", + fb->board->name, fb->panel->mode.name); + + ret = register_framebuffer(&fb->fb); + if (ret == 0) + goto out; + + printk(KERN_ERR "CLCD: cannot register framebuffer (%d)\n", ret); + + iounmap(fb->regs); + unuse_clk: + clk_unuse(fb->clk); + free_clk: + clk_put(fb->clk); + out: + return ret; +} + +static int clcdfb_probe(struct amba_device *dev, void *id) +{ + struct clcd_board *board = dev->dev.platform_data; + struct clcd_fb *fb; + int ret; + + if (!board) + return -EINVAL; + + ret = amba_request_regions(dev, NULL); + if (ret) { + printk(KERN_ERR "CLCD: unable to reserve regs region\n"); + goto out; + } + + fb = (struct clcd_fb *) kmalloc(sizeof(struct clcd_fb), GFP_KERNEL); + if (!fb) { + printk(KERN_INFO "CLCD: could not allocate new clcd_fb struct\n"); + ret = -ENOMEM; + goto free_region; + } + memset(fb, 0, sizeof(struct clcd_fb)); + + fb->dev = dev; + fb->board = board; + + ret = fb->board->setup(fb); + if (ret) + goto free_fb; + + ret = clcdfb_register(fb); + if (ret == 0) { + amba_set_drvdata(dev, fb); + goto out; + } + + fb->board->remove(fb); + free_fb: + kfree(fb); + free_region: + amba_release_regions(dev); + out: + return ret; +} + +static int clcdfb_remove(struct amba_device *dev) +{ + struct clcd_fb *fb = amba_get_drvdata(dev); + + amba_set_drvdata(dev, NULL); + + clcdfb_disable(fb); + unregister_framebuffer(&fb->fb); + iounmap(fb->regs); + clk_unuse(fb->clk); + clk_put(fb->clk); + + fb->board->remove(fb); + + kfree(fb); + + amba_release_regions(dev); + + return 0; +} + +static struct amba_id clcdfb_id_table[] = { + { + .id = 0x00041110, + .mask = 0x000fffff, + }, + { 0, 0 }, +}; + +static struct amba_driver clcd_driver = { + .drv = { + .name = "clcd-pl110", + }, + .probe = clcdfb_probe, + .remove = clcdfb_remove, + .id_table = clcdfb_id_table, +}; + +int __init amba_clcdfb_init(void) +{ + if (fb_get_options("ambafb", NULL)) + return -ENODEV; + + return amba_driver_register(&clcd_driver); +} + +module_init(amba_clcdfb_init); + +static void __exit amba_clcdfb_exit(void) +{ + amba_driver_unregister(&clcd_driver); +} + +module_exit(amba_clcdfb_exit); + +MODULE_DESCRIPTION("ARM PrimeCell PL110 CLCD core driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/video/amifb.c b/drivers/video/amifb.c index 95ea71c9b..baa4fa06b 100644 --- a/drivers/video/amifb.c +++ b/drivers/video/amifb.c @@ -1307,6 +1307,8 @@ static int amifb_set_par(struct fb_info *info) info->fix.ywrapstep = 1; info->fix.xpanstep = 0; info->fix.ypanstep = 0; + info->flags = FBINFO_DEFAULT | FBINFO_HWACCEL_YWRAP | + FBINFO_READS_FAST; /* override SCROLL_REDRAW */ } else { info->fix.ywrapstep = 0; if (par->vmode & FB_VMODE_SMOOTH_XPAN) @@ -1314,6 +1316,7 @@ static int amifb_set_par(struct fb_info *info) else info->fix.xpanstep = 16<fix.ypanstep = 1; + info->flags = FBINFO_DEFAULT | FBINFO_HWACCEL_YPAN; } return 0; } @@ -2254,6 +2257,13 @@ int __init amifb_init(void) u_long chipptr; u_int defmode; +#ifndef MODULE + char *option = NULL; + + if (fb_get_options("amifb", &option)) + return -ENODEV; + amifb_setup(option); +#endif if (!MACH_IS_AMIGA || !AMIGAHW_PRESENT(AMI_VIDEO)) return -ENXIO; @@ -2382,7 +2392,7 @@ default_chipset: fb_info.fbops = &amifb_ops; fb_info.par = ¤tpar; - fb_info.flags = FBINFO_FLAG_DEFAULT; + fb_info.flags = FBINFO_DEFAULT; if (!fb_find_mode(&fb_info.var, &fb_info, mode_option, ami_modedb, NUM_TOTAL_MODES, &ami_modedb[defmode], 4)) { @@ -3811,14 +3821,11 @@ static void ami_rebuild_copper(void) } +module_init(amifb_init); + #ifdef MODULE MODULE_LICENSE("GPL"); -int init_module(void) -{ - return amifb_init(); -} - void cleanup_module(void) { unregister_framebuffer(&fb_info); diff --git a/drivers/video/asiliantfb.c b/drivers/video/asiliantfb.c index 1563b0d27..bda3ed9ba 100644 --- a/drivers/video/asiliantfb.c +++ b/drivers/video/asiliantfb.c @@ -524,7 +524,7 @@ static void __init init_asiliant(struct fb_info *p, unsigned long addr) p->fix.smem_start = addr; p->var = asiliantfb_var; p->fbops = &asiliantfb_ops; - p->flags = FBINFO_FLAG_DEFAULT; + p->flags = FBINFO_DEFAULT; fb_alloc_cmap(&p->cmap, 256, 0); @@ -609,9 +609,14 @@ static struct pci_driver asiliantfb_driver = { int __init asiliantfb_init(void) { + if (fb_get_options("asiliantfb", NULL)) + return -ENODEV; + return pci_module_init(&asiliantfb_driver); } +module_init(asiliantfb_init); + static void __exit asiliantfb_exit(void) { pci_unregister_driver(&asiliantfb_driver); diff --git a/drivers/video/aty/aty128fb.c b/drivers/video/aty/aty128fb.c index 1c7bcd80f..f2d82d005 100644 --- a/drivers/video/aty/aty128fb.c +++ b/drivers/video/aty/aty128fb.c @@ -2439,6 +2439,14 @@ static int aty128_pci_resume(struct pci_dev *pdev) int __init aty128fb_init(void) { +#ifndef MODULE + char *option = NULL; + + if (fb_get_options("aty128fb", &option)) + return -ENODEV; + aty128fb_setup(option); +#endif + return pci_module_init(&aty128fb_driver); } @@ -2447,8 +2455,9 @@ static void __exit aty128fb_exit(void) pci_unregister_driver(&aty128fb_driver); } -#ifdef MODULE module_init(aty128fb_init); + +#ifdef MODULE module_exit(aty128fb_exit); MODULE_AUTHOR("(c)1999-2003 Brad Douglas "); diff --git a/drivers/video/bw2.c b/drivers/video/bw2.c index b3ab23b07..65b65addf 100644 --- a/drivers/video/bw2.c +++ b/drivers/video/bw2.c @@ -323,7 +323,7 @@ static void bw2_init_one(struct sbus_dev *sdev) resp = &res; all->info.var.xres = all->info.var.xres_virtual = 1152; all->info.var.yres = all->info.var.yres_virtual = 900; - all->info.bits_per_pixel = 1; + all->info.var.bits_per_pixel = 1; linebytes = 1152 / 8; } else #else @@ -337,6 +337,10 @@ static void bw2_init_one(struct sbus_dev *sdev) all->info.var.xres); } #endif + all->info.var.red.length = all->info.var.green.length = + all->info.var.blue.length = all->info.var.bits_per_pixel; + all->info.var.red.offset = all->info.var.green.offset = + all->info.var.blue.offset = 0; all->par.regs = (struct bw2_regs *) sbus_ioremap(resp, BWTWO_REGISTER_OFFSET, @@ -347,7 +351,7 @@ static void bw2_init_one(struct sbus_dev *sdev) all->par.fbsize = PAGE_ALIGN(linebytes * all->info.var.yres); - all->info.flags = FBINFO_FLAG_DEFAULT; + all->info.flags = FBINFO_DEFAULT; all->info.fbops = &bw2_ops; #if defined(CONFIG_SPARC32) if (sdev) @@ -382,6 +386,9 @@ int __init bw2_init(void) struct sbus_bus *sbus; struct sbus_dev *sdev; + if (fb_get_options("bw2fb", &option)) + return -ENODEV; + #ifdef CONFIG_SUN4 bw2_init_one(NULL); #endif @@ -412,8 +419,9 @@ bw2_setup(char *arg) return 0; } -#ifdef MODULE module_init(bw2_init); + +#ifdef MODULE module_exit(bw2_exit); #endif diff --git a/drivers/video/cfbcopyarea.c b/drivers/video/cfbcopyarea.c index 9c32d2b40..b4b286a4c 100644 --- a/drivers/video/cfbcopyarea.c +++ b/drivers/video/cfbcopyarea.c @@ -43,8 +43,9 @@ #define BYTES_PER_LONG 8 #endif -static void bitcpy(unsigned long *dst, int dst_idx, const unsigned long *src, - int src_idx, unsigned long n) +static void bitcpy(unsigned long __iomem *dst, int dst_idx, + const unsigned long __iomem *src, int src_idx, + unsigned long n) { unsigned long first, last; int shift = dst_idx-src_idx, left, right; @@ -185,8 +186,8 @@ static void bitcpy(unsigned long *dst, int dst_idx, const unsigned long *src, } } -static void bitcpy_rev(unsigned long *dst, int dst_idx, - const unsigned long *src, int src_idx, unsigned long n) +static void bitcpy_rev(unsigned long __iomem *dst, int dst_idx, + const unsigned long __iomem *src, int src_idx, unsigned long n) { unsigned long first, last; int shift = dst_idx-src_idx, left, right; @@ -344,7 +345,7 @@ void cfb_copyarea(struct fb_info *p, const struct fb_copyarea *area) int x2, y2, old_dx, old_dy, vxres, vyres; unsigned long next_line = p->fix.line_length; int dst_idx = 0, src_idx = 0, rev_copy = 0; - unsigned long *dst = NULL, *src = NULL; + unsigned long __iomem *dst = NULL, *src = NULL; if (p->state != FBINFO_STATE_RUNNING) return; @@ -394,7 +395,7 @@ void cfb_copyarea(struct fb_info *p, const struct fb_copyarea *area) rev_copy = 1; } - dst = src = (unsigned long *)((unsigned long)p->screen_base & + dst = src = (unsigned long __iomem *)((unsigned long)p->screen_base & ~(BYTES_PER_LONG-1)); dst_idx = src_idx = (unsigned long)p->screen_base & (BYTES_PER_LONG-1); dst_idx += dy*next_line*8 + dx*p->var.bits_per_pixel; diff --git a/drivers/video/cfbfillrect.c b/drivers/video/cfbfillrect.c index 20f3acfd8..4a94bf85e 100644 --- a/drivers/video/cfbfillrect.c +++ b/drivers/video/cfbfillrect.c @@ -119,7 +119,7 @@ static inline unsigned long pixel_to_pat(const struct fb_info *p, * Unaligned 32-bit pattern fill using 32/64-bit memory accesses */ -void bitfill32(unsigned long *dst, int dst_idx, u32 pat, u32 n) +void bitfill32(unsigned long __iomem *dst, int dst_idx, u32 pat, u32 n) { unsigned long val = pat; unsigned long first, last; @@ -178,7 +178,7 @@ void bitfill32(unsigned long *dst, int dst_idx, u32 pat, u32 n) * used for the next 32/64-bit word */ -void bitfill(unsigned long *dst, int dst_idx, unsigned long pat, int left, +void bitfill(unsigned long __iomem *dst, int dst_idx, unsigned long pat, int left, int right, u32 n) { unsigned long first, last; @@ -228,7 +228,7 @@ void bitfill(unsigned long *dst, int dst_idx, unsigned long pat, int left, } } -void bitfill32_rev(unsigned long *dst, int dst_idx, u32 pat, u32 n) +void bitfill32_rev(unsigned long __iomem *dst, int dst_idx, u32 pat, u32 n) { unsigned long val = pat, dat; unsigned long first, last; @@ -300,7 +300,7 @@ void bitfill32_rev(unsigned long *dst, int dst_idx, u32 pat, u32 n) * used for the next 32/64-bit word */ -void bitfill_rev(unsigned long *dst, int dst_idx, unsigned long pat, int left, +void bitfill_rev(unsigned long __iomem *dst, int dst_idx, unsigned long pat, int left, int right, u32 n) { unsigned long first, last, dat; @@ -364,7 +364,7 @@ void cfb_fillrect(struct fb_info *p, const struct fb_fillrect *rect) u32 bpp = p->var.bits_per_pixel; unsigned long x2, y2, vxres, vyres; unsigned long height, width, fg; - unsigned long *dst; + unsigned long __iomem *dst; int dst_idx, left; if (p->state != FBINFO_STATE_RUNNING) @@ -397,7 +397,7 @@ void cfb_fillrect(struct fb_info *p, const struct fb_fillrect *rect) else fg = rect->color; - dst = (unsigned long *)((unsigned long)p->screen_base & + dst = (unsigned long __iomem *)((unsigned long)p->screen_base & ~(BYTES_PER_LONG-1)); dst_idx = ((unsigned long)p->screen_base & (BYTES_PER_LONG-1))*8; dst_idx += rect->dy*p->fix.line_length*8+rect->dx*bpp; @@ -407,7 +407,7 @@ void cfb_fillrect(struct fb_info *p, const struct fb_fillrect *rect) p->fbops->fb_sync(p); if (!left) { u32 pat = pixel_to_pat32(p, fg); - void (*fill_op32)(unsigned long *dst, int dst_idx, u32 pat, + void (*fill_op32)(unsigned long __iomem *dst, int dst_idx, u32 pat, u32 n) = NULL; switch (rect->rop) { @@ -429,7 +429,7 @@ void cfb_fillrect(struct fb_info *p, const struct fb_fillrect *rect) unsigned long pat = pixel_to_pat(p, fg, (left-dst_idx) % bpp); int right = bpp-left; int r; - void (*fill_op)(unsigned long *dst, int dst_idx, + void (*fill_op)(unsigned long __iomem *dst, int dst_idx, unsigned long pat, int left, int right, u32 n) = NULL; diff --git a/drivers/video/cfbimgblt.c b/drivers/video/cfbimgblt.c index 31da88e32..4c123abaa 100644 --- a/drivers/video/cfbimgblt.c +++ b/drivers/video/cfbimgblt.c @@ -87,21 +87,22 @@ static u32 cfb_tab32[] = { #endif static inline void color_imageblit(const struct fb_image *image, - struct fb_info *p, u8 *dst1, + struct fb_info *p, u8 __iomem *dst1, u32 start_index, u32 pitch_index) { /* Draw the penguin */ - u32 *dst, *dst2, color = 0, val, shift; + u32 __iomem *dst, *dst2; + u32 color = 0, val, shift; int i, n, bpp = p->var.bits_per_pixel; u32 null_bits = 32 - bpp; u32 *palette = (u32 *) p->pseudo_palette; const u8 *src = image->data; - dst2 = (u32 *) dst1; + dst2 = (u32 __iomem *) dst1; for (i = image->height; i--; ) { n = image->width; - dst = (u32 *) dst1; + dst = (u32 __iomem *) dst1; shift = 0; val = 0; @@ -136,7 +137,7 @@ static inline void color_imageblit(const struct fb_image *image, dst1 += p->fix.line_length; if (pitch_index) { dst2 += p->fix.line_length; - dst1 = (u8 *)((long)dst2 & ~(sizeof(u32) - 1)); + dst1 = (u8 __iomem *)((long __force)dst2 & ~(sizeof(u32) - 1)); start_index += pitch_index; start_index &= 32 - 1; @@ -145,25 +146,26 @@ static inline void color_imageblit(const struct fb_image *image, } static inline void slow_imageblit(const struct fb_image *image, struct fb_info *p, - u8 *dst1, u32 fgcolor, + u8 __iomem *dst1, u32 fgcolor, u32 bgcolor, u32 start_index, u32 pitch_index) { u32 shift, color = 0, bpp = p->var.bits_per_pixel; - u32 *dst, *dst2, val, pitch = p->fix.line_length; + u32 __iomem *dst, *dst2; + u32 val, pitch = p->fix.line_length; u32 null_bits = 32 - bpp; u32 spitch = (image->width+7)/8; const u8 *src = image->data, *s; u32 i, j, l; - dst2 = (u32 *) dst1; + dst2 = (u32 __iomem *) dst1; for (i = image->height; i--; ) { shift = val = 0; l = 8; j = image->width; - dst = (u32 *) dst1; + dst = (u32 __iomem *) dst1; s = src; /* write leading bits */ @@ -201,7 +203,7 @@ static inline void slow_imageblit(const struct fb_image *image, struct fb_info * src += spitch; if (pitch_index) { dst2 += pitch; - dst1 = (u8 *)((long)dst2 & ~(sizeof(u32) - 1)); + dst1 = (u8 __iomem *)((long __force)dst2 & ~(sizeof(u32) - 1)); start_index += pitch_index; start_index &= 32 - 1; } @@ -218,14 +220,14 @@ static inline void slow_imageblit(const struct fb_image *image, struct fb_info * * beginning and end of a scanline is dword aligned */ static inline void fast_imageblit(const struct fb_image *image, struct fb_info *p, - u8 *dst1, u32 fgcolor, + u8 __iomem *dst1, u32 fgcolor, u32 bgcolor) { u32 fgx = fgcolor, bgx = bgcolor, bpp = p->var.bits_per_pixel; u32 ppw = 32/bpp, spitch = (image->width + 7)/8; u32 bit_mask, end_mask, eorx, shift; const char *s = image->data, *src; - u32 *dst; + u32 __iomem *dst; u32 *tab = NULL; int i, j, k; @@ -253,7 +255,7 @@ static inline void fast_imageblit(const struct fb_image *image, struct fb_info * k = image->width/ppw; for (i = image->height; i--; ) { - dst = (u32 *) dst1, shift = 8; src = s; + dst = (u32 __iomem *) dst1, shift = 8; src = s; for (j = k; j--; ) { shift -= ppw; @@ -273,7 +275,7 @@ void cfb_imageblit(struct fb_info *p, const struct fb_image *image) u32 width = image->width, height = image->height; u32 dx = image->dx, dy = image->dy; int x2, y2, vxres, vyres; - u8 *dst1; + u8 __iomem *dst1; if (p->state != FBINFO_STATE_RUNNING) return; @@ -325,7 +327,7 @@ void cfb_imageblit(struct fb_info *p, const struct fb_image *image) else slow_imageblit(image, p, dst1, fgcolor, bgcolor, start_index, pitch_index); - } else if (image->depth <= bpp) + } else color_imageblit(image, p, dst1, start_index, pitch_index); } diff --git a/drivers/video/cg14.c b/drivers/video/cg14.c index e0fc4c02c..c43322b2d 100644 --- a/drivers/video/cg14.c +++ b/drivers/video/cg14.c @@ -550,7 +550,7 @@ static void cg14_init_one(struct sbus_dev *sdev, int node, int parent_node) all->par.mode = MDI_8_PIX; all->par.ramsize = (is_8mb ? 0x800000 : 0x400000); - all->info.flags = FBINFO_FLAG_DEFAULT; + all->info.flags = FBINFO_DEFAULT | FBINFO_HWACCEL_YPAN; all->info.fbops = &cg14_ops; all->info.currcon = -1; all->info.par = &all->par; @@ -584,6 +584,9 @@ int __init cg14_init(void) struct sbus_bus *sbus; struct sbus_dev *sdev; + if (fb_get_options("cg14fb", NULL)) + return -ENODEV; + #ifdef CONFIG_SPARC32 { int root, node; @@ -626,8 +629,9 @@ cg14_setup(char *arg) return 0; } -#ifdef MODULE module_init(cg14_init); + +#ifdef MODULE module_exit(cg14_exit); #endif diff --git a/drivers/video/cg3.c b/drivers/video/cg3.c index dd0163cc3..078394109 100644 --- a/drivers/video/cg3.c +++ b/drivers/video/cg3.c @@ -398,7 +398,7 @@ static void cg3_init_one(struct sbus_dev *sdev) sbus_ioremap(&sdev->resource[0], CG3_REGS_OFFSET, sizeof(struct cg3_regs), "cg3 regs"); - all->info.flags = FBINFO_FLAG_DEFAULT; + all->info.flags = FBINFO_DEFAULT; all->info.fbops = &cg3_ops; #ifdef CONFIG_SPARC32 all->info.screen_base = (char *) @@ -444,6 +444,9 @@ int __init cg3_init(void) struct sbus_bus *sbus; struct sbus_dev *sdev; + if (fb_get_options("cg3fb", NULL)) + return -ENODEV; + for_all_sbusdev(sdev, sbus) { if (!strcmp(sdev->prom_name, "cgthree") || !strcmp(sdev->prom_name, "cgRDI")) @@ -473,8 +476,9 @@ cg3_setup(char *arg) return 0; } -#ifdef MODULE module_init(cg3_init); + +#ifdef MODULE module_exit(cg3_exit); #endif diff --git a/drivers/video/cg6.c b/drivers/video/cg6.c index 469ac2060..be87a66f1 100644 --- a/drivers/video/cg6.c +++ b/drivers/video/cg6.c @@ -712,7 +712,8 @@ static void cg6_init_one(struct sbus_dev *sdev) sbus_ioremap(&sdev->resource[0], CG6_FHC_OFFSET, sizeof(u32), "cgsix fhc"); - all->info.flags = FBINFO_FLAG_DEFAULT; + all->info.flags = FBINFO_DEFAULT | FBINFO_HWACCEL_IMAGEBLIT | + FBINFO_HWACCEL_COPYAREA | FBINFO_HWACCEL_FILLRECT; all->info.fbops = &cg6_ops; #ifdef CONFIG_SPARC32 all->info.screen_base = (char *) @@ -759,6 +760,9 @@ int __init cg6_init(void) struct sbus_bus *sbus; struct sbus_dev *sdev; + if (fb_get_options("cg6fb", NULL)) + return -ENODEV; + for_all_sbusdev(sdev, sbus) { if (!strcmp(sdev->prom_name, "cgsix") || !strcmp(sdev->prom_name, "cgthree+")) @@ -788,8 +792,9 @@ cg6_setup(char *arg) return 0; } -#ifdef MODULE module_init(cg6_init); + +#ifdef MODULE module_exit(cg6_exit); #endif diff --git a/drivers/video/chipsfb.c b/drivers/video/chipsfb.c index c804352c0..4ee5a25db 100644 --- a/drivers/video/chipsfb.c +++ b/drivers/video/chipsfb.c @@ -362,7 +362,7 @@ static void __init init_chips(struct fb_info *p, unsigned long addr) p->var = chipsfb_var; p->fbops = &chipsfb_ops; - p->flags = FBINFO_FLAG_DEFAULT; + p->flags = FBINFO_DEFAULT; fb_alloc_cmap(&p->cmap, 256, 0); @@ -462,9 +462,14 @@ static struct pci_driver chipsfb_driver = { int __init chips_init(void) { + if (fb_get_options("chipsfb", NULL)) + return -ENODEV; + return pci_module_init(&chipsfb_driver); } +module_init(chips_init); + static void __exit chipsfb_exit(void) { pci_unregister_driver(&chipsfb_driver); diff --git a/drivers/video/clps711xfb.c b/drivers/video/clps711xfb.c index a982e7520..8692e0029 100644 --- a/drivers/video/clps711xfb.c +++ b/drivers/video/clps711xfb.c @@ -364,6 +364,9 @@ int __init clps711xfb_init(void) { int err = -ENOMEM; + if (fb_get_options("clps711xfb", NULL)) + return -ENODEV; + cfb = kmalloc(sizeof(*cfb), GFP_KERNEL); if (!cfb) goto out; @@ -372,7 +375,7 @@ int __init clps711xfb_init(void) strcpy(cfb->fix.id, "clps711x"); cfb->fbops = &clps7111fb_ops; - cfb->flags = FBINFO_FLAG_DEFAULT; + cfb->flags = FBINFO_DEFAULT; clps711x_guess_lcd_params(cfb); @@ -432,9 +435,7 @@ static void __exit clps711xfb_exit(void) } } -#ifdef MODULE module_init(clps711xfb_init); -#endif module_exit(clps711xfb_exit); MODULE_AUTHOR("Russell King "); diff --git a/drivers/video/controlfb.c b/drivers/video/controlfb.c index 8ad33fa31..22896d692 100644 --- a/drivers/video/controlfb.c +++ b/drivers/video/controlfb.c @@ -556,6 +556,11 @@ static void control_set_hardware(struct fb_info_control *p, struct fb_par_contro int __init control_init(void) { struct device_node *dp; + char *option = NULL; + + if (fb_get_options("controlfb", &option)) + return -ENODEV; + control_setup(option); dp = find_devices("control"); if (dp != 0 && !control_of_init(dp)) @@ -564,6 +569,7 @@ int __init control_init(void) return -ENXIO; } +module_init(control_init); /* Work out which banks of VRAM we have installed. */ /* danj: I guess the card just ignores writes to nonexistant VRAM... */ @@ -1010,7 +1016,7 @@ static void __init control_init_info(struct fb_info *info, struct fb_info_contro info->par = &p->par; info->fbops = &controlfb_ops; info->pseudo_palette = p->pseudo_palette; - info->flags = FBINFO_FLAG_DEFAULT; + info->flags = FBINFO_DEFAULT | FBINFO_HWACCEL_YPAN; info->screen_base = (char *) p->frame_buffer + CTRLFB_OFF; fb_alloc_cmap(&info->cmap, 256, 0); diff --git a/drivers/video/dnfb.c b/drivers/video/dnfb.c index 11ba053b5..1dbb82dca 100644 --- a/drivers/video/dnfb.c +++ b/drivers/video/dnfb.c @@ -239,6 +239,9 @@ static int __devinit dnfb_probe(struct device *device) info->fbops = &dn_fb_ops; info->fix = dnfb_fix; info->var = dnfb_var; + info->var.red.length = 1; + info->var.red.offset = 0; + info->var.green = info->var.blue = info->var.red; info->screen_base = (u_char *) info->fix.smem_start; err = fb_alloc_cmap(&info->cmap, 2, 0); @@ -281,6 +284,9 @@ int __init dnfb_init(void) { int ret; + if (fb_get_options("dnfb", NULL)) + return -ENODEV; + ret = driver_register(&dnfb_driver); if (!ret) { @@ -291,4 +297,6 @@ int __init dnfb_init(void) return ret; } +module_init(dnfb_init); + MODULE_LICENSE("GPL"); diff --git a/drivers/video/epson1355fb.c b/drivers/video/epson1355fb.c index a998ee9d8..6cb54f809 100644 --- a/drivers/video/epson1355fb.c +++ b/drivers/video/epson1355fb.c @@ -1,541 +1,771 @@ /* - * linux/drivers/video/epson1355fb.c - * -- Support for the Epson SED1355 LCD/CRT controller + * linux/drivers/video/epson1355fb.c -- Epson S1D13505 frame buffer for 2.5. * - * Copyright (C) 2000 Philipp Rumpf + * Epson Research S1D13505 Embedded RAMDAC LCD/CRT Controller + * (previously known as SED1355) * - * based on linux/drivers/video/skeletonfb.c, which was + * Cf. http://www.erd.epson.com/vdc/html/S1D13505.html + * + * + * Copyright (C) Hewlett-Packard Company. All rights reserved. + * + * Written by Christopher Hoover + * + * Adapted from: + * + * linux/drivers/video/skeletonfb.c + * Modified to new api Jan 2001 by James Simmons (jsimmons@infradead.org) * Created 28 Dec 1997 by Geert Uytterhoeven * + * linux/drivers/video/epson1355fb.c (2.4 driver) + * Copyright (C) 2000 Philipp Rumpf + * * 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. - */ -/* TODO (roughly in order of priority): - * 16 bpp support - * crt support - * hw cursor support - * SwivelView + * License. See the file COPYING in the main directory of this archive for + * more details. + * + * + * Noteworthy Issues + * ----------------- + * + * This driver is complicated by the fact that this is a 16-bit chip + * and, on at least one platform (ceiva), we can only do 16-bit reads + * and writes to the framebuffer. We hide this from user space + * except in the case of mmap(). + * + * + * To Do + * ----- + * + * - Test 8-bit pseudocolor mode + * - Allow setting bpp, virtual resolution + * - Implement horizontal panning + * - (maybe) Implement hardware cursor */ -#include -#include -#include -#include -#include -#include -#include -#include -#include #include -#include +#include +#include #include +#include #include -#include